mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-06 16:15:51 +00:00
Added full support of Telegram Bot API 6.1;
Ported web-app utils; Deprecated emoji module;
This commit is contained in:
parent
7940613ec0
commit
3b66a2b2e0
8 changed files with 167 additions and 1 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
67
aiogram/utils/web_app.py
Normal 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")
|
||||
17
message.py
Normal file
17
message.py
Normal 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())
|
||||
Loading…
Add table
Add a link
Reference in a new issue