Telegram API 5.3 (#610)

* docs: api version update

* feat: personalized commands

* feat: custom placeholders

* refactor: ChatMember split

* fix: old names for ChatMemberStatus

* refactor: renamed kickChatMember to banChatMember

* style: align params

* refactor: renamed getChatMembersCount to getChatMemberCount (#614)

* feat: resolve ChatMember

* refactor: renamed BotCommandScopeTypes (similar to code style)

* refactor: resolve is a static method

* Construct BotCommandScope from type

* Make BotCommandScope classmethod instead of method

* Use classmethod for ChatMember resolve method

Co-authored-by: Hoi Dmytro <dmytro.hoi@gmail.com>
Co-authored-by: Alex Root Junior <jroot.junior@gmail.com>
This commit is contained in:
Oleg A 2021-07-04 23:52:55 +03:00 committed by GitHub
parent e70a76ff63
commit f18e4491c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 455 additions and 126 deletions

View file

@ -6,7 +6,7 @@
[![PyPi status](https://img.shields.io/pypi/status/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram)
[![Downloads](https://img.shields.io/pypi/dm/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram)
[![Supported python versions](https://img.shields.io/pypi/pyversions/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram)
[![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-5.2-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api)
[![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-5.3-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api)
[![Documentation Status](https://img.shields.io/readthedocs/aiogram?style=flat-square)](http://docs.aiogram.dev/en/latest/?badge=latest)
[![Github issues](https://img.shields.io/github/issues/aiogram/aiogram.svg?style=flat-square)](https://github.com/aiogram/aiogram/issues)
[![MIT License](https://img.shields.io/pypi/l/aiogram.svg?style=flat-square)](https://opensource.org/licenses/MIT)

View file

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

View file

@ -43,5 +43,5 @@ __all__ = (
'utils',
)
__version__ = '2.13'
__api_version__ = '5.2'
__version__ = '2.14'
__api_version__ = '5.3'

View file

@ -189,7 +189,7 @@ class Methods(Helper):
"""
Helper for Telegram API Methods listed on https://core.telegram.org/bots/api
List is updated to Bot API 5.2
List is updated to Bot API 5.3
"""
mode = HelperMode.lowerCamelCase
@ -225,6 +225,7 @@ class Methods(Helper):
GET_USER_PROFILE_PHOTOS = Item() # getUserProfilePhotos
GET_FILE = Item() # getFile
KICK_CHAT_MEMBER = Item() # kickChatMember
BAN_CHAT_MEMBER = Item() # banChatMember
UNBAN_CHAT_MEMBER = Item() # unbanChatMember
RESTRICT_CHAT_MEMBER = Item() # restrictChatMember
PROMOTE_CHAT_MEMBER = Item() # promoteChatMember
@ -244,12 +245,14 @@ class Methods(Helper):
LEAVE_CHAT = Item() # leaveChat
GET_CHAT = Item() # getChat
GET_CHAT_ADMINISTRATORS = Item() # getChatAdministrators
GET_CHAT_MEMBERS_COUNT = Item() # getChatMembersCount
GET_CHAT_MEMBER_COUNT = Item() # getChatMemberCount
GET_CHAT_MEMBERS_COUNT = Item() # getChatMembersCount (renamed to getChatMemberCount)
GET_CHAT_MEMBER = Item() # getChatMember
SET_CHAT_STICKER_SET = Item() # setChatStickerSet
DELETE_CHAT_STICKER_SET = Item() # deleteChatStickerSet
ANSWER_CALLBACK_QUERY = Item() # answerCallbackQuery
SET_MY_COMMANDS = Item() # setMyCommands
DELETE_MY_COMMANDS = Item() # deleteMyCommands
GET_MY_COMMANDS = Item() # getMyCommands
# Updating messages

View file

@ -1562,41 +1562,42 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
result = await self.request(api.Methods.GET_FILE, payload)
return types.File(**result)
async def kick_chat_member(self,
chat_id: typing.Union[base.Integer, base.String],
user_id: base.Integer,
until_date: typing.Union[base.Integer, datetime.datetime,
datetime.timedelta, None] = None,
revoke_messages: typing.Optional[base.Boolean] = None,
) -> base.Boolean:
async def ban_chat_member(self,
chat_id: typing.Union[base.Integer, base.String],
user_id: base.Integer,
until_date: typing.Union[base.Integer, datetime.datetime,
datetime.timedelta, None] = None,
revoke_messages: typing.Optional[base.Boolean] = None,
) -> base.Boolean:
"""
Use this method to kick a user from a group, a supergroup or a channel.
In the case of supergroups and channels, the user will not be able to return
to the chat on their own using invite links, etc., unless unbanned first.
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 first. The bot must be an
administrator in the chat for this to work and must have the
appropriate admin rights. Returns True on success.
The bot must be an administrator in the chat for this to work and must have
the appropriate admin rights.
Source: https://core.telegram.org/bots/api#banchatmember
Source: https://core.telegram.org/bots/api#kickchatmember
:param chat_id: Unique identifier for the target group or username of the
target supergroup or channel (in the format @channelusername)
:param chat_id: Unique identifier for the target group or
username of the target supergroup or channel (in the format
@channelusername)
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
:param user_id: Unique identifier of the target user
:type user_id: :obj:`base.Integer`
:param until_date: Date when the user will be unbanned. 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.
:type until_date: :obj:`typing.Union[base.Integer, datetime.datetime,
datetime.timedelta, None]`
: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.
:type until_date: :obj:`typing.Union[base.Integer,
datetime.datetime, datetime.timedelta, None]`
:param revoke_messages: Pass True to delete all messages from the chat for
the user that is being removed. If False, the user will be able to see
messages in the group that were sent before the user was removed. Always
True for supergroups and channels.
:param revoke_messages: Pass True to delete all messages from
the chat for the user that is being removed. If False, the user
will be able to see messages in the group that were sent before
the user was removed. Always True for supergroups and channels.
:type revoke_messages: :obj:`typing.Optional[base.Boolean]`
:return: Returns True on success
@ -1605,7 +1606,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
until_date = prepare_arg(until_date)
payload = generate_payload(**locals())
return await self.request(api.Methods.KICK_CHAT_MEMBER, payload)
return await self.request(api.Methods.BAN_CHAT_MEMBER, payload)
async def kick_chat_member(self,
chat_id: typing.Union[base.Integer, base.String],
user_id: base.Integer,
until_date: typing.Union[base.Integer, datetime.datetime,
datetime.timedelta, None] = None,
revoke_messages: typing.Optional[base.Boolean] = None,
) -> base.Boolean:
"""Renamed to ban_chat_member."""
return await self.ban_chat_member(
chat_id=chat_id,
user_id=user_id,
until_date=until_date,
revoke_messages=revoke_messages,
)
async def unban_chat_member(self,
chat_id: typing.Union[base.Integer, base.String],
@ -2130,13 +2146,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
payload = generate_payload(**locals())
result = await self.request(api.Methods.GET_CHAT_ADMINISTRATORS, payload)
return [types.ChatMember(**chatmember) for chatmember in result]
return [types.ChatMember.resolve(**chat_member) for chat_member in result]
async def get_chat_members_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer:
async def get_chat_member_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer:
"""
Use this method to get the number of members in a chat.
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
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
@ -2145,7 +2161,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
"""
payload = generate_payload(**locals())
return await self.request(api.Methods.GET_CHAT_MEMBERS_COUNT, payload)
return await self.request(api.Methods.GET_CHAT_MEMBER_COUNT, payload)
async def get_chat_members_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer:
"""Renamed to get_chat_member_count."""
return await self.get_chat_member_count(chat_id)
async def get_chat_member(self, chat_id: typing.Union[base.Integer, base.String],
user_id: base.Integer) -> types.ChatMember:
@ -2164,7 +2184,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
payload = generate_payload(**locals())
result = await self.request(api.Methods.GET_CHAT_MEMBER, payload)
return types.ChatMember(**result)
return types.ChatMember.resolve(**result)
async def set_chat_sticker_set(self, chat_id: typing.Union[base.Integer, base.String],
sticker_set_name: base.String) -> base.Boolean:
@ -2241,31 +2261,95 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
return await self.request(api.Methods.ANSWER_CALLBACK_QUERY, payload)
async def set_my_commands(self, commands: typing.List[types.BotCommand]) -> base.Boolean:
async def set_my_commands(self,
commands: typing.List[types.BotCommand],
scope: typing.Optional[types.BotCommandScope] = None,
language_code: typing.Optional[base.String] = None,
) -> base.Boolean:
"""
Use this method to change the list of the bot's commands.
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 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.
:type commands: :obj: `typing.List[types.BotCommand]`
:param scope: A JSON-serialized object, describing scope of
users for which the commands are relevant. Defaults to
BotCommandScopeDefault.
:type scope: :obj: `typing.Optional[types.BotCommandScope]`
: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
:type language_code: :obj: `typing.Optional[base.String]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
commands = prepare_arg(commands)
scope = prepare_arg(scope)
payload = generate_payload(**locals())
return await self.request(api.Methods.SET_MY_COMMANDS, payload)
async def get_my_commands(self) -> typing.List[types.BotCommand]:
async def delete_my_commands(self,
scope: typing.Optional[types.BotCommandScope] = None,
language_code: typing.Optional[base.String] = None,
) -> base.Boolean:
"""
Use this method to get the current list of the bot's commands.
Use this method to delete the list of the bot's commands for the
given scope and user language. After deletion, higher level
commands will be shown to affected users.
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
BotCommandScopeDefault.
:type scope: :obj: `typing.Optional[types.BotCommandScope]`
: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
:type language_code: :obj: `typing.Optional[base.String]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
scope = prepare_arg(scope)
payload = generate_payload(**locals())
return await self.request(api.Methods.DELETE_MY_COMMANDS, payload)
async def get_my_commands(self,
scope: typing.Optional[types.BotCommandScope] = None,
language_code: typing.Optional[base.String] = None,
) -> typing.List[types.BotCommand]:
"""
Use this method to get the current list of the bot's commands
for the given scope and user language. Returns Array of
BotCommand on success. If commands aren't set, an empty list is
returned.
Source: https://core.telegram.org/bots/api#getmycommands
:return: Returns Array of BotCommand on success.
:param scope: A JSON-serialized object, describing scope of
users for which the commands are relevant. Defaults to
BotCommandScopeDefault.
:type scope: :obj: `typing.Optional[types.BotCommandScope]`
: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
:type language_code: :obj: `typing.Optional[base.String]`
:return: Returns Array of BotCommand on success or empty list.
:rtype: :obj:`typing.List[types.BotCommand]`
"""
scope = prepare_arg(scope)
payload = generate_payload(**locals())
result = await self.request(api.Methods.GET_MY_COMMANDS, payload)

View file

@ -4,6 +4,10 @@ from .animation import Animation
from .audio import Audio
from .auth_widget_data import AuthWidgetData
from .bot_command import BotCommand
from .bot_command_scope import BotCommandScope, BotCommandScopeAllChatAdministrators, \
BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, BotCommandScopeChat, \
BotCommandScopeChatAdministrators, BotCommandScopeChatMember, \
BotCommandScopeDefault, BotCommandScopeType
from .callback_game import CallbackGame
from .callback_query import CallbackQuery
from .chat import Chat, ChatActions, ChatType
@ -82,6 +86,15 @@ __all__ = (
'Audio',
'AuthWidgetData',
'BotCommand',
'BotCommandScope',
'BotCommandScopeAllChatAdministrators',
'BotCommandScopeAllGroupChats',
'BotCommandScopeAllPrivateChats',
'BotCommandScopeChat',
'BotCommandScopeChatAdministrators',
'BotCommandScopeChatMember',
'BotCommandScopeDefault',
'BotCommandScopeType',
'CallbackGame',
'CallbackQuery',
'Chat',

View file

@ -0,0 +1,121 @@
import typing
from . import base, fields
from ..utils import helper
class BotCommandScopeType(helper.Helper):
mode = helper.HelperMode.lowercase
DEFAULT = helper.Item() # default
ALL_PRIVATE_CHATS = helper.Item() # all_private_chats
ALL_GROUP_CHATS = helper.Item() # all_group_chats
ALL_CHAT_ADMINISTRATORS = helper.Item() # all_chat_administrators
CHAT = helper.Item() # chat
CHAT_ADMINISTRATORS = helper.Item() # chat_administrators
CHAT_MEMBER = helper.Item() # chat_member
class BotCommandScope(base.TelegramObject):
"""
This object represents the scope to which bot commands are applied.
Currently, the following 7 scopes are supported:
BotCommandScopeDefault
BotCommandScopeAllPrivateChats
BotCommandScopeAllGroupChats
BotCommandScopeAllChatAdministrators
BotCommandScopeChat
BotCommandScopeChatAdministrators
BotCommandScopeChatMember
https://core.telegram.org/bots/api#botcommandscope
"""
type: base.String = fields.Field()
@classmethod
def from_type(cls, type: str, **kwargs: typing.Any):
if type == BotCommandScopeType.DEFAULT:
return BotCommandScopeDefault(type=type, **kwargs)
if type == BotCommandScopeType.ALL_PRIVATE_CHATS:
return BotCommandScopeAllPrivateChats(type=type, **kwargs)
if type == BotCommandScopeType.ALL_GROUP_CHATS:
return BotCommandScopeAllGroupChats(type=type, **kwargs)
if type == BotCommandScopeType.ALL_CHAT_ADMINISTRATORS:
return BotCommandScopeAllChatAdministrators(type=type, **kwargs)
if type == BotCommandScopeType.CHAT:
return BotCommandScopeChat(type=type, **kwargs)
if type == BotCommandScopeType.CHAT_ADMINISTRATORS:
return BotCommandScopeChatAdministrators(type=type, **kwargs)
if type == BotCommandScopeType.CHAT_MEMBER:
return BotCommandScopeChatMember(type=type, **kwargs)
raise ValueError(f"Unknown BotCommandScope type {type!r}")
class BotCommandScopeDefault(BotCommandScope):
"""
Represents the default scope of bot commands.
Default commands are used if no commands with a narrower scope are
specified for the user.
"""
type = fields.Field(default=BotCommandScopeType.DEFAULT)
class BotCommandScopeAllPrivateChats(BotCommandScope):
"""
Represents the scope of bot commands, covering all private chats.
"""
type = fields.Field(default=BotCommandScopeType.ALL_PRIVATE_CHATS)
class BotCommandScopeAllGroupChats(BotCommandScope):
"""
Represents the scope of bot commands, covering all group and
supergroup chats.
"""
type = fields.Field(default=BotCommandScopeType.ALL_GROUP_CHATS)
class BotCommandScopeAllChatAdministrators(BotCommandScope):
"""
Represents the scope of bot commands, covering all group and
supergroup chat administrators.
"""
type = fields.Field(default=BotCommandScopeType.ALL_CHAT_ADMINISTRATORS)
class BotCommandScopeChat(BotCommandScope):
"""
Represents the scope of bot commands, covering a specific chat.
"""
type = fields.Field(default=BotCommandScopeType.CHAT)
chat_id: typing.Union[base.String, base.Integer] = fields.Field()
def __init__(self, chat_id: typing.Union[base.String, base.Integer], **kwargs):
super().__init__(chat_id=chat_id, **kwargs)
class BotCommandScopeChatAdministrators(BotCommandScopeChat):
"""
Represents the scope of bot commands, covering all administrators
of a specific group or supergroup chat.
"""
type = fields.Field(default=BotCommandScopeType.CHAT_ADMINISTRATORS)
chat_id: typing.Union[base.String, base.Integer] = fields.Field()
class BotCommandScopeChatMember(BotCommandScopeChat):
"""
Represents the scope of bot commands, covering a specific member of
a group or supergroup chat.
"""
type = fields.Field(default=BotCommandScopeType.CHAT_MEMBER)
chat_id: typing.Union[base.String, base.Integer] = fields.Field()
user_id: base.Integer = fields.Field()
def __init__(
self,
chat_id: typing.Union[base.String, base.Integer],
user_id: base.Integer,
**kwargs,
):
super().__init__(chat_id=chat_id, user_id=user_id, **kwargs)

View file

@ -301,7 +301,7 @@ class Chat(base.TelegramObject):
can_send_other_messages=can_send_other_messages,
can_add_web_page_previews=can_add_web_page_previews)
async def promote(self,
async def promote(self,
user_id: base.Integer,
is_anonymous: typing.Optional[base.Boolean] = None,
can_change_info: typing.Optional[base.Boolean] = None,
@ -321,36 +321,36 @@ class Chat(base.TelegramObject):
:param user_id: Unique identifier of the target user
:type user_id: :obj:`base.Integer`
:param is_anonymous: Pass True, if the administrator's presence in the chat is hidden
:type is_anonymous: :obj:`typing.Optional[base.Boolean]`
:param can_change_info: Pass True, if the administrator can change chat title, photo and other settings
:type can_change_info: :obj:`typing.Optional[base.Boolean]`
:param can_post_messages: Pass True, if the administrator can create channel posts, channels only
:type can_post_messages: :obj:`typing.Optional[base.Boolean]`
:param can_edit_messages: Pass True, if the administrator can edit messages of other users, channels only
:type can_edit_messages: :obj:`typing.Optional[base.Boolean]`
:param can_delete_messages: Pass True, if the administrator can delete messages of other users
:type can_delete_messages: :obj:`typing.Optional[base.Boolean]`
:param can_invite_users: Pass True, if the administrator can invite new users to the chat
:type can_invite_users: :obj:`typing.Optional[base.Boolean]`
:param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members
:type can_restrict_members: :obj:`typing.Optional[base.Boolean]`
:param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only
:type can_pin_messages: :obj:`typing.Optional[base.Boolean]`
:param can_promote_members: Pass True, if the administrator can add new administrators
with a subset of his own privileges or demote administrators that he has promoted,
directly or indirectly (promoted by administrators that were appointed by him)
:type can_promote_members: :obj:`typing.Optional[base.Boolean]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
@ -484,16 +484,20 @@ class Chat(base.TelegramObject):
"""
return await self.bot.get_chat_administrators(self.id)
async def get_members_count(self) -> base.Integer:
async def get_member_count(self) -> base.Integer:
"""
Use this method to get the number of members in a chat.
Source: https://core.telegram.org/bots/api#getchatmemberscount
Source: https://core.telegram.org/bots/api#getchatmembercount
:return: Returns Int on success.
:rtype: :obj:`base.Integer`
"""
return await self.bot.get_chat_members_count(self.id)
return await self.bot.get_chat_member_count(self.id)
async def get_members_count(self) -> base.Integer:
"""Renamed to get_member_count."""
return await self.get_member_count(self.id)
async def get_member(self, user_id: base.Integer) -> ChatMember:
"""

View file

@ -1,53 +1,11 @@
import datetime
from typing import Optional
from . import base
from . import fields
from . import base, fields
from .user import User
from ..utils import helper
class ChatMember(base.TelegramObject):
"""
This object contains information about one member of a chat.
https://core.telegram.org/bots/api#chatmember
"""
user: User = fields.Field(base=User)
status: base.String = fields.Field()
custom_title: base.String = fields.Field()
is_anonymous: base.Boolean = fields.Field()
can_be_edited: base.Boolean = fields.Field()
can_manage_chat: base.Boolean = fields.Field()
can_post_messages: base.Boolean = fields.Field()
can_edit_messages: base.Boolean = fields.Field()
can_delete_messages: base.Boolean = fields.Field()
can_manage_voice_chats: base.Boolean = fields.Field()
can_restrict_members: base.Boolean = fields.Field()
can_promote_members: base.Boolean = fields.Field()
can_change_info: base.Boolean = fields.Field()
can_invite_users: base.Boolean = fields.Field()
can_pin_messages: base.Boolean = fields.Field()
is_member: base.Boolean = fields.Field()
can_send_messages: base.Boolean = fields.Field()
can_send_media_messages: base.Boolean = fields.Field()
can_send_polls: base.Boolean = fields.Field()
can_send_other_messages: base.Boolean = fields.Field()
can_add_web_page_previews: base.Boolean = fields.Field()
until_date: datetime.datetime = fields.DateTimeField()
def is_chat_creator(self) -> bool:
return ChatMemberStatus.is_chat_creator(self.status)
def is_chat_admin(self) -> bool:
return ChatMemberStatus.is_chat_admin(self.status)
def is_chat_member(self) -> bool:
return ChatMemberStatus.is_chat_member(self.status)
def __int__(self) -> int:
return self.user.id
class ChatMemberStatus(helper.Helper):
"""
Chat member status
@ -55,11 +13,13 @@ class ChatMemberStatus(helper.Helper):
mode = helper.HelperMode.lowercase
CREATOR = helper.Item() # creator
OWNER = CREATOR # creator
ADMINISTRATOR = helper.Item() # administrator
MEMBER = helper.Item() # member
RESTRICTED = helper.Item() # restricted
LEFT = helper.Item() # left
KICKED = helper.Item() # kicked
BANNED = KICKED # kicked
@classmethod
def is_chat_creator(cls, role: str) -> bool:
@ -72,3 +32,141 @@ class ChatMemberStatus(helper.Helper):
@classmethod
def is_chat_member(cls, role: str) -> bool:
return role in (cls.MEMBER, cls.ADMINISTRATOR, cls.CREATOR, cls.RESTRICTED)
@classmethod
def get_class_by_status(cls, status: str) -> Optional["ChatMember"]:
return {
cls.OWNER: ChatMemberOwner,
cls.ADMINISTRATOR: ChatMemberAdministrator,
cls.MEMBER: ChatMemberMember,
cls.RESTRICTED: ChatMemberRestricted,
cls.LEFT: ChatMemberLeft,
cls.BANNED: ChatMemberBanned,
}.get(status)
class ChatMember(base.TelegramObject):
"""
This object contains information about one member of a chat.
Currently, the following 6 types of chat members are supported:
ChatMemberOwner
ChatMemberAdministrator
ChatMemberMember
ChatMemberRestricted
ChatMemberLeft
ChatMemberBanned
https://core.telegram.org/bots/api#chatmember
"""
status: base.String = fields.Field()
user: User = fields.Field(base=User)
def __int__(self) -> int:
return self.user.id
@classmethod
def resolve(cls, **kwargs) -> "ChatMember":
status = kwargs.get("status")
mapping = {
ChatMemberStatus.OWNER: ChatMemberOwner,
ChatMemberStatus.ADMINISTRATOR: ChatMemberAdministrator,
ChatMemberStatus.MEMBER: ChatMemberMember,
ChatMemberStatus.RESTRICTED: ChatMemberRestricted,
ChatMemberStatus.LEFT: ChatMemberLeft,
ChatMemberStatus.BANNED: ChatMemberBanned,
}
class_ = mapping.get(status)
if class_ is None:
raise ValueError(f"Can't find `ChatMember` class for status `{status}`")
return class_(**kwargs)
class ChatMemberOwner(ChatMember):
"""
Represents a chat member that owns the chat and has all
administrator privileges.
https://core.telegram.org/bots/api#chatmemberowner
"""
status: base.String = fields.Field(default=ChatMemberStatus.OWNER)
user: User = fields.Field(base=User)
custom_title: base.String = fields.Field()
is_anonymous: base.Boolean = fields.Field()
class ChatMemberAdministrator(ChatMember):
"""
Represents a chat member that has some additional privileges.
https://core.telegram.org/bots/api#chatmemberadministrator
"""
status: base.String = fields.Field(default=ChatMemberStatus.ADMINISTRATOR)
user: User = fields.Field(base=User)
can_be_edited: base.Boolean = fields.Field()
custom_title: base.String = fields.Field()
is_anonymous: base.Boolean = fields.Field()
can_manage_chat: base.Boolean = fields.Field()
can_post_messages: base.Boolean = fields.Field()
can_edit_messages: base.Boolean = fields.Field()
can_delete_messages: base.Boolean = fields.Field()
can_manage_voice_chats: base.Boolean = fields.Field()
can_restrict_members: base.Boolean = fields.Field()
can_promote_members: base.Boolean = fields.Field()
can_change_info: base.Boolean = fields.Field()
can_invite_users: base.Boolean = fields.Field()
can_pin_messages: base.Boolean = fields.Field()
class ChatMemberMember(ChatMember):
"""
Represents a chat member that has no additional privileges or
restrictions.
https://core.telegram.org/bots/api#chatmembermember
"""
status: base.String = fields.Field(default=ChatMemberStatus.MEMBER)
user: User = fields.Field(base=User)
class ChatMemberRestricted(ChatMember):
"""
Represents a chat member that is under certain restrictions in the
chat. Supergroups only.
https://core.telegram.org/bots/api#chatmemberrestricted
"""
status: base.String = fields.Field(default=ChatMemberStatus.RESTRICTED)
user: User = fields.Field(base=User)
is_member: base.Boolean = fields.Field()
can_change_info: base.Boolean = fields.Field()
can_invite_users: base.Boolean = fields.Field()
can_pin_messages: base.Boolean = fields.Field()
can_send_messages: base.Boolean = fields.Field()
can_send_media_messages: base.Boolean = fields.Field()
can_send_polls: base.Boolean = fields.Field()
can_send_other_messages: base.Boolean = fields.Field()
can_add_web_page_previews: base.Boolean = fields.Field()
until_date: datetime.datetime = fields.DateTimeField()
class ChatMemberLeft(ChatMember):
"""
Represents a chat member that isn't currently a member of the chat,
but may join it themselves.
https://core.telegram.org/bots/api#chatmemberleft
"""
status: base.String = fields.Field(default=ChatMemberStatus.LEFT)
user: User = fields.Field(base=User)
class ChatMemberBanned(ChatMember):
"""
Represents a chat member that was banned in the chat and can't
return to the chat or view chat messages.
https://core.telegram.org/bots/api#chatmemberbanned
"""
status: base.String = fields.Field(default=ChatMemberStatus.BANNED)
user: User = fields.Field(base=User)
until_date: datetime.datetime = fields.DateTimeField()

View file

@ -6,31 +6,28 @@ from . import fields
class ForceReply(base.TelegramObject):
"""
Upon receiving a message with this object,
Telegram clients will display a reply interface to the user
(act as if the user has selected the bots message and tapped Reply').
This can be extremely useful if you want to create user-friendly step-by-step
Upon receiving a message with this object, Telegram clients will
display a reply interface to the user (act as if the user has
selected the bot's message and tapped 'Reply'). This can be
extremely useful if you want to create user-friendly step-by-step
interfaces without having to sacrifice privacy mode.
Example: A poll bot for groups runs in privacy mode
(only receives commands, replies to its messages and mentions).
There could be two ways to create a new poll
The last option is definitely more attractive.
And if you use ForceReply in your bots questions, it will receive the users answers even
if it only receives replies, commands and mentions without any extra work for the user.
https://core.telegram.org/bots/api#forcereply
"""
force_reply: base.Boolean = fields.Field(default=True)
input_field_placeholder: base.String = fields.Field()
selective: base.Boolean = fields.Field()
@classmethod
def create(cls, selective: typing.Optional[base.Boolean] = None):
def create(cls,
input_field_placeholder: typing.Optional[base.String] = None,
selective: typing.Optional[base.Boolean] = None,
) -> 'ForceReply':
"""
Create new force reply
:param selective:
:param input_field_placeholder:
:return:
"""
return cls(selective=selective)
return cls(selective=selective, input_field_placeholder=input_field_placeholder)

View file

@ -18,23 +18,32 @@ class KeyboardButtonPollType(base.TelegramObject):
class ReplyKeyboardMarkup(base.TelegramObject):
"""
This object represents a custom keyboard with reply options (see Introduction to bots for details and examples).
This object represents a custom keyboard with reply options
(see https://core.telegram.org/bots#keyboards to bots for details
and examples).
https://core.telegram.org/bots/api#replykeyboardmarkup
"""
keyboard: 'typing.List[typing.List[KeyboardButton]]' = fields.ListOfLists(base='KeyboardButton', default=[])
resize_keyboard: base.Boolean = fields.Field()
one_time_keyboard: base.Boolean = fields.Field()
input_field_placeholder: base.String = fields.Field()
selective: base.Boolean = fields.Field()
def __init__(self, keyboard: 'typing.List[typing.List[KeyboardButton]]' = None,
resize_keyboard: base.Boolean = None,
one_time_keyboard: base.Boolean = None,
input_field_placeholder: base.String = None,
selective: base.Boolean = None,
row_width: base.Integer = 3):
super(ReplyKeyboardMarkup, self).__init__(keyboard=keyboard, resize_keyboard=resize_keyboard,
one_time_keyboard=one_time_keyboard, selective=selective,
conf={'row_width': row_width})
super().__init__(
keyboard=keyboard,
resize_keyboard=resize_keyboard,
one_time_keyboard=one_time_keyboard,
input_field_placeholder=input_field_placeholder,
selective=selective,
conf={'row_width': row_width},
)
@property
def row_width(self):

View file

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

View file

@ -427,7 +427,7 @@ async def test_get_chat_administrators(bot: Bot):
""" getChatAdministrators method test """
from .types.dataset import CHAT, CHAT_MEMBER
chat = types.Chat(**CHAT)
member = types.ChatMember(**CHAT_MEMBER)
member = types.ChatMember.resolve(**CHAT_MEMBER)
async with FakeTelegram(message_data=[CHAT_MEMBER, CHAT_MEMBER]):
result = await bot.get_chat_administrators(chat_id=chat.id)
@ -435,14 +435,14 @@ async def test_get_chat_administrators(bot: Bot):
assert len(result) == 2
async def test_get_chat_members_count(bot: Bot):
async def test_get_chat_member_count(bot: Bot):
""" getChatMembersCount method test """
from .types.dataset import CHAT
chat = types.Chat(**CHAT)
count = 5
async with FakeTelegram(message_data=count):
result = await bot.get_chat_members_count(chat_id=chat.id)
result = await bot.get_chat_member_count(chat_id=chat.id)
assert result == count
@ -450,7 +450,7 @@ async def test_get_chat_member(bot: Bot):
""" getChatMember method test """
from .types.dataset import CHAT, CHAT_MEMBER
chat = types.Chat(**CHAT)
member = types.ChatMember(**CHAT_MEMBER)
member = types.ChatMember.resolve(**CHAT_MEMBER)
async with FakeTelegram(message_data=CHAT_MEMBER):
result = await bot.get_chat_member(chat_id=chat.id, user_id=member.user.id)

View file

@ -1,7 +1,7 @@
from aiogram import types
from .dataset import CHAT_MEMBER
chat_member = types.ChatMember(**CHAT_MEMBER)
chat_member = types.ChatMember.resolve(**CHAT_MEMBER)
def test_export():