Skip to content

Commit

Permalink
[3.x] Bot API 5.3 + Improvements (#618)
Browse files Browse the repository at this point in the history
* Regenerate API

* Update code

* Fixed command filter for photos

* Fix tests so they are able to run

* Test new and renamed API methods

* Reformat files

* Fix outer_middleware resolution (#637) (#640)

* Fix outer_middleware resolution (#637)

* Reformat files

* Reorder routers when resolve middlewares

Co-authored-by: Alex Root Junior <jroot.junior@gmail.com>

* Added possibility to use empty callback data factory filter

* Rename KeyboardConstructor to KeyboardBuilder

* Fixed type

Co-authored-by: evgfilim1 <evgfilim1@yandex.ru>
  • Loading branch information
JrooTJunior and evgfilim1 committed Jul 28, 2021
1 parent 4599913 commit ac2b0bb
Show file tree
Hide file tree
Showing 69 changed files with 1,222 additions and 205 deletions.
139 changes: 128 additions & 11 deletions aiogram/client/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
AnswerInlineQuery,
AnswerPreCheckoutQuery,
AnswerShippingQuery,
BanChatMember,
Close,
CopyMessage,
CreateChatInviteLink,
CreateNewStickerSet,
DeleteChatPhoto,
DeleteChatStickerSet,
DeleteMessage,
DeleteMyCommands,
DeleteStickerFromSet,
DeleteWebhook,
EditChatInviteLink,
Expand All @@ -48,6 +50,7 @@
GetChat,
GetChatAdministrators,
GetChatMember,
GetChatMemberCount,
GetChatMembersCount,
GetFile,
GetGameHighScores,
Expand Down Expand Up @@ -105,9 +108,15 @@
from ..types import (
UNSET,
BotCommand,
BotCommandScope,
Chat,
ChatInviteLink,
ChatMember,
ChatMemberAdministrator,
ChatMemberBanned,
ChatMemberLeft,
ChatMemberMember,
ChatMemberOwner,
ChatMemberRestricted,
ChatPermissions,
Downloadable,
File,
Expand Down Expand Up @@ -1410,6 +1419,35 @@ async def get_file(
)
return await self(call, request_timeout=request_timeout)

async def ban_chat_member(
self,
chat_id: Union[int, str],
user_id: int,
until_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None,
revoke_messages: Optional[bool] = None,
request_timeout: Optional[int] = None,
) -> bool:
"""
Use this method to ban a user in a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless `unbanned <https://core.telegram.org/bots/api#unbanchatmember>`_ first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success.
Source: https://core.telegram.org/bots/api#banchatmember
:param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format :code:`@channelusername`)
:param user_id: Unique identifier of the target user
:param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever. Applied for supergroups and channels only.
:param revoke_messages: Pass :code:`True` to delete all messages from the chat for the user that is being removed. If :code:`False`, the user will be able to see messages in the group that were sent before the user was removed. Always :code:`True` for supergroups and channels.
:param request_timeout: Request timeout
:return: In the case of supergroups and channels, the user will not be able to return to
the chat on their own using invite links, etc. Returns True on success.
"""
call = BanChatMember(
chat_id=chat_id,
user_id=user_id,
until_date=until_date,
revoke_messages=revoke_messages,
)
return await self(call, request_timeout=request_timeout)

async def kick_chat_member(
self,
chat_id: Union[int, str],
Expand All @@ -1419,9 +1457,13 @@ async def kick_chat_member(
request_timeout: Optional[int] = None,
) -> bool:
"""
Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless `unbanned <https://core.telegram.org/bots/api#unbanchatmember>`_ first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success.
.. warning:
Renamed from :code:`kickChatMember` in 5.3 bot API version and can be removed in near future
Use this method to ban a user in a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless `unbanned <https://core.telegram.org/bots/api#unbanchatmember>`_ first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success.
Source: https://core.telegram.org/bots/api#kickchatmember
Source: https://core.telegram.org/bots/api#banchatmember
:param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format :code:`@channelusername`)
:param user_id: Unique identifier of the target user
Expand All @@ -1447,7 +1489,7 @@ async def unban_chat_member(
request_timeout: Optional[int] = None,
) -> bool:
"""
Use this method to unban a previously kicked user in a supergroup or channel. The user will **not** return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be **removed** from the chat. If you don't want this, use the parameter *only_if_banned*. Returns :code:`True` on success.
Use this method to unban a previously banned user in a supergroup or channel. The user will **not** return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be **removed** from the chat. If you don't want this, use the parameter *only_if_banned*. Returns :code:`True` on success.
Source: https://core.telegram.org/bots/api#unbanchatmember
Expand Down Expand Up @@ -1884,7 +1926,16 @@ async def get_chat_administrators(
self,
chat_id: Union[int, str],
request_timeout: Optional[int] = None,
) -> List[ChatMember]:
) -> List[
Union[
ChatMemberOwner,
ChatMemberAdministrator,
ChatMemberMember,
ChatMemberRestricted,
ChatMemberLeft,
ChatMemberBanned,
]
]:
"""
Use this method to get a list of administrators in a chat. On success, returns an Array of :class:`aiogram.types.chat_member.ChatMember` objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned.
Expand All @@ -1902,15 +1953,38 @@ async def get_chat_administrators(
)
return await self(call, request_timeout=request_timeout)

async def get_chat_member_count(
self,
chat_id: Union[int, str],
request_timeout: Optional[int] = None,
) -> int:
"""
Use this method to get the number of members in a chat. Returns *Int* on success.
Source: https://core.telegram.org/bots/api#getchatmembercount
:param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)
:param request_timeout: Request timeout
:return: Returns Int on success.
"""
call = GetChatMemberCount(
chat_id=chat_id,
)
return await self(call, request_timeout=request_timeout)

async def get_chat_members_count(
self,
chat_id: Union[int, str],
request_timeout: Optional[int] = None,
) -> int:
"""
.. warning:
Renamed from :code:`getChatMembersCount` in 5.3 bot API version and can be removed in near future
Use this method to get the number of members in a chat. Returns *Int* on success.
Source: https://core.telegram.org/bots/api#getchatmemberscount
Source: https://core.telegram.org/bots/api#getchatmembercount
:param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)
:param request_timeout: Request timeout
Expand All @@ -1926,7 +2000,14 @@ async def get_chat_member(
chat_id: Union[int, str],
user_id: int,
request_timeout: Optional[int] = None,
) -> ChatMember:
) -> Union[
ChatMemberOwner,
ChatMemberAdministrator,
ChatMemberMember,
ChatMemberRestricted,
ChatMemberLeft,
ChatMemberBanned,
]:
"""
Use this method to get information about a member of a chat. Returns a :class:`aiogram.types.chat_member.ChatMember` object on success.
Expand Down Expand Up @@ -2022,35 +2103,71 @@ async def answer_callback_query(
async def set_my_commands(
self,
commands: List[BotCommand],
scope: Optional[BotCommandScope] = None,
language_code: Optional[str] = None,
request_timeout: Optional[int] = None,
) -> bool:
"""
Use this method to change the list of the bot's commands. Returns :code:`True` on success.
Use this method to change the list of the bot's commands. See `https://core.telegram.org/bots#commands <https://core.telegram.org/bots#commands>`_`https://core.telegram.org/bots#commands <https://core.telegram.org/bots#commands>`_ for more details about bot commands. Returns :code:`True` on success.
Source: https://core.telegram.org/bots/api#setmycommands
:param commands: A JSON-serialized list of bot commands to be set as the list of the bot's commands. At most 100 commands can be specified.
:param scope: A JSON-serialized object, describing scope of users for which the commands are relevant. Defaults to :class:`aiogram.types.bot_command_scope_default.BotCommandScopeDefault`.
:param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands
:param request_timeout: Request timeout
:return: Returns True on success.
"""
call = SetMyCommands(
commands=commands,
scope=scope,
language_code=language_code,
)
return await self(call, request_timeout=request_timeout)

async def delete_my_commands(
self,
scope: Optional[BotCommandScope] = None,
language_code: Optional[str] = None,
request_timeout: Optional[int] = None,
) -> bool:
"""
Use this method to delete the list of the bot's commands for the given scope and user language. After deletion, `higher level commands <https://core.telegram.org/bots/api#determining-list-of-commands>`_ will be shown to affected users. Returns :code:`True` on success.
Source: https://core.telegram.org/bots/api#deletemycommands
:param scope: A JSON-serialized object, describing scope of users for which the commands are relevant. Defaults to :class:`aiogram.types.bot_command_scope_default.BotCommandScopeDefault`.
:param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands
:param request_timeout: Request timeout
:return: Returns True on success.
"""
call = DeleteMyCommands(
scope=scope,
language_code=language_code,
)
return await self(call, request_timeout=request_timeout)

async def get_my_commands(
self,
scope: Optional[BotCommandScope] = None,
language_code: Optional[str] = None,
request_timeout: Optional[int] = None,
) -> List[BotCommand]:
"""
Use this method to get the current list of the bot's commands. Requires no parameters. Returns Array of :class:`aiogram.types.bot_command.BotCommand` on success.
Use this method to get the current list of the bot's commands for the given scope and user language. Returns Array of :class:`aiogram.types.bot_command.BotCommand` on success. If commands aren't set, an empty list is returned.
Source: https://core.telegram.org/bots/api#getmycommands
:param scope: A JSON-serialized object, describing scope of users. Defaults to :class:`aiogram.types.bot_command_scope_default.BotCommandScopeDefault`.
:param language_code: A two-letter ISO 639-1 language code or an empty string
:param request_timeout: Request timeout
:return: Returns Array of BotCommand on success.
:return: Returns Array of BotCommand on success. If commands aren't set, an empty list is
returned.
"""
call = GetMyCommands()
call = GetMyCommands(
scope=scope,
language_code=language_code,
)
return await self(call, request_timeout=request_timeout)

# =============================================================================================
Expand Down
16 changes: 10 additions & 6 deletions aiogram/dispatcher/event/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,19 @@ def _resolve_filters_chain(self) -> Generator[Type[BaseFilter], None, None]:
yield filter_
registry.append(filter_)

def _resolve_inner_middlewares(self) -> List[MiddlewareType]:
def _resolve_middlewares(self, *, outer: bool = False) -> List[MiddlewareType]:
"""
Get all inner middlewares in an tree
Get all middlewares in a tree
:param *:
"""
middlewares = []

for router in self.router.chain_head:
for router in reversed(list(self.router.chain_head)):
observer = router.observers[self.event_name]
middlewares.extend(observer.middlewares)
if outer:
middlewares.extend(observer.outer_middlewares)
else:
middlewares.extend(observer.middlewares)
return middlewares

def resolve_filters(self, full_config: Dict[str, Any]) -> List[BaseFilter]:
Expand Down Expand Up @@ -131,7 +135,7 @@ async def trigger(self, event: TelegramObject, **kwargs: Any) -> Any:
Propagate event to handlers and stops propagation on first match.
Handler will be called when all its filters is pass.
"""
wrapped_outer = self._wrap_middleware(self.outer_middlewares, self._trigger)
wrapped_outer = self._wrap_middleware(self._resolve_middlewares(outer=True), self._trigger)
return await wrapped_outer(event, kwargs)

async def _trigger(self, event: TelegramObject, **kwargs: Any) -> Any:
Expand All @@ -141,7 +145,7 @@ async def _trigger(self, event: TelegramObject, **kwargs: Any) -> Any:
kwargs.update(data)
try:
wrapped_inner = self._wrap_middleware(
self._resolve_inner_middlewares(), handler.call
self._resolve_middlewares(), handler.call
)
return await wrapped_inner(event, kwargs)
except SkipHandler:
Expand Down
10 changes: 5 additions & 5 deletions aiogram/dispatcher/filters/callback_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from decimal import Decimal
from enum import Enum
from fractions import Fraction
from typing import TYPE_CHECKING, Any, Dict, Optional, Type, TypeVar, Union
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Type, TypeVar, Union
from uuid import UUID

from magic_filter import MagicFilter
Expand Down Expand Up @@ -86,7 +86,7 @@ def unpack(cls: Type[T], value: str) -> T:
return cls(**payload)

@classmethod
def filter(cls, rule: MagicFilter) -> CallbackQueryFilter:
def filter(cls, rule: Optional[MagicFilter] = None) -> CallbackQueryFilter:
return CallbackQueryFilter(callback_data=cls, rule=rule)

class Config:
Expand All @@ -95,17 +95,17 @@ class Config:

class CallbackQueryFilter(BaseFilter):
callback_data: Type[CallbackData]
rule: MagicFilter
rule: Optional[MagicFilter] = None

async def __call__(self, query: CallbackQuery) -> Union[bool, Dict[str, Any]]:
async def __call__(self, query: CallbackQuery) -> Union[Literal[False], Dict[str, Any]]:
if not isinstance(query, CallbackQuery) or not query.data:
return False
try:
callback_data = self.callback_data.unpack(query.data)
except (TypeError, ValueError):
return False

if self.rule.resolve(callback_data):
if self.rule is None or self.rule.resolve(callback_data):
return {"callback_data": callback_data}
return False

Expand Down
2 changes: 1 addition & 1 deletion aiogram/dispatcher/filters/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async def __call__(self, message: Message, bot: Bot) -> Union[bool, Dict[str, An
return False

try:
command = await self.parse_command(text=cast(str, message.text), bot=bot)
command = await self.parse_command(text=text, bot=bot)
except CommandException:
return False
return {"command": command}
Expand Down
6 changes: 6 additions & 0 deletions aiogram/methods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .answer_inline_query import AnswerInlineQuery
from .answer_pre_checkout_query import AnswerPreCheckoutQuery
from .answer_shipping_query import AnswerShippingQuery
from .ban_chat_member import BanChatMember
from .base import Request, Response, TelegramMethod
from .close import Close
from .copy_message import CopyMessage
Expand All @@ -11,6 +12,7 @@
from .delete_chat_photo import DeleteChatPhoto
from .delete_chat_sticker_set import DeleteChatStickerSet
from .delete_message import DeleteMessage
from .delete_my_commands import DeleteMyCommands
from .delete_sticker_from_set import DeleteStickerFromSet
from .delete_webhook import DeleteWebhook
from .edit_chat_invite_link import EditChatInviteLink
Expand All @@ -24,6 +26,7 @@
from .get_chat import GetChat
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_file import GetFile
from .get_game_high_scores import GetGameHighScores
Expand Down Expand Up @@ -109,6 +112,7 @@
"SendChatAction",
"GetUserProfilePhotos",
"GetFile",
"BanChatMember",
"KickChatMember",
"UnbanChatMember",
"RestrictChatMember",
Expand All @@ -129,12 +133,14 @@
"LeaveChat",
"GetChat",
"GetChatAdministrators",
"GetChatMemberCount",
"GetChatMembersCount",
"GetChatMember",
"SetChatStickerSet",
"DeleteChatStickerSet",
"AnswerCallbackQuery",
"SetMyCommands",
"DeleteMyCommands",
"GetMyCommands",
"EditMessageText",
"EditMessageCaption",
Expand Down

0 comments on commit ac2b0bb

Please sign in to comment.