diff --git a/pytgcalls/pytgcalls/__init__.py b/pytgcalls/pytgcalls/__init__.py index 9d785482..aafad814 100644 --- a/pytgcalls/pytgcalls/__init__.py +++ b/pytgcalls/pytgcalls/__init__.py @@ -82,12 +82,9 @@ def GroupCallRaw(client, *args, **kwargs): 'GroupCallDevice', 'GroupCallRaw', ] -__version__ = '2.0.0' +__version__ = '2.1.0' __pdoc__ = { # files 'utils': False, 'dispatcher': False, - # packages - 'mtproto.data': False, - 'mtproto.exceptions': False, } diff --git a/pytgcalls/pytgcalls/implementation/group_call.py b/pytgcalls/pytgcalls/implementation/group_call.py index f10a85e6..a72cecc0 100644 --- a/pytgcalls/pytgcalls/implementation/group_call.py +++ b/pytgcalls/pytgcalls/implementation/group_call.py @@ -38,6 +38,8 @@ class GroupCallAction: NETWORK_STATUS_CHANGED = Action() '''When a status of network will be changed.''' + PARTICIPANT_LIST_UPDATED = Action() + '''When a list of participant will be updated.''' class GroupCallDispatcherMixin(DispatcherMixin): @@ -53,6 +55,22 @@ def on_network_status_changed(self, func: Callable) -> Callable: return self.add_handler(func, GroupCallAction.NETWORK_STATUS_CHANGED) + def on_participant_list_updated(self, func: Callable) -> Callable: + """When a list of participant will be updated. + + Args: + func (`Callable`): A functions that accept group_call and participants args. + + Note: + The `participants` arg is a `list` of `GroupCallParticipantWrapper`. + It contains only updated participants! It's not a list of all participants! + + Returns: + `Callable`: passed to args callback function. + """ + + return self.add_handler(func, GroupCallAction.PARTICIPANT_LIST_UPDATED) + class GroupCall(ABC, GroupCallDispatcherMixin, GroupCallNative): SEND_ACTION_UPDATE_EACH = 0.45 @@ -100,6 +118,8 @@ async def _group_call_participants_update_callback(self, update: UpdateGroupCall logger.debug('Group call participants update...') logger.debug(update) + self.trigger_handlers(GroupCallAction.PARTICIPANT_LIST_UPDATED, self, update.participants) + for participant in update.participants: ssrc = uint_ssrc(participant.source) diff --git a/pytgcalls/pytgcalls/mtproto/data/base_wrapper.py b/pytgcalls/pytgcalls/mtproto/data/base_wrapper.py index c2e8ccf1..a5812d4f 100644 --- a/pytgcalls/pytgcalls/mtproto/data/base_wrapper.py +++ b/pytgcalls/pytgcalls/mtproto/data/base_wrapper.py @@ -19,6 +19,8 @@ class WrapperBase: + __slots__ = '__dict__' + def __repr__(self): return f'<{self.__class__.__name__}>({vars(self)})' diff --git a/pytgcalls/pytgcalls/mtproto/data/group_call_participant_wrapper.py b/pytgcalls/pytgcalls/mtproto/data/group_call_participant_wrapper.py index 2aa4a539..5667b993 100644 --- a/pytgcalls/pytgcalls/mtproto/data/group_call_participant_wrapper.py +++ b/pytgcalls/pytgcalls/mtproto/data/group_call_participant_wrapper.py @@ -17,14 +17,83 @@ # You should have received a copy of the GNU Lesser General Public License v3 # along with tgcalls. If not, see . +from datetime import datetime +from typing import Optional, Union + from pytgcalls.mtproto.data import WrapperBase class GroupCallParticipantWrapper(WrapperBase): - def __init__(self, source: int, left: bool, peer, muted: bool, can_self_unmute: bool, is_self: bool): - self.source = source - self.left = left + """Group Call Participant wrapper for any MTProto client. + + Note: + `peer` will be `raw.base.Peer` when you are using Pyrogram bridge. + + `peer` will be `TypePeer` when you are using Telethon bridge. + + `date` and `active_date`will be `int` when you are using Pyrogram bridge. + + `date` and `active_date`will be `datetime` when you are using Telethon bridge. + + `video_joined` always will be `None` for Pyrogram until it will update. + """ + + def __init__( + self, + peer: Union['raw.base.Peer', 'TypePeer'], + date: Optional[Union[int, datetime]], + source: int, + muted: Optional[bool] = None, + left: Optional[bool] = None, + can_self_unmute: Optional[bool] = None, + just_joined: Optional[bool] = None, + versioned: Optional[bool] = None, + min: Optional[bool] = None, + muted_by_you: Optional[bool] = None, + volume_by_admin: Optional[bool] = None, + is_self: Optional[bool] = None, + video_joined: Optional[bool] = None, + active_date: Optional[Union[int, datetime]] = None, + volume: Optional[int] = None, + about: Optional[str] = None, + raise_hand_rating: Optional[int] = None, + **kwargs, # to bypass new attr from layers + ): self.peer = peer + '''Peer (as joined) of the group call participant''' + self.date = date + self.source = source + '''User's audio channel synchronization source identifier''' self.muted = muted + '''True, if the participant is muted for all users''' + self.left = left + '''True, if the participant left the group call''' self.can_self_unmute = can_self_unmute + '''True, if the participant is muted for all users, but can unmute themselves''' + self.just_joined = just_joined + self.versioned = versioned + self.min = min + self.muted_by_you = muted_by_you + '''True, if the current user can mute the participant only for self''' + self.volume_by_admin = volume_by_admin self.is_self = is_self + '''True, if the participant is the current user''' + self.video_joined = video_joined + self.active_date = active_date + self.volume = volume + '''Participant's volume level''' + self.about = about + '''The participant user's bio or the participant chat's description''' + self.raise_hand_rating = raise_hand_rating + + @classmethod + def create(cls, participant): + if hasattr(participant, '__slots__'): # pyrogram + attrs = participant.__slots__ + args_for_init = {} + for attr in attrs: + args_for_init[attr] = getattr(participant, attr, None) + + return cls(**args_for_init) + else: # telethon + return cls(**vars(participant)) diff --git a/pytgcalls/pytgcalls/mtproto/pyrogram_bridge.py b/pytgcalls/pytgcalls/mtproto/pyrogram_bridge.py index 2328fdb6..4bc3f799 100644 --- a/pytgcalls/pytgcalls/mtproto/pyrogram_bridge.py +++ b/pytgcalls/pytgcalls/mtproto/pyrogram_bridge.py @@ -63,17 +63,7 @@ async def _process_update(self, _, update, users, chats): await self._update_to_handler[type(update)](update) async def _process_group_call_participants_update(self, update): - participants = [ - GroupCallParticipantWrapper( - p.source, - p.left, - p.peer, - p.muted, - p.can_self_unmute, - p.is_self, - ) - for p in update.participants - ] + participants = [GroupCallParticipantWrapper.create(p) for p in update.participants] wrapped_update = UpdateGroupCallParticipantsWrapper(participants) await self.group_call_participants_update_callback(wrapped_update) diff --git a/pytgcalls/pytgcalls/mtproto/telethon_bridge.py b/pytgcalls/pytgcalls/mtproto/telethon_bridge.py index f0b3e786..5d9867c3 100644 --- a/pytgcalls/pytgcalls/mtproto/telethon_bridge.py +++ b/pytgcalls/pytgcalls/mtproto/telethon_bridge.py @@ -69,17 +69,7 @@ async def _process_update(self, update): raise StopPropagation async def _process_group_call_participants_update(self, update): - participants = [ - GroupCallParticipantWrapper( - p.source, - p.left, - p.peer, - p.muted, - p.can_self_unmute, - p.is_self, - ) - for p in update.participants - ] + participants = [GroupCallParticipantWrapper.create(p) for p in update.participants] wrapped_update = UpdateGroupCallParticipantsWrapper(participants) await self.group_call_participants_update_callback(wrapped_update)