mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-22 09:26:37 +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!
|
||||
|
||||
|logo| **Telethon** is an `asyncio
|
||||
<https://docs.python.org/3/library/asyncio.html>`_ **Python 3** library
|
||||
to interact with Telegram's API.
|
||||
|logo| **Telethon** is an asyncio_ **Python 3**
|
||||
MTProto_ library to interact with Telegram_'s API.
|
||||
|
||||
**If you're upgrading from Telethon pre-1.0 to 1.0, please make sure to read**
|
||||
`this section of the documentation
|
||||
<https://telethon.readthedocs.io/en/latest/extra/basic/asyncio-magic.html>`_,
|
||||
or ``pip install telethon-sync`` which is compatible with `synchronous code
|
||||
<https://github.com/LonamiWebs/Telethon/tree/sync>`_. Don't forget to remove
|
||||
the asynchronous version (``pip uninstall telethon``) if you do install sync.
|
||||
.. important::
|
||||
|
||||
If you have code using Telethon before its 1.0 version, you must
|
||||
read `Compatibility and Convenience`_ to learn how to migrate.
|
||||
|
||||
What is this?
|
||||
-------------
|
||||
|
@ -27,7 +24,7 @@ heavy job for you, so you can focus on developing an application.
|
|||
Installing
|
||||
----------
|
||||
|
||||
.. code:: sh
|
||||
.. code-block:: sh
|
||||
|
||||
pip3 install telethon
|
||||
|
||||
|
@ -35,9 +32,9 @@ Installing
|
|||
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
|
||||
# api_hash from https://my.telegram.org, under API Development.
|
||||
|
@ -51,7 +48,7 @@ Creating a client
|
|||
Doing stuff
|
||||
-----------
|
||||
|
||||
.. code:: python
|
||||
.. code-block:: python
|
||||
|
||||
print(client.get_me().stringify())
|
||||
|
||||
|
@ -62,14 +59,23 @@ Doing stuff
|
|||
messages = client.get_messages('username')
|
||||
messages[0].download_media()
|
||||
|
||||
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
|
||||
async def handler(event):
|
||||
await event.respond('Hey!')
|
||||
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
Do you like how Telethon looks? Check out `Read The Docs
|
||||
<http://telethon.rtfd.io/>`_ for a more in-depth explanation,
|
||||
with examples, troubleshooting issues, and more useful information.
|
||||
Do you like how Telethon looks? Check out `Read The Docs`_ for a more
|
||||
in-depth explanation, with examples, troubleshooting issues, and more
|
||||
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
|
||||
:width: 24pt
|
||||
|
|
|
@ -29,9 +29,9 @@ can go through a sorted list of everything you can do.
|
|||
.. important::
|
||||
|
||||
All the examples in this documentation assume that you have
|
||||
``from telethon import sync`` or ``import telethon.sync``
|
||||
for the sake of simplicity and that you understand what
|
||||
it does (see :ref:`asyncio-magic` for more). Simply add
|
||||
``from telethon import sync`` or ``import telethon.sync`` for the
|
||||
sake of simplicity and that you understand what it does (see
|
||||
:ref:`compatibility-and-convenience` for more). Simply add
|
||||
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::
|
||||
|
||||
Since ``v0.16.2`` this is further simplified. The ``Request`` itself
|
||||
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.
|
||||
|
||||
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 `client.get_entity
|
||||
<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
|
||||
==============
|
||||
|
||||
.. contents::
|
||||
|
||||
What are sessions?
|
||||
******************
|
||||
|
||||
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,
|
||||
|
|
|
@ -27,7 +27,6 @@ Behind the scenes, this method is ``await``'ing on the `client.disconnected
|
|||
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>` property,
|
||||
so the code above and the following are equivalent:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
|
@ -63,3 +62,12 @@ also be ran while the loop is running, so you can do this:
|
|||
await client.run_until_disconnected()
|
||||
|
||||
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
|
||||
************
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -453,11 +453,8 @@ Synchronous magic (v1.0)
|
|||
.. important::
|
||||
|
||||
If you come from Telethon pre-1.0 you **really** want to read
|
||||
:ref:`asyncio-magic` 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
|
||||
:ref:`compatibility-and-convenience` to port your scripts to
|
||||
the new version.
|
||||
|
||||
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
|
||||
|
|
|
@ -7,6 +7,8 @@ Bots
|
|||
|
||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Talking to Inline Bots
|
||||
**********************
|
||||
|
|
|
@ -7,6 +7,8 @@ Working with Chats and Channels
|
|||
|
||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Joining a chat or channel
|
||||
*************************
|
||||
|
|
|
@ -60,6 +60,7 @@ the ``telethon.sync`` package and that you have a client ready to use.
|
|||
|
||||
.. contents::
|
||||
|
||||
|
||||
Authorization
|
||||
*************
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ Users
|
|||
|
||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Retrieving full information
|
||||
***************************
|
||||
|
|
|
@ -7,6 +7,8 @@ Working with messages
|
|||
|
||||
These examples assume you have read :ref:`accessing-the-full-api`.
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Forwarding messages
|
||||
*******************
|
||||
|
|
|
@ -15,25 +15,10 @@ or use the menu on the left. Remember to read the :ref:`changelog`
|
|||
when you upgrade!
|
||||
|
||||
.. 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
|
||||
contains the friendly methods that **you should use** most of the time.
|
||||
|
||||
|
||||
.. 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.
|
||||
* Are you new here? Jump straight into :ref:`getting-started`!
|
||||
* Looking for available friendly methods? See :ref:`telethon-client`.
|
||||
* Used Telethon before v1.0? See :ref:`compatibility-and-convenience`.
|
||||
|
||||
|
||||
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/telegram-client
|
||||
extra/basic/entities
|
||||
extra/basic/asyncio-magic
|
||||
extra/basic/working-with-updates
|
||||
extra/basic/compatibility-and-convenience
|
||||
|
||||
|
||||
.. _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/update-modes
|
||||
extra/advanced-usage/mastering-telethon
|
||||
extra/advanced-usage/mastering-asyncio
|
||||
|
||||
|
||||
.. _Examples:
|
||||
|
|
|
@ -25,11 +25,3 @@ telethon\.extensions\.html module
|
|||
:members:
|
||||
:undoc-members:
|
||||
: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):
|
||||
"""
|
||||
Iterator over the message history for the specified entity.
|
||||
|
||||
If either `search`, `filter` or `from_user` are provided,
|
||||
: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
|
||||
the API retrieving more than 3000 messages will take longer
|
||||
than half a minute (or even more based on previous calls).
|
||||
|
||||
The limit may also be ``None``, which would eventually return
|
||||
the whole history.
|
||||
|
||||
|
@ -411,8 +411,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods):
|
|||
Whether the existing draft should be cleared or not.
|
||||
Has no effect when sending a file.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
||||
:tl:`KeyboardButton`):
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||
The matrix (list of lists), row list or button to be shown
|
||||
after sending the message. This parameter will only work if
|
||||
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
|
||||
in the message.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
||||
:tl:`KeyboardButton`):
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||
The matrix (list of lists), row list or button to be shown
|
||||
after sending the message. This parameter will only work if
|
||||
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
|
||||
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):
|
||||
Whether to allow using the cached version stored in the
|
||||
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
|
||||
without this setting before for it to work.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
||||
:tl:`KeyboardButton`):
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||
The matrix (list of lists), row list or button to be shown
|
||||
after sending the message. This parameter will only work if
|
||||
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)
|
||||
|
||||
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'
|
||||
' find out more details.'
|
||||
.format(peer)
|
||||
|
|
|
@ -34,8 +34,7 @@ class InlineBuilder:
|
|||
game (`bool`, optional):
|
||||
May be ``True`` to indicate that the game will be sent.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
||||
:tl:`KeyboardButton`, optional):
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`, optional):
|
||||
Same as ``buttons`` for `client.send_message
|
||||
<telethon.client.messages.MessageMethods.send_message>`.
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@ mimetypes.add_type('audio/ogg', '.ogg')
|
|||
USERNAME_RE = re.compile(
|
||||
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
|
||||
# special, very well known bots. This list may be incomplete though:
|
||||
|
@ -646,15 +649,16 @@ def parse_phone(phone):
|
|||
|
||||
|
||||
def parse_username(username):
|
||||
"""Parses the given username or channel access hash, given
|
||||
a string, username or URL. Returns a tuple consisting of
|
||||
both the stripped, lowercase username and whether it is
|
||||
a joinchat/ hash (in which case is not lowercase'd).
|
||||
"""
|
||||
Parses the given username or channel access hash, given
|
||||
a string, username or URL. Returns a tuple consisting of
|
||||
both the stripped, lowercase username and whether it is
|
||||
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()
|
||||
m = USERNAME_RE.match(username)
|
||||
m = USERNAME_RE.match(username) or TG_JOIN_RE.match(username)
|
||||
if m:
|
||||
username = username[m.end():]
|
||||
is_invite = bool(m.group(1))
|
||||
|
|
|
@ -108,6 +108,7 @@ LEARN_PYTHON = (
|
|||
"• [Official Docs](https://docs.python.org/3/tutorial/index.html).\n"
|
||||
"• [Dive Into Python 3](http://www.diveintopython3.net/).\n"
|
||||
"• [Learn Python](https://www.learnpython.org/).\n"
|
||||
"• [Project Python](http://projectpython.net/).\n"
|
||||
"• [Computer Science Circles](https://cscircles.cemc.uwaterloo.ca/).\n"
|
||||
"• [MIT OpenCourse](https://ocw.mit.edu/courses/electrical-engineering-"
|
||||
"and-computer-science/6-0001-introduction-to-computer-science-and-progr"
|
||||
|
|
Loading…
Reference in New Issue
Block a user