diff --git a/telethon/types/_custom/chatgetter.py b/telethon/types/_custom/chatgetter.py index 03306849..3f991d7f 100644 --- a/telethon/types/_custom/chatgetter.py +++ b/telethon/types/_custom/chatgetter.py @@ -6,138 +6,65 @@ from ... import errors, _tl class ChatGetter(abc.ABC): """ - Helper base class that introduces the `chat`, `input_chat` - and `chat_id` properties and `get_chat` and `get_input_chat` - methods. + Helper base class that introduces the chat-related properties and methods. """ - def __init__(self, chat_peer=None, *, input_chat=None, chat=None, broadcast=None): - self._chat_peer = chat_peer - self._input_chat = input_chat + def __init__(self, chat, client): self._chat = chat - self._broadcast = broadcast - self._client = None + self._client = client @property def chat(self): """ - Returns the :tl:`User`, :tl:`Chat` or :tl:`Channel` where this object - belongs to. It may be `None` if Telegram didn't send the chat. + Returns the `User` or `Chat` who sent this object, or `None` if there is no chat. - If you only need the ID, use `chat_id` instead. + The chat of an event is only guaranteed to include the ``id``. + If you need the chat to at least have basic information, use `get_chat` instead. - If you need to call a method which needs - this chat, use `input_chat` instead. - - If you're using `telethon.events`, use `get_chat()` instead. + Chats obtained through friendly methods (not events) will always have complete + information (so there is no need to use `get_chat` or ``chat.fetch()``). """ return self._chat async def get_chat(self): """ - Returns `chat`, but will make an API call to find the - chat unless it's already cached. + Returns `chat`, but will make an API call to find the chat unless it's already cached. If you only need the ID, use `chat_id` instead. - If you need to call a method which needs - this chat, use `get_input_chat()` instead. - """ - # See `get_sender` for information about 'min'. - if (self._chat is None or getattr(self._chat, 'min', None))\ - and await self.get_input_chat(): - try: - self._chat =\ - await self._client.get_entity(self._input_chat) - except ValueError: - await self._refetch_chat() - return self._chat + If you need to call a method which needs this chat, prefer `chat` instead. - @property - def input_chat(self): - """ - This :tl:`InputPeer` is the input version of the chat where the - message was sent. Similarly to `input_sender - `, this - doesn't have things like username or similar, but still useful in - some cases. + Telegram may send a "minimal" version of the chat to save on bandwidth when using events. + If you need all the information about the chat upfront, you can use ``chat.fetch()``. - Note that this might not be available if the library doesn't - have enough information available. - """ - return self._input_chat + .. code-block:: python - async def get_input_chat(self): - """ - Returns `input_chat`, but will make an API call to find the - input chat unless it's already cached. - """ - if self.input_chat is None and self.chat_id and self._client: - try: - # The chat may be recent, look in dialogs - target = self.chat_id - async for d in self._client.get_dialogs(100): - if d.id == target: - self._chat = d.entity - self._input_chat = d.input_entity - break - except errors.RpcError: - pass + @client.on(events.NewMessage) + async def handler(event): + # I only need the ID -> use chat_id + chat_id = event.chat_id - return self._input_chat + # I'm going to use the chat in a method -> use chat + await client.send_message(event.chat, 'Hi!') + + # I need the chat's title -> use get_chat + chat = await event.get_chat() + print(chat.title) + + # I want to see all the information about the chat -> use fetch + chat = await event.chat.fetch() + print(chat.stringify()) + + # ... + + async for message in client.get_messages(chat): + # Here there's no need to fetch the chat - get_messages already did + print(message.chat.stringify()) + """ + raise RuntimeError('TODO') @property def chat_id(self): """ - Returns the marked chat integer ID. Note that this value **will - be different** from ``peer_id`` for incoming private messages, since - the chat *to* which the messages go is to your own person, but - the *chat* itself is with the one who sent the message. - - TL;DR; this gets the ID that you expect. - - If there is a chat in the object, `chat_id` will *always* be set, - which is why you should use it instead of `chat.id `. - """ - return utils.get_peer_id(self._chat_peer) if self._chat_peer else None - - @property - def is_private(self): - """ - `True` if the message was sent as a private message. - - Returns `None` if there isn't enough information - (e.g. on `events.MessageDeleted `). - """ - return isinstance(self._chat_peer, _tl.PeerUser) if self._chat_peer else None - - @property - def is_group(self): - """ - True if the message was sent on a group or megagroup. - - Returns `None` if there isn't enough information - (e.g. on `events.MessageDeleted `). - """ - # TODO Cache could tell us more in the future - if self._broadcast is None and hasattr(self.chat, 'broadcast'): - self._broadcast = bool(self.chat.broadcast) - - if isinstance(self._chat_peer, _tl.PeerChannel): - if self._broadcast is None: - return None - else: - return not self._broadcast - - return isinstance(self._chat_peer, _tl.PeerChat) - - @property - def is_channel(self): - """`True` if the message was sent on a megagroup or channel.""" - # The only case where chat peer could be none is in MessageDeleted, - # however those always have the peer in channels. - return isinstance(self._chat_peer, _tl.PeerChannel) - - async def _refetch_chat(self): - """ - Re-fetches chat information through other means. + Alias for ``self.chat.id``, but checking if ``chat is not None`` first. """ + return self._chat.id if self._chat else None diff --git a/telethon/types/_custom/sendergetter.py b/telethon/types/_custom/sendergetter.py index 58d84657..9f264cc0 100644 --- a/telethon/types/_custom/sendergetter.py +++ b/telethon/types/_custom/sendergetter.py @@ -3,89 +3,65 @@ import abc class SenderGetter(abc.ABC): """ - Helper base class that introduces the `sender`, `input_sender` - and `sender_id` properties and `get_sender` and `get_input_sender` - methods. + Helper base class that introduces the sender-related properties and methods. """ - def __init__(self, sender_id=None, *, sender=None, input_sender=None): - self._sender_id = sender_id + def __init__(self, sender, client): self._sender = sender - self._input_sender = input_sender - self._client = None + self._client = client @property def sender(self): """ - Returns the :tl:`User` or :tl:`Channel` that sent this object. - It may be `None` if Telegram didn't send the sender. + Returns the `User` or `Chat` who sent this object, or `None` if there is no sender. - If you only need the ID, use `sender_id` instead. + The sender of an event is only guaranteed to include the ``id``. + If you need the sender to at least have basic information, use `get_sender` instead. - If you need to call a method which needs - this chat, use `input_sender` instead. - - If you're using `telethon.events`, use `get_sender()` instead. + Senders obtained through friendly methods (not events) will always have complete + information (so there is no need to use `get_sender` or ``sender.fetch()``). """ return self._sender async def get_sender(self): """ - Returns `sender`, but will make an API call to find the - sender unless it's already cached. + Returns `sender`, but will make an API call to find the sender unless it's already cached. If you only need the ID, use `sender_id` instead. - If you need to call a method which needs - this sender, use `get_input_sender()` instead. - """ - # ``sender.min`` is present both in :tl:`User` and :tl:`Channel`. - # It's a flag that will be set if only minimal information is - # available (such as display name, but username may be missing), - # in which case we want to force fetch the entire thing because - # the user explicitly called a method. If the user is okay with - # cached information, they may use the property instead. - if (self._sender is None or getattr(self._sender, 'min', None)) \ - and await self.get_input_sender(): - try: - self._sender =\ - await self._client.get_entity(self._input_sender) - except ValueError: - await self._refetch_sender() - return self._sender + If you need to call a method which needs this sender, prefer `sender` instead. - @property - def input_sender(self): - """ - This :tl:`InputPeer` is the input version of the user/channel who - sent the message. Similarly to `input_chat - `, this doesn't - have things like username or similar, but still useful in some cases. + Telegram may send a "minimal" version of the sender to save on bandwidth when using events. + If you need all the information about the sender upfront, you can use ``sender.fetch()``. - Note that this might not be available if the library can't - find the input chat, or if the message a broadcast on a channel. - """ - return self._input_sender + .. code-block:: python - async def get_input_sender(self): + @client.on(events.NewMessage) + async def handler(event): + # I only need the ID -> use sender_id + sender_id = event.sender_id + + # I'm going to use the sender in a method -> use sender + await client.send_message(event.sender, 'Hi!') + + # I need the sender's first name -> use get_sender + sender = await event.get_sender() + print(sender.first_name) + + # I want to see all the information about the sender -> use fetch + sender = await event.sender.fetch() + print(sender.stringify()) + + # ... + + async for message in client.get_messages(chat): + # Here there's no need to fetch the sender - get_messages already did + print(message.sender.stringify()) """ - Returns `input_sender`, but will make an API call to find the - input sender unless it's already cached. - """ - if self.input_sender is None and self._sender_id and self._client: - await self._refetch_sender() - return self._input_sender + raise RuntimeError('TODO') @property def sender_id(self): """ - Returns the marked sender integer ID, if present. - - If there is a sender in the object, `sender_id` will *always* be set, - which is why you should use it instead of `sender.id `. - """ - return self._sender_id - - async def _refetch_sender(self): - """ - Re-fetches sender information through other means. + Alias for ``self.sender.id``, but checking if ``sender is not None`` first. """ + return self._sender.id if sender else None