From e9f9994f4ae25b0fbc900b395c8296215cbd7334 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 17 Sep 2021 19:35:10 +0200 Subject: [PATCH] Unify client.iter_* methods --- readthedocs/misc/v2-migration-guide.rst | 34 +++++ telethon/_client/chats.py | 24 +--- telethon/_client/dialogs.py | 16 +-- telethon/_client/messages.py | 21 +-- telethon/_client/telegramclient.py | 168 +++++------------------- telethon/_misc/requestiter.py | 3 + 6 files changed, 78 insertions(+), 188 deletions(-) diff --git a/readthedocs/misc/v2-migration-guide.rst b/readthedocs/misc/v2-migration-guide.rst index 483c5287..e48fb41a 100644 --- a/readthedocs/misc/v2-migration-guide.rst +++ b/readthedocs/misc/v2-migration-guide.rst @@ -50,6 +50,40 @@ removed. This implies: // TODO provide standalone alternative for this? +The "iter" variant of the client methods have been removed +---------------------------------------------------------- + +Instead, you can now use the result of the ``get_*`` variant. For instance, where before you had: + +.. code-block:: python + + async for message in client.iter_messages(...): + pass + +You would now do: + + .. code-block:: python + + async for message in client.get_messages(...): + pass # ^^^ now it's get, not iter + +You can still use ``await`` on the ``get_`` methods to retrieve the list. + +The removed methods are: + +* iter_messages +* iter_dialogs +* iter_participants +* iter_admin_log +* iter_profile_photos +* iter_drafts + +The only exception to this rule is ``iter_download``. + +// TODO keep providing the old ``iter_`` versions? it doesn't really hurt, even if the recommended way changed +// TODO does the download really need to be special? get download is kind of weird though + + Raw API methods have been renamed and are now considered private ---------------------------------------------------------------- diff --git a/telethon/_client/chats.py b/telethon/_client/chats.py index 21fb02e5..ec786b36 100644 --- a/telethon/_client/chats.py +++ b/telethon/_client/chats.py @@ -401,7 +401,7 @@ class _ProfilePhotoIter(requestiter.RequestIter): self.request.offset_id = result.messages[-1].id -def iter_participants( +def get_participants( self: 'TelegramClient', entity: 'hints.EntityLike', limit: float = None, @@ -418,14 +418,8 @@ def iter_participants( aggressive=aggressive ) -async def get_participants( - self: 'TelegramClient', - *args, - **kwargs) -> 'hints.TotalList': - return await self.iter_participants(*args, **kwargs).collect() - -def iter_admin_log( +def get_admin_log( self: 'TelegramClient', entity: 'hints.EntityLike', limit: float = None, @@ -474,14 +468,8 @@ def iter_admin_log( group_call=group_call ) -async def get_admin_log( - self: 'TelegramClient', - *args, - **kwargs) -> 'hints.TotalList': - return await self.iter_admin_log(*args, **kwargs).collect() - -def iter_profile_photos( +def get_profile_photos( self: 'TelegramClient', entity: 'hints.EntityLike', limit: int = None, @@ -496,12 +484,6 @@ def iter_profile_photos( max_id=max_id ) -async def get_profile_photos( - self: 'TelegramClient', - *args, - **kwargs) -> 'hints.TotalList': - return await self.iter_profile_photos(*args, **kwargs).collect() - def action( self: 'TelegramClient', diff --git a/telethon/_client/dialogs.py b/telethon/_client/dialogs.py index b293edca..d4b4644b 100644 --- a/telethon/_client/dialogs.py +++ b/telethon/_client/dialogs.py @@ -136,7 +136,7 @@ class _DraftsIter(requestiter.RequestIter): return [] -def iter_dialogs( +def get_dialogs( self: 'TelegramClient', limit: float = None, *, @@ -162,11 +162,8 @@ def iter_dialogs( folder=folder ) -async def get_dialogs(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList': - return await self.iter_dialogs(*args, **kwargs).collect() - -def iter_drafts( +def get_drafts( self: 'TelegramClient', entity: 'hints.EntitiesLike' = None ) -> _DraftsIter: @@ -176,15 +173,6 @@ def iter_drafts( # TODO Passing a limit here makes no sense return _DraftsIter(self, None, entities=entity) -async def get_drafts( - self: 'TelegramClient', - entity: 'hints.EntitiesLike' = None -) -> 'hints.TotalList': - items = await self.iter_drafts(entity).collect() - if not entity or utils.is_list_like(entity): - return items - else: - return items[0] async def edit_folder( self: 'TelegramClient', diff --git a/telethon/_client/messages.py b/telethon/_client/messages.py index b7bbf518..c29610e4 100644 --- a/telethon/_client/messages.py +++ b/telethon/_client/messages.py @@ -318,7 +318,7 @@ class _IDsIter(requestiter.RequestIter): self.buffer.append(_custom.Message._new(self.client, message, entities, self._entity)) -def iter_messages( +def get_messages( self: 'TelegramClient', entity: 'hints.EntityLike', limit: float = None, @@ -368,25 +368,6 @@ def iter_messages( scheduled=scheduled ) -async def get_messages(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList': - if len(args) == 1 and 'limit' not in kwargs: - if 'min_id' in kwargs and 'max_id' in kwargs: - kwargs['limit'] = None - else: - kwargs['limit'] = 1 - - it = self.iter_messages(*args, **kwargs) - - ids = kwargs.get('ids') - if ids and not utils.is_list_like(ids): - async for message in it: - return message - else: - # Iterator exhausted = empty, to handle InputMessageReplyTo - return None - - return await it.collect() - async def _get_comment_data( self: 'TelegramClient', diff --git a/telethon/_client/telegramclient.py b/telethon/_client/telegramclient.py index 1a12b122..1482dbdd 100644 --- a/telethon/_client/telegramclient.py +++ b/telethon/_client/telegramclient.py @@ -716,7 +716,7 @@ class TelegramClient: # region Chats - def iter_participants( + def get_participants( self: 'TelegramClient', entity: 'hints.EntityLike', limit: float = None, @@ -784,32 +784,14 @@ class TelegramClient: from telethon.tl.types import ChannelParticipantsAdmins async for user in client.iter_participants(chat, filter=ChannelParticipantsAdmins): print(user.first_name) + + # Get a list of 0 people but print the total amount of participants in the chat + users = await client.get_participants(chat, limit=0) + print(users.total) """ - return chats.iter_participants(**locals()) + return chats.get_participants(**locals()) - async def get_participants( - self: 'TelegramClient', - *args, - **kwargs) -> 'hints.TotalList': - """ - Same as `iter_participants()`, but returns a - `TotalList ` instead. - - Example - .. code-block:: python - - users = await client.get_participants(chat) - print(users[0].first_name) - - for user in users: - if user.username is not None: - print(user.username) - """ - return await chats.get_participants(*args, **kwargs) - - get_participants.__signature__ = inspect.signature(iter_participants) - - def iter_admin_log( + def get_admin_log( self: 'TelegramClient', entity: 'hints.EntityLike', limit: float = None, @@ -931,30 +913,16 @@ class TelegramClient: async for event in client.iter_admin_log(channel): if event.changed_title: print('The title changed from', event.old, 'to', event.new) - """ - return chats.iter_admin_log(**locals()) - async def get_admin_log( - self: 'TelegramClient', - *args, - **kwargs) -> 'hints.TotalList': - """ - Same as `iter_admin_log()`, but returns a ``list`` instead. - - Example - .. code-block:: python - - # Get a list of deleted message events which said "heck" - events = await client.get_admin_log(channel, search='heck', delete=True) + # Get all events of deleted message events which said "heck" and print the last one + events = await client.get_admin_log(channel, limit=None, search='heck', delete=True) # Print the old message before it was deleted - print(events[0].old) + print(events[-1].old) """ - return await chats.get_admin_log(*args, **kwargs) + return chats.get_admin_log(**locals()) - get_admin_log.__signature__ = inspect.signature(iter_admin_log) - - def iter_profile_photos( + def get_profile_photos( self: 'TelegramClient', entity: 'hints.EntityLike', limit: int = None, @@ -991,29 +959,12 @@ class TelegramClient: # Download all the profile photos of some user async for photo in client.iter_profile_photos(user): await client.download_media(photo) - """ - return chats.iter_profile_photos(**locals()) - async def get_profile_photos( - self: 'TelegramClient', - *args, - **kwargs) -> 'hints.TotalList': - """ - Same as `iter_profile_photos()`, but returns a - `TotalList ` instead. - - Example - .. code-block:: python - - # Get the photos of a channel - photos = await client.get_profile_photos(channel) - - # Download the oldest photo + # Get all the photos of a channel and download the oldest one + photos = await client.get_profile_photos(channel, limit=None) await client.download_media(photos[-1]) """ - return await chats.get_profile_photos(*args, **kwargs) - - get_profile_photos.__signature__ = inspect.signature(iter_profile_photos) + return chats.get_profile_photos(**locals()) def action( self: 'TelegramClient', @@ -1443,7 +1394,7 @@ class TelegramClient: # region Dialogs - def iter_dialogs( + def get_dialogs( self: 'TelegramClient', limit: float = None, *, @@ -1517,19 +1468,9 @@ class TelegramClient: # Print all dialog IDs and the title, nicely formatted async for dialog in client.iter_dialogs(): print('{:>14}: {}'.format(dialog.id, dialog.title)) - """ - return dialogs.iter_dialogs(**locals()) - - async def get_dialogs(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList': - """ - Same as `iter_dialogs()`, but returns a - `TotalList ` instead. - - Example - .. code-block:: python # Get all open conversation, print the title of the first - dialogs = await client.get_dialogs() + dialogs = await client.get_dialogs(limit=None) first = dialogs[0] print(first.title) @@ -1537,18 +1478,16 @@ class TelegramClient: await client.send_message(first, 'hi') # Getting only non-archived dialogs (both equivalent) - non_archived = await client.get_dialogs(folder=0) - non_archived = await client.get_dialogs(archived=False) + non_archived = await client.get_dialogs(folder=0, limit=None) + non_archived = await client.get_dialogs(archived=False, limit=None) # Getting only archived dialogs (both equivalent) - archived = await client.get_dialogs(folder=1) - archived = await client.get_dialogs(archived=True) + archived = await client.get_dialogs(folder=1, limit=None) + archived = await client.get_dialogs(archived=True, limit=None) """ - return await dialogs.get_dialogs(*args, **kwargs) + return dialogs.get_dialogs(**locals()) - get_dialogs.__signature__ = inspect.signature(iter_dialogs) - - def iter_drafts( + def get_drafts( self: 'TelegramClient', entity: 'hints.EntitiesLike' = None ) -> dialogs._DraftsIter: @@ -1575,28 +1514,12 @@ class TelegramClient: # Getting the drafts with 'bot1' and 'bot2' async for draft in client.iter_drafts(['bot1', 'bot2']): print(draft.text) - """ - return dialogs.iter_drafts(**locals()) - - async def get_drafts( - self: 'TelegramClient', - entity: 'hints.EntitiesLike' = None - ) -> 'hints.TotalList': - """ - Same as `iter_drafts()`, but returns a list instead. - - Example - .. code-block:: python - - # Get drafts, print the text of the first - drafts = await client.get_drafts() - print(drafts[0].text) # Get the draft in your chat draft = await client.get_drafts('me') - print(drafts.text) + print(draft.text) """ - return await dialogs.get_drafts(**locals()) + return dialogs.get_drafts(**locals()) async def edit_folder( self: 'TelegramClient', @@ -2037,7 +1960,7 @@ class TelegramClient: # region Messages - def iter_messages( + def get_messages( self: 'TelegramClient', entity: 'hints.EntityLike', limit: float = None, @@ -2199,8 +2122,8 @@ class TelegramClient: async for message in client.iter_messages(chat, reverse=True): print(message.id, message.text) - # Filter by sender - async for message in client.iter_messages(chat, from_user='me'): + # Filter by sender, and limit to 10 + async for message in client.iter_messages(chat, 10, from_user='me'): print(message.text) # Server-side search with fuzzy text @@ -2215,43 +2138,22 @@ class TelegramClient: # Getting comments from a post in a channel: async for message in client.iter_messages(channel, reply_to=123): print(message.chat.title, message.text) - """ - return messages.iter_messages(**locals()) - - async def get_messages(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList': - """ - Same as `iter_messages()`, but returns a - `TotalList ` instead. - - If the `limit` is not set, it will be 1 by default unless both - `min_id` **and** `max_id` are set (as *named* arguments), in - which case the entire range will be returned. - - This is so because any integer limit would be rather arbitrary and - it's common to only want to fetch one message, but if a range is - specified it makes sense that it should return the entirety of it. - - If `ids` is present in the *named* arguments and is not a list, - a single `Message ` will be - returned for convenience instead of a list. - - Example - .. code-block:: python # Get 0 photos and print the total to show how many photos there are from telethon.tl.types import InputMessagesFilterPhotos photos = await client.get_messages(chat, 0, filter=InputMessagesFilterPhotos) print(photos.total) - # Get all the photos - photos = await client.get_messages(chat, None, filter=InputMessagesFilterPhotos) + # Get all the photos in a list + all_photos = await client.get_messages(chat, None, filter=InputMessagesFilterPhotos) - # Get messages by ID: + # Get the last photo or None if none has been sent yet (same as setting limit 1) + photo = await client.get_messages(chat, filter=InputMessagesFilterPhotos) + + # Get a single message given an ID: message_1337 = await client.get_messages(chat, ids=1337) """ - return await messages.get_messages(**locals()) - - get_messages.__signature__ = inspect.signature(iter_messages) + return messages.get_messages(**locals()) async def send_message( self: 'TelegramClient', diff --git a/telethon/_misc/requestiter.py b/telethon/_misc/requestiter.py index 6473fe0f..9c837c05 100644 --- a/telethon/_misc/requestiter.py +++ b/telethon/_misc/requestiter.py @@ -114,3 +114,6 @@ class RequestIter(abc.ABC): def __reversed__(self): self.reverse = not self.reverse return self # __aiter__ will be called after, too + + def __await__(self): + return self.collect().__await__()