From 66236eff0a1ab490d3466f966fc4f3940bf54be9 Mon Sep 17 00:00:00 2001 From: YouKnow Date: Wed, 24 Apr 2024 18:55:20 +0330 Subject: [PATCH 01/77] 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 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 02/77] 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 03/77] 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 44531cded17aa82d8618154b9ede558a2f826225 Mon Sep 17 00:00:00 2001 From: Shakhzhakhan Maxudbek Date: Sun, 28 Apr 2024 00:17:25 +0500 Subject: [PATCH 04/77] 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 05/77] 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 06/77] 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 07/77] 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 08/77] 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 09/77] 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 10/77] 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 11/77] 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 12/77] 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 13/77] 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 14/77] 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 15/77] 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 16/77] 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 17/77] 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 18/77] 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 19/77] 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 20/77] 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 21/77] 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 22/77] 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 23/77] 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 24/77] 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 25/77] 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 26/77] 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 27/77] 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 28/77] --- 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 29/77] 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 30/77] 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 31/77] 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 32/77] 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 33/77] 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 34/77] 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 35/77] 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 36/77] 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 37/77] 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 38/77] 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 39/77] 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 40/77] 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 41/77] 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 42/77] 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 43/77] 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 44/77] 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 45/77] 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 46/77] 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 47/77] 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 48/77] 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 49/77] 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 50/77] 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 51/77] 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 52/77] 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 53/77] 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 54/77] 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 55/77] 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 56/77] 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 57/77] 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 58/77] 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 59/77] 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 004f7dec172c8fc0854d54f39880b6f6c1b18aca Mon Sep 17 00:00:00 2001 From: Zaid _ Date: Thu, 27 Jun 2024 14:27:10 +0300 Subject: [PATCH 60/77] 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 61/77] 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 62/77] 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 63/77] 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 64/77] 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 65/77] 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 66/77] 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 67/77] 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 68/77] 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 69/77] 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 70/77] 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 71/77] 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 72/77] 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 73/77] 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 74/77] 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 75/77] 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 805b1669cfe39e6d506dcbc044deef7a2c7399f8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 8 Jul 2024 22:59:56 +0300 Subject: [PATCH 76/77] 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 77/77] 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