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 Changelog (Version History) is there to tell you when these important changes happen.


Some decisions when developing will inevitable be proven wrong in the future. One of these decisions was using threads. Now that Python 3.4 is reaching EOL and using asyncio is usable as of Python 3.5 it makes sense for a library like Telethon to make a good use of it.

If you have old code, just use old versions of the library! There is nothing wrong with that other than not getting new updates or fixes, but using a fixed version with pip install telethon== is easy enough to do.

You might want to consider using Virtual Environments in your projects.

There’s no point in maintaining a synchronous version because the whole point is that people don’t have time to upgrade, and there has been several changes and clean-ups. Using an older version is the right way to go.

Sometimes, other small decisions are made. These all will be reflected in the Changelog (Version History) which you should read when upgrading.

If you want to jump the asyncio boat, here are some of the things you will need to start migrating really old code:

# 1. Import the client from telethon.sync
from telethon.sync import TelegramClient

# 2. Change this monster...
    assert client.connect()
    if not client.is_user_authorized():
        me = client.sign_in(phone_number, input('Enter code: '))

    ...  # REST OF YOUR CODE

# ...for this:
with client:
    ...  # REST OF YOUR CODE

# 3. client.idle() no longer exists.
# Change this...
# this:

# 4. client.add_update_handler no longer exists.
# Change this...
# this:

In addition, all the update handlers must be async def, and you need to await method calls that rely on network requests, such as getting the chat or sender. If you don’t use updates, you’re done!



The entire documentation assumes you have done one of the following:

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 asyncio and just work with sequential code. This can prove to be a powerful hybrid for running under the Python REPL too.

from telethon.sync import TelegramClient
#            ^~~~~ note this part; it will manage the asyncio loop for you

with TelegramClient(...) as client:
    #     ^ 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

    # You can also have an hybrid between a synchronous
    # part and asynchronous event handlers.
    from telethon import events
    async def handler(event):
        await event.reply('hey')


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.


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.


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 telethon.sync and work inside an async def:

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()

        async def handler(event):
            await event.reply('hey')

        await client.run_until_disconnected()

loop = asyncio.get_event_loop()

The telethon.sync magic module simply wraps every method behind:

loop = asyncio.get_event_loop()

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.


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 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.