From b27a8ac6aec1d0f6b73e900d6378c82d8dca3110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danii=C5=82=20M=2E?= Date: Tue, 3 Feb 2026 20:25:18 +0100 Subject: [PATCH 1/2] fix: improve admin status detection for chat participants --- backend/community_manager/actions/chat.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/community_manager/actions/chat.py b/backend/community_manager/actions/chat.py index a04e2ac..a41d843 100644 --- a/backend/community_manager/actions/chat.py +++ b/backend/community_manager/actions/chat.py @@ -144,7 +144,10 @@ async def _load_participants( self.telegram_chat_user_service.create_or_update( chat_id=chat_identifier, user_id=user.id, - is_admin=hasattr(participant_user.participant, "admin_rights"), + is_admin=isinstance( + participant_user.participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ), is_managed=False, ) processed_user_ids.append(user.id) From 3dce1f579e5f3619df3839c8b92aab0d36b95a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danii=C5=82=20M=2E?= Date: Tue, 3 Feb 2026 20:39:59 +0100 Subject: [PATCH 2/2] feat: add utility functions for chat participant admin checks --- backend/community_manager/actions/chat.py | 19 +++++---- backend/community_manager/events/__init__.py | 45 ++++++-------------- backend/community_manager/utils.py | 40 +++++++++++++++++ 3 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 backend/community_manager/utils.py diff --git a/backend/community_manager/actions/chat.py b/backend/community_manager/actions/chat.py index a41d843..8532c81 100644 --- a/backend/community_manager/actions/chat.py +++ b/backend/community_manager/actions/chat.py @@ -18,6 +18,7 @@ from community_manager.dtos.chat import TargetChatMembersDTO from community_manager.events import ChatAdminChangeEventBuilder from community_manager.settings import community_manager_settings +from community_manager.utils import is_chat_participant_manager_admin from core.actions.authorization import AuthorizationAction from core.actions.base import BaseAction from core.actions.user import UserAction @@ -144,9 +145,8 @@ async def _load_participants( self.telegram_chat_user_service.create_or_update( chat_id=chat_identifier, user_id=user.id, - is_admin=isinstance( - participant_user.participant, - (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + is_admin=is_chat_participant_manager_admin( + participant_user.participant ), is_managed=False, ) @@ -1103,12 +1103,13 @@ async def kick_chat_member(self, chat_member: TelegramChatUser) -> None: f"Failed to kick user {chat_member.user.telegram_id!r} from chat {chat_member.chat_id!r} as bot user lacks admin privileges", exc_info=e, ) - self.telegram_chat_service.set_insufficient_privileges( - chat_id=chat_member.chat_id, value=True - ) - logger.info( - f"Set insufficient privileges flag for chat {chat_member.chat_id!r}." - ) + # TODO: Fix this logic after telegram_chat_user.is_manager_admin/is_admin is properly indexed + # self.telegram_chat_service.set_insufficient_privileges( + # chat_id=chat_member.chat_id, value=True + # ) + # logger.info( + # f"Set insufficient privileges flag for chat {chat_member.chat_id!r}." + # ) except RPCError as e: logger.error( f"Failed to kick user {chat_member.user.telegram_id!r} from chat {chat_member.chat_id!r}", diff --git a/backend/community_manager/events/__init__.py b/backend/community_manager/events/__init__.py index 13d7d0f..337ebe8 100644 --- a/backend/community_manager/events/__init__.py +++ b/backend/community_manager/events/__init__.py @@ -10,11 +10,14 @@ ChannelParticipantBanned, ChannelParticipantSelf, User as TelethonUser, - ChannelParticipantCreator, ChatInviteExported, ) -from core.constants import REQUIRED_ADMIN_PRIVILEGES, REQUIRED_BOT_PRIVILEGES +from community_manager.utils import ( + is_chat_participant_admin, + is_chat_participant_manager_admin, +) +from core.constants import REQUIRED_BOT_PRIVILEGES logger = logging.getLogger(__name__) @@ -121,18 +124,15 @@ def user(self) -> TelethonUser | None: @property def is_demoted(self) -> bool: - return isinstance( - self.prev_participant, ChannelParticipantAdmin - ) and not isinstance( - self.new_participant, - (ChannelParticipantAdmin, ChannelParticipantCreator), - ) + return is_chat_participant_admin( + chat_participant=self.prev_participant + ) and not is_chat_participant_admin(chat_participant=self.new_participant) @property def is_promoted(self) -> bool: - return not isinstance( - self.prev_participant, ChannelParticipantAdmin - ) and isinstance(self.new_participant, ChannelParticipantAdmin) + return not is_chat_participant_admin( + chat_participant=self.prev_participant + ) and is_chat_participant_admin(chat_participant=self.new_participant) @property def has_enough_rights(self) -> bool: @@ -142,28 +142,7 @@ def has_enough_rights(self) -> bool: ) return False - if not isinstance(self.new_participant, ChannelParticipantAdmin): - logger.debug( - "User %d is not an admin in the chat %d", - self.user.id, - self.original_update.channel_id, - ) - return False - elif not all( - [ - getattr(self.new_participant.admin_rights, right) - for right in REQUIRED_ADMIN_PRIVILEGES - ] - ): - logger.warning( - "User %d has insufficient permissions in the chat %d: %s", - self.user.id, - self.original_update.channel_id, - self.new_participant.admin_rights, - ) - return False - - return True + return is_chat_participant_manager_admin(self.new_participant) @property def sufficient_bot_privileges(self) -> bool: diff --git a/backend/community_manager/utils.py b/backend/community_manager/utils.py new file mode 100644 index 0000000..0486684 --- /dev/null +++ b/backend/community_manager/utils.py @@ -0,0 +1,40 @@ +from telethon.tl.types import ( + ChannelParticipantAdmin, + ChannelParticipantCreator, + ChannelParticipant, +) + +from core.constants import REQUIRED_ADMIN_PRIVILEGES + + +def is_chat_participant_admin( + chat_participant: ChannelParticipant, +) -> bool: + """ + Check if the given chat participant is an admin or creator. + :param chat_participant: The chat participant to check. + :return: True if the participant is an admin or creator, False otherwise. + """ + return isinstance( + chat_participant, + (ChannelParticipantAdmin, ChannelParticipantCreator), + ) + + +def is_chat_participant_manager_admin( + chat_participant: ChannelParticipant + | ChannelParticipantAdmin + | ChannelParticipantCreator, +) -> bool: + """ + Check if the given chat participant is an admin with manager privileges. + :param chat_participant: The chat participant to check. + :return: True if the participant is an admin with manager privileges, False otherwise. + """ + if not is_chat_participant_admin(chat_participant): + return False + + return all( + getattr(chat_participant.admin_rights, right) + for right in REQUIRED_ADMIN_PRIVILEGES + )