Clean up and complete RTD documentation

This commit is contained in:
Lonami Exo
2018-01-05 00:59:53 +01:00
parent 6cb5931e7a
commit cb45e8fca9
29 changed files with 1096 additions and 702 deletions

View File

@@ -1,117 +0,0 @@
.. _accessing-the-full-api:
==========================
Accessing the Full API
==========================
The ``TelegramClient`` doesnt offer a method for every single request
the Telegram API supports. However, its very simple to ``.invoke()``
any request. Whenever you need something, dont forget to `check the
documentation`__ and look for the `method you need`__. There you can go
through a sorted list of everything you can do.
You should also refer to the documentation to see what the objects
(constructors) Telegram returns look like. Every constructor inherits
from a common type, and thats the reason for this distinction.
Say ``client.send_message()`` didnt exist, we could use the `search`__
to look for “message”. There we would find `SendMessageRequest`__,
which we can work with.
Every request is a Python class, and has the parameters needed for you
to invoke it. You can also call ``help(request)`` for information on
what input parameters it takes. Remember to “Copy import to the
clipboard”, or your script wont be aware of this class! Now we have:
.. code-block:: python
from telethon.tl.functions.messages import SendMessageRequest
If youre going to use a lot of these, you may do:
.. code-block:: python
import telethon.tl.functions as tl
# We now have access to 'tl.messages.SendMessageRequest'
We see that this request must take at least two parameters, a ``peer``
of type `InputPeer`__, and a ``message`` which is just a Python
``str``\ ing.
How can we retrieve this ``InputPeer``? We have two options. We manually
`construct one`__, for instance:
.. code-block:: python
from telethon.tl.types import InputPeerUser
peer = InputPeerUser(user_id, user_hash)
Or we call ``.get_input_entity()``:
.. code-block:: python
peer = client.get_input_entity('someone')
When youre going to invoke an API method, most require you to pass an
``InputUser``, ``InputChat``, or so on, this is why using
``.get_input_entity()`` is more straightforward (and sometimes
immediate, if you know the ID of the user for instance). If you also
need to have information about the whole user, use ``.get_entity()``
instead:
.. code-block:: python
entity = client.get_entity('someone')
In the later case, when you use the entity, the library will cast it to
its “input” version for you. If you already have the complete user and
want to cache its input version so the library doesnt have to do this
every time its used, simply call ``.get_input_peer``:
.. code-block:: python
from telethon import utils
peer = utils.get_input_user(entity)
After this small parenthesis about ``.get_entity`` versus
``.get_input_entity``, we have everything we need. To ``.invoke()`` our
request we do:
.. code-block:: python
result = client(SendMessageRequest(peer, 'Hello there!'))
# __call__ is an alias for client.invoke(request). Both will work
Message sent! Of course, this is only an example.
There are nearly 250 methods available as of layer 73,
and you can use every single of them as you wish.
Remember to use the right types! To sum up:
.. code-block:: python
result = client(SendMessageRequest(
client.get_input_entity('username'), 'Hello there!'
))
.. note::
Note that some requests have a "hash" parameter. This is **not** your ``api_hash``!
It likely isn't your self-user ``.access_hash`` either.
It's a special hash used by Telegram to only send a difference of new data
that you don't already have with that request,
so you can leave it to 0, and it should work (which means no hash is known yet).
For those requests having a "limit" parameter,
you can often set it to zero to signify "return as many items as possible".
This won't work for all of them though,
for instance, in "messages.search" it will actually return 0 items.
__ https://lonamiwebs.github.io/Telethon
__ https://lonamiwebs.github.io/Telethon/methods/index.html
__ https://lonamiwebs.github.io/Telethon/?q=message
__ https://lonamiwebs.github.io/Telethon/methods/messages/send_message.html
__ https://lonamiwebs.github.io/Telethon/types/input_peer.html
__ https://lonamiwebs.github.io/Telethon/constructors/input_peer_user.html

View File

