Skip to content

Pyrogram ↔ tdata

opentele-ng converts tdata to and from Pyrogram sessions, in addition to Telethon.

Pyrogram is an optional dependency:

pip install "opentele-ng[pyrogram]"

That pulls pyrofork, the maintained distribution of the pyrogram package. Vanilla pyrogram and kurigram provide the same import name and work as well. Nothing is imported unless you call one of the methods below, so a plain pip install opentele-ng is unaffected.

tdata → Pyrogram

import asyncio
from opentele.api import API
from opentele.td import TDesktop


async def main() -> None:
    tdesk = TDesktop(r"C:\Users\<user>\AppData\Roaming\Telegram Desktop\tdata")

    # Reuses the existing authorization — no QR, no OTP.
    client = await tdesk.ToPyrogram(api=API.TelegramDesktop)

    async with client:
        print(await client.get_me())


asyncio.run(main())

The returned client is in-memory by default, so nothing is written to disk. To get a .session file instead:

client = await tdesk.ToPyrogram(name="me", in_memory=False, workdir="/tmp/sessions")

That writes <name>.session in workdir. Omit workdir and the file lands next to your entry script (sys.argv[0]'s directory, Pyrogram's default) — not in the process CWD. If the file already exists you get FileExistsError: Pyrogram would keep its peers / usernames / update_state tables and the new session would inherit the previous account's cached contacts. Pass overwrite=True to replace the file outright, or pick another name.

Any other keyword goes straight to pyrogram.Client, e.g. proxy={...}.

With flag=CreateNewSession the QR login is performed by an internal Telethon client, and **kwargs never reach it — Telethon and Pyrogram disagree on the shape of proxy (tuple vs dict). Configure that leg separately, or the authorization goes out over a direct connection:

client = await tdesk.ToPyrogram(
    flag=CreateNewSession,
    api=API.TelegramAndroid,
    proxy={"scheme": "socks5", "hostname": "127.0.0.1", "port": 9050},
    telethon_kwargs={"proxy": ("socks5", "127.0.0.1", 9050)},
)

app_version follows Pyrogram's MTProto layer

Pyrogram ships its own generated schema, so it announces a different layer than Telethon (pyrofork 2.3.69 → 220, Telethon 1.44 → 227). A Telegram Desktop app_version is therefore re-derived for Pyrogram's layer, so the version you claim matches the layer on the wire. Pass align_layer=False to keep api.app_version exactly as given. Android / iOS / web fingerprints are never touched.

Why this path carries a better fingerprint than Telethon

Telethon cannot set lang_pack, which every official client sends. Pyrogram accepts it, so ToPyrogram() puts the whole APIData fingerprint on the wire — api_id, api_hash, device_model, system_version, app_version, lang_code, system_lang_code and lang_pack.

Pyrogram → tdata

import asyncio
from opentele.td import TDesktop
from pyrogram import Client


async def main() -> None:
    client = Client("my_account", api_id=..., api_hash=...)

    tdesk = await TDesktop.FromPyrogram(client)
    tdesk.SaveTData("new_tdata")


asyncio.run(main())

The client does not need to be connected — credentials are read from its storage (and the storage is left exactly as it was found: opened only if it was closed, and closed again afterwards).

It does need to be a session tdata can represent:

Session Result
Authorized user session converted
No auth_key PyrogramUnauthorized — nothing to convert
Test-DC session (test_mode) PyrogramSessionUnsupportedtdata written here always carries the production MTP config, so the key would be dead
Bot session (is_bot) PyrogramSessionUnsupported — Telegram Desktop cannot sign in as a bot

Login flags

Flag ToPyrogram() FromPyrogram()
UseCurrentSession (default) ✅ reuses the existing authorization
CreateNewSession ✅ QR-authorizes a new session through the Telethon path first, then converts it LoginFlagInvalid

CreateNewSession is what you want when the target API differs from the one that created the tdata — reusing a session across APIs is what gets accounts flagged. See Using official APIs.

FromPyrogram() has no CreateNewSession: Pyrogram has no equivalent of Telethon's QRLoginToNewClient() here, so there is no way to mint a second session from the first. Convert to Telethon first if you need it.