mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-12-01 13:53:45 +03:00
421 lines
15 KiB
ReStructuredText
421 lines
15 KiB
ReStructuredText
.. _full-api:
|
|
|
|
============
|
|
The Full API
|
|
============
|
|
|
|
.. important::
|
|
|
|
While you have access to this, you should always use the friendly
|
|
methods listed on :ref:`client-ref` unless you have a better reason
|
|
not to, like a method not existing or you wanting more control.
|
|
|
|
.. contents::
|
|
|
|
|
|
Introduction
|
|
============
|
|
|
|
The :ref:`telethon-client` doesn't offer a method for every single request
|
|
the Telegram API supports. However, it's very simple to *call* or *invoke*
|
|
any request defined in Telegram's API.
|
|
|
|
This section will teach you how to use what Telethon calls the `TL reference`_.
|
|
The linked page contains a list and a way to search through *all* types
|
|
generated from the definition of Telegram's API (in ``.tl`` file format,
|
|
hence the name). These types include requests and constructors.
|
|
|
|
.. note::
|
|
|
|
The reason to keep both https://tl.telethon.dev and this
|
|
documentation alive is that the former allows instant search results
|
|
as you type, and a "Copy import" button. If you like namespaces, you
|
|
can also do ``from telethon.tl import types, functions``. Both work.
|
|
|
|
Telegram makes these ``.tl`` files public, which other implementations, such
|
|
as Telethon, can also use to generate code. These files are versioned under
|
|
what's called "layers". ``.tl`` files consist of thousands of definitions,
|
|
and newer layers often add, change, or remove them. Each definition refers
|
|
to either a Remote Procedure Call (RPC) function, or a type (which the
|
|
`TL reference`_ calls "constructors", as they construct particular type
|
|
instances).
|
|
|
|
As such, the `TL reference`_ is a good place to go to learn about all possible
|
|
requests, types, and what they look like. If you're curious about what's been
|
|
changed between layers, you can refer to the `TL diff`_ site.
|
|
|
|
|
|
Navigating the TL reference
|
|
===========================
|
|
|
|
Functions
|
|
---------
|
|
|
|
"Functions" is the term used for the Remote Procedure Calls (RPC) that can be
|
|
sent to Telegram to ask it to perform something (e.g. "send message"). These
|
|
requests have an associated return type. These can be invoked ("called"):
|
|
|
|
.. code-block:: python
|
|
|
|
client = TelegramClient(...)
|
|
function_instance = SomeRequest(...)
|
|
|
|
# Invoke the request
|
|
returned_type = await client(function_instance)
|
|
|
|
Whenever you find the type for a function in the `TL reference`_, the page
|
|
will contain the following information:
|
|
|
|
* What type of account can use the method. This information is regenerated
|
|
from time to time (by attempting to invoke the function under both account
|
|
types and finding out where it fails). Some requests can only be used by
|
|
bot accounts, others by user accounts, and others by both.
|
|
* The TL definition. This helps you get a feel for the what the function
|
|
looks like. This is not Python code. It just contains the definition in
|
|
a concise manner.
|
|
* "Copy import" button. Does what it says: it will copy the necessary Python
|
|
code to import the function to your system's clipboard for easy access.
|
|
* Returns. The returned type. When you invoke the function, this is what the
|
|
result will be. It also includes which of the constructors can be returned
|
|
inline, to save you a click.
|
|
* Parameters. The parameters accepted by the function, including their type,
|
|
whether they expect a list, and whether they're optional.
|
|
* Known RPC errors. A best-effort list of known errors the request may cause.
|
|
This list is not complete and may be out of date, but should provide an
|
|
overview of what could go wrong.
|
|
* Example. Autogenerated example, showcasing how you may want to call it.
|
|
Bear in mind that this is *autogenerated*. It may be spitting out non-sense.
|
|
The goal of this example is not to show you everything you can do with the
|
|
request, only to give you a feel for what it looks like to use it.
|
|
|
|
It is very important to click through the links and navigate to get the full
|
|
picture. A specific page will show you what the specific function returns and
|
|
needs as input parameters. But it may reference other types, so you need to
|
|
navigate to those to learn what those contain or need.
|
|
|
|
Types
|
|
-----
|
|
|
|
"Types" as understood by TL are not actually generated in Telethon.
|
|
They would be the "abstract base class" of the constructors, but since Python
|
|
is duck-typed, there is hardly any need to generate mostly unnecessary code.
|
|
The page for a type contains:
|
|
|
|
* Constructors. Every type will have one or more constructors. These
|
|
constructors *are* generated and can be immported and used.
|
|
* Requests returning this type. A helpful way to find out "what requests can
|
|
return this?". This is how you may learn what request you need to use to
|
|
obtain a particular instance of a type.
|
|
* Requests accepting this type as input. A helpful way to find out "what
|
|
requests can use this type as one of their input parameters?". This is how
|
|
you may learn where a type is used.
|
|
* Other types containing this type. A helpful way to find out "where else
|
|
does this type appear?". This is how you can walk back through nested
|
|
objects.
|
|
|
|
Constructors
|
|
------------
|
|
|
|
Constructors are used to create instances of a particular type, and are also
|
|
returned when invoking requests. You will have to create instances yourself
|
|
when invoking requests that need a particular type as input.
|
|
The page for a constructor contains:
|
|
|
|
* Belongs to. The parent type. This is a link back to the types page for the
|
|
specific constructor. It also contains the sibling constructors inline, to
|
|
save you a click.
|
|
* Members. Both the input parameters *and* fields the constructor contains.
|
|
|
|
|
|
Using the TL reference
|
|
======================
|
|
|
|
After you've found a request you want to send, a good start would be to simply
|
|
copy and paste the autogenerated example into your script. Then you can simply
|
|
tweak it to your needs.
|
|
|
|
If you want to do it from scratch, first, make sure to import the request into
|
|
your code (either using the "Copy import" button near the top, or by manually
|
|
spelling out the package under ``telethon.tl.functions.*``).
|
|
|
|
Then, start reading the parameters one by one. If the parameter cannot be
|
|
omitted, you **will** need to specify it, so make sure to spell it out as
|
|
an input parameter when constructing the request instance. Let's look at
|
|
`PingRequest`_ for example. First, we copy the import:
|
|
|
|
.. code-block:: python
|
|
|
|
from telethon.tl.functions import PingRequest
|
|
|
|
Then, we look at the parameters:
|
|
|
|
ping_id - long
|
|
|
|
A single parameter, and it's a long (a integer number with a large range of
|
|
values). It doesn't say it can be omitted, so we must provide it, like so:
|
|
|
|
.. code-block:: python
|
|
|
|
PingRequest(
|
|
ping_id=48641868471
|
|
)
|
|
|
|
(In this case, the ping ID is a random number. You often have to guess what
|
|
the parameter needs just by looking at the name.)
|
|
|
|
Now that we have our request, we can invoke it:
|
|
|
|
.. code-block:: python
|
|
|
|
response = await client(PingRequest(
|
|
ping_id=48641868471
|
|
))
|
|
|
|
To find out what ``response`` looks like, we can do as the autogenerated
|
|
example suggests and "stringify" the result as a pretty-printed string:
|
|
|
|
.. code-block:: python
|
|
|
|
print(result.stringify())
|
|
|
|
This will print out the following:
|
|
|
|
.. code-block:: python
|
|
|
|
Pong(
|
|
msg_id=781875678118,
|
|
ping_id=48641868471
|
|
)
|
|
|
|
Which is a very easy way to get a feel for a response. You should nearly
|
|
always print the stringified result, at least once, when trying out requests,
|
|
to get a feel for what the response may look like.
|
|
|
|
But of course, you don't need to do that. Without writing any code, you could
|
|
have navigated through the "Returns" link to learn ``PingRequest`` returns a
|
|
``Pong``, which only has one constructor, and the constructor has two members,
|
|
``msg_id`` and ``ping_id``.
|
|
|
|
If you wanted to create your own ``Pong``, you would use both members as input
|
|
parameters:
|
|
|
|
.. code-block:: python
|
|
|
|
my_pong = Pong(
|
|
msg_id=781875678118,
|
|
ping_id=48641868471
|
|
)
|
|
|
|
(Yes, constructing object instances can use the same code that ``.stringify``
|
|
would return!)
|
|
|
|
And if you wanted to access the ``msg_id`` member, you would simply access it
|
|
like any other attribute access in Python:
|
|
|
|
.. code-block:: python
|
|
|
|
print(response.msg_id)
|
|
|
|
|
|
Example walkthrough
|
|
===================
|
|
|
|
Say `client.send_message()
|
|
<telethon.client.messages.MessageMethods.send_message>` didn't exist,
|
|
we could `use the search`_ to look for "message". There we would find
|
|
:tl:`SendMessageRequest`, which we can work with.
|
|
|
|
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 won't be aware of this class! Now we have:
|
|
|
|
.. code-block:: python
|
|
|
|
from telethon.tl.functions.messages import SendMessageRequest
|
|
|
|
If you're going to use a lot of these, you may do:
|
|
|
|
.. code-block:: python
|
|
|
|
from telethon.tl import types, functions
|
|
# We now have access to 'functions.messages.SendMessageRequest'
|
|
|
|
We see that this request must take at least two parameters, a ``peer``
|
|
of type :tl:`InputPeer`, and a ``message`` which is just a Python
|
|
`str`\ ing.
|
|
|
|
How can we retrieve this :tl:`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 `client.get_input_entity()
|
|
<telethon.client.users.UserMethods.get_input_entity>`:
|
|
|
|
.. code-block:: python
|
|
|
|
import telethon
|
|
|
|
async def main():
|
|
peer = await client.get_input_entity('someone')
|
|
|
|
client.loop.run_until_complete(main())
|
|
|
|
.. note::
|
|
|
|
Remember that ``await`` must occur inside an ``async def``.
|
|
Every full API example assumes you already know and do this.
|
|
|
|
|
|
When you're going to invoke an API method, most require you to pass an
|
|
:tl:`InputUser`, :tl:`InputChat`, or so on, this is why using
|
|
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
|
|
is more straightforward (and often immediate, if you've seen the user before,
|
|
know their ID, etc.). If you also **need** to have information about the whole
|
|
user, use `client.get_entity() <telethon.client.users.UserMethods.get_entity>`
|
|
instead:
|
|
|
|
.. code-block:: python
|
|
|
|
entity = await client.get_entity('someone')
|
|
|
|
In the later case, when you use the entity, the library will cast it to
|
|
its "input" version for you. If you already have the complete user and
|
|
want to cache its input version so the library doesn't have to do this
|
|
every time its used, simply call `telethon.utils.get_input_peer`:
|
|
|
|
.. code-block:: python
|
|
|
|
from telethon import utils
|
|
peer = utils.get_input_peer(entity)
|
|
|
|
|
|
.. 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.
|
|
|
|
After this small parenthesis about `client.get_entity
|
|
<telethon.client.users.UserMethods.get_entity>` versus
|
|
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`,
|
|
we have everything we need. To invoke our
|
|
request we do:
|
|
|
|
.. code-block:: python
|
|
|
|
result = await client(SendMessageRequest(peer, 'Hello there!'))
|
|
|
|
Message sent! Of course, this is only an example. There are over 250
|
|
methods available as of layer 80, and you can use every single of them
|
|
as you wish. Remember to use the right types! To sum up:
|
|
|
|
.. code-block:: python
|
|
|
|
result = await client(SendMessageRequest(
|
|
await client.get_input_entity('username'), 'Hello there!'
|
|
))
|
|
|
|
|
|
This can further be simplified to:
|
|
|
|
.. code-block:: python
|
|
|
|
result = await client(SendMessageRequest('username', 'Hello there!'))
|
|
# Or even
|
|
result = await client(SendMessageRequest(PeerChannel(id), '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 default amount". This won't work for all of them
|
|
though, for instance, in "messages.search" it will actually return 0 items.
|
|
|
|
|
|
Requests in Parallel
|
|
====================
|
|
|
|
The library will automatically merge outgoing requests into a single
|
|
*container*. Telegram's API supports sending multiple requests in a
|
|
single container, which is faster because it has less overhead and
|
|
the server can run them without waiting for others. You can also
|
|
force using a container manually:
|
|
|
|
.. code-block:: python
|
|
|
|
async def main():
|
|
|
|
# Letting the library do it behind the scenes
|
|
await asyncio.wait([
|
|
client.send_message('me', 'Hello'),
|
|
client.send_message('me', ','),
|
|
client.send_message('me', 'World'),
|
|
client.send_message('me', '.')
|
|
])
|
|
|
|
# Manually invoking many requests at once
|
|
await client([
|
|
SendMessageRequest('me', 'Hello'),
|
|
SendMessageRequest('me', ', '),
|
|
SendMessageRequest('me', 'World'),
|
|
SendMessageRequest('me', '.')
|
|
])
|
|
|
|
Note that you cannot guarantee the order in which they are run.
|
|
Try running the above code more than one time. You will see the
|
|
order in which the messages arrive is different.
|
|
|
|
If you use the raw API (the first option), you can use ``ordered``
|
|
to tell the server that it should run the requests sequentially.
|
|
This will still be faster than going one by one, since the server
|
|
knows all requests directly:
|
|
|
|
.. code-block:: python
|
|
|
|
await client([
|
|
SendMessageRequest('me', 'Hello'),
|
|
SendMessageRequest('me', ', '),
|
|
SendMessageRequest('me', 'World'),
|
|
SendMessageRequest('me', '.')
|
|
], ordered=True)
|
|
|
|
If any of the requests fails with a Telegram error (not connection
|
|
errors or any other unexpected events), the library will raise
|
|
`telethon.errors.common.MultiError`. You can ``except`` this
|
|
and still access the successful results:
|
|
|
|
.. code-block:: python
|
|
|
|
from telethon.errors import MultiError
|
|
|
|
try:
|
|
await client([
|
|
SendMessageRequest('me', 'Hello'),
|
|
SendMessageRequest('me', ''),
|
|
SendMessageRequest('me', 'World')
|
|
], ordered=True)
|
|
except MultiError as e:
|
|
# The first and third requests worked.
|
|
first = e.results[0]
|
|
third = e.results[2]
|
|
# The second request failed.
|
|
second = e.exceptions[1]
|
|
|
|
.. _TL reference: https://tl.telethon.dev
|
|
.. _TL diff: https://diff.telethon.dev
|
|
.. _PingRequest: https://tl.telethon.dev/methods/ping.html
|
|
.. _use the search: https://tl.telethon.dev/?q=message&redirect=no
|