Revisit documentation, cross-references and unnecessary indents

This commit is contained in:
Lonami Exo 2018-06-20 11:05:33 +02:00
parent 1b7e7320a4
commit c85ba4accc
20 changed files with 651 additions and 522 deletions

View File

@ -11,11 +11,11 @@ Accessing the Full API
reason not to, like a method not existing or you wanting more control. reason not to, like a method not existing or you wanting more control.
The `telethon.telegram_client.TelegramClient` doesn't offer a method for every The :ref:`TelegramClient <telethon-client>` doesn't offer a method for
single request the Telegram API supports. However, it's very simple to *call* every single request the Telegram API supports. However, it's very simple to
or *invoke* any request. Whenever you need something, don't forget to `check *call* or *invoke* any request. Whenever you need something, don't forget to
the documentation`__ and look for the `method you need`__. There you can go `check the documentation`__ and look for the `method you need`__. There you
through a sorted list of everything you can do. can go through a sorted list of everything you can do.
.. note:: .. note::
@ -30,7 +30,8 @@ You should also refer to the documentation to see what the objects
(constructors) Telegram returns look like. Every constructor inherits (constructors) Telegram returns look like. Every constructor inherits
from a common type, and that's the reason for this distinction. from a common type, and that's the reason for this distinction.
Say `telethon.telegram_client.TelegramClient.send_message` didn't exist, Say `client.send_message
<telethon.client.messages.MessageMethods.send_message>` didn't exist,
we could use the `search`__ to look for "message". There we would find we could use the `search`__ to look for "message". There we would find
:tl:`SendMessageRequest`, which we can work with. :tl:`SendMessageRequest`, which we can work with.
@ -39,16 +40,16 @@ to invoke it. You can also call ``help(request)`` for information on
what input parameters it takes. Remember to "Copy import to the what input parameters it takes. Remember to "Copy import to the
clipboard", or your script won't be aware of this class! Now we have: clipboard", or your script won't be aware of this class! Now we have:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.messages import SendMessageRequest from telethon.tl.functions.messages import SendMessageRequest
If you're going to use a lot of these, you may do: If you're going to use a lot of these, you may do:
.. code-block:: python .. code-block:: python
from telethon.tl import types, functions from telethon.tl import types, functions
# We now have access to 'functions.messages.SendMessageRequest' # We now have access to 'functions.messages.SendMessageRequest'
We see that this request must take at least two parameters, a ``peer`` We see that this request must take at least two parameters, a ``peer``
of type :tl:`InputPeer`, and a ``message`` which is just a Python of type :tl:`InputPeer`, and a ``message`` which is just a Python
@ -57,74 +58,79 @@ of type :tl:`InputPeer`, and a ``message`` which is just a Python
How can we retrieve this :tl:`InputPeer`? We have two options. We manually How can we retrieve this :tl:`InputPeer`? We have two options. We manually
construct one, for instance: construct one, for instance:
.. code-block:: python .. code-block:: python
from telethon.tl.types import InputPeerUser from telethon.tl.types import InputPeerUser
peer = InputPeerUser(user_id, user_hash) peer = InputPeerUser(user_id, user_hash)
Or we call `telethon.telegram_client.TelegramClient.get_input_entity()`: Or we call `client.get_input_entity
<telethon.client.users.UserMethods.get_input_entity>`:
.. code-block:: python .. code-block:: python
peer = client.get_input_entity('someone') peer = client.get_input_entity('someone')
When you're going to invoke an API method, most require you to pass an When you're going to invoke an API method, most require you to pass an
:tl:`InputUser`, :tl:`InputChat`, or so on, this is why using :tl:`InputUser`, :tl:`InputChat`, or so on, this is why using
``.get_input_entity()`` is more straightforward (and often `client.get_input_entity <telethon.client.users.UserMethods.get_input_entity>`
immediate, if you've seen the user before, know their ID, etc.). is more straightforward (and often immediate, if you've seen the user before,
If you also need to have information about the whole user, use know their ID, etc.). If you also **need** to have information about the whole
`telethon.telegram_client.TelegramClient.get_entity()` instead: user, use `client.get_entity <telethon.client.users.UserMethods.get_entity>`
instead:
.. code-block:: python .. code-block:: python
entity = client.get_entity('someone') entity = client.get_entity('someone')
In the later case, when you use the entity, the library will cast it to In the later case, when you use the entity, the library will cast it to
its "input" version for you. If you already have the complete user and its "input" version for you. If you already have the complete user and
want to cache its input version so the library doesn't have to do this want to cache its input version so the library doesn't have to do this
every time its used, simply call `telethon.utils.get_input_peer`: every time its used, simply call `telethon.utils.get_input_peer`:
.. code-block:: python .. code-block:: python
from telethon import utils from telethon import utils
peer = utils.get_input_user(entity) peer = utils.get_input_user(entity)
.. note:: .. note::
Since ``v0.16.2`` this is further simplified. The ``Request`` itself Since ``v0.16.2`` this is further simplified. The ``Request`` itself
will call ``client.get_input_entity()`` for you when required, but will call `client.get_input_entity <
it's good to remember what's happening. telethon.client.users.UserMethods.get_input_entity>` for you when required,
but it's good to remember what's happening.
After this small parenthesis about ``.get_entity`` versus After this small parenthesis about `client.get_entity
``.get_input_entity``, we have everything we need. To ``.invoke()`` our <telethon.client.users.UserMethods.get_entity>` versus
`client.get_input_entity <telethon.client.users.UserMethods.get_input_entity>`,
we have everything we need. To invoke our
request we do: request we do:
.. code-block:: python .. code-block:: python
result = client(SendMessageRequest(peer, 'Hello there!')) result = client(SendMessageRequest(peer, 'Hello there!'))
# __call__ is an alias for client.invoke(request). Both will work # __call__ is an alias for client.invoke(request). Both will work
Message sent! Of course, this is only an example. There are nearly 250 Message sent! Of course, this is only an example. There are nearly 250
methods available as of layer 73, and you can use every single of them methods available as of layer 73, and you can use every single of them
as you wish. Remember to use the right types! To sum up: as you wish. Remember to use the right types! To sum up:
.. code-block:: python .. code-block:: python
result = client(SendMessageRequest( result = client(SendMessageRequest(
client.get_input_entity('username'), 'Hello there!' client.get_input_entity('username'), 'Hello there!'
)) ))
This can further be simplified to: This can further be simplified to:
.. code-block:: python .. code-block:: python
result = client(SendMessageRequest('username', 'Hello there!')) result = client(SendMessageRequest('username', 'Hello there!'))
# Or even # Or even
result = client(SendMessageRequest(PeerChannel(id), 'Hello there!')) result = client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
.. note:: .. note::

View File

@ -4,7 +4,8 @@
Session Files Session Files
============== ==============
The first parameter you pass to the constructor of the ``TelegramClient`` is The first parameter you pass to the constructor of the
:ref:`TelegramClient <telethon-client>` is
the ``session``, and defaults to be the session name (or full path). That is, the ``session``, and defaults to be the session name (or full path). That is,
if you create a ``TelegramClient('anon')`` instance and connect, an if you create a ``TelegramClient('anon')`` instance and connect, an
``anon.session`` file will be created in the working directory. ``anon.session`` file will be created in the working directory.
@ -42,7 +43,8 @@ If you don't want to use the default SQLite session storage, you can also use
one of the other implementations or implement your own storage. one of the other implementations or implement your own storage.
To use a custom session storage, simply pass the custom session instance to To use a custom session storage, simply pass the custom session instance to
``TelegramClient`` instead of the session name. :ref:`TelegramClient <telethon-client>` instead of
the session name.
Telethon contains two implementations of the abstract ``Session`` class: Telethon contains two implementations of the abstract ``Session`` class:

View File

@ -24,15 +24,15 @@ Once that's ready, the next step is to create a ``TelegramClient``.
This class will be your main interface with Telegram's API, and creating This class will be your main interface with Telegram's API, and creating
one is very simple: one is very simple:
.. code-block:: python .. code-block:: python
from telethon import TelegramClient from telethon import TelegramClient
# Use your own values here # Use your own values here
api_id = 12345 api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef' api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('some_name', api_id, api_hash) client = TelegramClient('some_name', api_id, api_hash)
Note that ``'some_name'`` will be used to save your session (persistent Note that ``'some_name'`` will be used to save your session (persistent
@ -46,26 +46,44 @@ your disk. This is by default a database file using Python's ``sqlite3``.
creates the file in your working directory, but absolute paths work too. creates the file in your working directory, but absolute paths work too.
.. important::
The process shown here shows how to sign in *manually*. You **should**
use `client.start() <telethon.client.auth.AuthMethods.start>` instead
unless you have a better reason not to (e.g. you need more control):
.. code-block:: python
client.start()
This is explained after going through the manual process.
Before using the client, you must be connected to Telegram. Before using the client, you must be connected to Telegram.
Doing so is very easy: Doing so is very easy:
``client.connect() # Must return True, otherwise, try again`` .. code-block:: python
client.connect() # Must return True, otherwise, try again
You may or may not be authorized yet. You must be authorized You may or may not be authorized yet. You must be authorized
before you're able to send any request: before you're able to send any request:
``client.is_user_authorized() # Returns True if you can send requests`` .. code-block:: python
If you're not authorized, you need to ``.sign_in()``: client.is_user_authorized() # Returns True if you can send requests
.. code-block:: python If you're not authorized, you need to `.sign_in
<telethon.client.auth.AuthMethods.sign_in>`:
phone_number = '+34600000000' .. code-block:: python
client.send_code_request(phone_number)
myself = client.sign_in(phone_number, input('Enter code: ')) phone_number = '+34600000000'
# If .sign_in raises PhoneNumberUnoccupiedError, use .sign_up instead client.send_code_request(phone_number)
# If .sign_in raises SessionPasswordNeeded error, call .sign_in(password=...) myself = client.sign_in(phone_number, input('Enter code: '))
# You can import both exceptions from telethon.errors. # If .sign_in raises PhoneNumberUnoccupiedError, use .sign_up instead
# If .sign_in raises SessionPasswordNeeded error, call .sign_in(password=...)
# You can import both exceptions from telethon.errors.
.. note:: .. note::
@ -82,24 +100,26 @@ mentioned ``.stringify()`` method, and printing these might prove useful.
As a full example: As a full example:
.. code-block:: python .. code-block:: python
client = TelegramClient('anon', api_id, api_hash) client = TelegramClient('anon', api_id, api_hash)
assert client.connect() assert client.connect()
if not client.is_user_authorized(): if not client.is_user_authorized():
client.send_code_request(phone_number) client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: ')) me = client.sign_in(phone_number, input('Enter code: '))
All of this, however, can be done through a call to ``.start()``: All of this, however, can be done through a call to `.start()
<telethon.client.auth.AuthMethods.start>`:
.. code-block:: python .. code-block:: python
client = TelegramClient('anon', api_id, api_hash) client = TelegramClient('anon', api_id, api_hash)
client.start() client.start()
The code shown is just what ``.start()`` will be doing behind the scenes The code shown is just what `.start()
<telethon.client.auth.AuthMethods.start>` will be doing behind the scenes
(with a few extra checks), so that you know how to sign in case you want (with a few extra checks), so that you know how to sign in case you want
to avoid using ``input()`` (the default) for whatever reason. If no phone to avoid using ``input()`` (the default) for whatever reason. If no phone
or bot token is provided, you will be asked one through ``input()``. The or bot token is provided, you will be asked one through ``input()``. The
@ -108,25 +128,27 @@ method also accepts a ``phone=`` and ``bot_token`` parameters.
You can use either, as both will work. Determining which You can use either, as both will work. Determining which
is just a matter of taste, and how much control you need. is just a matter of taste, and how much control you need.
Remember that you can get yourself at any time with ``client.get_me()``. Remember that you can get yourself at any time with `client.get_me()
<telethon.client.users.UserMethods.get_me>`.
.. warning:: .. warning::
Please note that if you fail to login around 5 times (or change the first Please note that if you fail to login around 5 times (or change the first
parameter of the ``TelegramClient``, which is the session name) you will parameter of the :ref:`TelegramClient <telethon-client>`, which is the session
receive a ``FloodWaitError`` of around 22 hours, so be careful not to mess name) you will receive a ``FloodWaitError`` of around 22 hours, so be
this up! This shouldn't happen if you're doing things as explained, though. careful not to mess this up! This shouldn't happen if you're doing things
as explained, though.
.. note:: .. note::
If you want to use a **proxy**, you have to `install PySocks`__ If you want to use a **proxy**, you have to `install PySocks`__
(via pip or manual) and then set the appropriated parameters: (via pip or manual) and then set the appropriated parameters:
.. code-block:: python .. code-block:: python
import socks import socks
client = TelegramClient('session_id', client = TelegramClient('session_id',
api_id=12345, api_hash='0123456789abcdef0123456789abcdef', api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
proxy=(socks.SOCKS5, 'localhost', 4444) proxy=(socks.SOCKS5, 'localhost', 4444)
) )
The ``proxy=`` argument should be a tuple, a list or a dict, The ``proxy=`` argument should be a tuple, a list or a dict,
consisting of parameters described `here`__. consisting of parameters described `here`__.
@ -137,64 +159,69 @@ Two Factor Authorization (2FA)
****************************** ******************************
If you have Two Factor Authorization (from now on, 2FA) enabled on your If you have Two Factor Authorization (from now on, 2FA) enabled on your
account, calling :meth:`telethon.TelegramClient.sign_in` will raise a account, calling `.sign_in()
``SessionPasswordNeededError``. When this happens, just <telethon.client.auth.AuthMethods.sign_in>` will raise a
:meth:`telethon.TelegramClient.sign_in` again with a ``password=``: ``SessionPasswordNeededError``. When this happens, just use the method
again with a ``password=``:
.. code-block:: python .. code-block:: python
import getpass import getpass
from telethon.errors import SessionPasswordNeededError from telethon.errors import SessionPasswordNeededError
client.sign_in(phone) client.sign_in(phone)
try: try:
client.sign_in(code=input('Enter code: ')) client.sign_in(code=input('Enter code: '))
except SessionPasswordNeededError: except SessionPasswordNeededError:
client.sign_in(password=getpass.getpass()) client.sign_in(password=getpass.getpass())
The mentioned ``.start()`` method will handle this for you as well, but The mentioned `.start()
you must set the ``password=`` parameter beforehand (it won't be asked). <telethon.client.auth.AuthMethods.start>` method will handle this for you as
well, but you must set the ``password=`` parameter beforehand (it won't be
asked).
If you don't have 2FA enabled, but you would like to do so through the
library, use `client.edit_2fa()
<telethon.client.auth.AuthMethods.edit_2fa>`.
If you don't have 2FA enabled, but you would like to do so through the library,
use ``client.edit_2fa()``.
Be sure to know what you're doing when using this function and Be sure to know what you're doing when using this function and
you won't run into any problems. you won't run into any problems. Take note that if you want to
Take note that if you want to set only the email/hint and leave set only the email/hint and leave the current password unchanged,
the current password unchanged, you need to "redo" the 2fa. you need to "redo" the 2fa.
See the examples below: See the examples below:
.. code-block:: python .. code-block:: python
from telethon.errors import EmailUnconfirmedError from telethon.errors import EmailUnconfirmedError
# Sets 2FA password for first time: # Sets 2FA password for first time:
client.edit_2fa(new_password='supersecurepassword') client.edit_2fa(new_password='supersecurepassword')
# Changes password: # Changes password:
client.edit_2fa(current_password='supersecurepassword', client.edit_2fa(current_password='supersecurepassword',
new_password='changedmymind') new_password='changedmymind')
# Clears current password (i.e. removes 2FA): # Clears current password (i.e. removes 2FA):
client.edit_2fa(current_password='changedmymind', new_password=None) client.edit_2fa(current_password='changedmymind', new_password=None)
# Sets new password with recovery email: # Sets new password with recovery email:
try: try:
client.edit_2fa(new_password='memes and dreams', client.edit_2fa(new_password='memes and dreams',
email='JohnSmith@example.com') email='JohnSmith@example.com')
# Raises error (you need to check your email to complete 2FA setup.) # Raises error (you need to check your email to complete 2FA setup.)
except EmailUnconfirmedError: except EmailUnconfirmedError:
# You can put email checking code here if desired. # You can put email checking code here if desired.
pass pass
# Also take note that unless you remove 2FA or explicitly # Also take note that unless you remove 2FA or explicitly
# give email parameter again it will keep the last used setting # give email parameter again it will keep the last used setting
# Set hint after already setting password: # Set hint after already setting password:
client.edit_2fa(current_password='memes and dreams', client.edit_2fa(current_password='memes and dreams',
new_password='memes and dreams', new_password='memes and dreams',
hint='It keeps you alive') hint='It keeps you alive')
__ https://github.com/Anorov/PySocks#installation __ https://github.com/Anorov/PySocks#installation
__ https://github.com/Anorov/PySocks#usage-1 __ https://github.com/Anorov/PySocks#usage-1

View File

@ -22,9 +22,9 @@ in response to certain methods, such as :tl:`GetUsersRequest`.
To "encounter" an ID, you would have to "find it" like you would in the To "encounter" an ID, you would have to "find it" like you would in the
normal app. If the peer is in your dialogs, you would need to normal app. If the peer is in your dialogs, you would need to
`client.get_dialogs() <telethon.telegram_client.TelegramClient.get_dialogs>`. `client.get_dialogs() <telethon.client.dialogs.DialogMethods.get_dialogs>`.
If the peer is someone in a group, you would similarly If the peer is someone in a group, you would similarly
`client.get_participants(group) <telethon.telegram_client.TelegramClient.get_participants>`. `client.get_participants(group) <telethon.client.chats.ChatMethods.get_participants>`.
Once you have encountered an ID, the library will (by default) have saved Once you have encountered an ID, the library will (by default) have saved
their ``access_hash`` for you, which is needed to invoke most methods. their ``access_hash`` for you, which is needed to invoke most methods.
@ -69,9 +69,11 @@ you're able to just do this:
my_channel = client.get_entity(PeerChannel(some_id)) my_channel = client.get_entity(PeerChannel(some_id))
All methods in the :ref:`telegram-client` call ``.get_input_entity()`` prior All methods in the :ref:`telegram-client` call `.get_input_entity()
<telethon.client.users.UserMethods.get_input_entity>` prior
to sending the requst to save you from the hassle of doing so manually. to sending the requst to save you from the hassle of doing so manually.
That way, convenience calls such as ``client.send_message('lonami', 'hi!')`` That way, convenience calls such as `client.send_message('lonami', 'hi!')
<telethon.client.messages.MessageMethods.send_message>`
become possible. become possible.
Every entity the library encounters (in any response to any call) will by Every entity the library encounters (in any response to any call) will by
@ -88,8 +90,10 @@ Entities vs. Input Entities
Don't worry if you don't understand this section, just remember some Don't worry if you don't understand this section, just remember some
of the details listed here are important. When you're calling a method, of the details listed here are important. When you're calling a method,
don't call ``.get_entity()`` beforehand, just use the username or phone, don't call `client.get_entity() <telethon.client.users.UserMethods.get_entity>`
or the entity retrieved by other means like ``.get_dialogs()``. beforehand, just use the username or phone, or the entity retrieved by
other means like `client.get_dialogs()
<telethon.client.dialogs.DialogMethods.get_dialogs>`.
On top of the normal types, the API also make use of what they call their On top of the normal types, the API also make use of what they call their
@ -108,21 +112,27 @@ before you can "use them".
As we just mentioned, API calls don't need to know the whole information As we just mentioned, API calls don't need to know the whole information
about the entities, only their ID and hash. For this reason, another method, about the entities, only their ID and hash. For this reason, another method,
``.get_input_entity()`` is available. This will always use the cache while `client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
possible, making zero API calls most of the time. When a request is made, is available. This will always use the cache while possible, making zero API
if you provided the full entity, e.g. an :tl:`User`, the library will convert calls most of the time. When a request is made, if you provided the full
it to the required :tl:`InputPeer` automatically for you. entity, e.g. an :tl:`User`, the library will convert it to the required
:tl:`InputPeer` automatically for you.
**You should always favour** ``.get_input_entity()`` **over** ``.get_entity()`` **You should always favour**
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
**over**
`client.get_entity() <telethon.client.users.UserMethods.get_entity>`
for this reason! Calling the latter will always make an API call to get for this reason! Calling the latter will always make an API call to get
the most recent information about said entity, but invoking requests don't the most recent information about said entity, but invoking requests don't
need this information, just the ``InputPeer``. Only use ``.get_entity()`` need this information, just the :tl:`InputPeer`. Only use
`client.get_entity() <telethon.client.users.UserMethods.get_entity>`
if you need to get actual information, like the username, name, title, etc. if you need to get actual information, like the username, name, title, etc.
of the entity. of the entity.
To further simplify the workflow, since the version ``0.16.2`` of the To further simplify the workflow, since the version ``0.16.2`` of the
library, the raw requests you make to the API are also able to call library, the raw requests you make to the API are also able to call
``.get_input_entity`` wherever needed, so you can even do things like: `client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
wherever needed, so you can even do things like:
.. code-block:: python .. code-block:: python

View File

@ -9,83 +9,85 @@ Getting Started
Simple Installation Simple Installation
******************* *******************
``pip3 install telethon`` .. code-block:: sh
**More details**: :ref:`installation` pip3 install telethon
**More details**: :ref:`installation`
Creating a client Creating a client
***************** *****************
.. code-block:: python .. code-block:: python
from telethon import TelegramClient from telethon import TelegramClient
# These example values won't work. You must get your own api_id and # These example values won't work. You must get your own api_id and
# api_hash from https://my.telegram.org, under API Development. # api_hash from https://my.telegram.org, under API Development.
api_id = 12345 api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef' api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('session_name', api_id, api_hash) client = TelegramClient('session_name', api_id, api_hash)
client.start() client.start()
**More details**: :ref:`creating-a-client` **More details**: :ref:`creating-a-client`
Basic Usage Basic Usage
*********** ***********
.. code-block:: python .. code-block:: python
# Getting information about yourself # Getting information about yourself
print(client.get_me().stringify()) print(client.get_me().stringify())
# Sending a message (you can use 'me' or 'self' to message yourself) # Sending a message (you can use 'me' or 'self' to message yourself)
client.send_message('username', 'Hello World from Telethon!') client.send_message('username', 'Hello World from Telethon!')
# Sending a file # Sending a file
client.send_file('username', '/home/myself/Pictures/holidays.jpg') client.send_file('username', '/home/myself/Pictures/holidays.jpg')
# Retrieving messages from a chat # Retrieving messages from a chat
from telethon import utils from telethon import utils
for message in client.iter_messages('username', limit=10): for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message) print(utils.get_display_name(message.sender), message.message)
# Listing all the dialogs (conversations you have open) # Listing all the dialogs (conversations you have open)
for dialog in client.get_dialogs(limit=10): for dialog in client.get_dialogs(limit=10):
print(utils.get_display_name(dialog.entity), dialog.draft.text) print(utils.get_display_name(dialog.entity), dialog.draft.text)
# Downloading profile photos (default path is the working directory) # Downloading profile photos (default path is the working directory)
client.download_profile_photo('username') client.download_profile_photo('username')
# Once you have a message with .media (if message.media) # Once you have a message with .media (if message.media)
# you can download it using client.download_media(): # you can download it using client.download_media():
messages = client.get_messages('username') messages = client.get_messages('username')
client.download_media(messages[0]) client.download_media(messages[0])
**More details**: :ref:`telegram-client` **More details**: :ref:`telegram-client`
See :ref:`telethon-client` for all available friendly methods. See :ref:`telethon-client` for all available friendly methods.
Handling Updates Handling Updates
**************** ****************
.. code-block:: python .. code-block:: python
from telethon import events from telethon import events
# We need to have some worker running # We need to have some worker running
client.updates.workers = 1 client.updates.workers = 1
@client.on(events.NewMessage(incoming=True, pattern='(?i)hi')) @client.on(events.NewMessage(incoming=True, pattern='(?i)hi'))
def handler(event): def handler(event):
event.reply('Hello!') event.reply('Hello!')
# If you want to handle updates you can't let the script end. # If you want to handle updates you can't let the script end.
input('Press enter to exit.') input('Press enter to exit.')
**More details**: :ref:`working-with-updates` **More details**: :ref:`working-with-updates`
---------- ----------

