mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-26 03:13:45 +03:00
Update documentation with new sections
This commit is contained in:
parent
fb9660afe0
commit
8c14259728
38
README.rst
38
README.rst
|
@ -4,16 +4,13 @@ Telethon
|
||||||
|
|
||||||
⭐️ Thanks **everyone** who has starred the project, it means a lot!
|
⭐️ Thanks **everyone** who has starred the project, it means a lot!
|
||||||
|
|
||||||
|logo| **Telethon** is an `asyncio
|
|logo| **Telethon** is an asyncio_ **Python 3**
|
||||||
<https://docs.python.org/3/library/asyncio.html>`_ **Python 3** library
|
MTProto_ library to interact with Telegram_'s API.
|
||||||
to interact with Telegram's API.
|
|
||||||
|
|
||||||
**If you're upgrading from Telethon pre-1.0 to 1.0, please make sure to read**
|
.. important::
|
||||||
`this section of the documentation
|
|
||||||
<https://telethon.readthedocs.io/en/latest/extra/basic/asyncio-magic.html>`_,
|
If you have code using Telethon before its 1.0 version, you must
|
||||||
or ``pip install telethon-sync`` which is compatible with `synchronous code
|
read `Compatibility and Convenience`_ to learn how to migrate.
|
||||||
<https://github.com/LonamiWebs/Telethon/tree/sync>`_. Don't forget to remove
|
|
||||||
the asynchronous version (``pip uninstall telethon``) if you do install sync.
|
|
||||||
|
|
||||||
What is this?
|
What is this?
|
||||||
-------------
|
-------------
|
||||||
|
@ -27,7 +24,7 @@ heavy job for you, so you can focus on developing an application.
|
||||||
Installing
|
Installing
|
||||||
----------
|
----------
|
||||||
|
|
||||||
.. code:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
pip3 install telethon
|
pip3 install telethon
|
||||||
|
|
||||||
|
@ -35,9 +32,9 @@ Installing
|
||||||
Creating a client
|
Creating a client
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
.. code:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from telethon import TelegramClient, sync
|
from telethon import TelegramClient, events, sync
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -51,7 +48,7 @@ Creating a client
|
||||||
Doing stuff
|
Doing stuff
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. code:: python
|
.. code-block:: python
|
||||||
|
|
||||||
print(client.get_me().stringify())
|
print(client.get_me().stringify())
|
||||||
|
|
||||||
|
@ -62,14 +59,23 @@ Doing stuff
|
||||||
messages = client.get_messages('username')
|
messages = client.get_messages('username')
|
||||||
messages[0].download_media()
|
messages[0].download_media()
|
||||||
|
|
||||||
|
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
|
||||||
|
async def handler(event):
|
||||||
|
await event.respond('Hey!')
|
||||||
|
|
||||||
|
|
||||||
Next steps
|
Next steps
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Do you like how Telethon looks? Check out `Read The Docs
|
Do you like how Telethon looks? Check out `Read The Docs`_ for a more
|
||||||
<http://telethon.rtfd.io/>`_ for a more in-depth explanation,
|
in-depth explanation, with examples, troubleshooting issues, and more
|
||||||
with examples, troubleshooting issues, and more useful information.
|
useful information.
|
||||||
|
|
||||||
|
.. _asyncio: https://docs.python.org/3/library/asyncio.html
|
||||||
|
.. _MTProto: https://core.telegram.org/mtproto
|
||||||
|
.. _Telegram: https://telegram.org
|
||||||
|
.. _Compatibility and Convenience: https://telethon.readthedocs.io/en/latest/extra/basic/compatibility-and-convenience.html
|
||||||
|
.. _Read The Docs: https://telethon.readthedocs.io
|
||||||
|
|
||||||
.. |logo| image:: logo.svg
|
.. |logo| image:: logo.svg
|
||||||
:width: 24pt
|
:width: 24pt
|
||||||
|
|
|
@ -29,9 +29,9 @@ can go through a sorted list of everything you can do.
|
||||||
.. important::
|
.. important::
|
||||||
|
|
||||||
All the examples in this documentation assume that you have
|
All the examples in this documentation assume that you have
|
||||||
``from telethon import sync`` or ``import telethon.sync``
|
``from telethon import sync`` or ``import telethon.sync`` for the
|
||||||
for the sake of simplicity and that you understand what
|
sake of simplicity and that you understand what it does (see
|
||||||
it does (see :ref:`asyncio-magic` for more). Simply add
|
:ref:`compatibility-and-convenience` for more). Simply add
|
||||||
either line at the beginning of your project and it will work.
|
either line at the beginning of your project and it will work.
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,10 +108,9 @@ every time its used, simply call `telethon.utils.get_input_peer`:
|
||||||
.. 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 <
|
will call `client.get_input_entity
|
||||||
telethon.client.users.UserMethods.get_input_entity>` for you when required,
|
<telethon.client.users.UserMethods.get_input_entity>` for you when
|
||||||
but it's good to remember what's happening.
|
required, but it's good to remember what's happening.
|
||||||
|
|
||||||
|
|
||||||
After this small parenthesis about `client.get_entity
|
After this small parenthesis about `client.get_entity
|
||||||
<telethon.client.users.UserMethods.get_entity>` versus
|
<telethon.client.users.UserMethods.get_entity>` versus
|
||||||
|
|
319
readthedocs/extra/advanced-usage/mastering-asyncio.rst
Normal file
319
readthedocs/extra/advanced-usage/mastering-asyncio.rst
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
.. _mastering-asyncio:
|
||||||
|
|
||||||
|
=================
|
||||||
|
Mastering asyncio
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
|
What's asyncio?
|
||||||
|
***************
|
||||||
|
|
||||||
|
asyncio_ is a Python 3's built-in library. This means it's already installed if
|
||||||
|
you have Python 3. Since Python 3.5, it is convenient to work with asynchronous
|
||||||
|
code. Before (Python 3.4) we didn't have ``async`` or ``await``, but now we do.
|
||||||
|
|
||||||
|
asyncio_ stands for *Asynchronous Input Output*. This is a very powerful
|
||||||
|
concept to use whenever you work IO. Interacting with the web or external
|
||||||
|
APIs such as Telegram's makes a lot of sense this way.
|
||||||
|
|
||||||
|
|
||||||
|
Why asyncio?
|
||||||
|
************
|
||||||
|
|
||||||
|
Asynchronous IO makes a lot of sense in a library like Telethon.
|
||||||
|
You send a request to the server (such as "get some message"), and
|
||||||
|
thanks to asyncio_, your code won't block while a response arrives.
|
||||||
|
|
||||||
|
The alternative would be to spawn a thread for each update so that
|
||||||
|
other code can run while the response arrives. That is *a lot* more
|
||||||
|
expensive.
|
||||||
|
|
||||||
|
The code will also run faster, because instead of switching back and
|
||||||
|
forth between the OS and your script, your script can handle it all.
|
||||||
|
Avoiding switching saves quite a bit of time, in Python or any other
|
||||||
|
language that supports asynchronous IO. It will also be cheaper,
|
||||||
|
because tasks are smaller than threads, which are smaller than processes.
|
||||||
|
|
||||||
|
|
||||||
|
What are asyncio basics?
|
||||||
|
************************
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# First we need the asyncio library
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Then we need a loop to work with
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
# We also need something to run
|
||||||
|
async def main():
|
||||||
|
for char in 'Hello, world!\n':
|
||||||
|
print(char, end='', flush=True)
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
|
||||||
|
# Then, we need to run the loop with a task
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
|
||||||
|
|
||||||
|
What does telethon.sync do?
|
||||||
|
***************************
|
||||||
|
|
||||||
|
The moment you import any of these:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from telethon import sync, ...
|
||||||
|
# or
|
||||||
|
from telethon.sync import ...
|
||||||
|
# or
|
||||||
|
import telethon.sync
|
||||||
|
|
||||||
|
The ``sync`` module rewrites most ``async def``
|
||||||
|
methods in Telethon to something similar to this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def new_method():
|
||||||
|
result = original_method()
|
||||||
|
if loop.is_running():
|
||||||
|
# the loop is already running, return the await-able to the user
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
# the loop is not running yet, so we can run it for the user
|
||||||
|
return loop.run_until_complete(result)
|
||||||
|
|
||||||
|
|
||||||
|
That means you can do this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
print(client.get_me().username)
|
||||||
|
|
||||||
|
Instead of this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
me = loop.run_until_complete(client.get_me())
|
||||||
|
print(me.username)
|
||||||
|
|
||||||
|
|
||||||
|
As you can see, it's a lot of boilerplate and noise having to type
|
||||||
|
``run_until_complete`` all the time, so you can let the magic module
|
||||||
|
to rewrite it for you. But notice the comment above: it won't run
|
||||||
|
the loop if it's already running, because it can't. That means this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# 3. the loop is running here
|
||||||
|
print(
|
||||||
|
client.get_me() # 4. this will return a coroutine!
|
||||||
|
.username # 5. this fails, coroutines don't have usernames
|
||||||
|
)
|
||||||
|
|
||||||
|
loop.run_until_complete( # 2. run the loop and the ``main()`` coroutine
|
||||||
|
main() # 1. calling ``async def`` "returns" a coroutine
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Will fail. So if you're inside an ``async def``, then the loop is
|
||||||
|
running, and if the loop is running, you must ``await`` things yourself:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
print((await client.get_me()).username)
|
||||||
|
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
|
||||||
|
|
||||||
|
What are async, await and coroutines?
|
||||||
|
*************************************
|
||||||
|
|
||||||
|
The ``async`` keyword lets you define asynchronous functions,
|
||||||
|
also known as coroutines, and also iterate over asynchronous
|
||||||
|
loops or use ``async with``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# ^ this declares the main() coroutine function
|
||||||
|
|
||||||
|
async with client:
|
||||||
|
# ^ this is an asynchronous with block
|
||||||
|
|
||||||
|
async for message in client.iter_messages(chat):
|
||||||
|
# ^ this is a for loop over an asynchronous generator
|
||||||
|
|
||||||
|
print(message.sender.username)
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
# ^ this assigns the default event loop from the main thread to a variable
|
||||||
|
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
# ^ this runs the *entire* loop until the main() function finishes.
|
||||||
|
# While the main() function does not finish, the loop will be running.
|
||||||
|
# While the loop is running, you can't run it again.
|
||||||
|
|
||||||
|
|
||||||
|
The ``await`` keyword blocks the *current* task, and the loop can run
|
||||||
|
other tasks. Tasks can be thought of as "threads", since many can run
|
||||||
|
concurrently:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def hello(delay):
|
||||||
|
await asyncio.sleep(delay) # await tells the loop this task is "busy"
|
||||||
|
print('hello') # eventually the loop resumes the code here
|
||||||
|
|
||||||
|
async def world(delay):
|
||||||
|
# the loop decides this method should run first
|
||||||
|
await asyncio.sleep(delay) # await tells the loop this task is "busy"
|
||||||
|
print('world') # eventually the loop finishes all tasks
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop() # get the default loop for the main thread
|
||||||
|
loop.create_task(world(2)) # create the world task, passing 2 as delay
|
||||||
|
loop.create_task(hello(delay=1)) # another task, but with delay 1
|
||||||
|
try:
|
||||||
|
# run the event loop forever; ctrl+c to stop it
|
||||||
|
# we could also run the loop for three seconds:
|
||||||
|
# loop.run_until_complete(asyncio.sleep(3))
|
||||||
|
loop.run_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
The same example, but without the comment noise:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def hello(delay):
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
print('hello')
|
||||||
|
|
||||||
|
async def world(delay):
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
print('world')
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.create_task(world(2))
|
||||||
|
loop.create_task(hello(1))
|
||||||
|
loop.run_until_complete(asyncio.sleep(3))
|
||||||
|
|
||||||
|
|
||||||
|
Can I use threads?
|
||||||
|
******************
|
||||||
|
|
||||||
|
Yes, you can, but you must understand that the loops themselves are
|
||||||
|
not thread safe. and you must be sure to know what is happening. You
|
||||||
|
may want to create a loop in a new thread and make sure to pass it to
|
||||||
|
the client:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
|
||||||
|
def go():
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
client = TelegramClient(..., loop=loop)
|
||||||
|
...
|
||||||
|
|
||||||
|
threading.Thread(target=go).start()
|
||||||
|
|
||||||
|
|
||||||
|
Generally, **you don't need threads** unless you know what you're doing.
|
||||||
|
Just create another task, as shown above. If you're using the Telethon
|
||||||
|
with a library that uses threads, you must be careful to use ``threading.Lock``
|
||||||
|
whenever you use the client, or enable the compatible mode. For that, see
|
||||||
|
:ref:`compatibility-and-convenience`.
|
||||||
|
|
||||||
|
You may have seen this error:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
RuntimeError: There is no current event loop in thread 'Thread-1'.
|
||||||
|
|
||||||
|
It just means you didn't create a loop for that thread, and if you don't
|
||||||
|
pass a loop when creating the client, it uses ``asyncio.get_event_loop()``,
|
||||||
|
which only works in the main thread.
|
||||||
|
|
||||||
|
What else can asyncio do?
|
||||||
|
*************************
|
||||||
|
|
||||||
|
Asynchronous IO is a really powerful tool, as we've seen. There are plenty
|
||||||
|
of other useful libraries that also use asyncio_ and that you can integrate
|
||||||
|
with Telethon.
|
||||||
|
|
||||||
|
* `aiocron <https://github.com/gawel/aiocron>`_ lets you schedule
|
||||||
|
things to run things at a desired time, or run some tasks daily.
|
||||||
|
* `aiohttp <https://github.com/aio-libs/aiohttp>`_ is like the infamous
|
||||||
|
`requests <https://github.com/requests/requests/>`_ but asynchronous.
|
||||||
|
* `quart <https://gitlab.com/pgjones/quart>`_ is an asynchronous alternative
|
||||||
|
to `Flask <http://flask.pocoo.org/>`_.
|
||||||
|
|
||||||
|
And of course, `asyncio <https://docs.python.org/3/library/asyncio.html>`_
|
||||||
|
itself! It has a lot of methods that let you do nice things. For example,
|
||||||
|
you can run requests in parallel:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
last, sent, download_path = await asyncio.gather(
|
||||||
|
client.get_messages('TelethonChat', 10),
|
||||||
|
client.send_message('TelethonOfftopic', 'Hey guys!'),
|
||||||
|
client.download_profile_photo('TelethonChat')
|
||||||
|
)
|
||||||
|
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
|
||||||
|
|
||||||
|
This code will get the 10 last messages from `@TelethonChat
|
||||||
|
<https://t.me/TelethonChat>`_, send one to `@TelethonOfftopic
|
||||||
|
<https://t.me/TelethonOfftopic>`_, and also download the profile
|
||||||
|
photo of the main group. asyncio_ will run all these three tasks
|
||||||
|
at the same time. You can run all the tasks you want this way.
|
||||||
|
|
||||||
|
A different way would be:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
loop.create_task(client.get_messages('TelethonChat', 10))
|
||||||
|
loop.create_task(client.send_message('TelethonOfftopic', 'Hey guys!'))
|
||||||
|
loop.create_task(client.download_profile_photo('TelethonChat'))
|
||||||
|
|
||||||
|
They will run in the background as long as the loop is running too.
|
||||||
|
|
||||||
|
|
||||||
|
Why does client.start() work outside async?
|
||||||
|
*******************************************
|
||||||
|
|
||||||
|
Because it's so common that it's really convenient to offer said
|
||||||
|
functionality by default. This means you can set up all your event
|
||||||
|
handlers and start the client without worrying about loops at all.
|
||||||
|
|
||||||
|
Using the client in a ``with`` block, `start
|
||||||
|
<telethon.client.auth.AuthMethods.start>`, `run_until_disconnected
|
||||||
|
<telethon.client.updates.UpdateMethods.run_until_disconnected>`, and
|
||||||
|
`disconnect <telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`
|
||||||
|
all support this.
|
||||||
|
|
||||||
|
Where can I read more?
|
||||||
|
**********************
|
||||||
|
|
||||||
|
`Check out my blog post
|
||||||
|
<https://lonamiwebs.github.io/blog/asyncio/>`_ about asyncio_, which
|
||||||
|
has some more examples and pictures to help you understand what happens
|
||||||
|
when the loop runs.
|
||||||
|
|
||||||
|
.. _asyncio: https://docs.python.org/3/library/asyncio.html
|
|
@ -4,6 +4,11 @@
|
||||||
Session Files
|
Session Files
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
What are sessions?
|
||||||
|
******************
|
||||||
|
|
||||||
The first parameter you pass to the constructor of the
|
The first parameter you pass to the constructor of the
|
||||||
:ref:`TelegramClient <telethon-client>` is
|
: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,
|
||||||
|
|
|
@ -27,7 +27,6 @@ Behind the scenes, this method is ``await``'ing on the `client.disconnected
|
||||||
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>` property,
|
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>` property,
|
||||||
so the code above and the following are equivalent:
|
so the code above and the following are equivalent:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -63,3 +62,12 @@ also be ran while the loop is running, so you can do this:
|
||||||
await client.run_until_disconnected()
|
await client.run_until_disconnected()
|
||||||
|
|
||||||
loop.run_until_complete(main())
|
loop.run_until_complete(main())
|
||||||
|
|
||||||
|
|
||||||
|
If you need to process updates sequentially (i.e. not in parallel),
|
||||||
|
you should set ``sequential_updates=True`` when creating the client:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with TelegramClient(..., sequential_updates=True) as client:
|
||||||
|
...
|
||||||
|
|
|
@ -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
|
Introduction
|
||||||
************
|
************
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
Getting Started
|
Getting Started
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Simple Installation
|
Simple Installation
|
||||||
*******************
|
*******************
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Automatic Installation
|
Automatic Installation
|
||||||
**********************
|
**********************
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
TelegramClient
|
TelegramClient
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
************
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Make sure to use the friendly methods described in :ref:`telethon-client`!
|
Make sure to use the friendly methods described in :ref:`telethon-client`!
|
||||||
|
|
|
@ -6,14 +6,9 @@ Working with Updates
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
|
|
||||||
Make sure you have read at least the first part of :ref:`asyncio-magic`
|
Coming from Telethon before it reached its version 1.0?
|
||||||
before working with updates. **This is a big change from Telethon pre-1.0
|
Make sure to read :ref:`compatibility-and-convenience`!
|
||||||
and 1.0, and your old handlers won't work with this version**.
|
Otherwise, you can ignore this note and just follow along.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
The library comes with the `telethon.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
|
||||||
|
|
|
@ -453,11 +453,8 @@ Synchronous magic (v1.0)
|
||||||
.. important::
|
.. important::
|
||||||
|
|
||||||
If you come from Telethon pre-1.0 you **really** want to read
|
If you come from Telethon pre-1.0 you **really** want to read
|
||||||
:ref:`asyncio-magic` to port your scripts to the new version.
|
:ref:`compatibility-and-convenience` to port your scripts to
|
||||||
|
the new version.
|
||||||
If you're not ready for this, you can ``pip install telethon-sync``.
|
|
||||||
It's a synchronous branch that mimics the ``asyncio`` version with
|
|
||||||
threads and should work under Python 3.4
|
|
||||||
|
|
||||||
The library has been around for well over a year. A lot of improvements have
|
The library has been around for well over a year. A lot of improvements have
|
||||||
been made, a lot of user complaints have been fixed, and a lot of user desires
|
been made, a lot of user complaints have been fixed, and a lot of user desires
|
||||||
|
|
|
@ -7,6 +7,8 @@ Bots
|
||||||
|
|
||||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Talking to Inline Bots
|
Talking to Inline Bots
|
||||||
**********************
|
**********************
|
||||||
|
|
|
@ -7,6 +7,8 @@ Working with Chats and Channels
|
||||||
|
|
||||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Joining a chat or channel
|
Joining a chat or channel
|
||||||
*************************
|
*************************
|
||||||
|
|
|
@ -60,6 +60,7 @@ the ``telethon.sync`` package and that you have a client ready to use.
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Authorization
|
Authorization
|
||||||
*************
|
*************
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ Users
|
||||||
|
|
||||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Retrieving full information
|
Retrieving full information
|
||||||
***************************
|
***************************
|
||||||
|
|
|
@ -7,6 +7,8 @@ Working with messages
|
||||||
|
|
||||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Forwarding messages
|
Forwarding messages
|
||||||
*******************
|
*******************
|
||||||
|
|
|
@ -15,25 +15,10 @@ or use the menu on the left. Remember to read the :ref:`changelog`
|
||||||
when you upgrade!
|
when you upgrade!
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
If you're new here, you want to read :ref:`getting-started`. If you're
|
|
||||||
looking for the method reference, you should check :ref:`telethon-client`.
|
|
||||||
|
|
||||||
The mentioned :ref:`telethon-client` is an important section and it
|
* Are you new here? Jump straight into :ref:`getting-started`!
|
||||||
contains the friendly methods that **you should use** most of the time.
|
* Looking for available friendly methods? See :ref:`telethon-client`.
|
||||||
|
* Used Telethon before v1.0? See :ref:`compatibility-and-convenience`.
|
||||||
|
|
||||||
.. note::
|
|
||||||
The library uses `asyncio <https://docs.python.org/3/library/asyncio.html>`_
|
|
||||||
under the hood, but you don't need to know anything about it unless you're
|
|
||||||
going to work with updates! If you're a user of Telethon pre-1.0 and you
|
|
||||||
aren't ready to convert your event handlers into ``async``, you can use
|
|
||||||
`a simpler version <https://github.com/LonamiWebs/Telethon/tree/sync>`_
|
|
||||||
(select the "sync" version in ``readthedocs``' bottom left corner).
|
|
||||||
|
|
||||||
If you used Telethon pre-1.0 but your scripts don't use updates or threads,
|
|
||||||
running ``import telethon.sync`` should make them Just Work. Otherwise,
|
|
||||||
we have :ref:`asyncio-magic` to teach you why ``asyncio`` is good and
|
|
||||||
how to use it.
|
|
||||||
|
|
||||||
|
|
||||||
What is this?
|
What is this?
|
||||||
|
@ -56,8 +41,8 @@ heavy job for you, so you can focus on developing an application.
|
||||||
extra/basic/creating-a-client
|
extra/basic/creating-a-client
|
||||||
extra/basic/telegram-client
|
extra/basic/telegram-client
|
||||||
extra/basic/entities
|
extra/basic/entities
|
||||||
extra/basic/asyncio-magic
|
|
||||||
extra/basic/working-with-updates
|
extra/basic/working-with-updates
|
||||||
|
extra/basic/compatibility-and-convenience
|
||||||
|
|
||||||
|
|
||||||
.. _Advanced-usage:
|
.. _Advanced-usage:
|
||||||
|
@ -70,6 +55,7 @@ heavy job for you, so you can focus on developing an application.
|
||||||
extra/advanced-usage/sessions
|
extra/advanced-usage/sessions
|
||||||
extra/advanced-usage/update-modes
|
extra/advanced-usage/update-modes
|
||||||
extra/advanced-usage/mastering-telethon
|
extra/advanced-usage/mastering-telethon
|
||||||
|
extra/advanced-usage/mastering-asyncio
|
||||||
|
|
||||||
|
|
||||||
.. _Examples:
|
.. _Examples:
|
||||||
|
|
|
@ -25,11 +25,3 @@ telethon\.extensions\.html module
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
telethon\.extensions\.tcpclient module
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: telethon.extensions.tcpclient
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods):
|
||||||
reverse=False, _total=None):
|
reverse=False, _total=None):
|
||||||
"""
|
"""
|
||||||
Iterator over the message history for the specified entity.
|
Iterator over the message history for the specified entity.
|
||||||
|
|
||||||
If either `search`, `filter` or `from_user` are provided,
|
If either `search`, `filter` or `from_user` are provided,
|
||||||
:tl:`messages.Search` will be used instead of :tl:`messages.getHistory`.
|
:tl:`messages.Search` will be used instead of :tl:`messages.getHistory`.
|
||||||
|
|
||||||
|
@ -48,6 +47,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods):
|
||||||
Number of messages to be retrieved. Due to limitations with
|
Number of messages to be retrieved. Due to limitations with
|
||||||
the API retrieving more than 3000 messages will take longer
|
the API retrieving more than 3000 messages will take longer
|
||||||
than half a minute (or even more based on previous calls).
|
than half a minute (or even more based on previous calls).
|
||||||
|
|
||||||
The limit may also be ``None``, which would eventually return
|
The limit may also be ``None``, which would eventually return
|
||||||
the whole history.
|
the whole history.
|
||||||
|
|
||||||
|
@ -411,8 +411,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods):
|
||||||
Whether the existing draft should be cleared or not.
|
Whether the existing draft should be cleared or not.
|
||||||
Has no effect when sending a file.
|
Has no effect when sending a file.
|
||||||
|
|
||||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||||
:tl:`KeyboardButton`):
|
|
||||||
The matrix (list of lists), row list or button to be shown
|
The matrix (list of lists), row list or button to be shown
|
||||||
after sending the message. This parameter will only work if
|
after sending the message. This parameter will only work if
|
||||||
you have signed in as a bot. You can also pass your own
|
you have signed in as a bot. You can also pass your own
|
||||||
|
@ -616,8 +615,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods):
|
||||||
The file object that should replace the existing media
|
The file object that should replace the existing media
|
||||||
in the message.
|
in the message.
|
||||||
|
|
||||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||||
:tl:`KeyboardButton`):
|
|
||||||
The matrix (list of lists), row list or button to be shown
|
The matrix (list of lists), row list or button to be shown
|
||||||
after sending the message. This parameter will only work if
|
after sending the message. This parameter will only work if
|
||||||
you have signed in as a bot. You can also pass your own
|
you have signed in as a bot. You can also pass your own
|
||||||
|
|
|
@ -71,6 +71,10 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods):
|
||||||
Optional JPEG thumbnail (for documents). **Telegram will
|
Optional JPEG thumbnail (for documents). **Telegram will
|
||||||
ignore this parameter** unless you pass a ``.jpg`` file!
|
ignore this parameter** unless you pass a ``.jpg`` file!
|
||||||
|
|
||||||
|
The file must also be small in dimensions and in-disk size.
|
||||||
|
Successful thumbnails were files below 20kb and 200x200px.
|
||||||
|
Width/height and dimensions/size ratios may be important.
|
||||||
|
|
||||||
allow_cache (`bool`, optional):
|
allow_cache (`bool`, optional):
|
||||||
Whether to allow using the cached version stored in the
|
Whether to allow using the cached version stored in the
|
||||||
database or not. Defaults to ``True`` to avoid re-uploads.
|
database or not. Defaults to ``True`` to avoid re-uploads.
|
||||||
|
@ -94,8 +98,7 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods):
|
||||||
Set `allow_cache` to ``False`` if you sent the same file
|
Set `allow_cache` to ``False`` if you sent the same file
|
||||||
without this setting before for it to work.
|
without this setting before for it to work.
|
||||||
|
|
||||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||||
:tl:`KeyboardButton`):
|
|
||||||
The matrix (list of lists), row list or button to be shown
|
The matrix (list of lists), row list or button to be shown
|
||||||
after sending the message. This parameter will only work if
|
after sending the message. This parameter will only work if
|
||||||
you have signed in as a bot. You can also pass your own
|
you have signed in as a bot. You can also pass your own
|
||||||
|
|
|
@ -304,7 +304,7 @@ class UserMethods(TelegramBaseClient):
|
||||||
return utils.get_input_peer(peer)
|
return utils.get_input_peer(peer)
|
||||||
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Could not find the input entity for "{}". Please read https://'
|
'Could not find the input entity for {!r}. Please read https://'
|
||||||
'telethon.readthedocs.io/en/latest/extra/basic/entities.html to'
|
'telethon.readthedocs.io/en/latest/extra/basic/entities.html to'
|
||||||
' find out more details.'
|
' find out more details.'
|
||||||
.format(peer)
|
.format(peer)
|
||||||
|
|
|
@ -34,8 +34,7 @@ class InlineBuilder:
|
||||||
game (`bool`, optional):
|
game (`bool`, optional):
|
||||||
May be ``True`` to indicate that the game will be sent.
|
May be ``True`` to indicate that the game will be sent.
|
||||||
|
|
||||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`, optional):
|
||||||
:tl:`KeyboardButton`, optional):
|
|
||||||
Same as ``buttons`` for `client.send_message
|
Same as ``buttons`` for `client.send_message
|
||||||
<telethon.client.messages.MessageMethods.send_message>`.
|
<telethon.client.messages.MessageMethods.send_message>`.
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ mimetypes.add_type('audio/ogg', '.ogg')
|
||||||
USERNAME_RE = re.compile(
|
USERNAME_RE = re.compile(
|
||||||
r'@|(?:https?://)?(?:www\.)?(?:telegram\.(?:me|dog)|t\.me)/(joinchat/)?'
|
r'@|(?:https?://)?(?:www\.)?(?:telegram\.(?:me|dog)|t\.me)/(joinchat/)?'
|
||||||
)
|
)
|
||||||
|
TG_JOIN_RE = re.compile(
|
||||||
|
r'tg://(join)?invite='
|
||||||
|
)
|
||||||
|
|
||||||
# The only shorter-than-five-characters usernames are those used for some
|
# The only shorter-than-five-characters usernames are those used for some
|
||||||
# special, very well known bots. This list may be incomplete though:
|
# special, very well known bots. This list may be incomplete though:
|
||||||
|
@ -646,15 +649,16 @@ def parse_phone(phone):
|
||||||
|
|
||||||
|
|
||||||
def parse_username(username):
|
def parse_username(username):
|
||||||
"""Parses the given username or channel access hash, given
|
"""
|
||||||
|
Parses the given username or channel access hash, given
|
||||||
a string, username or URL. Returns a tuple consisting of
|
a string, username or URL. Returns a tuple consisting of
|
||||||
both the stripped, lowercase username and whether it is
|
both the stripped, lowercase username and whether it is
|
||||||
a joinchat/ hash (in which case is not lowercase'd).
|
a joinchat/ hash (in which case is not lowercase'd).
|
||||||
|
|
||||||
Returns ``None`` if the ``username`` is not valid.
|
Returns ``(None, False)`` if the ``username`` or link is not valid.
|
||||||
"""
|
"""
|
||||||
username = username.strip()
|
username = username.strip()
|
||||||
m = USERNAME_RE.match(username)
|
m = USERNAME_RE.match(username) or TG_JOIN_RE.match(username)
|
||||||
if m:
|
if m:
|
||||||
username = username[m.end():]
|
username = username[m.end():]
|
||||||
is_invite = bool(m.group(1))
|
is_invite = bool(m.group(1))
|
||||||
|
|
|
@ -108,6 +108,7 @@ LEARN_PYTHON = (
|
||||||
"• [Official Docs](https://docs.python.org/3/tutorial/index.html).\n"
|
"• [Official Docs](https://docs.python.org/3/tutorial/index.html).\n"
|
||||||
"• [Dive Into Python 3](http://www.diveintopython3.net/).\n"
|
"• [Dive Into Python 3](http://www.diveintopython3.net/).\n"
|
||||||
"• [Learn Python](https://www.learnpython.org/).\n"
|
"• [Learn Python](https://www.learnpython.org/).\n"
|
||||||
|
"• [Project Python](http://projectpython.net/).\n"
|
||||||
"• [Computer Science Circles](https://cscircles.cemc.uwaterloo.ca/).\n"
|
"• [Computer Science Circles](https://cscircles.cemc.uwaterloo.ca/).\n"
|
||||||
"• [MIT OpenCourse](https://ocw.mit.edu/courses/electrical-engineering-"
|
"• [MIT OpenCourse](https://ocw.mit.edu/courses/electrical-engineering-"
|
||||||
"and-computer-science/6-0001-introduction-to-computer-science-and-progr"
|
"and-computer-science/6-0001-introduction-to-computer-science-and-progr"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user