The Full API¶
Important
While you have access to this, you should always use the friendly methods listed on Client Reference unless you have a better reason not to, like a method not existing or you wanting more control.
Contents
Introduction¶
The TelegramClient 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.
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:
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:
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:
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:
print(result.stringify())
This will print out the following:
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:
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:
print(response.msg_id)
Example walkthrough¶
Say client.send_message()
didn’t 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 won’t be aware of this class! Now we have:
from telethon.tl.functions.messages import SendMessageRequest
If you’re going to use a lot of these, you may do:
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 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:
from telethon.tl.types import InputPeerUser
peer = InputPeerUser(user_id, user_hash)
Or we call client.get_input_entity()
:
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
InputUser, InputChat, or so on, this is why using
client.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()
instead:
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
:
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
for you when
required, but it’s good to remember what’s happening.
After this small parenthesis about client.get_entity
versus
client.get_input_entity()
,
we have everything we need. To invoke our
request we do:
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:
result = await client(SendMessageRequest(
await client.get_input_entity('username'), 'Hello there!'
))
This can further be simplified to:
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:
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:
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:
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]