View File

@ -10,24 +10,28 @@ Automatic Installation
To install Telethon, simply do: To install Telethon, simply do:
``pip3 install telethon`` .. code-block:: sh
pip3 install telethon
Needless to say, you must have Python 3 and PyPi installed in your system. Needless to say, you must have Python 3 and PyPi installed in your system.
See https://python.org and https://pypi.python.org/pypi/pip for more. See https://python.org and https://pypi.python.org/pypi/pip for more.
If you already have the library installed, upgrade with: If you already have the library installed, upgrade with:
``pip3 install --upgrade telethon`` .. code-block:: sh
pip3 install --upgrade telethon
You can also install the library directly from GitHub or a fork: You can also install the library directly from GitHub or a fork:
.. code-block:: sh .. code-block:: sh
# pip3 install git+https://github.com/LonamiWebs/Telethon.git # pip3 install git+https://github.com/LonamiWebs/Telethon.git
or or
$ git clone https://github.com/LonamiWebs/Telethon.git $ git clone https://github.com/LonamiWebs/Telethon.git
$ cd Telethon/ $ cd Telethon/
# pip install -Ue . # pip install -Ue .
If you don't have root access, simply pass the ``--user`` flag to the pip If you don't have root access, simply pass the ``--user`` flag to the pip
command. If you want to install a specific branch, append ``@branch`` to command. If you want to install a specific branch, append ``@branch`` to
@ -38,7 +42,9 @@ which can be really slow when uploading or downloading files. If you don't
mind using a C extension, install `cryptg <https://github.com/Lonami/cryptg>`__ mind using a C extension, install `cryptg <https://github.com/Lonami/cryptg>`__
via ``pip`` or as an extra: via ``pip`` or as an extra:
``pip3 install telethon[cryptg]`` .. code-block:: sh
pip3 install telethon[cryptg]
Manual Installation Manual Installation
@ -47,14 +53,27 @@ Manual Installation
1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and 1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and
``rsa`` (`GitHub`__ | `PyPi`__) modules: ``rsa`` (`GitHub`__ | `PyPi`__) modules:
``sudo -H pip3 install pyaes rsa`` .. code-block:: sh
pip3 install pyaes rsa
2. Clone Telethon's GitHub repository: 2. Clone Telethon's GitHub repository:
``git clone https://github.com/LonamiWebs/Telethon.git``
3. Enter the cloned repository: ``cd Telethon`` .. code-block:: sh
4. Run the code generator: ``python3 setup.py gen tl errors`` git clone https://github.com/LonamiWebs/Telethon.git
3. Enter the cloned repository:
.. code-block:: sh
cd Telethon
4. Run the code generator:
.. code-block:: sh
python3 setup.py gen
5. Done! 5. Done!

