Update docstrings to have consistent style

This commit is contained in:
Lonami Exo 2019-05-20 11:38:26 +02:00
parent 4b74d16438
commit 7c1c040d50
15 changed files with 501 additions and 295 deletions

View File

@ -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
<telethon.sessions.abstract.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:

View File

@ -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

View File

@ -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
<telethon.tl.custom.inlineresult.InlineResult>`.
Example:
Example
.. code-block:: python
# Make an inline query to @like

View File

@ -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

View File

@ -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 <telethon.helpers.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:

View File

@ -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 <telethon.helpers.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 <telethon.tl.custom.conversation.Conversation>`.
Example:
Example
.. code-block:: python
# <you> denotes outgoing messages you sent

View File

@ -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 <telethon.tl.custom.message.Message>` | :tl:`Media`):
The media or message containing the media that will be downloaded.
See also `Message.download_media() <telethon.tl.custom.message.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 <telethon.tl.custom.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:

View File

@ -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

View File

@ -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 <telethon.tl.custom.message.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() <telethon.tl.custom.message.Message.respond>`
and `Message.reply() <telethon.tl.custom.message.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 <telethon.tl.custom.message.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() <telethon.tl.custom.message.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() <telethon.tl.custom.message.Message.edit>`.
Arguments
entity (`entity` | `Message <telethon.tl.custom.message.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() <telethon.tl.custom.message.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)

View File

@ -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"

View File

@ -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:

View File

@ -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

View File

@ -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) <get_input_entity>` 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) <get_input_entity>` 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)

View File

@ -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

1 name codes description
185 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
186 PHOTO_THUMB_URL_EMPTY 400 The URL used as a thumbnail appears to be empty or has caused another HTTP error
187 PIN_RESTRICTED 400 You can't pin messages in private chats with other people
188 POLL_OPTION_DUPLICATE 400 A duplicate option was sent in the same poll
189 POLL_UNSUPPORTED 400 This layer does not support polls in the issued method
190 PRIVACY_KEY_INVALID 400 The privacy key is invalid
191 PTS_CHANGE_EMPTY 500 No PTS change

View File

@ -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,

1 method usability errors
234 messages.sendEncryptedFile user MSG_WAIT_FAILED
235 messages.sendEncryptedService user DATA_INVALID ENCRYPTION_DECLINED MSG_WAIT_FAILED USER_IS_BLOCKED
236 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
237 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 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
238 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
239 messages.sendMultiMedia both
240 messages.sendVote user