@@ -1,24 +1,28 @@
.. _creating-a-client:
===================
=================
Creating a Client
===================
=================
Before working with Telegram's API, you need to get your own API ID and hash:
1. Follow `this link <https://my.telegram.org/>`_ and login with your phone number.
1. Follow `this link <https://my.telegram.org/>`_ and login with your
phone number.
2. Click under API Development tools.
3. A *Create new application* window will appear. Fill in your application details.
There is no need to enter any *URL*, and only the first two fields (*App title* and *Short name*)
can be changed later as far as I'm aware.
3. A *Create new application* window will appear. Fill in your application
details. There is no need to enter any *URL*, and only the first two
fields (*App title* and *Short name*) can currently be changed later.
4. Click on *Create application* at the end. Remember that your **API hash is secret**
and Telegram won't let you revoke it. Don't post it anywhere!
4. Click on *Create application* at the end. Remember that your
**API hash is secret** and Telegram won't let you revoke it.
Don't post it anywhere!
Once that's ready, the next step is to create a ``TelegramClient``.
This class will be your main interface with Telegram's API, and creating one is very simple:
This class will be your main interface with Telegram's API, and creating
one is very simple:
.. code-block:: python
@@ -31,14 +35,18 @@ This class will be your main interface with Telegram's API, and creating one is
client = TelegramClient('some_name', api_id, api_hash)
Note that ``'some_name'`` will be used to save your session (persistent information such as access key and others)
as ``'some_name.session'`` in your disk. This is simply a JSON file which you can (but shouldn't) modify.
Before using the client, you must be connected to Telegram. Doing so is very easy:
Note that ``'some_name'`` will be used to save your session (persistent
information such as access key and others) as ``'some_name.session'`` in
your disk. This is by default a database file using Python's ``sqlite3``.
Before using the client, you must be connected to Telegram.
Doing so is very easy:
``client.connect() # Must return True, otherwise, try again``
You may or may not be authorized yet. You must be authorized before you're able to send any request:
You may or may not be authorized yet. You must be authorized
before you're able to send any request:
``client.is_user_authorized() # Returns True if you can send requests``
@@ -52,13 +60,25 @@ If you're not authorized, you need to ``.sign_in()``:
# If .sign_in raises SessionPasswordNeeded error, call .sign_in(password=...)
# You can import both exceptions from telethon.errors.
``myself`` is your Telegram user.
You can view all the information about yourself by doing ``print(myself.stringify())``.
You're now ready to use the client as you wish!
``myself`` is your Telegram user. You can view all the information about
yourself by doing ``print(myself.stringify())``. You're now ready to use
the client as you wish! Remember that any object returned by the API has
mentioned ``.stringify()`` method, and printing these might prove useful.
As a full example:
.. code-block:: python
client = TelegramClient('anon', api_id, api_hash)
assert client.connect()
if not client.is_user_authorized():
client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: '))
.. note::
If you want to use a **proxy**, you have to `install PySocks`__ (via pip or manual)
and then set the appropriated parameters:
If you want to use a **proxy**, you have to `install PySocks`__
(via pip or manual) and then set the appropriated parameters:
.. code-block:: python
@@ -72,5 +92,58 @@ You're now ready to use the client as you wish!
consisting of parameters described `here`__.
Two Factor Authorization (2FA)
******************************
If you have Two Factor Authorization (from now on, 2FA) enabled on your
account, calling :meth:`telethon.TelegramClient.sign_in` will raise a
`SessionPasswordNeededError`. When this happens, just
:meth:`telethon.TelegramClient.sign_in` again with a ``password=``:
.. code-block:: python
import getpass
from telethon.errors import SessionPasswordNeededError
client.sign_in(phone)
try:
client.sign_in(code=input('Enter code: '))
except SessionPasswordNeededError:
client.sign_in(password=getpass.getpass())
If you don't have 2FA enabled, but you would like to do so through Telethon,
take as example the following code snippet:
.. code-block:: python
import os
from hashlib import sha256
from telethon.tl.functions import account
from telethon.tl.types.account import PasswordInputSettings
new_salt = client(account.GetPasswordRequest()).new_salt
salt = new_salt + os.urandom(8) # new random salt
pw = 'secret'.encode('utf-8') # type your new password here
hint = 'hint'
pw_salted = salt + pw + salt
pw_hash = sha256(pw_salted).digest()
result = client(account.UpdatePasswordSettingsRequest(
current_password_hash=salt,
new_settings=PasswordInputSettings(
new_salt=salt,
new_password_hash=pw_hash,
hint=hint
)
))
Thanks to `Issue 259 <https://github.com/LonamiWebs/Telethon/issues/259>`_
for the tip!
__ https://github.com/Anorov/PySocks#installation
__ https://github.com/Anorov/PySocks#usage-1%3E
__ https://github.com/Anorov/PySocks#usage-1%3E

View File

@@ -0,0 +1,87 @@
=========================
Users, Chats and Channels
=========================
Introduction
************
The library widely uses the concept of "entities". An entity will refer
to any ``User``, ``Chat`` or ``Channel`` object that the API may return
in response to certain methods, such as ``GetUsersRequest``.
To save bandwidth, the API also makes use of their "input" versions.
The input version of an entity (e.g. ``InputPeerUser``, ``InputChat``,
etc.) only contains the minimum required information that's required
for Telegram to be able to identify who you're referring to: their ID
and hash. This ID/hash pair is unique per user, so if you use the pair
given by another user **or bot** it will **not** work.
To save *even more* bandwidth, the API also makes use of the ``Peer``
versions, which just have an ID. This serves to identify them, but
peers alone are not enough to use them. You need to know their hash
before you can "use them".
Luckily, the library tries to simplify this mess the best it can.
Getting entities
****************
Through the use of the :ref:`sessions`, the library will automatically
remember the ID and hash pair, along with some extra information, so
you're able to just do this:
.. code-block:: python
# dialogs are the "conversations you have open"
# this method returns a list of Dialog, which
# have the .entity attribute and other information.
dialogs = client.get_dialogs(limit=200)
# all of these work and do the same
lonami = client.get_entity('lonami')
lonami = client.get_entity('t.me/lonami')
lonami = client.get_entity('https://telegram.dog/lonami')
# other kind of entities
channel = client.get_entity('telegram.me/joinchat/AAAAAEkk2WdoDrB4-Q8-gg')
contact = client.get_entity('+34xxxxxxxxx')
friend = client.get_entity(friend_id)
# using peers/input peers (note that the API may return these)
# users, chats and channels may all have the same ID, so it's
# necessary to wrap (at least) chat and channels inside Peer.
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
my_user = client.get_entity(PeerUser(some_id))
my_chat = client.get_entity(PeerChat(some_id))
my_channel = client.get_entity(PeerChannel(some_id))
All methods in the :ref:`telegram-client` call ``.get_entity()`` to further
save you from the hassle of doing so manually, so doing things like
``client.send_message('lonami', 'hi!')`` is possible.
Every entity the library "sees" (in any response to any call) will by
default be cached in the ``.session`` file, to avoid performing
unnecessary API calls. If the entity cannot be found, some calls
like ``ResolveUsernameRequest`` or ``GetContactsRequest`` may be
made to obtain the required information.
Entities vs. Input Entities
***************************
As we mentioned before, API calls don't need to know the whole information
about the entities, only their ID and hash. For this reason, another method,
``.get_input_entity()`` is available. This will always use the cache while
possible, making zero API calls most of the time. When a request is made,
if you provided the full entity, e.g. an ``User``, the library will convert
it to the required ``InputPeer`` automatically for you.
**You should always favour ``.get_input_entity()``** over ``.get_entity()``
for this reason! Calling the latter will always make an API call to get
the most recent information about said entity, but invoking requests don't
need this information, just the ``InputPeer``. Only use ``.get_entity()``
if you need to get actual information, like the username, name, title, etc.
of the entity.

View File

@@ -3,13 +3,13 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
===============
Getting Started
===============
=================
Getting Started!
=================
Simple Installation
*********************
*******************
``pip install telethon``
@@ -17,7 +17,7 @@ Simple Installation
Creating a client
**************
*****************
.. code-block:: python
@@ -39,8 +39,9 @@ Creating a client
**More details**: :ref:`creating-a-client`
Simple Stuff
**************
Basic Usage
***********
.. code-block:: python
print(me.stringify())
@@ -52,3 +53,5 @@ Simple Stuff
total, messages, senders = client.get_message_history('username')
client.download_media(messages[0])
**More details**: :ref:`telegram-client`

View File

@@ -1,18 +1,20 @@
.. _installation:
=================
============
Installation
=================
============
Automatic Installation
^^^^^^^^^^^^^^^^^^^^^^^
**********************
To install Telethon, simply do:
``pip install telethon``
If you get something like ``"SyntaxError: invalid syntax"`` or any other error while installing,
it's probably because ``pip`` defaults to Python 2, which is not supported. Use ``pip3`` instead.
If you get something like ``"SyntaxError: invalid syntax"`` or any other
error while installing/importing the library, it's probably because ``pip``
defaults to Python 2, which is not supported. Use ``pip3`` instead.
If you already have the library installed, upgrade with:
@@ -20,7 +22,7 @@ If you already have the library installed, upgrade with:
You can also install the library directly from GitHub or a fork:
.. code-block:: python
.. code-block:: sh
# pip install git+https://github.com/LonamiWebs/Telethon.git
or
@@ -32,13 +34,15 @@ If you don't have root access, simply pass the ``--user`` flag to the pip comman
Manual Installation
^^^^^^^^^^^^^^^^^^^^
*******************
1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and ``rsa`` (`GitHub`__ | `PyPi`__) modules:
1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and
``rsa`` (`GitHub`__ | `PyPi`__) modules:
``sudo -H pip install pyaes rsa``
2. Clone Telethon's GitHub repository: ``git clone https://github.com/LonamiWebs/Telethon.git``
2. Clone Telethon's GitHub repository:
``git clone https://github.com/LonamiWebs/Telethon.git``
3. Enter the cloned repository: ``cd Telethon``
@@ -50,22 +54,15 @@ To generate the documentation, ``cd docs`` and then ``python3 generate.py``.
Optional dependencies
^^^^^^^^^^^^^^^^^^^^^^^^
If you're using the library under ARM (or even if you aren't),
you may want to install ``sympy`` through ``pip`` for a substantial speed-up
when generating the keys required to connect to Telegram
(you can of course do this on desktop too). See `issue #199`__ for more.
If ``libssl`` is available on your system, it will also be used wherever encryption is needed.
If neither of these are available, a pure Python callback will be used instead,
so you can still run the library wherever Python is available!
*********************
If ``libssl`` is available on your system, it will be used wherever encryption
is needed, but otherwise it will fall back to pure Python implementation so it
will also work without it.
__ https://github.com/ricmoo/pyaes
__ https://pypi.python.org/pypi/pyaes
__ https://github.com/sybrenstuvel/python-rsa/
__ https://pypi.python.org/pypi/rsa/3.4.2
__ https://github.com/LonamiWebs/Telethon/issues/199
__ https://github.com/LonamiWebs/Telethon/issues/199

