tdata to Telethon
Take the account out of a Telegram Desktop tdata folder and hand it to Telethon: as a string
session, as a .session file with its .json companion, or as a ready opentele2 TelegramClient.
Telethon is a Python library, so Python connects; Node and Rust write the same strings and files
for a Python process to use.
Work on a copy of the folder. The string, the .session file and the folder are the same
logged-in account: never share them, never commit them and keep them out of logs.
Install
pip install opentele2Python 3.8+ · abi3 wheels · works over Telethon
npm install opentele2Node 18+ · subpaths opentele2/mtcute and opentele2/telethon
cargo add opentele2-core --features telethon-sqlitetelethon-sqlite writes the .session file over the core's bundled SQLite
Load the folder
Copy the tdata folder of a logged-in Telegram Desktop next to your script. A folder with a local
passcode needs it to load; a wrong or missing passcode fails the load. The
main account is the one the conversions use.
from opentele2.td import TDesktop
tdesk = TDesktop("tdata") # TDesktop("tdata", passcode="...") for a locked folder
print(tdesk.accountsCount, tdesk.mainAccount.UserId, tdesk.mainAccount.MainDcId)tdesk.accounts lists every account; each Account has the same ToTelethon as the folder.
import { loadTdata } from 'opentele2'
const tdata = await loadTdata('tdata') // loadTdata('tdata', passcode) for a locked folder
console.log(tdata.accounts.length, tdata.mainAccount.userId, tdata.mainAccount.mainDc)use opentele2_core::tdata::TData;
fn main() -> opentele2_core::Result<()> {
let tdata = TData::load("tdata", b"", None)?; // passcode bytes, key file name
let account = tdata.main_account().expect("folder has an account");
println!("{} {} {}", tdata.accounts.len(), account.user_id, account.main_dc);
Ok(())
}Get a string session
A Telethon string session (version 1) holds the DC, its address and port and the auth key. The
address is the first TCP endpoint of the main DC from the account’s stored config, or the built-in
production table when it has none.
import asyncio
from telethon.sessions import StringSession
from opentele2.api import UseCurrentSession
from opentele2.td import TDesktop
async def main():
client = await TDesktop("tdata").ToTelethon(StringSession(), UseCurrentSession)
print(client.session.save()) # the folder's own auth key; nothing is sent yet
asyncio.run(main())Without a client, telethon_string_session(TData.load("tdata").main_account) from
opentele2._native gives the same string; pass True as the second argument for the IPv6
endpoint.
import { loadTdata, telethonStringSession } from 'opentele2'
const { mainAccount } = await loadTdata('tdata')
console.log(telethonStringSession(mainAccount)) // telethonStringSession(mainAccount, true) for IPv6use opentele2_core::session::telethon;
use opentele2_core::tdata::TData;
fn main() -> opentele2_core::Result<()> {
let tdata = TData::load("tdata", b"", None)?;
let account = tdata.main_account().expect("folder has an account");
let session = telethon::StringSession::from_account(account, false)?; // true for IPv6
println!("{}", session.encode());
Ok(())
}StringSession’s Debug prints the key id only; the encoded string carries the full key.
Connect with the Telegram Desktop profile
A session from tdata belongs to Telegram Desktop, so connect with the Telegram Desktop
API profile. The opentele2 TelegramClient is a
telethon.TelegramClient subclass that sends that profile in initConnection, lang_pack and
tz_offset included.
import asyncio
from telethon.sessions import StringSession
from opentele2.api import API
from opentele2.tl import TelegramClient
async def main(string: str):
client = TelegramClient(StringSession(string), api=API.TelegramDesktop)
await client.connect()
print(await client.get_me())
await client.disconnect()TDesktop("tdata").ToTelethon("telethon.session", UseCurrentSession) does both steps at once: it
writes the folder’s key into telethon.session and returns the client, ready for connect().
Telethon runs in Python. Hand the string or the .session + .json pair from the other steps to a Python process and connect there with the opentele2 TelegramClient. To stay in this language, use mtcute in Node or grammers in Rust (the next two guides).
Telethon runs in Python. Hand the string or the .session + .json pair from the other steps to a Python process and connect there with the opentele2 TelegramClient. To stay in this language, use mtcute in Node or grammers in Rust (the next two guides).
Write a .session + .json pair
The pair is a Telethon SQLiteSession file (version 8) and a .json companion holding the API
profile, so whoever loads it later connects with the same profile. The
pair guide covers its keys and the way back.
import asyncio
from opentele2.td import TDesktop
async def main():
session_file, json_file = await TDesktop("tdata").SaveSessionJson("telethon.session")
print(session_file, json_file) # telethon.session telethon.json
asyncio.run(main())SaveSessionJson(path, api=None, fetch_user_info=False) writes the folder’s own session with the
folder’s profile; pass api= to store another one.
import { loadTdata, profile } from 'opentele2'
import { saveSessionPair } from 'opentele2/telethon'
const { mainAccount } = await loadTdata('tdata')
const paths = await saveSessionPair('telethon', mainAccount, profile('TelegramDesktop'))
console.log(paths.session, paths.json) // telethon.session telethon.jsonThe companion’s id is the account’s user id; { ipv6: true } as the fourth argument writes the
IPv6 endpoint.
// cargo add opentele2-core --features telethon-sqlite
use opentele2_core::api::{self, Client, Versions};
use opentele2_core::session::telethon_file::{self, SessionRows};
use opentele2_core::tdata::TData;
fn main() -> opentele2_core::Result<()> {
let tdata = TData::load("tdata", b"", None)?;
let account = tdata.main_account().expect("folder has an account");
let rows = SessionRows::from_account(account, false)?;
let (session_path, json_path) = telethon_file::pair_paths("telethon");
telethon_file::write_session_file(&session_path, &rows)?;
let desktop = api::profile(Client::Desktop, &Versions::offline());
std::fs::write(json_path, telethon_file::companion_text(&desktop, None))?;
Ok(())
}companion_text(&profile, extra) takes the account keys as an optional serde_json map, for
example id and session_file.
Or log a new session in
UseCurrentSession reuses the folder’s auth key, so Telethon and Telegram Desktop share one
session. CreateNewSession logs a new one in by QR code through the folder’s session, on the
device you choose; it needs the network and, with 2FA on, the password.
import asyncio
from opentele2.api import API, CreateNewSession
from opentele2.td import TDesktop
async def main():
tdesk = TDesktop("tdata")
client = await tdesk.ToTelethon(
"new.session", CreateNewSession, api=API.TelegramAndroid.Generate("my-id"), password=None
)
await client.connect()
print(await client.get_me())
asyncio.run(main())password= is the 2FA password; without it a 2FA account raises NoPasswordProvided, and a wrong
one PasswordIncorrect (both in opentele2.exception).
QR login runs over mtcute clients in Node: qrLoginToNewClient in opentele2/mtcute. The
client to tdata guide shows it; save the new client’s account with
saveSessionPair to hand it to Telethon.
QR login runs over grammers clients in Rust: client::qr_login in opentele2-grammers with the
client feature. The client to tdata guide shows it.
Keep each session on the profile it was created with. A session that suddenly announces another device or app looks like a different client to the server; see initConnection fingerprints.
Next
- Sessions: every session format and how endpoints are chosen.
- Telethon .session + .json: the pair’s keys and loading it back.
- Reference: Python · Node · Rust.