Skip to content

Commit

Permalink
Fix getting and using participants
Browse files Browse the repository at this point in the history
  • Loading branch information
Lonami committed Nov 2, 2023
1 parent cea9fc6 commit e4706e3
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 9 deletions.
4 changes: 0 additions & 4 deletions client/src/telethon/_impl/client/client/bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@


class InlineResults(metaclass=NoPublicConstructor):
"""
:final:
"""

def __init__(
self,
client: Client,
Expand Down
13 changes: 10 additions & 3 deletions client/src/telethon/_impl/client/client/chats.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(
self._chat = chat
self._peer: Optional[abcs.InputPeer] = None
self._offset = 0
self._seen = set()

async def _fetch_next(self) -> None:
if self._peer is None:
Expand All @@ -45,10 +46,16 @@ async def _fetch_next(self) -> None:

chat_map = build_chat_map(result.users, result.chats)

self._buffer.extend(
Participant._from_raw_channel(p, chat_map) for p in result.participants
)
seen_count = len(self._seen)
for p in result.participants:
part = Participant._from_raw_channel(p, chat_map)
if part not in self._seen:
self._seen.add(part._peer_id())
self._buffer.append(part)

self._total = result.count
self._offset += len(result.participants)
self._done = len(self._seen) == seen_count

elif isinstance(self._peer, types.InputPeerChat):
result = await self._client(
Expand Down
2 changes: 2 additions & 0 deletions client/src/telethon/_impl/client/types/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .admin_right import AdminRight
from .async_list import AsyncList
from .callback_answer import CallbackAnswer
from .chat import (
Expand All @@ -22,6 +23,7 @@
from .recent_action import RecentAction

__all__ = [
"AdminRight",
"AsyncList",
"CallbackAnswer",
"Channel",
Expand Down
104 changes: 104 additions & 0 deletions client/src/telethon/_impl/client/types/admin_right.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from __future__ import annotations

from enum import Enum
from typing import Set

from ...tl import abcs, types


class AdminRight(Enum):
"""
A right that can be granted to a chat's administrator.
.. note::
The specific values of the enumeration are not covered by `semver <https://semver.org/>`_.
They also may do nothing in future updates if Telegram decides to change them.
"""

CHANGE_INFO = "change_info"
"""Allows editing the description in a group or channel."""

POST_MESSAGES = "post_messages"
"""Allows sending messages in a broadcast channel."""

EDIT_MESSAGES = "edit_messages"
"""Allows editing messages in a group or channel."""

DELETE_MESSAGES = "delete_messages"
"""Allows deleting messages in a group or channel."""

BAN_USERS = "ban_users"
"""Allows setting the banned rights of other users in a group or channel."""

INVITE_USERS = "invite_users"
"""Allows inviting other users to the group or channel."""

PIN_MESSAGES = "pin_messages"
"""Allows pinning a message to the group or channel."""

MANAGE_ADMINS = "add_admins"
"""Allows setting the same or less administrator rights to other users in the group or channel."""

REMAIN_ANONYMOUS = "anonymous"
"""Allows the administrator to remain anonymous."""

MANAGE_CALLS = "manage_call"
"""Allows managing group or channel calls."""

OTHER = "other"
"""Unspecified."""

MANAGE_TOPICS = "manage_topics"
"""Allows managing the topics in a group."""

POST_STORIES = "post_stories"
"""Allows posting stories in a channel."""

EDIT_STORIES = "edit_stories"
"""Allows editing stories in a channel."""

DELETE_STORIES = "delete_stories"
"""Allows deleting stories in a channel."""

@classmethod
def _from_raw(cls, rights: abcs.ChatAdminRights) -> Set[AdminRight]:
assert isinstance(rights, types.ChatAdminRights)
all_rights = (
cls.CHANGE_INFO if rights.change_info else None,
cls.POST_MESSAGES if rights.post_messages else None,
cls.EDIT_MESSAGES if rights.edit_messages else None,
cls.DELETE_MESSAGES if rights.delete_messages else None,
cls.BAN_USERS if rights.ban_users else None,
cls.INVITE_USERS if rights.invite_users else None,
cls.PIN_MESSAGES if rights.pin_messages else None,
cls.MANAGE_ADMINS if rights.add_admins else None,
cls.REMAIN_ANONYMOUS if rights.anonymous else None,
cls.MANAGE_CALLS if rights.manage_call else None,
cls.OTHER if rights.other else None,
cls.MANAGE_TOPICS if rights.manage_topics else None,
cls.POST_STORIES if rights.post_stories else None,
cls.EDIT_STORIES if rights.edit_stories else None,
cls.DELETE_STORIES if rights.delete_stories else None,
)
return set(filter(None, iter(all_rights)))

@classmethod
def _chat_rights(cls) -> Set[AdminRight]:
return {
cls.CHANGE_INFO,
cls.POST_MESSAGES,
cls.EDIT_MESSAGES,
cls.DELETE_MESSAGES,
cls.BAN_USERS,
cls.INVITE_USERS,
cls.PIN_MESSAGES,
cls.MANAGE_ADMINS,
cls.REMAIN_ANONYMOUS,
cls.MANAGE_CALLS,
cls.OTHER,
cls.MANAGE_TOPICS,
cls.POST_STORIES,
cls.EDIT_STORIES,
cls.DELETE_STORIES,
}
100 changes: 98 additions & 2 deletions client/src/telethon/_impl/client/types/participant.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import Dict, Self, Union
from typing import Dict, Optional, Self, Set, Union

from ...tl import abcs, types
from .chat import Chat
from .admin_right import AdminRight
from .chat import Chat, User, peer_id
from .meta import NoPublicConstructor


Expand Down Expand Up @@ -66,3 +67,98 @@ def _from_raw_chat(
return cls._create(participant, chat_map)
else:
raise RuntimeError("unexpected case")

def _peer_id(self) -> int:
if isinstance(
self._raw,
(
types.ChannelParticipant,
types.ChannelParticipantSelf,
types.ChannelParticipantCreator,
types.ChannelParticipantAdmin,
types.ChatParticipant,
types.ChatParticipantCreator,
types.ChatParticipantAdmin,
),
):
return self._raw.user_id
elif isinstance(
self._raw, (types.ChannelParticipantBanned, types.ChannelParticipantLeft)
):
return peer_id(self._raw.peer)
else:
raise RuntimeError("unexpected case")

@property
def user(self) -> Optional[User]:
"""
The user participant that is currently present in the chat.
This will be :data:`None` if the participant was instead :attr:`banned` or has :attr:`left`.
"""
if isinstance(
self._raw,
(
types.ChannelParticipant,
types.ChannelParticipantSelf,
types.ChannelParticipantCreator,
types.ChannelParticipantAdmin,
types.ChatParticipant,
types.ChatParticipantCreator,
types.ChatParticipantAdmin,
),
):
user = self._chat_map[self._raw.user_id]
assert isinstance(user, User)
return user
else:
return None

@property
def banned(self) -> Optional[Chat]:
"""
The banned participant.
This will usually be a :class:`User`.
"""
if isinstance(self._raw, types.ChannelParticipantBanned):
return self._chat_map[peer_id(self._raw.peer)]
else:
return None

@property
def left(self) -> Optional[Chat]:
"""
The participant that has left the group.
This will usually be a :class:`User`.
"""
if isinstance(self._raw, types.ChannelParticipantLeft):
return self._chat_map[peer_id(self._raw.peer)]
else:
return None

@property
def creator(self) -> bool:
"""
:data:`True` if the participant is the creator of the chat.
"""
return isinstance(
self._raw, (types.ChannelParticipantCreator, types.ChatParticipantCreator)
)

@property
def admin_rights(self) -> Optional[Set[AdminRight]]:
"""
The set of administrator rights this participant has been granted, if they are an administrator.
"""
if isinstance(
self._raw, (types.ChannelParticipantCreator, types.ChannelParticipantAdmin)
):
return AdminRight._from_raw(self._raw.admin_rights)
elif isinstance(
self._raw, (types.ChatParticipantCreator, types.ChatParticipantAdmin)
):
return AdminRight._chat_rights()
else:
return None
2 changes: 2 additions & 0 deletions client/src/telethon/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Classes for the various objects the library returns.
"""
from .._impl.client.types import (
AdminRight,
AsyncList,
CallbackAnswer,
Channel,
Expand All @@ -22,6 +23,7 @@
from .._impl.session import PackedChat, PackedType

__all__ = [
"AdminRight",
"AsyncList",
"CallbackAnswer",
"Channel",
Expand Down
File renamed without changes.
14 changes: 14 additions & 0 deletions client/tests/types_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pytest import mark
from telethon._impl.client.types import AdminRight
from telethon._impl.tl import types


@mark.parametrize("slot", types.ChatAdminRights.__slots__)
def test_admin_right_covers_all(slot: str) -> None:
kwargs = {slot: False for slot in types.ChatAdminRights.__slots__}
kwargs[slot] = True

rights = types.ChatAdminRights(**kwargs)
rights_set = AdminRight._from_raw(rights)
assert len(rights_set) == 1
assert next(iter(rights_set)).value == slot

0 comments on commit e4706e3

Please sign in to comment.