Add chat member tools (#1527)

* feat: add ChatMemberAdapter

* chore: apply formatter

* docs: added changelog

* docs: rm redundant import

* feat: add pre-defined groups
This commit is contained in:
Oleg A 2024-07-06 20:34:07 +03:00 committed by GitHub
parent 5f05dfc664
commit 46e033e6da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 200 additions and 6 deletions

1
CHANGES/1525.feature.rst Normal file
View file

@ -0,0 +1 @@
Added ChatMember resolution tool and updated 2.x migration guide.

View file

@ -0,0 +1,37 @@
from typing import Tuple, Type, Union
from pydantic import Field, TypeAdapter
from typing_extensions import Annotated
from aiogram.types import (
ChatMember,
ChatMemberAdministrator,
ChatMemberBanned,
ChatMemberLeft,
ChatMemberMember,
ChatMemberOwner,
ChatMemberRestricted,
)
ChatMemberUnion = Union[
ChatMemberOwner,
ChatMemberAdministrator,
ChatMemberMember,
ChatMemberRestricted,
ChatMemberLeft,
ChatMemberBanned,
]
ChatMemberCollection = Tuple[Type[ChatMember], ...]
ChatMemberAdapter = TypeAdapter(
Annotated[
ChatMemberUnion,
Field(discriminator="status"),
]
)
ADMINS: ChatMemberCollection = (ChatMemberOwner, ChatMemberAdministrator)
USERS: ChatMemberCollection = (ChatMemberMember, ChatMemberRestricted)
MEMBERS: ChatMemberCollection = ADMINS + USERS
NOT_MEMBERS: ChatMemberCollection = (ChatMemberLeft, ChatMemberBanned)

View file

@ -26,7 +26,7 @@ On this page, you can read about the changes made in relation to the last stable
Feel free to contribute to this page, if you find something that is not mentioned here.
Dependencies
==========
============
- The dependencies required for :code:`i18n` are no longer part of the default package.
If your application uses translation functionality, be sure to add an optional dependency:
@ -188,6 +188,8 @@ Here are some usage examples:
print(type(message_obj))
# <class 'aiogram.types.message.Message'>
.. code-block::
# Version 3.x
message_dict = {"id": 42, ...}
message_obj = Message.model_validate(message_dict)
@ -200,15 +202,18 @@ Here are some usage examples:
.. code-block::
async def handler(message: Message) -> None:
# Version 2.x
async def handler(message: Message) -> None:
message_json = message.as_json()
print(message_json)
# {"id": 42, ...}
print(type(message_json))
# <class 'str'>
.. code-block::
# Version 3.x
async def handler(message: Message) -> None:
message_json = json.dumps(deserialize_telegram_object_to_python(message))
print(message_json)
# {"id": 42, ...}
@ -227,9 +232,65 @@ Here are some usage examples:
print(type(message_dict))
# <class 'dict'>
.. code-block::
async def handler(message: Message) -> None:
# Version 3.x
message_dict = deserialize_telegram_object_to_python(message)
print(message_dict)
# {"id": 42, ...}
print(type(message_dict))
# <class 'dict'>
ChatMember tools
================
- Now :class:`aiogram.types.chat_member.ChatMember` no longer contains tools to resolve an object with the appropriate status.
.. code-block::
# Version 2.x
from aiogram.types import ChatMember
chat_member = ChatMember.resolve(**dict_data)
.. code-block::
# Version 3.x
from aiogram.utils.chat_member import ChatMemberAdapter
chat_member = ChatMemberAdapter.validate_python(dict_data)
- Now :class:`aiogram.types.chat_member.ChatMember` and all its child classes no longer
contain methods for checking for membership in certain logical groups.
As a substitute, you can use pre-defined groups or create such groups yourself
and check their entry using the :func:`isinstance` function
.. code-block::
# Version 2.x
if chat_member.is_chat_admin():
print("ChatMember is chat admin")
if chat_member.is_chat_member():
print("ChatMember is in the chat")
.. code-block::
# Version 3.x
from aiogram.utils.chat_member import ADMINS, MEMBERS
if isinstance(chat_member, ADMINS):
print("ChatMember is chat admin")
if isinstance(chat_member, MEMBERS):
print("ChatMember is in the chat")
.. note::
You also can independently create group similar to ADMINS that fits the logic of your application.
E.g., you can create a PUNISHED group and include banned and restricted members there!

View file

@ -0,0 +1,95 @@
from datetime import datetime
from typing import Type
import pytest
from aiogram.types import (
ChatMember,
ChatMemberAdministrator,
ChatMemberBanned,
ChatMemberLeft,
ChatMemberMember,
ChatMemberOwner,
ChatMemberRestricted,
User,
)
from aiogram.utils.chat_member import ChatMemberAdapter
USER = User(
id=42,
first_name="John Doe",
is_bot=False,
).model_dump()
CHAT_MEMBER_ADMINISTRATOR = ChatMemberAdministrator(
user=USER,
can_be_edited=False,
can_manage_chat=True,
can_change_info=True,
can_delete_messages=True,
can_invite_users=True,
can_restrict_members=True,
can_pin_messages=True,
can_manage_topics=False,
can_promote_members=False,
can_manage_video_chats=True,
can_post_stories=True,
can_edit_stories=True,
can_delete_stories=True,
is_anonymous=False,
can_manage_voice_chats=False,
).model_dump()
CHAT_MEMBER_BANNED = ChatMemberBanned(
user=USER,
until_date=datetime.now(),
).model_dump()
CHAT_MEMBER_LEFT = ChatMemberLeft(
user=USER,
).model_dump()
CHAT_MEMBER_MEMBER = ChatMemberMember(
user=USER,
).model_dump()
CHAT_MEMBER_OWNER = ChatMemberOwner(
user=USER,
is_anonymous=True,
).model_dump()
CHAT_MEMBER_RESTRICTED = ChatMemberRestricted(
user=USER,
is_member=True,
can_send_messages=False,
can_send_audios=False,
can_send_documents=False,
can_send_photos=False,
can_send_videos=False,
can_send_video_notes=False,
can_send_voice_notes=False,
can_send_polls=False,
can_send_other_messages=False,
can_add_web_page_previews=False,
can_change_info=False,
can_invite_users=False,
can_pin_messages=False,
can_manage_topics=False,
until_date=datetime.now(),
).model_dump()
@pytest.mark.parametrize(
("data", "resolved_type"),
[
(CHAT_MEMBER_ADMINISTRATOR, ChatMemberAdministrator),
(CHAT_MEMBER_BANNED, ChatMemberBanned),
(CHAT_MEMBER_LEFT, ChatMemberLeft),
(CHAT_MEMBER_MEMBER, ChatMemberMember),
(CHAT_MEMBER_OWNER, ChatMemberOwner),
(CHAT_MEMBER_RESTRICTED, ChatMemberRestricted),
],
)
def test_chat_member_resolution(data: dict, resolved_type: Type[ChatMember]) -> None:
chat_member = ChatMemberAdapter.validate_python(data)
assert isinstance(chat_member, resolved_type)