import { createInterface, Interface as RlInterface } from 'node:readline' import { Readable, Writable } from 'node:stream' import { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core' import { BaseTelegramClient as BaseTelegramClientBase, BaseTelegramClientOptions as BaseTelegramClientOptionsBase, TelegramClient as TelegramClientBase, TelegramClientOptions, } from '@mtcute/core/client.js' import { setPlatform } from '@mtcute/core/platform.js' import { downloadToFile } from './methods/download-file.js' import { DenoPlatform } from './platform.js' import { SqliteStorage } from './sqlite/index.js' import { DenoCryptoProvider } from './utils/crypto.js' import { TcpTransport } from './utils/tcp.js' export type { TelegramClientOptions } export interface BaseTelegramClientOptions extends PartialOnly, 'transport' | 'crypto'> { /** * Storage to use for this client. * * If a string is passed, it will be used as * a name for an SQLite database file. * * @default `"client.session"` */ storage?: string | ITelegramStorageProvider /** * **ADVANCED USE ONLY** * * Whether to not set up the platform. * This is useful if you call `setPlatform` yourself. */ platformless?: boolean } export class BaseTelegramClient extends BaseTelegramClientBase { constructor(opts: BaseTelegramClientOptions) { if (!opts.platformless) setPlatform(new DenoPlatform()) super({ crypto: new DenoCryptoProvider(), transport: () => new TcpTransport(), ...opts, storage: typeof opts.storage === 'string' ? new SqliteStorage(opts.storage) : opts.storage ?? new SqliteStorage('client.session'), }) } } /** * Telegram client for use in Deno */ export class TelegramClient extends TelegramClientBase { constructor(opts: TelegramClientOptions) { if ('client' in opts) { super(opts) return } super({ client: new BaseTelegramClient(opts), }) } private _rl?: RlInterface /** * Tiny wrapper over Node `readline` package * for simpler user input for `.run()` method. * * Associated `readline` interface is closed * after `run()` returns, or with the client. * * @param text Text of the question */ input(text: string): Promise { if (!this._rl) { this._rl = createInterface({ // eslint-disable-next-line input: Readable.fromWeb(Deno.stdin.readable as any), // eslint-disable-next-line output: Writable.fromWeb(Deno.stdout.writable as any), }) } return new Promise((res) => this._rl?.question(text, res)) } close(): Promise { this._rl?.close() return super.close() } start(params: Parameters[0] = {}): Promise { if (!params.botToken) { if (!params.phone) params.phone = () => this.input('phone > ') if (!params.code) params.code = () => this.input('code > ') if (!params.password) { params.password = () => this.input('2fa password > ') } } return super.start(params).then((user) => { if (this._rl) { this._rl.close() delete this._rl } return user }) } run( params: Parameters[0] | ((user: User) => void | Promise), then?: (user: User) => void | Promise, ): void { if (typeof params === 'function') { then = params params = {} } this.start(params) .then(then) .catch((err) => this.emitError(err)) } downloadToFile( filename: string, location: FileDownloadLocation, params?: FileDownloadParameters | undefined, ): Promise { return downloadToFile(this, filename, location, params) } }