View File

@@ -1,55 +0,0 @@
.. _sending-requests:
==================
Sending Requests
==================
Since we're working with Python, one must not forget that they can do ``help(client)`` or ``help(TelegramClient)``
at any time for a more detailed description and a list of all the available methods.
Calling ``help()`` from an interactive Python session will always list all the methods for any object, even yours!
Interacting with the Telegram API is done through sending **requests**,
this is, any "method" listed on the API. There are a few methods on the ``TelegramClient`` class
that abstract you from the need of manually importing the requests you need.
For instance, retrieving your own user can be done in a single line:
``myself = client.get_me()``
Internally, this method has sent a request to Telegram, who replied with the information about your own user.
If you want to retrieve any other user, chat or channel (channels are a special subset of chats),
you want to retrieve their "entity". This is how the library refers to either of these:
.. code-block:: python
# The method will infer that you've passed an username
# It also accepts phone numbers, and will get the user
# from your contact list.
lonami = client.get_entity('lonami')
Note that saving and using these entities will be more important when Accessing the Full API.
For now, this is a good way to get information about an user or chat.
Other common methods for quick scripts are also available:
.. code-block:: python
# Sending a message (use an entity/username/etc)
client.send_message('TheAyyBot', 'ayy')
# Sending a photo, or a file
client.send_file(myself, '/path/to/the/file.jpg', force_document=True)
# Downloading someone's profile photo. File is saved to 'where'
where = client.download_profile_photo(someone)
# Retrieving the message history
total, messages, senders = client.get_message_history(someone)
# Downloading the media from a specific message
# You can specify either a directory, a filename, or nothing at all
where = client.download_media(message, '/path/to/output')
Remember that you can call ``.stringify()`` to any object Telegram returns to pretty print it.
Calling ``str(result)`` does the same operation, but on a single line.

