Client versions
Every API profile carries an app version, and the web profiles a browser user agent. A version that lags far behind the official app is a tell, so opentele2 keeps a built-in set of versions and can fetch the current ones from the clients’ public release sources.
The versions
One record, PlatformVersions (api::Versions in Rust), holds them all:
| Field | What it is |
|---|---|
android_app_version, android_ | Telegram Android, for example 13.0.3 and 7183 |
android_sdk_range, android_latest_sdk | the Android SDK levels generated devices run |
android_x_app_version | Telegram X |
ios_app_version, ios_build_number, ios_version_range, ios_system_version | Telegram iOS and the iOS versions |
macos_app_version, macos_build_number, macos_system_version | Telegram for macOS |
desktop_app_version, desktop_ | Telegram Desktop, 7.3.1; bound to the tdata version |
web_a_version, web_k_version | Telegram Web A and Web K |
chrome_version, user_agent | stable Chrome and the user agent of the default web profiles |
firefox_version | stable Firefox, whose major bounds the Firefox web devices |
Node spells the same fields in camelCase (androidAppVersion, chromeVersion, …).
Offline defaults
The built-in set ships inside the core: Versions::offline() in Rust, platformVersions() in
Node, _native.platform_versions() in Python. Every function that takes versions (profiles,
Generate, validation, consistency expectations) uses it when you pass none.
Live versions
opentele2-versions queries nine sources in parallel: Telegram Desktop, Android, Telegram X, iOS,
macOS, Web K, Web A, Chrome and Firefox, from their public release feeds (GitHub releases, the
stores’ lookup APIs, the Telegram stable channel, the web clients’ version files, Chrome for
Testing and Mozilla’s product details). Each HTTP request has a timeout, 10 seconds by default. A
source that fails or times out only leaves its fields unset; fetching never fails for the network,
and with no network the result is empty.
The result is a patch: only the fields some source answered.
Importing opentele2 fetches the live versions once per process and applies them to
opentele2.fingerprint.PLATFORM_VERSIONS; the API classes take their app versions from it.
Fetching is best effort: on any failure the built-in versions stay.
from opentele2.fingerprint import get_platform_versions
versions = get_platform_versions() # PLATFORM_VERSIONS, fetched at import
print(versions.android_app_version, versions.chrome_version, versions.firefox_version)Set OPENTELE_NO_FETCH before the import to skip the fetch and stay on the built-in set, for
tests, CI and offline machines:
OPENTELE_NO_FETCH=1 python main.pyNode never fetches by itself. fetchVersions(timeoutMs) runs the nine sources on request:
import { applyVersionsPatch, fetchVersions, generate, platformVersions, profile } from 'opentele2'
const patch = await fetchVersions() // timeoutMs per request, 10000 by default
const versions = applyVersionsPatch(platformVersions(), patch)
console.log(profile('TelegramAndroid', versions).appVersion)
console.log(generate('TelegramWeb_A', 'my-id', null, versions).deviceModel)Rust never fetches by itself. opentele2_versions::fetch_all(timeout) runs the nine sources over
blocking HTTP, without an async runtime:
use opentele2_core::api::{self, Client, Versions};
fn main() {
let patch = opentele2_versions::fetch_all(opentele2_versions::DEFAULT_TIMEOUT);
let mut versions = Versions::offline();
patch.apply(&mut versions);
println!("{}", api::profile(Client::Android, &versions).app_version);
}Applying a patch
A patch is written onto a versions record field by field, with two rules:
desktop_app_versionis reported but never applied. The Telegram Desktop version stays bound to the version written into tdata, 7.3.1, so a profile never claims a desktop build newer than the folder it came with.chrome_versionrebuildsuser_agentof the default web profiles to match.
The Chrome and Firefox majors also decide the web device pool, so a web device generated for a unique id changes when either browser moves to a new major.
from opentele2 import _native
patch = {"chrome_version": "156.0.0.0", "android_app_version": "13.1.0"}
versions = _native.apply_versions_patch(_native.platform_versions(), patch)
print(versions["user_agent"]) # ... Chrome/156.0.0.0 Safari/537.36
print(_native.profile("TelegramAndroid", versions)["app_version"]) # 13.1.0 (7183)_native.fetch_versions(timeout) returns the same patch the import applies.
import { applyVersionsPatch, platformVersions, profile } from 'opentele2'
const versions = applyVersionsPatch(platformVersions(), {
chromeVersion: '156.0.0.0',
androidAppVersion: '13.1.0',
})
console.log(versions.userAgent) // ... Chrome/156.0.0.0 Safari/537.36
console.log(profile('TelegramAndroid', versions).appVersion) // 13.1.0 (7183)use opentele2_core::api::Versions;
fn main() {
let mut versions = Versions::offline();
versions.set_chrome_version("156.0.0.0"); // rebuilds user_agent as the patch does
versions.android_app_version = "13.1.0".to_owned();
println!("{}", versions.user_agent);
}Strict validation and the consistency expectations compare a profile’s app version with a versions record. Validate a profile against the same versions it was built with.