Live client versions
An app version far behind the official release is a tell. opentele2 ships a built-in set of versions and can fetch the current ones from the clients’ public release sources: Telegram Desktop, Android, Telegram X, iOS, macOS, Web K, Web A, plus stable Chrome and Firefox for the web devices. The fetch returns a patch, the fields some source answered, which you apply onto a versions record.
Fetching sends plain HTTP requests to public release feeds and nothing about any account. It never fails for the network: a source that fails or times out leaves its fields out, and with no network the patch is empty.
Know who fetches when
| Fetches by itself | Fetch on request | Turn it off | |
|---|---|---|---|
| Python | once per process, at import opentele2 | _native. | OPENTELE_NO_FETCH before the import |
| Node | never | fetch | nothing to turn off |
| Rust | never | opentele2_ | nothing to turn off; leave the crate out |
Each request has a timeout, 10 seconds by default.
Read the built-in versions
The built-in set ships inside the core. Every function that takes versions (profiles, Generate,
validation, consistency expectations) uses it when you pass none.
from opentele2 import _native
versions = _native.platform_versions()
print(versions["android_app_version"], versions["desktop_app_version"], versions["chrome_version"])opentele2.fingerprint.get_platform_versions() returns PLATFORM_VERSIONS, the record the API
classes read: the built-in set with the import-time patch applied.
import { platformVersions } from 'opentele2'
const versions = platformVersions()
console.log(versions.androidAppVersion, versions.desktopAppVersion, versions.chromeVersion)use opentele2_core::api::Versions;
fn main() {
let versions = Versions::offline();
println!("{} {}", versions.android_app_version, versions.chrome_version);
}Fetch the current versions
from opentele2.fingerprint import get_platform_versions
versions = get_platform_versions() # fetched once, when opentele2 was imported
print(versions.android_app_version, versions.chrome_version, versions.firefox_version)On any failure the built-in versions stay. _native.fetch_versions(10.0) runs the nine sources
again and returns the patch as a dict, without applying it.
import { fetchVersions } from 'opentele2'
const patch = await fetchVersions() // timeoutMs per request, 10000 by default
console.log(patch) // { androidAppVersion: '...', chromeVersion: '...', ... }// cargo add opentele2-versions
fn main() {
// Blocking HTTP over rustls, in parallel; no async runtime needed.
let patch = opentele2_versions::fetch_all(opentele2_versions::DEFAULT_TIMEOUT);
println!("empty: {}", patch.is_empty());
}Apply the patch
A patch is written onto a versions record field by field, with two rules: desktop_app_version
is reported but never applied, because the Telegram Desktop version stays bound to the one written
into tdata (7.3.1); and chrome_version rebuilds user_agent of the default web profiles.
from opentele2 import _native
# A patch from _native.fetch_versions(), or one of your own:
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)
print(_native.generate("TelegramWeb_A", "my-id", None, versions)["device_model"])import { applyVersionsPatch, fetchVersions, generate, platformVersions, profile } from 'opentele2'
const versions = applyVersionsPatch(platformVersions(), await fetchVersions())
console.log(profile('TelegramAndroid', versions).appVersion)
console.log(generate('TelegramWeb_A', 'my-id', null, versions).deviceModel)use opentele2_core::api::{self, Client, Versions};
fn main() -> opentele2_core::Result<()> {
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);
println!("{}", api::generate(Client::WebA, Some("my-id"), &versions)?.device_model);
Ok(())
}The Chrome and Firefox majors decide the web device pool, so a web device generated for a unique id changes when either browser moves to a new major. Store the unique id, not the device, and validate a profile against the same versions it was built with.
Stay offline
Set OPENTELE_NO_FETCH before opentele2 is imported, for tests, CI and machines without network:
OPENTELE_NO_FETCH=1 python main.pyimport os
os.environ["OPENTELE_NO_FETCH"] = "1" # before the first import of opentele2
import opentele2 # noqa: E402Node never fetches by itself: without a fetchVersions() call, everything runs on
platformVersions().
The core never fetches: Versions::offline() is the built-in set, and only opentele2-versions
talks to the network. Leave that crate out to build without any HTTP client.
Next
- Client versions: every field and source.
- Validate a fingerprint: strict mode compares app versions.
- Reference: Python · Node · Rust.