opentele2/mtcute
The mtcute side of the package: sessions and initConnection options for mtcute’s
TelegramClient, a crypto provider on the core, and the client flows (QR login with 2FA, session
admin, consistency checks, tdata from a client) run over an mtcute client.
import { TelegramClient } from '@mtcute/node'
import { loadTdata, profile } from 'opentele2'
import { createCryptoProvider, initConnectionOptions, toStringSession } from 'opentele2/mtcute'
const desktop = profile('TelegramDesktop')
const { mainAccount } = await loadTdata('tdata')
const client = new TelegramClient({
apiId: desktop.apiId,
apiHash: desktop.apiHash,
initConnectionOptions: initConnectionOptions(desktop),
crypto: await createCryptoProvider(),
})
await client.importSession(toStringSession(mainAccount))Sessions and options
function toStringSession(account: AccountInput): string
function initConnectionOptions(profile: ApiProfile): InitConnectionOptions
function createCryptoProvider(): Promise<ICryptoProvider>
interface InitConnectionOptions {
deviceModel: string
systemVersion: string
appVersion: string
systemLangCode: string
langPack: string
langCode: string
params?: tl.TypeJSONValue
}| Function | Description |
|---|---|
to | The mtcute v3 string session of the account, for client.importSession() or the TelegramClient constructor. |
init | The initConnectionOptions of a profile() or generate() result; pass apiId and apiHash to the client separately. A tdesktop profile also gets params with tz_offset, computed when the options are built. |
createCryptoProvider() | An mtcute ICryptoProvider whose every primitive is the core: hashes, HMAC, PBKDF2, AES-256-CTR and -IGE, pq factorisation, gzip and the OS random source. It needs neither Node’s crypto module nor @mtcute/node; its gzip writes a gzip stream. |
FlowClient
The flows take any object with the members they use; an mtcute TelegramClient has them all.
interface FlowClient {
call(request: { _: string; [field: string]: unknown }): Promise<any>
getPrimaryDcId?(): Promise<number>
changePrimaryDc?(dcId: number): Promise<void>
notifyLoggedIn?(authorization: tl.auth.TypeAuthorization): Promise<unknown>
getMe?(): Promise<{ id: number | bigint }>
exportSession?(): Promise<string>
disconnect?(): Promise<unknown>
storage?: { clear(withAuthKeys?: boolean): Promise<unknown> }
onServerUpdate?: {
add(listener: (update: tl.TypeUpdates) => void): void
remove(listener: (update: tl.TypeUpdates) => void): void
}
}Only call is required everywhere, so a fake that answers it with canned TL objects drives every
flow. changePrimaryDc is required for a QR login DC migration, exportSession by
tdataFromClient, and disconnect with storage.clear to discard a session of another api id.
QR login
function qrLoginToNewClient<C extends FlowClient>(
authorized: FlowClient,
fresh: C,
options: QrLoginOptions,
): Promise<QrLoginResult<C>>
function postLoginRequests(client: FlowClient, langPack?: string, delay?: PostLoginDelay): Promise<number>
interface QrLoginOptions {
apiId?: number
apiHash?: string
password?: string
profile?: ApiProfile
postLogin?: boolean
delay?: PostLoginDelay
onUrl?: (url: string, expires: Date | null) => void
}
interface QrLoginResult<C extends FlowClient = FlowClient> { client: C; userId: number }
type PostLoginDelay = (minMs: number, maxMs: number) => Promise<void>
const QR_LOGIN_ATTEMPTS: number // 5
const QR_LOGIN_TIMEOUT: string
const USER_ID_UNRESOLVED: string
const POST_LOGIN_DELAYS_MS: ReadonlyArray<readonly [number, number] | null>qrLoginToNewClient logs fresh, a client with empty storage, in through the authorised
authorized by QR login: DC alignment, auth.exportLoginToken on fresh (following
loginTokenMigrateTo), auth.acceptLoginToken on authorized, 2FA through
account.getPassword and the native srpCheck, then the post-login requests.
| Option | Description |
|---|---|
apiId, apiHash | Sent with auth.exportLoginToken; profile.apiId and profile.apiHash by default. |
password | The 2FA password; without it a 2FA account rejects with OT_. |
profile | The new client’s profile; its langPack drives the post-login requests. |
postLogin | Whether to send the post-login requests; true by default. |
delay | The wait before each post-login request; a random sleep of randomDelayMs by default. |
onUrl | Receives each exported token’s qrLoginUrl and expiry before it is accepted. |
An already authorised fresh of the same api id is returned as is with the
EXISTING_SESSION_KEPT warning; one of another api id warns EXISTING_SESSION_REPLACED, has its
session discarded locally (the old authorization is not terminated on the server) and is logged in
again. The promise rejects with OT_NO_PASSWORD_PROVIDED, OT_PASSWORD_INCORRECT,
OT_UNSUPPORTED_PASSWORD_ALGORITHM, OT_QR_LOGIN_TIMEOUT, OT_SIGN_UP_REQUIRED, OT_SRP_BAD_B
or mtcute’s RpcError (AUTH_TOKEN_* included). postLoginRequests sends the post-login
requests alone and resolves to how many failed.
Session admin
function isAuthorized(client: FlowClient): Promise<boolean>
function getMeId(client: FlowClient): Promise<number>
function getSessions(client: FlowClient): Promise<tl.account.RawAuthorizations>
function getCurrentSession(client: FlowClient): Promise<tl.RawAuthorization | null>
function terminateSession(client: FlowClient, hash: tl.Long): Promise<void>
function terminateAllSessions(client: FlowClient): Promise<number>
function sessionRow(authorization: tl.RawAuthorization): SessionRow & { current: boolean; officialApp: boolean }
function formatSessions(source: FlowClient | tl.account.RawAuthorizations | tl.RawAuthorization[]): Promise<string>
function isOfficialApp(client: FlowClient): Promise<boolean>| Function | Description |
|---|---|
isAuthorized(client) | Whether updates.getState succeeds. |
getMeId(client) | The self user id; rejects with OT_USER_ID_UNRESOLVED without one. |
getSessions(client) | account.; rejects with OT_ when the answer holds no list. |
get | The session flagged current, else null. |
terminate | account.; rejects with OT_ or OT_HASH_INVALID, the RPC error as cause. |
terminate | Ends every non-current session in list order and resolves to how many were ended. |
session | The session table row of an mtcute authorization. |
formatSessions(source) | The session table for a client (its sessions are fetched), an account.authorizations answer or a list of authorizations. |
isOfficialApp(client) | Whether the current session is officialApp. |
Consistency checks
function runConsistencyChecks(
client: FlowClient,
profile: ApiProfile,
versions?: Versions | null,
options?: ConsistencyOptions,
): Promise<CheckResult[]>
interface ConsistencyOptions {
layer?: number
autoWarn?: boolean
}The seven check results for client used with profile, in consistencyChecks() order; a
failing check emits the consistency warning with autoWarn (default true). layer is the
library layer layer_match compares, @mtcute/core’s tl.LAYER by default. Join the results
with consistencySummary.
tdata from a client
function tdataFromClient(client: FlowClient, options?: TdataFromClientOptions): Promise<TData>
interface TdataFromClientOptions {
passcode?: string | Uint8Array
performanceMode?: boolean
}A new tdata folder with the client’s session as its only account: main DC and auth key from
client.exportSession(), user id from getMeId. Save it with saveTdata. To store a new session
instead of the client’s own, log one in with qrLoginToNewClient first.