View File

@@ -1,48 +0,0 @@
.. _sessions:
==============
Session Files
==============
The first parameter you pass the constructor of the
``TelegramClient`` is the ``session``, and defaults to be the session
name (or full path). That is, if you create a ``TelegramClient('anon')``
instance and connect, an ``anon.session`` file will be created on the
working directory.
These JSON session files contain the required information to talk to the
Telegram servers, such as to which IP the client should connect, port,
authorization key so that messages can be encrypted, and so on.
These files will by default also save all the input entities that youve
seen, so that you can get information about an user or channel by just
their ID. Telegram will **not** send their ``access_hash`` required to
retrieve more information about them, if it thinks you have already seem
them. For this reason, the library needs to store this information
offline.
The library will by default too save all the entities (users with their
name, username, chats and so on) **in memory**, not to disk, so that you
can quickly access them by username or phone number. This can be
disabled too. Run ``help(client.session.entities)`` to see the available
methods (or ``help(EntityDatabase)``).
If youre not going to work without updates, or dont need to cache the
``access_hash`` associated with the entities ID, you can disable this
by setting ``client.session.save_entities = False``.
If you dont want to save the files as JSON, you can also create your
custom ``Session`` subclass and override the ``.save()`` and ``.load()``
methods. For example, you could save it on a database:
.. code-block:: python
class DatabaseSession(Session):
def save():
# serialize relevant data to the database
def load():
# load relevant data to the database
You should read the ``session.py`` source file to know what “relevant
data” you need to keep track of.

