Skip to Content
ReferenceRustopentele2-grammers

opentele2-grammers

The bridge between opentele2 and grammers 0.11: grammers sessions and connection params from tdata accounts and API profiles, and back. The core never depends on grammers; this crate is the optional bridge.

docs.rs  · crates.io  · MIT

Cargo.toml
[dependencies] opentele2-core = "2.0.0" opentele2-grammers = "2.0.0" # opentele2-grammers = { version = "2.0.0", features = ["client"] }

Features

FeatureDefaultEffect
clientnoThe client flows over grammers-client 0.11 in the client module: QR login with DC migration and 2FA, the self user id, session admin, the seven consistency checks and tdata from a client session. Off by default so the default crate has no client.

Sessions and params

src/lib.rs
pub fn session_data(account: &Account) -> SessionData; pub fn account_from_session(data: &SessionData, index: i32) -> Result<Account>; pub fn connection_params(profile: &ApiProfile) -> ConnectionParams;
FunctionDescription
session_data(account)A grammers SessionData for a tdata account, ready for MemorySession::from or SessionData::import_to: the main DC as home DC, the DCs with an IPv4 and an IPv6 endpoint (the stored config’s when there is one, else every production DC), the account’s key on the main DC only, the self user in peer_infos and an empty updates state. It cannot fail.
account_from_session(data, index)The inverse: the home DC key and the self user of a SessionData as the account at index. Error::AuthKeyNotFound without a home DC key, Error::BadConfigData without exactly one self user id.
connection_params(profile)A grammers ConnectionParams with the profile’s device model, system version, app version and language codes. Pass the profile’s api_id to grammers separately and its api_hash at sign-in.
src/main.rs
use grammers_session::storages::MemorySession; use opentele2_core::api::{self, Client, Versions}; 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 = MemorySession::from(opentele2_grammers::session_data(account)); let desktop = api::profile(Client::Desktop, &Versions::offline()); let _params = opentele2_grammers::connection_params(&desktop); Ok(()) }
Warning:

grammers 0.11 builds initConnection itself and always sends an empty lang_pack and no params, so a session driven by grammers with the desktop profile is not byte-identical on the wire to Telegram Desktop: the device, system, app and language fields match, those two do not. grammers’ SessionData and its DcOption print the raw auth key with Debug; do not log them.

client (feature client)

Every flow runs over an Invoker, which grammers_client::Client implements, so it can also run over a fake that returns canned TL objects. Flows that read or change session state also take the grammers Session the client’s SenderPool was built with.

src/client.rs
pub trait Invoker: Sync { fn invoke<R>(&self, request: &R) -> impl Future<Output = Result<R::Return, InvocationError>> + Send where R: RemoteCall + Sync; } pub async fn is_authorized<I: Invoker>(invoker: &I) -> Result<bool, ClientError>; pub async fn qr_login<A, N, S>(authorized: &A, new: &N, new_session: &S, api_id: i32, api_hash: &str, password: Option<&str>) -> Result<i64, ClientError>; pub async fn post_login_requests<I, D, F>(invoker: &I, lang_pack: &str, delay: D) -> usize; pub async fn get_me_id<I: Invoker>(invoker: &I) -> Result<i64, ClientError>; pub async fn sessions<I: Invoker>(invoker: &I) -> Result<Vec<tl::types::Authorization>, ClientError>; pub async fn current_session<I: Invoker>(invoker: &I) -> Result<Option<tl::types::Authorization>, ClientError>; pub async fn terminate_session<I: Invoker>(invoker: &I, hash: i64) -> Result<(), ClientError>; pub async fn terminate_all_sessions<I: Invoker>(invoker: &I) -> Result<usize, ClientError>; pub fn session_row(authorization: &tl::types::Authorization) -> SessionRow; pub async fn sessions_table<I: Invoker>(invoker: &I) -> Result<String, ClientError>; pub async fn is_official_app<I: Invoker>(invoker: &I) -> Result<bool, ClientError>; pub async fn run_consistency_checks<I: Invoker>(invoker: &I, expectations: &ConsistencyExpectations) -> ConsistencyReport; pub async fn account_from_client<I, S>(invoker: &I, session: &S, index: i32) -> Result<Account, ClientError>; pub async fn tdata_from_client<I, S>(invoker: &I, session: &S, passcode: &[u8], rng: &mut (dyn FnMut(&mut [u8]) + Send)) -> Result<TData, ClientError>;
FunctionDescription
qr_login(...)Logs new in through authorized by QR login, following auth.loginTokenMigrateTo, with the 2FA password; resolves to the new user id.
post_login_requests(invoker, lang_pack, delay)Sends the requests an official client sends after login; delay(min_ms, max_ms) is the caller’s timer. Returns how many failed.
get_me_id, is_authorizedThe self user id, and whether the session is authorised.
sessions, current_session, terminate_session, terminate_all_sessionsSession admin over account.getAuthorizations and account.resetAuthorization.
session_row, sessions_table, is_official_appThe session table row and table, and whether the current session is official.
run_consistency_checks(invoker, expectations)The seven checks of Check::ALL, returning the core ConsistencyReport; its warning is left to the caller.
account_from_client(invoker, session, index)The client’s current session as a tdata account.
tdata_from_client(invoker, session, passcode, rng)A new folder with that account; save it with TData::save_random.

ClientError names why a flow failed: NoPasswordProvided, PasswordIncorrect, AuthTokenAlreadyAccepted, AuthTokenExpired, AuthTokenInvalid, QrLoginTimeout, FreshResetAuthorisationForbidden, HashInvalid, UnsupportedPasswordAlgorithm, Srp, SignUpRequired, UserIdUnresolved, Invocation (grammers’ InvocationError) and Core (the core Error).

Limits

  • QR login waits for no updateLoginToken: grammers 0.11 delivers updates only through SenderPool::updates, which the caller owns, so qr_login exports the token again as soon as auth.acceptLoginToken returns.
  • The crate has no warning channel: the per-attempt QR warnings are not emitted, and the existing-session warnings and the consistency warning are left to the caller.
  • Connecting the new client and handling an existing session are left to the caller, who builds the new client’s session. No flow here logs out.
  • The lang_pack check asks about the profile’s lang pack, while grammers 0.11 sends an empty one.
Last updated on