mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-09 13:29:47 +00:00
Update documentation with new sections
This commit is contained in:
@@ -1,322 +0,0 @@
|
||||
.. _asyncio-magic:
|
||||
|
||||
==================
|
||||
Magic with asyncio
|
||||
==================
|
||||
|
||||
.. important::
|
||||
|
||||
TL; DR; If you've upgraded to Telethon 1.0 from a previous version
|
||||
**and you're not using events or updates**, add this line:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import telethon.sync
|
||||
|
||||
At the beginning of your main script and you will be good. If you **do**
|
||||
use updates or events, keep reading, or ``pip install telethon-sync``, a
|
||||
branch that mimics the ``asyncio`` code with threads and should work
|
||||
under Python 3.4.
|
||||
|
||||
You might also want to check the :ref:`changelog`.
|
||||
|
||||
|
||||
The sync module
|
||||
***************
|
||||
|
||||
It's time to tell you the truth. The library has been doing magic behind
|
||||
the scenes. We're sorry to tell you this, but at least it wasn't dark magic!
|
||||
|
||||
You may have noticed one of these lines across the documentation:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon import sync
|
||||
# or
|
||||
import telethon.sync
|
||||
|
||||
Either of these lines will import the *magic* ``sync`` module. When you
|
||||
import this module, you can suddenly use all the methods defined in the
|
||||
:ref:`TelegramClient <telethon-client>` like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
client.send_message('me', 'Hello!')
|
||||
|
||||
for dialog in client.iter_dialogs():
|
||||
print(dialog.title)
|
||||
|
||||
|
||||
What happened behind the scenes is that all those methods, called *coroutines*,
|
||||
were rewritten to be normal methods that will block (with some exceptions).
|
||||
This means you can use the library without worrying about ``asyncio`` and
|
||||
event loops.
|
||||
|
||||
However, this only works until you run the event loop yourself explicitly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
|
||||
async def coro():
|
||||
client.send_message('me', 'Hello!') # <- no longer works!
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(coro())
|
||||
|
||||
|
||||
What things will work and when?
|
||||
*******************************
|
||||
|
||||
You can use all the methods in the :ref:`TelegramClient <telethon-client>`
|
||||
in a synchronous, blocking way without trouble, as long as you're not running
|
||||
the loop as we saw above (the ``loop.run_until_complete(...)`` line runs "the
|
||||
loop"). If you're running the loop, then *you* are the one responsible to
|
||||
``await`` everything. So to fix the code above:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
|
||||
async def coro():
|
||||
await client.send_message('me', 'Hello!')
|
||||
# ^ notice this new await
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(coro())
|
||||
|
||||
The library can only run the loop until the method completes if the loop
|
||||
isn't already running, which is why the magic can't work if you run the
|
||||
loop yourself.
|
||||
|
||||
**When you work with updates or events**, the loop needs to be
|
||||
running one way or another (using `client.run_until_disconnected()
|
||||
<telethon.client.updates.UpdateMethods.run_until_disconnected>` runs the loop),
|
||||
so your event handlers must be ``async def``.
|
||||
|
||||
.. important::
|
||||
|
||||
Turning your event handlers into ``async def`` is the biggest change
|
||||
between Telethon pre-1.0 and 1.0, but updating will likely cause a
|
||||
noticeable speed-up in your programs. Keep reading!
|
||||
|
||||
|
||||
So in short, you can use **all** methods in the client with ``await`` or
|
||||
without it if the loop isn't running:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
client.send_message('me', 'Hello!') # works
|
||||
|
||||
async def main():
|
||||
await client.send_message('me', 'Hello!') # also works
|
||||
|
||||
loop.run_until_complete(main())
|
||||
|
||||
|
||||
When you work with updates, you should stick using the ``async def main``
|
||||
way, since your event handlers will be ``async def`` too.
|
||||
|
||||
.. note::
|
||||
|
||||
There are two exceptions. Both `client.run_until_disconnected()
|
||||
<telethon.client.updates.UpdateMethods.run_until_disconnected>` and
|
||||
`client.start() <telethon.client.auth.AuthMethods.start>` work in
|
||||
and outside of ``async def`` for convenience without importing the
|
||||
magic module. The rest of methods remain ``async`` unless you import it.
|
||||
|
||||
You can skip the rest if you already know how ``asyncio`` works and you
|
||||
already understand what the magic does and how it works. Just remember
|
||||
to ``await`` all your methods if you're inside an ``async def`` or are
|
||||
using updates and you will be good.
|
||||
|
||||
|
||||
Why asyncio?
|
||||
************
|
||||
|
||||
Python's `asyncio <https://docs.python.org/3/library/asyncio.html>`_ is the
|
||||
standard way to run asynchronous code from within Python. Since Python 3.5,
|
||||
using ``async def`` and ``await`` became possible, and Python 3.6 further
|
||||
improves what you can do with asynchronous code, although it's not the only
|
||||
way (other projects like `Trio <https://github.com/python-trio>`_ also exist).
|
||||
|
||||
Telegram is a service where all API calls are executed in an asynchronous
|
||||
way. You send your request, and eventually, Telegram will process it and
|
||||
respond to it. It feels natural to make a library that also behaves this
|
||||
way: you send a request, and you can ``await`` for its result.
|
||||
|
||||
Now that we know that Telegram's API follows an asynchronous model, you
|
||||
should understand the benefits of developing a library that does the same,
|
||||
it greatly simplifies the internal code and eases working with the API.
|
||||
|
||||
Using ``asyncio`` keeps a cleaner library that will be easier to understand,
|
||||
develop, and that will be faster than using threads, which are harder to get
|
||||
right and can cause issues. It also enables to use the powerful ``asyncio``
|
||||
system such as futures, timeouts, cancellation, etc. in a natural way.
|
||||
|
||||
If you're still not convinced or you're just not ready for using ``asyncio``,
|
||||
the library offers a synchronous interface without the need for all the
|
||||
``async`` and ``await`` you would otherwise see. `Follow this link
|
||||
<https://github.com/LonamiWebs/Telethon/tree/sync>`_ to find out more.
|
||||
|
||||
|
||||
How do I get started?
|
||||
*********************
|
||||
|
||||
To get started with ``asyncio``, all you need is to setup your main
|
||||
``async def`` like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
pass # Your code goes here
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
|
||||
You don't need to ``import telethon.sync`` if you're going to work this
|
||||
way. This is the best way to work in real programs since the loop won't
|
||||
be starting and ending all the time, but is a bit more annoying to setup.
|
||||
|
||||
Inside ``async def main()``, you can use the ``await`` keyword. Most
|
||||
methods in the :ref:`TelegramClient <telethon-client>` are ``async def``.
|
||||
You must ``await`` all ``async def``, also known as a *coroutines*:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def main():
|
||||
client = TelegramClient(...)
|
||||
|
||||
# client.start() is a coroutine (async def), it needs an await
|
||||
await client.start()
|
||||
|
||||
# Sending a message also interacts with the API, and needs an await
|
||||
await client.send_message('me', 'Hello myself!')
|
||||
|
||||
|
||||
If you don't know anything else about ``asyncio``, this will be enough
|
||||
to get you started. Once you're ready to learn more about it, you will
|
||||
be able to use that power and everything you've learnt with Telethon.
|
||||
Just remember that if you use ``await``, you need to be inside of an
|
||||
``async def``.
|
||||
|
||||
Another way to use ``async def`` is to use ``loop.run_until_complete(f())``,
|
||||
but the loop must not be running before.
|
||||
|
||||
If you want to handle updates (and don't let the script die), you must
|
||||
`await client.run_until_disconnected()
|
||||
<telethon.client.updates.UpdateMethods.run_until_disconnected>`
|
||||
which is a property that you can wait on until you call
|
||||
`await client.disconnect()
|
||||
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
client = TelegramClient(...)
|
||||
|
||||
@client.on(events.NewMessage)
|
||||
async def handler(event):
|
||||
print(event)
|
||||
|
||||
async def main():
|
||||
await client.start()
|
||||
await client.run_until_disconnected()
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
|
||||
`client.run_until_disconnected()
|
||||
<telethon.client.updates.UpdateMethods.run_until_disconnected>` and
|
||||
`client.start()
|
||||
<telethon.client.auth.AuthMethods.start>` are special-cased and work
|
||||
inside or outside ``async def`` for convenience, even without importing
|
||||
the ``sync`` module, so you can also do this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
client = TelegramClient(...)
|
||||
|
||||
@client.on(events.NewMessage)
|
||||
async def handler(event):
|
||||
print(event)
|
||||
|
||||
if __name__ == '__main__':
|
||||
client.start()
|
||||
client.run_until_disconnected()
|
||||
|
||||
|
||||
Which methods should I use and when?
|
||||
************************************
|
||||
|
||||
Something to note is that you must always get an event loop if you
|
||||
want to be able to make any API calls. This is done as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
The loop must be running, or things will never get sent.
|
||||
Normally, you use ``run_until_complete``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def coroutine():
|
||||
await asyncio.sleep(1)
|
||||
|
||||
loop.run_until_complete(coroutine())
|
||||
|
||||
Note that ``asyncio.sleep`` is in itself a coroutine, so this will
|
||||
work too:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
loop.run_until_complete(asyncio.sleep(1))
|
||||
|
||||
Generally, you make an ``async def main()`` if you need to ``await``
|
||||
a lot of things, instead of typing ``run_until_complete`` all the time:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def main():
|
||||
message = await client.send_message('me', 'Hi')
|
||||
await asyncio.sleep(1)
|
||||
await message.delete()
|
||||
|
||||
loop.run_until_complete(main())
|
||||
|
||||
# vs
|
||||
|
||||
message = loop.run_until_complete(client.send_message('me', 'Hi'))
|
||||
loop.run_until_complete(asyncio.sleep(1))
|
||||
loop.run_until_complete(message.delete())
|
||||
|
||||
You can see that the first version has more lines, but you had to type
|
||||
a lot less. You can also rename the run method to something shorter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Note no parenthesis (), we're not running it, just copying the method
|
||||
rc = loop.run_until_complete
|
||||
message = rc(client.send_message('me', 'Hi'))
|
||||
rc(asyncio.sleep(1))
|
||||
rc(message.delete())
|
||||
|
||||
The documentation generally runs the loop until complete behind the
|
||||
scenes if you've imported the magic ``sync`` module, but if you haven't,
|
||||
you need to run the loop yourself. We recommend that you use the
|
||||
``async def main()`` method to do all your work with ``await``.
|
||||
It's the easiest and most performant thing to do.
|
||||
|
||||
|
||||
More resources to learn asyncio
|
||||
*******************************
|
||||
|
||||
If you would like to learn a bit more about why ``asyncio`` is something
|
||||
you should learn, `check out my blog post
|
||||
<https://lonamiwebs.github.io/blog/asyncio/>`_ that goes into more detail.
|
171
readthedocs/extra/basic/compatibility-and-convenience.rst
Normal file
171
readthedocs/extra/basic/compatibility-and-convenience.rst
Normal file
@@ -0,0 +1,171 @@
|
||||
.. _compatibility-and-convenience:
|
||||
|
||||
=============================
|
||||
Compatibility and Convenience
|
||||
=============================
|
||||
|
||||
Telethon is an ``asyncio`` library. Compatibility is an important concern,
|
||||
and while it can't always be kept and mistakes happens, the :ref:`changelog`
|
||||
is there to tell you when these important changes happen.
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Compatibility
|
||||
*************
|
||||
|
||||
.. important::
|
||||
|
||||
**You should not enable the thread-compatibility mode for new projects.**
|
||||
It comes with a cost, and new projects will greatly benefit from using
|
||||
``asyncio`` by default such as increased speed and easier reasoning about
|
||||
the code flow. You should only enable it for old projects you don't have
|
||||
the time to upgrade to ``asyncio``.
|
||||
|
||||
There exists a fair amount of code online using Telethon before it reached
|
||||
its 1.0 version, where it became fully asynchronous by default. Since it was
|
||||
necessary to clean some things, compatibility was not kept 100% but the
|
||||
changes are simple:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 1. The library no longer uses threads.
|
||||
# Add this at the **beginning** of your script to work around that.
|
||||
from telethon import full_sync
|
||||
full_sync.enable()
|
||||
|
||||
# 2. client.connect() no longer returns True.
|
||||
# Change this...
|
||||
assert client.connect()
|
||||
# ...for this:
|
||||
client.connect()
|
||||
|
||||
# 3. client.idle() no longer exists.
|
||||
# Change this...
|
||||
client.idle()
|
||||
# ...to this:
|
||||
client.run_until_disconnected()
|
||||
|
||||
# 4. client.add_update_handler no longer exists.
|
||||
# Change this...
|
||||
client.add_update_handler(handler)
|
||||
# ...to this:
|
||||
client.add_event_handler(handler)
|
||||
|
||||
# 5. It's good practice to stop the full_sync mode once you're done
|
||||
try:
|
||||
... # all your code in here
|
||||
finally:
|
||||
full_sync.stop()
|
||||
|
||||
|
||||
Convenience
|
||||
***********
|
||||
|
||||
.. note::
|
||||
|
||||
The entire documentation assumes you have done one of the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon import TelegramClient, sync
|
||||
# or
|
||||
from telethon.sync import TelegramClient
|
||||
|
||||
This makes the examples shorter and easier to think about.
|
||||
|
||||
For quick scripts that don't need updates, it's a lot more convenient to
|
||||
forget about ``full_sync`` or ``asyncio`` and just work with sequential code.
|
||||
This can prove to be a powerful hybrid for running under the Python REPL too.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon.sync import TelegramClient
|
||||
# ^~~~~ note this part; it will manage the asyncio loop for you
|
||||
|
||||
with TelegramClient(...) as client:
|
||||
print(client.get_me().username)
|
||||
# ^ notice the lack of await, or loop.run_until_complete().
|
||||
# Since there is no loop running, this is done behind the scenes.
|
||||
#
|
||||
message = client.send_message('me', 'Hi!')
|
||||
import time
|
||||
time.sleep(5)
|
||||
message.delete()
|
||||
|
||||
# You can also have an hybrid between a synchronous
|
||||
# part and asynchronous event handlers.
|
||||
#
|
||||
from telethon import events
|
||||
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
|
||||
async def handler(event):
|
||||
await event.reply('hey')
|
||||
|
||||
client.run_until_disconnected()
|
||||
|
||||
|
||||
Some methods, such as ``with``, ``start``, ``disconnect`` and
|
||||
``run_until_disconnected`` work both in synchronous and asynchronous
|
||||
contexts by default for convenience, and to avoid the little overhead
|
||||
it has when using methods like sending a message, getting messages, etc.
|
||||
This keeps the best of both worlds as a sane default.
|
||||
|
||||
.. note::
|
||||
|
||||
As a rule of thumb, if you're inside an ``async def`` and you need
|
||||
the client, you need to ``await`` calls to the API. If you call other
|
||||
functions that also need API calls, make them ``async def`` and ``await``
|
||||
them too. Otherwise, there is no need to do so with this mode.
|
||||
|
||||
Speed
|
||||
*****
|
||||
|
||||
When you're ready to micro-optimize your application, or if you simply
|
||||
don't need to call any non-basic methods from a synchronous context,
|
||||
just get rid of both ``telethon.sync`` and ``telethon.full_sync``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
from telethon import TelegramClient, events
|
||||
|
||||
async def main():
|
||||
async with TelegramClient(...) as client:
|
||||
print((await client.get_me()).username)
|
||||
# ^_____________________^ notice these parenthesis
|
||||
# You want to ``await`` the call, not the username.
|
||||
#
|
||||
message = await client.send_message('me', 'Hi!')
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
|
||||
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
|
||||
async def handler(event):
|
||||
await event.reply('hey')
|
||||
|
||||
await client.run_until_disconnected()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
|
||||
|
||||
The ``telethon.sync`` magic module simply wraps every method behind:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
|
||||
So that you don't have to write it yourself every time. That's the
|
||||
overhead you pay if you import it, and what you save if you don't.
|
||||
|
||||
Learning
|
||||
********
|
||||
|
||||
You know the library uses ``asyncio`` everywhere, and you want to learn
|
||||
how to do things right. Even though ``asyncio`` is its own topic, the
|
||||
documentation wants you to learn how to use Telethon correctly, and for
|
||||
that, you need to use ``asyncio`` correctly too. For this reason, there
|
||||
is a section called :ref:`mastering-asyncio` that will introduce you to
|
||||
the ``asyncio`` world, with links to more resources for learning how to
|
||||
use it. Feel free to check that section out once you have read the rest.
|
@@ -5,6 +5,40 @@ Users, Chats and Channels
|
||||
=========================
|
||||
|
||||
|
||||
.. important::
|
||||
|
||||
TL;DR; If you're here because of *"Could not find the input entity for"*,
|
||||
you must ask yourself "how did I find this entity through official
|
||||
applications"? Now do the same with the library. Use what applies:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with client:
|
||||
# Does it have an username? Use it!
|
||||
entity = client.get_entity(username)
|
||||
|
||||
# Do you have a conversation open with them? Get dialogs.
|
||||
client.get_dialogs()
|
||||
|
||||
# Are they participant of some group? Get them.
|
||||
client.get_participants('TelethonChat')
|
||||
|
||||
# Is the entity the original sender of a forwarded message? Get it.
|
||||
client.get_messages('TelethonChat', 100)
|
||||
|
||||
# NOW you can use the ID, anywhere!
|
||||
entity = client.get_entity(123456)
|
||||
client.send_message(123456, 'Hi!')
|
||||
|
||||
Once the library has "seen" the entity, you can use their **integer** ID.
|
||||
You can't use entities from IDs the library hasn't seen. You must make the
|
||||
library see them *at least once* and disconnect properly. You know where
|
||||
the entities are and you must tell the library. It won't guess for you.
|
||||
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
|
@@ -5,6 +5,8 @@
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Simple Installation
|
||||
*******************
|
||||
|
@@ -4,6 +4,8 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Automatic Installation
|
||||
**********************
|
||||
|
@@ -4,10 +4,6 @@
|
||||
TelegramClient
|
||||
==============
|
||||
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
.. note::
|
||||
|
||||
Make sure to use the friendly methods described in :ref:`telethon-client`!
|
||||
|
@@ -6,14 +6,9 @@ Working with Updates
|
||||
|
||||
.. important::
|
||||
|
||||
Make sure you have read at least the first part of :ref:`asyncio-magic`
|
||||
before working with updates. **This is a big change from Telethon pre-1.0
|
||||
and 1.0, and your old handlers won't work with this version**.
|
||||
|
||||
To port your code to the new version, you should just prefix all your
|
||||
event handlers with ``async`` and ``await`` everything that makes an
|
||||
API call, such as replying, deleting messages, etc.
|
||||
|
||||
Coming from Telethon before it reached its version 1.0?
|
||||
Make sure to read :ref:`compatibility-and-convenience`!
|
||||
Otherwise, you can ignore this note and just follow along.
|
||||
|
||||
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
|
||||
|
Reference in New Issue
Block a user