UNPKG

tdlib-native

Version:

🚀 Telegram TDLib native nodejs wrapper

377 lines (332 loc) • 9.22 kB
import { Client, TDError } from "./client"; import { setTdlibParameters$DirectInput, phoneNumberAuthenticationSettings$Input, authenticationCodeInfo, emailAddressAuthenticationCodeInfo, registerUser$DirectInput, TermsOfService, AuthorizationState, AuthorizationState$Type, Update$Type, typename } from "./generated/types"; type AuthenticateOptions = { readonly signal?: AbortSignal; }; type Waiter<T, A extends readonly unknown[] = []> = | T | ((...parameters: A) => T | PromiseLike<T>); /** * * * @template {string|object|number|boolean} T * @template {unknown[]} A * * @param {Waiter<T, A>} waiter * @param {...A} parameters * @returns {Promise<T>} */ async function wait< T extends string | object | number | boolean, A extends readonly unknown[] >(waiter: Waiter<T, A>, ...parameters: A): Promise<T> { if (typeof waiter === "function") { return await waiter(...parameters); } return waiter; } type AuthenticatorState = { parameters: Waiter<setTdlibParameters$DirectInput>; phone: Waiter<string>; token: Waiter<string>; phoneSettings: Waiter<phoneNumberAuthenticationSettings$Input>; email: Waiter<string>; otherDevice: Waiter<string, [link: string]>; code: Waiter<string, [info: authenticationCodeInfo]>; emailCode: Waiter<string, [info: emailAddressAuthenticationCodeInfo]>; password: Waiter<string, [hint: string]>; register: Waiter<registerUser$DirectInput, [tos: TermsOfService]>; }; export type StageTDLibParameters = { tdlibParameters(parameters: AuthenticatorState["parameters"]): StageSelect; } export type StageSelect = { token(token: AuthenticatorState["token"]): StageAuthenticate; phone( phone: AuthenticatorState["phone"], settings?: AuthenticatorState["phoneSettings"] ): StageUser; } export type StageAuthenticate = { authenticate(options?: AuthenticateOptions): Promise<void>; } export type StageUser = { email(email: AuthenticatorState["email"]): StageUser; code(email: AuthenticatorState["code"]): StageUser; emailCode(email: AuthenticatorState["emailCode"]): StageUser; password(password: AuthenticatorState["password"]): StageUser; register(data: AuthenticatorState["register"]): StageUser; } & StageAuthenticate /** * * * @export * @class Authenticator * @implements {StageTDLibParameters} * @implements {StageSelect} * @implements {StageAuthenticate} * @implements {StageUser} */ export class Authenticator implements StageTDLibParameters, StageSelect, StageAuthenticate, StageUser { /** * * * @static * @param {Client} client * @returns {StageTDLibParameters} {StageTDLibParameters} * @memberof Authenticator */ static create(client: Client): StageTDLibParameters { return new Authenticator(client); } private readonly _client: Client; private readonly _state: Partial<AuthenticatorState> = {}; /** * Creates an instance of Authenticator. * @param {Client} client * @memberof Authenticator */ private constructor(client: Client) { this._client = client; } /** * * * @private * @param {AuthorizationState} state * @param {function(): void} resolve * @returns {Promise<void>} {Promise<void>} * @memberof Authenticator */ private async _handleUpdate( state: AuthorizationState, resolve: () => void ): Promise<void> { switch (state._) { case AuthorizationState$Type.Ready: { resolve(); return; } case AuthorizationState$Type.Closed: case AuthorizationState$Type.Closing: case AuthorizationState$Type.LoggingOut: { throw new TDError("Closing"); } case AuthorizationState$Type.WaitCode: { if (!this._state.code) { return; } await this._client.api.checkAuthenticationCode({ code: await wait(this._state.code, state.code_info) }); return; } case AuthorizationState$Type.WaitEmailAddress: { if (!this._state.email) { return; } await this._client.api.setAuthenticationEmailAddress({ email_address: await wait(this._state.email) }); return; } case AuthorizationState$Type.WaitEmailCode: { if (!this._state.emailCode) { return; } await this._client.api.checkAuthenticationEmailCode({ code: { [typename]: "emailAddressAuthenticationCode", code: await wait(this._state.emailCode, state.code_info) } }); return; } case AuthorizationState$Type.WaitOtherDeviceConfirmation: { if (!this._state.otherDevice) { return; } await wait(this._state.otherDevice, state.link); return; } case AuthorizationState$Type.WaitPassword: { if (!this._state.password) { return; } await this._client.api.checkAuthenticationPassword({ password: await wait(this._state.password, state.password_hint) }); return; } case AuthorizationState$Type.WaitPhoneNumber: { if (this._state.token) { await this._client.api.checkAuthenticationBotToken({ token: await wait(this._state.token) }); return; } if (this._state.phone) { await this._client.api.setAuthenticationPhoneNumber({ phone_number: await wait(this._state.phone), settings: this._state.phoneSettings ? await wait(this._state.phoneSettings) : undefined }); } return; } case AuthorizationState$Type.WaitTdlibParameters: { if (!this._state.parameters) { return; } await this._client.api.setTdlibParameters( await wait(this._state.parameters) ); return; } case AuthorizationState$Type.WaitRegistration: { if (!this._state.register) { return; } await this._client.api.registerUser( await wait(this._state.register, state.terms_of_service) ); return; } default: } } /** * * * @param {AuthenticateOptions} [options] * @returns {Promise<void>} {Promise<void>} * @throws {TDError} if underlying method call throws * @throws {Error} if authentication state changes to closed before authentication completes * @throws {Error} whatever is reason of cancellation token * @memberof Authenticator */ async authenticate(options?: AuthenticateOptions): Promise<void> { let unsubscribe: (() => void) | undefined; const promise = new Promise<void>((resolve, reject) => { unsubscribe = this._client.updates.subscribe((update) => { if (update._ !== Update$Type.AuthorizationState) { return; } this._handleUpdate(update.authorization_state, resolve).catch(reject); }); options?.signal?.addEventListener("abort", reject, { once: true }); }); try { return await promise; } finally { unsubscribe?.(); } } /** * * * @param {*} parameters * @returns {StageSelect} {StageSelect} * @memberof Authenticator */ tdlibParameters(parameters: AuthenticatorState["parameters"]): StageSelect { this._state.parameters = parameters; return this; } /** * * * @param {Waiter<string>} token * @returns {StageSelect} {StageSelect} * @memberof Authenticator */ token(token: AuthenticatorState["token"]): StageAuthenticate { this._state.token = token; return this; } /** * * * @param {Waiter<string>} phone * @param {*} settings * @returns {StageUser} {StageUser} * @memberof Authenticator */ phone( phone: AuthenticatorState["phone"], settings?: AuthenticatorState["phoneSettings"] ): StageUser { this._state.phone = phone; this._state.phoneSettings = settings; return this; } /** * * * @param {Waiter<string>} email * @returns {StageUser} {StageUser} * @memberof Authenticator */ email(email: AuthenticatorState["email"]): StageUser { this._state.email = email; return this; } /** * * * @param {Waiter<string>} code * @returns {StageUser} {StageUser} * @memberof Authenticator */ code(code: AuthenticatorState["code"]): StageUser { this._state.code = code; return this; } /** * * * @param {Waiter<string>} emailCode * @returns {StageUser} {StageUser} * @memberof Authenticator */ emailCode(emailCode: AuthenticatorState["emailCode"]): StageUser { this._state.emailCode = emailCode; return this; } /** * * * @param {Waiter<string>} password * @returns {StageUser} {StageUser} * @memberof Authenticator */ password(password: AuthenticatorState["password"]): StageUser { this._state.password = password; return this; } /** * * * @param {*} data * @returns {StageUser} {StageUser} * @memberof Authenticator */ register(data: AuthenticatorState["register"]): StageUser { this._state.register = data; return this; } }