Merge branch 'dev-2.x'

This commit is contained in:
Alex Root Junior 2022-06-22 01:09:57 +03:00
commit a834ae5dd2
No known key found for this signature in database
GPG key ID: 074C1D455EBEA4AC
18 changed files with 336 additions and 49 deletions

View file

@ -21,7 +21,7 @@ AIOGramBot
:target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-6.0-blue.svg?style=flat-square&logo=telegram
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-6.1-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api
:alt: Telegram Bot API

View file

@ -43,5 +43,5 @@ __all__ = (
'utils',
)
__version__ = '2.20'
__api_version__ = '6.0'
__version__ = '2.21'
__api_version__ = '6.1'

View file

@ -287,6 +287,7 @@ class Methods(Helper):
# Payments
SEND_INVOICE = Item() # sendInvoice
CREATE_INVOICE_LINK = Item() # createInvoiceLink
ANSWER_SHIPPING_QUERY = Item() # answerShippingQuery
ANSWER_PRE_CHECKOUT_QUERY = Item() # answerPreCheckoutQuery

View file

@ -117,6 +117,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
max_connections: typing.Optional[base.Integer] = None,
allowed_updates: typing.Optional[typing.List[base.String]] = None,
drop_pending_updates: typing.Optional[base.Boolean] = None,
secret_token: typing.Optional[str] = None,
) -> base.Boolean:
"""
Use this method to specify a url and receive incoming updates via an outgoing
@ -165,6 +166,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
:param drop_pending_updates: Pass True to drop all pending updates
:type drop_pending_updates: :obj:`typing.Optional[base.Boolean]`
:param secret_token: A secret token to be sent in a header X-Telegram-Bot-Api-Secret-Token
in every webhook request, 1-256 characters. Only characters A-Z, a-z, 0-9, _ and - are allowed.
The header is useful to ensure that the request comes from a webhook set by you.
:type secret_token: :obj:`typing.Optional[str]`
:return: Returns true
:rtype: :obj:`base.Boolean`
"""
@ -3397,6 +3402,69 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
result = await self.request(api.Methods.SEND_INVOICE, payload_)
return types.Message(**result)
async def create_invoice_link(self,
title: base.String,
description: base.String,
payload: base.String,
provider_token: base.String,
currency: base.String,
prices: typing.List[types.LabeledPrice],
max_tip_amount: typing.Optional[int] = None,
suggested_tip_amounts: typing.Optional[typing.List[int]] = None,
provider_data: typing.Optional[base.String] = None,
photo_url: typing.Optional[str] = None,
photo_size: typing.Optional[int] = None,
photo_width: typing.Optional[int] = None,
photo_height: typing.Optional[int] = None,
need_name: typing.Optional[bool] = None,
need_phone_number: typing.Optional[bool] = None,
need_email: typing.Optional[bool] = None,
need_shipping_address: typing.Optional[bool] = None,
send_phone_number_to_provider: typing.Optional[bool] = None,
send_email_to_provider: typing.Optional[bool] = None,
is_flexible: typing.Optional[bool] = None,
) -> str:
"""
Use this method to create a link for an invoice. On success, the created link is returned.
Source: https://core.telegram.org/bots/api#createinvoicelink
:param title: Product name, 1-32 characters
:param description: Product description, 1-255 characters
:param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
:param provider_token: Payment provider token, obtained via BotFather
:param currency: Three-letter ISO 4217 currency code, see more on currencies
:param prices: Price breakdown, a JSON-serialized list of components
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
:param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency
(integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145.
See the exp parameter in currencies.json, it shows the number of digits past the decimal point for
each currency (2 for the majority of currencies). Defaults to 0
:param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest units
of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified.
The suggested tip amounts must be positive, passed in a strictly increased order and must not
exceed max_tip_amount.
:param provider_data: JSON-serialized data about the invoice, which will be shared with the payment provider.
A detailed description of required fields should be provided by the payment provider.
:param photo_url: URL of the product photo for the invoice.
Can be a photo of the goods or a marketing image for a service.
:param photo_size: Photo size in bytes
:param photo_width: Photo width
:param photo_height: Photo height
:param need_name: Pass True, if you require the user's full name to complete the order
:param need_phone_number: Pass True, if you require the user's phone number to complete the order
:param need_email: Pass True, if you require the user's email address to complete the order
:param need_shipping_address: Pass True, if you require the user's shipping address to complete the order
:param send_phone_number_to_provider: Pass True, if the user's phone number should be sent to the provider
:param send_email_to_provider: Pass True, if the user's email address should be sent to the provider
:param is_flexible: Pass True, if the final price depends on the shipping method
:return:
"""
prices = prepare_arg([price.to_python() if hasattr(price, 'to_python') else price for price in prices])
payload = generate_payload(**locals())
return await self.request(api.Methods.CREATE_INVOICE_LINK, payload)
async def answer_shipping_query(self, shipping_query_id: base.String, ok: base.Boolean,
shipping_options: typing.Union[typing.List[types.ShippingOption], None] = None,
error_message: typing.Optional[base.String] = None) -> base.Boolean:

View file

@ -1367,7 +1367,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
bucket = await self.storage.get_bucket(chat=chat_id, user=user_id)
if bucket and key in bucket:
del bucket['key']
del bucket[key]
await self.storage.set_bucket(chat=chat_id, user=user_id, bucket=bucket)
return True
return False

View file

@ -728,18 +728,20 @@ class ChatTypeFilter(BoundFilter):
self.chat_type: typing.Set[str] = set(chat_type)
async def check(self, obj: Union[Message, CallbackQuery, ChatMemberUpdated]):
async def check(self, obj: Union[Message, CallbackQuery, ChatMemberUpdated, InlineQuery]):
if isinstance(obj, Message):
obj = obj.chat
chat_type = obj.chat.type
elif isinstance(obj, CallbackQuery):
obj = obj.message.chat
chat_type = obj.message.chat.type if obj.message else None
elif isinstance(obj, ChatMemberUpdated):
obj = obj.chat
chat_type = obj.chat.type
elif isinstance(obj, InlineQuery):
chat_type = obj.chat_type
else:
warnings.warn("ChatTypeFilter doesn't support %s as input", type(obj))
return False
return obj.type in self.chat_type
return chat_type in self.chat_type
class MediaGroupFilter(BoundFilter):

View file

@ -76,8 +76,8 @@ class Handler:
"""
for handler_obj in self.handlers:
registered = handler_obj.handler
if handler is registered:
self.handlers.remove(handler_obj)
if handler in self.handlers:
self.handlers.remove(handler)
return True
raise ValueError('This handler is not registered!')

View file

@ -448,6 +448,17 @@ class DisableWebPagePreviewMixin:
return bot.disable_web_page_preview
class ProtectContentMixin:
def protect_content(self):
"""
Protect content
:return:
"""
setattr(self, "protect_content", True)
return self
class ParseModeMixin:
def as_html(self):
"""
@ -480,7 +491,9 @@ class ParseModeMixin:
return bot.parse_mode
class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificationMixin, DisableWebPagePreviewMixin):
class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin,
DisableNotificationMixin, DisableWebPagePreviewMixin,
ProtectContentMixin):
"""
You can send message with webhook by using this instance of this object.
All arguments is equal with Bot.send_message method.
@ -488,7 +501,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
__slots__ = ('chat_id', 'text', 'parse_mode',
'disable_web_page_preview', 'disable_notification',
'reply_to_message_id', 'reply_markup')
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_MESSAGE
@ -497,6 +510,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
parse_mode: Optional[String] = None,
disable_web_page_preview: Optional[Boolean] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -509,6 +523,8 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
:param disable_web_page_preview: Boolean (Optional) - Disables link previews for links in this message
:param disable_notification: Boolean (Optional) - Sends the message silently. Users will receive
a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -526,6 +542,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
self.parse_mode = parse_mode
self.disable_web_page_preview = disable_web_page_preview
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -536,6 +553,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
@ -565,18 +583,19 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
return self
class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for forward messages of any kind on to webhook.
"""
__slots__ = ('chat_id', 'from_chat_id', 'message_id', 'disable_notification')
__slots__ = ('chat_id', 'from_chat_id', 'message_id', 'disable_notification', 'protect_content')
method = api.Methods.FORWARD_MESSAGE
def __init__(self, chat_id: Union[Integer, String] = None,
from_chat_id: Union[Integer, String] = None,
message_id: Integer = None,
disable_notification: Optional[Boolean] = None):
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None):
"""
:param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the
target channel (in the format @channelusername)
@ -584,12 +603,15 @@ class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin):
message was sent (or channel username in the format @channelusername)
:param disable_notification: Boolean (Optional) - Sends the message silently. Users will receive a
notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param message_id: Integer - Message identifier in the chat specified in from_chat_id
"""
self.chat_id = chat_id
self.from_chat_id = from_chat_id
self.message_id = message_id
self.disable_notification = disable_notification
self.protect_content = protect_content
def message(self, message: types.Message):
"""
@ -607,16 +629,18 @@ class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'chat_id': self.chat_id,
'from_chat_id': self.from_chat_id,
'message_id': self.message_id,
'disable_notification': self.disable_notification
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
}
class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send photo on to webhook.
"""
__slots__ = ('chat_id', 'photo', 'caption', 'disable_notification', 'reply_to_message_id', 'reply_markup')
__slots__ = ('chat_id', 'photo', 'caption', 'disable_notification',
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_PHOTO
@ -624,6 +648,7 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
photo: String,
caption: Optional[String] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -637,6 +662,8 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
0-1024 characters after entities parsing
:param disable_notification: Boolean (Optional) - Sends the message silently. Users will receive
a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -646,6 +673,7 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.photo = photo
self.caption = caption
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -655,18 +683,20 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'photo': self.photo,
'caption': self.caption,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send audio on to webhook.
"""
__slots__ = ('chat_id', 'audio', 'caption', 'duration', 'performer', 'title',
'disable_notification', 'reply_to_message_id', 'reply_markup')
'disable_notification', 'protect_content',
'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_AUDIO
@ -677,6 +707,7 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
performer: Optional[String] = None,
title: Optional[String] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -693,6 +724,8 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param title: String (Optional) - Track name
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -705,6 +738,7 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.performer = performer
self.title = title
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -717,17 +751,19 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'performer': self.performer,
'title': self.title,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send document on to webhook.
"""
__slots__ = ('chat_id', 'document', 'caption', 'disable_notification', 'reply_to_message_id', 'reply_markup')
__slots__ = ('chat_id', 'document', 'caption', 'disable_notification',
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_DOCUMENT
@ -735,6 +771,7 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
document: String,
caption: Optional[String] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -749,6 +786,8 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
(may also be used when resending documents by file_id), 0-1024 characters after entities parsing
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -758,6 +797,7 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.document = document
self.caption = caption
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -767,17 +807,19 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'document': self.document,
'caption': self.caption,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send video on to webhook.
"""
__slots__ = ('chat_id', 'video', 'duration', 'width', 'height', 'caption', 'disable_notification',
__slots__ = ('chat_id', 'video', 'duration', 'width', 'height', 'caption',
'disable_notification', 'protect_content',
'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_VIDEO
@ -789,6 +831,7 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
height: Optional[Integer] = None,
caption: Optional[String] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -806,6 +849,8 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
0-1024 characters after entities parsing
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -818,6 +863,7 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.height = height
self.caption = caption
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -830,18 +876,19 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'height': self.height,
'caption': self.caption,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send voice on to webhook.
"""
__slots__ = ('chat_id', 'voice', 'caption', 'duration', 'disable_notification',
'reply_to_message_id', 'reply_markup')
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_VOICE
@ -850,6 +897,7 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
caption: Optional[String] = None,
duration: Optional[Integer] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -864,6 +912,8 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param duration: Integer (Optional) - Duration of the voice message in seconds
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard,
@ -874,6 +924,7 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.caption = caption
self.duration = duration
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -884,18 +935,19 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'caption': self.caption,
'duration': self.duration,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send video note on to webhook.
"""
__slots__ = ('chat_id', 'video_note', 'duration', 'length', 'disable_notification',
'reply_to_message_id', 'reply_markup')
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_VIDEO_NOTE
@ -904,6 +956,7 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
duration: Optional[Integer] = None,
length: Optional[Integer] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -917,6 +970,8 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param length: Integer (Optional) - Video width and height
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -927,6 +982,7 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.duration = duration
self.length = length
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -937,23 +993,26 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'duration': self.duration,
'length': self.length,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use this method to send a group of photos or videos as an album.
"""
__slots__ = ('chat_id', 'media', 'disable_notification', 'reply_to_message_id')
__slots__ = ('chat_id', 'media', 'disable_notification',
'protect_content', 'reply_to_message_id')
method = api.Methods.SEND_MEDIA_GROUP
def __init__(self, chat_id: Union[Integer, String],
media: Union[types.MediaGroup, List] = None,
disable_notification: typing.Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: typing.Optional[Integer] = None):
"""
Use this method to send a group of photos or videos as an album.
@ -966,6 +1025,8 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
: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.Optional[base.Boolean]`
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
: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]`
:return: On success, an array of the sent Messages is returned.
@ -980,6 +1041,7 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.chat_id = chat_id
self.media = media
self.disable_notifications = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
def prepare(self):
@ -993,6 +1055,7 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'chat_id': self.chat_id,
'media': media,
'disable_notifications': self.disable_notifications,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id
}
@ -1023,18 +1086,20 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
return self
class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send location on to webhook.
"""
__slots__ = ('chat_id', 'latitude', 'longitude', 'disable_notification', 'reply_to_message_id', 'reply_markup')
__slots__ = ('chat_id', 'latitude', 'longitude', 'disable_notification',
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_LOCATION
def __init__(self, chat_id: Union[Integer, String],
latitude: Float, longitude: Float,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -1045,6 +1110,8 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param longitude: Float - Longitude of location
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -1054,6 +1121,7 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.latitude = latitude
self.longitude = longitude
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -1063,18 +1131,21 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'latitude': self.latitude,
'longitude': self.longitude,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send venue on to webhook.
"""
__slots__ = ('chat_id', 'latitude', 'longitude', 'title', 'address', 'foursquare_id',
'disable_notification', 'reply_to_message_id', 'reply_markup')
__slots__ = ('chat_id', 'latitude', 'longitude', 'title',
'address', 'foursquare_id',
'disable_notification', 'protect_content',
'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_VENUE
@ -1085,6 +1156,7 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
address: String,
foursquare_id: Optional[String] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -1098,6 +1170,8 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param foursquare_id: String (Optional) - Foursquare identifier of the venue
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -1110,6 +1184,7 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.address = address
self.foursquare_id = foursquare_id
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -1122,18 +1197,19 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'address': self.address,
'foursquare_id': self.foursquare_id,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send contact on to webhook.
"""
__slots__ = ('chat_id', 'phone_number', 'first_name', 'last_name', 'disable_notification',
'reply_to_message_id', 'reply_markup')
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_CONTACT
@ -1142,6 +1218,7 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
first_name: String,
last_name: Optional[String] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[Union[
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
@ -1153,6 +1230,8 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param last_name: String (Optional) - Contact's last name
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
- Additional interface options. A JSON-serialized object for an inline keyboard,
@ -1163,6 +1242,7 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.first_name = first_name
self.last_name = last_name
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -1173,6 +1253,7 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'first_name': self.first_name,
'last_name': self.last_name,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1730,18 +1811,20 @@ class DeleteMessage(BaseResponse):
}
class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send sticker on to webhook.
"""
__slots__ = ('chat_id', 'sticker', 'disable_notification', 'reply_to_message_id', 'reply_markup')
__slots__ = ('chat_id', 'sticker', 'disable_notification', 'protect_content',
'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_STICKER
def __init__(self, chat_id: Union[Integer, String],
sticker: String,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[
Union[types.InlineKeyboardMarkup,
@ -1755,6 +1838,8 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
or upload a new one using multipart/form-data. More info on Sending Files »
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional) -
Additional interface options. A JSON-serialized object for an inline keyboard,
@ -1763,6 +1848,7 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.chat_id = chat_id
self.sticker = sticker
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -1771,6 +1857,7 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'chat_id': self.chat_id,
'sticker': self.sticker,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1985,7 +2072,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'currency', 'prices', 'photo_url', 'photo_size', 'photo_width', 'photo_height',
'need_name', 'need_phone_number', 'need_email', 'need_shipping_address',
'send_phone_number_to_provider', 'send_email_to_provider', 'is_flexible',
'disable_notification', 'reply_to_message_id', 'reply_markup')
'disable_notification', 'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_INVOICE
@ -2009,6 +2096,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
send_email_to_provider: Optional[Boolean] = None,
is_flexible: Optional[Boolean] = None,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[types.InlineKeyboardMarkup] = None):
"""
@ -2042,6 +2130,8 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param is_flexible: Boolean (Optional) - Pass True, if the final price depends on the shipping method
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: types.InlineKeyboardMarkup (Optional) - 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.
@ -2066,6 +2156,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.send_email_to_provider = send_email_to_provider
self.is_flexible = is_flexible
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -2091,6 +2182,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'send_email_to_provider': self.send_email_to_provider,
'is_flexible': self.is_flexible,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}
@ -2168,18 +2260,20 @@ class AnswerPreCheckoutQuery(BaseResponse):
}
class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
"""
Use that response type for send game on to webhook.
"""
__slots__ = ('chat_id', 'game_short_name', 'disable_notification', 'reply_to_message_id', 'reply_markup')
__slots__ = ('chat_id', 'game_short_name', 'disable_notification',
'protect_content', 'reply_to_message_id', 'reply_markup')
method = api.Methods.SEND_GAME
def __init__(self, chat_id: Integer,
game_short_name: String,
disable_notification: Optional[Boolean] = None,
protect_content: Optional[Boolean] = None,
reply_to_message_id: Optional[Integer] = None,
reply_markup: Optional[types.InlineKeyboardMarkup] = None):
"""
@ -2188,6 +2282,8 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
Set up your games via Botfather.
:param disable_notification: Boolean (Optional) - Sends the message silently.
Users will receive a notification with no sound.
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
from forwarding and saving
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
:param reply_markup: types.InlineKeyboardMarkup (Optional) - 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.
@ -2195,6 +2291,7 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
self.chat_id = chat_id
self.game_short_name = game_short_name
self.disable_notification = disable_notification
self.protect_content = protect_content
self.reply_to_message_id = reply_to_message_id
self.reply_markup = reply_markup
@ -2203,6 +2300,7 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'chat_id': self.chat_id,
'game_short_name': self.game_short_name,
'disable_notification': self.disable_notification,
'protect_content': self.protect_content,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup),
}

View file

@ -3,12 +3,14 @@ from __future__ import annotations
import io
import logging
import typing
import warnings
from typing import TypeVar
from babel.support import LazyProxy
from .fields import BaseField
from ..utils import json
from ..utils.exceptions import AIOGramWarning
from ..utils.mixins import ContextInstanceMixin
if typing.TYPE_CHECKING:
from ..bot.bot import Bot
@ -243,8 +245,10 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject):
return self.props[key].set_value(self, value, self.conf.get('parent', self))
self.values[key] = value
# Log warning when Telegram silently adds new Fields
log.warning("Field '%s' doesn't exist in %s", key, self.__class__)
# Show warning when Telegram silently adds new Fields
warnings.warn(f"Bot API Field {key!r} is not defined in {self.__class__!r} class.\n"
"Bot API has been updated. Check for updates at https://telegram.org/blog and "
"https://github.com/aiogram/aiogram/releases", AIOGramWarning)
def __contains__(self, item: str) -> bool:
"""

View file

@ -31,6 +31,8 @@ class Chat(base.TelegramObject):
photo: ChatPhoto = fields.Field(base=ChatPhoto)
bio: base.String = fields.Field()
has_private_forwards: base.Boolean = fields.Field()
join_to_send_messages: base.Boolean = fields.Field()
join_by_request: base.Boolean = fields.Field()
description: base.String = fields.Field()
invite_link: base.String = fields.Field()
pinned_message: 'Message' = fields.Field(base='Message')
@ -652,6 +654,7 @@ class ChatType(helper.Helper):
"""
List of chat types
:key: SENDER
:key: PRIVATE
:key: GROUP
:key: SUPER_GROUP
@ -661,6 +664,7 @@ class ChatType(helper.Helper):
mode = helper.HelperMode.lowercase
SENDER = helper.Item() # sender
PRIVATE = helper.Item() # private
GROUP = helper.Item() # group
SUPERGROUP = helper.Item() # supergroup

View file

@ -1,6 +1,7 @@
import abc
import datetime
import weakref
import sys
__all__ = ('BaseField', 'Field', 'ListField', 'DateTimeField', 'TextField', 'ListOfLists', 'ConstField')
@ -168,8 +169,13 @@ class DateTimeField(Field):
out: datetime
"""
def serialize(self, value: datetime.datetime):
return round(value.timestamp())
if sys.platform == "win32":
def serialize(self, value: datetime.datetime):
return round((value - datetime.datetime(1970, 1, 1)).total_seconds())
else:
def serialize(self, value: datetime.datetime):
return round(value.timestamp())
def deserialize(self, value, parent=None):
return datetime.datetime.fromtimestamp(value)

View file

@ -270,6 +270,15 @@ class Message(base.TelegramObject):
return text_decorator.unparse(text, entities)
@property
def from_id(self) -> int:
"""
User id if sent by user or chat/channel id if sent on behalf of a channel or chat
:return: int
"""
return self.sender_chat.id if self.sender_chat else self.from_user.id
@property
def md_text(self) -> str:
"""

View file

@ -3,7 +3,7 @@ from . import fields
from . import mixins
from .mask_position import MaskPosition
from .photo_size import PhotoSize
from .file import File
class Sticker(base.TelegramObject, mixins.Downloadable):
"""
@ -20,6 +20,7 @@ class Sticker(base.TelegramObject, mixins.Downloadable):
thumb: PhotoSize = fields.Field(base=PhotoSize)
emoji: base.String = fields.Field()
set_name: base.String = fields.Field()
premium_animation: File = fields.Field(base=File)
mask_position: MaskPosition = fields.Field(base=MaskPosition)
file_size: base.Integer = fields.Field()

View file

@ -22,6 +22,8 @@ class User(base.TelegramObject):
last_name: base.String = fields.Field()
username: base.String = fields.Field()
language_code: base.String = fields.Field()
is_premium: base.Boolean = fields.Field()
added_to_attachment_menu: base.Boolean = fields.Field()
can_join_groups: base.Boolean = fields.Field()
can_read_all_group_messages: base.Boolean = fields.Field()
supports_inline_queries: base.Boolean = fields.Field()

View file

@ -1,3 +1,5 @@
import warnings
try:
import emoji
except ImportError:
@ -5,8 +7,14 @@ except ImportError:
def emojize(text):
warnings.warn(message="'aiogram.utils.emoji' module deprecated, use emoji symbols directly instead,"
"this function will be removed in aiogram v2.22",
category=DeprecationWarning, stacklevel=2)
return emoji.emojize(text, use_aliases=True)
def demojize(text):
warnings.warn(message="'aiogram.utils.emoji' module deprecated, use emoji symbols directly instead,"
"this function will be removed in aiogram v2.22",
category=DeprecationWarning, stacklevel=2)
return emoji.demojize(text)

67
aiogram/utils/web_app.py Normal file
View file

@ -0,0 +1,67 @@
import hashlib
import hmac
from operator import itemgetter
from typing import Callable, Any, Dict
from urllib.parse import parse_qsl
def check_webapp_signature(token: str, init_data: str) -> bool:
"""
Check incoming WebApp init data signature
Source: https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
:param token:
:param init_data:
:return:
"""
try:
parsed_data = dict(parse_qsl(init_data))
except ValueError:
# Init data is not a valid query string
return False
if "hash" not in parsed_data:
# Hash is not present in init data
return False
hash_ = parsed_data.pop('hash')
data_check_string = "\n".join(
f"{k}={v}" for k, v in sorted(parsed_data.items(), key=itemgetter(0))
)
secret_key = hmac.new(
key=b"WebAppData", msg=token.encode(), digestmod=hashlib.sha256
)
calculated_hash = hmac.new(
key=secret_key.digest(), msg=data_check_string.encode(), digestmod=hashlib.sha256
).hexdigest()
return calculated_hash == hash_
def parse_init_data(init_data: str, _loads: Callable[..., Any]) -> Dict[str, Any]:
"""
Parse WebApp init data and return it as dict
:param init_data:
:param _loads:
:return:
"""
result = {}
for key, value in parse_qsl(init_data):
if (value.startswith('[') and value.endswith(']')) or (value.startswith('{') and value.endswith('}')):
value = _loads(value)
result[key] = value
return result
def safe_parse_webapp_init_data(token: str, init_data: str, _loads: Callable[..., Any]) -> Dict[str, Any]:
"""
Validate WebApp init data and return it as dict
:param token:
:param init_data:
:param _loads:
:return:
"""
if check_webapp_signature(token, init_data):
return parse_init_data(init_data, _loads)
raise ValueError("Invalid init data signature")

View file

@ -22,7 +22,7 @@ Welcome to aiogram's documentation!
:target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-6.0-blue.svg?style=flat-square&logo=telegram
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-6.1-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api
:alt: Telegram Bot API

17
message.py Normal file
View file

@ -0,0 +1,17 @@
import asyncio
import secrets
from aiogram import Bot
async def main():
bot = Bot(token="5115369270:AAFlipWd1qbhc7cIe0nRM-SyGLkTC_9Ulgg")
index = 0
while True:
index += 1
print(index)
await bot.send_message(chat_id=879238251, text=secrets.token_urlsafe(24))
await asyncio.sleep(.2)
asyncio.run(main())