Create a base class for Chat

This commit is contained in:
Lonami Exo 2023-10-16 20:11:08 +02:00
parent c4d399e32d
commit 04806a2a4a
8 changed files with 128 additions and 85 deletions

View File

@ -1,5 +1,5 @@
from .async_list import AsyncList
from .chat import Channel, Chat, ChatLike, Group, RestrictionReason, User
from .chat import Channel, Chat, ChatLike, Group, User
from .dialog import Dialog
from .draft import Draft
from .file import File, InFileLike, OutFileLike, OutWrapper
@ -17,7 +17,6 @@ __all__ = [
"Chat",
"ChatLike",
"Group",
"RestrictionReason",
"User",
"Dialog",
"Draft",

View File

@ -2,10 +2,10 @@ from typing import Union
from ....session import PackedChat
from .channel import Channel
from .chat import Chat
from .group import Group
from .user import RestrictionReason, User
from .user import User
Chat = Union[Channel, Group, User]
ChatLike = Union[Chat, PackedChat, int, str]
__all__ = ["Chat", "ChatLike", "Channel", "Group", "RestrictionReason", "User"]
__all__ = ["Chat", "ChatLike", "Channel", "Group", "User"]

View File

@ -3,9 +3,10 @@ from typing import Optional, Self, Union
from ....session import PackedChat, PackedType
from ....tl import abcs, types
from ..meta import NoPublicConstructor
from .chat import Chat
class Channel(metaclass=NoPublicConstructor):
class Channel(Chat, metaclass=NoPublicConstructor):
"""
A broadcast channel.
@ -32,10 +33,25 @@ class Channel(metaclass=NoPublicConstructor):
else:
raise RuntimeError("unexpected case")
# region Overrides
@property
def id(self) -> int:
return self._raw.id
@property
def name(self) -> str:
"""
The channel's title.
This property is always present, but may be the empty string.
"""
return self._raw.title
@property
def username(self) -> Optional[str]:
return getattr(self._raw, "username", None)
def pack(self) -> Optional[PackedChat]:
if self._raw.access_hash is None:
return None
@ -48,14 +64,4 @@ class Channel(metaclass=NoPublicConstructor):
access_hash=None,
)
@property
def title(self) -> str:
return getattr(self._raw, "title", None) or ""
@property
def full_name(self) -> str:
return self.title
@property
def username(self) -> Optional[str]:
return getattr(self._raw, "username", None)
# endregion Overrides

View File

@ -0,0 +1,60 @@
import abc
from typing import Optional
from ....session import PackedChat
class Chat(abc.ABC):
"""
The base class for all chat types.
This will either be a :class:`User`, :class:`Group` or :class:`Channel`.
"""
__slots__ = ()
@property
@abc.abstractmethod
def id(self) -> int:
"""
The chat's integer identifier.
This identifier is always a positive number.
This property is always present.
"""
@property
@abc.abstractmethod
def name(self) -> str:
"""
The full name of the user, group or channel.
For users, this will be the :attr:`User.first_name` concatenated with the :attr:`User.last_name`.
For groups and channels, this will be their title.
If there is no name (such as for deleted accounts), an empty string ``''`` will be returned.
"""
@property
@abc.abstractmethod
def username(self) -> Optional[str]:
"""
The primary *@username* of the chat.
The returned string will *not* contain the at-sign ``@``.
"""
@abc.abstractmethod
def pack(self) -> Optional[PackedChat]:
"""
Pack the chat into a compact and reusable object.
This object can be easily serialized and saved to persistent storage.
Unlike resolving usernames, packed chats can be reused without costly calls.
.. seealso::
:doc:`/concepts/chats`
"""

View File

@ -3,9 +3,10 @@ from typing import Optional, Self, Union
from ....session import PackedChat, PackedType
from ....tl import abcs, types
from ..meta import NoPublicConstructor
from .chat import Chat
class Group(metaclass=NoPublicConstructor):
class Group(Chat, metaclass=NoPublicConstructor):
"""
A small group or supergroup.
@ -38,10 +39,25 @@ class Group(metaclass=NoPublicConstructor):
else:
raise RuntimeError("unexpected case")
# region Overrides
@property
def id(self) -> int:
return self._raw.id
@property
def name(self) -> str:
"""
The group's title.
This property is always present, but may be the empty string.
"""
return self._raw.title
@property
def username(self) -> Optional[str]:
return getattr(self._raw, "username", None)
def pack(self) -> Optional[PackedChat]:
if isinstance(self._raw, (types.ChatEmpty, types.Chat, types.ChatForbidden)):
return PackedChat(ty=PackedType.CHAT, id=self._raw.id, access_hash=None)
@ -52,18 +68,13 @@ class Group(metaclass=NoPublicConstructor):
ty=PackedType.MEGAGROUP, id=self._raw.id, access_hash=None
)
@property
def title(self) -> str:
return getattr(self._raw, "title", None) or ""
@property
def full_name(self) -> str:
return self.title
@property
def username(self) -> Optional[str]:
return getattr(self._raw, "username", None)
# endregion Overrides
@property
def is_megagroup(self) -> bool:
"""
Whether the group is a supergroup.
These are known as "megagroups" in Telegram's API, and are different from "gigagroups".
"""
return isinstance(self._raw, (types.Channel, types.ChannelForbidden))

