diff --git a/telethon/client/account.py b/telethon/client/account.py index 884bfae3..b6a2e1e7 100644 --- a/telethon/client/account.py +++ b/telethon/client/account.py @@ -127,13 +127,6 @@ class AccountMethods(UserMethods): them. In other words, returns the current client modified so that requests are done as a takeout: - >>> from telethon.sync import TelegramClient - >>> - >>> with TelegramClient(...) as client: - >>> with client.takeout() as takeout: - >>> client.get_messages('me') # normal call - >>> takeout.get_messages('me') # wrapped through takeout - Some of the calls made through the takeout session will have lower flood limits. This is useful if you want to export the data from conversations or mass-download media, since the rate limits will @@ -159,7 +152,7 @@ class AccountMethods(UserMethods): preserved for future usage as `client.session.takeout_id `. - Args: + Arguments finalize (`bool`): Whether the takeout session should be finalized upon exit or not. @@ -191,14 +184,16 @@ class AccountMethods(UserMethods): The maximum file size, in bytes, that you plan to download for each message with media. - Example: - + Example .. code-block:: python from telethon import errors try: with client.takeout() as takeout: + client.get_messages('me') # normal call + takeout.get_messages('me') # wrapped through takeout (less limits) + for message in takeout.iter_messages(chat, wait_time=0): ... # Do something with the message @@ -228,12 +223,17 @@ class AccountMethods(UserMethods): """ Finishes the current takeout session. - Args: + Arguments success (`bool`): Whether the takeout completed successfully or not. - Returns: + Returns ``True`` if the operation was successful, ``False`` otherwise. + + Example + .. code-block:: python + + client.end_takeout(success=False) """ try: async with _TakeoutClient(True, self, None) as takeout: diff --git a/telethon/client/auth.py b/telethon/client/auth.py index 9010c301..3428c004 100644 --- a/telethon/client/auth.py +++ b/telethon/client/auth.py @@ -44,7 +44,7 @@ class AuthMethods(MessageParseMethods, UserMethods): coroutine that you should await on your own code; otherwise the loop is ran until said coroutine completes. - Args: + Arguments phone (`str` | `int` | `callable`): The phone (or callable without arguments to get it) to which the code will be sent. If a bot-token-like @@ -81,11 +81,11 @@ class AuthMethods(MessageParseMethods, UserMethods): How many times the code/password callback should be retried or switching between signing in and signing up. - Returns: + Returns This `TelegramClient`, so initialization can be chained with ``.start()``. - Example: + Example .. code-block:: python client = TelegramClient('anon', api_id, api_hash) @@ -271,7 +271,7 @@ class AuthMethods(MessageParseMethods, UserMethods): This method will send the code if it's not provided. - Args: + Arguments phone (`str` | `int`): The phone to send the code to if no code was provided, or to override the phone that was previously used with @@ -295,9 +295,18 @@ class AuthMethods(MessageParseMethods, UserMethods): The hash returned by `send_code_request`. This can be left as ``None`` to use the last hash known for the phone to be used. - Returns: + Returns The signed in user, or the information about :meth:`send_code_request`. + + Example + .. code-block:: python + + phone = '+34 123 123 123' + client.sign_in(phone) # send code + + code = input('enter code: ') + client.sign_in(phone, code) """ me = await self.get_me() if me: @@ -351,7 +360,7 @@ class AuthMethods(MessageParseMethods, UserMethods): will be banned otherwise.** See https://telegram.org/tos and https://core.telegram.org/api/terms. - Args: + Arguments code (`str` | `int`): The code sent by Telegram @@ -369,8 +378,17 @@ class AuthMethods(MessageParseMethods, UserMethods): The hash returned by `send_code_request`. This can be left as ``None`` to use the last hash known for the phone to be used. - Returns: + Returns The new created :tl:`User`. + + Example + .. code-block:: python + + phone = '+34 123 123 123' + client.send_code_request(phone) + + code = input('enter code: ') + client.sign_up(code, first_name='Anna', last_name='Banana') """ me = await self.get_me() if me: @@ -421,15 +439,27 @@ class AuthMethods(MessageParseMethods, UserMethods): """ Sends the Telegram code needed to login to the given phone number. - Args: + Arguments phone (`str` | `int`): The phone to which the code will be sent. force_sms (`bool`, optional): Whether to force sending as SMS. - Returns: + Returns An instance of :tl:`SentCode`. + + Example + .. code-block:: python + + phone = '+34 123 123 123' + sent = client.send_code_request(phone) + print(sent) + + if sent.phone_registered: + print('This phone has an existing account registered') + else: + print('This phone does not have an account registered') """ result = None phone = utils.parse_phone(phone) or self._phone @@ -461,11 +491,10 @@ class AuthMethods(MessageParseMethods, UserMethods): """ Logs out Telegram and deletes the current ``*.session`` file. - Returns: + Returns ``True`` if the operation was successful. - Example: - + Example .. code-block:: python # Note: you will need to login again! @@ -504,37 +533,47 @@ class AuthMethods(MessageParseMethods, UserMethods): Has no effect if both current and new password are omitted. - current_password (`str`, optional): - The current password, to authorize changing to ``new_password``. - Must be set if changing existing 2FA settings. - Must **not** be set if 2FA is currently disabled. - Passing this by itself will remove 2FA (if correct). + Arguments + current_password (`str`, optional): + The current password, to authorize changing to ``new_password``. + Must be set if changing existing 2FA settings. + Must **not** be set if 2FA is currently disabled. + Passing this by itself will remove 2FA (if correct). - new_password (`str`, optional): - The password to set as 2FA. - If 2FA was already enabled, ``current_password`` **must** be set. - Leaving this blank or ``None`` will remove the password. + new_password (`str`, optional): + The password to set as 2FA. + If 2FA was already enabled, ``current_password`` **must** be set. + Leaving this blank or ``None`` will remove the password. - hint (`str`, optional): - Hint to be displayed by Telegram when it asks for 2FA. - Leaving unspecified is highly discouraged. - Has no effect if ``new_password`` is not set. + hint (`str`, optional): + Hint to be displayed by Telegram when it asks for 2FA. + Leaving unspecified is highly discouraged. + Has no effect if ``new_password`` is not set. - email (`str`, optional): - Recovery and verification email. If present, you must also - set `email_code_callback`, else it raises ``ValueError``. + email (`str`, optional): + Recovery and verification email. If present, you must also + set `email_code_callback`, else it raises ``ValueError``. - email_code_callback (`callable`, optional): - If an email is provided, a callback that returns the code sent - to it must also be set. This callback may be asynchronous. - It should return a string with the code. The length of the - code will be passed to the callback as an input parameter. + email_code_callback (`callable`, optional): + If an email is provided, a callback that returns the code sent + to it must also be set. This callback may be asynchronous. + It should return a string with the code. The length of the + code will be passed to the callback as an input parameter. - If the callback returns an invalid code, it will raise - ``CodeInvalidError``. + If the callback returns an invalid code, it will raise + ``CodeInvalidError``. - Returns: + Returns ``True`` if successful, ``False`` otherwise. + + Example + .. code-block:: python + + # Setting a password for your account which didn't have + client.edit_2fa(new_password='I_<3_Telethon') + + # Removing the password + client.edit_2fa(current_password='I_<3_Telethon') """ if new_password is None and current_password is None: return False diff --git a/telethon/client/bots.py b/telethon/client/bots.py index 1fefecf0..e8f87bed 100644 --- a/telethon/client/bots.py +++ b/telethon/client/bots.py @@ -19,10 +19,7 @@ class BotMethods(UserMethods): """ Makes an inline query to the specified bot (e.g. ``@vote New Poll``). - >>> client = ... - >>> client.inline_query('vote', 'My New Poll') - - Args: + Arguments bot (`entity`): The bot entity to which the inline query should be made. @@ -36,12 +33,11 @@ class BotMethods(UserMethods): The geo point location information to send to the bot for localised results. Available under some bots. - Returns: + Returns A list of `custom.InlineResult `. - Example: - + Example .. code-block:: python # Make an inline query to @like diff --git a/telethon/client/buttons.py b/telethon/client/buttons.py index a5afe2dc..7b73d369 100644 --- a/telethon/client/buttons.py +++ b/telethon/client/buttons.py @@ -24,6 +24,22 @@ class ButtonMethods(UpdateMethods): the markup very often. Otherwise, it is not necessary. This method is **not** asynchronous (don't use ``await`` on it). + + Arguments + buttons (`hints.MarkupLike`): + The button, list of buttons, array of buttons or markup + to convert into a markup. + + inline_only (`bool`, optional): + Whether the buttons **must** be inline buttons only or not. + + Example + .. code-block:: python + + from telethon import Button + + markup = client.build_reply_markup(Button.inline('hi')) + client.send_message('click me', buttons=markup) """ if buttons is None: return None diff --git a/telethon/client/chats.py b/telethon/client/chats.py index 273979f6..35a8ae89 100644 --- a/telethon/client/chats.py +++ b/telethon/client/chats.py @@ -289,7 +289,7 @@ class ChatMethods(UserMethods): """ Iterator over the participants belonging to the specified chat. - Args: + Arguments entity (`entity`): The entity from which to retrieve the participants list. @@ -323,14 +323,13 @@ class ChatMethods(UserMethods): This has no effect if a ``filter`` is given. - Yields: + Yields The :tl:`User` objects returned by :tl:`GetParticipantsRequest` with an additional ``.participant`` attribute which is the matched :tl:`ChannelParticipant` type for channels/megagroups or :tl:`ChatParticipants` for normal chats. - Example: - + Example .. code-block:: python # Show all user IDs in a chat @@ -362,6 +361,16 @@ class ChatMethods(UserMethods): """ Same as `iter_participants()`, but returns a `TotalList ` instead. + + Example + .. code-block:: python + + users = client.get_participants(chat) + print(users[0].first_name) + + for user in users: + if user.username is not None: + print(user.username) """ return await self.iter_participants(*args, **kwargs).collect() @@ -397,7 +406,7 @@ class ChatMethods(UserMethods): *all* event types will be returned. If at least one of them is ``True``, only those that are true will be returned. - Args: + Arguments entity (`entity`): The channel entity from which to get its admin log. @@ -472,22 +481,15 @@ class ChatMethods(UserMethods): delete (`bool`): If ``True``, events of message deletions will be returned. - Yields: + Yields Instances of `telethon.tl.custom.adminlogevent.AdminLogEvent`. - Example: - + Example .. code-block:: python for event in client.iter_admin_log(channel): if event.changed_title: print('The title changed from', event.old, 'to', event.new) - - # Get a list of deleted message events which said "heck" - events = client.get_admin_log(channel, search='heck', delete=True) - - # Print the old message before it was deleted - print(events[0].old) """ return _AdminLogIter( self, @@ -519,6 +521,15 @@ class ChatMethods(UserMethods): **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 = client.get_admin_log(channel, search='heck', delete=True) + + # Print the old message before it was deleted + print(events[0].old) """ return await self.iter_admin_log(*args, **kwargs).collect() @@ -533,22 +544,14 @@ class ChatMethods(UserMethods): Returns a context-manager object to represent a "chat action". Chat actions indicate things like "user is typing", "user is - uploading a photo", etc. Normal usage is as follows: - - .. code-block:: python - - async with client.action(chat, 'typing'): - await asyncio.sleep(2) # type for 2 seconds - await client.send_message(chat, 'Hello world! I type slow ^^') + uploading a photo", etc. If the action is ``'cancel'``, you should just ``await`` the result, - since it makes no sense to use a context-manager for it: + since it makes no sense to use a context-manager for it. - .. code-block:: python + See the example below for intended usage. - await client.action(chat, 'cancel') - - Args: + Arguments entity (`entity`): The entity where the action should be showed in. @@ -587,10 +590,23 @@ class ChatMethods(UserMethods): you don't want progress to be shown when it has already completed. - If you are uploading a file, you may do - ``progress_callback=chat.progress`` to update the progress of - the action. Some clients don't care about this progress, though, - so it's mostly not needed, but still available. + Returns + Either a context-manager object or a coroutine. + + Example + .. code-block:: python + + # Type for 2 seconds, then send a message + async with client.action(chat, 'typing'): + await asyncio.sleep(2) + await client.send_message(chat, 'Hello world! I type slow ^^') + + # Cancel any previous action + await client.action(chat, 'cancel') + + # Upload a document, showing its progress (most clients ignore this) + async with client.action(chat, 'document') as action: + client.send_file(chat, zip_file, progress_callback=action.progress) """ if isinstance(action, str): try: diff --git a/telethon/client/dialogs.py b/telethon/client/dialogs.py index c9ff0a92..94b4e3ef 100644 --- a/telethon/client/dialogs.py +++ b/telethon/client/dialogs.py @@ -117,7 +117,7 @@ class DialogMethods(UserMethods): """ Iterator over the dialogs (open conversations/subscribed channels). - Args: + Arguments limit (`int` | `None`): How many dialogs to be retrieved as maximum. Can be set to ``None`` to retrieve all dialogs. Note that this may take @@ -159,31 +159,15 @@ class DialogMethods(UserMethods): archived (`bool`, optional): Alias for `folder`. If unspecified, all will be returned, ``False`` implies ``folder=0`` and ``True`` implies ``folder=1``. - Yields: + Yields Instances of `telethon.tl.custom.dialog.Dialog`. - Example: - + Example .. code-block:: python - # Get all open conversation, print the title of the first - dialogs = client.get_dialogs() - first = dialogs[0] - print(first.title) - - # Use the dialog somewhere else - client.send_message(first, 'hi') - - # Get drafts - drafts = client.get_drafts() - - # Getting only non-archived dialogs (both equivalent) - non_archived = client.get_dialogs(folder=0) - non_archived = client.get_dialogs(archived=False) - - # Getting only archived dialogs (both equivalent) - archived = client.get_dialogs(folder=1) - non_archived = client.get_dialogs(archived=True) + # Print all dialog IDs and the title, nicely formatted + for dialog in client.iter_dialogs(): + print('{:>14}: {}'.format(dialog.id, dialog.title)) """ if archived is not None: folder = 1 if archived else 0 @@ -202,6 +186,25 @@ class DialogMethods(UserMethods): """ Same as `iter_dialogs()`, but returns a `TotalList ` instead. + + Example + .. code-block:: python + + # Get all open conversation, print the title of the first + dialogs = client.get_dialogs() + first = dialogs[0] + print(first.title) + + # Use the dialog somewhere else + client.send_message(first, 'hi') + + # Getting only non-archived dialogs (both equivalent) + non_archived = client.get_dialogs(folder=0) + non_archived = client.get_dialogs(archived=False) + + # Getting only archived dialogs (both equivalent) + archived = client.get_dialogs(folder=1) + non_archived = client.get_dialogs(archived=True) """ return await self.iter_dialogs(*args, **kwargs).collect() @@ -213,6 +216,13 @@ class DialogMethods(UserMethods): You can call `telethon.tl.custom.draft.Draft.set_message` to change the message or `telethon.tl.custom.draft.Draft.delete` among other things. + + Example + .. code-block:: python + + # Clear all drafts + for draft in client.get_drafts(): + draft.delete() """ # TODO Passing a limit here makes no sense return _DraftsIter(self, None) @@ -220,6 +230,13 @@ class DialogMethods(UserMethods): async def get_drafts(self: 'TelegramClient') -> 'hints.TotalList': """ Same as `iter_drafts()`, but returns a list instead. + + Example + .. code-block:: python + + # Get drafts, print the text of the first + drafts = client.get_drafts() + print(drafts[0].text) """ return await self.iter_drafts().collect() @@ -233,7 +250,7 @@ class DialogMethods(UserMethods): """ Archives (or un-archives) one or more dialogs. - Args: + Arguments entity (entities): The entity or list of entities to move to the desired archive folder. @@ -258,11 +275,10 @@ class DialogMethods(UserMethods): You can only use this parameter if the other two are not set. - Returns: + Returns The :tl:`Updates` object that the request produces. - Example: - + Example .. code-block:: python # Archiving the first 5 dialogs @@ -316,7 +332,7 @@ class DialogMethods(UserMethods): with them, but rather a way to easily send messages and await for responses or other reactions. Refer to its documentation for more. - Args: + Arguments entity (`entity`): The entity with which a new conversation should be opened. @@ -381,11 +397,10 @@ class DialogMethods(UserMethods): With the setting disabled, both ``msg2`` and ``msg3`` will be ``'Hi!'`` since one is a response and also a reply. - Returns: + Returns A `Conversation `. - Example: - + Example .. code-block:: python # denotes outgoing messages you sent diff --git a/telethon/client/downloads.py b/telethon/client/downloads.py index 9a11046b..95bcea60 100644 --- a/telethon/client/downloads.py +++ b/telethon/client/downloads.py @@ -30,7 +30,7 @@ class DownloadMethods(UserMethods): """ Downloads the profile photo from the given user, chat or channel. - Args: + Arguments entity (`entity`): From who the photo will be downloaded. @@ -53,14 +53,14 @@ class DownloadMethods(UserMethods): download_big (`bool`, optional): Whether to use the big version of the available photos. - Returns: + Returns ``None`` if no photo was provided, or if it was Empty. On success the file path is returned since it may differ from the one given. - Example: - + Example .. code-block:: python + # Download your own profile photo path = client.download_profile_photo('me') print(path) """ @@ -147,42 +147,44 @@ class DownloadMethods(UserMethods): ``cryptg`` (through ``pip install cryptg``) so that decrypting the received data is done in C instead of Python (much faster). - message (`Message ` | :tl:`Media`): - The media or message containing the media that will be downloaded. + See also `Message.download_media() `. - file (`str` | `file`, optional): - The output file path, directory, or stream-like object. - If the path exists and is a file, it will be overwritten. - If file is the type `bytes`, it will be downloaded in-memory - as a bytestring (e.g. ``file=bytes``). + Arguments + message (`Message ` | :tl:`Media`): + The media or message containing the media that will be downloaded. - progress_callback (`callable`, optional): - A callback function accepting two parameters: - ``(received bytes, total)``. + file (`str` | `file`, optional): + The output file path, directory, or stream-like object. + If the path exists and is a file, it will be overwritten. + If file is the type `bytes`, it will be downloaded in-memory + as a bytestring (e.g. ``file=bytes``). - thumb (`int` | :tl:`PhotoSize`, optional): - Which thumbnail size from the document or photo to download, - instead of downloading the document or photo itself. + progress_callback (`callable`, optional): + A callback function accepting two parameters: + ``(received bytes, total)``. - If it's specified but the file does not have a thumbnail, - this method will return ``None``. + thumb (`int` | :tl:`PhotoSize`, optional): + Which thumbnail size from the document or photo to download, + instead of downloading the document or photo itself. - The parameter should be an integer index between ``0`` and - ``len(sizes)``. ``0`` will download the smallest thumbnail, - and ``len(sizes) - 1`` will download the largest thumbnail. - You can also use negative indices. + If it's specified but the file does not have a thumbnail, + this method will return ``None``. - You can also pass the :tl:`PhotoSize` instance to use. + The parameter should be an integer index between ``0`` and + ``len(sizes)``. ``0`` will download the smallest thumbnail, + and ``len(sizes) - 1`` will download the largest thumbnail. + You can also use negative indices. - In short, use ``thumb=0`` if you want the smallest thumbnail - and ``thumb=-1`` if you want the largest thumbnail. + You can also pass the :tl:`PhotoSize` instance to use. - Returns: + In short, use ``thumb=0`` if you want the smallest thumbnail + and ``thumb=-1`` if you want the largest thumbnail. + + Returns ``None`` if no media was provided, or if it was Empty. On success the file path is returned since it may differ from the one given. - Example: - + Example .. code-block:: python path = client.download_media(message) @@ -190,7 +192,6 @@ class DownloadMethods(UserMethods): # or path = message.download_media() message.download_media(filename) - """ # TODO This won't work for messageService if isinstance(message, types.Message): @@ -236,7 +237,7 @@ class DownloadMethods(UserMethods): """ Low-level method to download files from their input location. - Args: + Arguments input_location (:tl:`InputFileLocation`): The file location from which the file will be downloaded. See `telethon.utils.get_input_location` source for a complete @@ -265,6 +266,13 @@ class DownloadMethods(UserMethods): dc_id (`int`, optional): The data center the library should connect to in order to download the file. You shouldn't worry about this. + + Example + .. code-block:: python + + # Download a file and print its header + data = client.download_file(input_file, bytes) + print(data[:16]) """ if not part_size_kb: if not file_size: diff --git a/telethon/client/messageparse.py b/telethon/client/messageparse.py index 741adadb..4a0723f0 100644 --- a/telethon/client/messageparse.py +++ b/telethon/client/messageparse.py @@ -38,6 +38,15 @@ class MessageParseMethods(UserMethods): that ``assert text == unparse(*parse(text))``. See :tl:`MessageEntity` for allowed message entities. + + Example + .. code-block:: python + + # Disabling default formatting + client.parse_mode = None + + # Enabling HTML as the default format + client.parse_mode = 'html' """ return self._parse_mode diff --git a/telethon/client/messages.py b/telethon/client/messages.py index a0884163..54e1553b 100644 --- a/telethon/client/messages.py +++ b/telethon/client/messages.py @@ -319,7 +319,13 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): If either `search`, `filter` or `from_user` are provided, :tl:`messages.Search` will be used instead of :tl:`messages.getHistory`. - Args: + .. note:: + + Telegram's flood wait limit for :tl:`GetHistoryRequest` seems to + be around 30 seconds per 10 requests, therefore a sleep of 1 + second is the default for this limit (or above). + + Arguments entity (`entity`): The entity from whom to retrieve the message history. @@ -406,16 +412,10 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): You cannot use this if both `entity` and `ids` are ``None``. - Yields: + Yields Instances of `telethon.tl.custom.message.Message`. - Notes: - Telegram's flood wait limit for :tl:`GetHistoryRequest` seems to - be around 30 seconds per 10 requests, therefore a sleep of 1 - second is the default for this limit (or above). - - Example: - + Example .. code-block:: python # From most-recent to oldest @@ -439,7 +439,6 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): for message in client.iter_messages(chat, filter=InputMessagesFilterPhotos): print(message.photo) """ - if ids is not None: return _IDsIter(self, limit, entity=entity, ids=ids) @@ -476,8 +475,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): a single `Message ` will be returned for convenience instead of a list. - Example: - + Example .. code-block:: python # Get 0 photos and print the total to show how many photos there are @@ -540,8 +538,10 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): is also done through this method. Simply send ``'/start data'`` to the bot. - Args: + See also `Message.respond() ` + and `Message.reply() `. + Arguments entity (`entity`): To who will it be sent. @@ -596,20 +596,14 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): channel or not. Defaults to ``False``, which means it will notify them. Set it to ``True`` to alter this behaviour. - Returns: - + Returns The sent `custom.Message `. - Example: - + Example .. code-block:: python - client.send_message('lonami', 'Thanks for the Telethon library!') - - # Replies and responses - message = client.send_message('me', 'Trying out **markdown**') - message.reply('Trying replies') - message.respond('Trying responses') + # Markdown is the default + client.send_message('lonami', 'Thanks for the **Telethon** library!') # Default to another parse mode client.parse_mode = 'html' @@ -748,7 +742,9 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): (the "forwarded from" text), you should use `send_message` with the original message instead. This will send a copy of it. - Args: + See also `Message.forward_to() `. + + Arguments entity (`entity`): To which entity the message(s) will be forwarded. @@ -777,7 +773,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): ``True`` will group always (even converting separate images into albums), and ``False`` will never group. - Returns: + Returns The list of forwarded `telethon.tl.custom.message.Message`, or a single one if a list wasn't provided as input. @@ -785,8 +781,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): will fail with ``MessageIdInvalidError``. If only some are invalid, the list will have ``None`` instead of those messages. - Example: - + Example .. code-block:: python # a single one @@ -884,7 +879,9 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): """ Edits the given message to change its text or media. - Args: + See also `Message.edit() `. + + Arguments entity (`entity` | `Message `): From which chat to edit the message. This can also be the message to be edited, and the entity will be inferred @@ -925,38 +922,28 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): you have signed in as a bot. You can also pass your own :tl:`ReplyMarkup` here. - Examples: + Returns + The edited `telethon.tl.custom.message.Message`, unless + `entity` was a :tl:`InputBotInlineMessageID` in which + case this method returns a boolean. - >>> client = ... - >>> message = client.send_message('username', 'hello') - >>> - >>> client.edit_message('username', message, 'hello!') - >>> # or - >>> client.edit_message('username', message.id, 'Hello') - >>> # or - >>> client.edit_message(message, 'Hello!') - - Raises: + Raises ``MessageAuthorRequiredError`` if you're not the author of the message but tried editing it anyway. ``MessageNotModifiedError`` if the contents of the message were not modified at all. - Returns: - The edited `telethon.tl.custom.message.Message`, unless - `entity` was a :tl:`InputBotInlineMessageID` in which - case this method returns a boolean. - - Example: - + Example .. code-block:: python - client.edit_message(message, 'New text') + message = client.send_message(chat, 'hello') + + client.edit_message(chat, message, 'hello!') # or - message.edit('New text') + client.edit_message(chat, message.id, 'hello!!') # or - client.edit_message(chat, message_id, 'New text') + client.edit_message(message, 'hello!!!') """ if isinstance(entity, types.InputBotInlineMessageID): text = message @@ -1002,7 +989,16 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): """ Deletes the given messages, optionally "for everyone". - Args: + See also `Message.delete() `. + + .. warning:: + + This method does **not** validate that the message IDs belong + to the chat that you passed! It's possible for the method to + delete messages from different private chats and small group + chats at once, so make sure to pass the right IDs. + + Arguments entity (`entity`): From who the message will be deleted. This can actually be ``None`` for normal chats, but **must** be present @@ -1025,17 +1021,14 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): Disabling this has no effect on channels or megagroups, since it will unconditionally delete the message for everyone. - Returns: + Returns A list of :tl:`AffectedMessages`, each item being the result for the delete calls of the messages in chunks of 100 each. - Example: - + Example .. code-block:: python client.delete_messages(chat, messages) - # or - message.delete() """ if not utils.is_list_like(message_ids): message_ids = (message_ids,) @@ -1074,7 +1067,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): If neither message nor maximum ID are provided, all messages will be marked as read by assuming that ``max_id = 0``. - Args: + Arguments entity (`entity`): The chat where these messages are located. @@ -1092,8 +1085,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): If no message is provided, this will be the only action taken. - Example: - + Example .. code-block:: python client.send_read_acknowledge(last_message) diff --git a/telethon/client/telegrambaseclient.py b/telethon/client/telegrambaseclient.py index 85d6fd23..e5a722ef 100644 --- a/telethon/client/telegrambaseclient.py +++ b/telethon/client/telegrambaseclient.py @@ -34,7 +34,7 @@ class TelegramBaseClient(abc.ABC): basic stuff like connecting, switching data center, etc, and leaves the `__call__` unimplemented. - Args: + Arguments session (`str` | `telethon.sessions.abstract.Session`, `None`): The file name of the session file to be used if a string is given (it may be a full path), or the Session instance to be @@ -346,6 +346,18 @@ class TelegramBaseClient(abc.ABC): def loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop: """ Property with the ``asyncio`` event loop used by this client. + + Example + .. code-block:: python + + # Download media in the background + task = client.loop_create_task(message.download_media()) + + # Do some work + ... + + # Join the task (wait for it to complete) + await task """ return self._loop @@ -353,6 +365,15 @@ class TelegramBaseClient(abc.ABC): def disconnected(self: 'TelegramClient') -> asyncio.Future: """ Property with a ``Future`` that resolves upon disconnection. + + Example + .. code-block:: python + + # Wait for a disconnection to occur + try: + await client.disconnected + except OSError: + print('Error on disconnect') """ return self._sender.disconnected @@ -363,6 +384,14 @@ class TelegramBaseClient(abc.ABC): async def connect(self: 'TelegramClient') -> None: """ Connects to Telegram. + + Example + .. code-block:: python + + try: + client.connect() + except OSError: + print('Failed to connect') """ await self._sender.connect(self._connection( self.session.server_address, @@ -385,6 +414,12 @@ class TelegramBaseClient(abc.ABC): Returns ``True`` if the user has connected. This method is **not** asynchronous (don't use ``await`` on it). + + Example + .. code-block:: python + + while client.is_connected(): + await asyncio.sleep(1) """ sender = getattr(self, '_sender', None) return sender and sender.is_connected() @@ -397,8 +432,7 @@ class TelegramBaseClient(abc.ABC): coroutine that you should await on your own code; otherwise the loop is ran until said coroutine completes. - Example: - + Example .. code-block:: python # You don't need to use this if you used "with client" diff --git a/telethon/client/updates.py b/telethon/client/updates.py index 01e1aca0..00c3f3a0 100644 --- a/telethon/client/updates.py +++ b/telethon/client/updates.py @@ -39,6 +39,15 @@ class UpdateMethods(UserMethods): If the loop is already running, this method returns a coroutine that you should await on your own code. + + Example + .. code-block:: python + + # Blocks the current task here until a disconnection occurs. + # + # You will still receive updates, since this prevents the + # script from exiting. + client.run_until_disconnected() """ if self.loop.is_running(): return self._run_until_disconnected() @@ -54,19 +63,22 @@ class UpdateMethods(UserMethods): """ Decorator used to `add_event_handler` more conveniently. - >>> from telethon import TelegramClient, events - >>> client = TelegramClient(...) - >>> - >>> @client.on(events.NewMessage) - ... async def handler(event): - ... ... - ... - >>> - Args: + Arguments event (`_EventBuilder` | `type`): The event builder class or instance to be used, for instance ``events.NewMessage``. + + Example + .. code-block:: python + + from telethon import TelegramClient, events + client = TelegramClient(...) + + # Here we use client.on + @client.on(events.NewMessage) + async def handler(event): + ... """ def decorator(f): self.add_event_handler(f, event) @@ -83,7 +95,7 @@ class UpdateMethods(UserMethods): The callback will be called when the specified event occurs. - Args: + Arguments callback (`callable`): The callable function accepting one parameter to be used. @@ -98,6 +110,17 @@ class UpdateMethods(UserMethods): If left unspecified, `telethon.events.raw.Raw` (the :tl:`Update` objects with no further processing) will be passed instead. + + Example + .. code-block:: python + + from telethon import TelegramClient, events + client = TelegramClient(...) + + async def handler(event): + ... + + client.add_event_handler(handler, events.NewMessage) """ builders = events._get_handlers(callback) if builders is not None: @@ -121,6 +144,21 @@ class UpdateMethods(UserMethods): If no event is given, all events for this callback are removed. Returns how many callbacks were removed. + + Example + .. code-block:: python + + @client.on(events.Raw) + @client.on(events.NewMessage) + async def handler(event): + ... + + # Removes only the "Raw" handling + # "handler" will still receive "events.NewMessage" + client.remove_event_handler(handler, events.Raw) + + # "handler" will stop receiving anything + client.remove_event_handler(handler) """ found = 0 if event and not isinstance(event, type): @@ -141,7 +179,19 @@ class UpdateMethods(UserMethods): """ Lists all registered event handlers. - Returns a list of pairs consisting of ``(callback, event)``. + Returns + A list of pairs consisting of ``(callback, event)``. + + Example + .. code-block:: python + + @client.on(events.NewMessage(pattern='hello')) + async def on_greeting(event): + '''Greets someone''' + await event.reply('Hi') + + for callback, event in client.list_event_handlers(): + print(id(callback), type(event)) """ return [(callback, event) for event, callback in self._event_builders] @@ -152,6 +202,11 @@ class UpdateMethods(UserMethods): so that the updates it loads can by processed by your script. This can also be used to forcibly fetch new updates if there are any. + + Example + .. code-block:: python + + client.catch_up() """ pts, date = self._state_cache[None] if not pts: diff --git a/telethon/client/uploads.py b/telethon/client/uploads.py index 888cbfd6..5fbb6296 100644 --- a/telethon/client/uploads.py +++ b/telethon/client/uploads.py @@ -109,7 +109,17 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods): """ Sends message with the given file to the specified entity. - Args: + .. note:: + + If the ``hachoir3`` package (``hachoir`` module) is installed, + it will be used to determine metadata from audio and video files. + + If the ``pillow`` package is installed and you are sending a photo, + it will be resized to fit within the maximum dimensions allowed + by Telegram to avoid ``errors.PhotoInvalidDimensionsError``. This + cannot be done if you are sending :tl:`InputFile`, however. + + Arguments entity (`entity`): Who will receive the file. @@ -220,21 +230,11 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods): these to MP4 before sending if you want them to be streamable. Unsupported formats will result in ``VideoContentTypeError``. - Notes: - If the ``hachoir3`` package (``hachoir`` module) is installed, - it will be used to determine metadata from audio and video files. - - If the `pillow` package is installed and you are sending a photo, - it will be resized to fit within the maximum dimensions allowed - by Telegram to avoid ``errors.PhotoInvalidDimensionsError``. This - cannot be done if you are sending :tl:`InputFile`, however. - - Returns: + Returns The `telethon.tl.custom.message.Message` (or messages) containing the sent file, or messages if a list of them was passed. - Example: - + Example .. code-block:: python # Normal files like photos @@ -422,7 +422,7 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods): remotely in the Telegram servers, which can be later used on. This will **not** upload the file to your own chat or any chat at all. - Args: + Arguments file (`str` | `bytes` | `file`): The path of the file, byte array, or stream that will be sent. Note that if a byte array or a stream is given, a filename @@ -449,13 +449,12 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods): A callback function accepting two parameters: ``(sent bytes, total)``. - Returns: + Returns :tl:`InputFileBig` if the file size is larger than 10MB, `telethon.tl.custom.inputsizedfile.InputSizedFile` (subclass of :tl:`InputFile`) otherwise. - Example: - + Example .. code-block:: python # Photos as photo and document diff --git a/telethon/client/users.py b/telethon/client/users.py index 0600635f..99c24085 100644 --- a/telethon/client/users.py +++ b/telethon/client/users.py @@ -108,14 +108,19 @@ class UserMethods(TelegramBaseClient): If the user has not logged in yet, this method returns ``None``. - Args: + Arguments input_peer (`bool`, optional): Whether to return the :tl:`InputPeerUser` version or the normal :tl:`User`. This can be useful if you just need to know the ID of yourself. - Returns: + Returns Your own :tl:`User`. + + Example + .. code-block:: python + + print(client.get_me().username) """ if input_peer and self._self_input_peer: return self._self_input_peer @@ -137,6 +142,14 @@ class UserMethods(TelegramBaseClient): async def is_bot(self: 'TelegramClient') -> bool: """ Return ``True`` if the signed-in user is a bot, ``False`` otherwise. + + Example + .. code-block:: python + + if client.is_bot(): + print('Beep') + else: + print('Hello') """ if self._bot is None: self._bot = (await self.get_me()).bot @@ -146,6 +159,14 @@ class UserMethods(TelegramBaseClient): async def is_user_authorized(self: 'TelegramClient') -> bool: """ Returns ``True`` if the user is authorized (i.e. has logged in). + + Example + .. code-block:: python + + if not client.is_user_authorized(): + client.send_code_request(phone) + code = input('enter code: ') + client.sign_in(phone, code) """ if self._authorized is None: try: @@ -165,35 +186,35 @@ class UserMethods(TelegramBaseClient): or :tl:`Channel`. You can also pass a list or iterable of entities, and they will be efficiently fetched from the network. - entity (`str` | `int` | :tl:`Peer` | :tl:`InputPeer`): - If a username is given, **the username will be resolved** making - an API call every time. Resolving usernames is an expensive - operation and will start hitting flood waits around 50 usernames - in a short period of time. + Arguments + entity (`str` | `int` | :tl:`Peer` | :tl:`InputPeer`): + If a username is given, **the username will be resolved** making + an API call every time. Resolving usernames is an expensive + operation and will start hitting flood waits around 50 usernames + in a short period of time. - If you want to get the entity for a *cached* username, you should - first `get_input_entity(username) ` which will - use the cache), and then use `get_entity` with the result of the - previous call. + If you want to get the entity for a *cached* username, you should + first `get_input_entity(username) ` which will + use the cache), and then use `get_entity` with the result of the + previous call. - Similar limits apply to invite links, and you should use their - ID instead. + Similar limits apply to invite links, and you should use their + ID instead. - Using phone numbers (from people in your contact list), exact - names, integer IDs or :tl:`Peer` rely on a `get_input_entity` - first, which in turn needs the entity to be in cache, unless - a :tl:`InputPeer` was passed. + Using phone numbers (from people in your contact list), exact + names, integer IDs or :tl:`Peer` rely on a `get_input_entity` + first, which in turn needs the entity to be in cache, unless + a :tl:`InputPeer` was passed. - Unsupported types will raise ``TypeError``. + Unsupported types will raise ``TypeError``. - If the entity can't be found, ``ValueError`` will be raised. + If the entity can't be found, ``ValueError`` will be raised. - Returns: + Returns :tl:`User`, :tl:`Chat` or :tl:`Channel` corresponding to the input entity. A list will be returned if more than one was given. - Example: - + Example .. code-block:: python from telethon import utils @@ -284,57 +305,57 @@ class UserMethods(TelegramBaseClient): first, but if you're going to use an entity often, consider making the call: - >>> import asyncio - >>> rc = asyncio.get_event_loop().run_until_complete - >>> - >>> from telethon import TelegramClient - >>> client = TelegramClient(...) - >>> # If you're going to use "username" often in your code - >>> # (make a lot of calls), consider getting its input entity - >>> # once, and then using the "user" everywhere instead. - >>> user = rc(client.get_input_entity('username')) - >>> # The same applies to IDs, chats or channels. - >>> chat = rc(client.get_input_entity(-123456789)) + Arguments + entity (`str` | `int` | :tl:`Peer` | :tl:`InputPeer`): + If a username or invite link is given, **the library will + use the cache**. This means that it's possible to be using + a username that *changed* or an old invite link (this only + happens if an invite link for a small group chat is used + after it was upgraded to a mega-group). - entity (`str` | `int` | :tl:`Peer` | :tl:`InputPeer`): - If a username or invite link is given, **the library will - use the cache**. This means that it's possible to be using - a username that *changed* or an old invite link (this only - happens if an invite link for a small group chat is used - after it was upgraded to a mega-group). + If the username or ID from the invite link is not found in + the cache, it will be fetched. The same rules apply to phone + numbers (``'+34 123456789'``) from people in your contact list. - If the username or ID from the invite link is not found in - the cache, it will be fetched. The same rules apply to phone - numbers (``'+34 123456789'``) from people in your contact list. + If an exact name is given, it must be in the cache too. This + is not reliable as different people can share the same name + and which entity is returned is arbitrary, and should be used + only for quick tests. - If an exact name is given, it must be in the cache too. This - is not reliable as different people can share the same name - and which entity is returned is arbitrary, and should be used - only for quick tests. + If a positive integer ID is given, the entity will be searched + in cached users, chats or channels, without making any call. - If a positive integer ID is given, the entity will be searched - in cached users, chats or channels, without making any call. + If a negative integer ID is given, the entity will be searched + exactly as either a chat (prefixed with ``-``) or as a channel + (prefixed with ``-100``). - If a negative integer ID is given, the entity will be searched - exactly as either a chat (prefixed with ``-``) or as a channel - (prefixed with ``-100``). + If a :tl:`Peer` is given, it will be searched exactly in the + cache as either a user, chat or channel. - If a :tl:`Peer` is given, it will be searched exactly in the - cache as either a user, chat or channel. + If the given object can be turned into an input entity directly, + said operation will be done. - If the given object can be turned into an input entity directly, - said operation will be done. + Unsupported types will raise ``TypeError``. - Unsupported types will raise ``TypeError``. + If the entity can't be found, ``ValueError`` will be raised. - If the entity can't be found, ``ValueError`` will be raised. - - Returns: + Returns :tl:`InputPeerUser`, :tl:`InputPeerChat` or :tl:`InputPeerChannel` or :tl:`InputPeerSelf` if the parameter is ``'me'`` or ``'self'``. If you need to get the ID of yourself, you should use `get_me` with ``input_peer=True``) instead. + + Example + .. code-block:: python + + # If you're going to use "username" often in your code + # (make a lot of calls), consider getting its input entity + # once, and then using the "user" everywhere instead. + user = client.get_input_entity('username') + + # The same applies to IDs, chats or channels. + chat = client.get_input_entity(-123456789) """ # Short-circuit if the input parameter directly maps to an InputPeer try: @@ -411,6 +432,11 @@ class UserMethods(TelegramBaseClient): If ``add_mark is False``, then a positive ID will be returned instead. By default, bot-API style IDs (signed) are returned. + + Example + .. code-block:: python + + print(client.get_peer_id('me')) """ if isinstance(peer, int): return utils.get_peer_id(peer, add_mark=add_mark) diff --git a/telethon_generator/data/errors.csv b/telethon_generator/data/errors.csv index 7a43231b..9b2432fc 100644 --- a/telethon_generator/data/errors.csv +++ b/telethon_generator/data/errors.csv @@ -185,6 +185,7 @@ PHOTO_INVALID_DIMENSIONS,400,The photo dimensions are invalid (hint: `pip instal PHOTO_SAVE_FILE_INVALID,400,The photo you tried to send cannot be saved by Telegram. A reason may be that it exceeds 10MB. Try resizing it locally PHOTO_THUMB_URL_EMPTY,400,The URL used as a thumbnail appears to be empty or has caused another HTTP error PIN_RESTRICTED,400,You can't pin messages in private chats with other people +POLL_OPTION_DUPLICATE,400,A duplicate option was sent in the same poll POLL_UNSUPPORTED,400,This layer does not support polls in the issued method PRIVACY_KEY_INVALID,400,The privacy key is invalid PTS_CHANGE_EMPTY,500,No PTS change diff --git a/telethon_generator/data/methods.csv b/telethon_generator/data/methods.csv index b5ef6b48..f43967f9 100644 --- a/telethon_generator/data/methods.csv +++ b/telethon_generator/data/methods.csv @@ -234,7 +234,7 @@ messages.sendEncrypted,user,CHAT_ID_INVALID DATA_INVALID ENCRYPTION_DECLINED MSG messages.sendEncryptedFile,user,MSG_WAIT_FAILED messages.sendEncryptedService,user,DATA_INVALID ENCRYPTION_DECLINED MSG_WAIT_FAILED USER_IS_BLOCKED messages.sendInlineBotResult,user,CHAT_SEND_INLINE_FORBIDDEN CHAT_WRITE_FORBIDDEN INLINE_RESULT_EXPIRED PEER_ID_INVALID QUERY_ID_EMPTY WEBPAGE_CURL_FAILED WEBPAGE_MEDIA_EMPTY -messages.sendMedia,both,BOT_POLLS_DISABLED CHANNEL_INVALID CHANNEL_PRIVATE CHAT_ADMIN_REQUIRED CHAT_SEND_MEDIA_FORBIDDEN CHAT_WRITE_FORBIDDEN EXTERNAL_URL_INVALID FILE_PARTS_INVALID FILE_PART_LENGTH_INVALID INPUT_USER_DEACTIVATED MEDIA_CAPTION_TOO_LONG MEDIA_EMPTY PEER_ID_INVALID PHOTO_EXT_INVALID PHOTO_INVALID_DIMENSIONS PHOTO_SAVE_FILE_INVALID RANDOM_ID_DUPLICATE STORAGE_CHECK_FAILED Timeout USER_BANNED_IN_CHANNEL USER_IS_BLOCKED USER_IS_BOT VIDEO_CONTENT_TYPE_INVALID WEBPAGE_CURL_FAILED WEBPAGE_MEDIA_EMPTY +messages.sendMedia,both,BOT_POLLS_DISABLED CHANNEL_INVALID CHANNEL_PRIVATE CHAT_ADMIN_REQUIRED CHAT_SEND_MEDIA_FORBIDDEN CHAT_WRITE_FORBIDDEN EXTERNAL_URL_INVALID FILE_PARTS_INVALID FILE_PART_LENGTH_INVALID INPUT_USER_DEACTIVATED MEDIA_CAPTION_TOO_LONG MEDIA_EMPTY PEER_ID_INVALID PHOTO_EXT_INVALID PHOTO_INVALID_DIMENSIONS PHOTO_SAVE_FILE_INVALID POLL_OPTION_DUPLICATE RANDOM_ID_DUPLICATE STORAGE_CHECK_FAILED Timeout USER_BANNED_IN_CHANNEL USER_IS_BLOCKED USER_IS_BOT VIDEO_CONTENT_TYPE_INVALID WEBPAGE_CURL_FAILED WEBPAGE_MEDIA_EMPTY messages.sendMessage,both,AUTH_KEY_DUPLICATED BUTTON_DATA_INVALID BUTTON_TYPE_INVALID BUTTON_URL_INVALID CHANNEL_INVALID CHANNEL_PRIVATE CHAT_ADMIN_REQUIRED CHAT_ID_INVALID CHAT_RESTRICTED CHAT_WRITE_FORBIDDEN ENTITIES_TOO_LONG ENTITY_MENTION_USER_INVALID INPUT_USER_DEACTIVATED MESSAGE_EMPTY MESSAGE_TOO_LONG PEER_ID_INVALID RANDOM_ID_DUPLICATE REPLY_MARKUP_INVALID REPLY_MARKUP_TOO_LONG Timeout USER_BANNED_IN_CHANNEL USER_IS_BLOCKED USER_IS_BOT YOU_BLOCKED_USER messages.sendMultiMedia,both, messages.sendVote,user,