View File

@ -15,9 +15,9 @@ Introduction
available methods are in the :ref:`telethon-client` reference, including available methods are in the :ref:`telethon-client` reference, including
detailed descriptions to what they do. detailed descriptions to what they do.
The ``TelegramClient`` is the central class of the library, the one The :ref:`TelegramClient <telethon-client>` is the
you will be using most of the time. For this reason, it's important central class of the library, the one you will be using most of the time. For
to know what it offers. this reason, it's important to know what it offers.
Since we're working with Python, one must not forget that we can do Since we're working with Python, one must not forget that we can do
``help(client)`` or ``help(TelegramClient)`` at any time for a more ``help(client)`` or ``help(TelegramClient)`` at any time for a more
@ -27,12 +27,14 @@ methods for any object, even yours!
Interacting with the Telegram API is done through sending **requests**, Interacting with the Telegram API is done through sending **requests**,
this is, any "method" listed on the API. There are a few methods (and this is, any "method" listed on the API. There are a few methods (and
growing!) on the ``TelegramClient`` class that abstract you from the growing!) on the :ref:`TelegramClient <telethon-client>` class that abstract
need of manually importing the requests you need. you from the need of manually importing the requests you need.
For instance, retrieving your own user can be done in a single line: For instance, retrieving your own user can be done in a single line:
``myself = client.get_me()`` .. code-block:: python
myself = client.get_me()
Internally, this method has sent a request to Telegram, who replied with Internally, this method has sent a request to Telegram, who replied with
the information about your own user, and then the desired information the information about your own user, and then the desired information
@ -42,12 +44,12 @@ If you want to retrieve any other user, chat or channel (channels are a
special subset of chats), you want to retrieve their "entity". This is special subset of chats), you want to retrieve their "entity". This is
how the library refers to either of these: how the library refers to either of these:
.. code-block:: python .. code-block:: python
# The method will infer that you've passed an username # The method will infer that you've passed an username
# It also accepts phone numbers, and will get the user # It also accepts phone numbers, and will get the user
# from your contact list. # from your contact list.
lonami = client.get_entity('lonami') lonami = client.get_entity('lonami')
The so called "entities" are another important whole concept on its own, The so called "entities" are another important whole concept on its own,
but for now you don't need to worry about it. Simply know that they are but for now you don't need to worry about it. Simply know that they are
@ -55,32 +57,32 @@ a good way to get information about an user, chat or channel.
Many other common methods for quick scripts are also available: Many other common methods for quick scripts are also available:
.. code-block:: python .. code-block:: python
# Note that you can use 'me' or 'self' to message yourself # Note that you can use 'me' or 'self' to message yourself
client.send_message('username', 'Hello World from Telethon!') client.send_message('username', 'Hello World from Telethon!')
# .send_message's parse mode defaults to markdown, so you # .send_message's parse mode defaults to markdown, so you
# can use **bold**, __italics__, [links](https://example.com), `code`, # can use **bold**, __italics__, [links](https://example.com), `code`,
# and even [mentions](@username)/[mentions](tg://user?id=123456789) # and even [mentions](@username)/[mentions](tg://user?id=123456789)
client.send_message('username', '**Using** __markdown__ `too`!') client.send_message('username', '**Using** __markdown__ `too`!')
client.send_file('username', '/home/myself/Pictures/holidays.jpg') client.send_file('username', '/home/myself/Pictures/holidays.jpg')
# The utils package has some goodies, like .get_display_name() # The utils package has some goodies, like .get_display_name()
from telethon import utils from telethon import utils
for message in client.iter_messages('username', limit=10): for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message) print(utils.get_display_name(message.sender), message.message)
# Dialogs are the conversations you have open # Dialogs are the conversations you have open
for dialog in client.get_dialogs(limit=10): for dialog in client.get_dialogs(limit=10):
print(utils.get_display_name(dialog.entity), dialog.draft.text) print(utils.get_display_name(dialog.entity), dialog.draft.text)
# Default path is the working directory # Default path is the working directory
client.download_profile_photo('username') client.download_profile_photo('username')
# Call .disconnect() when you're done # Call .disconnect() when you're done
client.disconnect() client.disconnect()
Remember that you can call ``.stringify()`` to any object Telegram returns Remember that you can call ``.stringify()`` to any object Telegram returns
to pretty print it. Calling ``str(result)`` does the same operation, but on to pretty print it. Calling ``str(result)`` does the same operation, but on
@ -91,9 +93,9 @@ Available methods
***************** *****************
The :ref:`reference <telethon-package>` lists all the "handy" methods The :ref:`reference <telethon-package>` lists all the "handy" methods
available for you to use in the ``TelegramClient`` class. These are simply available for you to use in the :ref:`TelegramClient <telethon-client>` class.
wrappers around the "raw" Telegram API, making it much more manageable and These are simply wrappers around the "raw" Telegram API, making it much more
easier to work with. manageable and easier to work with.
Please refer to :ref:`accessing-the-full-api` if these aren't enough, Please refer to :ref:`accessing-the-full-api` if these aren't enough,
and don't be afraid to read the source code of the InteractiveTelegramClient_ and don't be afraid to read the source code of the InteractiveTelegramClient_

