Add to ChatMemberOwner new default fields (#645)

* new: add field type ConstField

* new: add const fields for ChatMemberOwner

* new: better typings for get_chat_administrators

* new: add tests for chat owner fields

* fix: Type typing for class

* enh: alias is_chat_owner for is_chat_creator
This commit is contained in:
Ramzan Bekbulatov 2021-07-31 21:47:37 +03:00 committed by GitHub
parent 7feb0cade0
commit f6f2972a11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 13 deletions

View file

@ -2129,7 +2129,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
return types.Chat(**result)
async def get_chat_administrators(self, chat_id: typing.Union[base.Integer, base.String]
) -> typing.List[types.ChatMember]:
) -> typing.List[typing.Union[types.ChatMemberOwner, types.ChatMemberAdministrator]]:
"""
Use this method to get a list of administrators in a chat.

View file

@ -7,7 +7,7 @@ import typing
from . import base, fields
from .chat_invite_link import ChatInviteLink
from .chat_location import ChatLocation
from .chat_member import ChatMember
from .chat_member import ChatMember, ChatMemberAdministrator, ChatMemberOwner
from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto
from .input_file import InputFile
@ -470,7 +470,7 @@ class Chat(base.TelegramObject):
"""
return await self.bot.leave_chat(self.id)
async def get_administrators(self) -> typing.List[ChatMember]:
async def get_administrators(self) -> typing.List[typing.Union[ChatMemberOwner, ChatMemberAdministrator]]:
"""
Use this method to get a list of administrators in a chat.
@ -480,7 +480,7 @@ class Chat(base.TelegramObject):
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.
:rtype: :obj:`typing.List[types.ChatMember]`
:rtype: :obj:`typing.List[typing.Union[types.ChatMemberOwner, types.ChatMemberAdministrator]]`
"""
return await self.bot.get_chat_administrators(self.id)

View file

@ -1,6 +1,5 @@
import datetime
import typing
from typing import Optional
from . import base, fields
from .user import User
@ -29,6 +28,8 @@ class ChatMemberStatus(helper.Helper):
def is_chat_creator(cls, role: str) -> bool:
return role == cls.CREATOR
is_chat_owner = is_chat_creator
@classmethod
def is_chat_admin(cls, role: str) -> bool:
return role in (cls.ADMINISTRATOR, cls.CREATOR)
@ -38,7 +39,7 @@ class ChatMemberStatus(helper.Helper):
return role in (cls.MEMBER, cls.ADMINISTRATOR, cls.CREATOR, cls.RESTRICTED)
@classmethod
def get_class_by_status(cls, status: str) -> Optional["ChatMember"]:
def get_class_by_status(cls, status: str) -> typing.Optional[typing.Type["ChatMember"]]:
return {
cls.OWNER: ChatMemberOwner,
cls.ADMINISTRATOR: ChatMemberAdministrator,
@ -69,7 +70,9 @@ class ChatMember(base.TelegramObject):
return self.user.id
@classmethod
def resolve(cls, **kwargs) -> "ChatMember":
def resolve(cls, **kwargs) -> typing.Union["ChatMemberOwner", "ChatMemberAdministrator",
"ChatMemberMember", "ChatMemberRestricted",
"ChatMemberLeft", "ChatMemberBanned"]:
status = kwargs.get("status")
mapping = {
ChatMemberStatus.OWNER: ChatMemberOwner,
@ -89,12 +92,16 @@ class ChatMember(base.TelegramObject):
def to_object(cls,
data: typing.Dict[str, typing.Any],
conf: typing.Dict[str, typing.Any] = None
) -> "ChatMember":
return cls.resolve(**data)
) -> typing.Union["ChatMemberOwner", "ChatMemberAdministrator",
"ChatMemberMember", "ChatMemberRestricted",
"ChatMemberLeft", "ChatMemberBanned"]:
return cls.resolve(conf=conf, **data)
def is_chat_creator(self) -> bool:
return ChatMemberStatus.is_chat_creator(self.status)
is_chat_owner = is_chat_creator
def is_chat_admin(self) -> bool:
return ChatMemberStatus.is_chat_admin(self.status)
@ -113,6 +120,22 @@ class ChatMemberOwner(ChatMember):
custom_title: base.String = fields.Field()
is_anonymous: base.Boolean = fields.Field()
# Next fields cannot be received from API but
# it useful for compatibility and cleaner code:
# >>> if member.is_admin() and member.can_promote_members:
# >>> await message.reply('You can promote me')
can_be_edited: base.Boolean = fields.ConstField(False)
can_manage_chat: base.Boolean = fields.ConstField(True)
can_post_messages: base.Boolean = fields.ConstField(True)
can_edit_messages: base.Boolean = fields.ConstField(True)
can_delete_messages: base.Boolean = fields.ConstField(True)
can_manage_voice_chats: base.Boolean = fields.ConstField(True)
can_restrict_members: base.Boolean = fields.ConstField(True)
can_promote_members: base.Boolean = fields.ConstField(True)
can_change_info: base.Boolean = fields.ConstField(True)
can_invite_users: base.Boolean = fields.ConstField(True)
can_pin_messages: base.Boolean = fields.ConstField(True)
class ChatMemberAdministrator(ChatMember):
"""

View file

@ -2,7 +2,7 @@ import abc
import datetime
import weakref
__all__ = ('BaseField', 'Field', 'ListField', 'DateTimeField', 'TextField', 'ListOfLists')
__all__ = ('BaseField', 'Field', 'ListField', 'DateTimeField', 'TextField', 'ListOfLists', 'ConstField')
class BaseField(metaclass=abc.ABCMeta):
@ -192,5 +192,13 @@ class TextField(Field):
def deserialize(self, value, parent=None):
if value is not None and not isinstance(value, str):
raise TypeError(f"Field '{self.alias}' should be str not {type(value).__name__}")
raise TypeError(f"Field {self.alias!r} should be str not {type(value).__name__!r}")
return value
class ConstField(Field):
def __init__(self, default=None, **kwargs):
super(ConstField, self).__init__(default=default, **kwargs)
def __set__(self, instance, value):
raise TypeError(f"Field {self.alias!r} is not mutable")