View File

@ -1,39 +1,12 @@
from typing import List, Optional, Self
from typing import Optional, Self
from ....session import PackedChat, PackedType
from ....tl import abcs, types
from ..meta import NoPublicConstructor
from .chat import Chat
class RestrictionReason(metaclass=NoPublicConstructor):
"""
A restriction reason for :class:`telethon.types.User`.
"""
__slots__ = ("_raw",)
def __init__(self, raw: types.RestrictionReason) -> None:
self._raw = raw
@classmethod
def _from_raw(cls, reason: abcs.RestrictionReason) -> Self:
assert isinstance(reason, types.RestrictionReason)
return cls._create(reason)
@property
def platforms(self) -> List[str]:
return self._raw.platform.split("-")
@property
def reason(self) -> str:
return self._raw.reason
@property
def text(self) -> str:
return self._raw.text
class User(metaclass=NoPublicConstructor):
class User(Chat, metaclass=NoPublicConstructor):
"""
A user, representing either a bot account or an account created with a phone number.
@ -95,10 +68,27 @@ class User(metaclass=NoPublicConstructor):
else:
raise RuntimeError("unexpected case")
# region Overrides
@property
def id(self) -> int:
return self._raw.id
@property
def name(self) -> str:
"""
The user's full name.
This property joins both the :attr:`first_name` and :attr:`last_name` into a single string.
This property is always present, but may be the empty string.
"""
return f"{self.first_name} {self.last_name}".strip()
@property
def username(self) -> Optional[str]:
return self._raw.username
def pack(self) -> Optional[PackedChat]:
if self._raw.access_hash is not None:
return PackedChat(
@ -109,6 +99,8 @@ class User(metaclass=NoPublicConstructor):
else:
return None
# endregion Overrides
@property
def first_name(self) -> str:
return self._raw.first_name or ""
@ -117,14 +109,6 @@ class User(metaclass=NoPublicConstructor):
def last_name(self) -> str:
return self._raw.last_name or ""
@property
def full_name(self) -> str:
return f"{self.first_name} {self.last_name}".strip()
@property
def username(self) -> Optional[str]:
return self._raw.username
@property
def phone(self) -> Optional[str]:
return self._raw.phone
@ -132,9 +116,3 @@ class User(metaclass=NoPublicConstructor):
@property
def bot(self) -> bool:
return self._raw.bot
@property
def restriction_reasons(self) -> List[RestrictionReason]:
return [
RestrictionReason._from_raw(r) for r in (self._raw.restriction_reason or [])
]

View File

@ -27,10 +27,7 @@ def build_chat_map(users: List[abcs.User], chats: List[abcs.Chat]) -> Dict[int,
for c in chats
)
# https://github.com/python/mypy/issues/2115
result: Dict[int, Chat] = {
c.id: c for c in itertools.chain(users_iter, chats_iter) # type: ignore [attr-defined, misc]
}
result: Dict[int, Chat] = {c.id: c for c in itertools.chain(users_iter, chats_iter)}
if len(result) != len(users) + len(chats):
# The fabled ID collision between different chat types.

View File

@ -5,20 +5,16 @@ from ._impl.client.types import (
AsyncList,
Channel,
Chat,
ChatLike,
Dialog,
Draft,
File,
Group,
InFileLike,
InlineResult,
LoginToken,
Message,
OutFileLike,
Participant,
PasswordToken,
RecentAction,
RestrictionReason,
User,
)
from ._impl.session import PackedChat, PackedType
@ -28,19 +24,15 @@ __all__ = [
"AsyncList",
"Channel",
"Chat",
"ChatLike",
"Dialog",
"Draft",
"File",
"Group",
"InFileLike",
"LoginToken",
"Message",
"OutFileLike",
"Participant",
"PasswordToken",
"RecentAction",
"RestrictionReason",
"User",
"PackedChat",
"PackedType",