View File

@ -5,21 +5,27 @@ Working with Updates
==================== ====================
The library comes with the :mod:`events` module. *Events* are an abstraction The library comes with the `telethon.events` module. *Events* are an abstraction
over what Telegram calls `updates`__, and are meant to ease simple and common over what Telegram calls `updates`__, and are meant to ease simple and common
usage when dealing with them, since there are many updates. If you're looking usage when dealing with them, since there are many updates. If you're looking
for the method reference, check :ref:`telethon-events-package`, otherwise, for the method reference, check :ref:`telethon-events-package`, otherwise,
let's dive in! let's dive in!
.. note:: .. important::
The library logs by default no output, and any exception that occurs The library logs by default no output, and any exception that occurs
inside your handlers will be "hidden" from you to prevent the thread inside your handlers will be "hidden" from you to prevent the thread
from terminating (so it can still deliver events). You should enable from terminating (so it can still deliver events). You should enable
logging (``import logging; logging.basicConfig(level=logging.ERROR)``) logging when working with events, at least the error level, to see if
when working with events, at least the error level, to see if this is this is happening so you can debug the error.
happening so you can debug the error.
**When using updates, please enable logging:**
.. code-block:: python
import logging
logging.basicConfig(level=logging.ERROR)
.. contents:: .. contents::
@ -62,7 +68,8 @@ Nothing we don't know already.
This Python decorator will attach itself to the ``my_event_handler`` This Python decorator will attach itself to the ``my_event_handler``
definition, and basically means that *on* a ``NewMessage`` *event*, definition, and basically means that *on* a `NewMessage
<telethon.events.newmessage.NewMessage>` *event*,
the callback function you're about to define will be called: the callback function you're about to define will be called:
.. code-block:: python .. code-block:: python
@ -72,8 +79,10 @@ the callback function you're about to define will be called:
event.reply('hi!') event.reply('hi!')
If a ``NewMessage`` event occurs, and ``'hello'`` is in the text of the If a `NewMessage
message, we ``reply`` to the event with a ``'hi!'`` message. <telethon.events.newmessage.NewMessage>` event occurs,
and ``'hello'`` is in the text of the message, we ``reply`` to the event
with a ``'hi!'`` message.
.. code-block:: python .. code-block:: python
@ -88,10 +97,11 @@ do other things instead idling. For this refer to :ref:`update-modes`.
More on events More on events
************** **************
The ``NewMessage`` event has much more than what was shown. You can access The `NewMessage <telethon.events.newmessage.NewMessage>` event has much
the ``.sender`` of the message through that member, or even see if the message more than what was shown. You can access the ``.sender`` of the message
had ``.media``, a ``.photo`` or a ``.document`` (which you could download with through that member, or even see if the message had ``.media``, a ``.photo``
for example ``client.download_media(event.photo)``. or a ``.document`` (which you could download with for example
`client.download_media(event.photo) <telethon.client.downloads.DownloadMethods.download_media>`.
If you don't want to ``.reply`` as a reply, you can use the ``.respond()`` If you don't want to ``.reply`` as a reply, you can use the ``.respond()``
method instead. Of course, there are more events such as ``ChatAction`` or method instead. Of course, there are more events such as ``ChatAction`` or
@ -102,34 +112,35 @@ instance, ``NewMessage.Event``), except for the ``Raw`` event which just
passes the ``Update`` object. passes the ``Update`` object.
Note that ``.reply()`` and ``.respond()`` are just wrappers around the Note that ``.reply()`` and ``.respond()`` are just wrappers around the
``client.send_message()`` method which supports the ``file=`` parameter. `client.send_message() <telethon.client.messages.MessageMethods.send_message>`
This means you can reply with a photo if you do ``client.reply(file=photo)``. method which supports the ``file=`` parameter.
This means you can reply with a photo if you do ``event.reply(file=photo)``.
You can put the same event on many handlers, and even different events on You can put the same event on many handlers, and even different events on
the same handler. You can also have a handler work on only specific chats, the same handler. You can also have a handler work on only specific chats,
for example: for example:
.. code-block:: python .. code-block:: python
import ast import ast
import random import random
# Either a single item or a list of them will work for the chats. # Either a single item or a list of them will work for the chats.
# You can also use the IDs, Peers, or even User/Chat/Channel objects. # You can also use the IDs, Peers, or even User/Chat/Channel objects.
@client.on(events.NewMessage(chats=('TelethonChat', 'TelethonOffTopic'))) @client.on(events.NewMessage(chats=('TelethonChat', 'TelethonOffTopic')))
def normal_handler(event): def normal_handler(event):
if 'roll' in event.raw_text: if 'roll' in event.raw_text:
event.reply(str(random.randint(1, 6))) event.reply(str(random.randint(1, 6)))
# Similarly, you can use incoming=True for messages that you receive # Similarly, you can use incoming=True for messages that you receive
@client.on(events.NewMessage(chats='TelethonOffTopic', outgoing=True)) @client.on(events.NewMessage(chats='TelethonOffTopic', outgoing=True))
def admin_handler(event): def admin_handler(event):
if event.raw_text.startswith('eval'): if event.raw_text.startswith('eval'):
expression = event.raw_text.replace('eval', '').strip() expression = event.raw_text.replace('eval', '').strip()
event.reply(str(ast.literal_eval(expression))) event.reply(str(ast.literal_eval(expression)))
You can pass one or more chats to the ``chats`` parameter (as a list or tuple), You can pass one or more chats to the ``chats`` parameter (as a list or tuple),
@ -143,15 +154,20 @@ solution. Try it!
Events without decorators Events without decorators
************************* *************************
If for any reason you can't use the ``@client.on`` syntax, don't worry. If for any reason you can't use the `@client.on
You can call ``client.add_event_handler(callback, event)`` to achieve <telethon.client.updates.UpdateMethods.on>` syntax, don't worry.
You can call `client.add_event_handler(callback, event)
<telethon.client.updates.UpdateMethods.add_event_handler>` to achieve
the same effect. the same effect.
Similar to that method, you also have :meth:`client.remove_event_handler` Similarly, you also have `client.remove_event_handler
and :meth:`client.list_event_handlers` which do as they names indicate. <telethon.client.updates.UpdateMethods.remove_event_handler>`
and `client.list_event_handlers
<telethon.client.updates.UpdateMethods.list_event_handlers>`.
The ``event`` type is optional in all methods and defaults to ``events.Raw`` The ``event`` type is optional in all methods and defaults to
for adding, and ``None`` when removing (so all callbacks would be removed). `events.Raw <telethon.events.raw.Raw>` for adding, and ``None`` when
removing (so all callbacks would be removed).
Stopping propagation of Updates Stopping propagation of Updates
@ -159,8 +175,8 @@ Stopping propagation of Updates
There might be cases when an event handler is supposed to be used solitary and There might be cases when an event handler is supposed to be used solitary and
it makes no sense to process any other handlers in the chain. For this case, it makes no sense to process any other handlers in the chain. For this case,
it is possible to raise a ``StopPropagation`` exception which will cause the it is possible to raise a `telethon.events.StopPropagation` exception which
propagation of the update through your handlers to stop: will cause the propagation of the update through your handlers to stop:
.. code-block:: python .. code-block:: python

View File

@ -12,27 +12,32 @@ that servers as a nice interface with the most commonly used methods on
Telegram such as sending messages, retrieving the message history, Telegram such as sending messages, retrieving the message history,
handling updates, etc. handling updates, etc.
The ``TelegramClient`` inherits the ``TelegramBareClient``. The later is The ``TelegramClient`` inherits from several mixing ``Method`` classes,
basically a pruned version of the ``TelegramClient``, which knows basic since there are so many methods that having them in a single file would
stuff like ``.invoke()``\ 'ing requests, downloading files, or switching make maintenance painful (it was three thousand lines before this separation
between data centers. This is primary to keep the method count per class happened!). It's a "god object", but there is only a way to interact with
and file low and manageable. Telegram really.
Both clients make use of the ``network/mtproto_sender.py``. The The ``TelegramBaseClient`` is an ABC which will support all of these mixins
``MtProtoSender`` class handles packing requests with the ``salt``, so they can work together nicely. It doesn't even know how to invoke things
``id``, ``sequence``, etc., and also handles how to process responses because they need to be resolved with user information first (to work with
(i.e. pong, RPC errors). This class communicates through Telegram via input entities comfortably).
its ``.connection`` member.
The ``Connection`` class uses a ``extensions/tcp_client``, a C#-like The client makes use of the ``network/mtprotosender.py``. The
``TcpClient`` to ease working with sockets in Python. All the ``MTProtoSender`` is responsible for connecting, reconnecting,
packing, unpacking, sending and receiving items from the network.
Basically, the low-level communication with Telegram, and handling
MTProto-related functions and types such as ``BadSalt``.
The sender makes use of a ``Connection`` class which knows the format in
which outgoing messages should be sent (how to encode their length and
their body, if they're further encrypted).
For now, all connection modes make use of the ``extensions/tcpclient``,
a C#-like ``TcpClient`` to ease working with sockets in Python. All the
``TcpClient`` know is how to connect through TCP and writing/reading ``TcpClient`` know is how to connect through TCP and writing/reading
from the socket with optional cancel. from the socket with optional cancel.
The ``Connection`` class bundles up all the connections modes and sends
and receives the messages accordingly (TCP full, obfuscated,
intermediate…).
Auto-generated code Auto-generated code
******************* *******************

View File

@ -5,10 +5,10 @@ Test Servers
To run Telethon on a test server, use the following code: To run Telethon on a test server, use the following code:
.. code-block:: python .. code-block:: python
client = TelegramClient(None, api_id, api_hash) client = TelegramClient(None, api_id, api_hash)
client.session.set_dc(dc_id, '149.154.167.40', 80) client.session.set_dc(dc_id, '149.154.167.40', 80)
You can check your ``'test ip'`` on https://my.telegram.org. You can check your ``'test ip'`` on https://my.telegram.org.
@ -28,8 +28,8 @@ Valid phone numbers are ``99966XYYYY``, where ``X`` is the ``dc_id`` and
be ``9996621234``. The code sent by Telegram will be ``dc_id`` repeated five be ``9996621234``. The code sent by Telegram will be ``dc_id`` repeated five
times, in this case, ``22222`` so we can hardcode that: times, in this case, ``22222`` so we can hardcode that:
.. code-block:: python .. code-block:: python
client = TelegramClient(None, api_id, api_hash) client = TelegramClient(None, api_id, api_hash)
client.session.set_dc(2, '149.154.167.40', 80) client.session.set_dc(2, '149.154.167.40', 80)
client.start(phone='9996621234', code_callback=lambda: '22222') client.start(phone='9996621234', code_callback=lambda: '22222')

View File

@ -8,7 +8,7 @@ be kind and don't forget to mention it helped you!
You should start by reading the source code on the `first You should start by reading the source code on the `first
release <https://github.com/LonamiWebs/Telethon/releases/tag/v0.1>`__ of release <https://github.com/LonamiWebs/Telethon/releases/tag/v0.1>`__ of
the project, and start creating a ``MtProtoSender``. Once this is made, the project, and start creating a ``MTProtoSender``. Once this is made,
you should write by hand the code to authenticate on the Telegram's you should write by hand the code to authenticate on the Telegram's
server, which are some steps required to get the key required to talk to server, which are some steps required to get the key required to talk to
them. Save it somewhere! Then, simply mimic, or reinvent other parts of them. Save it somewhere! Then, simply mimic, or reinvent other parts of

View File

@ -15,26 +15,26 @@ You can query an inline bot, such as `@VoteBot`__ (note, *query*,
not *interact* with a voting message), by making use of the not *interact* with a voting message), by making use of the
:tl:`GetInlineBotResultsRequest` request: :tl:`GetInlineBotResultsRequest` request:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.messages import GetInlineBotResultsRequest from telethon.tl.functions.messages import GetInlineBotResultsRequest
bot_results = client(GetInlineBotResultsRequest( bot_results = client(GetInlineBotResultsRequest(
bot, user_or_chat, 'query', '' bot, user_or_chat, 'query', ''
)) ))
And you can select any of their results by using And you can select any of their results by using
:tl:`SendInlineBotResultRequest`: :tl:`SendInlineBotResultRequest`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.messages import SendInlineBotResultRequest from telethon.tl.functions.messages import SendInlineBotResultRequest
client(SendInlineBotResultRequest( client(SendInlineBotResultRequest(
get_input_peer(user_or_chat), get_input_peer(user_or_chat),
obtained_query_id, obtained_query_id,
obtained_str_id obtained_str_id
)) ))
Talking to Bots with special reply markup Talking to Bots with special reply markup
@ -43,15 +43,15 @@ Talking to Bots with special reply markup
To interact with a message that has a special reply markup, such as To interact with a message that has a special reply markup, such as
`@VoteBot`__ polls, you would use :tl:`GetBotCallbackAnswerRequest`: `@VoteBot`__ polls, you would use :tl:`GetBotCallbackAnswerRequest`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.messages import GetBotCallbackAnswerRequest from telethon.tl.functions.messages import GetBotCallbackAnswerRequest
client(GetBotCallbackAnswerRequest( client(GetBotCallbackAnswerRequest(
user_or_chat, user_or_chat,
msg.id, msg.id,
data=msg.reply_markup.rows[wanted_row].buttons[wanted_button].data data=msg.reply_markup.rows[wanted_row].buttons[wanted_button].data
)) ))
It's a bit verbose, but it has all the information you would need to It's a bit verbose, but it has all the information you would need to
show it visually (button rows, and buttons within each row, each with show it visually (button rows, and buttons within each row, each with

View File

@ -22,14 +22,14 @@ Joining a public channel
Once you have the :ref:`entity <entities>` of the channel you want to join Once you have the :ref:`entity <entities>` of the channel you want to join
to, you can make use of the :tl:`JoinChannelRequest` to join such channel: to, you can make use of the :tl:`JoinChannelRequest` to join such channel:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.channels import JoinChannelRequest from telethon.tl.functions.channels import JoinChannelRequest
client(JoinChannelRequest(channel)) client(JoinChannelRequest(channel))
# In the same way, you can also leave such channel # In the same way, you can also leave such channel
from telethon.tl.functions.channels import LeaveChannelRequest from telethon.tl.functions.channels import LeaveChannelRequest
client(LeaveChannelRequest(input_channel)) client(LeaveChannelRequest(input_channel))
For more on channels, check the `channels namespace`__. For more on channels, check the `channels namespace`__.
@ -48,10 +48,10 @@ enough information to join! The part after the
example, is the ``hash`` of the chat or channel. Now you can use example, is the ``hash`` of the chat or channel. Now you can use
:tl:`ImportChatInviteRequest` as follows: :tl:`ImportChatInviteRequest` as follows:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.messages import ImportChatInviteRequest from telethon.tl.functions.messages import ImportChatInviteRequest
updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg')) updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg'))
Adding someone else to such chat or channel Adding someone else to such chat or channel
@ -61,26 +61,26 @@ If you don't want to add yourself, maybe because you're already in,
you can always add someone else with the :tl:`AddChatUserRequest`, which you can always add someone else with the :tl:`AddChatUserRequest`, which
use is very straightforward, or :tl:`InviteToChannelRequest` for channels: use is very straightforward, or :tl:`InviteToChannelRequest` for channels:
.. code-block:: python .. code-block:: python
# For normal chats # For normal chats
from telethon.tl.functions.messages import AddChatUserRequest from telethon.tl.functions.messages import AddChatUserRequest
# Note that ``user_to_add`` is NOT the name of the parameter. # Note that ``user_to_add`` is NOT the name of the parameter.
# It's the user you want to add (``user_id=user_to_add``). # It's the user you want to add (``user_id=user_to_add``).
client(AddChatUserRequest( client(AddChatUserRequest(
chat_id, chat_id,
user_to_add, user_to_add,
fwd_limit=10 # Allow the user to see the 10 last messages fwd_limit=10 # Allow the user to see the 10 last messages
)) ))
# For channels (which includes megagroups) # For channels (which includes megagroups)
from telethon.tl.functions.channels import InviteToChannelRequest from telethon.tl.functions.channels import InviteToChannelRequest
client(InviteToChannelRequest( client(InviteToChannelRequest(
channel, channel,
[users_to_add] [users_to_add]
)) ))
Checking a link without joining Checking a link without joining
@ -112,25 +112,25 @@ closest thing to "no filter" is to simply use
If we want to get *all* the members, we need to use a moving offset and If we want to get *all* the members, we need to use a moving offset and
a fixed limit: a fixed limit:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.channels import GetParticipantsRequest from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch from telethon.tl.types import ChannelParticipantsSearch
from time import sleep from time import sleep
offset = 0 offset = 0
limit = 100 limit = 100
all_participants = [] all_participants = []
while True: while True:
participants = client(GetParticipantsRequest( participants = client(GetParticipantsRequest(
channel, ChannelParticipantsSearch(''), offset, limit, channel, ChannelParticipantsSearch(''), offset, limit,
hash=0 hash=0
)) ))
if not participants.users: if not participants.users:
break break
all_participants.extend(participants.users) all_participants.extend(participants.users)
offset += len(participants.users) offset += len(participants.users)
.. note:: .. note::
@ -164,39 +164,39 @@ Admin Permissions
Giving or revoking admin permissions can be done with the :tl:`EditAdminRequest`: Giving or revoking admin permissions can be done with the :tl:`EditAdminRequest`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.channels import EditAdminRequest from telethon.tl.functions.channels import EditAdminRequest
from telethon.tl.types import ChannelAdminRights from telethon.tl.types import ChannelAdminRights
# You need both the channel and who to grant permissions # You need both the channel and who to grant permissions
# They can either be channel/user or input channel/input user. # They can either be channel/user or input channel/input user.
# #
# ChannelAdminRights is a list of granted permissions. # ChannelAdminRights is a list of granted permissions.
# Set to True those you want to give. # Set to True those you want to give.
rights = ChannelAdminRights( rights = ChannelAdminRights(
post_messages=None, post_messages=None,
add_admins=None, add_admins=None,
invite_users=None, invite_users=None,
change_info=True, change_info=True,
ban_users=None, ban_users=None,
delete_messages=True, delete_messages=True,
pin_messages=True, pin_messages=True,
invite_link=None, invite_link=None,
edit_messages=None edit_messages=None
) )
# Equivalent to: # Equivalent to:
# rights = ChannelAdminRights( # rights = ChannelAdminRights(
# change_info=True, # change_info=True,
# delete_messages=True, # delete_messages=True,
# pin_messages=True # pin_messages=True
# ) # )
# Once you have a ChannelAdminRights, invoke it # Once you have a ChannelAdminRights, invoke it
client(EditAdminRequest(channel, user, rights)) client(EditAdminRequest(channel, user, rights))
# User will now be able to change group info, delete other people's # User will now be able to change group info, delete other people's
# messages and pin messages. # messages and pin messages.
.. note:: .. note::
@ -218,41 +218,41 @@ Similar to how you give or revoke admin permissions, you can edit the
banned rights of an user through :tl:`EditBannedRequest` and its parameter banned rights of an user through :tl:`EditBannedRequest` and its parameter
:tl:`ChannelBannedRights`: :tl:`ChannelBannedRights`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.channels import EditBannedRequest from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights from telethon.tl.types import ChannelBannedRights
from datetime import datetime, timedelta from datetime import datetime, timedelta
# Restricting an user for 7 days, only allowing view/send messages. # Restricting an user for 7 days, only allowing view/send messages.
# #
# Note that it's "reversed". You must set to ``True`` the permissions # Note that it's "reversed". You must set to ``True`` the permissions
# you want to REMOVE, and leave as ``None`` those you want to KEEP. # you want to REMOVE, and leave as ``None`` those you want to KEEP.
rights = ChannelBannedRights( rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7), until_date=datetime.now() + timedelta(days=7),
view_messages=None, view_messages=None,
send_messages=None, send_messages=None,
send_media=True, send_media=True,
send_stickers=True, send_stickers=True,
send_gifs=True, send_gifs=True,
send_games=True, send_games=True,
send_inline=True, send_inline=True,
embed_links=True embed_links=True
) )
# The above is equivalent to # The above is equivalent to
rights = ChannelBannedRights( rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7), until_date=datetime.now() + timedelta(days=7),
send_media=True, send_media=True,
send_stickers=True, send_stickers=True,
send_gifs=True, send_gifs=True,
send_games=True, send_games=True,
send_inline=True, send_inline=True,
embed_links=True embed_links=True
) )
client(EditBannedRequest(channel, user, rights)) client(EditBannedRequest(channel, user, rights))
Kicking a member Kicking a member
@ -262,15 +262,15 @@ Telegram doesn't actually have a request to kick an user from a group.
Instead, you need to restrict them so they can't see messages. Any date Instead, you need to restrict them so they can't see messages. Any date
is enough: is enough:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.channels import EditBannedRequest from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights from telethon.tl.types import ChannelBannedRights
client(EditBannedRequest(channel, user, ChannelBannedRights( client(EditBannedRequest(channel, user, ChannelBannedRights(
until_date=None, until_date=None,
view_messages=True view_messages=True
))) )))
__ https://github.com/Kyle2142 __ https://github.com/Kyle2142
@ -285,17 +285,17 @@ It has been asked `quite`__ `a few`__ `times`__ (really, `many`__), and
while I don't understand why so many people ask this, the solution is to while I don't understand why so many people ask this, the solution is to
use :tl:`GetMessagesViewsRequest`, setting ``increment=True``: use :tl:`GetMessagesViewsRequest`, setting ``increment=True``:
.. code-block:: python .. code-block:: python
# Obtain `channel' through dialogs or through client.get_entity() or anyhow. # Obtain `channel' through dialogs or through client.get_entity() or anyhow.
# Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list. # Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list.
client(GetMessagesViewsRequest( client(GetMessagesViewsRequest(
peer=channel, peer=channel,
id=msg_ids, id=msg_ids,
increment=True increment=True
)) ))
Note that you can only do this **once or twice a day** per account, Note that you can only do this **once or twice a day** per account,

View File

@ -15,15 +15,15 @@ If you need to retrieve the bio, biography or about information for an user
you should use :tl:`GetFullUser`: you should use :tl:`GetFullUser`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.users import GetFullUserRequest from telethon.tl.functions.users import GetFullUserRequest
full = client(GetFullUserRequest(user)) full = client(GetFullUserRequest(user))
# or even # or even
full = client(GetFullUserRequest('username')) full = client(GetFullUserRequest('username'))
bio = full.about bio = full.about
See :tl:`UserFull` to know what other fields you can access. See :tl:`UserFull` to know what other fields you can access.
@ -35,11 +35,11 @@ Updating your name and/or bio
The first name, last name and bio (about) can all be changed with the same The first name, last name and bio (about) can all be changed with the same
request. Omitted fields won't change after invoking :tl:`UpdateProfile`: request. Omitted fields won't change after invoking :tl:`UpdateProfile`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.account import UpdateProfileRequest from telethon.tl.functions.account import UpdateProfileRequest
client(UpdateProfileRequest(about='This is a test from Telethon')) client(UpdateProfileRequest(about='This is a test from Telethon'))
Updating your username Updating your username
@ -47,11 +47,11 @@ Updating your username
You need to use :tl:`account.UpdateUsername`: You need to use :tl:`account.UpdateUsername`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.account import UpdateUsernameRequest from telethon.tl.functions.account import UpdateUsernameRequest
client(UpdateUsernameRequest('new_username')) client(UpdateUsernameRequest('new_username'))
Updating your profile photo Updating your profile photo
@ -61,10 +61,10 @@ The easiest way is to upload a new file and use that as the profile photo
through :tl:`UploadProfilePhoto`: through :tl:`UploadProfilePhoto`:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.photos import UploadProfilePhotoRequest from telethon.tl.functions.photos import UploadProfilePhotoRequest
client(UploadProfilePhotoRequest( client(UploadProfilePhotoRequest(
client.upload_file('/path/to/some/file') client.upload_file('/path/to/some/file')
)) ))

View File

@ -20,31 +20,31 @@ Forwarding messages
.. code-block:: python .. code-block:: python
# If you only have the message IDs # If you only have the message IDs
client.forward_messages( client.forward_messages(
entity, # to which entity you are forwarding the messages entity, # to which entity you are forwarding the messages
message_ids, # the IDs of the messages (or message) to forward message_ids, # the IDs of the messages (or message) to forward
from_entity # who sent the messages? from_entity # who sent the messages?
) )
# If you have ``Message`` objects # If you have ``Message`` objects
client.forward_messages( client.forward_messages(
entity, # to which entity you are forwarding the messages entity, # to which entity you are forwarding the messages
messages # the messages (or message) to forward messages # the messages (or message) to forward
) )
# You can also do it manually if you prefer # You can also do it manually if you prefer
from telethon.tl.functions.messages import ForwardMessagesRequest from telethon.tl.functions.messages import ForwardMessagesRequest
messages = foo() # retrieve a few messages (or even one, in a list) messages = foo() # retrieve a few messages (or even one, in a list)
from_entity = bar() from_entity = bar()
to_entity = baz() to_entity = baz()
client(ForwardMessagesRequest( client(ForwardMessagesRequest(
from_peer=from_entity, # who sent these messages? from_peer=from_entity, # who sent these messages?
id=[msg.id for msg in messages], # which are the messages? id=[msg.id for msg in messages], # which are the messages?
to_peer=to_entity # who are we forwarding them to? to_peer=to_entity # who are we forwarding them to?
)) ))
The named arguments are there for clarity, although they're not needed because The named arguments are there for clarity, although they're not needed because
they appear in order. You can obviously just wrap a single message on the list they appear in order. You can obviously just wrap a single message on the list
@ -65,26 +65,26 @@ Searching Messages
Messages are searched through the obvious :tl:`SearchRequest`, but you may run Messages are searched through the obvious :tl:`SearchRequest`, but you may run
into issues_. A valid example would be: into issues_. A valid example would be:
.. code-block:: python .. code-block:: python
from telethon.tl.functions.messages import SearchRequest from telethon.tl.functions.messages import SearchRequest
from telethon.tl.types import InputMessagesFilterEmpty from telethon.tl.types import InputMessagesFilterEmpty
filter = InputMessagesFilterEmpty() filter = InputMessagesFilterEmpty()
result = client(SearchRequest( result = client(SearchRequest(
peer=peer, # On which chat/conversation peer=peer, # On which chat/conversation
q='query', # What to search for q='query', # What to search for
filter=filter, # Filter to use (maybe filter for media) filter=filter, # Filter to use (maybe filter for media)
min_date=None, # Minimum date min_date=None, # Minimum date
max_date=None, # Maximum date max_date=None, # Maximum date
offset_id=0, # ID of the message to use as offset offset_id=0, # ID of the message to use as offset
add_offset=0, # Additional offset add_offset=0, # Additional offset
limit=10, # How many results limit=10, # How many results
max_id=0, # Maximum message ID max_id=0, # Maximum message ID
min_id=0, # Minimum message ID min_id=0, # Minimum message ID
from_id=None, # Who must have sent the message (peer) from_id=None, # Who must have sent the message (peer)
hash=0 # Special number to return nothing on no-change hash=0 # Special number to return nothing on no-change
)) ))
It's important to note that the optional parameter ``from_id`` could have It's important to note that the optional parameter ``from_id`` could have
been omitted (defaulting to ``None``). Changing it to :tl:`InputUserEmpty`, as one been omitted (defaulting to ``None``). Changing it to :tl:`InputUserEmpty`, as one
@ -113,31 +113,31 @@ referenced through this pair of ID/hash (unique per user), and you need to
use this handle when sending a "document" message. This working example will use this handle when sending a "document" message. This working example will
send yourself the very first sticker you have: send yourself the very first sticker you have:
.. code-block:: python .. code-block:: python
# Get all the sticker sets this user has # Get all the sticker sets this user has
sticker_sets = client(GetAllStickersRequest(0)) sticker_sets = client(GetAllStickersRequest(0))
# Choose a sticker set # Choose a sticker set
sticker_set = sticker_sets.sets[0] sticker_set = sticker_sets.sets[0]
# Get the stickers for this sticker set # Get the stickers for this sticker set
stickers = client(GetStickerSetRequest( stickers = client(GetStickerSetRequest(
stickerset=InputStickerSetID( stickerset=InputStickerSetID(
id=sticker_set.id, access_hash=sticker_set.access_hash id=sticker_set.id, access_hash=sticker_set.access_hash
)
))
# Stickers are nothing more than files, so send that
client(SendMediaRequest(
peer=client.get_me(),
media=InputMediaDocument(
id=InputDocument(
id=stickers.documents[0].id,
access_hash=stickers.documents[0].access_hash
) )
)) )
))
# Stickers are nothing more than files, so send that
client(SendMediaRequest(
peer=client.get_me(),
media=InputMediaDocument(
id=InputDocument(
id=stickers.documents[0].id,
access_hash=stickers.documents[0].access_hash
)
)
))
.. _issues: https://github.com/LonamiWebs/Telethon/issues/215 .. _issues: https://github.com/LonamiWebs/Telethon/issues/215

View File

@ -14,25 +14,25 @@ will be printed unless you explicitly enable it.
You can also `use the module`__ on your own project very easily: You can also `use the module`__ on your own project very easily:
.. code-block:: python .. code-block:: python
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.debug('Debug messages') logger.debug('Debug messages')
logger.info('Useful information') logger.info('Useful information')
logger.warning('This is a warning!') logger.warning('This is a warning!')
If you want to enable ``logging`` for your project *but* use a different If you want to enable ``logging`` for your project *but* use a different
log level for the library: log level for the library:
.. code-block:: python .. code-block:: python
import logging import logging
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
# For instance, show only warnings and above # For instance, show only warnings and above
logging.getLogger('telethon').setLevel(level=logging.WARNING) logging.getLogger('telethon').setLevel(level=logging.WARNING)
__ https://docs.python.org/3/library/logging.html __ https://docs.python.org/3/library/logging.html

View File

@ -50,14 +50,14 @@ The current winner is `issue
**Issue:** **Issue:**
.. figure:: https://user-images.githubusercontent.com/6297805/29822978-9a9a6ef0-8ccd-11e7-9ec5-934ea0f57681.jpg .. figure:: https://user-images.githubusercontent.com/6297805/29822978-9a9a6ef0-8ccd-11e7-9ec5-934ea0f57681.jpg
:alt: Winner issue :alt: Winner issue
Winner issue Winner issue
**Answer:** **Answer:**
.. figure:: https://user-images.githubusercontent.com/6297805/29822983-9d523402-8ccd-11e7-9fb1-5783740ee366.jpg .. figure:: https://user-images.githubusercontent.com/6297805/29822983-9d523402-8ccd-11e7-9fb1-5783740ee366.jpg
:alt: Winner issue answer :alt: Winner issue answer
Winner issue answer Winner issue answer

View File

@ -31,7 +31,16 @@ class UpdateMethods(UserMethods):
def on(self, event): def on(self, event):
""" """
Decorator helper method around add_event_handler(). Decorator helper method around `add_event_handler`. Example:
>>> from telethon import TelegramClient, events
>>> client = TelegramClient(...)
>>>
>>> @client.on(events.NewMessage)
... async def handler(event):
... ...
...
>>>
Args: Args:
event (`_EventBuilder` | `type`): event (`_EventBuilder` | `type`):

View File

@ -82,7 +82,29 @@ class EventBuilder(abc.ABC):
class EventCommon(abc.ABC): class EventCommon(abc.ABC):
"""Intermediate class with common things to all events""" """
Intermediate class with common things to all events.
Attributes:
pattern_match (`obj`):
The resulting object from calling the passed ``pattern`` function.
Here's an example using a string (defaults to regex match):
>>> from telethon import TelegramClient, events
>>> client = TelegramClient(...)
>>>
>>> @client.on(events.NewMessage(pattern=r'hi (\w+)!'))
... def handler(event):
... # In this case, the result is a ``Match`` object
... # since the ``str`` pattern was converted into
... # the ``re.compile(pattern).match`` function.
... print('Welcomed', event.pattern_match.group(1))
...
>>>
original_update (:tl:`Update`):
The original Telegram update object that caused this event.
"""
_event_name = 'Event' _event_name = 'Event'
def __init__(self, chat_peer=None, msg_id=None, broadcast=False): def __init__(self, chat_peer=None, msg_id=None, broadcast=False):
@ -146,6 +168,9 @@ class EventCommon(abc.ABC):
@property @property
def client(self): def client(self):
"""
The `telethon.TelegramClient` that created this event.
"""
return self._client return self._client
@property @property

View File

@ -177,6 +177,12 @@ class Message:
@property @property
async def chat(self): async def chat(self):
"""
The (:tl:`User` | :tl:`Chat` | :tl:`Channel`, optional) on which
the event occurred. This property may make an API call the first time
to get the most up to date version of the chat (mostly when the event
doesn't belong to a channel), so keep that in mind.
"""
if self._chat is None: if self._chat is None:
try: try:
self._chat =\ self._chat =\