View File

@@ -0,0 +1,99 @@
.. _telegram-client:
==============
TelegramClient
==============
Introduction
************
The ``TelegramClient`` is the central class of the library, the one
you will be using most of the time. For this reason, it's important
to know what it offers.
Since we're working with Python, one must not forget that we can do
``help(client)`` or ``help(TelegramClient)`` at any time for a more
detailed description and a list of all the available methods. Calling
``help()`` from an interactive Python session will always list all the
methods for any object, even yours!
Interacting with the Telegram API is done through sending **requests**,
this is, any "method" listed on the API. There are a few methods (and
growing!) on the ``TelegramClient`` class that abstract you from the
need of manually importing the requests you need.
For instance, retrieving your own user can be done in a single line:
``myself = client.get_me()``
Internally, this method has sent a request to Telegram, who replied with
the information about your own user, and then the desired information
was extracted from their response.
If you want to retrieve any other user, chat or channel (channels are a
special subset of chats), you want to retrieve their "entity". This is
how the library refers to either of these:
.. code-block:: python
# The method will infer that you've passed an username
# It also accepts phone numbers, and will get the user
# from your contact list.
lonami = client.get_entity('lonami')
The so called "entities" are another important whole concept on its own,
and you should
Note that saving and using these entities will be more important when
Accessing the Full API. For now, this is a good way to get information
about an user or chat.
Other common methods for quick scripts are also available:
.. code-block:: python
# Sending a message (use an entity/username/etc)
client.send_message('TheAyyBot', 'ayy')
# Sending a photo, or a file
client.send_file(myself, '/path/to/the/file.jpg', force_document=True)
# Downloading someone's profile photo. File is saved to 'where'
where = client.download_profile_photo(someone)
# Retrieving the message history
messages = client.get_message_history(someone)
# Downloading the media from a specific message
# You can specify either a directory, a filename, or nothing at all
where = client.download_media(message, '/path/to/output')
# Call .disconnect() when you're done
client.disconnect()
Remember that you can call ``.stringify()`` to any object Telegram returns
to pretty print it. Calling ``str(result)`` does the same operation, but on
a single line.
Available methods
*****************
This page lists all the "handy" methods available for you to use in the
``TelegramClient`` class. These are simply wrappers around the "raw"
Telegram API, making it much more manageable and easier to work with.
Please refer to :ref:`accessing-the-full-api` if these aren't enough,
and don't be afraid to read the source code of the InteractiveTelegramClient_
or even the TelegramClient_ itself to learn how it works.
.. _InteractiveTelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/interactive_telegram_client.py
.. _TelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon/telegram_client.py
.. automodule:: telethon.telegram_client
:members:
:undoc-members:
:show-inheritance:

View File

