mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-06 07:50:32 +00:00
[3.x] Bot API 6.0 (#890)
* Base implementation * Bump license * Revert re-generated tests * Fix tests, improved docs * Remove TODO * Removed unreachable code * Changed type of `last_synchronization_error_date` * Fixed wrongly cleaned code
This commit is contained in:
parent
286cf39c8a
commit
497436595d
81 changed files with 1942 additions and 147 deletions
|
|
@ -12,3 +12,4 @@ python:
|
|||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
- redis
|
||||
|
|
|
|||
1
CHANGES/890.feature.rst
Normal file
1
CHANGES/890.feature.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
Added full support of `Telegram Bot API 6.0 <https://core.telegram.org/bots/api-changelog#april-16-2022>`_
|
||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2017-2019 Alex Root Junior
|
||||
Copyright (c) 2017-2022 Alex Root Junior
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from ..methods import (
|
|||
AnswerInlineQuery,
|
||||
AnswerPreCheckoutQuery,
|
||||
AnswerShippingQuery,
|
||||
AnswerWebAppQuery,
|
||||
ApproveChatJoinRequest,
|
||||
BanChatMember,
|
||||
BanChatSenderChat,
|
||||
|
|
@ -54,10 +55,12 @@ from ..methods import (
|
|||
GetChatMember,
|
||||
GetChatMemberCount,
|
||||
GetChatMembersCount,
|
||||
GetChatMenuButton,
|
||||
GetFile,
|
||||
GetGameHighScores,
|
||||
GetMe,
|
||||
GetMyCommands,
|
||||
GetMyDefaultAdministratorRights,
|
||||
GetStickerSet,
|
||||
GetUpdates,
|
||||
GetUserProfilePhotos,
|
||||
|
|
@ -89,12 +92,14 @@ from ..methods import (
|
|||
SendVoice,
|
||||
SetChatAdministratorCustomTitle,
|
||||
SetChatDescription,
|
||||
SetChatMenuButton,
|
||||
SetChatPermissions,
|
||||
SetChatPhoto,
|
||||
SetChatStickerSet,
|
||||
SetChatTitle,
|
||||
SetGameScore,
|
||||
SetMyCommands,
|
||||
SetMyDefaultAdministratorRights,
|
||||
SetPassportDataErrors,
|
||||
SetStickerPositionInSet,
|
||||
SetStickerSetThumb,
|
||||
|
|
@ -113,6 +118,7 @@ from ..types import (
|
|||
BotCommand,
|
||||
BotCommandScope,
|
||||
Chat,
|
||||
ChatAdministratorRights,
|
||||
ChatInviteLink,
|
||||
ChatMemberAdministrator,
|
||||
ChatMemberBanned,
|
||||
|
|
@ -135,6 +141,7 @@ from ..types import (
|
|||
InputMediaVideo,
|
||||
LabeledPrice,
|
||||
MaskPosition,
|
||||
MenuButton,
|
||||
Message,
|
||||
MessageEntity,
|
||||
MessageId,
|
||||
|
|
@ -142,6 +149,7 @@ from ..types import (
|
|||
Poll,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
SentWebAppMessage,
|
||||
ShippingOption,
|
||||
StickerSet,
|
||||
Update,
|
||||
|
|
@ -1618,7 +1626,7 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
can_post_messages: Optional[bool] = None,
|
||||
can_edit_messages: Optional[bool] = None,
|
||||
can_delete_messages: Optional[bool] = None,
|
||||
can_manage_voice_chats: Optional[bool] = None,
|
||||
can_manage_video_chats: Optional[bool] = None,
|
||||
can_restrict_members: Optional[bool] = None,
|
||||
can_promote_members: Optional[bool] = None,
|
||||
can_change_info: Optional[bool] = None,
|
||||
|
|
@ -1638,7 +1646,7 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
:param can_post_messages: Pass :code:`True`, if the administrator can create channel posts, channels only
|
||||
:param can_edit_messages: Pass :code:`True`, if the administrator can edit messages of other users and can pin messages, channels only
|
||||
:param can_delete_messages: Pass :code:`True`, if the administrator can delete messages of other users
|
||||
:param can_manage_voice_chats: Pass :code:`True`, if the administrator can manage voice chats
|
||||
:param can_manage_video_chats: Pass :code:`True`, if the administrator can manage video chats
|
||||
:param can_restrict_members: Pass :code:`True`, if the administrator can restrict, ban or unban chat members
|
||||
:param can_promote_members: Pass :code:`True`, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him)
|
||||
:param can_change_info: Pass :code:`True`, if the administrator can change chat title, photo and other settings
|
||||
|
|
@ -1655,7 +1663,7 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
can_post_messages=can_post_messages,
|
||||
can_edit_messages=can_edit_messages,
|
||||
can_delete_messages=can_delete_messages,
|
||||
can_manage_voice_chats=can_manage_voice_chats,
|
||||
can_manage_video_chats=can_manage_video_chats,
|
||||
can_restrict_members=can_restrict_members,
|
||||
can_promote_members=can_promote_members,
|
||||
can_change_info=can_change_info,
|
||||
|
|
@ -2344,6 +2352,88 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def set_chat_menu_button(
|
||||
self,
|
||||
chat_id: Optional[int] = None,
|
||||
menu_button: Optional[MenuButton] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to change the bot's menu button in a private chat, or the default menu button. Returns :code:`True` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setchatmenubutton
|
||||
|
||||
:param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed
|
||||
:param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to :class:`aiogram.types.menu_button_default.MenuButtonDefault`
|
||||
:param request_timeout: Request timeout
|
||||
:return: Returns True on success.
|
||||
"""
|
||||
call = SetChatMenuButton(
|
||||
chat_id=chat_id,
|
||||
menu_button=menu_button,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def get_chat_menu_button(
|
||||
self,
|
||||
chat_id: Optional[int] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> MenuButton:
|
||||
"""
|
||||
Use this method to get the current value of the bot's menu button in a private chat, or the default menu button. Returns :class:`aiogram.types.menu_button.MenuButton` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getchatmenubutton
|
||||
|
||||
:param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be returned
|
||||
:param request_timeout: Request timeout
|
||||
:return: Returns MenuButton on success.
|
||||
"""
|
||||
call = GetChatMenuButton(
|
||||
chat_id=chat_id,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def set_my_default_administrator_rights(
|
||||
self,
|
||||
rights: Optional[ChatAdministratorRights] = None,
|
||||
for_channels: Optional[bool] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify the list before adding the bot. Returns :code:`True` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setmydefaultadministratorrights
|
||||
|
||||
:param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared.
|
||||
:param for_channels: Pass :code:`True` to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed.
|
||||
:param request_timeout: Request timeout
|
||||
:return: Returns True on success.
|
||||
"""
|
||||
call = SetMyDefaultAdministratorRights(
|
||||
rights=rights,
|
||||
for_channels=for_channels,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def get_my_default_administrator_rights(
|
||||
self,
|
||||
for_channels: Optional[bool] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> ChatAdministratorRights:
|
||||
"""
|
||||
Use this method to get the current default administrator rights of the bot. Returns :class:`aiogram.types.chat_administrator_rights.ChatAdministratorRights` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getmydefaultadministratorrights
|
||||
|
||||
:param for_channels: Pass :code:`True` to get default administrator rights of the bot in channels. Otherwise, default administrator rights of the bot for groups and supergroups will be returned.
|
||||
:param request_timeout: Request timeout
|
||||
:return: Returns ChatAdministratorRights on success.
|
||||
"""
|
||||
call = GetMyDefaultAdministratorRights(
|
||||
for_channels=for_channels,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
# =============================================================================================
|
||||
# Group: Updating messages
|
||||
# Source: https://core.telegram.org/bots/api#updating-messages
|
||||
|
|
@ -2656,7 +2746,7 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
Source: https://core.telegram.org/bots/api#createnewstickerset
|
||||
|
||||
:param user_id: User identifier of created sticker set owner
|
||||
:param name: Short name of sticker set, to be used in :code:`t.me/addstickers/` URLs (e.g., *animals*). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in *'_by_<bot username>'*. *<bot_username>* is case insensitive. 1-64 characters.
|
||||
:param name: Short name of sticker set, to be used in :code:`t.me/addstickers/` URLs (e.g., *animals*). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in :code:`"_by_<bot_username>"`. :code:`<bot_username>` is case insensitive. 1-64 characters.
|
||||
:param title: Sticker set title, 1-64 characters
|
||||
:param emojis: One or more emoji corresponding to the sticker
|
||||
:param png_sticker: **PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » <sending-files>`
|
||||
|
|
@ -2827,6 +2917,28 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def answer_web_app_query(
|
||||
self,
|
||||
web_app_query_id: str,
|
||||
result: InlineQueryResult,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> SentWebAppMessage:
|
||||
"""
|
||||
Use this method to set the result of an interaction with a `Web App <https://core.telegram.org/bots/webapps>`_ and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a :class:`aiogram.types.sent_web_app_message.SentWebAppMessage` object is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#answerwebappquery
|
||||
|
||||
:param web_app_query_id: Unique identifier for the query to be answered
|
||||
:param result: A JSON-serialized object describing the message to be sent
|
||||
:param request_timeout: Request timeout
|
||||
:return: On success, a SentWebAppMessage object is returned.
|
||||
"""
|
||||
call = AnswerWebAppQuery(
|
||||
web_app_query_id=web_app_query_id,
|
||||
result=result,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
# =============================================================================================
|
||||
# Group: Payments
|
||||
# Source: https://core.telegram.org/bots/api#payments
|
||||
|
|
|
|||
|
|
@ -80,20 +80,20 @@ class Dispatcher(Router):
|
|||
self.update.outer_middleware(self.fsm)
|
||||
self.shutdown.register(self.fsm.close)
|
||||
|
||||
self._data: Dict[str, Any] = {}
|
||||
self.workflow_data: Dict[str, Any] = {}
|
||||
self._running_lock = Lock()
|
||||
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
return self._data[item]
|
||||
return self.workflow_data[item]
|
||||
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
self._data[key] = value
|
||||
self.workflow_data[key] = value
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
del self._data[key]
|
||||
del self.workflow_data[key]
|
||||
|
||||
def get(self, key: str, /, default: Optional[Any] = None) -> Optional[Any]:
|
||||
return self._data.get(key, default)
|
||||
return self.workflow_data.get(key, default)
|
||||
|
||||
@property
|
||||
def storage(self) -> BaseStorage:
|
||||
|
|
@ -136,7 +136,7 @@ class Dispatcher(Router):
|
|||
self.update.trigger,
|
||||
update,
|
||||
{
|
||||
**self._data,
|
||||
**self.workflow_data,
|
||||
**kwargs,
|
||||
"bot": bot,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from aiogram.dispatcher.filters import BaseFilter
|
|||
from aiogram.types import CallbackQuery, InlineQuery, Message, Poll
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from aiogram.utils.i18n.lazy_proxy import LazyProxy
|
||||
from aiogram.utils.i18n.lazy_proxy import LazyProxy # NOQA
|
||||
|
||||
TextType = Union[str, "LazyProxy"]
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ def setup_application(app: Application, dispatcher: Dispatcher, /, **kwargs: Any
|
|||
"app": app,
|
||||
"dispatcher": dispatcher,
|
||||
**kwargs,
|
||||
**dispatcher.workflow_data,
|
||||
}
|
||||
|
||||
async def on_startup(*a: Any, **kw: Any) -> None: # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from .answer_callback_query import AnswerCallbackQuery
|
|||
from .answer_inline_query import AnswerInlineQuery
|
||||
from .answer_pre_checkout_query import AnswerPreCheckoutQuery
|
||||
from .answer_shipping_query import AnswerShippingQuery
|
||||
from .answer_web_app_query import AnswerWebAppQuery
|
||||
from .approve_chat_join_request import ApproveChatJoinRequest
|
||||
from .ban_chat_member import BanChatMember
|
||||
from .ban_chat_sender_chat import BanChatSenderChat
|
||||
|
|
@ -31,10 +32,12 @@ from .get_chat_administrators import GetChatAdministrators
|
|||
from .get_chat_member import GetChatMember
|
||||
from .get_chat_member_count import GetChatMemberCount
|
||||
from .get_chat_members_count import GetChatMembersCount
|
||||
from .get_chat_menu_button import GetChatMenuButton
|
||||
from .get_file import GetFile
|
||||
from .get_game_high_scores import GetGameHighScores
|
||||
from .get_me import GetMe
|
||||
from .get_my_commands import GetMyCommands
|
||||
from .get_my_default_administrator_rights import GetMyDefaultAdministratorRights
|
||||
from .get_sticker_set import GetStickerSet
|
||||
from .get_updates import GetUpdates
|
||||
from .get_user_profile_photos import GetUserProfilePhotos
|
||||
|
|
@ -66,12 +69,14 @@ from .send_video_note import SendVideoNote
|
|||
from .send_voice import SendVoice
|
||||
from .set_chat_administrator_custom_title import SetChatAdministratorCustomTitle
|
||||
from .set_chat_description import SetChatDescription
|
||||
from .set_chat_menu_button import SetChatMenuButton
|
||||
from .set_chat_permissions import SetChatPermissions
|
||||
from .set_chat_photo import SetChatPhoto
|
||||
from .set_chat_sticker_set import SetChatStickerSet
|
||||
from .set_chat_title import SetChatTitle
|
||||
from .set_game_score import SetGameScore
|
||||
from .set_my_commands import SetMyCommands
|
||||
from .set_my_default_administrator_rights import SetMyDefaultAdministratorRights
|
||||
from .set_passport_data_errors import SetPassportDataErrors
|
||||
from .set_sticker_position_in_set import SetStickerPositionInSet
|
||||
from .set_sticker_set_thumb import SetStickerSetThumb
|
||||
|
|
@ -150,6 +155,10 @@ __all__ = (
|
|||
"SetMyCommands",
|
||||
"DeleteMyCommands",
|
||||
"GetMyCommands",
|
||||
"SetChatMenuButton",
|
||||
"GetChatMenuButton",
|
||||
"SetMyDefaultAdministratorRights",
|
||||
"GetMyDefaultAdministratorRights",
|
||||
"EditMessageText",
|
||||
"EditMessageCaption",
|
||||
"EditMessageMedia",
|
||||
|
|
@ -165,6 +174,7 @@ __all__ = (
|
|||
"DeleteStickerFromSet",
|
||||
"SetStickerSetThumb",
|
||||
"AnswerInlineQuery",
|
||||
"AnswerWebAppQuery",
|
||||
"SendInvoice",
|
||||
"AnswerShippingQuery",
|
||||
"AnswerPreCheckoutQuery",
|
||||
|
|
|
|||
31
aiogram/methods/answer_web_app_query.py
Normal file
31
aiogram/methods/answer_web_app_query.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict
|
||||
|
||||
from ..types import InlineQueryResult, SentWebAppMessage
|
||||
from .base import Request, TelegramMethod, prepare_parse_mode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class AnswerWebAppQuery(TelegramMethod[SentWebAppMessage]):
|
||||
"""
|
||||
Use this method to set the result of an interaction with a `Web App <https://core.telegram.org/bots/webapps>`_ and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a :class:`aiogram.types.sent_web_app_message.SentWebAppMessage` object is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#answerwebappquery
|
||||
"""
|
||||
|
||||
__returning__ = SentWebAppMessage
|
||||
|
||||
web_app_query_id: str
|
||||
"""Unique identifier for the query to be answered"""
|
||||
result: InlineQueryResult
|
||||
"""A JSON-serialized object describing the message to be sent"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
prepare_parse_mode(
|
||||
bot, data["result"], parse_mode_property="parse_mode", entities_property="entities"
|
||||
)
|
||||
return Request(method="answerWebAppQuery", data=data)
|
||||
|
|
@ -21,7 +21,7 @@ class CreateNewStickerSet(TelegramMethod[bool]):
|
|||
user_id: int
|
||||
"""User identifier of created sticker set owner"""
|
||||
name: str
|
||||
"""Short name of sticker set, to be used in :code:`t.me/addstickers/` URLs (e.g., *animals*). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in *'_by_<bot username>'*. *<bot_username>* is case insensitive. 1-64 characters."""
|
||||
"""Short name of sticker set, to be used in :code:`t.me/addstickers/` URLs (e.g., *animals*). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in :code:`"_by_<bot_username>"`. :code:`<bot_username>` is case insensitive. 1-64 characters."""
|
||||
title: str
|
||||
"""Sticker set title, 1-64 characters"""
|
||||
emojis: str
|
||||
|
|
|
|||
27
aiogram/methods/get_chat_menu_button.py
Normal file
27
aiogram/methods/get_chat_menu_button.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import MenuButton, MenuButtonCommands, MenuButtonDefault, MenuButtonWebApp
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class GetChatMenuButton(TelegramMethod[MenuButton]):
|
||||
"""
|
||||
Use this method to get the current value of the bot's menu button in a private chat, or the default menu button. Returns :class:`aiogram.types.menu_button.MenuButton` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getchatmenubutton
|
||||
"""
|
||||
|
||||
__returning__ = Union[MenuButtonDefault, MenuButtonWebApp, MenuButtonCommands]
|
||||
|
||||
chat_id: Optional[int] = None
|
||||
"""Unique identifier for the target private chat. If not specified, default bot's menu button will be returned"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="getChatMenuButton", data=data)
|
||||
27
aiogram/methods/get_my_default_administrator_rights.py
Normal file
27
aiogram/methods/get_my_default_administrator_rights.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
from ..types import ChatAdministratorRights
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class GetMyDefaultAdministratorRights(TelegramMethod[ChatAdministratorRights]):
|
||||
"""
|
||||
Use this method to get the current default administrator rights of the bot. Returns :class:`aiogram.types.chat_administrator_rights.ChatAdministratorRights` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getmydefaultadministratorrights
|
||||
"""
|
||||
|
||||
__returning__ = ChatAdministratorRights
|
||||
|
||||
for_channels: Optional[bool] = None
|
||||
"""Pass :code:`True` to get default administrator rights of the bot in channels. Otherwise, default administrator rights of the bot for groups and supergroups will be returned."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="getMyDefaultAdministratorRights", data=data)
|
||||
|
|
@ -31,8 +31,8 @@ class PromoteChatMember(TelegramMethod[bool]):
|
|||
"""Pass :code:`True`, if the administrator can edit messages of other users and can pin messages, channels only"""
|
||||
can_delete_messages: Optional[bool] = None
|
||||
"""Pass :code:`True`, if the administrator can delete messages of other users"""
|
||||
can_manage_voice_chats: Optional[bool] = None
|
||||
"""Pass :code:`True`, if the administrator can manage voice chats"""
|
||||
can_manage_video_chats: Optional[bool] = None
|
||||
"""Pass :code:`True`, if the administrator can manage video chats"""
|
||||
can_restrict_members: Optional[bool] = None
|
||||
"""Pass :code:`True`, if the administrator can restrict, ban or unban chat members"""
|
||||
can_promote_members: Optional[bool] = None
|
||||
|
|
|
|||
29
aiogram/methods/set_chat_menu_button.py
Normal file
29
aiogram/methods/set_chat_menu_button.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
from ..types import MenuButton
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SetChatMenuButton(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to change the bot's menu button in a private chat, or the default menu button. Returns :code:`True` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setchatmenubutton
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
chat_id: Optional[int] = None
|
||||
"""Unique identifier for the target private chat. If not specified, default bot's menu button will be changed"""
|
||||
menu_button: Optional[MenuButton] = None
|
||||
"""A JSON-serialized object for the new bot's menu button. Defaults to :class:`aiogram.types.menu_button_default.MenuButtonDefault`"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="setChatMenuButton", data=data)
|
||||
29
aiogram/methods/set_my_default_administrator_rights.py
Normal file
29
aiogram/methods/set_my_default_administrator_rights.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
from ..types import ChatAdministratorRights
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SetMyDefaultAdministratorRights(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify the list before adding the bot. Returns :code:`True` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setmydefaultadministratorrights
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
rights: Optional[ChatAdministratorRights] = None
|
||||
"""A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared."""
|
||||
for_channels: Optional[bool] = None
|
||||
"""Pass :code:`True` to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="setMyDefaultAdministratorRights", data=data)
|
||||
|
|
@ -13,6 +13,7 @@ from .bot_command_scope_default import BotCommandScopeDefault
|
|||
from .callback_game import CallbackGame
|
||||
from .callback_query import CallbackQuery
|
||||
from .chat import Chat
|
||||
from .chat_administrator_rights import ChatAdministratorRights
|
||||
from .chat_invite_link import ChatInviteLink
|
||||
from .chat_join_request import ChatJoinRequest
|
||||
from .chat_location import ChatLocation
|
||||
|
|
@ -81,6 +82,10 @@ from .labeled_price import LabeledPrice
|
|||
from .location import Location
|
||||
from .login_url import LoginUrl
|
||||
from .mask_position import MaskPosition
|
||||
from .menu_button import MenuButton
|
||||
from .menu_button_commands import MenuButtonCommands
|
||||
from .menu_button_default import MenuButtonDefault
|
||||
from .menu_button_web_app import MenuButtonWebApp
|
||||
from .message import ContentType, Message
|
||||
from .message_auto_delete_timer_changed import MessageAutoDeleteTimerChanged
|
||||
from .message_entity import MessageEntity
|
||||
|
|
@ -107,6 +112,7 @@ from .proximity_alert_triggered import ProximityAlertTriggered
|
|||
from .reply_keyboard_markup import ReplyKeyboardMarkup
|
||||
from .reply_keyboard_remove import ReplyKeyboardRemove
|
||||
from .response_parameters import ResponseParameters
|
||||
from .sent_web_app_message import SentWebAppMessage
|
||||
from .shipping_address import ShippingAddress
|
||||
from .shipping_option import ShippingOption
|
||||
from .shipping_query import ShippingQuery
|
||||
|
|
@ -118,12 +124,14 @@ from .user import User
|
|||
from .user_profile_photos import UserProfilePhotos
|
||||
from .venue import Venue
|
||||
from .video import Video
|
||||
from .video_chat_ended import VideoChatEnded
|
||||
from .video_chat_participants_invited import VideoChatParticipantsInvited
|
||||
from .video_chat_scheduled import VideoChatScheduled
|
||||
from .video_chat_started import VideoChatStarted
|
||||
from .video_note import VideoNote
|
||||
from .voice import Voice
|
||||
from .voice_chat_ended import VoiceChatEnded
|
||||
from .voice_chat_participants_invited import VoiceChatParticipantsInvited
|
||||
from .voice_chat_scheduled import VoiceChatScheduled
|
||||
from .voice_chat_started import VoiceChatStarted
|
||||
from .web_app_data import WebAppData
|
||||
from .web_app_info import WebAppInfo
|
||||
from .webhook_info import WebhookInfo
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -155,14 +163,16 @@ __all__ = (
|
|||
"Poll",
|
||||
"Location",
|
||||
"Venue",
|
||||
"WebAppData",
|
||||
"ProximityAlertTriggered",
|
||||
"MessageAutoDeleteTimerChanged",
|
||||
"VoiceChatScheduled",
|
||||
"VoiceChatStarted",
|
||||
"VoiceChatEnded",
|
||||
"VoiceChatParticipantsInvited",
|
||||
"VideoChatScheduled",
|
||||
"VideoChatStarted",
|
||||
"VideoChatEnded",
|
||||
"VideoChatParticipantsInvited",
|
||||
"UserProfilePhotos",
|
||||
"File",
|
||||
"WebAppInfo",
|
||||
"ReplyKeyboardMarkup",
|
||||
"KeyboardButton",
|
||||
"KeyboardButtonPollType",
|
||||
|
|
@ -174,6 +184,7 @@ __all__ = (
|
|||
"ForceReply",
|
||||
"ChatPhoto",
|
||||
"ChatInviteLink",
|
||||
"ChatAdministratorRights",
|
||||
"ChatMember",
|
||||
"ChatMemberOwner",
|
||||
"ChatMemberAdministrator",
|
||||
|
|
@ -194,6 +205,10 @@ __all__ = (
|
|||
"BotCommandScopeChat",
|
||||
"BotCommandScopeChatAdministrators",
|
||||
"BotCommandScopeChatMember",
|
||||
"MenuButton",
|
||||
"MenuButtonCommands",
|
||||
"MenuButtonWebApp",
|
||||
"MenuButtonDefault",
|
||||
"ResponseParameters",
|
||||
"InputMedia",
|
||||
"InputMediaPhoto",
|
||||
|
|
@ -234,6 +249,7 @@ __all__ = (
|
|||
"InputContactMessageContent",
|
||||
"InputInvoiceMessageContent",
|
||||
"ChosenInlineResult",
|
||||
"SentWebAppMessage",
|
||||
"LabeledPrice",
|
||||
"Invoice",
|
||||
"ShippingAddress",
|
||||
|
|
|
|||
39
aiogram/types/chat_administrator_rights.py
Normal file
39
aiogram/types/chat_administrator_rights.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class ChatAdministratorRights(TelegramObject):
|
||||
"""
|
||||
Represents the rights of an administrator in a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#chatadministratorrights
|
||||
"""
|
||||
|
||||
is_anonymous: bool
|
||||
""":code:`True`, if the user's presence in the chat is hidden"""
|
||||
can_manage_chat: bool
|
||||
""":code:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege"""
|
||||
can_delete_messages: bool
|
||||
""":code:`True`, if the administrator can delete messages of other users"""
|
||||
can_manage_video_chats: bool
|
||||
""":code:`True`, if the administrator can manage video chats"""
|
||||
can_restrict_members: bool
|
||||
""":code:`True`, if the administrator can restrict, ban or unban chat members"""
|
||||
can_promote_members: bool
|
||||
""":code:`True`, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user)"""
|
||||
can_change_info: bool
|
||||
""":code:`True`, if the user is allowed to change the chat title, photo and other settings"""
|
||||
can_invite_users: bool
|
||||
""":code:`True`, if the user is allowed to invite new users to the chat"""
|
||||
can_post_messages: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the administrator can post in the channel; channels only"""
|
||||
can_edit_messages: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the administrator can edit messages of other users and can pin messages; channels only"""
|
||||
can_pin_messages: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the user is allowed to pin messages; groups and supergroups only"""
|
||||
|
|
@ -37,8 +37,8 @@ class ChatMember(TelegramObject):
|
|||
"""*Optional*. :code:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege"""
|
||||
can_delete_messages: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the administrator can delete messages of other users"""
|
||||
can_manage_voice_chats: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the administrator can manage voice chats"""
|
||||
can_manage_video_chats: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the administrator can manage video chats"""
|
||||
can_restrict_members: Optional[bool] = None
|
||||
"""*Optional*. :code:`True`, if the administrator can restrict, ban or unban chat members"""
|
||||
can_promote_members: Optional[bool] = None
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ class ChatMemberAdministrator(ChatMember):
|
|||
""":code:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege"""
|
||||
can_delete_messages: bool
|
||||
""":code:`True`, if the administrator can delete messages of other users"""
|
||||
can_manage_voice_chats: bool
|
||||
""":code:`True`, if the administrator can manage voice chats"""
|
||||
can_manage_video_chats: bool
|
||||
""":code:`True`, if the administrator can manage video chats"""
|
||||
can_restrict_members: bool
|
||||
""":code:`True`, if the administrator can restrict, ban or unban chat members"""
|
||||
can_promote_members: bool
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from .base import MutableTelegramObject
|
|||
if TYPE_CHECKING:
|
||||
from .callback_game import CallbackGame
|
||||
from .login_url import LoginUrl
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
class InlineKeyboardButton(MutableTelegramObject):
|
||||
|
|
@ -20,10 +21,12 @@ class InlineKeyboardButton(MutableTelegramObject):
|
|||
"""Label text on the button"""
|
||||
url: Optional[str] = None
|
||||
"""*Optional*. HTTP or tg:// url to be opened when the button is pressed. Links :code:`tg://user?id=<user_id>` can be used to mention a user by their ID without using a username, if this is allowed by their privacy settings."""
|
||||
login_url: Optional[LoginUrl] = None
|
||||
"""*Optional*. An HTTP URL used to automatically authorize the user. Can be used as a replacement for the `Telegram Login Widget <https://core.telegram.org/widgets/login>`_."""
|
||||
callback_data: Optional[str] = None
|
||||
"""*Optional*. Data to be sent in a `callback query <https://core.telegram.org/bots/api#callbackquery>`_ to the bot when button is pressed, 1-64 bytes"""
|
||||
web_app: Optional[WebAppInfo] = None
|
||||
"""*Optional*. Description of the `Web App <https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method :class:`aiogram.methods.answer_web_app_query.AnswerWebAppQuery`. Available only in private chats between a user and the bot."""
|
||||
login_url: Optional[LoginUrl] = None
|
||||
"""*Optional*. An HTTP URL used to automatically authorize the user. Can be used as a replacement for the `Telegram Login Widget <https://core.telegram.org/widgets/login>`_."""
|
||||
switch_inline_query: Optional[str] = None
|
||||
"""*Optional*. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. Can be empty, in which case just the bot's username will be inserted."""
|
||||
switch_inline_query_current_chat: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from .base import MutableTelegramObject
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from .keyboard_button_poll_type import KeyboardButtonPollType
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
class WebApp(MutableTelegramObject):
|
||||
|
|
@ -19,15 +20,18 @@ class KeyboardButton(MutableTelegramObject):
|
|||
|
||||
**Note:** *request_poll* option will only work in Telegram versions released after 23 January, 2020. Older clients will display *unsupported message*.
|
||||
|
||||
**Note:** *web_app* option will only work in Telegram versions released after 16 April, 2022. Older clients will display *unsupported message*.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#keyboardbutton
|
||||
"""
|
||||
|
||||
text: str
|
||||
"""Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed"""
|
||||
request_contact: Optional[bool] = None
|
||||
"""*Optional*. If :code:`True`, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only"""
|
||||
"""*Optional*. If :code:`True`, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only."""
|
||||
request_location: Optional[bool] = None
|
||||
"""*Optional*. If :code:`True`, the user's current location will be sent when the button is pressed. Available in private chats only"""
|
||||
"""*Optional*. If :code:`True`, the user's current location will be sent when the button is pressed. Available in private chats only."""
|
||||
request_poll: Optional[KeyboardButtonPollType] = None
|
||||
"""*Optional*. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only"""
|
||||
web_app: Optional[WebApp] = None
|
||||
"""*Optional*. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only."""
|
||||
web_app: Optional[WebAppInfo] = None
|
||||
"""*Optional*. If specified, the described `Web App <https://core.telegram.org/bots/webapps>`_ will be launched when the button is pressed. The Web App will be able to send a 'web_app_data' service message. Available in private chats only."""
|
||||
|
|
|
|||
22
aiogram/types/menu_button.py
Normal file
22
aiogram/types/menu_button.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class MenuButton(TelegramObject):
|
||||
"""
|
||||
This object describes the bot's menu button in a private chat. It should be one of
|
||||
|
||||
- :class:`aiogram.types.menu_button_commands.MenuButtonCommands`
|
||||
- :class:`aiogram.types.menu_button_web_app.MenuButtonWebApp`
|
||||
- :class:`aiogram.types.menu_button_default.MenuButtonDefault`
|
||||
|
||||
If a menu button other than :class:`aiogram.types.menu_button_default.MenuButtonDefault` is set for a private chat, then it is applied in the chat. Otherwise the default menu button is applied. By default, the menu button opens the list of bot commands.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubutton
|
||||
"""
|
||||
16
aiogram/types/menu_button_commands.py
Normal file
16
aiogram/types/menu_button_commands.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from . import MenuButton
|
||||
|
||||
|
||||
class MenuButtonCommands(MenuButton):
|
||||
"""
|
||||
Represents a menu button, which opens the bot's list of commands.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubuttoncommands
|
||||
"""
|
||||
|
||||
type: str = Field("commands", const=True)
|
||||
"""Type of the button, must be *commands*"""
|
||||
16
aiogram/types/menu_button_default.py
Normal file
16
aiogram/types/menu_button_default.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from . import MenuButton
|
||||
|
||||
|
||||
class MenuButtonDefault(MenuButton):
|
||||
"""
|
||||
Describes that no specific value for the menu button was set.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubuttondefault
|
||||
"""
|
||||
|
||||
type: str = Field("default", const=True)
|
||||
"""Type of the button, must be *default*"""
|
||||
25
aiogram/types/menu_button_web_app.py
Normal file
25
aiogram/types/menu_button_web_app.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from . import MenuButton
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
class MenuButtonWebApp(MenuButton):
|
||||
"""
|
||||
Represents a menu button, which launches a `Web App <https://core.telegram.org/bots/webapps>`_.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubuttonwebapp
|
||||
"""
|
||||
|
||||
type: str = Field("web_app", const=True)
|
||||
"""Type of the button, must be *web_app*"""
|
||||
text: str
|
||||
"""Text on the button"""
|
||||
web_app: WebAppInfo
|
||||
"""Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method :class:`aiogram.methods.answer_web_app_query.AnswerWebAppQuery`."""
|
||||
|
|
@ -63,15 +63,16 @@ if TYPE_CHECKING:
|
|||
from .user import User
|
||||
from .venue import Venue
|
||||
from .video import Video
|
||||
from .video_chat_ended import VideoChatEnded
|
||||
from .video_chat_participants_invited import VideoChatParticipantsInvited
|
||||
from .video_chat_scheduled import VideoChatScheduled
|
||||
from .video_chat_started import VideoChatStarted
|
||||
from .video_note import VideoNote
|
||||
from .voice import Voice
|
||||
from .voice_chat_ended import VoiceChatEnded
|
||||
from .voice_chat_participants_invited import VoiceChatParticipantsInvited
|
||||
from .voice_chat_scheduled import VoiceChatScheduled
|
||||
from .voice_chat_started import VoiceChatStarted
|
||||
from .web_app_data import WebAppData
|
||||
|
||||
|
||||
class _BaseMessage(TelegramObject):
|
||||
class Message(TelegramObject):
|
||||
"""
|
||||
This object represents a message.
|
||||
|
||||
|
|
@ -184,19 +185,19 @@ class _BaseMessage(TelegramObject):
|
|||
"""*Optional*. Telegram Passport data"""
|
||||
proximity_alert_triggered: Optional[ProximityAlertTriggered] = None
|
||||
"""*Optional*. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location."""
|
||||
voice_chat_scheduled: Optional[VoiceChatScheduled] = None
|
||||
"""*Optional*. Service message: voice chat scheduled"""
|
||||
voice_chat_started: Optional[VoiceChatStarted] = None
|
||||
"""*Optional*. Service message: voice chat started"""
|
||||
voice_chat_ended: Optional[VoiceChatEnded] = None
|
||||
"""*Optional*. Service message: voice chat ended"""
|
||||
voice_chat_participants_invited: Optional[VoiceChatParticipantsInvited] = None
|
||||
"""*Optional*. Service message: new participants invited to a voice chat"""
|
||||
video_chat_scheduled: Optional[VideoChatScheduled] = None
|
||||
"""*Optional*. Service message: video chat scheduled"""
|
||||
video_chat_started: Optional[VideoChatStarted] = None
|
||||
"""*Optional*. Service message: video chat started"""
|
||||
video_chat_ended: Optional[VideoChatEnded] = None
|
||||
"""*Optional*. Service message: video chat ended"""
|
||||
video_chat_participants_invited: Optional[VideoChatParticipantsInvited] = None
|
||||
"""*Optional*. Service message: new participants invited to a video chat"""
|
||||
web_app_data: Optional[WebAppData] = None
|
||||
"""*Optional*. Service message: data sent by a Web App"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons."""
|
||||
|
||||
|
||||
class Message(_BaseMessage):
|
||||
@property
|
||||
def content_type(self) -> str:
|
||||
if self.text:
|
||||
|
|
@ -257,12 +258,16 @@ class Message(_BaseMessage):
|
|||
return ContentType.DICE
|
||||
if self.message_auto_delete_timer_changed:
|
||||
return ContentType.MESSAGE_AUTO_DELETE_TIMER_CHANGED
|
||||
if self.voice_chat_started:
|
||||
return ContentType.VOICE_CHAT_STARTED
|
||||
if self.voice_chat_ended:
|
||||
return ContentType.VOICE_CHAT_ENDED
|
||||
if self.voice_chat_participants_invited:
|
||||
return ContentType.VOICE_CHAT_PARTICIPANTS_INVITED
|
||||
if self.video_chat_scheduled:
|
||||
return ContentType.VIDEO_CHAT_SCHEDULED
|
||||
if self.video_chat_started:
|
||||
return ContentType.VIDEO_CHAT_STARTED
|
||||
if self.video_chat_ended:
|
||||
return ContentType.VIDEO_CHAT_ENDED
|
||||
if self.video_chat_participants_invited:
|
||||
return ContentType.VIDEO_CHAT_PARTICIPANTS_INVITED
|
||||
if self.web_app_data:
|
||||
return ContentType.WEB_APP_DATA
|
||||
|
||||
return ContentType.UNKNOWN
|
||||
|
||||
|
|
@ -1899,9 +1904,11 @@ class ContentType(helper.Helper):
|
|||
POLL = helper.Item() # poll
|
||||
DICE = helper.Item() # dice
|
||||
MESSAGE_AUTO_DELETE_TIMER_CHANGED = helper.Item() # message_auto_delete_timer_changed
|
||||
VOICE_CHAT_STARTED = helper.Item() # voice_chat_started
|
||||
VOICE_CHAT_ENDED = helper.Item() # voice_chat_ended
|
||||
VOICE_CHAT_PARTICIPANTS_INVITED = helper.Item() # voice_chat_participants_invited
|
||||
VIDEO_CHAT_SCHEDULED = helper.Item() # video_chat_scheduled
|
||||
VIDEO_CHAT_STARTED = helper.Item() # video_chat_started
|
||||
VIDEO_CHAT_ENDED = helper.Item() # video_chat_ended
|
||||
VIDEO_CHAT_PARTICIPANTS_INVITED = helper.Item() # video_chat_participants_invited
|
||||
WEB_APP_DATA = helper.Item() # web_app_data
|
||||
|
||||
UNKNOWN = helper.Item() # unknown
|
||||
ANY = helper.Item() # any
|
||||
|
|
|
|||
16
aiogram/types/sent_web_app_message.py
Normal file
16
aiogram/types/sent_web_app_message.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class SentWebAppMessage(TelegramObject):
|
||||
"""
|
||||
Contains information about an inline message sent by a `Web App <https://core.telegram.org/bots/webapps>`_ on behalf of a user.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sentwebappmessage
|
||||
"""
|
||||
|
||||
inline_message_id: Optional[str] = None
|
||||
"""*Optional*. Identifier of the sent inline message. Available only if there is an `inline keyboard <https://core.telegram.org/bots/api#inlinekeyboardmarkup>`_ attached to the message."""
|
||||
|
|
@ -26,6 +26,8 @@ class Sticker(TelegramObject):
|
|||
"""Sticker height"""
|
||||
is_animated: bool
|
||||
""":code:`True`, if the sticker is `animated <https://telegram.org/blog/animated-stickers>`_"""
|
||||
is_video: bool
|
||||
""":code:`True`, if the sticker is a `video sticker <https://telegram.org/blog/video-stickers-better-reactions>`_"""
|
||||
thumb: Optional[PhotoSize] = None
|
||||
"""*Optional*. Sticker thumbnail in the .WEBP or .JPG format"""
|
||||
emoji: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@ class StickerSet(TelegramObject):
|
|||
"""Sticker set title"""
|
||||
is_animated: bool
|
||||
""":code:`True`, if the sticker set contains `animated stickers <https://telegram.org/blog/animated-stickers>`_"""
|
||||
is_video: bool
|
||||
""":code:`True`, if the sticker set contains `video stickers <https://telegram.org/blog/video-stickers-better-reactions>`_"""
|
||||
contains_masks: bool
|
||||
""":code:`True`, if the sticker set contains masks"""
|
||||
stickers: List[Sticker]
|
||||
"""List of all set stickers"""
|
||||
thumb: Optional[PhotoSize] = None
|
||||
"""*Optional*. Sticker set thumbnail in the .WEBP or .TGS format"""
|
||||
"""*Optional*. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format"""
|
||||
|
|
|
|||
19
aiogram/types/video_chat_ended.py
Normal file
19
aiogram/types/video_chat_ended.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class VideoChatEnded(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a video chat ended in the chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#videochatended
|
||||
"""
|
||||
|
||||
duration: int
|
||||
"""Video chat duration in seconds"""
|
||||
19
aiogram/types/video_chat_participants_invited.py
Normal file
19
aiogram/types/video_chat_participants_invited.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User
|
||||
|
||||
|
||||
class VideoChatParticipantsInvited(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about new members invited to a video chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#videochatparticipantsinvited
|
||||
"""
|
||||
|
||||
users: List[User]
|
||||
"""New members that were invited to the video chat"""
|
||||
16
aiogram/types/video_chat_scheduled.py
Normal file
16
aiogram/types/video_chat_scheduled.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class VideoChatScheduled(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a video chat scheduled in the chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#videochatscheduled
|
||||
"""
|
||||
|
||||
start_date: datetime
|
||||
"""Point in time (Unix timestamp) when the video chat is supposed to be started by a chat administrator"""
|
||||
11
aiogram/types/video_chat_started.py
Normal file
11
aiogram/types/video_chat_started.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class VideoChatStarted(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a video chat started in the chat. Currently holds no information.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#videochatstarted
|
||||
"""
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class VoiceChatEnded(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a voice chat ended in the chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#voicechatended
|
||||
"""
|
||||
|
||||
duration: int
|
||||
"""Voice chat duration in seconds"""
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User
|
||||
|
||||
|
||||
class VoiceChatParticipantsInvited(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about new members invited to a voice chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#voicechatparticipantsinvited
|
||||
"""
|
||||
|
||||
users: Optional[List[User]] = None
|
||||
"""*Optional*. New members that were invited to the voice chat"""
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class VoiceChatScheduled(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a voice chat scheduled in the chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#voicechatscheduled
|
||||
"""
|
||||
|
||||
start_date: int
|
||||
"""Point in time (Unix timestamp) when the voice chat is supposed to be started by a chat administrator"""
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class VoiceChatStarted(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a voice chat started in the chat. Currently holds no information.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#voicechatstarted
|
||||
"""
|
||||
16
aiogram/types/web_app_data.py
Normal file
16
aiogram/types/web_app_data.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class WebAppData(TelegramObject):
|
||||
"""
|
||||
Contains data sent from a `Web App <https://core.telegram.org/bots/webapps>`_ to the bot.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#webappdata
|
||||
"""
|
||||
|
||||
data: str
|
||||
"""The data. Be aware that a bad client can send arbitrary data in this field."""
|
||||
button_text: str
|
||||
"""Text of the *web_app* keyboard button, from which the Web App was opened. Be aware that a bad client can send arbitrary data in this field."""
|
||||
14
aiogram/types/web_app_info.py
Normal file
14
aiogram/types/web_app_info.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class WebAppInfo(TelegramObject):
|
||||
"""
|
||||
Contains information about a `Web App <https://core.telegram.org/bots/webapps>`_.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#webappinfo
|
||||
"""
|
||||
|
||||
url: str
|
||||
"""An HTTPS URL of a Web App to be opened with additional data as specified in `Initializing Web Apps <https://core.telegram.org/bots/webapps#initializing-web-apps>`_"""
|
||||
|
|
@ -25,6 +25,8 @@ class WebhookInfo(TelegramObject):
|
|||
"""*Optional*. Unix time for the most recent error that happened when trying to deliver an update via webhook"""
|
||||
last_error_message: Optional[str] = None
|
||||
"""*Optional*. Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook"""
|
||||
last_synchronization_error_date: Optional[datetime.datetime] = None
|
||||
"""*Optional*. Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters"""
|
||||
max_connections: Optional[int] = None
|
||||
"""*Optional*. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery"""
|
||||
allowed_updates: Optional[List[str]] = None
|
||||
|
|
|
|||
129
aiogram/utils/web_app.py
Normal file
129
aiogram/utils/web_app.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
from datetime import datetime
|
||||
from operator import itemgetter
|
||||
from typing import Any, Callable, Optional
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
from aiogram.types import TelegramObject
|
||||
|
||||
|
||||
class WebAppUser(TelegramObject):
|
||||
"""
|
||||
This object contains the data of the Web App user.
|
||||
|
||||
Source: https://core.telegram.org/bots/webapps#webappuser
|
||||
"""
|
||||
|
||||
id: int
|
||||
"""A unique identifier for the user or bot. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. It has at most 52 significant bits, so a 64-bit integer or a double-precision float type is safe for storing this identifier."""
|
||||
is_bot: Optional[bool] = None
|
||||
"""True, if this user is a bot. Returns in the receiver field only."""
|
||||
first_name: str
|
||||
"""First name of the user or bot."""
|
||||
last_name: Optional[str] = None
|
||||
"""Last name of the user or bot."""
|
||||
username: Optional[str] = None
|
||||
"""Username of the user or bot."""
|
||||
language_code: Optional[str] = None
|
||||
"""IETF language tag of the user's language. Returns in user field only."""
|
||||
photo_url: Optional[str] = None
|
||||
"""URL of the user’s profile photo. The photo can be in .jpeg or .svg formats. Only returned for Web Apps launched from the attachment menu."""
|
||||
|
||||
|
||||
class WebAppInitData(TelegramObject):
|
||||
"""
|
||||
This object contains data that is transferred to the Web App when it is opened. It is empty if the Web App was launched from a keyboard button.
|
||||
|
||||
Source: https://core.telegram.org/bots/webapps#webappinitdata
|
||||
"""
|
||||
|
||||
query_id: Optional[str] = None
|
||||
"""A unique identifier for the Web App session, required for sending messages via the answerWebAppQuery method."""
|
||||
user: Optional[WebAppUser] = None
|
||||
"""An object containing data about the current user."""
|
||||
receiver: Optional[WebAppUser] = None
|
||||
"""An object containing data about the chat partner of the current user in the chat where the bot was launched via the attachment menu. Returned only for Web Apps launched via the attachment menu."""
|
||||
start_param: Optional[str] = None
|
||||
"""The value of the startattach parameter, passed via link. Only returned for Web Apps when launched from the attachment menu via link. The value of the start_param parameter will also be passed in the GET-parameter tgWebAppStartParam, so the Web App can load the correct interface right away."""
|
||||
auth_date: datetime
|
||||
"""Unix time when the form was opened."""
|
||||
hash: str
|
||||
"""A hash of all passed parameters, which the bot server can use to check their validity."""
|
||||
|
||||
|
||||
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: bot Token
|
||||
:param init_data: data from frontend to be validated
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
parsed_data = dict(parse_qsl(init_data, strict_parsing=True))
|
||||
except ValueError: # pragma: no cover
|
||||
# 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_webapp_init_data(
|
||||
init_data: str,
|
||||
*,
|
||||
loads: Callable[..., Any] = json.loads,
|
||||
) -> WebAppInitData:
|
||||
"""
|
||||
Parse WebApp init data and return it as WebAppInitData object
|
||||
|
||||
This method doesn't make any security check, so you shall not trust to this data,
|
||||
use :code:`safe_parse_webapp_init_data` instead.
|
||||
|
||||
:param init_data: data from frontend to be parsed
|
||||
: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 WebAppInitData(**result)
|
||||
|
||||
|
||||
def safe_parse_webapp_init_data(
|
||||
token: str,
|
||||
init_data: str,
|
||||
*,
|
||||
loads: Callable[..., Any] = json.loads,
|
||||
) -> WebAppInitData:
|
||||
"""
|
||||
Validate raw WebApp init data and return it as WebAppInitData object
|
||||
|
||||
Raise :type:`ValueError` when data is invalid
|
||||
|
||||
:param token: bot token
|
||||
:param init_data: data from frontend to be parsed and validated
|
||||
:param loads:
|
||||
:return:
|
||||
"""
|
||||
if check_webapp_signature(token, init_data):
|
||||
return parse_webapp_init_data(init_data, loads=loads)
|
||||
raise ValueError("Invalid init data signature")
|
||||
51
docs/api/methods/answer_web_app_query.rst
Normal file
51
docs/api/methods/answer_web_app_query.rst
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#################
|
||||
answerWebAppQuery
|
||||
#################
|
||||
|
||||
Returns: :obj:`SentWebAppMessage`
|
||||
|
||||
.. automodule:: aiogram.methods.answer_web_app_query
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: SentWebAppMessage = await bot.answer_web_app_query(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.answer_web_app_query import AnswerWebAppQuery`
|
||||
- alias: :code:`from aiogram.methods import AnswerWebAppQuery`
|
||||
|
||||
In handlers with current bot
|
||||
----------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: SentWebAppMessage = await AnswerWebAppQuery(...)
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: SentWebAppMessage = await bot(AnswerWebAppQuery(...))
|
||||
|
||||
As reply into Webhook in handler
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
return AnswerWebAppQuery(...)
|
||||
44
docs/api/methods/get_chat_menu_button.rst
Normal file
44
docs/api/methods/get_chat_menu_button.rst
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#################
|
||||
getChatMenuButton
|
||||
#################
|
||||
|
||||
Returns: :obj:`MenuButton`
|
||||
|
||||
.. automodule:: aiogram.methods.get_chat_menu_button
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: MenuButton = await bot.get_chat_menu_button(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.get_chat_menu_button import GetChatMenuButton`
|
||||
- alias: :code:`from aiogram.methods import GetChatMenuButton`
|
||||
|
||||
In handlers with current bot
|
||||
----------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: MenuButton = await GetChatMenuButton(...)
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: MenuButton = await bot(GetChatMenuButton(...))
|
||||
44
docs/api/methods/get_my_default_administrator_rights.rst
Normal file
44
docs/api/methods/get_my_default_administrator_rights.rst
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
###############################
|
||||
getMyDefaultAdministratorRights
|
||||
###############################
|
||||
|
||||
Returns: :obj:`ChatAdministratorRights`
|
||||
|
||||
.. automodule:: aiogram.methods.get_my_default_administrator_rights
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: ChatAdministratorRights = await bot.get_my_default_administrator_rights(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.get_my_default_administrator_rights import GetMyDefaultAdministratorRights`
|
||||
- alias: :code:`from aiogram.methods import GetMyDefaultAdministratorRights`
|
||||
|
||||
In handlers with current bot
|
||||
----------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: ChatAdministratorRights = await GetMyDefaultAdministratorRights(...)
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: ChatAdministratorRights = await bot(GetMyDefaultAdministratorRights(...))
|
||||
|
|
@ -82,6 +82,10 @@ Available methods
|
|||
set_my_commands
|
||||
delete_my_commands
|
||||
get_my_commands
|
||||
set_chat_menu_button
|
||||
get_chat_menu_button
|
||||
set_my_default_administrator_rights
|
||||
get_my_default_administrator_rights
|
||||
|
||||
Updating messages
|
||||
=================
|
||||
|
|
@ -118,6 +122,7 @@ Inline mode
|
|||
:maxdepth: 1
|
||||
|
||||
answer_inline_query
|
||||
answer_web_app_query
|
||||
|
||||
Payments
|
||||
========
|
||||
|
|
|
|||
51
docs/api/methods/set_chat_menu_button.rst
Normal file
51
docs/api/methods/set_chat_menu_button.rst
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#################
|
||||
setChatMenuButton
|
||||
#################
|
||||
|
||||
Returns: :obj:`bool`
|
||||
|
||||
.. automodule:: aiogram.methods.set_chat_menu_button
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: bool = await bot.set_chat_menu_button(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.set_chat_menu_button import SetChatMenuButton`
|
||||
- alias: :code:`from aiogram.methods import SetChatMenuButton`
|
||||
|
||||
In handlers with current bot
|
||||
----------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: bool = await SetChatMenuButton(...)
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: bool = await bot(SetChatMenuButton(...))
|
||||
|
||||
As reply into Webhook in handler
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
return SetChatMenuButton(...)
|
||||
51
docs/api/methods/set_my_default_administrator_rights.rst
Normal file
51
docs/api/methods/set_my_default_administrator_rights.rst
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
###############################
|
||||
setMyDefaultAdministratorRights
|
||||
###############################
|
||||
|
||||
Returns: :obj:`bool`
|
||||
|
||||
.. automodule:: aiogram.methods.set_my_default_administrator_rights
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: bool = await bot.set_my_default_administrator_rights(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.set_my_default_administrator_rights import SetMyDefaultAdministratorRights`
|
||||
- alias: :code:`from aiogram.methods import SetMyDefaultAdministratorRights`
|
||||
|
||||
In handlers with current bot
|
||||
----------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: bool = await SetMyDefaultAdministratorRights(...)
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: bool = await bot(SetMyDefaultAdministratorRights(...))
|
||||
|
||||
As reply into Webhook in handler
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
return SetMyDefaultAdministratorRights(...)
|
||||
9
docs/api/types/chat_administrator_rights.rst
Normal file
9
docs/api/types/chat_administrator_rights.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#######################
|
||||
ChatAdministratorRights
|
||||
#######################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.chat_administrator_rights
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -40,14 +40,16 @@ Available types
|
|||
poll
|
||||
location
|
||||
venue
|
||||
web_app_data
|
||||
proximity_alert_triggered
|
||||
message_auto_delete_timer_changed
|
||||
voice_chat_scheduled
|
||||
voice_chat_started
|
||||
voice_chat_ended
|
||||
voice_chat_participants_invited
|
||||
video_chat_scheduled
|
||||
video_chat_started
|
||||
video_chat_ended
|
||||
video_chat_participants_invited
|
||||
user_profile_photos
|
||||
file
|
||||
web_app_info
|
||||
reply_keyboard_markup
|
||||
keyboard_button
|
||||
keyboard_button_poll_type
|
||||
|
|
@ -59,6 +61,7 @@ Available types
|
|||
force_reply
|
||||
chat_photo
|
||||
chat_invite_link
|
||||
chat_administrator_rights
|
||||
chat_member
|
||||
chat_member_owner
|
||||
chat_member_administrator
|
||||
|
|
@ -79,6 +82,10 @@ Available types
|
|||
bot_command_scope_chat
|
||||
bot_command_scope_chat_administrators
|
||||
bot_command_scope_chat_member
|
||||
menu_button
|
||||
menu_button_commands
|
||||
menu_button_web_app
|
||||
menu_button_default
|
||||
response_parameters
|
||||
input_media
|
||||
input_media_photo
|
||||
|
|
@ -135,6 +142,7 @@ Inline mode
|
|||
input_contact_message_content
|
||||
input_invoice_message_content
|
||||
chosen_inline_result
|
||||
sent_web_app_message
|
||||
|
||||
Payments
|
||||
========
|
||||
|
|
|
|||
9
docs/api/types/menu_button.rst
Normal file
9
docs/api/types/menu_button.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##########
|
||||
MenuButton
|
||||
##########
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.menu_button
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
##################
|
||||
VoiceChatScheduled
|
||||
MenuButtonCommands
|
||||
##################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.voice_chat_scheduled
|
||||
.. automodule:: aiogram.types.menu_button_commands
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/menu_button_default.rst
Normal file
9
docs/api/types/menu_button_default.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#################
|
||||
MenuButtonDefault
|
||||
#################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.menu_button_default
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/menu_button_web_app.rst
Normal file
9
docs/api/types/menu_button_web_app.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
################
|
||||
MenuButtonWebApp
|
||||
################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.menu_button_web_app
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/sent_web_app_message.rst
Normal file
9
docs/api/types/sent_web_app_message.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#################
|
||||
SentWebAppMessage
|
||||
#################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.sent_web_app_message
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
##############
|
||||
VoiceChatEnded
|
||||
VideoChatEnded
|
||||
##############
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.voice_chat_ended
|
||||
.. automodule:: aiogram.types.video_chat_ended
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
############################
|
||||
VoiceChatParticipantsInvited
|
||||
VideoChatParticipantsInvited
|
||||
############################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.voice_chat_participants_invited
|
||||
.. automodule:: aiogram.types.video_chat_participants_invited
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/video_chat_scheduled.rst
Normal file
9
docs/api/types/video_chat_scheduled.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##################
|
||||
VideoChatScheduled
|
||||
##################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.video_chat_scheduled
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
################
|
||||
VoiceChatStarted
|
||||
VideoChatStarted
|
||||
################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.voice_chat_started
|
||||
.. automodule:: aiogram.types.video_chat_started
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/web_app_data.rst
Normal file
9
docs/api/types/web_app_data.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##########
|
||||
WebAppData
|
||||
##########
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.web_app_data
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/web_app_info.rst
Normal file
9
docs/api/types/web_app_info.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##########
|
||||
WebAppInfo
|
||||
##########
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.web_app_info
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -7,3 +7,4 @@ Utils
|
|||
keyboard
|
||||
i18n
|
||||
chat_action
|
||||
web_app
|
||||
|
|
|
|||
55
docs/utils/web_app.rst
Normal file
55
docs/utils/web_app.rst
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
======
|
||||
WebApз
|
||||
======
|
||||
|
||||
Telegram Bot API 6.0 announces a revolution in the development of chatbots using WebApp feature.
|
||||
|
||||
You can read more details on it in the official `blog <https://telegram.org/blog/notifications-bots#bot-revolution>`_
|
||||
and `documentation <https://core.telegram.org/bots/webapps>`_.
|
||||
|
||||
`aiogram` implements simple utils to remove headache with the data validation from Telegram WebApp on the backend side.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
For example from frontend you will pass :code:`application/x-www-form-urlencoded` POST request
|
||||
with :code:`_auth` field in body and wants to return User info inside response as :code:`application/json`
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from aiogram.utils.web_app import safe_parse_webapp_init_data
|
||||
from aiohttp.web_request import Request
|
||||
from aiohttp.web_response import json_response
|
||||
|
||||
async def check_data_handler(request: Request):
|
||||
bot: Bot = request.app["bot"]
|
||||
|
||||
data = await request.post() # application/x-www-form-urlencoded
|
||||
try:
|
||||
data = safe_parse_webapp_init_data(token=bot.token, init_data=data["_auth"])
|
||||
except ValueError:
|
||||
return json_response({"ok": False, "err": "Unauthorized"}, status=401)
|
||||
return json_response({"ok": True, "data": data.user.dict()})
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
||||
.. autofunction:: aiogram.utils.web_app.check_webapp_signature
|
||||
|
||||
.. autofunction:: aiogram.utils.web_app.parse_webapp_init_data
|
||||
|
||||
.. autofunction:: aiogram.utils.web_app.safe_parse_webapp_init_data
|
||||
|
||||
|
||||
Types
|
||||
=====
|
||||
|
||||
.. autoclass:: aiogram.utils.web_app.WebAppInitData
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
.. autoclass:: aiogram.utils.web_app.WebAppUser
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
376
examples/web_app/demo.html
Normal file
376
examples/web_app/demo.html
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
|
||||
<meta name="format-detection" content="telephone=no"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="MobileOptimized" content="176"/>
|
||||
<meta name="HandheldFriendly" content="True"/>
|
||||
<meta name="robots" content="noindex,nofollow"/>
|
||||
<script src="https://telegram.org/js/telegram-web-app.js?1"></script>
|
||||
<script>
|
||||
function setThemeClass() {
|
||||
document.documentElement.className = Telegram.WebApp.colorScheme;
|
||||
}
|
||||
|
||||
Telegram.WebApp.onEvent('themeChanged', setThemeClass);
|
||||
setThemeClass();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background-color: var(--tg-theme-bg-color, #ffffff);
|
||||
color: var(--tg-theme-text-color, #222222);
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color-scheme: var(--tg-color-scheme);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--tg-theme-link-color, #2678b6);
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
margin: 15px 0;
|
||||
padding: 12px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: var(--tg-theme-button-color, #50a8eb);
|
||||
color: var(--tg-theme-button-text-color, #ffffff);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
opacity: 0.6;
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
button.close_btn {
|
||||
/*position: fixed;*/
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
padding: 16px 20px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 15px 15px 65px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 40px 0 15px;
|
||||
}
|
||||
|
||||
ul {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
li {
|
||||
color: var(--tg-theme-hint-color, #a8a8a8);
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: rgba(0, 0, 0, .07);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
margin: 7px 0;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dark pre {
|
||||
background: rgba(255, 255, 255, .15);
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: .8em;
|
||||
color: var(--tg-theme-hint-color, #a8a8a8);
|
||||
}
|
||||
|
||||
.ok {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.err {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#fixed_wrap {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: translateY(100vh);
|
||||
}
|
||||
|
||||
.viewport_border,
|
||||
.viewport_stable_border {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: var(--tg-viewport-height, 100vh);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.viewport_stable_border {
|
||||
height: var(--tg-viewport-stable-height, 100vh);
|
||||
}
|
||||
|
||||
.viewport_border:before,
|
||||
.viewport_stable_border:before {
|
||||
content: attr(text);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background: gray;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 7px;
|
||||
padding: 2px 4px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.viewport_stable_border:before {
|
||||
background: green;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.viewport_border:after,
|
||||
.viewport_stable_border:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border: 2px dashed gray;
|
||||
}
|
||||
|
||||
.viewport_stable_border:after {
|
||||
border-color: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="visibility: hidden;">
|
||||
<section>
|
||||
<button id="main_btn" onclick="sendMessage('');">Send «Hello, World!»</button>
|
||||
<button id="with_webview_btn" onclick="sendMessage('', true);">
|
||||
Send «Hello, World!» with inline webview button
|
||||
</button>
|
||||
<button onclick="webviewExpand();">Expand Webview</button>
|
||||
<button onclick="toggleMainButton(this);">Hide Main Button</button>
|
||||
<div id="btn_status" class="hint" style="display: none;">
|
||||
</div>
|
||||
<p>Test links:</p>
|
||||
<ul>
|
||||
<li><a id="regular_link" href="?nextpage=1">Regular link #1</a> (opens inside webview)</li>
|
||||
<li><a href="https://telegram.org/" target="_blank">target="_blank" link</a> (opens outside
|
||||
webview)
|
||||
</li>
|
||||
<li><a href="javascript:window.open('https://telegram.org/');">window.open() link</a>
|
||||
(opens outside webview)
|
||||
</li>
|
||||
<li><a href="https://t.me/like">LikeBot t.me link</a> (opens inside Telegram app)</li>
|
||||
<li><a href="tg://resolve?domain=vote">VoteBot tg:// link</a> (does not open)</li>
|
||||
</ul>
|
||||
<p>Test permissions:</p>
|
||||
<ul>
|
||||
<li><a href="javascript:;" onclick="return requestLocation(this);">Request Location</a>
|
||||
<span></span></li>
|
||||
<li><a href="javascript:;" onclick="return requestVideo(this);">Request Video</a>
|
||||
<span></span></li>
|
||||
<li><a href="javascript:;" onclick="return requestAudio(this);">Request Audio</a>
|
||||
<span></span></li>
|
||||
</ul>
|
||||
<pre><code id="webview_data"></code></pre>
|
||||
<div class="hint">
|
||||
Data passed to webview.
|
||||
<span id="webview_data_status" style="display: none;">Checking hash...</span>
|
||||
</div>
|
||||
<pre><code id="theme_data"></code></pre>
|
||||
<div class="hint">
|
||||
Theme params
|
||||
</div>
|
||||
</section>
|
||||
<div class="viewport_border"></div>
|
||||
<div class="viewport_stable_border"></div>
|
||||
<script src="https://webappcontent.telegram.org/js/jquery.min.js"></script>
|
||||
<script>
|
||||
Telegram.WebApp.ready();
|
||||
|
||||
var initData = Telegram.WebApp.initData || '';
|
||||
var initDataUnsafe = Telegram.WebApp.initDataUnsafe || {};
|
||||
|
||||
function sendMessage(msg_id, with_webview) {
|
||||
if (!initDataUnsafe.query_id) {
|
||||
alert('WebViewQueryId not defined');
|
||||
return;
|
||||
}
|
||||
$('button').prop('disabled', true);
|
||||
$('#btn_status').text('Sending...').removeClass('ok err').show();
|
||||
$.ajax('/demo/sendMessage', {
|
||||
type: 'POST',
|
||||
data: {
|
||||
_auth: initData,
|
||||
msg_id: msg_id || '',
|
||||
with_webview: !initDataUnsafe.receiver && with_webview ? 1 : 0
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (result) {
|
||||
$('button').prop('disabled', false);
|
||||
if (result.response) {
|
||||
if (result.response.ok) {
|
||||
$('#btn_status').html('Message sent successfully!').addClass('ok').show();
|
||||
} else {
|
||||
$('#btn_status').text(result.response.description).addClass('err').show();
|
||||
alert(result.response.description);
|
||||
}
|
||||
} else {
|
||||
$('#btn_status').text('Unknown error').addClass('err').show();
|
||||
alert('Unknown error');
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
$('button').prop('disabled', false);
|
||||
$('#btn_status').text('Server error').addClass('err').show();
|
||||
alert('Server error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function webviewExpand() {
|
||||
Telegram.WebApp.expand();
|
||||
}
|
||||
|
||||
function webviewClose() {
|
||||
Telegram.WebApp.close();
|
||||
}
|
||||
|
||||
function requestLocation(el) {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(function (position) {
|
||||
$(el).next('span').html('(' + position.coords.latitude + ', ' + position.coords.longitude + ')').attr('class', 'ok');
|
||||
});
|
||||
} else {
|
||||
$(el).next('span').html('Geolocation is not supported in this browser.').attr('class', 'err');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function requestVideo(el) {
|
||||
if (navigator.mediaDevices) {
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: true
|
||||
}).then(function (stream) {
|
||||
$(el).next('span').html('(Access granted)').attr('class', 'ok');
|
||||
});
|
||||
} else {
|
||||
$(el).next('span').html('Media devices is not supported in this browser.').attr('class', 'err');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function requestAudio(el) {
|
||||
if (navigator.mediaDevices) {
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: true,
|
||||
video: false
|
||||
}).then(function (stream) {
|
||||
$(el).next('span').html('(Access granted)').attr('class', 'ok');
|
||||
});
|
||||
} else {
|
||||
$(el).next('span').html('Media devices is not supported in this browser.').attr('class', 'err');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Telegram.WebApp.onEvent('themeChanged', function () {
|
||||
$('#theme_data').html(JSON.stringify(Telegram.WebApp.themeParams, null, 2));
|
||||
});
|
||||
|
||||
$('#main_btn').toggle(!!initDataUnsafe.query_id);
|
||||
$('#with_webview_btn').toggle(!!initDataUnsafe.query_id && !initDataUnsafe.receiver);
|
||||
// $('#data_btn').toggle(!initDataUnsafe.query_id || !initDataUnsafe.receiver);
|
||||
$('#webview_data').html(JSON.stringify(initDataUnsafe, null, 2));
|
||||
$('#theme_data').html(JSON.stringify(Telegram.WebApp.themeParams, null, 2));
|
||||
$('#regular_link').attr('href', $('#regular_link').attr('href') + location.hash);
|
||||
$('#text_field').focus();
|
||||
if (initDataUnsafe.query_id && initData) {
|
||||
$('#webview_data_status').show();
|
||||
$.ajax('/demo/checkData', {
|
||||
type: 'POST',
|
||||
data: {_auth: initData},
|
||||
dataType: 'json',
|
||||
success: function (result) {
|
||||
if (result.ok) {
|
||||
$('#webview_data_status').html('Hash is correct').addClass('ok');
|
||||
} else {
|
||||
$('#webview_data_status').html(result.error).addClass('err');
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
$('#webview_data_status').html('Server error').addClass('err');
|
||||
}
|
||||
});
|
||||
}
|
||||
$('body').css('visibility', '');
|
||||
Telegram.WebApp.MainButton
|
||||
.setText('CLOSE WEBVIEW')
|
||||
.show()
|
||||
.onClick(function () {
|
||||
webviewClose();
|
||||
});
|
||||
|
||||
function toggleMainButton(el) {
|
||||
var mainButton = Telegram.WebApp.MainButton;
|
||||
if (mainButton.isVisible) {
|
||||
mainButton.hide();
|
||||
el.innerHTML = 'Show Main Button';
|
||||
} else {
|
||||
mainButton.show();
|
||||
el.innerHTML = 'Hide Main Button';
|
||||
}
|
||||
}
|
||||
|
||||
function round(val, d) {
|
||||
var k = Math.pow(10, d || 0);
|
||||
return Math.round(val * k) / k;
|
||||
}
|
||||
|
||||
function setViewportData() {
|
||||
$('.viewport_border').attr('text', window.innerWidth + ' x ' + round(Telegram.WebApp.viewportHeight, 2));
|
||||
$('.viewport_stable_border').attr('text', window.innerWidth + ' x ' + round(Telegram.WebApp.viewportStableHeight, 2) + ' | is_expanded: ' + (Telegram.WebApp.isExpanded ? 'true' : 'false'));
|
||||
}
|
||||
|
||||
Telegram.WebApp.onEvent('viewportChanged', setViewportData);
|
||||
setViewportData();
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!-- page generated in 1.11ms -->
|
||||
48
examples/web_app/handlers.py
Normal file
48
examples/web_app/handlers.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from aiogram import Bot, F, Router
|
||||
from aiogram.dispatcher.filters import Command
|
||||
from aiogram.types import (
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup,
|
||||
MenuButtonWebApp,
|
||||
Message,
|
||||
WebAppInfo,
|
||||
)
|
||||
|
||||
my_router = Router()
|
||||
|
||||
|
||||
@my_router.message(Command(commands=["start"]))
|
||||
async def command_start(message: Message, bot: Bot, base_url: str):
|
||||
await bot.set_chat_menu_button(
|
||||
chat_id=message.chat.id,
|
||||
menu_button=MenuButtonWebApp(text="Open Menu", web_app=WebAppInfo(url=f"{base_url}/demo")),
|
||||
)
|
||||
await message.answer("""Hi!\nSend me any type of message to start.\nOr just send /webview""")
|
||||
|
||||
|
||||
@my_router.message(Command(commands=["webview"]))
|
||||
async def command_webview(message: Message, base_url: str):
|
||||
await message.answer(
|
||||
"Good. Now you can try to send it via Webview",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="Open Webview", web_app=WebAppInfo(url=f"{base_url}/demo")
|
||||
)
|
||||
]
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@my_router.message(~F.message.via_bot) # Echo to all messages except messages via bot
|
||||
async def echo_all(message: Message, base_url: str):
|
||||
await message.answer(
|
||||
"Test webview",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[InlineKeyboardButton(text="Open", web_app=WebAppInfo(url=f"{base_url}/demo"))]
|
||||
]
|
||||
),
|
||||
)
|
||||
49
examples/web_app/main.py
Normal file
49
examples/web_app/main.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import logging
|
||||
from os import getenv
|
||||
|
||||
from aiohttp.web import run_app
|
||||
from aiohttp.web_app import Application
|
||||
from handlers import my_router
|
||||
from routes import check_data_handler, demo_handler, send_message_handler
|
||||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.dispatcher.webhook.aiohttp_server import SimpleRequestHandler, setup_application
|
||||
from aiogram.types import MenuButtonWebApp, WebAppInfo
|
||||
|
||||
TELEGRAM_TOKEN = getenv("TELEGRAM_TOKEN")
|
||||
APP_BASE_URL = getenv("APP_BASE_URL")
|
||||
|
||||
|
||||
async def on_startup(bot: Bot, base_url: str):
|
||||
await bot.set_webhook(f"{base_url}/webhook")
|
||||
await bot.set_chat_menu_button(
|
||||
menu_button=MenuButtonWebApp(text="Open Menu", web_app=WebAppInfo(url=f"{base_url}/demo"))
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
bot = Bot(token=TELEGRAM_TOKEN, parse_mode="HTML")
|
||||
dispatcher = Dispatcher()
|
||||
dispatcher["base_url"] = APP_BASE_URL
|
||||
dispatcher.startup.register(on_startup)
|
||||
|
||||
dispatcher.include_router(my_router)
|
||||
|
||||
app = Application()
|
||||
app["bot"] = bot
|
||||
|
||||
app.router.add_get("/demo", demo_handler)
|
||||
app.router.add_post("/demo/checkData", check_data_handler)
|
||||
app.router.add_post("/demo/sendMessage", send_message_handler)
|
||||
SimpleRequestHandler(
|
||||
dispatcher=dispatcher,
|
||||
bot=bot,
|
||||
).register(app, path="/webhook")
|
||||
setup_application(app, dispatcher, bot=bot)
|
||||
|
||||
run_app(app, host="127.0.0.1", port=8081)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
||||
64
examples/web_app/routes.py
Normal file
64
examples/web_app/routes.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
from pathlib import Path
|
||||
|
||||
from aiohttp.web_fileresponse import FileResponse
|
||||
from aiohttp.web_request import Request
|
||||
from aiohttp.web_response import json_response
|
||||
|
||||
from aiogram import Bot
|
||||
from aiogram.types import (
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResultArticle,
|
||||
InputTextMessageContent,
|
||||
WebAppInfo,
|
||||
)
|
||||
from aiogram.utils.web_app import check_webapp_signature, safe_parse_webapp_init_data
|
||||
|
||||
|
||||
async def demo_handler(request: Request):
|
||||
return FileResponse(Path(__file__).parent.resolve() / "demo.html")
|
||||
|
||||
|
||||
async def check_data_handler(request: Request):
|
||||
bot: Bot = request.app["bot"]
|
||||
|
||||
data = await request.post()
|
||||
if check_webapp_signature(bot.token, data["_auth"]):
|
||||
return json_response({"ok": True})
|
||||
return json_response({"ok": False, "err": "Unauthorized"}, status=401)
|
||||
|
||||
|
||||
async def send_message_handler(request: Request):
|
||||
bot: Bot = request.app["bot"]
|
||||
data = await request.post()
|
||||
try:
|
||||
web_app_init_data = safe_parse_webapp_init_data(token=bot.token, init_data=data["_auth"])
|
||||
except ValueError:
|
||||
return json_response({"ok": False, "err": "Unauthorized"}, status=401)
|
||||
|
||||
print(data)
|
||||
reply_markup = None
|
||||
if data["with_webview"] == "1":
|
||||
reply_markup = InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="Open",
|
||||
web_app=WebAppInfo(url=str(request.url.with_scheme("https"))),
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
await bot.answer_web_app_query(
|
||||
web_app_query_id=web_app_init_data.query_id,
|
||||
result=InlineQueryResultArticle(
|
||||
id=web_app_init_data.query_id,
|
||||
title="Demo",
|
||||
input_message_content=InputTextMessageContent(
|
||||
message_text="Hello, World!",
|
||||
parse_mode=None,
|
||||
),
|
||||
reply_markup=reply_markup,
|
||||
),
|
||||
)
|
||||
return json_response({"ok": True})
|
||||
33
tests/test_api/test_methods/test_answer_web_app_query.py
Normal file
33
tests/test_api/test_methods/test_answer_web_app_query.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import AnswerWebAppQuery, Request
|
||||
from aiogram.types import InlineQueryResult, SentWebAppMessage
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestAnswerWebAppQuery:
|
||||
@pytest.mark.asyncio
|
||||
async def test_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(AnswerWebAppQuery, ok=True, result=SentWebAppMessage())
|
||||
|
||||
response: SentWebAppMessage = await AnswerWebAppQuery(
|
||||
web_app_query_id="test",
|
||||
result=InlineQueryResult(),
|
||||
)
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "answerWebAppQuery"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(AnswerWebAppQuery, ok=True, result=SentWebAppMessage())
|
||||
|
||||
response: SentWebAppMessage = await bot.answer_web_app_query(
|
||||
web_app_query_id="test",
|
||||
result=InlineQueryResult(),
|
||||
)
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "answerWebAppQuery"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
0
tests/test_api/test_methods/test_approve_chat_join_request.py
Normal file → Executable file
0
tests/test_api/test_methods/test_approve_chat_join_request.py
Normal file → Executable file
0
tests/test_api/test_methods/test_ban_chat_sender_chat.py
Normal file → Executable file
0
tests/test_api/test_methods/test_ban_chat_sender_chat.py
Normal file → Executable file
0
tests/test_api/test_methods/test_decline_chat_join_request.py
Normal file → Executable file
0
tests/test_api/test_methods/test_decline_chat_join_request.py
Normal file → Executable file
27
tests/test_api/test_methods/test_get_chat_menu_button.py
Normal file
27
tests/test_api/test_methods/test_get_chat_menu_button.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import GetChatMenuButton, Request
|
||||
from aiogram.types import MenuButton, MenuButtonDefault
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestGetChatMenuButton:
|
||||
@pytest.mark.asyncio
|
||||
async def test_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(GetChatMenuButton, ok=True, result=MenuButtonDefault())
|
||||
|
||||
response: MenuButton = await GetChatMenuButton()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "getChatMenuButton"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(GetChatMenuButton, ok=True, result=MenuButtonDefault())
|
||||
|
||||
response: MenuButton = await bot.get_chat_menu_button()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "getChatMenuButton"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import GetMyDefaultAdministratorRights, Request
|
||||
from aiogram.types import ChatAdministratorRights
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestGetMyDefaultAdministratorRights:
|
||||
@pytest.mark.asyncio
|
||||
async def test_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(
|
||||
GetMyDefaultAdministratorRights,
|
||||
ok=True,
|
||||
result=ChatAdministratorRights(
|
||||
is_anonymous=False,
|
||||
can_manage_chat=False,
|
||||
can_delete_messages=False,
|
||||
can_manage_video_chats=False,
|
||||
can_restrict_members=False,
|
||||
can_promote_members=False,
|
||||
can_change_info=False,
|
||||
can_invite_users=False,
|
||||
),
|
||||
)
|
||||
|
||||
response: ChatAdministratorRights = await GetMyDefaultAdministratorRights()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "getMyDefaultAdministratorRights"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(
|
||||
GetMyDefaultAdministratorRights,
|
||||
ok=True,
|
||||
result=ChatAdministratorRights(
|
||||
is_anonymous=False,
|
||||
can_manage_chat=False,
|
||||
can_delete_messages=False,
|
||||
can_manage_video_chats=False,
|
||||
can_restrict_members=False,
|
||||
can_promote_members=False,
|
||||
can_change_info=False,
|
||||
can_invite_users=False,
|
||||
),
|
||||
)
|
||||
|
||||
response: ChatAdministratorRights = await bot.get_my_default_administrator_rights()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "getMyDefaultAdministratorRights"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
|
@ -16,6 +16,7 @@ class TestGetStickerSet:
|
|||
name="test",
|
||||
title="test",
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
contains_masks=False,
|
||||
stickers=[
|
||||
Sticker(
|
||||
|
|
@ -23,6 +24,7 @@ class TestGetStickerSet:
|
|||
width=42,
|
||||
height=42,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
file_unique_id="file id",
|
||||
)
|
||||
],
|
||||
|
|
@ -42,6 +44,7 @@ class TestGetStickerSet:
|
|||
name="test",
|
||||
title="test",
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
contains_masks=False,
|
||||
stickers=[
|
||||
Sticker(
|
||||
|
|
@ -49,6 +52,7 @@ class TestGetStickerSet:
|
|||
width=42,
|
||||
height=42,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
file_unique_id="file id",
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class TestSendSticker:
|
|||
width=42,
|
||||
height=42,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
file_unique_id="file id",
|
||||
),
|
||||
chat=Chat(id=42, type="private"),
|
||||
|
|
@ -45,6 +46,7 @@ class TestSendSticker:
|
|||
width=42,
|
||||
height=42,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
file_unique_id="file id",
|
||||
),
|
||||
chat=Chat(id=42, type="private"),
|
||||
|
|
|
|||
26
tests/test_api/test_methods/test_set_chat_menu_button.py
Normal file
26
tests/test_api/test_methods/test_set_chat_menu_button.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import Request, SetChatMenuButton
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestSetChatMenuButton:
|
||||
@pytest.mark.asyncio
|
||||
async def test_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(SetChatMenuButton, ok=True, result=True)
|
||||
|
||||
response: bool = await SetChatMenuButton()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "setChatMenuButton"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(SetChatMenuButton, ok=True, result=True)
|
||||
|
||||
response: bool = await bot.set_chat_menu_button()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "setChatMenuButton"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import Request, SetMyDefaultAdministratorRights
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestSetMyDefaultAdministratorRights:
|
||||
@pytest.mark.asyncio
|
||||
async def test_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(SetMyDefaultAdministratorRights, ok=True, result=True)
|
||||
|
||||
response: bool = await SetMyDefaultAdministratorRights()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "setMyDefaultAdministratorRights"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(SetMyDefaultAdministratorRights, ok=True, result=True)
|
||||
|
||||
response: bool = await bot.set_my_default_administrator_rights()
|
||||
request: Request = bot.get_request()
|
||||
assert request.method == "setMyDefaultAdministratorRights"
|
||||
# assert request.data == {}
|
||||
assert response == prepare_result.result
|
||||
0
tests/test_api/test_methods/test_unban_chat_sender_chat.py
Normal file → Executable file
0
tests/test_api/test_methods/test_unban_chat_sender_chat.py
Normal file → Executable file
|
|
@ -52,11 +52,13 @@ from aiogram.types import (
|
|||
User,
|
||||
Venue,
|
||||
Video,
|
||||
VideoChatEnded,
|
||||
VideoChatParticipantsInvited,
|
||||
VideoChatScheduled,
|
||||
VideoChatStarted,
|
||||
VideoNote,
|
||||
Voice,
|
||||
VoiceChatEnded,
|
||||
VoiceChatParticipantsInvited,
|
||||
VoiceChatStarted,
|
||||
WebAppData,
|
||||
)
|
||||
from aiogram.types.message import ContentType, Message
|
||||
|
||||
|
|
@ -122,6 +124,7 @@ TEST_MESSAGE_STICKER = Message(
|
|||
width=42,
|
||||
height=42,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
|
|
@ -318,29 +321,38 @@ TEST_MESSAGE_MESSAGE_AUTO_DELETE_TIMER_CHANGED = Message(
|
|||
message_auto_delete_timer_changed=MessageAutoDeleteTimerChanged(message_auto_delete_time=42),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
)
|
||||
TEST_MESSAGE_VOICE_CHAT_STARTED = Message(
|
||||
TEST_MESSAGE_VIDEO_CHAT_STARTED = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
voice_chat_started=VoiceChatStarted(),
|
||||
video_chat_started=VideoChatStarted(),
|
||||
)
|
||||
TEST_MESSAGE_VOICE_CHAT_ENDED = Message(
|
||||
TEST_MESSAGE_VIDEO_CHAT_ENDED = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
voice_chat_ended=VoiceChatEnded(duration=42),
|
||||
video_chat_ended=VideoChatEnded(duration=42),
|
||||
)
|
||||
TEST_MESSAGE_VOICE_CHAT_PARTICIPANTS_INVITED = Message(
|
||||
TEST_MESSAGE_VIDEO_CHAT_PARTICIPANTS_INVITED = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
voice_chat_participants_invited=VoiceChatParticipantsInvited(
|
||||
video_chat_participants_invited=VideoChatParticipantsInvited(
|
||||
users=[User(id=69, is_bot=False, first_name="Test")]
|
||||
),
|
||||
)
|
||||
TEST_MESSAGE_VIDEO_CHAT_SCHEDULED = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
video_chat_scheduled=VideoChatScheduled(
|
||||
start_date=datetime.datetime.now(),
|
||||
),
|
||||
)
|
||||
TEST_MESSAGE_DICE = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
|
|
@ -348,6 +360,13 @@ TEST_MESSAGE_DICE = Message(
|
|||
dice=Dice(value=6, emoji="X"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
)
|
||||
TEST_MESSAGE_WEB_APP_DATA = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
web_app_data=WebAppData(data="test", button_text="Test"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
)
|
||||
TEST_MESSAGE_UNKNOWN = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
|
|
@ -391,13 +410,15 @@ class TestMessage:
|
|||
TEST_MESSAGE_MESSAGE_AUTO_DELETE_TIMER_CHANGED,
|
||||
ContentType.MESSAGE_AUTO_DELETE_TIMER_CHANGED,
|
||||
],
|
||||
[TEST_MESSAGE_VOICE_CHAT_STARTED, ContentType.VOICE_CHAT_STARTED],
|
||||
[TEST_MESSAGE_VOICE_CHAT_ENDED, ContentType.VOICE_CHAT_ENDED],
|
||||
[TEST_MESSAGE_VIDEO_CHAT_SCHEDULED, ContentType.VIDEO_CHAT_SCHEDULED],
|
||||
[TEST_MESSAGE_VIDEO_CHAT_STARTED, ContentType.VIDEO_CHAT_STARTED],
|
||||
[TEST_MESSAGE_VIDEO_CHAT_ENDED, ContentType.VIDEO_CHAT_ENDED],
|
||||
[
|
||||
TEST_MESSAGE_VOICE_CHAT_PARTICIPANTS_INVITED,
|
||||
ContentType.VOICE_CHAT_PARTICIPANTS_INVITED,
|
||||
TEST_MESSAGE_VIDEO_CHAT_PARTICIPANTS_INVITED,
|
||||
ContentType.VIDEO_CHAT_PARTICIPANTS_INVITED,
|
||||
],
|
||||
[TEST_MESSAGE_DICE, ContentType.DICE],
|
||||
[TEST_MESSAGE_WEB_APP_DATA, ContentType.WEB_APP_DATA],
|
||||
[TEST_MESSAGE_UNKNOWN, ContentType.UNKNOWN],
|
||||
],
|
||||
)
|
||||
|
|
@ -535,9 +556,9 @@ class TestMessage:
|
|||
[TEST_MESSAGE_PASSPORT_DATA, None],
|
||||
[TEST_MESSAGE_POLL, SendPoll],
|
||||
[TEST_MESSAGE_MESSAGE_AUTO_DELETE_TIMER_CHANGED, None],
|
||||
[TEST_MESSAGE_VOICE_CHAT_STARTED, None],
|
||||
[TEST_MESSAGE_VOICE_CHAT_ENDED, None],
|
||||
[TEST_MESSAGE_VOICE_CHAT_PARTICIPANTS_INVITED, None],
|
||||
[TEST_MESSAGE_VIDEO_CHAT_STARTED, None],
|
||||
[TEST_MESSAGE_VIDEO_CHAT_ENDED, None],
|
||||
[TEST_MESSAGE_VIDEO_CHAT_PARTICIPANTS_INVITED, None],
|
||||
[TEST_MESSAGE_DICE, SendDice],
|
||||
[TEST_MESSAGE_UNKNOWN, None],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ class TestDispatcher:
|
|||
assert dp.get("foo", 42) == 42
|
||||
|
||||
dp["foo"] = 1
|
||||
assert dp._data["foo"] == 1
|
||||
assert dp.workflow_data["foo"] == 1
|
||||
assert dp["foo"] == 1
|
||||
|
||||
del dp["foo"]
|
||||
assert "foo" not in dp._data
|
||||
assert "foo" not in dp.workflow_data
|
||||
|
||||
def test_storage_property(self, dispatcher: Dispatcher):
|
||||
assert dispatcher.storage is dispatcher.fsm.storage
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ class TestChatMemberUpdatedStatusFilter:
|
|||
"can_be_edited": True,
|
||||
"can_manage_chat": True,
|
||||
"can_delete_messages": True,
|
||||
"can_manage_voice_chats": True,
|
||||
"can_manage_video_chats": True,
|
||||
"can_restrict_members": True,
|
||||
"can_promote_members": True,
|
||||
"can_change_info": True,
|
||||
|
|
|
|||
80
tests/test_utils/test_web_app.py
Normal file
80
tests/test_utils/test_web_app.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.utils.web_app import (
|
||||
WebAppInitData,
|
||||
check_webapp_signature,
|
||||
parse_webapp_init_data,
|
||||
safe_parse_webapp_init_data,
|
||||
)
|
||||
|
||||
|
||||
class TestWebApp:
|
||||
@pytest.mark.parametrize(
|
||||
"token,case,result",
|
||||
[
|
||||
[
|
||||
"42:TEST",
|
||||
"auth_date=1650385342"
|
||||
"&user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D"
|
||||
"&query_id=test"
|
||||
"&hash=46d2ea5e32911ec8d30999b56247654460c0d20949b6277af519e76271182803",
|
||||
True,
|
||||
],
|
||||
[
|
||||
"42:INVALID",
|
||||
"auth_date=1650385342"
|
||||
"&user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D"
|
||||
"&query_id=test"
|
||||
"&hash=46d2ea5e32911ec8d30999b56247654460c0d20949b6277af519e76271182803",
|
||||
False,
|
||||
],
|
||||
[
|
||||
"42:TEST",
|
||||
"user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D&query_id=test&hash=test",
|
||||
False,
|
||||
],
|
||||
[
|
||||
"42:TEST",
|
||||
"user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D&query_id=test",
|
||||
False,
|
||||
],
|
||||
["42:TEST", "", False],
|
||||
["42:TEST", "test&foo=bar=baz", False],
|
||||
],
|
||||
)
|
||||
def test_check_webapp_signature(self, token, case, result):
|
||||
assert check_webapp_signature(token, case) is result
|
||||
|
||||
def test_parse_web_app_init_data(self):
|
||||
parsed = parse_webapp_init_data(
|
||||
"auth_date=1650385342"
|
||||
"&user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D"
|
||||
"&query_id=test"
|
||||
"&hash=46d2ea5e32911ec8d30999b56247654460c0d20949b6277af519e76271182803",
|
||||
)
|
||||
assert isinstance(parsed, WebAppInitData)
|
||||
assert parsed.user
|
||||
assert parsed.user.first_name == "Test"
|
||||
assert parsed.user.id == 42
|
||||
assert parsed.query_id == "test"
|
||||
assert parsed.hash == "46d2ea5e32911ec8d30999b56247654460c0d20949b6277af519e76271182803"
|
||||
assert parsed.auth_date.year == 2022
|
||||
|
||||
def test_valid_safe_parse_webapp_init_data(self):
|
||||
assert safe_parse_webapp_init_data(
|
||||
"42:TEST",
|
||||
"auth_date=1650385342"
|
||||
"&user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D"
|
||||
"&query_id=test"
|
||||
"&hash=46d2ea5e32911ec8d30999b56247654460c0d20949b6277af519e76271182803",
|
||||
)
|
||||
|
||||
def test_invalid_safe_parse_webapp_init_data(self):
|
||||
with pytest.raises(ValueError):
|
||||
safe_parse_webapp_init_data(
|
||||
"42:TOKEN",
|
||||
"auth_date=1650385342"
|
||||
"&user=%7B%22id%22%3A42%2C%22first_name%22%3A%22Test%22%7D"
|
||||
"&query_id=test"
|
||||
"&hash=test",
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue