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.
The `telethon.telegram_client.TelegramClient` doesn't offer a method for every
single request the Telegram API supports. However, it's very simple to *call*
or *invoke* any request. Whenever you need something, don't forget to `check
the documentation`__ and look for the `method you need`__. There you can go
through a sorted list of everything you can do.
The :ref:`TelegramClient <telethon-client>` doesn't offer a method for
every single request the Telegram API supports. However, it's very simple to
*call* or *invoke* any request. Whenever you need something, don't forget to
`check the documentation`__ and look for the `method you need`__. There you
can go through a sorted list of everything you can do.
.. 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
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
: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
clipboard", or your script won't be aware of this class! Now we have:
.. code-block:: python
from telethon.tl.functions.messages import SendMessageRequest
.. code-block:: python
from telethon.tl.functions.messages import SendMessageRequest
If you're going to use a lot of these, you may do:
.. code-block:: python
from telethon.tl import types, functions
# We now have access to 'functions.messages.SendMessageRequest'
.. code-block:: python
from telethon.tl import types, functions
# We now have access to 'functions.messages.SendMessageRequest'
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
@ -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
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
:tl:`InputUser`, :tl:`InputChat`, or so on, this is why using
``.get_input_entity()`` is more straightforward (and often
immediate, if you've seen the user before, know their ID, etc.).
If you also need to have information about the whole user, use
`telethon.telegram_client.TelegramClient.get_entity()` instead:
`client.get_input_entity <telethon.client.users.UserMethods.get_input_entity>`
is more straightforward (and often immediate, if you've seen the user before,
know their ID, etc.). If you also **need** to have information about the whole
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
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
every time its used, simply call `telethon.utils.get_input_peer`:
.. code-block:: python
.. code-block:: python
from telethon import utils
peer = utils.get_input_user(entity)
from telethon import utils
peer = utils.get_input_user(entity)
.. note::
Since ``v0.16.2`` this is further simplified. The ``Request`` itself
will call ``client.get_input_entity()`` for you when required, but
it's good to remember what's happening.
will call `client.get_input_entity <
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
``.get_input_entity``, we have everything we need. To ``.invoke()`` our
After this small parenthesis about `client.get_entity
<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:
.. code-block:: python
.. code-block:: python
result = client(SendMessageRequest(peer, 'Hello there!'))
# __call__ is an alias for client.invoke(request). Both will work
result = client(SendMessageRequest(peer, 'Hello there!'))
# __call__ is an alias for client.invoke(request). Both will work
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
as you wish. Remember to use the right types! To sum up:
.. code-block:: python
.. code-block:: python
result = client(SendMessageRequest(
client.get_input_entity('username'), 'Hello there!'
))
result = client(SendMessageRequest(
client.get_input_entity('username'), 'Hello there!'
))
This can further be simplified to:
.. code-block:: python
.. code-block:: python
result = client(SendMessageRequest('username', 'Hello there!'))
# Or even
result = client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
result = client(SendMessageRequest('username', 'Hello there!'))
# Or even
result = client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
.. note::

View File

@ -4,7 +4,8 @@
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,
if you create a ``TelegramClient('anon')`` instance and connect, an
``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.
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:

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
one is very simple:
.. code-block:: python
.. code-block:: python
from telethon import TelegramClient
from telethon import TelegramClient
# Use your own values here
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
# Use your own values here
api_id = 12345
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
@ -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.
.. 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.
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
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'
client.send_code_request(phone_number)
myself = client.sign_in(phone_number, input('Enter code: '))
# 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.
.. code-block:: python
phone_number = '+34600000000'
client.send_code_request(phone_number)
myself = client.sign_in(phone_number, input('Enter code: '))
# 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::
@ -82,24 +100,26 @@ mentioned ``.stringify()`` method, and printing these might prove useful.
As a full example:
.. code-block:: python
.. code-block:: python
client = TelegramClient('anon', api_id, api_hash)
assert client.connect()
if not client.is_user_authorized():
client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: '))
client = TelegramClient('anon', api_id, api_hash)
assert client.connect()
if not client.is_user_authorized():
client.send_code_request(phone_number)
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.start()
client = TelegramClient('anon', api_id, api_hash)
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
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
@ -108,25 +128,27 @@ method also accepts a ``phone=`` and ``bot_token`` parameters.
You can use either, as both will work. Determining which
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::
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
receive a ``FloodWaitError`` of around 22 hours, so be careful not to mess
this up! This shouldn't happen if you're doing things as explained, though.
parameter of the :ref:`TelegramClient <telethon-client>`, which is the session
name) you will receive a ``FloodWaitError`` of around 22 hours, so be
careful not to mess this up! This shouldn't happen if you're doing things
as explained, though.
.. note::
If you want to use a **proxy**, you have to `install PySocks`__
(via pip or manual) and then set the appropriated parameters:
.. code-block:: python
.. code-block:: python
import socks
client = TelegramClient('session_id',
api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
proxy=(socks.SOCKS5, 'localhost', 4444)
)
import socks
client = TelegramClient('session_id',
api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
proxy=(socks.SOCKS5, 'localhost', 4444)
)
The ``proxy=`` argument should be a tuple, a list or a dict,
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
account, calling :meth:`telethon.TelegramClient.sign_in` will raise a
``SessionPasswordNeededError``. When this happens, just
:meth:`telethon.TelegramClient.sign_in` again with a ``password=``:
account, calling `.sign_in()
<telethon.client.auth.AuthMethods.sign_in>` will raise a
``SessionPasswordNeededError``. When this happens, just use the method
again with a ``password=``:
.. code-block:: python
.. code-block:: python
import getpass
from telethon.errors import SessionPasswordNeededError
import getpass
from telethon.errors import SessionPasswordNeededError
client.sign_in(phone)
try:
client.sign_in(code=input('Enter code: '))
except SessionPasswordNeededError:
client.sign_in(password=getpass.getpass())
client.sign_in(phone)
try:
client.sign_in(code=input('Enter code: '))
except SessionPasswordNeededError:
client.sign_in(password=getpass.getpass())
The mentioned ``.start()`` method will handle this for you as well, but
you must set the ``password=`` parameter beforehand (it won't be asked).
The mentioned `.start()
<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
you won't run into any problems.
Take note that if you want to set only the email/hint and leave
the current password unchanged, you need to "redo" the 2fa.
you won't run into any problems. Take note that if you want to
set only the email/hint and leave the current password unchanged,
you need to "redo" the 2fa.
See the examples below:
.. code-block:: python
.. code-block:: python
from telethon.errors import EmailUnconfirmedError
# Sets 2FA password for first time:
client.edit_2fa(new_password='supersecurepassword')
# Changes password:
client.edit_2fa(current_password='supersecurepassword',
new_password='changedmymind')
# Clears current password (i.e. removes 2FA):
client.edit_2fa(current_password='changedmymind', new_password=None)
# Sets new password with recovery email:
try:
client.edit_2fa(new_password='memes and dreams',
email='JohnSmith@example.com')
# Raises error (you need to check your email to complete 2FA setup.)
except EmailUnconfirmedError:
# You can put email checking code here if desired.
pass
# Also take note that unless you remove 2FA or explicitly
# give email parameter again it will keep the last used setting
# Set hint after already setting password:
client.edit_2fa(current_password='memes and dreams',
new_password='memes and dreams',
hint='It keeps you alive')
from telethon.errors import EmailUnconfirmedError
# Sets 2FA password for first time:
client.edit_2fa(new_password='supersecurepassword')
# Changes password:
client.edit_2fa(current_password='supersecurepassword',
new_password='changedmymind')
# Clears current password (i.e. removes 2FA):
client.edit_2fa(current_password='changedmymind', new_password=None)
# Sets new password with recovery email:
try:
client.edit_2fa(new_password='memes and dreams',
email='JohnSmith@example.com')
# Raises error (you need to check your email to complete 2FA setup.)
except EmailUnconfirmedError:
# You can put email checking code here if desired.
pass
# Also take note that unless you remove 2FA or explicitly
# give email parameter again it will keep the last used setting
# Set hint after already setting password:
client.edit_2fa(current_password='memes and dreams',
new_password='memes and dreams',
hint='It keeps you alive')
__ https://github.com/Anorov/PySocks#installation
__ 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
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
`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
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))
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.
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.
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
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,
or the entity retrieved by other means like ``.get_dialogs()``.
don't call `client.get_entity() <telethon.client.users.UserMethods.get_entity>`
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
@ -108,21 +112,27 @@ before you can "use them".
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,
``.get_input_entity()`` is available. This will always use the cache while
possible, making zero API calls most of the time. When a request is made,
if you provided the full entity, e.g. an :tl:`User`, the library will convert
it to the required :tl:`InputPeer` automatically for you.
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
is available. This will always use the cache while possible, making zero API
calls most of the time. When a request is made, if you provided the full
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
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.
of the entity.
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
``.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

View File

@ -9,83 +9,85 @@ Getting Started
Simple Installation
*******************
``pip3 install telethon``
.. code-block:: sh
**More details**: :ref:`installation`
pip3 install telethon
**More details**: :ref:`installation`
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
# api_hash from https://my.telegram.org, under API Development.
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
# 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_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('session_name', api_id, api_hash)
client.start()
client = TelegramClient('session_name', api_id, api_hash)
client.start()
**More details**: :ref:`creating-a-client`
**More details**: :ref:`creating-a-client`
Basic Usage
***********
.. code-block:: python
.. code-block:: python
# Getting information about yourself
print(client.get_me().stringify())
# Getting information about yourself
print(client.get_me().stringify())
# Sending a message (you can use 'me' or 'self' to message yourself)
client.send_message('username', 'Hello World from Telethon!')
# Sending a message (you can use 'me' or 'self' to message yourself)
client.send_message('username', 'Hello World from Telethon!')
# Sending a file
client.send_file('username', '/home/myself/Pictures/holidays.jpg')
# Sending a file
client.send_file('username', '/home/myself/Pictures/holidays.jpg')
# Retrieving messages from a chat
from telethon import utils
for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message)
# Retrieving messages from a chat
from telethon import utils
for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message)
# Listing all the dialogs (conversations you have open)
for dialog in client.get_dialogs(limit=10):
print(utils.get_display_name(dialog.entity), dialog.draft.text)
# Listing all the dialogs (conversations you have open)
for dialog in client.get_dialogs(limit=10):
print(utils.get_display_name(dialog.entity), dialog.draft.text)
# Downloading profile photos (default path is the working directory)
client.download_profile_photo('username')
# Downloading profile photos (default path is the working directory)
client.download_profile_photo('username')
# Once you have a message with .media (if message.media)
# you can download it using client.download_media():
messages = client.get_messages('username')
client.download_media(messages[0])
# Once you have a message with .media (if message.media)
# you can download it using client.download_media():
messages = client.get_messages('username')
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
****************
.. code-block:: python
.. code-block:: python
from telethon import events
from telethon import events
# We need to have some worker running
client.updates.workers = 1
# We need to have some worker running
client.updates.workers = 1
@client.on(events.NewMessage(incoming=True, pattern='(?i)hi'))
def handler(event):
event.reply('Hello!')
@client.on(events.NewMessage(incoming=True, pattern='(?i)hi'))
def handler(event):
event.reply('Hello!')
# If you want to handle updates you can't let the script end.
input('Press enter to exit.')
# If you want to handle updates you can't let the script end.
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:
``pip3 install telethon``
.. code-block:: sh
pip3 install telethon
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.
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:
.. code-block:: sh
.. code-block:: sh
# pip3 install git+https://github.com/LonamiWebs/Telethon.git
or
$ git clone https://github.com/LonamiWebs/Telethon.git
$ cd Telethon/
# pip install -Ue .
# pip3 install git+https://github.com/LonamiWebs/Telethon.git
or
$ git clone https://github.com/LonamiWebs/Telethon.git
$ cd Telethon/
# pip install -Ue .
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
@ -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>`__
via ``pip`` or as an extra:
``pip3 install telethon[cryptg]``
.. code-block:: sh
pip3 install telethon[cryptg]
Manual Installation
@ -47,14 +53,27 @@ Manual Installation
1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and
``rsa`` (`GitHub`__ | `PyPi`__) modules:
``sudo -H pip3 install pyaes rsa``
.. code-block:: sh
pip3 install pyaes rsa
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!

View File

@ -15,9 +15,9 @@ Introduction
available methods are in the :ref:`telethon-client` reference, including
detailed descriptions to what they do.
The ``TelegramClient`` is the central class of the library, the one
you will be using most of the time. For this reason, it's important
to know what it offers.
The :ref:`TelegramClient <telethon-client>` is the
central class of the library, the one you will be using most of the time. For
this reason, it's important to know what it offers.
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
@ -27,12 +27,14 @@ methods for any object, even yours!
Interacting with the Telegram API is done through sending **requests**,
this is, any "method" listed on the API. There are a few methods (and
growing!) on the ``TelegramClient`` class that abstract you from the
need of manually importing the requests you need.
growing!) on the :ref:`TelegramClient <telethon-client>` class that abstract
you from the need of manually importing the requests you need.
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
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
how the library refers to either of these:
.. code-block:: python
.. code-block:: python
# The method will infer that you've passed an username
# It also accepts phone numbers, and will get the user
# from your contact list.
lonami = client.get_entity('lonami')
# The method will infer that you've passed an username
# It also accepts phone numbers, and will get the user
# from your contact list.
lonami = client.get_entity('lonami')
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
@ -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:
.. code-block:: python
.. code-block:: python
# Note that you can use 'me' or 'self' to message yourself
client.send_message('username', 'Hello World from Telethon!')
# Note that you can use 'me' or 'self' to message yourself
client.send_message('username', 'Hello World from Telethon!')
# .send_message's parse mode defaults to markdown, so you
# can use **bold**, __italics__, [links](https://example.com), `code`,
# and even [mentions](@username)/[mentions](tg://user?id=123456789)
client.send_message('username', '**Using** __markdown__ `too`!')
# .send_message's parse mode defaults to markdown, so you
# can use **bold**, __italics__, [links](https://example.com), `code`,
# and even [mentions](@username)/[mentions](tg://user?id=123456789)
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()
from telethon import utils
for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message)
# The utils package has some goodies, like .get_display_name()
from telethon import utils
for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message)
# Dialogs are the conversations you have open
for dialog in client.get_dialogs(limit=10):
print(utils.get_display_name(dialog.entity), dialog.draft.text)
# Dialogs are the conversations you have open
for dialog in client.get_dialogs(limit=10):
print(utils.get_display_name(dialog.entity), dialog.draft.text)
# Default path is the working directory
client.download_profile_photo('username')
# Default path is the working directory
client.download_profile_photo('username')
# Call .disconnect() when you're done
client.disconnect()
# Call .disconnect() when you're done
client.disconnect()
Remember that you can call ``.stringify()`` to any object Telegram returns
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
available for you to use in the ``TelegramClient`` class. These are simply
wrappers around the "raw" Telegram API, making it much more manageable and
easier to work with.
available for you to use in the :ref:`TelegramClient <telethon-client>` class.
These are simply wrappers around the "raw" Telegram API, making it much more
manageable and easier to work with.
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_

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
usage when dealing with them, since there are many updates. If you're looking
for the method reference, check :ref:`telethon-events-package`, otherwise,
let's dive in!
.. note::
.. important::
The library logs by default no output, and any exception that occurs
inside your handlers will be "hidden" from you to prevent the thread
from terminating (so it can still deliver events). You should enable
logging (``import logging; logging.basicConfig(level=logging.ERROR)``)
when working with events, at least the error level, to see if this is
happening so you can debug the error.
logging when working with events, at least the error level, to see if
this is happening so you can debug the error.
**When using updates, please enable logging:**
.. code-block:: python
import logging
logging.basicConfig(level=logging.ERROR)
.. contents::
@ -62,7 +68,8 @@ Nothing we don't know already.
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:
.. code-block:: python
@ -72,8 +79,10 @@ the callback function you're about to define will be called:
event.reply('hi!')
If a ``NewMessage`` event occurs, and ``'hello'`` is in the text of the
message, we ``reply`` to the event with a ``'hi!'`` message.
If a `NewMessage
<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
@ -88,10 +97,11 @@ do other things instead idling. For this refer to :ref:`update-modes`.
More on events
**************
The ``NewMessage`` event has much more than what was shown. You can access
the ``.sender`` of the message through that member, or even see if the message
had ``.media``, a ``.photo`` or a ``.document`` (which you could download with
for example ``client.download_media(event.photo)``.
The `NewMessage <telethon.events.newmessage.NewMessage>` event has much
more than what was shown. You can access the ``.sender`` of the message
through that member, or even see if the message had ``.media``, a ``.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()``
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.
Note that ``.reply()`` and ``.respond()`` are just wrappers around the
``client.send_message()`` method which supports the ``file=`` parameter.
This means you can reply with a photo if you do ``client.reply(file=photo)``.
`client.send_message() <telethon.client.messages.MessageMethods.send_message>`
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
the same handler. You can also have a handler work on only specific chats,
for example:
.. code-block:: python
.. code-block:: python
import ast
import random
import ast
import random
# 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.
@client.on(events.NewMessage(chats=('TelethonChat', 'TelethonOffTopic')))
def normal_handler(event):
if 'roll' in event.raw_text:
event.reply(str(random.randint(1, 6)))
# 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.
@client.on(events.NewMessage(chats=('TelethonChat', 'TelethonOffTopic')))
def normal_handler(event):
if 'roll' in event.raw_text:
event.reply(str(random.randint(1, 6)))
# Similarly, you can use incoming=True for messages that you receive
@client.on(events.NewMessage(chats='TelethonOffTopic', outgoing=True))
def admin_handler(event):
if event.raw_text.startswith('eval'):
expression = event.raw_text.replace('eval', '').strip()
event.reply(str(ast.literal_eval(expression)))
# Similarly, you can use incoming=True for messages that you receive
@client.on(events.NewMessage(chats='TelethonOffTopic', outgoing=True))
def admin_handler(event):
if event.raw_text.startswith('eval'):
expression = event.raw_text.replace('eval', '').strip()
event.reply(str(ast.literal_eval(expression)))
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
*************************
If for any reason you can't use the ``@client.on`` syntax, don't worry.
You can call ``client.add_event_handler(callback, event)`` to achieve
If for any reason you can't use the `@client.on
<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.
Similar to that method, you also have :meth:`client.remove_event_handler`
and :meth:`client.list_event_handlers` which do as they names indicate.
Similarly, you also have `client.remove_event_handler
<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``
for adding, and ``None`` when removing (so all callbacks would be removed).
The ``event`` type is optional in all methods and defaults to
`events.Raw <telethon.events.raw.Raw>` for adding, and ``None`` when
removing (so all callbacks would be removed).
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
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
propagation of the update through your handlers to stop:
it is possible to raise a `telethon.events.StopPropagation` exception which
will cause the propagation of the update through your handlers to stop:
.. 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,
handling updates, etc.
The ``TelegramClient`` inherits the ``TelegramBareClient``. The later is
basically a pruned version of the ``TelegramClient``, which knows basic
stuff like ``.invoke()``\ 'ing requests, downloading files, or switching
between data centers. This is primary to keep the method count per class
and file low and manageable.
The ``TelegramClient`` inherits from several mixing ``Method`` classes,
since there are so many methods that having them in a single file would
make maintenance painful (it was three thousand lines before this separation
happened!). It's a "god object", but there is only a way to interact with
Telegram really.
Both clients make use of the ``network/mtproto_sender.py``. The
``MtProtoSender`` class handles packing requests with the ``salt``,
``id``, ``sequence``, etc., and also handles how to process responses
(i.e. pong, RPC errors). This class communicates through Telegram via
its ``.connection`` member.
The ``TelegramBaseClient`` is an ABC which will support all of these mixins
so they can work together nicely. It doesn't even know how to invoke things
because they need to be resolved with user information first (to work with
input entities comfortably).
The ``Connection`` class uses a ``extensions/tcp_client``, a C#-like
``TcpClient`` to ease working with sockets in Python. All the
The client makes use of the ``network/mtprotosender.py``. 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
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
*******************

View File

@ -5,10 +5,10 @@ Test Servers
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.session.set_dc(dc_id, '149.154.167.40', 80)
client = TelegramClient(None, api_id, api_hash)
client.session.set_dc(dc_id, '149.154.167.40', 80)
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
times, in this case, ``22222`` so we can hardcode that:
.. code-block:: python
.. code-block:: python
client = TelegramClient(None, api_id, api_hash)
client.session.set_dc(2, '149.154.167.40', 80)
client.start(phone='9996621234', code_callback=lambda: '22222')
client = TelegramClient(None, api_id, api_hash)
client.session.set_dc(2, '149.154.167.40', 80)
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
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
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

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
: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, user_or_chat, 'query', ''
))
bot_results = client(GetInlineBotResultsRequest(
bot, user_or_chat, 'query', ''
))
And you can select any of their results by using
:tl:`SendInlineBotResultRequest`:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.messages import SendInlineBotResultRequest
from telethon.tl.functions.messages import SendInlineBotResultRequest
client(SendInlineBotResultRequest(
get_input_peer(user_or_chat),
obtained_query_id,
obtained_str_id
))
client(SendInlineBotResultRequest(
get_input_peer(user_or_chat),
obtained_query_id,
obtained_str_id
))
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
`@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(
user_or_chat,
msg.id,
data=msg.reply_markup.rows[wanted_row].buttons[wanted_button].data
))
client(GetBotCallbackAnswerRequest(
user_or_chat,
msg.id,
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
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
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
client(JoinChannelRequest(channel))
from telethon.tl.functions.channels import JoinChannelRequest
client(JoinChannelRequest(channel))
# In the same way, you can also leave such channel
from telethon.tl.functions.channels import LeaveChannelRequest
client(LeaveChannelRequest(input_channel))
# In the same way, you can also leave such channel
from telethon.tl.functions.channels import LeaveChannelRequest
client(LeaveChannelRequest(input_channel))
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
:tl:`ImportChatInviteRequest` as follows:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.messages import ImportChatInviteRequest
updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg'))
from telethon.tl.functions.messages import ImportChatInviteRequest
updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg'))
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
use is very straightforward, or :tl:`InviteToChannelRequest` for channels:
.. code-block:: python
.. code-block:: python
# For normal chats
from telethon.tl.functions.messages import AddChatUserRequest
# For normal chats
from telethon.tl.functions.messages import AddChatUserRequest
# 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``).
client(AddChatUserRequest(
chat_id,
user_to_add,
fwd_limit=10 # Allow the user to see the 10 last messages
))
# 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``).
client(AddChatUserRequest(
chat_id,
user_to_add,
fwd_limit=10 # Allow the user to see the 10 last messages
))
# For channels (which includes megagroups)
from telethon.tl.functions.channels import InviteToChannelRequest
# For channels (which includes megagroups)
from telethon.tl.functions.channels import InviteToChannelRequest
client(InviteToChannelRequest(
channel,
[users_to_add]
))
client(InviteToChannelRequest(
channel,
[users_to_add]
))
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
a fixed limit:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
from time import sleep
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
from time import sleep
offset = 0
limit = 100
all_participants = []
offset = 0
limit = 100
all_participants = []
while True:
participants = client(GetParticipantsRequest(
channel, ChannelParticipantsSearch(''), offset, limit,
hash=0
))
if not participants.users:
break
all_participants.extend(participants.users)
offset += len(participants.users)
while True:
participants = client(GetParticipantsRequest(
channel, ChannelParticipantsSearch(''), offset, limit,
hash=0
))
if not participants.users:
break
all_participants.extend(participants.users)
offset += len(participants.users)
.. note::
@ -164,39 +164,39 @@ Admin Permissions
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.types import ChannelAdminRights
from telethon.tl.functions.channels import EditAdminRequest
from telethon.tl.types import ChannelAdminRights
# You need both the channel and who to grant permissions
# They can either be channel/user or input channel/input user.
#
# ChannelAdminRights is a list of granted permissions.
# Set to True those you want to give.
rights = ChannelAdminRights(
post_messages=None,
add_admins=None,
invite_users=None,
change_info=True,
ban_users=None,
delete_messages=True,
pin_messages=True,
invite_link=None,
edit_messages=None
)
# Equivalent to:
# rights = ChannelAdminRights(
# change_info=True,
# delete_messages=True,
# pin_messages=True
# )
# You need both the channel and who to grant permissions
# They can either be channel/user or input channel/input user.
#
# ChannelAdminRights is a list of granted permissions.
# Set to True those you want to give.
rights = ChannelAdminRights(
post_messages=None,
add_admins=None,
invite_users=None,
change_info=True,
ban_users=None,
delete_messages=True,
pin_messages=True,
invite_link=None,
edit_messages=None
)
# Equivalent to:
# rights = ChannelAdminRights(
# change_info=True,
# delete_messages=True,
# pin_messages=True
# )
# Once you have a ChannelAdminRights, invoke it
client(EditAdminRequest(channel, user, rights))
# Once you have a ChannelAdminRights, invoke it
client(EditAdminRequest(channel, user, rights))
# User will now be able to change group info, delete other people's
# messages and pin messages.
# User will now be able to change group info, delete other people's
# messages and pin messages.
.. 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
:tl:`ChannelBannedRights`:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights
from telethon.tl.functions.channels import EditBannedRequest
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.
#
# 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.
rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7),
view_messages=None,
send_messages=None,
send_media=True,
send_stickers=True,
send_gifs=True,
send_games=True,
send_inline=True,
embed_links=True
)
# Restricting an user for 7 days, only allowing view/send messages.
#
# 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.
rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7),
view_messages=None,
send_messages=None,
send_media=True,
send_stickers=True,
send_gifs=True,
send_games=True,
send_inline=True,
embed_links=True
)
# The above is equivalent to
rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7),
send_media=True,
send_stickers=True,
send_gifs=True,
send_games=True,
send_inline=True,
embed_links=True
)
# The above is equivalent to
rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7),
send_media=True,
send_stickers=True,
send_gifs=True,
send_games=True,
send_inline=True,
embed_links=True
)
client(EditBannedRequest(channel, user, rights))
client(EditBannedRequest(channel, user, rights))
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
is enough:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights
from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights
client(EditBannedRequest(channel, user, ChannelBannedRights(
until_date=None,
view_messages=True
)))
client(EditBannedRequest(channel, user, ChannelBannedRights(
until_date=None,
view_messages=True
)))
__ 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
use :tl:`GetMessagesViewsRequest`, setting ``increment=True``:
.. code-block:: python
.. code-block:: python
# Obtain `channel' through dialogs or through client.get_entity() or anyhow.
# Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list.
# Obtain `channel' through dialogs or through client.get_entity() or anyhow.
# Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list.
client(GetMessagesViewsRequest(
peer=channel,
id=msg_ids,
increment=True
))
client(GetMessagesViewsRequest(
peer=channel,
id=msg_ids,
increment=True
))
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`:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.users import GetFullUserRequest
from telethon.tl.functions.users import GetFullUserRequest
full = client(GetFullUserRequest(user))
# or even
full = client(GetFullUserRequest('username'))
full = client(GetFullUserRequest(user))
# or even
full = client(GetFullUserRequest('username'))
bio = full.about
bio = full.about
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
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
@ -47,11 +47,11 @@ Updating your username
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
@ -61,10 +61,10 @@ The easiest way is to upload a new file and use that as the profile photo
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.upload_file('/path/to/some/file')
))
client(UploadProfilePhotoRequest(
client.upload_file('/path/to/some/file')
))

View File

@ -20,31 +20,31 @@ Forwarding messages
.. code-block:: python
# If you only have the message IDs
client.forward_messages(
entity, # to which entity you are forwarding the messages
message_ids, # the IDs of the messages (or message) to forward
from_entity # who sent the messages?
)
# If you only have the message IDs
client.forward_messages(
entity, # to which entity you are forwarding the messages
message_ids, # the IDs of the messages (or message) to forward
from_entity # who sent the messages?
)
# If you have ``Message`` objects
client.forward_messages(
entity, # to which entity you are forwarding the messages
messages # the messages (or message) to forward
)
# If you have ``Message`` objects
client.forward_messages(
entity, # to which entity you are forwarding the messages
messages # the messages (or message) to forward
)
# You can also do it manually if you prefer
from telethon.tl.functions.messages import ForwardMessagesRequest
# You can also do it manually if you prefer
from telethon.tl.functions.messages import ForwardMessagesRequest
messages = foo() # retrieve a few messages (or even one, in a list)
from_entity = bar()
to_entity = baz()
messages = foo() # retrieve a few messages (or even one, in a list)
from_entity = bar()
to_entity = baz()
client(ForwardMessagesRequest(
from_peer=from_entity, # who sent these messages?
id=[msg.id for msg in messages], # which are the messages?
to_peer=to_entity # who are we forwarding them to?
))
client(ForwardMessagesRequest(
from_peer=from_entity, # who sent these messages?
id=[msg.id for msg in messages], # which are the messages?
to_peer=to_entity # who are we forwarding them to?
))
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
@ -65,26 +65,26 @@ Searching Messages
Messages are searched through the obvious :tl:`SearchRequest`, but you may run
into issues_. A valid example would be:
.. code-block:: python
.. code-block:: python
from telethon.tl.functions.messages import SearchRequest
from telethon.tl.types import InputMessagesFilterEmpty
from telethon.tl.functions.messages import SearchRequest
from telethon.tl.types import InputMessagesFilterEmpty
filter = InputMessagesFilterEmpty()
result = client(SearchRequest(
peer=peer, # On which chat/conversation
q='query', # What to search for
filter=filter, # Filter to use (maybe filter for media)
min_date=None, # Minimum date
max_date=None, # Maximum date
offset_id=0, # ID of the message to use as offset
add_offset=0, # Additional offset
limit=10, # How many results
max_id=0, # Maximum message ID
min_id=0, # Minimum message ID
from_id=None, # Who must have sent the message (peer)
hash=0 # Special number to return nothing on no-change
))
filter = InputMessagesFilterEmpty()
result = client(SearchRequest(
peer=peer, # On which chat/conversation
q='query', # What to search for
filter=filter, # Filter to use (maybe filter for media)
min_date=None, # Minimum date
max_date=None, # Maximum date
offset_id=0, # ID of the message to use as offset
add_offset=0, # Additional offset
limit=10, # How many results
max_id=0, # Maximum message ID
min_id=0, # Minimum message ID
from_id=None, # Who must have sent the message (peer)
hash=0 # Special number to return nothing on no-change
))
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
@ -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
send yourself the very first sticker you have:
.. code-block:: python
.. code-block:: python
# Get all the sticker sets this user has
sticker_sets = client(GetAllStickersRequest(0))
# Get all the sticker sets this user has
sticker_sets = client(GetAllStickersRequest(0))
# Choose a sticker set
sticker_set = sticker_sets.sets[0]
# Choose a sticker set
sticker_set = sticker_sets.sets[0]
# Get the stickers for this sticker set
stickers = client(GetStickerSetRequest(
stickerset=InputStickerSetID(
id=sticker_set.id, access_hash=sticker_set.access_hash
# Get the stickers for this sticker set
stickers = client(GetStickerSetRequest(
stickerset=InputStickerSetID(
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

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:
.. code-block:: python
.. code-block:: python
import logging
logger = logging.getLogger(__name__)
import logging
logger = logging.getLogger(__name__)
logger.debug('Debug messages')
logger.info('Useful information')
logger.warning('This is a warning!')
logger.debug('Debug messages')
logger.info('Useful information')
logger.warning('This is a warning!')
If you want to enable ``logging`` for your project *but* use a different
log level for the library:
.. code-block:: python
.. code-block:: python
import logging
logging.basicConfig(level=logging.DEBUG)
# For instance, show only warnings and above
logging.getLogger('telethon').setLevel(level=logging.WARNING)
import logging
logging.basicConfig(level=logging.DEBUG)
# For instance, show only warnings and above
logging.getLogger('telethon').setLevel(level=logging.WARNING)
__ https://docs.python.org/3/library/logging.html

View File

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

View File

@ -31,7 +31,16 @@ class UpdateMethods(UserMethods):
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:
event (`_EventBuilder` | `type`):

View File

@ -82,7 +82,29 @@ class EventBuilder(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'
def __init__(self, chat_peer=None, msg_id=None, broadcast=False):
@ -146,6 +168,9 @@ class EventCommon(abc.ABC):
@property
def client(self):
"""
The `telethon.TelegramClient` that created this event.
"""
return self._client
@property

View File

@ -177,6 +177,12 @@ class Message:
@property
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:
try:
self._chat =\