From dff3d993e18d8035f09ff9633ff7cc60a0afd5ac Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 17 Mar 2021 15:11:23 +0100 Subject: [PATCH] Add support for updates about chat member status changes --- compiler/docs/compiler.py | 4 + docs/source/api/decorators.rst | 2 + docs/source/api/handlers.rst | 2 + pyrogram/dispatcher.py | 15 +++- pyrogram/handlers/__init__.py | 1 + .../handlers/chat_member_updated_handler.py | 47 ++++++++++ pyrogram/methods/decorators/__init__.py | 4 +- .../decorators/on_chat_member_updated.py | 56 ++++++++++++ pyrogram/types/user_and_chats/__init__.py | 4 +- .../user_and_chats/chat_member_updated.py | 89 +++++++++++++++++++ 10 files changed, 219 insertions(+), 5 deletions(-) create mode 100644 pyrogram/handlers/chat_member_updated_handler.py create mode 100644 pyrogram/methods/decorators/on_chat_member_updated.py create mode 100644 pyrogram/types/user_and_chats/chat_member_updated.py diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 78328459..0a34b206 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -349,6 +349,7 @@ def pyrogram_api(): ChatAdminWithInviteLinks ChatEvent ChatEventFilter + ChatMemberUpdated Dialog Restriction """, @@ -373,6 +374,9 @@ def pyrogram_api(): Poll PollOption Dice + VoiceChatStarted + VoiceChatEnded + VoiceChatMembersInvited """, bots_keyboard=""" Bots & Keyboards diff --git a/docs/source/api/decorators.rst b/docs/source/api/decorators.rst index 13bfc9e4..c859063a 100644 --- a/docs/source/api/decorators.rst +++ b/docs/source/api/decorators.rst @@ -43,6 +43,7 @@ Index - :meth:`~Client.on_callback_query` - :meth:`~Client.on_inline_query` - :meth:`~Client.on_chosen_inline_result` + - :meth:`~Client.on_chat_member_updated` - :meth:`~Client.on_deleted_messages` - :meth:`~Client.on_user_status` - :meth:`~Client.on_poll` @@ -59,6 +60,7 @@ Details .. autodecorator:: pyrogram.Client.on_callback_query() .. autodecorator:: pyrogram.Client.on_inline_query() .. autodecorator:: pyrogram.Client.on_chosen_inline_result() +.. autodecorator:: pyrogram.Client.on_chat_member_updated() .. autodecorator:: pyrogram.Client.on_deleted_messages() .. autodecorator:: pyrogram.Client.on_user_status() .. autodecorator:: pyrogram.Client.on_poll() diff --git a/docs/source/api/handlers.rst b/docs/source/api/handlers.rst index 4e79d139..b06ebbed 100644 --- a/docs/source/api/handlers.rst +++ b/docs/source/api/handlers.rst @@ -41,6 +41,7 @@ Index - :class:`CallbackQueryHandler` - :class:`InlineQueryHandler` - :class:`ChosenInlineResultHandler` + - :class:`ChatMemberUpdated` - :class:`UserStatusHandler` - :class:`PollHandler` - :class:`DisconnectHandler` @@ -57,6 +58,7 @@ Details .. autoclass:: CallbackQueryHandler() .. autoclass:: InlineQueryHandler() .. autoclass:: ChosenInlineResultHandler() +.. autoclass:: ChatMemberUpdated() .. autoclass:: UserStatusHandler() .. autoclass:: PollHandler() .. autoclass:: DisconnectHandler() diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py index f4b818a4..0f46d642 100644 --- a/pyrogram/dispatcher.py +++ b/pyrogram/dispatcher.py @@ -26,7 +26,7 @@ from pyrogram import utils from pyrogram.handlers import ( CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, - ChosenInlineResultHandler + ChosenInlineResultHandler, ChatMemberUpdatedHandler ) from pyrogram.raw.types import ( UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, @@ -34,7 +34,7 @@ from pyrogram.raw.types import ( UpdateDeleteMessages, UpdateDeleteChannelMessages, UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateUserStatus, UpdateBotInlineQuery, UpdateMessagePoll, - UpdateBotInlineSend + UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant ) log = logging.getLogger(__name__) @@ -62,6 +62,11 @@ class Dispatcher: UpdateInlineBotCallbackQuery ) + CHAT_MEMBER_UPDATES = ( + UpdateChatParticipant, + UpdateChannelParticipant + ) + MESSAGE_UPDATES = NEW_MESSAGE_UPDATES + EDIT_MESSAGE_UPDATES def __init__(self, client: "pyrogram.Client"): @@ -98,6 +103,9 @@ class Dispatcher: async def chosen_inline_result_parser(update, users, chats): return pyrogram.types.ChosenInlineResult._parse(self.client, update, users), ChosenInlineResultHandler + async def chat_member_updated_parser(update, users, chats): + return pyrogram.types.ChatMemberUpdated._parse(self.client, update, users, chats), ChatMemberUpdatedHandler + self.update_parsers = { Dispatcher.MESSAGE_UPDATES: message_parser, Dispatcher.DELETE_MESSAGES_UPDATES: deleted_messages_parser, @@ -105,7 +113,8 @@ class Dispatcher: (UpdateUserStatus,): user_status_parser, (UpdateBotInlineQuery,): inline_query_parser, (UpdateMessagePoll,): poll_parser, - (UpdateBotInlineSend,): chosen_inline_result_parser + (UpdateBotInlineSend,): chosen_inline_result_parser, + Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser } self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py index 74e230a4..71ecab72 100644 --- a/pyrogram/handlers/__init__.py +++ b/pyrogram/handlers/__init__.py @@ -17,6 +17,7 @@ # along with Pyrogram. If not, see . from .callback_query_handler import CallbackQueryHandler +from .chat_member_updated_handler import ChatMemberUpdatedHandler from .chosen_inline_result_handler import ChosenInlineResultHandler from .deleted_messages_handler import DeletedMessagesHandler from .disconnect_handler import DisconnectHandler diff --git a/pyrogram/handlers/chat_member_updated_handler.py b/pyrogram/handlers/chat_member_updated_handler.py new file mode 100644 index 00000000..d03eae11 --- /dev/null +++ b/pyrogram/handlers/chat_member_updated_handler.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2021 Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class ChatMemberUpdatedHandler(Handler): + """The ChatMemberUpdated handler class. Used to handle changes in the status of a chat member. + It is intended to be used with :meth:`~pyrogram.Client.add_handler`. + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_chat_member_updated` decorator. + + Parameters: + callback (``callable``): + Pass a function that will be called when a new ChatMemberUpdated event arrives. It takes + *(client, chat_member_updated)* as positional arguments (look at the section below for a detailed + description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of updates to be passed in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the handler. + + chat_member_updated (:obj:`~pyrogram.types.ChatMemberUpdated`): + The received chat member update. + """ + + def __init__(self, callback: callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py index fe96ed13..01ddffce 100644 --- a/pyrogram/methods/decorators/__init__.py +++ b/pyrogram/methods/decorators/__init__.py @@ -17,6 +17,7 @@ # along with Pyrogram. If not, see . from .on_callback_query import OnCallbackQuery +from .on_chat_member_updated import OnChatMemberUpdated from .on_chosen_inline_result import OnChosenInlineResult from .on_deleted_messages import OnDeletedMessages from .on_disconnect import OnDisconnect @@ -36,6 +37,7 @@ class Decorators( OnUserStatus, OnInlineQuery, OnPoll, - OnChosenInlineResult + OnChosenInlineResult, + OnChatMemberUpdated ): pass diff --git a/pyrogram/methods/decorators/on_chat_member_updated.py b/pyrogram/methods/decorators/on_chat_member_updated.py new file mode 100644 index 00000000..e184bbef --- /dev/null +++ b/pyrogram/methods/decorators/on_chat_member_updated.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2021 Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Callable + +import pyrogram +from pyrogram.filters import Filter +from pyrogram.scaffold import Scaffold + + +class OnChatMemberUpdated(Scaffold): + def on_chat_member_updated( + self=None, + filters=None, + group: int = 0 + ) -> callable: + """Decorator for handling event changes on chat members. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.ChatMemberUpdated`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of updates to be passed in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.ChatMemberUpdatedHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + func.handler = ( + pyrogram.handlers.ChatMemberUpdatedHandler(func, self), + group if filters is None else filters + ) + + return func + + return decorator diff --git a/pyrogram/types/user_and_chats/__init__.py b/pyrogram/types/user_and_chats/__init__.py index 3fc4eeaf..f3022e37 100644 --- a/pyrogram/types/user_and_chats/__init__.py +++ b/pyrogram/types/user_and_chats/__init__.py @@ -22,6 +22,7 @@ from .chat_event import ChatEvent from .chat_event_filter import ChatEventFilter from .chat_invite_link import ChatInviteLink from .chat_member import ChatMember +from .chat_member_updated import ChatMemberUpdated from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from .chat_preview import ChatPreview @@ -49,5 +50,6 @@ __all__ = [ "ChatAdminWithInviteLinks", "VoiceChatStarted", "VoiceChatEnded", - "VoiceChatMembersInvited" + "VoiceChatMembersInvited", + "ChatMemberUpdated" ] diff --git a/pyrogram/types/user_and_chats/chat_member_updated.py b/pyrogram/types/user_and_chats/chat_member_updated.py new file mode 100644 index 00000000..5794069f --- /dev/null +++ b/pyrogram/types/user_and_chats/chat_member_updated.py @@ -0,0 +1,89 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2021 Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Dict, Union + +import pyrogram +from pyrogram import raw +from pyrogram import types +from ..object import Object +from ..update import Update + + +class ChatMemberUpdated(Object, Update): + """Represents changes in the status of a chat member. + + Parameters: + chat (:obj:`~pyrogram.types.Chat`): + Chat the user belongs to. + + from_user (:obj:`~pyrogram.types.User`): + Performer of the action, which resulted in the change. + + date (``int``): + Date the change was done in Unix time. + + old_chat_member (:obj:`~pyrogram.types.ChatMember`): + Previous information about the chat member. + + new_chat_member (:obj:`~pyrogram.types.ChatMember`): + New information about the chat member. + + invite_link (:obj:`~pyrogram.types.ChatInviteLink`, *optional*): + Chat invite link, which was used by the user to join the chat; for joining by invite link events only. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + chat: "types.Chat", + from_user: "types.User", + date: int, + old_chat_member: "types.ChatMember", + new_chat_member: "types.ChatMember", + invite_link: "types.ChatInviteLink" = None, + ): + super().__init__(client) + + self.chat = chat + self.from_user = from_user + self.date = date + self.old_chat_member = old_chat_member + self.new_chat_member = new_chat_member + self.invite_link = invite_link + + @staticmethod + def _parse( + client: "pyrogram.Client", + update: Union["raw.types.UpdateChatParticipant", "raw.types.UpdateChannelParticipant"], + users: Dict[int, "raw.types.User"], + chats: Dict[int, "raw.types.Chat"] + ) -> "ChatMemberUpdated": + chat_id = getattr(update, "chat_id", None) or getattr(update, "channel_id") + invite_link = types.ChatInviteLink._parse(client, update.invite, users) if update.invite else None + + return ChatMemberUpdated( + chat=types.Chat._parse_chat(client, chats[chat_id]), + from_user=types.User._parse(client, users[update.actor_id]), + date=update.date, + old_chat_member=types.ChatMember._parse(client, update.prev_participant, users), + new_chat_member=types.ChatMember._parse(client, update.new_participant, users), + invite_link=invite_link, + client=client + )