From a42f8599c752d15991d2bc9822ebafa155193318 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 13:12:04 +0300 Subject: [PATCH 01/39] increased Telegram Bot API version --- aiogram/__init__.py | 2 +- aiogram/bot/api.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aiogram/__init__.py b/aiogram/__init__.py index 9a11b7a2..1c66de7e 100644 --- a/aiogram/__init__.py +++ b/aiogram/__init__.py @@ -44,4 +44,4 @@ __all__ = ( ) __version__ = '2.10' -__api_version__ = '4.9' +__api_version__ = '5.0' diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index cb258cdf..592257ee 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -153,7 +153,7 @@ class Methods(Helper): """ Helper for Telegram API Methods listed on https://core.telegram.org/bots/api - List is updated to Bot API 4.9 + List is updated to Bot API 5.0 """ mode = HelperMode.lowerCamelCase From 9b7afe159cbd826a0229186203b251cbede5d2f6 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 13:27:46 +0300 Subject: [PATCH 02/39] AIOG-T-64 added logOut method --- aiogram/bot/api.py | 1 + aiogram/bot/bot.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index 592257ee..2338b492 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -165,6 +165,7 @@ class Methods(Helper): # Available methods GET_ME = Item() # getMe + LOG_OUT = Item() # logOut SEND_MESSAGE = Item() # sendMessage FORWARD_MESSAGE = Item() # forwardMessage SEND_PHOTO = Item() # sendPhoto diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index c5678a37..ad357643 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -176,6 +176,24 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_ME, payload) return types.User(**result) + async def log_out(self) -> types.User: + """ + Use this method to log out from the cloud Bot API server before launching + the bot locally. You **must** log out the bot before running it locally, + otherwise there is no guarantee that the bot will receive updates. + After a successful call, you will not be able to log in again using the + same token for 10 minutes. Returns True on success. Requires no parameters. + + Source: https://core.telegram.org/bots/api#logout + + :return: Returns True on success + :rtype: :obj:`base.Boolean` + """ + payload = generate_payload(**locals()) + + result = await self.request(api.Methods.LOG_OUT, payload) + return result + async def send_message(self, chat_id: typing.Union[base.Integer, base.String], text: base.String, parse_mode: typing.Union[base.String, None] = None, disable_web_page_preview: typing.Union[base.Boolean, None] = None, From 8fbc182fc940267f5029aebea009e343dfbe3f4a Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 13:27:57 +0300 Subject: [PATCH 03/39] AIOG-T-64 added logOut method test --- tests/test_bot.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_bot.py b/tests/test_bot.py index cf1c3c3b..bbfb91fb 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -24,6 +24,14 @@ async def test_get_me(bot: Bot, event_loop): assert result == user +async def test_log_out(bot: Bot, event_loop): + """ logOut method test """ + + async with FakeTelegram(message_data=True, loop=event_loop): + result = await bot.log_out() + assert result is True + + async def test_send_message(bot: Bot, event_loop): """ sendMessage method test """ from .types.dataset import MESSAGE From ee523afb8c2c7ba18987d8f6982584d548e167e8 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 14:03:54 +0300 Subject: [PATCH 04/39] AIOG-T-64 logOut type annotation fix --- aiogram/bot/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index ad357643..3c24071c 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -176,7 +176,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_ME, payload) return types.User(**result) - async def log_out(self) -> types.User: + async def log_out(self) -> base.Boolean: """ Use this method to log out from the cloud Bot API server before launching the bot locally. You **must** log out the bot before running it locally, From f544fa704006c7551f1039d59d3f63ed14fe4398 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 14:22:07 +0300 Subject: [PATCH 05/39] AIOG-T-65 added close (close_bot) method --- aiogram/bot/api.py | 1 + aiogram/bot/bot.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index 2338b492..a4379334 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -166,6 +166,7 @@ class Methods(Helper): # Available methods GET_ME = Item() # getMe LOG_OUT = Item() # logOut + CLOSE = Item() # close SEND_MESSAGE = Item() # sendMessage FORWARD_MESSAGE = Item() # forwardMessage SEND_PHOTO = Item() # sendPhoto diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 3c24071c..63fc0934 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -194,6 +194,24 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.LOG_OUT, payload) return result + async def close_bot(self) -> base.Boolean: + """ + Use this method to close the bot instance before moving it from one local + server to another. You need to delete the webhook before calling this method + to ensure that the bot isn't launched again after server restart. The method + will return error 429 in the first 10 minutes after the bot is launched. + Returns True on success. Requires no parameters. + + Source: https://core.telegram.org/bots/api#close + + :return: Returns True on success + :rtype: :obj:`base.Boolean` + """ + payload = generate_payload(**locals()) + + result = await self.request(api.Methods.CLOSE, payload) + return result + async def send_message(self, chat_id: typing.Union[base.Integer, base.String], text: base.String, parse_mode: typing.Union[base.String, None] = None, disable_web_page_preview: typing.Union[base.Boolean, None] = None, From 84e302156a5ab2e173b87b01d05768bd75a492f3 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 14:25:51 +0300 Subject: [PATCH 06/39] AIOG-T-65 old `close` method deprecation warn --- aiogram/bot/base.py | 3 +++ aiogram/bot/bot.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/aiogram/bot/base.py b/aiogram/bot/base.py index f45546c3..ee2e1af8 100644 --- a/aiogram/bot/base.py +++ b/aiogram/bot/base.py @@ -15,6 +15,7 @@ from . import api from ..types import ParseMode, base from ..utils import json from ..utils.auth_widget import check_integrity +from ..utils.deprecated import deprecated class BaseBot: @@ -173,6 +174,8 @@ class BaseBot: finally: self._ctx_token.reset(token) + @deprecated("This method behaviour will be changed in aiogram v3.0. " + "More info: https://core.telegram.org/bots/api#close") async def close(self): """ Close all client sessions diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 63fc0934..9fe01ff0 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -7,6 +7,7 @@ import warnings from .base import BaseBot, api from .. import types from ..types import base +from ..utils.deprecated import deprecated from ..utils.mixins import DataMixin, ContextInstanceMixin from ..utils.payload import generate_payload, prepare_arg, prepare_attachment, prepare_file @@ -194,6 +195,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.LOG_OUT, payload) return result + @deprecated("This method will be renamed to `close` in aiogram v3.0") async def close_bot(self) -> base.Boolean: """ Use this method to close the bot instance before moving it from one local From 6a2911f73b9028694409594356604075338cce6f Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 14:33:16 +0300 Subject: [PATCH 07/39] AIOG-T-65 `close_bot` test added --- tests/test_bot.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_bot.py b/tests/test_bot.py index bbfb91fb..45d3a3fa 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -32,6 +32,14 @@ async def test_log_out(bot: Bot, event_loop): assert result is True +async def test_close_bot(bot: Bot, event_loop): + """ close method test """ + + async with FakeTelegram(message_data=True, loop=event_loop): + result = await bot.close_bot() + assert result is True + + async def test_send_message(bot: Bot, event_loop): """ sendMessage method test """ from .types.dataset import MESSAGE From e2c7c4f0fc771decb6961ea4021de1e14eb2cc5b Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 14:47:15 +0300 Subject: [PATCH 08/39] AIOG-T-67 added ip_address param to set_webhook, updated docs --- aiogram/bot/bot.py | 58 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 9fe01ff0..66a4fb48 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -97,26 +97,54 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return [types.Update(**update) for update in result] async def set_webhook(self, url: base.String, - certificate: typing.Union[base.InputFile, None] = None, - max_connections: typing.Union[base.Integer, None] = None, - allowed_updates: typing.Union[typing.List[base.String], None] = None) -> base.Boolean: + certificate: typing.Optional[base.InputFile] = None, + ip_address: typing.Optional[base.String] = None, + max_connections: typing.Optional[base.Integer] = None, + allowed_updates: typing.Optional[typing.List[base.String]] = None) -> base.Boolean: """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. - Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, - containing a JSON-serialized Update. In case of an unsuccessful request, - we will give up after a reasonable amount of attempts. + Use this method to specify a url and receive incoming updates via an outgoing + webhook. Whenever there is an update for the bot, we will send an HTTPS POST + request to the specified url, containing a JSON-serialized Update. In case + of an unsuccessful request, we will give up after a reasonable amount of + attempts. Returns True on success. + + If you'd like to make sure that the Webhook request comes from Telegram, + we recommend using a secret path in the URL, e.g. + `https://www.example.com/`. + Since nobody else knows your bot's token, you can be pretty sure it's us. Source: https://core.telegram.org/bots/api#setwebhook - :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration + :param url: HTTPS url to send updates to. Use an empty string to remove + webhook integration :type url: :obj:`base.String` - :param certificate: Upload your public key certificate so that the root certificate in use can be checked - :type certificate: :obj:`typing.Union[base.InputFile, None]` - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook - for update delivery, 1-100. - :type max_connections: :obj:`typing.Union[base.Integer, None]` - :param allowed_updates: List the types of updates you want your bot to receive - :type allowed_updates: :obj:`typing.Union[typing.List[base.String], None]` + + :param certificate: Upload your public key certificate so that the root + certificate in use can be checked. See our self-signed guide for details: + https://core.telegram.org/bots/self-signed + :type certificate: :obj:`typing.Optional[base.InputFile]` + + :param ip_address: The fixed IP address which will be used to send webhook + requests instead of the IP address resolved through DNS + :type ip_address: :obj:`typing.Optional[base.String]` + + :param max_connections: Maximum allowed number of simultaneous HTTPS + connections to the webhook for update delivery, 1-100. Defaults to 40. + Use lower values to limit the load on your bot's server, and higher + values to increase your bot's throughput. + :type max_connections: :obj:`typing.Optional[base.Integer]` + + :param allowed_updates: A list of the update types you want your bot to + receive. For example, specify [“message”, “edited_channel_post”, + “callback_query”] to only receive updates of these types. See Update for + a complete list of available update types. Specify an empty list to + receive all updates regardless of type (default). If not specified, the + previous setting will be used. + Please note that this parameter doesn't affect updates created before the + call to the setWebhook, so unwanted updates may be received for a short + period of time. + :type allowed_updates: :obj:`typing.Optional[typing.List[base.String]]` + :return: Returns true :rtype: :obj:`base.Boolean` """ From 6880c65c73e70bf3e6fecd6c7e4d4dcd8e42acd5 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 14:51:34 +0300 Subject: [PATCH 09/39] updated deprecation text Co-authored-by: Martin Winks --- aiogram/bot/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiogram/bot/base.py b/aiogram/bot/base.py index ee2e1af8..71e4effc 100644 --- a/aiogram/bot/base.py +++ b/aiogram/bot/base.py @@ -174,7 +174,7 @@ class BaseBot: finally: self._ctx_token.reset(token) - @deprecated("This method behaviour will be changed in aiogram v3.0. " + @deprecated("This method's behavior will be changed in aiogram v3.0. " "More info: https://core.telegram.org/bots/api#close") async def close(self): """ From bb156408cf23bb8a47e72ca925d6142123aae631 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 15:20:12 +0300 Subject: [PATCH 10/39] AIOG-T-69 param `drop_pending_updates` added in methods `setWebhook` and `deleteWebhook` --- aiogram/bot/bot.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 66a4fb48..11409fcc 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -96,11 +96,14 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_UPDATES, payload) return [types.Update(**update) for update in result] - async def set_webhook(self, url: base.String, + async def set_webhook(self, + url: base.String, certificate: typing.Optional[base.InputFile] = None, ip_address: typing.Optional[base.String] = None, max_connections: typing.Optional[base.Integer] = None, - allowed_updates: typing.Optional[typing.List[base.String]] = None) -> base.Boolean: + allowed_updates: typing.Optional[typing.List[base.String]] = None, + drop_pending_updates: typing.Optional[base.Boolean] = None, + ) -> base.Boolean: """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST @@ -145,6 +148,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): period of time. :type allowed_updates: :obj:`typing.Optional[typing.List[base.String]]` + :param drop_pending_updates: Pass True to drop all pending updates + :type drop_pending_updates: :obj:`typing.Optional[base.Boolean]` + :return: Returns true :rtype: :obj:`base.Boolean` """ @@ -157,13 +163,18 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SET_WEBHOOK, payload, files) return result - async def delete_webhook(self) -> base.Boolean: + async def delete_webhook(self, + drop_pending_updates: typing.Optional[base.Boolean] = None, + ) -> base.Boolean: """ - Use this method to remove webhook integration if you decide to switch back to getUpdates. - Returns True on success. Requires no parameters. + Use this method to remove webhook integration if you decide to switch back + to getUpdates. Returns True on success. Source: https://core.telegram.org/bots/api#deletewebhook + :param drop_pending_updates: Pass True to drop all pending updates + :type drop_pending_updates: :obj:`typing.Optional[base.Boolean]` + :return: Returns True on success :rtype: :obj:`base.Boolean` """ From a3c481760b5960ae2d5be1d385f923af0bd907bf Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 16:00:27 +0300 Subject: [PATCH 11/39] AIOG-T-71 new `ChatLocation` class --- aiogram/types/chat_location.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 aiogram/types/chat_location.py diff --git a/aiogram/types/chat_location.py b/aiogram/types/chat_location.py new file mode 100644 index 00000000..0438c544 --- /dev/null +++ b/aiogram/types/chat_location.py @@ -0,0 +1,16 @@ +from . import base +from . import fields +from .location import Location + + +class ChatLocation(base.TelegramObject): + """ + Represents a location to which a chat is connected. + + https://core.telegram.org/bots/api#chatlocation + """ + location: Location = fields.Field() + address: base.String = fields.Field() + + def __init__(self, location: Location, address: base.String): + super().__init__(location=location, address=address) From b2b2c981802b87e938c7dafc8a3c840d28d49a4d Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 16:01:07 +0300 Subject: [PATCH 12/39] AIOG-T-70 updated `Chat` class: bio, linked chats, location --- aiogram/types/chat.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index 28cc5ed0..5eef7717 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -4,12 +4,13 @@ import asyncio import datetime import typing -from ..utils import helper, markdown from . import base, fields +from .chat_location import ChatLocation from .chat_member import ChatMember from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from .input_file import InputFile +from ..utils import helper, markdown from ..utils.deprecated import deprecated @@ -27,6 +28,7 @@ class Chat(base.TelegramObject): last_name: base.String = fields.Field() all_members_are_administrators: base.Boolean = fields.Field() photo: ChatPhoto = fields.Field(base=ChatPhoto) + bio: typing.Optional[base.String] = fields.Field() description: base.String = fields.Field() invite_link: base.String = fields.Field() pinned_message: 'Message' = fields.Field(base='Message') @@ -34,6 +36,8 @@ class Chat(base.TelegramObject): slow_mode_delay: base.Integer = fields.Field() sticker_set_name: base.String = fields.Field() can_set_sticker_set: base.Boolean = fields.Field() + linked_chat_id: typing.Optional[base.Integer] = fields.Field() + location: typing.Optional[ChatLocation] = fields.Field() def __hash__(self): return self.id @@ -182,7 +186,8 @@ class Chat(base.TelegramObject): return await self.bot.set_chat_description(self.id, description) async def kick(self, user_id: base.Integer, - until_date: typing.Union[base.Integer, datetime.datetime, datetime.timedelta, None] = None) -> base.Boolean: + until_date: typing.Union[ + base.Integer, datetime.datetime, datetime.timedelta, None] = None) -> base.Boolean: """ Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the group @@ -338,7 +343,8 @@ class Chat(base.TelegramObject): :param custom_title: New custom title for the administrator; 0-16 characters, emoji are not allowed :return: True on success. """ - return await self.bot.set_chat_administrator_custom_title(chat_id=self.id, user_id=user_id, custom_title=custom_title) + return await self.bot.set_chat_administrator_custom_title(chat_id=self.id, user_id=user_id, + custom_title=custom_title) async def pin_message(self, message_id: base.Integer, disable_notification: base.Boolean = False) -> base.Boolean: """ From 418d0ddff6585477d2a81466e97453f44e71adae Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 16:53:31 +0300 Subject: [PATCH 13/39] AIOG-T-68 field `ip_address` added to class `WebhookInfo` --- aiogram/types/webhook_info.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aiogram/types/webhook_info.py b/aiogram/types/webhook_info.py index 995d3aaa..dc1a7cd9 100644 --- a/aiogram/types/webhook_info.py +++ b/aiogram/types/webhook_info.py @@ -13,6 +13,7 @@ class WebhookInfo(base.TelegramObject): url: base.String = fields.Field() has_custom_certificate: base.Boolean = fields.Field() pending_update_count: base.Integer = fields.Field() + ip_address: base.String = fields.Field() last_error_date: base.Integer = fields.Field() last_error_message: base.String = fields.Field() max_connections: base.Integer = fields.Field() From 5ae56f176e6f7a21cfd94689ed09cf481ac14801 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 17:18:46 +0300 Subject: [PATCH 14/39] AIOG-T-72 param `only_if_banned` added to `unbanChatMember` method --- aiogram/bot/bot.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 11409fcc..4ea34f8d 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1147,20 +1147,32 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.KICK_CHAT_MEMBER, payload) return result - async def unban_chat_member(self, chat_id: typing.Union[base.Integer, base.String], - user_id: base.Integer) -> base.Boolean: + async def unban_chat_member(self, + chat_id: typing.Union[base.Integer, base.String], + user_id: base.Integer, + only_if_banned: typing.Optional[base.Boolean] = None, + ) -> base.Boolean: """ - Use this method to unban a previously kicked user in a supergroup or channel. ` - The user will not return to the group or channel automatically, but will be able to join via link, etc. - - The bot must be an administrator for this to work. + Use this method to unban a previously kicked user in a supergroup or channel. + The user will not return to the group or channel automatically, but will be + able to join via link, etc. The bot must be an administrator for this to + work. By default, this method guarantees that after the call the user is not + a member of the chat, but will be able to join it. So if the user is a member + of the chat they will also be removed from the chat. If you don't want this, + use the parameter only_if_banned. Returns True on success. Source: https://core.telegram.org/bots/api#unbanchatmember - :param chat_id: Unique identifier for the target group or username of the target supergroup or channel + :param chat_id: Unique identifier for the target group or username of the + target supergroup or channel (in the format @username) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param user_id: Unique identifier of the target user :type user_id: :obj:`base.Integer` + + :param only_if_banned: Do nothing if the user is not banned + :type only_if_banned: :obj:`typing.Optional[base.Boolean]` + :return: Returns True on success :rtype: :obj:`base.Boolean` """ From 0e334e6abc03f4c3bd70d970046f7f30f40c7647 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 17:19:34 +0300 Subject: [PATCH 15/39] AIOG-T-72 updated Chat.unban shortcut --- aiogram/types/chat.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index 5eef7717..30135a44 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -210,21 +210,35 @@ class Chat(base.TelegramObject): """ return await self.bot.kick_chat_member(self.id, user_id=user_id, until_date=until_date) - async def unban(self, user_id: base.Integer) -> base.Boolean: + async def unban(self, + user_id: base.Integer, + only_if_banned: typing.Optional[base.Boolean] = None, + ) -> base.Boolean: """ - Use this method to unban a previously kicked user in a supergroup or channel. ` - The user will not return to the group or channel automatically, but will be able to join via link, etc. - - The bot must be an administrator for this to work. + Use this method to unban a previously kicked user in a supergroup or channel. + The user will not return to the group or channel automatically, but will be + able to join via link, etc. The bot must be an administrator for this to + work. By default, this method guarantees that after the call the user is not + a member of the chat, but will be able to join it. So if the user is a member + of the chat they will also be removed from the chat. If you don't want this, + use the parameter only_if_banned. Returns True on success. Source: https://core.telegram.org/bots/api#unbanchatmember :param user_id: Unique identifier of the target user :type user_id: :obj:`base.Integer` + + :param only_if_banned: Do nothing if the user is not banned + :type only_if_banned: :obj:`typing.Optional[base.Boolean]` + :return: Returns True on success. :rtype: :obj:`base.Boolean` """ - return await self.bot.unban_chat_member(self.id, user_id=user_id) + return await self.bot.unban_chat_member( + chat_id=self.id, + user_id=user_id, + only_if_banned=only_if_banned, + ) async def restrict(self, user_id: base.Integer, permissions: typing.Optional[ChatPermissions] = None, From 9581870a53c671ac5a1261a91994053d7cea06c1 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 17:36:12 +0300 Subject: [PATCH 16/39] AIOG-T-73 field `file_name` added to `Audio` and `Video` classes --- aiogram/types/audio.py | 1 + aiogram/types/video.py | 1 + 2 files changed, 2 insertions(+) diff --git a/aiogram/types/audio.py b/aiogram/types/audio.py index 6859668f..1657c9cc 100644 --- a/aiogram/types/audio.py +++ b/aiogram/types/audio.py @@ -15,6 +15,7 @@ class Audio(base.TelegramObject, mixins.Downloadable): duration: base.Integer = fields.Field() performer: base.String = fields.Field() title: base.String = fields.Field() + file_name: base.String = fields.Field() mime_type: base.String = fields.Field() file_size: base.Integer = fields.Field() thumb: PhotoSize = fields.Field(base=PhotoSize) diff --git a/aiogram/types/video.py b/aiogram/types/video.py index 97dbb90f..d4958761 100644 --- a/aiogram/types/video.py +++ b/aiogram/types/video.py @@ -16,5 +16,6 @@ class Video(base.TelegramObject, mixins.Downloadable): height: base.Integer = fields.Field() duration: base.Integer = fields.Field() thumb: PhotoSize = fields.Field(base=PhotoSize) + file_name: base.String = fields.Field() mime_type: base.String = fields.Field() file_size: base.Integer = fields.Field() From 0d19644616b45cff1010650cb0a09006ae8fcefc Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 18:01:34 +0300 Subject: [PATCH 17/39] AIOG-T-74 param `disable_content_type_detection` added in `sendDocument` method and `InputMediaDocument` class --- aiogram/bot/bot.py | 72 +++++++++++++++-------- aiogram/types/input_media.py | 25 ++++++-- aiogram/types/message.py | 110 +++++++++++++++++++++++------------ 3 files changed, 138 insertions(+), 69 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 4ea34f8d..f2c18cca 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -431,40 +431,62 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): async def send_document(self, chat_id: typing.Union[base.Integer, base.String], document: typing.Union[base.InputFile, base.String], thumb: typing.Union[base.InputFile, base.String, None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + disable_content_type_detection: typing.Optional[base.Boolean] = None, + disable_notification: typing.Optional[base.Boolean] = None, + reply_to_message_id: typing.Optional[base.Integer] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None) -> types.Message: """ - Use this method to send general files. - - Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. + Use this method to send general files. On success, the sent Message is + returned. Bots can currently send files of any type of up to 50 MB in size, + this limit may be changed in the future. Source: https://core.telegram.org/bots/api#senddocument - :param chat_id: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the + target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param document: File to send :type document: :obj:`typing.Union[base.InputFile, base.String]` + :param thumb: Thumbnail of the file sent :type thumb: :obj:`typing.Union[base.InputFile, base.String, None]` - :param caption: Document caption (may also be used when resending documents by file_id), 0-1024 characters - :type caption: :obj:`typing.Union[base.String, None]` - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, - fixed-width text or inline URLs in your bot's message. - :type parse_mode: :obj:`typing.Union[base.String, None]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` - :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, types.ForceReply], None]` + + :param caption: Document caption (may also be used when resending documents + by file_id), 0-1024 characters + :type caption: :obj:`typing.Optional[base.String]` + + :param disable_content_type_detection: Disables automatic server-side content + type detection for files uploaded using multipart/form-data + :type disable_content_type_detection: :obj:`typing.Optional[base.Boolean]` + + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show + bold, italic, fixed-width text or inline URLs in your bot's message. + :type parse_mode: :obj:`typing.Optional[base.String]` + + :param disable_notification: Sends the message silently. Users will receive a + notification with no sound + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :param reply_to_message_id: If the message is a reply, ID of the original + message + :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + + :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:`typing.Union[types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply], + None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ diff --git a/aiogram/types/input_media.py b/aiogram/types/input_media.py index 25422df1..254e3840 100644 --- a/aiogram/types/input_media.py +++ b/aiogram/types/input_media.py @@ -118,16 +118,29 @@ class InputMediaAnimation(InputMedia): class InputMediaDocument(InputMedia): """ - Represents a photo to be sent. + Represents a general file to be sent. https://core.telegram.org/bots/api#inputmediadocument """ - def __init__(self, media: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, parse_mode: base.String = None, **kwargs): - super(InputMediaDocument, self).__init__(type='document', media=media, thumb=thumb, - caption=caption, parse_mode=parse_mode, - conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String, None] = None, + caption: base.String = None, + parse_mode: base.String = None, + disable_content_type_detection: typing.Optional[base.Boolean] = None, + **kwargs, + ): + super().__init__( + type='document', + media=media, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + disable_content_type_detection=disable_content_type_detection, + conf=kwargs, + ) class InputMediaAudio(InputMedia): diff --git a/aiogram/types/message.py b/aiogram/types/message.py index fc6bc77b..c69455c2 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -513,6 +513,7 @@ class Message(base.TelegramObject): thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + disable_content_type_detection: typing.Optional[base.Boolean] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_markup: typing.Union[ InlineKeyboardMarkup, @@ -524,30 +525,45 @@ class Message(base.TelegramObject): reply: base.Boolean = False, ) -> Message: """ - Use this method to send general files. - - Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. + Use this method to send general files. On success, the sent Message is + returned. Bots can currently send files of any type of up to 50 MB in size, + this limit may be changed in the future. Source: https://core.telegram.org/bots/api#senddocument - :param document: File to send. + :param document: File to send :type document: :obj:`typing.Union[base.InputFile, base.String]` - :param thumb: Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail‘s width and height should not exceed 320. - :type thumb: :obj:`typing.Union[typing.Union[base.InputFile, base.String], None]` - :param caption: Document caption (may also be used when resending documents by file_id), 0-200 characters - :type caption: :obj:`typing.Union[base.String, None]` - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, - fixed-width text or inline URLs in the media caption - :type parse_mode: :obj:`typing.Union[base.String, None]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` - :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 thumb: Thumbnail of the file sent + :type thumb: :obj:`typing.Union[base.InputFile, base.String, None]` + + :param caption: Document caption (may also be used when resending documents + by file_id), 0-1024 characters + :type caption: :obj:`typing.Optional[base.String]` + + :param disable_content_type_detection: Disables automatic server-side content + type detection for files uploaded using multipart/form-data + :type disable_content_type_detection: :obj:`typing.Optional[base.Boolean]` + + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show + bold, italic, fixed-width text or inline URLs in your bot's message. + :type parse_mode: :obj:`typing.Optional[base.String]` + + :param disable_notification: Sends the message silently. Users will receive a + notification with no sound + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :param reply: True if the message is a reply + :type reply: :obj:`typing.Optional[base.Boolean]` + + :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:`typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply], None]` - :param reply: fill 'reply_to_message_id' - :return: On success, the sent Message is returned. + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply], + None]` + + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ return await self.bot.send_document( @@ -556,6 +572,7 @@ class Message(base.TelegramObject): document=document, caption=caption, parse_mode=parse_mode, + disable_content_type_detection=disable_content_type_detection, disable_notification=disable_notification, reply_to_message_id=self.message_id if reply else None, reply_markup=reply_markup, @@ -1299,6 +1316,7 @@ class Message(base.TelegramObject): thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + disable_content_type_detection: typing.Optional[base.Boolean] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_markup: typing.Union[ InlineKeyboardMarkup, @@ -1310,30 +1328,45 @@ class Message(base.TelegramObject): reply: base.Boolean = True, ) -> Message: """ - Use this method to send general files. - - Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. + Use this method to send general files. On success, the sent Message is + returned. Bots can currently send files of any type of up to 50 MB in size, + this limit may be changed in the future. Source: https://core.telegram.org/bots/api#senddocument - :param document: File to send. + :param document: File to send :type document: :obj:`typing.Union[base.InputFile, base.String]` - :param thumb: Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail‘s width and height should not exceed 320. - :type thumb: :obj:`typing.Union[typing.Union[base.InputFile, base.String], None]` - :param caption: Document caption (may also be used when resending documents by file_id), 0-200 characters - :type caption: :obj:`typing.Union[base.String, None]` - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, - fixed-width text or inline URLs in the media caption - :type parse_mode: :obj:`typing.Union[base.String, None]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` - :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 thumb: Thumbnail of the file sent + :type thumb: :obj:`typing.Union[base.InputFile, base.String, None]` + + :param caption: Document caption (may also be used when resending documents + by file_id), 0-1024 characters + :type caption: :obj:`typing.Optional[base.String]` + + :param disable_content_type_detection: Disables automatic server-side content + type detection for files uploaded using multipart/form-data + :type disable_content_type_detection: :obj:`typing.Optional[base.Boolean]` + + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show + bold, italic, fixed-width text or inline URLs in your bot's message. + :type parse_mode: :obj:`typing.Optional[base.String]` + + :param disable_notification: Sends the message silently. Users will receive a + notification with no sound + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :param reply: True if the message is a reply + :type reply: :obj:`typing.Optional[base.Boolean]` + + :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:`typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply], None]` - :param reply: fill 'reply_to_message_id' - :return: On success, the sent Message is returned. + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply], + None]` + + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ return await self.bot.send_document( @@ -1342,6 +1375,7 @@ class Message(base.TelegramObject): thumb=thumb, caption=caption, parse_mode=parse_mode, + disable_content_type_detection=disable_content_type_detection, disable_notification=disable_notification, reply_to_message_id=self.message_id if reply else None, reply_markup=reply_markup, From d92ace680534c9433994e0e7bfd4028cd855109f Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 18:20:36 +0300 Subject: [PATCH 18/39] AIOG-T-75 Added the ability to pin messages in private chats (docs update) --- aiogram/bot/bot.py | 26 ++++++++++++++++++-------- aiogram/types/chat.py | 22 +++++++++++++++------- aiogram/types/message.py | 16 ++++++++++------ 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index f2c18cca..d1d9b826 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1450,21 +1450,31 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SET_CHAT_DESCRIPTION, payload) return result - async def pin_chat_message(self, chat_id: typing.Union[base.Integer, base.String], message_id: base.Integer, - disable_notification: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def pin_chat_message(self, + chat_id: typing.Union[base.Integer, base.String], + message_id: base.Integer, + disable_notification: typing.Optional[base.Boolean] = None, + ) -> base.Boolean: """ - Use this method to pin a message in a supergroup. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to add a message to the list of pinned messages in a chat. + If the chat is not a private chat, the bot must be an administrator in the + chat for this to work and must have the 'can_pin_messages' admin right in a + supergroup or 'can_edit_messages' admin right in a channel. Returns True on + success. Source: https://core.telegram.org/bots/api#pinchatmessage - :param chat_id: Unique identifier for the target chat or username of the target supergroup + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param message_id: Identifier of a message to pin :type message_id: :obj:`base.Integer` - :param disable_notification: Pass True, if it is not necessary to send a notification to - all group members about the new pinned message - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + + :param disable_notification: Pass True, if it is not necessary to send a + notification to all group members about the new pinned message + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + :return: Returns True on success :rtype: :obj:`base.Boolean` """ diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index 30135a44..6d2b6590 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -360,19 +360,27 @@ class Chat(base.TelegramObject): return await self.bot.set_chat_administrator_custom_title(chat_id=self.id, user_id=user_id, custom_title=custom_title) - async def pin_message(self, message_id: base.Integer, disable_notification: base.Boolean = False) -> base.Boolean: + async def pin_message(self, + message_id: base.Integer, + disable_notification: typing.Optional[base.Boolean] = False, + ) -> base.Boolean: """ - Use this method to pin a message in a supergroup. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to add a message to the list of pinned messages in a chat. + If the chat is not a private chat, the bot must be an administrator in the + chat for this to work and must have the 'can_pin_messages' admin right in a + supergroup or 'can_edit_messages' admin right in a channel. Returns True on + success. Source: https://core.telegram.org/bots/api#pinchatmessage :param message_id: Identifier of a message to pin :type message_id: :obj:`base.Integer` - :param disable_notification: Pass True, if it is not necessary to send a notification to - all group members about the new pinned message - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` - :return: Returns True on success. + + :param disable_notification: Pass True, if it is not necessary to send a + notification to all group members about the new pinned message + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :return: Returns True on success :rtype: :obj:`base.Boolean` """ return await self.bot.pin_chat_message(self.id, message_id, disable_notification) diff --git a/aiogram/types/message.py b/aiogram/types/message.py index c69455c2..3b0c4698 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -2098,17 +2098,21 @@ class Message(base.TelegramObject): return await self.bot.delete_message(self.chat.id, self.message_id) async def pin( - self, disable_notification: typing.Union[base.Boolean, None] = None + self, disable_notification: typing.Optional[base.Boolean] = None, ) -> base.Boolean: """ - Use this method to pin a message in a supergroup. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to add a message to the list of pinned messages in a chat. + If the chat is not a private chat, the bot must be an administrator in the + chat for this to work and must have the 'can_pin_messages' admin right in a + supergroup or 'can_edit_messages' admin right in a channel. Returns True on + success. Source: https://core.telegram.org/bots/api#pinchatmessage - :param disable_notification: Pass True, if it is not necessary to send a notification to - all group members about the new pinned message - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param disable_notification: Pass True, if it is not necessary to send a + notification to all group members about the new pinned message + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + :return: Returns True on success :rtype: :obj:`base.Boolean` """ From aef6fd2c524151aa9024286ca3a08c43fb6ab4cc Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 18:37:20 +0300 Subject: [PATCH 19/39] AIOG-T-76 Added the parameter message_id to the method unpinChatMessage to allow unpinning of the specific pinned message --- aiogram/bot/bot.py | 20 ++++++++++++++++---- aiogram/types/chat.py | 22 +++++++++++++++++----- aiogram/types/message.py | 17 +++++++++++++++++ 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index d1d9b826..fda35703 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1483,15 +1483,27 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.PIN_CHAT_MESSAGE, payload) return result - async def unpin_chat_message(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Boolean: + async def unpin_chat_message(self, + chat_id: typing.Union[base.Integer, base.String], + message_id: typing.Optional[base.Integer] = None, + ) -> base.Boolean: """ - Use this method to unpin a message in a supergroup chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to remove a message from the list of pinned messages in a + chat. If the chat is not a private chat, the bot must be an administrator in + the chat for this to work and must have the 'can_pin_messages' admin right in + a supergroup or 'can_edit_messages' admin right in a channel. Returns True on + success. Source: https://core.telegram.org/bots/api#unpinchatmessage - :param chat_id: Unique identifier for the target chat or username of the target supergroup + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + + :param message_id: Identifier of a message to unpin. If not specified, the + most recent pinned message (by sending date) will be unpinned. + :type message_id: :obj:`typing.Optional[base.Integer]` + :return: Returns True on success :rtype: :obj:`base.Boolean` """ diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index 6d2b6590..0380fbed 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -385,17 +385,29 @@ class Chat(base.TelegramObject): """ return await self.bot.pin_chat_message(self.id, message_id, disable_notification) - async def unpin_message(self) -> base.Boolean: + async def unpin_message(self, + message_id: typing.Optional[base.Integer] = None, + ) -> base.Boolean: """ - Use this method to unpin a message in a supergroup chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to remove a message from the list of pinned messages in a + chat. If the chat is not a private chat, the bot must be an administrator in + the chat for this to work and must have the 'can_pin_messages' admin right in + a supergroup or 'can_edit_messages' admin right in a channel. Returns True on + success. Source: https://core.telegram.org/bots/api#unpinchatmessage - :return: Returns True on success. + :param message_id: Identifier of a message to unpin. If not specified, the + most recent pinned message (by sending date) will be unpinned. + :type message_id: :obj:`typing.Optional[base.Integer]` + + :return: Returns True on success :rtype: :obj:`base.Boolean` """ - return await self.bot.unpin_chat_message(self.id) + return await self.bot.unpin_chat_message( + chat_id=self.id, + message_id=message_id, + ) async def leave(self) -> base.Boolean: """ diff --git a/aiogram/types/message.py b/aiogram/types/message.py index 3b0c4698..aadb56a1 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -2118,6 +2118,23 @@ class Message(base.TelegramObject): """ return await self.chat.pin_message(self.message_id, disable_notification) + async def unpin(self) -> base.Boolean: + """ + Use this method to remove a message from the list of pinned messages in a + chat. If the chat is not a private chat, the bot must be an administrator in + the chat for this to work and must have the 'can_pin_messages' admin right in + a supergroup or 'can_edit_messages' admin right in a channel. Returns True on + success. + + Source: https://core.telegram.org/bots/api#unpinchatmessage + + :return: Returns True on success + :rtype: :obj:`base.Boolean` + """ + return await self.chat.unpin_message( + message_id=self.message_id, + ) + async def send_copy( self: Message, chat_id: typing.Union[str, int], From 3328979f28732dfe1d92b677354bd588f267b77b Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 18:45:10 +0300 Subject: [PATCH 20/39] AIOG-T-77 Added the method unpinAllChatMessages, which can be used to unpin all pinned messages in a chat. --- aiogram/bot/api.py | 1 + aiogram/bot/bot.py | 23 +++++++++++++++++++++++ aiogram/types/chat.py | 16 ++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index a4379334..ba4bd2b1 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -200,6 +200,7 @@ class Methods(Helper): SET_CHAT_DESCRIPTION = Item() # setChatDescription PIN_CHAT_MESSAGE = Item() # pinChatMessage UNPIN_CHAT_MESSAGE = Item() # unpinChatMessage + UNPIN_ALL_CHAT_MESSAGES = Item() # unpinAllChatMessages LEAVE_CHAT = Item() # leaveChat GET_CHAT = Item() # getChat GET_CHAT_ADMINISTRATORS = Item() # getChatAdministrators diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index fda35703..9ccf0c5f 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1512,6 +1512,29 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.UNPIN_CHAT_MESSAGE, payload) return result + async def unpin_all_chat_messages(self, + chat_id: typing.Union[base.Integer, base.String], + ) -> base.Boolean: + """ + Use this method to clear the list of pinned messages in a chat. If the chat + is not a private chat, the bot must be an administrator in the chat for this + to work and must have the 'can_pin_messages' admin right in a supergroup or + 'can_edit_messages' admin right in a channel. Returns True on success. + + Source: https://core.telegram.org/bots/api#unpinallchatmessages + + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) + :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + + :return: Returns True on success + :rtype: :obj:`base.Boolean` + """ + payload = generate_payload(**locals()) + + result = await self.request(api.Methods.UNPIN_ALL_CHAT_MESSAGES, payload) + return result + async def leave_chat(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Boolean: """ Use this method for your bot to leave a group, supergroup or channel. diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index 0380fbed..901e2052 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -409,6 +409,22 @@ class Chat(base.TelegramObject): message_id=message_id, ) + async def unpin_all_messages(self): + """ + Use this method to clear the list of pinned messages in a chat. If the chat + is not a private chat, the bot must be an administrator in the chat for this + to work and must have the 'can_pin_messages' admin right in a supergroup or + 'can_edit_messages' admin right in a channel. Returns True on success. + + Source: https://core.telegram.org/bots/api#unpinallchatmessages + + :return: Returns True on success + :rtype: :obj:`base.Boolean` + """ + return await self.bot.unpin_all_chat_messages( + chat_id=self.id, + ) + async def leave(self) -> base.Boolean: """ Use this method for your bot to leave a group, supergroup or channel. From 1a00fec59f425b10416849f93e12d0ca03a7e403 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 19:00:55 +0300 Subject: [PATCH 21/39] AIOG-T-78 updated send_media_group description; added media qty check --- aiogram/bot/bot.py | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 9ccf0c5f..71fd1cab 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -8,6 +8,7 @@ from .base import BaseBot, api from .. import types from ..types import base from ..utils.deprecated import deprecated +from ..utils.exceptions import ValidationError from ..utils.mixins import DataMixin, ContextInstanceMixin from ..utils.payload import generate_payload, prepare_arg, prepare_attachment, prepare_file @@ -733,24 +734,36 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_VIDEO_NOTE, payload, files) return types.Message(**result) - async def send_media_group(self, chat_id: typing.Union[base.Integer, base.String], + async def send_media_group(self, + chat_id: typing.Union[base.Integer, base.String], media: typing.Union[types.MediaGroup, typing.List], - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, - None] = None) -> typing.List[types.Message]: + disable_notification: typing.Optional[base.Boolean] = None, + reply_to_message_id: typing.Optional[base.Integer] = None, + ) -> typing.List[types.Message]: """ - Use this method to send a group of photos or videos as an album. + Use this method to send a group of photos, videos, documents or audios as + an album. Documents and audio files can be only group in an album with + messages of the same type. On success, an array of Messages that were sent + is returned. Source: https://core.telegram.org/bots/api#sendmediagroup - :param chat_id: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` - :param media: A JSON-serialized array describing photos and videos to be sent + + :param media: A JSON-serialized array describing messages to be sent, must + include 2-10 items :type media: :obj:`typing.Union[types.MediaGroup, typing.List]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + + :param disable_notification: Sends messages silently. Users will receive a + notification with no sound. + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :param reply_to_message_id: If the messages are a reply, ID of the original + message + :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :return: On success, an array of the sent Messages is returned :rtype: typing.List[types.Message] """ @@ -758,6 +771,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): if isinstance(media, list): media = types.MediaGroup(media) + # check MediaGroup quantity + if 2 > len(media.media) > 10: + raise ValidationError("Media group must include 2-10 items") + files = dict(media.get_files()) media = prepare_arg(media) From c561710ac2c611953a24099472e97763d57a8c84 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 19:48:12 +0300 Subject: [PATCH 22/39] AIOG-T-80 field `live_period` added to `Location` class --- aiogram/types/location.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aiogram/types/location.py b/aiogram/types/location.py index ea2f81c4..33d6a545 100644 --- a/aiogram/types/location.py +++ b/aiogram/types/location.py @@ -1,3 +1,5 @@ +import typing + from . import base from . import fields @@ -10,3 +12,4 @@ class Location(base.TelegramObject): """ longitude: base.Float = fields.Field() latitude: base.Float = fields.Field() + live_period: typing.Optional[base.Integer] = fields.Field() From d54348386aa9b82de3bd351a2cba9749ce920a71 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:16:34 +0300 Subject: [PATCH 23/39] AIOG-T-81 Added support for live location heading --- aiogram/bot/bot.py | 53 +++++++++++++++++++------- aiogram/types/inline_query_result.py | 27 ++++++++----- aiogram/types/input_message_content.py | 15 +++++--- aiogram/types/location.py | 1 + 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 71fd1cab..98f044ae 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -785,9 +785,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): async def send_location(self, chat_id: typing.Union[base.Integer, base.String], latitude: base.Float, longitude: base.Float, - live_period: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, + live_period: typing.Optional[base.Integer] = None, + heading: typing.Optional[base.Integer] = None, + disable_notification: typing.Optional[base.Boolean] = None, + reply_to_message_id: typing.Optional[base.Integer] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -799,20 +800,31 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param latitude: Latitude of the location :type latitude: :obj:`base.Float` + :param longitude: Longitude of the location :type longitude: :obj:`base.Float` + :param live_period: Period in seconds for which the location will be updated - :type live_period: :obj:`typing.Union[base.Integer, None]` + :type live_period: :obj:`typing.Optional[base.Integer]` + + :param heading: For live locations, a direction in which the user is moving, + in degrees. Must be between 1 and 360 if specified. + :type heading: :obj:`typing.Optional[base.Integer]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -822,12 +834,15 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_LOCATION, payload) return types.Message(**result) - async def edit_message_live_location(self, latitude: base.Float, longitude: base.Float, + async def edit_message_live_location(self, + latitude: base.Float, + longitude: base.Float, chat_id: typing.Union[base.Integer, base.String, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + message_id: typing.Optional[base.Integer] = None, + inline_message_id: typing.Optional[base.String] = None, + heading: typing.Optional[base.Integer] = None, + reply_markup: typing.Optional[types.InlineKeyboardMarkup] = None, + ) -> types.Message or base.Boolean: """ Use this method to edit live location messages sent by the bot or via the bot (for inline bots). A location can be edited until its live_period expires or editing is explicitly disabled by a call @@ -837,16 +852,26 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Required if inline_message_id is not specified :type chat_id: :obj:`typing.Union[base.Integer, base.String, None]` + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message - :type message_id: :obj:`typing.Union[base.Integer, None]` + :type message_id: :obj:`typing.Optional[base.Integer]` + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message - :type inline_message_id: :obj:`typing.Union[base.String, None]` + :type inline_message_id: :obj:`typing.Optional[base.String]` + :param latitude: Latitude of new location :type latitude: :obj:`base.Float` + :param longitude: Longitude of new location :type longitude: :obj:`base.Float` + + :param heading: Direction in which the user is moving, in degrees. Must be + between 1 and 360 if specified. + :type heading: :obj:`typing.Optional[base.Integer]` + :param reply_markup: A JSON-serialized object for a new inline keyboard - :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, None]` + :type reply_markup: :obj:`typing.Optional[types.InlineKeyboardMarkup]` + :return: On success, if the edited message was sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index fccaa2a1..5799772a 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -352,9 +352,6 @@ class InlineQueryResultLocation(InlineQueryResult): Alternatively, you can use input_message_content to send a message with the specified content instead of the location. - Note: This will only work in Telegram versions released after 9 April, 2016. - Older clients will ignore them. - https://core.telegram.org/bots/api#inlinequeryresultlocation """ type: base.String = fields.Field(alias='type', default='location') @@ -362,6 +359,7 @@ class InlineQueryResultLocation(InlineQueryResult): longitude: base.Float = fields.Field() title: base.String = fields.Field() live_period: base.Integer = fields.Field() + heading: typing.Optional[base.Integer] = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) thumb_url: base.String = fields.Field() thumb_width: base.Integer = fields.Field() @@ -373,17 +371,26 @@ class InlineQueryResultLocation(InlineQueryResult): longitude: base.Float, title: base.String, live_period: typing.Optional[base.Integer] = None, + heading: typing.Optional[base.Integer] = None, reply_markup: typing.Optional[InlineKeyboardMarkup] = None, input_message_content: typing.Optional[InputMessageContent] = None, thumb_url: typing.Optional[base.String] = None, thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None): - super(InlineQueryResultLocation, self).__init__(id=id, latitude=latitude, longitude=longitude, - title=title, live_period=live_period, - reply_markup=reply_markup, - input_message_content=input_message_content, - thumb_url=thumb_url, thumb_width=thumb_width, - thumb_height=thumb_height) + thumb_height: typing.Optional[base.Integer] = None, + ): + super().__init__( + id=id, + latitude=latitude, + longitude=longitude, + title=title, + live_period=live_period, + heading=heading, + reply_markup=reply_markup, + input_message_content=input_message_content, + thumb_url=thumb_url, + thumb_width=thumb_width, + thumb_height=thumb_height + ) class InlineQueryResultVenue(InlineQueryResult): diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 736a4454..21a9479d 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -40,17 +40,20 @@ class InputLocationMessageContent(InputMessageContent): """ Represents the content of a location message to be sent as the result of an inline query. - Note: This will only work in Telegram versions released after 9 April, 2016. - Older clients will ignore them. - https://core.telegram.org/bots/api#inputlocationmessagecontent """ latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() + heading: typing.Optional[base.Integer] = fields.Field() - def __init__(self, latitude: base.Float, - longitude: base.Float): - super(InputLocationMessageContent, self).__init__(latitude=latitude, longitude=longitude) + def __init__(self, + latitude: base.Float, + longitude: base.Float, + ): + super().__init__( + latitude=latitude, + longitude=longitude, + ) class InputTextMessageContent(InputMessageContent): diff --git a/aiogram/types/location.py b/aiogram/types/location.py index 33d6a545..c8951b8a 100644 --- a/aiogram/types/location.py +++ b/aiogram/types/location.py @@ -13,3 +13,4 @@ class Location(base.TelegramObject): longitude: base.Float = fields.Field() latitude: base.Float = fields.Field() live_period: typing.Optional[base.Integer] = fields.Field() + heading: typing.Optional[base.Integer] = fields.Field() From 5903540f415f377f5b23a69616dce8d39c28ff7d Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:24:11 +0300 Subject: [PATCH 24/39] AIOG-T-82 added the field proximity_alert_distance to the classes Location, InlineQueryResultLocation, InputLocationMessageContent; fixed heading in InputLocationMessageContent --- aiogram/types/inline_query_result.py | 3 +++ aiogram/types/input_message_content.py | 5 +++++ aiogram/types/location.py | 1 + 3 files changed, 9 insertions(+) diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index 5799772a..9c2432d1 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -360,6 +360,7 @@ class InlineQueryResultLocation(InlineQueryResult): title: base.String = fields.Field() live_period: base.Integer = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() + proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) thumb_url: base.String = fields.Field() thumb_width: base.Integer = fields.Field() @@ -372,6 +373,7 @@ class InlineQueryResultLocation(InlineQueryResult): title: base.String, live_period: typing.Optional[base.Integer] = None, heading: typing.Optional[base.Integer] = None, + proximity_alert_radius: typing.Optional[base.Integer] = None, reply_markup: typing.Optional[InlineKeyboardMarkup] = None, input_message_content: typing.Optional[InputMessageContent] = None, thumb_url: typing.Optional[base.String] = None, @@ -385,6 +387,7 @@ class InlineQueryResultLocation(InlineQueryResult): title=title, live_period=live_period, heading=heading, + proximity_alert_radius=proximity_alert_radius, reply_markup=reply_markup, input_message_content=input_message_content, thumb_url=thumb_url, diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 21a9479d..32fee9da 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -45,14 +45,19 @@ class InputLocationMessageContent(InputMessageContent): latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() + proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() def __init__(self, latitude: base.Float, longitude: base.Float, + heading: typing.Optional[base.Integer] = None, + proximity_alert_radius: typing.Optional[base.Integer] = None, ): super().__init__( latitude=latitude, longitude=longitude, + heading=heading, + proximity_alert_radius=proximity_alert_radius, ) diff --git a/aiogram/types/location.py b/aiogram/types/location.py index c8951b8a..9e81c865 100644 --- a/aiogram/types/location.py +++ b/aiogram/types/location.py @@ -14,3 +14,4 @@ class Location(base.TelegramObject): latitude: base.Float = fields.Field() live_period: typing.Optional[base.Integer] = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() + proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() From 8e7de80efe09e5d229de2610043c05e4bd409b20 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:28:17 +0300 Subject: [PATCH 25/39] AIOG-T-82 added parameter proximity_alert_distance to the methods sendLocation and editMessageLiveLocation --- aiogram/bot/bot.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 98f044ae..cc69a4b0 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -787,6 +787,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): latitude: base.Float, longitude: base.Float, live_period: typing.Optional[base.Integer] = None, heading: typing.Optional[base.Integer] = None, + proximity_alert_radius: typing.Optional[base.Integer] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, @@ -814,6 +815,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): in degrees. Must be between 1 and 360 if specified. :type heading: :obj:`typing.Optional[base.Integer]` + :param proximity_alert_radius: For live locations, a maximum distance for + proximity alerts about approaching another chat member, in meters. Must + be between 1 and 100000 if specified. + :type proximity_alert_radius: :obj:`typing.Optional[base.Integer]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Optional[base.Boolean]` @@ -841,6 +847,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): message_id: typing.Optional[base.Integer] = None, inline_message_id: typing.Optional[base.String] = None, heading: typing.Optional[base.Integer] = None, + proximity_alert_radius: typing.Optional[base.Integer] = None, reply_markup: typing.Optional[types.InlineKeyboardMarkup] = None, ) -> types.Message or base.Boolean: """ @@ -869,6 +876,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): between 1 and 360 if specified. :type heading: :obj:`typing.Optional[base.Integer]` + :param proximity_alert_radius: For live locations, a maximum distance for + proximity alerts about approaching another chat member, in meters. Must + be between 1 and 100000 if specified. + :type proximity_alert_radius: :obj:`typing.Optional[base.Integer]` + :param reply_markup: A JSON-serialized object for a new inline keyboard :type reply_markup: :obj:`typing.Optional[types.InlineKeyboardMarkup]` From 450809b70d251837b5b67173542da3d68fc4feaa Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:32:02 +0300 Subject: [PATCH 26/39] AIOG-T-83 Added the type ProximityAlertTriggered --- aiogram/types/proximity_alert_triggered.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 aiogram/types/proximity_alert_triggered.py diff --git a/aiogram/types/proximity_alert_triggered.py b/aiogram/types/proximity_alert_triggered.py new file mode 100644 index 00000000..240854d8 --- /dev/null +++ b/aiogram/types/proximity_alert_triggered.py @@ -0,0 +1,15 @@ +from . import base +from . import fields +from .user import User + + +class ProximityAlertTriggered(base.TelegramObject): + """ + This object represents the content of a service message, sent whenever a user in + the chat triggers a proximity alert set by another user. + + https://core.telegram.org/bots/api#proximityalerttriggered + """ + traveler: User = fields.Field() + watcher: User = fields.Field() + distance: base.Integer = fields.Field() From 95491b578c9b07649c952755ca759d9fbd6d0856 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:37:25 +0300 Subject: [PATCH 27/39] AIOG-T-83 Added field proximity_alert_triggered to the class Message --- aiogram/types/message.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aiogram/types/message.py b/aiogram/types/message.py index aadb56a1..f9fc0d72 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -24,6 +24,7 @@ from .message_entity import MessageEntity from .passport_data import PassportData from .photo_size import PhotoSize from .poll import Poll +from .proximity_alert_triggered import ProximityAlertTriggered from .reply_keyboard import ReplyKeyboardMarkup, ReplyKeyboardRemove from .sticker import Sticker from .successful_payment import SuccessfulPayment @@ -89,6 +90,7 @@ class Message(base.TelegramObject): successful_payment: SuccessfulPayment = fields.Field(base=SuccessfulPayment) connected_website: base.String = fields.Field() passport_data: PassportData = fields.Field(base=PassportData) + proximity_alert_triggered: typing.Optional[ProximityAlertTriggered] = fields.Field(base=ProximityAlertTriggered) reply_markup: InlineKeyboardMarkup = fields.Field(base=InlineKeyboardMarkup) @property @@ -150,6 +152,8 @@ class Message(base.TelegramObject): return ContentType.GROUP_CHAT_CREATED if self.passport_data: return ContentType.PASSPORT_DATA + if self.proximity_alert_triggered: + return ContentType.PROXIMITY_ALERT_TRIGGERED return ContentType.UNKNOWN @@ -2302,6 +2306,7 @@ class ContentType(helper.Helper): DELETE_CHAT_PHOTO = helper.Item() # delete_chat_photo GROUP_CHAT_CREATED = helper.Item() # group_chat_created PASSPORT_DATA = helper.Item() # passport_data + PROXIMITY_ALERT_TRIGGERED = helper.Item() # proximity_alert_triggered UNKNOWN = helper.Item() # unknown ANY = helper.Item() # any From 5d749b2f9a19240e7423c3892f51243ae39e6003 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:45:23 +0300 Subject: [PATCH 28/39] AIOG-T-84 Added the field horizontal_accuracy to the classes Location, InlineQueryResultLocation, InputLocationMessageContent --- aiogram/types/inline_query_result.py | 3 +++ aiogram/types/input_message_content.py | 3 +++ aiogram/types/location.py | 1 + 3 files changed, 7 insertions(+) diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index 9c2432d1..8fe788bf 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -358,6 +358,7 @@ class InlineQueryResultLocation(InlineQueryResult): latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() title: base.String = fields.Field() + horizontal_accuracy: typing.Optional[base.Float] = fields.Field() live_period: base.Integer = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() @@ -371,6 +372,7 @@ class InlineQueryResultLocation(InlineQueryResult): latitude: base.Float, longitude: base.Float, title: base.String, + horizontal_accuracy: typing.Optional[base.Float] = None, live_period: typing.Optional[base.Integer] = None, heading: typing.Optional[base.Integer] = None, proximity_alert_radius: typing.Optional[base.Integer] = None, @@ -385,6 +387,7 @@ class InlineQueryResultLocation(InlineQueryResult): latitude=latitude, longitude=longitude, title=title, + horizontal_accuracy=horizontal_accuracy, live_period=live_period, heading=heading, proximity_alert_radius=proximity_alert_radius, diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 32fee9da..b20b2d7d 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -44,18 +44,21 @@ class InputLocationMessageContent(InputMessageContent): """ latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() + horizontal_accuracy: typing.Optional[base.Float] = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() def __init__(self, latitude: base.Float, longitude: base.Float, + horizontal_accuracy: typing.Optional[base.Float] = None, heading: typing.Optional[base.Integer] = None, proximity_alert_radius: typing.Optional[base.Integer] = None, ): super().__init__( latitude=latitude, longitude=longitude, + horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, ) diff --git a/aiogram/types/location.py b/aiogram/types/location.py index 9e81c865..5f159e33 100644 --- a/aiogram/types/location.py +++ b/aiogram/types/location.py @@ -12,6 +12,7 @@ class Location(base.TelegramObject): """ longitude: base.Float = fields.Field() latitude: base.Float = fields.Field() + horizontal_accuracy: typing.Optional[base.Float] = fields.Field() live_period: typing.Optional[base.Integer] = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() From 2a399c504499d3e541a12f46e1617ba338d46b83 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:47:55 +0300 Subject: [PATCH 29/39] AIOG-T-84 Added the parameter horizontal_accuracy to the methods sendLocation and editMessageLiveLocation. --- aiogram/bot/bot.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index cc69a4b0..c5f36a10 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -785,6 +785,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): async def send_location(self, chat_id: typing.Union[base.Integer, base.String], latitude: base.Float, longitude: base.Float, + horizontal_accuracy: typing.Optional[base.Float] = None, live_period: typing.Optional[base.Integer] = None, heading: typing.Optional[base.Integer] = None, proximity_alert_radius: typing.Optional[base.Integer] = None, @@ -808,6 +809,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param longitude: Longitude of the location :type longitude: :obj:`base.Float` + :param horizontal_accuracy: The radius of uncertainty for the location, + measured in meters; 0-1500 + :type horizontal_accuracy: :obj:`typing.Optional[base.Float]` + :param live_period: Period in seconds for which the location will be updated :type live_period: :obj:`typing.Optional[base.Integer]` @@ -846,6 +851,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): chat_id: typing.Union[base.Integer, base.String, None] = None, message_id: typing.Optional[base.Integer] = None, inline_message_id: typing.Optional[base.String] = None, + horizontal_accuracy: typing.Optional[base.Float] = None, heading: typing.Optional[base.Integer] = None, proximity_alert_radius: typing.Optional[base.Integer] = None, reply_markup: typing.Optional[types.InlineKeyboardMarkup] = None, @@ -872,6 +878,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param longitude: Longitude of new location :type longitude: :obj:`base.Float` + :param horizontal_accuracy: The radius of uncertainty for the location, + measured in meters; 0-1500 + :type horizontal_accuracy: :obj:`typing.Optional[base.Float]` + :param heading: Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. :type heading: :obj:`typing.Optional[base.Integer]` From 7fac35cc146efc1763114027e6f195b0a35ca1bf Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 21:49:47 +0300 Subject: [PATCH 30/39] Added live_period to InputLocationMessageContent (missed?) --- aiogram/types/input_message_content.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index b20b2d7d..8ea1dd7f 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -45,6 +45,7 @@ class InputLocationMessageContent(InputMessageContent): latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() horizontal_accuracy: typing.Optional[base.Float] = fields.Field() + live_period: typing.Optional[base.Integer] = fields.Field() heading: typing.Optional[base.Integer] = fields.Field() proximity_alert_radius: typing.Optional[base.Integer] = fields.Field() @@ -52,6 +53,7 @@ class InputLocationMessageContent(InputMessageContent): latitude: base.Float, longitude: base.Float, horizontal_accuracy: typing.Optional[base.Float] = None, + live_period: typing.Optional[base.Integer] = None, heading: typing.Optional[base.Integer] = None, proximity_alert_radius: typing.Optional[base.Integer] = None, ): @@ -59,6 +61,7 @@ class InputLocationMessageContent(InputMessageContent): latitude=latitude, longitude=longitude, horizontal_accuracy=horizontal_accuracy, + live_period=live_period, heading=heading, proximity_alert_radius=proximity_alert_radius, ) From 10c25e43f5d0ccaa203dc426fb4ab83e4bd82924 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 22:11:25 +0300 Subject: [PATCH 31/39] AIOG-T-85 Added the field sender_chat to the class Message --- aiogram/types/message.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aiogram/types/message.py b/aiogram/types/message.py index f9fc0d72..522903c5 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -44,6 +44,7 @@ class Message(base.TelegramObject): message_id: base.Integer = fields.Field() from_user: User = fields.Field(alias="from", base=User) + sender_chat: Chat = fields.Field(base=Chat) date: datetime.datetime = fields.DateTimeField() chat: Chat = fields.Field(base=Chat) forward_from: User = fields.Field(base=User) From 3d71b1c192e8424d38075c875b7a7ea21b9607c9 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 22:14:39 +0300 Subject: [PATCH 32/39] AIOG-T-86 Added `is_anonymous` field to `chatMember` class --- aiogram/types/chat_member.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aiogram/types/chat_member.py b/aiogram/types/chat_member.py index 274c8a26..4aa52b80 100644 --- a/aiogram/types/chat_member.py +++ b/aiogram/types/chat_member.py @@ -1,6 +1,4 @@ import datetime -import warnings -from typing import Optional from . import base from . import fields @@ -17,6 +15,7 @@ class ChatMember(base.TelegramObject): user: User = fields.Field(base=User) status: base.String = fields.Field() custom_title: base.String = fields.Field() + is_anonymous: base.Boolean = fields.Field() until_date: datetime.datetime = fields.DateTimeField() can_be_edited: base.Boolean = fields.Field() can_change_info: base.Boolean = fields.Field() From a5ad55a6009a49d89a0ff14ac7585f1dbf83bdb7 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 22:21:13 +0300 Subject: [PATCH 33/39] AIOG-T-87 Added the parameter is_anonymous to the method promoteChatMember --- aiogram/bot/bot.py | 50 ++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index c5f36a10..e8f54eac 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1323,16 +1323,19 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.RESTRICT_CHAT_MEMBER, payload) return result - async def promote_chat_member(self, chat_id: typing.Union[base.Integer, base.String], + async def promote_chat_member(self, + chat_id: typing.Union[base.Integer, base.String], user_id: base.Integer, - can_change_info: typing.Union[base.Boolean, None] = None, - can_post_messages: typing.Union[base.Boolean, None] = None, - can_edit_messages: typing.Union[base.Boolean, None] = None, - can_delete_messages: typing.Union[base.Boolean, None] = None, - can_invite_users: typing.Union[base.Boolean, None] = None, - can_restrict_members: typing.Union[base.Boolean, None] = None, - can_pin_messages: typing.Union[base.Boolean, None] = None, - can_promote_members: typing.Union[base.Boolean, None] = None) -> base.Boolean: + is_anonymous: typing.Optional[base.Boolean] = None, + can_change_info: typing.Optional[base.Boolean] = None, + can_post_messages: typing.Optional[base.Boolean] = None, + can_edit_messages: typing.Optional[base.Boolean] = None, + can_delete_messages: typing.Optional[base.Boolean] = None, + can_invite_users: typing.Optional[base.Boolean] = None, + can_restrict_members: typing.Optional[base.Boolean] = None, + can_pin_messages: typing.Optional[base.Boolean] = None, + can_promote_members: typing.Optional[base.Boolean] = None, + ) -> base.Boolean: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1342,26 +1345,39 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param user_id: Unique identifier of the target user :type user_id: :obj:`base.Integer` + + :param is_anonymous: Pass True, if the administrator's presence in the chat is hidden + :type is_anonymous: :obj:`typing.Optional[base.Boolean]` + :param can_change_info: Pass True, if the administrator can change chat title, photo and other settings - :type can_change_info: :obj:`typing.Union[base.Boolean, None]` + :type can_change_info: :obj:`typing.Optional[base.Boolean]` + :param can_post_messages: Pass True, if the administrator can create channel posts, channels only - :type can_post_messages: :obj:`typing.Union[base.Boolean, None]` + :type can_post_messages: :obj:`typing.Optional[base.Boolean]` + :param can_edit_messages: Pass True, if the administrator can edit messages of other users, channels only - :type can_edit_messages: :obj:`typing.Union[base.Boolean, None]` + :type can_edit_messages: :obj:`typing.Optional[base.Boolean]` + :param can_delete_messages: Pass True, if the administrator can delete messages of other users - :type can_delete_messages: :obj:`typing.Union[base.Boolean, None]` + :type can_delete_messages: :obj:`typing.Optional[base.Boolean]` + :param can_invite_users: Pass True, if the administrator can invite new users to the chat - :type can_invite_users: :obj:`typing.Union[base.Boolean, None]` + :type can_invite_users: :obj:`typing.Optional[base.Boolean]` + :param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members - :type can_restrict_members: :obj:`typing.Union[base.Boolean, None]` + :type can_restrict_members: :obj:`typing.Optional[base.Boolean]` + :param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only - :type can_pin_messages: :obj:`typing.Union[base.Boolean, None]` + :type can_pin_messages: :obj:`typing.Optional[base.Boolean]` + :param can_promote_members: Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) - :type can_promote_members: :obj:`typing.Union[base.Boolean, None]` + :type can_promote_members: :obj:`typing.Optional[base.Boolean]` + :return: Returns True on success :rtype: :obj:`base.Boolean` """ From 6e9b07a540382ce6f857e1ec2152d00320d0e318 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 22:56:09 +0300 Subject: [PATCH 34/39] AIOG-T-89 Added the method `copyMessage` --- aiogram/bot/api.py | 1 + aiogram/bot/bot.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index ba4bd2b1..c83b7ede 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -169,6 +169,7 @@ class Methods(Helper): CLOSE = Item() # close SEND_MESSAGE = Item() # sendMessage FORWARD_MESSAGE = Item() # forwardMessage + COPY_MESSAGE = Item() # copyMessage SEND_PHOTO = Item() # sendPhoto SEND_AUDIO = Item() # sendAudio SEND_DOCUMENT = Item() # sendDocument diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index e8f54eac..109a948f 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -321,6 +321,83 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.FORWARD_MESSAGE, payload) return types.Message(**result) + async def copy_message(self, + chat_id: typing.Union[base.Integer, base.String], + from_chat_id: typing.Union[base.Integer, base.String], + message_id: base.Integer, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, + disable_notification: typing.Optional[base.Boolean] = None, + reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, None] = None, + ) -> types.Message: + """ + Use this method to copy messages of any kind. The method is analogous to the + method forwardMessages, but the copied message doesn't have a link to the + original message. Returns the MessageId of the sent message on success. + + Source: https://core.telegram.org/bots/api#copymessage + + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) + :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + + :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:`typing.Union[base.Integer, base.String]` + + :param message_id: Message identifier in the chat specified in from_chat_id + :type message_id: :obj:`base.Integer` + + :param caption: New caption for media, 0-1024 characters after entities + parsing. If not specified, the original caption is kept + :type caption: :obj:`typing.Optional[base.String]` + + :param parse_mode: Mode for parsing entities in the new caption. See + formatting options for more details: + https://core.telegram.org/bots/api#formatting-options + :type parse_mode: :obj:`typing.Optional[base.String]` + + :param caption_entities: List of special entities that appear in the new + caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + + :param disable_notification: Sends the message silently. Users will receive + a notification with no sound + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :param reply_to_message_id: If the message is a reply, ID of the original + message + :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + + :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:`typing.Union[types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, + None]` + + :return: On success, the sent Message is returned + :rtype: :obj:`types.Message` + """ + reply_markup = prepare_arg(reply_markup) + payload = generate_payload(**locals()) + + if self.parse_mode: + payload.setdefault('parse_mode', self.parse_mode) + + result = await self.request(api.Methods.COPY_MESSAGE, payload) + return types.Message(**result) + async def send_photo(self, chat_id: typing.Union[base.Integer, base.String], photo: typing.Union[base.InputFile, base.String], caption: typing.Union[base.String, None] = None, From c39ebcf7de765f58aad35737fda4fb1cbd4a74eb Mon Sep 17 00:00:00 2001 From: Oleg A Date: Thu, 5 Nov 2020 23:23:00 +0300 Subject: [PATCH 35/39] AIOG-T-90 Poll docs update --- aiogram/bot/bot.py | 76 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 109a948f..7a7e5c76 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1104,7 +1104,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_CONTACT, payload) return types.Message(**result) - async def send_poll(self, chat_id: typing.Union[base.Integer, base.String], + async def send_poll(self, + chat_id: typing.Union[base.Integer, base.String], question: base.String, options: typing.List[base.String], is_anonymous: typing.Optional[base.Boolean] = None, @@ -1115,54 +1116,85 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): explanation_parse_mode: typing.Optional[base.String] = None, open_period: typing.Union[base.Integer, None] = None, close_date: typing.Union[ - base.Integer, datetime.datetime, datetime.timedelta, None] = None, + base.Integer, + datetime.datetime, + datetime.timedelta, + None] = None, is_closed: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ - Use this method to send a native poll. A native poll can't be sent to a private chat. - On success, the sent Message is returned. + Use this method to send a native poll. On success, the sent Message is + returned. Source: https://core.telegram.org/bots/api#sendpoll - :param chat_id: Unique identifier for the target chat - or username of the target channel (in the format @channelusername). - A native poll can't be sent to a private chat. + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` - :param question: Poll question, 1-255 characters + + :param question: Poll question, 1-300 characters :type question: :obj:`base.String` - :param options: List of answer options, 2-10 strings 1-100 characters each + + :param options: A list of answer options, 2-10 strings 1-100 characters each :type options: :obj:`typing.List[base.String]` + :param is_anonymous: True, if the poll needs to be anonymous, defaults to True :type is_anonymous: :obj:`typing.Optional[base.Boolean]` + :param type: Poll type, “quiz” or “regular”, defaults to “regular” :type type: :obj:`typing.Optional[base.String]` - :param allows_multiple_answers: True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False + + :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:`typing.Optional[base.Boolean]` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + + :param correct_option_id: 0-based identifier of the correct answer option, + required for polls in quiz mode :type correct_option_id: :obj:`typing.Optional[base.Integer]` - :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:`typing.Optional[base.String]` - :param explanation_parse_mode: Mode for parsing entities in the explanation. See formatting options for more details. + + :param explanation_parse_mode: Mode for parsing entities in the explanation. + See formatting options for more details. :type explanation_parse_mode: :obj:`typing.Optional[base.String]` - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. + + :param open_period: Amount of time in seconds the poll will be active after + creation, 5-600. Can't be used together with close_date. :type open_period: :obj:`typing.Union[base.Integer, None]` - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open_period. - :type close_date: :obj:`typing.Union[base.Integer, datetime.datetime, datetime.timedelta, None]` + + :param close_date: Point in time (Unix timestamp) when the poll will be + automatically closed. Must be at least 5 and no more than 600 seconds in + the future. Can't be used together with open_period. + :type close_date: :obj:`typing.Union[base.Integer, datetime.datetime, + datetime.timedelta, None]` + :param is_closed: Pass True, if the poll needs to be immediately closed :type is_closed: :obj:`typing.Optional[base.Boolean]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + + :param disable_notification: Sends the message silently. Users will receive + a notification with no sound. :type disable_notification: :obj:`typing.Optional[Boolean]` - :param reply_to_message_id: If the message is a reply, ID of the original message + + :param reply_to_message_id: If the message is a reply, ID of the original + message :type reply_to_message_id: :obj:`typing.Optional[Integer]` - :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:`typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, + None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ From f4196920550683122a55b24d116ecc82ff9c4a3a Mon Sep 17 00:00:00 2001 From: Oleg A Date: Fri, 6 Nov 2020 00:33:03 +0300 Subject: [PATCH 36/39] AIOG-T-91 ability to manually specify text entities --- aiogram/bot/bot.py | 184 ++++++++-- aiogram/types/inline_query_result.py | 490 ++++++++++++++----------- aiogram/types/input_media.py | 108 ++++-- aiogram/types/input_message_content.py | 19 +- 4 files changed, 523 insertions(+), 278 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 7a7e5c76..5f49772f 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -254,15 +254,19 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.CLOSE, payload) return result - async def send_message(self, chat_id: typing.Union[base.Integer, base.String], text: base.String, - parse_mode: typing.Union[base.String, None] = None, - disable_web_page_preview: typing.Union[base.Boolean, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, + async def send_message(self, + chat_id: typing.Union[base.Integer, base.String], + text: base.String, + parse_mode: typing.Optional[base.String] = None, + entities: typing.Optional[typing.List[types.MessageEntity]] = None, + disable_web_page_preview: typing.Optional[base.Boolean] = None, + disable_notification: typing.Optional[base.Boolean] = None, + reply_to_message_id: typing.Optional[base.Integer] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ Use this method to send text messages. @@ -270,21 +274,32 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param text: Text of the message to be sent :type text: :obj:`base.String` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. - :type parse_mode: :obj:`typing.Union[base.String, None]` + :type parse_mode: :obj:`typing.Optional[base.String]` + + :param entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param disable_web_page_preview: Disables link previews for links in this message - :type disable_web_page_preview: :obj:`typing.Union[base.Boolean, None]` + :type disable_web_page_preview: :obj:`typing.Optional[base.Boolean]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -398,16 +413,19 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.COPY_MESSAGE, payload) return types.Message(**result) - async def send_photo(self, chat_id: typing.Union[base.Integer, base.String], + async def send_photo(self, + chat_id: typing.Union[base.Integer, base.String], photo: typing.Union[base.InputFile, base.String], caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ Use this method to send photos. @@ -415,21 +433,32 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param photo: Photo to send :type photo: :obj:`typing.Union[base.InputFile, base.String]` + :param caption: Photo caption (may also be used when resending photos by file_id), 0-1024 characters :type caption: :obj:`typing.Union[base.String, None]` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -444,10 +473,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_PHOTO, payload, files) return types.Message(**result) - async def send_audio(self, chat_id: typing.Union[base.Integer, base.String], + async def send_audio(self, + chat_id: typing.Union[base.Integer, base.String], audio: typing.Union[base.InputFile, base.String], caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, duration: typing.Union[base.Integer, None] = None, performer: typing.Union[base.String, None] = None, title: typing.Union[base.String, None] = None, @@ -457,7 +488,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -468,29 +500,44 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param audio: Audio file to send :type audio: :obj:`typing.Union[base.InputFile, base.String]` + :param caption: Audio caption, 0-1024 characters :type caption: :obj:`typing.Union[base.String, None]` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param duration: Duration of the audio in seconds :type duration: :obj:`typing.Union[base.Integer, None]` + :param performer: Performer :type performer: :obj:`typing.Union[base.String, None]` + :param title: Track name :type title: :obj:`typing.Union[base.String, None]` + :param thumb: Thumbnail of the file sent :type thumb: :obj:`typing.Union[base.InputFile, base.String, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -506,21 +553,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_AUDIO, payload, files) return types.Message(**result) - async def send_document(self, chat_id: typing.Union[base.Integer, base.String], + async def send_document(self, + chat_id: typing.Union[base.Integer, base.String], document: typing.Union[base.InputFile, base.String], thumb: typing.Union[base.InputFile, base.String, None] = None, caption: typing.Optional[base.String] = None, parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, disable_content_type_detection: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, - reply_markup: typing.Union[ - types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, - None, - ] = None) -> types.Message: + reply_markup: typing.Union[types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None] = None, + ) -> types.Message: """ Use this method to send general files. On success, the sent Message is returned. Bots can currently send files of any type of up to 50 MB in size, @@ -550,6 +598,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Optional[base.String]` + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Optional[base.Boolean]` @@ -588,6 +640,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): thumb: typing.Union[base.InputFile, base.String, None] = None, caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, supports_streaming: typing.Union[base.Boolean, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, @@ -603,31 +656,47 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param video: Video to send :type video: :obj:`typing.Union[base.InputFile, base.String]` + :param duration: Duration of sent video in seconds :type duration: :obj:`typing.Union[base.Integer, None]` + :param width: Video width :type width: :obj:`typing.Union[base.Integer, None]` + :param height: Video height :type height: :obj:`typing.Union[base.Integer, None]` + :param thumb: Thumbnail of the file sent :type thumb: :obj:`typing.Union[base.InputFile, base.String, None]` + :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters :type caption: :obj:`typing.Union[base.String, None]` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param supports_streaming: Pass True, if the uploaded video is suitable for streaming :type supports_streaming: :obj:`typing.Union[base.Boolean, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -652,12 +721,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, reply_markup: typing.Union[typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply], None] = None + types.ForceReply], None] = None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). @@ -670,32 +740,47 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param animation: Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data :type animation: :obj:`typing.Union[base.InputFile, base.String]` + :param duration: Duration of sent animation in seconds :type duration: :obj:`typing.Union[base.Integer, None]` + :param width: Animation width :type width: :obj:`typing.Union[base.Integer, None]` + :param height: Animation height :type height: :obj:`typing.Union[base.Integer, None]` + :param thumb: Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 320. :type thumb: :obj:`typing.Union[typing.Union[base.InputFile, base.String], None]` + :param caption: Animation caption (may also be used when resending animation by file_id), 0-1024 characters :type caption: :obj:`typing.Union[base.String, None]` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :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:`typing.Union[typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply], None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -711,17 +796,20 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_ANIMATION, payload, files) return types.Message(**result) - async def send_voice(self, chat_id: typing.Union[base.Integer, base.String], + async def send_voice(self, + chat_id: typing.Union[base.Integer, base.String], voice: typing.Union[base.InputFile, base.String], caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, duration: typing.Union[base.Integer, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -733,23 +821,35 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param voice: Audio file to send :type voice: :obj:`typing.Union[base.InputFile, base.String]` + :param caption: Voice message caption, 0-1024 characters :type caption: :obj:`typing.Union[base.String, None]` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param duration: Duration of the voice message in seconds :type duration: :obj:`typing.Union[base.Integer, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -1114,6 +1214,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): correct_option_id: typing.Optional[base.Integer] = None, explanation: typing.Optional[base.String] = None, explanation_parse_mode: typing.Optional[base.String] = None, + explanation_entities: typing.Optional[typing.List[types.MessageEntity]] = None, open_period: typing.Union[base.Integer, None] = None, close_date: typing.Union[ base.Integer, @@ -1167,6 +1268,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): See formatting options for more details. :type explanation_parse_mode: :obj:`typing.Optional[base.String]` + :param explanation_entities: List of special entities that appear in message + text, which can be specified instead of parse_mode + :type explanation_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. :type open_period: :obj:`typing.Union[base.Integer, None]` @@ -1921,14 +2026,17 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_MY_COMMANDS, payload) return [types.BotCommand(**bot_command_data) for bot_command_data in result] - async def edit_message_text(self, text: base.String, + async def edit_message_text(self, + text: base.String, chat_id: typing.Union[base.Integer, base.String, None] = None, message_id: typing.Union[base.Integer, None] = None, inline_message_id: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + entities: typing.Optional[typing.List[types.MessageEntity]] = None, disable_web_page_preview: typing.Union[base.Boolean, None] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + None] = None, + ) -> types.Message or base.Boolean: """ Use this method to edit text and game messages sent by the bot or via the bot (for inline bots). @@ -1937,19 +2045,30 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Required if inline_message_id is not specified Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String, None]` + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message :type message_id: :obj:`typing.Union[base.Integer, None]` + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message :type inline_message_id: :obj:`typing.Union[base.String, None]` + :param text: New text of the message :type text: :obj:`base.String` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param disable_web_page_preview: Disables link previews for links in this message :type disable_web_page_preview: :obj:`typing.Union[base.Boolean, None]` + :param reply_markup: A JSON-serialized object for an inline keyboard :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, None]` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` @@ -1969,6 +2088,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): inline_message_id: typing.Union[base.String, None] = None, caption: typing.Union[base.String, None] = None, parse_mode: typing.Union[base.String, None] = None, + caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None) -> types.Message or base.Boolean: """ @@ -1979,17 +2099,27 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Required if inline_message_id is not specified Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String, None]` + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message :type message_id: :obj:`typing.Union[base.Integer, None]` + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message :type inline_message_id: :obj:`typing.Union[base.String, None]` + :param caption: New caption of the message :type caption: :obj:`typing.Union[base.String, None]` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. :type parse_mode: :obj:`typing.Union[base.String, None]` + + :param caption_entities: List of special entities that appear in message text, + which can be specified instead of parse_mode + :type caption_entities: :obj:`typing.Optional[typing.List[types.MessageEntity]]` + :param reply_markup: A JSON-serialized object for an inline keyboard :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, None]` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index 8fe788bf..a09ec5ce 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -1,6 +1,6 @@ import typing -from . import base +from . import base, MessageEntity from . import fields from .inline_keyboard import InlineKeyboardMarkup from .input_message_content import InputMessageContent @@ -83,23 +83,29 @@ class InlineQueryResultPhoto(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - photo_url: base.String, - thumb_url: base.String, - photo_width: typing.Optional[base.Integer] = None, - photo_height: typing.Optional[base.Integer] = None, - title: typing.Optional[base.String] = None, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultPhoto, self).__init__(id=id, photo_url=photo_url, thumb_url=thumb_url, - photo_width=photo_width, photo_height=photo_height, title=title, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + photo_url: base.String, + thumb_url: base.String, + photo_width: typing.Optional[base.Integer] = None, + photo_height: typing.Optional[base.Integer] = None, + title: typing.Optional[base.String] = None, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, photo_url=photo_url, thumb_url=thumb_url, + photo_width=photo_width, photo_height=photo_height, title=title, + description=description, caption=caption, + parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) class InlineQueryResultGif(InlineQueryResult): @@ -123,23 +129,29 @@ class InlineQueryResultGif(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - gif_url: base.String, - gif_width: typing.Optional[base.Integer] = None, - gif_height: typing.Optional[base.Integer] = None, - gif_duration: typing.Optional[base.Integer] = None, - thumb_url: typing.Optional[base.String] = None, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultGif, self).__init__(id=id, gif_url=gif_url, gif_width=gif_width, - gif_height=gif_height, gif_duration=gif_duration, - thumb_url=thumb_url, title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + gif_url: base.String, + gif_width: typing.Optional[base.Integer] = None, + gif_height: typing.Optional[base.Integer] = None, + gif_duration: typing.Optional[base.Integer] = None, + thumb_url: typing.Optional[base.String] = None, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, gif_url=gif_url, gif_width=gif_width, gif_height=gif_height, + gif_duration=gif_duration, thumb_url=thumb_url, title=title, + caption=caption, parse_mode=parse_mode, reply_markup=reply_markup, + caption_entities=caption_entities, + input_message_content=input_message_content, + ) class InlineQueryResultMpeg4Gif(InlineQueryResult): @@ -163,23 +175,30 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - mpeg4_url: base.String, - thumb_url: base.String, - mpeg4_width: typing.Optional[base.Integer] = None, - mpeg4_height: typing.Optional[base.Integer] = None, - mpeg4_duration: typing.Optional[base.Integer] = None, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultMpeg4Gif, self).__init__(id=id, mpeg4_url=mpeg4_url, mpeg4_width=mpeg4_width, - mpeg4_height=mpeg4_height, mpeg4_duration=mpeg4_duration, - thumb_url=thumb_url, title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + mpeg4_url: base.String, + thumb_url: base.String, + mpeg4_width: typing.Optional[base.Integer] = None, + mpeg4_height: typing.Optional[base.Integer] = None, + mpeg4_duration: typing.Optional[base.Integer] = None, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, mpeg4_url=mpeg4_url, mpeg4_width=mpeg4_width, + mpeg4_height=mpeg4_height, mpeg4_duration=mpeg4_duration, + thumb_url=thumb_url, title=title, caption=caption, + parse_mode=parse_mode, reply_markup=reply_markup, + caption_entities=caption_entities, + input_message_content=input_message_content, + ) class InlineQueryResultVideo(InlineQueryResult): @@ -207,26 +226,32 @@ class InlineQueryResultVideo(InlineQueryResult): description: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - video_url: base.String, - mime_type: base.String, - thumb_url: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - video_width: typing.Optional[base.Integer] = None, - video_height: typing.Optional[base.Integer] = None, - video_duration: typing.Optional[base.Integer] = None, - description: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultVideo, self).__init__(id=id, video_url=video_url, mime_type=mime_type, - thumb_url=thumb_url, title=title, caption=caption, - video_width=video_width, video_height=video_height, - video_duration=video_duration, description=description, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + video_url: base.String, + mime_type: base.String, + thumb_url: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + video_width: typing.Optional[base.Integer] = None, + video_height: typing.Optional[base.Integer] = None, + video_duration: typing.Optional[base.Integer] = None, + description: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, video_url=video_url, mime_type=mime_type, thumb_url=thumb_url, + title=title, caption=caption, video_width=video_width, + video_height=video_height, video_duration=video_duration, + description=description, parse_mode=parse_mode, + reply_markup=reply_markup, caption_entities=caption_entities, + input_message_content=input_message_content, + ) class InlineQueryResultAudio(InlineQueryResult): @@ -248,21 +273,27 @@ class InlineQueryResultAudio(InlineQueryResult): audio_duration: base.Integer = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - audio_url: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - performer: typing.Optional[base.String] = None, - audio_duration: typing.Optional[base.Integer] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultAudio, self).__init__(id=id, audio_url=audio_url, title=title, - caption=caption, parse_mode=parse_mode, - performer=performer, audio_duration=audio_duration, - reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + audio_url: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + performer: typing.Optional[base.String] = None, + audio_duration: typing.Optional[base.Integer] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, audio_url=audio_url, title=title, + caption=caption, parse_mode=parse_mode, + performer=performer, audio_duration=audio_duration, + reply_markup=reply_markup, caption_entities=caption_entities, + input_message_content=input_message_content, + ) class InlineQueryResultVoice(InlineQueryResult): @@ -285,19 +316,25 @@ class InlineQueryResultVoice(InlineQueryResult): voice_duration: base.Integer = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - voice_url: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - voice_duration: typing.Optional[base.Integer] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultVoice, self).__init__(id=id, voice_url=voice_url, title=title, - caption=caption, voice_duration=voice_duration, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + voice_url: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + voice_duration: typing.Optional[base.Integer] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, voice_url=voice_url, title=title, caption=caption, + voice_duration=voice_duration, parse_mode=parse_mode, + reply_markup=reply_markup, caption_entities=caption_entities, + input_message_content=input_message_content, + ) class InlineQueryResultDocument(InlineQueryResult): @@ -323,25 +360,31 @@ class InlineQueryResultDocument(InlineQueryResult): thumb_width: base.Integer = fields.Field() thumb_height: base.Integer = fields.Field() - def __init__(self, *, - id: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - document_url: typing.Optional[base.String] = None, - mime_type: typing.Optional[base.String] = None, - description: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None): - super(InlineQueryResultDocument, self).__init__(id=id, title=title, caption=caption, - document_url=document_url, mime_type=mime_type, - description=description, reply_markup=reply_markup, - input_message_content=input_message_content, - thumb_url=thumb_url, thumb_width=thumb_width, - thumb_height=thumb_height, parse_mode=parse_mode) + def __init__( + self, + *, + id: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + document_url: typing.Optional[base.String] = None, + mime_type: typing.Optional[base.String] = None, + description: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + ): + super().__init__( + id=id, title=title, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, document_url=document_url, + mime_type=mime_type, description=description, reply_markup=reply_markup, + input_message_content=input_message_content, + thumb_url=thumb_url, thumb_width=thumb_width, + thumb_height=thumb_height, + ) class InlineQueryResultLocation(InlineQueryResult): @@ -523,19 +566,24 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - photo_file_id: base.String, - title: typing.Optional[base.String] = None, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedPhoto, self).__init__(id=id, photo_file_id=photo_file_id, title=title, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + photo_file_id: base.String, + title: typing.Optional[base.String] = None, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, photo_file_id=photo_file_id, title=title, description=description, + caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) class InlineQueryResultCachedGif(InlineQueryResult): @@ -554,18 +602,23 @@ class InlineQueryResultCachedGif(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - gif_file_id: base.String, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedGif, self).__init__(id=id, gif_file_id=gif_file_id, - title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + gif_file_id: base.String, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, gif_file_id=gif_file_id, title=title, caption=caption, + parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): @@ -584,18 +637,23 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - mpeg4_file_id: base.String, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedMpeg4Gif, self).__init__(id=id, mpeg4_file_id=mpeg4_file_id, - title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + mpeg4_file_id: base.String, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, mpeg4_file_id=mpeg4_file_id, title=title, caption=caption, + parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) class InlineQueryResultCachedSticker(InlineQueryResult): @@ -644,20 +702,25 @@ class InlineQueryResultCachedDocument(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - title: base.String, - document_file_id: base.String, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedDocument, self).__init__(id=id, title=title, - document_file_id=document_file_id, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + title: base.String, + document_file_id: base.String, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, title=title, document_file_id=document_file_id, + description=description, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedVideo(InlineQueryResult): @@ -677,19 +740,24 @@ class InlineQueryResultCachedVideo(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - video_file_id: base.String, - title: base.String, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedVideo, self).__init__(id=id, video_file_id=video_file_id, title=title, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + video_file_id: base.String, + title: base.String, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, video_file_id=video_file_id, title=title, description=description, + caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) class InlineQueryResultCachedVoice(InlineQueryResult): @@ -710,18 +778,23 @@ class InlineQueryResultCachedVoice(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - voice_file_id: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedVoice, self).__init__(id=id, voice_file_id=voice_file_id, - title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + voice_file_id: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, voice_file_id=voice_file_id, title=title, caption=caption, + parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) class InlineQueryResultCachedAudio(InlineQueryResult): @@ -742,14 +815,19 @@ class InlineQueryResultCachedAudio(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - audio_file_id: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedAudio, self).__init__(id=id, audio_file_id=audio_file_id, - caption=caption, parse_mode=parse_mode, - reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + audio_file_id: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super().__init__( + id=id, audio_file_id=audio_file_id, caption=caption, + parse_mode=parse_mode, caption_entities=caption_entities, + reply_markup=reply_markup, input_message_content=input_message_content, + ) diff --git a/aiogram/types/input_media.py b/aiogram/types/input_media.py index 254e3840..943a534c 100644 --- a/aiogram/types/input_media.py +++ b/aiogram/types/input_media.py @@ -5,6 +5,7 @@ import typing from . import base from . import fields from .input_file import InputFile +from .message_entity import MessageEntity ATTACHMENT_PREFIX = 'attach://' @@ -106,14 +107,23 @@ class InputMediaAnimation(InputMedia): height: base.Integer = fields.Field() duration: base.Integer = fields.Field() - def __init__(self, media: base.InputFile, - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None, - parse_mode: base.String = None, **kwargs): - super(InputMediaAnimation, self).__init__(type='animation', media=media, thumb=thumb, caption=caption, - width=width, height=height, duration=duration, - parse_mode=parse_mode, conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + width: base.Integer = None, + height: base.Integer = None, + duration: base.Integer = None, + parse_mode: base.String = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + **kwargs, + ): + super().__init__( + type='animation', media=media, thumb=thumb, caption=caption, width=width, + height=height, duration=duration, parse_mode=parse_mode, + caption_entities=caption_entities, conf=kwargs, + ) class InputMediaDocument(InputMedia): @@ -129,15 +139,13 @@ class InputMediaDocument(InputMedia): thumb: typing.Union[base.InputFile, base.String, None] = None, caption: base.String = None, parse_mode: base.String = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, disable_content_type_detection: typing.Optional[base.Boolean] = None, **kwargs, ): super().__init__( - type='document', - media=media, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, + type='document', media=media, thumb=thumb, caption=caption, + parse_mode=parse_mode, caption_entities=caption_entities, disable_content_type_detection=disable_content_type_detection, conf=kwargs, ) @@ -154,17 +162,23 @@ class InputMediaAudio(InputMedia): performer: base.String = fields.Field() title: base.String = fields.Field() - def __init__(self, media: base.InputFile, - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - duration: base.Integer = None, - performer: base.String = None, - title: base.String = None, - parse_mode: base.String = None, **kwargs): - super(InputMediaAudio, self).__init__(type='audio', media=media, thumb=thumb, - caption=caption, duration=duration, - performer=performer, title=title, - parse_mode=parse_mode, conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + duration: base.Integer = None, + performer: base.String = None, + title: base.String = None, + parse_mode: base.String = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + **kwargs, + ): + super().__init__( + type='audio', media=media, thumb=thumb, caption=caption, + duration=duration, performer=performer, title=title, + parse_mode=parse_mode, caption_entities=caption_entities, conf=kwargs, + ) class InputMediaPhoto(InputMedia): @@ -174,11 +188,18 @@ class InputMediaPhoto(InputMedia): https://core.telegram.org/bots/api#inputmediaphoto """ - def __init__(self, media: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, parse_mode: base.String = None, **kwargs): - super(InputMediaPhoto, self).__init__(type='photo', media=media, thumb=thumb, - caption=caption, parse_mode=parse_mode, - conf=kwargs) + def __init__( + self, + media: base.InputFile, + caption: base.String = None, + parse_mode: base.String = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + **kwargs, + ): + super().__init__( + type='photo', media=media, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, conf=kwargs, + ) class InputMediaVideo(InputMedia): @@ -192,16 +213,25 @@ class InputMediaVideo(InputMedia): duration: base.Integer = fields.Field() supports_streaming: base.Boolean = fields.Field() - def __init__(self, media: base.InputFile, - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None, - parse_mode: base.String = None, - supports_streaming: base.Boolean = None, **kwargs): - super(InputMediaVideo, self).__init__(type='video', media=media, thumb=thumb, caption=caption, - width=width, height=height, duration=duration, - parse_mode=parse_mode, - supports_streaming=supports_streaming, conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + width: base.Integer = None, + height: base.Integer = None, + duration: base.Integer = None, + parse_mode: base.String = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + supports_streaming: base.Boolean = None, + **kwargs, + ): + super().__init__( + type='video', media=media, thumb=thumb, caption=caption, + width=width, height=height, duration=duration, + parse_mode=parse_mode, caption_entities=caption_entities, + supports_streaming=supports_streaming, conf=kwargs + ) class MediaGroup(base.TelegramObject): diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 8ea1dd7f..2ee2085b 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -1,6 +1,6 @@ import typing -from . import base +from . import base, MessageEntity from . import fields @@ -83,14 +83,21 @@ class InputTextMessageContent(InputMessageContent): except RuntimeError: pass - def __init__(self, message_text: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - disable_web_page_preview: typing.Optional[base.Boolean] = None): + def __init__( + self, + message_text: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + caption_entities: typing.Optional[typing.List[MessageEntity]] = None, + disable_web_page_preview: typing.Optional[base.Boolean] = None, + ): if parse_mode is None: parse_mode = self.safe_get_parse_mode() - super(InputTextMessageContent, self).__init__(message_text=message_text, parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview) + super().__init__( + message_text=message_text, parse_mode=parse_mode, + caption_entities=caption_entities, + disable_web_page_preview=disable_web_page_preview, + ) class InputVenueMessageContent(InputMessageContent): From 5662d090b5bf79c5ec24569f22cc42870f129caa Mon Sep 17 00:00:00 2001 From: Oleg A Date: Fri, 6 Nov 2020 00:47:26 +0300 Subject: [PATCH 37/39] AIOG-T-92 Google Places as a venue API provider --- aiogram/bot/bot.py | 64 ++++++++++++++++++-------- aiogram/types/inline_query_result.py | 49 ++++++++++++-------- aiogram/types/input_message_content.py | 27 ++++++++--- aiogram/types/venue.py | 2 + 4 files changed, 97 insertions(+), 45 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 5f49772f..32f9117c 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1115,44 +1115,72 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return result return types.Message(**result) - async def send_venue(self, chat_id: typing.Union[base.Integer, base.String], - latitude: base.Float, longitude: base.Float, - title: base.String, address: base.String, - foursquare_id: typing.Union[base.String, None] = None, - foursquare_type: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, + async def send_venue(self, + chat_id: typing.Union[base.Integer, base.String], + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Optional[base.String] = None, + foursquare_type: typing.Optional[base.String] = None, + google_place_id: typing.Optional[base.String] = None, + google_place_type: typing.Optional[base.String] = None, + disable_notification: typing.Optional[base.Boolean] = None, + reply_to_message_id: typing.Optional[base.Integer] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ Use this method to send information about a venue. Source: https://core.telegram.org/bots/api#sendvenue - :param chat_id: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the + target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param latitude: Latitude of the venue :type latitude: :obj:`base.Float` + :param longitude: Longitude of the venue :type longitude: :obj:`base.Float` + :param title: Name of the venue :type title: :obj:`base.String` + :param address: Address of the venue :type address: :obj:`base.String` + :param foursquare_id: Foursquare identifier of the venue - :type foursquare_id: :obj:`typing.Union[base.String, None]` + :type foursquare_id: :obj:`typing.Optional[base.String]` + :param foursquare_type: Foursquare type of the venue, if known - :type foursquare_type: :obj:`typing.Union[base.String, None]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`typing.Union[base.Boolean, None]` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` - :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 foursquare_type: :obj:`typing.Optional[base.String]` + + :param google_place_id: Google Places identifier of the venue + :type google_place_id: :obj:`typing.Optional[base.String]` + + :param google_place_type: Google Places type of the venue. See supported + types: https://developers.google.com/places/web-service/supported_types + :type google_place_type: :obj:`typing.Optional[base.String]` + + :param disable_notification: Sends the message silently. Users will receive + a notification with no sound + :type disable_notification: :obj:`typing.Optional[base.Boolean]` + + :param reply_to_message_id: If the message is a reply, ID of the original + message + :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + + :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:`typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, + None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index a09ec5ce..af7dd3dd 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -460,31 +460,40 @@ class InlineQueryResultVenue(InlineQueryResult): title: base.String = fields.Field() address: base.String = fields.Field() foursquare_id: base.String = fields.Field() + foursquare_type: base.String = fields.Field() + google_place_id: base.String = fields.Field() + google_place_type: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) thumb_url: base.String = fields.Field() thumb_width: base.Integer = fields.Field() thumb_height: base.Integer = fields.Field() - foursquare_type: base.String = fields.Field() - def __init__(self, *, - id: base.String, - latitude: base.Float, - longitude: base.Float, - title: base.String, - address: base.String, - foursquare_id: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None, - foursquare_type: typing.Optional[base.String] = None): - super(InlineQueryResultVenue, self).__init__(id=id, latitude=latitude, longitude=longitude, - title=title, address=address, foursquare_id=foursquare_id, - reply_markup=reply_markup, - input_message_content=input_message_content, thumb_url=thumb_url, - thumb_width=thumb_width, thumb_height=thumb_height, - foursquare_type=foursquare_type) + def __init__( + self, + *, + id: base.String, + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Optional[base.String] = None, + foursquare_type: typing.Optional[base.String] = None, + google_place_id: typing.Optional[base.String] = None, + google_place_type: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + ): + super().__init__( + id=id, latitude=latitude, longitude=longitude, title=title, + address=address, foursquare_id=foursquare_id, + foursquare_type=foursquare_type, google_place_id=google_place_id, + google_place_type=google_place_type, reply_markup=reply_markup, + input_message_content=input_message_content, thumb_url=thumb_url, + thumb_width=thumb_width, thumb_height=thumb_height, + ) class InlineQueryResultContact(InlineQueryResult): diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 2ee2085b..a18d2b96 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -114,11 +114,24 @@ class InputVenueMessageContent(InputMessageContent): title: base.String = fields.Field() address: base.String = fields.Field() foursquare_id: base.String = fields.Field() + foursquare_type: base.String = fields.Field() + google_place_id: base.String = fields.Field() + google_place_type: base.String = fields.Field() - def __init__(self, latitude: typing.Optional[base.Float] = None, - longitude: typing.Optional[base.Float] = None, - title: typing.Optional[base.String] = None, - address: typing.Optional[base.String] = None, - foursquare_id: typing.Optional[base.String] = None): - super(InputVenueMessageContent, self).__init__(latitude=latitude, longitude=longitude, title=title, - address=address, foursquare_id=foursquare_id) + def __init__( + self, + latitude: typing.Optional[base.Float] = None, + longitude: typing.Optional[base.Float] = None, + title: typing.Optional[base.String] = None, + address: typing.Optional[base.String] = None, + foursquare_id: typing.Optional[base.String] = None, + foursquare_type: typing.Optional[base.String] = None, + google_place_id: typing.Optional[base.String] = None, + google_place_type: typing.Optional[base.String] = None, + ): + super().__init__( + latitude=latitude, longitude=longitude, title=title, + address=address, foursquare_id=foursquare_id, + foursquare_type=foursquare_type, google_place_id=google_place_id, + google_place_type=google_place_type, + ) diff --git a/aiogram/types/venue.py b/aiogram/types/venue.py index f7b2a277..b851650b 100644 --- a/aiogram/types/venue.py +++ b/aiogram/types/venue.py @@ -14,3 +14,5 @@ class Venue(base.TelegramObject): address: base.String = fields.Field() foursquare_id: base.String = fields.Field() foursquare_type: base.String = fields.Field() + google_place_id: base.String = fields.Field() + google_place_type: base.String = fields.Field() From 95932a7e905e1ede4a93d55bc494b695b9ff370d Mon Sep 17 00:00:00 2001 From: Oleg A Date: Fri, 6 Nov 2020 01:08:07 +0300 Subject: [PATCH 38/39] AIOG-T-93 Added the field allow_sending_without_reply to the methods --- aiogram/bot/bot.py | 165 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 156 insertions(+), 9 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 32f9117c..cd802a5e 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -262,6 +262,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): disable_web_page_preview: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -295,6 +296,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, @@ -421,6 +426,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -454,6 +460,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, @@ -485,6 +495,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): thumb: typing.Union[base.InputFile, base.String, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -533,6 +544,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, @@ -563,6 +578,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): disable_content_type_detection: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -610,6 +626,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): message :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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 @@ -644,6 +664,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): supports_streaming: typing.Union[base.Boolean, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -692,6 +713,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, @@ -724,6 +749,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): caption_entities: typing.Optional[typing.List[types.MessageEntity]] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -776,6 +802,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, @@ -805,6 +835,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): duration: typing.Union[base.Integer, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -845,6 +876,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, @@ -871,6 +906,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): thumb: typing.Union[base.InputFile, base.String, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -883,22 +919,34 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param video_note: Video note to send :type video_note: :obj:`typing.Union[base.InputFile, base.String]` + :param duration: Duration of sent video in seconds :type duration: :obj:`typing.Union[base.Integer, None]` + :param length: Video width and height :type length: :obj:`typing.Union[base.Integer, None]` + :param thumb: Thumbnail of the file sent :type thumb: :obj:`typing.Union[base.InputFile, base.String, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -916,6 +964,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): media: typing.Union[types.MediaGroup, typing.List], disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, ) -> typing.List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as @@ -941,6 +990,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): message :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :return: On success, an array of the sent Messages is returned :rtype: typing.List[types.Message] """ @@ -968,6 +1021,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): proximity_alert_radius: typing.Optional[base.Integer] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -1008,6 +1062,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, @@ -1127,6 +1185,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): google_place_type: typing.Optional[base.String] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -1174,6 +1233,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): message :type reply_to_message_id: :obj:`typing.Optional[base.Integer]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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 @@ -1196,6 +1259,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): vcard: typing.Union[base.String, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -1207,22 +1271,34 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param phone_number: Contact's phone number :type phone_number: :obj:`base.String` + :param first_name: Contact's first name :type first_name: :obj:`base.String` + :param last_name: Contact's last name :type last_name: :obj:`typing.Union[base.String, None]` + :param vcard: vcard :type vcard: :obj:`typing.Union[base.String, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -1252,6 +1328,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): is_closed: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -1321,6 +1398,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): message :type reply_to_message_id: :obj:`typing.Optional[Integer]` + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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 @@ -1345,10 +1426,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): disable_notification: typing.Union[base.Boolean, None] = None, emoji: typing.Union[base.String, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + types.ForceReply, None] = None, + ) -> types.Message: """ Use this method to send a dice, which will have a random value from 1 to 6. On success, the sent Message is returned. @@ -1357,18 +1440,31 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): Source: https://core.telegram.org/bots/api#senddice - :param chat_id: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the + target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` - :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of “🎲” or “🎯”. Defauts to “🎲” + + :param emoji: Emoji on which the dice throw animation is based. Currently, + must be one of “🎲” or “🎯”. Defaults to “🎲” :type emoji: :obj:`typing.Union[base.String, None]` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound + + :param disable_notification: Sends the message silently. Users will receive + a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` - :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 allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -2293,6 +2389,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): sticker: typing.Union[base.InputFile, base.String], disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, @@ -2304,16 +2401,25 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param sticker: Sticker to send :type sticker: :obj:`typing.Union[base.InputFile, base.String]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :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:`typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -2596,6 +2702,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): is_flexible: typing.Union[base.Boolean, None] = None, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, + allow_sending_without_reply: typing.Optional[base.Boolean] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None) -> types.Message: """ Use this method to send invoices. @@ -2604,54 +2711,81 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target private chat :type chat_id: :obj:`base.Integer` + :param title: Product name, 1-32 characters :type title: :obj:`base.String` + :param description: Product description, 1-255 characters :type description: :obj:`base.String` + :param payload: Bot-defined invoice payload, 1-128 bytes This will not be displayed to the user, use for your internal processes. :type payload: :obj:`base.String` + :param provider_token: Payments provider token, obtained via Botfather :type provider_token: :obj:`base.String` + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter :type start_parameter: :obj:`base.String` + :param currency: Three-letter ISO 4217 currency code, see more on currencies :type currency: :obj:`base.String` + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`typing.List[types.LabeledPrice]` + :param provider_data: JSON-encoded data about the invoice, which will be shared with the payment provider :type provider_data: :obj:`typing.Union[typing.Dict, None]` + :param photo_url: URL of the product photo for the invoice :type photo_url: :obj:`typing.Union[base.String, None]` + :param photo_size: Photo size :type photo_size: :obj:`typing.Union[base.Integer, None]` + :param photo_width: Photo width :type photo_width: :obj:`typing.Union[base.Integer, None]` + :param photo_height: Photo height :type photo_height: :obj:`typing.Union[base.Integer, None]` + :param need_name: Pass True, if you require the user's full name to complete the order :type need_name: :obj:`typing.Union[base.Boolean, None]` + :param need_phone_number: Pass True, if you require the user's phone number to complete the order :type need_phone_number: :obj:`typing.Union[base.Boolean, None]` + :param need_email: Pass True, if you require the user's email to complete the order :type need_email: :obj:`typing.Union[base.Boolean, None]` + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order :type need_shipping_address: :obj:`typing.Union[base.Boolean, None]` + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider :type send_phone_number_to_provider: :obj:`typing.Union[base.Boolean, None]` + :param send_email_to_provider: Pass True, if user's email address should be sent to provider :type send_email_to_provider: :obj:`typing.Union[base.Boolean, None]` + :param is_flexible: Pass True, if the final price depends on the shipping method :type is_flexible: :obj:`typing.Union[base.Boolean, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :param reply_markup: A JSON-serialized object for an inline keyboard If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button. :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ @@ -2759,10 +2893,14 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): # === Games === # https://core.telegram.org/bots/api#games - async def send_game(self, chat_id: base.Integer, game_short_name: base.String, + async def send_game(self, + chat_id: base.Integer, + game_short_name: base.String, disable_notification: typing.Union[base.Boolean, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None) -> types.Message: + allow_sending_without_reply: typing.Optional[base.Boolean] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message: """ Use this method to send a game. @@ -2770,16 +2908,25 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat :type chat_id: :obj:`base.Integer` - :param game_short_name: Short name of the game, serves as the unique identifier for the game. \ + + :param game_short_name: Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather. :type game_short_name: :obj:`base.String` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_to_message_id: If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`typing.Union[base.Integer, None]` + + :param allow_sending_without_reply: Pass True, if the message should be sent + even if the specified replied-to message is not found + :type allow_sending_without_reply: :obj:`typing.Optional[base.Boolean]` + :param reply_markup: A JSON-serialized object for an inline keyboard If empty, one ‘Play game_title’ button will be shown. If not empty, the first button must launch the game. :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, None]` + :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ From acaf2433cbf79d1ea4a3b5635392c63ca651d191 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Fri, 6 Nov 2020 01:14:27 +0300 Subject: [PATCH 39/39] AIOG-T-94 football and slot machine dice --- aiogram/bot/bot.py | 13 +++++++------ aiogram/types/dice.py | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index cd802a5e..6a09e13c 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1422,7 +1422,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_POLL, payload) return types.Message(**result) - async def send_dice(self, chat_id: typing.Union[base.Integer, base.String], + async def send_dice(self, + chat_id: typing.Union[base.Integer, base.String], disable_notification: typing.Union[base.Boolean, None] = None, emoji: typing.Union[base.String, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, @@ -1433,19 +1434,19 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): types.ForceReply, None] = None, ) -> types.Message: """ - Use this method to send a dice, which will have a random value from 1 to 6. + Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. - (Yes, we're aware of the “proper” singular of die. - But it's awkward, and we decided to help it change. One dice at a time!) Source: https://core.telegram.org/bots/api#senddice :param chat_id: Unique identifier for the target chat or username of the - target channel + target channel (in the format @channelusername) :type chat_id: :obj:`typing.Union[base.Integer, base.String]` :param emoji: Emoji on which the dice throw animation is based. Currently, - must be one of “🎲” or “🎯”. Defaults to “🎲” + must be one of “🎲”, “🎯”, “🏀”, “⚽”, or “🎰”. Dice can have values 1-6 + for “🎲” and “🎯”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. + Defaults to “🎲” :type emoji: :obj:`typing.Union[base.String, None]` :param disable_notification: Sends the message silently. Users will receive diff --git a/aiogram/types/dice.py b/aiogram/types/dice.py index 7b3f1727..70c50e09 100644 --- a/aiogram/types/dice.py +++ b/aiogram/types/dice.py @@ -17,3 +17,5 @@ class DiceEmoji: DICE = '🎲' DART = '🎯' BASKETBALL = '🏀' + FOOTBALL = '⚽' + SLOT_MACHINE = '🎰'