From d80c6b3bb44e85f3dc7e78db5d77e9940313ff91 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sun, 29 Oct 2023 20:54:31 +0100 Subject: [PATCH] Remove utils file This gets rid of awkward lazy imports as well. --- .../src/telethon/_impl/client/client/chats.py | 3 +- .../telethon/_impl/client/client/dialogs.py | 3 +- .../src/telethon/_impl/client/client/files.py | 4 +- .../telethon/_impl/client/client/messages.py | 12 +- .../telethon/_impl/client/client/updates.py | 2 +- .../src/telethon/_impl/client/client/users.py | 12 +- .../_impl/client/events/filters/messages.py | 2 +- .../telethon/_impl/client/types/__init__.py | 22 +++- .../_impl/client/types/chat/__init__.py | 85 +++++++++++++- .../src/telethon/_impl/client/types/dialog.py | 6 +- .../src/telethon/_impl/client/types/draft.py | 12 +- .../src/telethon/_impl/client/types/file.py | 2 +- .../_impl/client/types/inline_result.py | 6 +- .../telethon/_impl/client/types/message.py | 39 +++++-- client/src/telethon/_impl/client/utils.py | 108 ------------------ 15 files changed, 167 insertions(+), 151 deletions(-) delete mode 100644 client/src/telethon/_impl/client/utils.py diff --git a/client/src/telethon/_impl/client/client/chats.py b/client/src/telethon/_impl/client/client/chats.py index ad3c3317..a8a7b2a2 100644 --- a/client/src/telethon/_impl/client/client/chats.py +++ b/client/src/telethon/_impl/client/client/chats.py @@ -3,8 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional from ...tl import abcs, functions, types -from ..types import AsyncList, ChatLike, File, Participant, RecentAction -from ..utils import build_chat_map +from ..types import AsyncList, ChatLike, File, Participant, RecentAction, build_chat_map from .messages import SearchList if TYPE_CHECKING: diff --git a/client/src/telethon/_impl/client/client/dialogs.py b/client/src/telethon/_impl/client/client/dialogs.py index f35473eb..e52a723c 100644 --- a/client/src/telethon/_impl/client/client/dialogs.py +++ b/client/src/telethon/_impl/client/client/dialogs.py @@ -4,8 +4,7 @@ import time from typing import TYPE_CHECKING, Optional from ...tl import functions, types -from ..types import AsyncList, ChatLike, Dialog, Draft -from ..utils import build_chat_map, build_msg_map +from ..types import AsyncList, ChatLike, Dialog, Draft, build_chat_map, build_msg_map from .messages import parse_message if TYPE_CHECKING: diff --git a/client/src/telethon/_impl/client/client/files.py b/client/src/telethon/_impl/client/client/files.py index da127d61..1919d16a 100644 --- a/client/src/telethon/_impl/client/client/files.py +++ b/client/src/telethon/_impl/client/client/files.py @@ -16,9 +16,9 @@ from ..types import ( Message, OutFileLike, OutWrapper, + expand_stripped_size, + generate_random_id, ) -from ..types.file import expand_stripped_size -from ..utils import generate_random_id from .messages import parse_message if TYPE_CHECKING: diff --git a/client/src/telethon/_impl/client/client/messages.py b/client/src/telethon/_impl/client/client/messages.py index b27335ab..ca104c24 100644 --- a/client/src/telethon/_impl/client/client/messages.py +++ b/client/src/telethon/_impl/client/client/messages.py @@ -7,8 +7,16 @@ from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union from ...session import PackedChat from ...tl import abcs, functions, types from ..parsers import parse_html_message, parse_markdown_message -from ..types import AsyncList, Chat, ChatLike, Message, buttons -from ..utils import build_chat_map, generate_random_id, peer_id +from ..types import ( + AsyncList, + Chat, + ChatLike, + Message, + build_chat_map, + buttons, + generate_random_id, + peer_id, +) if TYPE_CHECKING: from .client import Client diff --git a/client/src/telethon/_impl/client/client/updates.py b/client/src/telethon/_impl/client/client/updates.py index e65dfdb1..5a94ca92 100644 --- a/client/src/telethon/_impl/client/client/updates.py +++ b/client/src/telethon/_impl/client/client/updates.py @@ -16,7 +16,7 @@ from ...session import Gap from ...tl import abcs from ..events import Event as EventBase from ..events.filters import Filter -from ..utils import build_chat_map +from ..types import build_chat_map if TYPE_CHECKING: from .client import Client diff --git a/client/src/telethon/_impl/client/client/users.py b/client/src/telethon/_impl/client/client/users.py index 74591ec5..eb6b72bc 100644 --- a/client/src/telethon/_impl/client/client/users.py +++ b/client/src/telethon/_impl/client/client/users.py @@ -5,8 +5,16 @@ from typing import TYPE_CHECKING, Optional from ...mtproto import RpcError from ...session import PackedChat, PackedType from ...tl import abcs, functions, types -from ..types import AsyncList, Channel, Chat, ChatLike, Group, User -from ..utils import build_chat_map, peer_id +from ..types import ( + AsyncList, + Channel, + Chat, + ChatLike, + Group, + User, + build_chat_map, + peer_id, +) if TYPE_CHECKING: from .client import Client diff --git a/client/src/telethon/_impl/client/events/filters/messages.py b/client/src/telethon/_impl/client/events/filters/messages.py index fa451192..50d30576 100644 --- a/client/src/telethon/_impl/client/events/filters/messages.py +++ b/client/src/telethon/_impl/client/events/filters/messages.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Literal, Optional, Tuple, Union from ..event import Event if TYPE_CHECKING: - from ...client import Client + from ...client.client import Client class Text: diff --git a/client/src/telethon/_impl/client/types/__init__.py b/client/src/telethon/_impl/client/types/__init__.py index b284f9b0..6e03e6ef 100644 --- a/client/src/telethon/_impl/client/types/__init__.py +++ b/client/src/telethon/_impl/client/types/__init__.py @@ -1,12 +1,21 @@ from .async_list import AsyncList from .callback_answer import CallbackAnswer -from .chat import Channel, Chat, ChatLike, Group, User +from .chat import ( + Channel, + Chat, + ChatLike, + Group, + User, + build_chat_map, + expand_peer, + peer_id, +) from .dialog import Dialog from .draft import Draft -from .file import File, InFileLike, OutFileLike, OutWrapper +from .file import File, InFileLike, OutFileLike, OutWrapper, expand_stripped_size from .inline_result import InlineResult from .login_token import LoginToken -from .message import Message +from .message import Message, adapt_date, build_msg_map, generate_random_id from .meta import NoPublicConstructor from .participant import Participant from .password_token import PasswordToken @@ -20,15 +29,22 @@ __all__ = [ "ChatLike", "Group", "User", + "build_chat_map", + "expand_peer", + "peer_id", "Dialog", "Draft", "File", "InFileLike", "OutFileLike", "OutWrapper", + "expand_stripped_size", "InlineResult", "LoginToken", "Message", + "adapt_date", + "build_msg_map", + "generate_random_id", "NoPublicConstructor", "Participant", "PasswordToken", diff --git a/client/src/telethon/_impl/client/types/chat/__init__.py b/client/src/telethon/_impl/client/types/chat/__init__.py index 14460b5d..1d03aea7 100644 --- a/client/src/telethon/_impl/client/types/chat/__init__.py +++ b/client/src/telethon/_impl/client/types/chat/__init__.py @@ -1,6 +1,10 @@ -from typing import Union +import itertools +import sys +from collections import defaultdict +from typing import DefaultDict, Dict, List, Optional, Union from ....session import PackedChat +from ....tl import abcs, types from .channel import Channel from .chat import Chat from .group import Group @@ -8,4 +12,81 @@ from .user import User ChatLike = Union[Chat, PackedChat, int, str] -__all__ = ["Chat", "ChatLike", "Channel", "Group", "User"] + +def build_chat_map(users: List[abcs.User], chats: List[abcs.Chat]) -> Dict[int, Chat]: + users_iter = (User._from_raw(u) for u in users) + chats_iter = ( + Channel._from_raw(c) + if isinstance(c, (types.Channel, types.ChannelForbidden)) and c.broadcast + else Group._from_raw(c) + for c in chats + ) + + 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. + counter: DefaultDict[int, List[Union[abcs.User, abcs.Chat]]] = defaultdict(list) + for user in users: + if (id := getattr(user, "id", None)) is not None: + counter[id].append(user) + for chat in chats: + if (id := getattr(chat, "id", None)) is not None: + counter[id].append(chat) + + for k, v in counter.items(): + if len(v) > 1: + for x in v: + print(x, file=sys.stderr) + + raise RuntimeError( + f"chat identifier collision: {k}; please report this" + ) + + return result + + +def peer_id(peer: abcs.Peer) -> int: + if isinstance(peer, types.PeerUser): + return peer.user_id + elif isinstance(peer, types.PeerChat): + return peer.chat_id + elif isinstance(peer, types.PeerChannel): + return peer.channel_id + else: + raise RuntimeError("unexpected case") + + +def expand_peer(peer: abcs.Peer, *, broadcast: Optional[bool]) -> Chat: + if isinstance(peer, types.PeerUser): + return User._from_raw(types.UserEmpty(id=peer.user_id)) + elif isinstance(peer, types.PeerChat): + return Group._from_raw(types.ChatEmpty(id=peer.chat_id)) + elif isinstance(peer, types.PeerChannel): + if broadcast is None: + broadcast = True # assume broadcast by default (Channel type is more accurate than Group) + + channel = types.ChannelForbidden( + broadcast=broadcast, + megagroup=not broadcast, + id=peer.channel_id, + access_hash=0, + title="", + until_date=None, + ) + + return Channel._from_raw(channel) if broadcast else Group._from_raw(channel) + else: + raise RuntimeError("unexpected case") + + +__all__ = [ + "Chat", + "ChatLike", + "Channel", + "Group", + "User", + "build_chat_map", + "peer_id", + "expand_peer", +] diff --git a/client/src/telethon/_impl/client/types/dialog.py b/client/src/telethon/_impl/client/types/dialog.py index 6ca095fa..54357a11 100644 --- a/client/src/telethon/_impl/client/types/dialog.py +++ b/client/src/telethon/_impl/client/types/dialog.py @@ -3,13 +3,13 @@ from __future__ import annotations from typing import TYPE_CHECKING, Dict, Optional, Self, Union from ...tl import abcs, types -from .chat import Chat +from .chat import Chat, peer_id from .draft import Draft from .message import Message from .meta import NoPublicConstructor if TYPE_CHECKING: - from ..client import Client + from ..client.client import Client class Dialog(metaclass=NoPublicConstructor): @@ -51,8 +51,6 @@ class Dialog(metaclass=NoPublicConstructor): """ The chat where messages are sent in this dialog. """ - from ..utils import peer_id - return self._chat_map[peer_id(self._raw.peer)] @property diff --git a/client/src/telethon/_impl/client/types/draft.py b/client/src/telethon/_impl/client/types/draft.py index 866779fb..c517968a 100644 --- a/client/src/telethon/_impl/client/types/draft.py +++ b/client/src/telethon/_impl/client/types/draft.py @@ -6,12 +6,12 @@ from typing import TYPE_CHECKING, Dict, Optional, Self from ...session import PackedChat from ...tl import abcs, functions, types from ..parsers import generate_html_message, generate_markdown_message -from .chat import Chat -from .message import Message +from .chat import Chat, expand_peer, peer_id +from .message import Message, generate_random_id from .meta import NoPublicConstructor if TYPE_CHECKING: - from ..client import Client + from ..client.client import Client class Draft(metaclass=NoPublicConstructor): @@ -60,8 +60,6 @@ class Draft(metaclass=NoPublicConstructor): This is also the chat where the message will be sent to by :meth:`send`. """ - from ..utils import expand_peer, peer_id - return self._chat_map.get(peer_id(self._peer)) or expand_peer( self._peer, broadcast=None ) @@ -161,8 +159,6 @@ class Draft(metaclass=NoPublicConstructor): ) async def _packed_chat(self) -> PackedChat: - from ..utils import peer_id - packed = None if chat := self._chat_map.get(peer_id(self._peer)): packed = chat.pack() @@ -184,8 +180,6 @@ class Draft(metaclass=NoPublicConstructor): await draft.send(clear=False) """ - from ..utils import generate_random_id - packed = await self._packed_chat() peer = packed._to_input_peer() diff --git a/client/src/telethon/_impl/client/types/file.py b/client/src/telethon/_impl/client/types/file.py index 21e428c8..b92a7a16 100644 --- a/client/src/telethon/_impl/client/types/file.py +++ b/client/src/telethon/_impl/client/types/file.py @@ -10,7 +10,7 @@ from ...tl import abcs, types from .meta import NoPublicConstructor if TYPE_CHECKING: - from ..client import Client + from ..client.client import Client math_round = round diff --git a/client/src/telethon/_impl/client/types/inline_result.py b/client/src/telethon/_impl/client/types/inline_result.py index a4046b9f..3a4746a0 100644 --- a/client/src/telethon/_impl/client/types/inline_result.py +++ b/client/src/telethon/_impl/client/types/inline_result.py @@ -4,11 +4,11 @@ from typing import TYPE_CHECKING, Optional, Union from ...tl import abcs, functions, types from .chat import ChatLike -from .message import Message +from .message import Message, generate_random_id from .meta import NoPublicConstructor if TYPE_CHECKING: - from ..client import Client + from ..client.client import Client class InlineResult(metaclass=NoPublicConstructor): @@ -62,8 +62,6 @@ class InlineResult(metaclass=NoPublicConstructor): :return: The sent message. """ - from ..utils import generate_random_id - if chat is None and isinstance(self._default_peer, types.InputPeerEmpty): raise ValueError("no target chat was specified") diff --git a/client/src/telethon/_impl/client/types/message.py b/client/src/telethon/_impl/client/types/message.py index 3dc164ff..ca979ef0 100644 --- a/client/src/telethon/_impl/client/types/message.py +++ b/client/src/telethon/_impl/client/types/message.py @@ -1,17 +1,37 @@ from __future__ import annotations import datetime +import time from typing import TYPE_CHECKING, Any, Dict, List, Optional, Self, Union from ...tl import abcs, types from ..parsers import generate_html_message, generate_markdown_message from .buttons import Button, as_concrete_row, create_button -from .chat import Chat, ChatLike +from .chat import Chat, ChatLike, expand_peer, peer_id from .file import File from .meta import NoPublicConstructor if TYPE_CHECKING: - from ..client import Client + from ..client.client import Client + + +_last_id = 0 + + +def generate_random_id() -> int: + global _last_id + if _last_id == 0: + _last_id = int(time.time() * 1e9) + _last_id += 1 + return _last_id + + +def adapt_date(date: Optional[int]) -> Optional[datetime.datetime]: + return ( + datetime.datetime.fromtimestamp(date, tz=datetime.timezone.utc) + if date is not None + else None + ) class Message(metaclass=NoPublicConstructor): @@ -148,8 +168,6 @@ class Message(metaclass=NoPublicConstructor): """ The date when the message was sent. """ - from ..utils import adapt_date - return adapt_date(getattr(self._raw, "date", None)) @property @@ -157,8 +175,6 @@ class Message(metaclass=NoPublicConstructor): """ The :term:`chat` when the message was sent. """ - from ..utils import expand_peer, peer_id - peer = self._raw.peer_id or types.PeerUser(user_id=0) pid = peer_id(peer) if pid not in self._chat_map: @@ -176,8 +192,6 @@ class Message(metaclass=NoPublicConstructor): If there is no sender, it means the message was sent by an anonymous user. """ - from ..utils import expand_peer, peer_id - if (from_ := getattr(self._raw, "from_id", None)) is not None: return self._chat_map.get(peer_id(from_)) or expand_peer( from_, broadcast=getattr(self._raw, "post", None) @@ -444,3 +458,12 @@ class Message(metaclass=NoPublicConstructor): return not self._raw.noforwards else: return False + + +def build_msg_map( + client: Client, messages: List[abcs.Message], chat_map: Dict[int, Chat] +) -> Dict[int, Message]: + return { + msg.id: msg + for msg in (Message._from_raw(client, m, chat_map) for m in messages) + } diff --git a/client/src/telethon/_impl/client/utils.py b/client/src/telethon/_impl/client/utils.py deleted file mode 100644 index b82175db..00000000 --- a/client/src/telethon/_impl/client/utils.py +++ /dev/null @@ -1,108 +0,0 @@ -from __future__ import annotations - -import datetime -import itertools -import sys -import time -from collections import defaultdict -from typing import TYPE_CHECKING, DefaultDict, Dict, List, Optional, Union - -from ..tl import abcs, types -from .types import Channel, Chat, Group, Message, User - -if TYPE_CHECKING: - from .client import Client - -_last_id = 0 - - -def generate_random_id() -> int: - global _last_id - if _last_id == 0: - _last_id = int(time.time() * 1e9) - _last_id += 1 - return _last_id - - -def build_chat_map(users: List[abcs.User], chats: List[abcs.Chat]) -> Dict[int, Chat]: - users_iter = (User._from_raw(u) for u in users) - chats_iter = ( - Channel._from_raw(c) - if isinstance(c, (types.Channel, types.ChannelForbidden)) and c.broadcast - else Group._from_raw(c) - for c in chats - ) - - 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. - counter: DefaultDict[int, List[Union[abcs.User, abcs.Chat]]] = defaultdict(list) - for user in users: - if (id := getattr(user, "id", None)) is not None: - counter[id].append(user) - for chat in chats: - if (id := getattr(chat, "id", None)) is not None: - counter[id].append(chat) - - for k, v in counter.items(): - if len(v) > 1: - for x in v: - print(x, file=sys.stderr) - - raise RuntimeError( - f"chat identifier collision: {k}; please report this" - ) - - return result - - -def build_msg_map( - client: Client, messages: List[abcs.Message], chat_map: Dict[int, Chat] -) -> Dict[int, Message]: - return { - msg.id: msg - for msg in (Message._from_raw(client, m, chat_map) for m in messages) - } - - -def peer_id(peer: abcs.Peer) -> int: - if isinstance(peer, types.PeerUser): - return peer.user_id - elif isinstance(peer, types.PeerChat): - return peer.chat_id - elif isinstance(peer, types.PeerChannel): - return peer.channel_id - else: - raise RuntimeError("unexpected case") - - -def expand_peer(peer: abcs.Peer, *, broadcast: Optional[bool]) -> Chat: - if isinstance(peer, types.PeerUser): - return User._from_raw(types.UserEmpty(id=peer.user_id)) - elif isinstance(peer, types.PeerChat): - return Group._from_raw(types.ChatEmpty(id=peer.chat_id)) - elif isinstance(peer, types.PeerChannel): - if broadcast is None: - broadcast = True # assume broadcast by default (Channel type is more accurate than Group) - - channel = types.ChannelForbidden( - broadcast=broadcast, - megagroup=not broadcast, - id=peer.channel_id, - access_hash=0, - title="", - until_date=None, - ) - - return Channel._from_raw(channel) if broadcast else Group._from_raw(channel) - else: - raise RuntimeError("unexpected case") - - -def adapt_date(date: Optional[int]) -> Optional[datetime.datetime]: - return ( - datetime.datetime.fromtimestamp(date, tz=datetime.timezone.utc) - if date is not None - else None - )