@@ -14,23 +14,24 @@ The library can run in four distinguishable modes:
- With several worker threads that run your update handlers.
- A mix of the above.
Since this section is about updates, we'll describe the simplest way to work with them.
.. warning::
Remember that you should always call ``client.disconnect()`` once you're done.
Since this section is about updates, we'll describe the simplest way to
work with them.
Using multiple workers
^^^^^^^^^^^^^^^^^^^^^^^
**********************
When you create your client, simply pass a number to the ``update_workers`` parameter:
When you create your client, simply pass a number to the
``update_workers`` parameter:
``client = TelegramClient('session', api_id, api_hash, update_workers=4)``
4 workers should suffice for most cases (this is also the default on `Python Telegram Bot`__).
You can set this value to more, or even less if you need.
4 workers should suffice for most cases (this is also the default on
`Python Telegram Bot`__). You can set this value to more, or even less
if you need.
The next thing you want to do is to add a method that will be called when an `Update`__ arrives:
The next thing you want to do is to add a method that will be called when
an `Update`__ arrives:
.. code-block:: python
@@ -41,7 +42,8 @@ The next thing you want to do is to add a method that will be called when an `Up
# do more work here, or simply sleep!
That's it! Now let's do something more interesting.
Every time an user talks to use, let's reply to them with the same text reversed:
Every time an user talks to use, let's reply to them with the same
text reversed:
.. code-block:: python
@@ -56,16 +58,18 @@ Every time an user talks to use, let's reply to them with the same text reversed
input('Press enter to stop this!')
client.disconnect()
We only ask you one thing: don't keep this running for too long, or your contacts will go mad.
We only ask you one thing: don't keep this running for too long, or your
contacts will go mad.
Spawning no worker at all
^^^^^^^^^^^^^^^^^^^^^^^^^^
*************************
All the workers do is loop forever and poll updates from a queue that is filled from the ``ReadThread``,
responsible for reading every item off the network.
If you only need a worker and the ``MainThread`` would be doing no other job,
this is the preferred way. You can easily do the same as the workers like so:
All the workers do is loop forever and poll updates from a queue that is
filled from the ``ReadThread``, responsible for reading every item off
the network. If you only need a worker and the ``MainThread`` would be
doing no other job, this is the preferred way. You can easily do the same
as the workers like so:
.. code-block:: python
@@ -81,24 +85,27 @@ this is the preferred way. You can easily do the same as the workers like so:
client.disconnect()
Note that ``poll`` accepts a ``timeout=`` parameter,
and it will return ``None`` if other thread got the update before you could or if the timeout expired,
so it's important to check ``if not update``.
Note that ``poll`` accepts a ``timeout=`` parameter, and it will return
``None`` if other thread got the update before you could or if the timeout
expired, so it's important to check ``if not update``.
This can coexist with the rest of ``N`` workers, or you can set it to ``0`` additional workers:
This can coexist with the rest of ``N`` workers, or you can set it to ``0``
additional workers:
``client = TelegramClient('session', api_id, api_hash, update_workers=0)``
You **must** set it to ``0`` (or other number), as it defaults to ``None`` and there is a different.
``None`` workers means updates won't be processed *at all*,
so you must set it to some value (0 or greater) if you want ``client.updates.poll()`` to work.
You **must** set it to ``0`` (or other number), as it defaults to ``None``
and there is a different. ``None`` workers means updates won't be processed
*at all*, so you must set it to some value (``0`` or greater) if you want
``client.updates.poll()`` to work.
Using the main thread instead the ``ReadThread``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
************************************************
If you have no work to do on the ``MainThread`` and you were planning to have a ``while True: sleep(1)``,
don't do that. Instead, don't spawn the secondary ``ReadThread`` at all like so:
If you have no work to do on the ``MainThread`` and you were planning to have
a ``while True: sleep(1)``, don't do that. Instead, don't spawn the secondary
``ReadThread`` at all like so:
.. code-block:: python
@@ -111,8 +118,8 @@ And then ``.idle()`` from the ``MainThread``:
``client.idle()``
You can stop it with :kbd:`Control+C`,
and you can configure the signals to be used in a similar fashion to `Python Telegram Bot`__.
You can stop it with :kbd:`Control+C`, and you can configure the signals
to be used in a similar fashion to `Python Telegram Bot`__.
As a complete example:
@@ -132,4 +139,4 @@ As a complete example:
__ https://python-telegram-bot.org/
__ https://lonamiwebs.github.io/Telethon/types/update.html
__ https://github.com/python-telegram-bot/python-telegram-bot/blob/4b3315db6feebafb94edcaa803df52bb49999ced/telegram/ext/updater.py#L460
__ https://github.com/python-telegram-bot/python-telegram-bot/blob/4b3315db6feebafb94edcaa803df52bb49999ced/telegram/ext/updater.py#L460