UNPKG

@tgsnake/core

Version:

Pure Telegram MTProto library for nodejs

462 lines (461 loc) 19 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = void 0; const platform_node_js_1 = require("../platform.node.js"); const Errors = __importStar(require("../errors/index.js")); const index_js_1 = require("../raw/index.js"); const index_js_2 = require("../session/secretChats/index.js"); const connection_js_1 = require("../connection/connection.js"); const _Session = __importStar(require("./Session.js")); const Version = __importStar(require("../Version.node.js")); const helpers = __importStar(require("../helpers.js")); const Files = __importStar(require("../file/index.js")); class Client { _apiId; _apiHash; _storage; _testMode; _proxy; _ipv6; _deviceModel; _systemVersion; _appVersion; _systemLangCode; _langCode; _maxRetries; _isCdn; _sleepTreshold; _takeout; _noUpdates; _takeoutId; _dcId; _defaultDcId; _session; _isConnected; _connectionMode; _local; _secretChat; _getFileSemaphore; _saveFileSemaphore; _maxReconnectRetries; _me; _handler = []; constructor(session, apiHash, apiId, clientOptions) { this._storage = session; this._apiHash = apiHash; this._apiId = apiId ?? session.apiId; this._testMode = clientOptions?.testMode ?? false; this._proxy = clientOptions?.proxy; this._ipv6 = clientOptions?.ipv6 ?? false; this._deviceModel = clientOptions?.deviceModel ?? platform_node_js_1.os.type().toString(); this._systemVersion = clientOptions?.systemVersion ?? ('Deno' in globalThis ? Deno.version?.deno ? `Deno ${Deno.version?.deno}` : `Deno unknown` : platform_node_js_1.os.release().toString()); this._appVersion = clientOptions?.appVersion ?? Version.version; this._systemLangCode = clientOptions?.systemLangCode ?? 'en'; this._langCode = clientOptions?.langCode ?? this._systemLangCode; this._sleepTreshold = clientOptions?.sleepTreshold ?? 10000; this._maxRetries = clientOptions?.maxRetries ?? 5; this._isCdn = clientOptions?.isCdn ?? false; this._noUpdates = clientOptions?.noUpdates ?? false; this._takeout = clientOptions?.takeout ?? false; this._connectionMode = clientOptions?.tcp ?? connection_js_1.TCP.TCPFull; this._local = clientOptions?.local ?? ((platform_node_js_1.isBrowser && globalThis && globalThis.location.protocol !== 'https:') || true); this._secretChat = new index_js_2.SecretChat(session, this); this._getFileSemaphore = new platform_node_js_1.Semaphore(clientOptions?.maxConcurrentTransmissions || 1); this._saveFileSemaphore = new platform_node_js_1.Semaphore(clientOptions?.maxConcurrentTransmissions || 1); this._maxReconnectRetries = clientOptions?.maxReconnectRetries || 3; this._defaultDcId = clientOptions?.defaultDCId || 2; } exportSession() { return _Session.exportSession.call(this); } invoke(query, retries = this._maxRetries, timeout = 15000, sleepTreshold = this._sleepTreshold) { return _Session.invoke.call(this, query, retries, timeout, sleepTreshold); } logout() { return _Session.logout.call(this); } start(auth) { return _Session.start.call(this, auth); } connect() { return _Session.connect.call(this); } async handleUpdate(update) { if (!this._noUpdates) { await this.fetchPeers('users' in update ? update.users : []); await this.fetchPeers('chats' in update ? update.chats : []); if (update instanceof index_js_1.Raw.Updates) { const parsed = []; for (const up of update.updates) { if (up instanceof index_js_1.Raw.UpdateEncryption) { if (up.chat instanceof index_js_1.Raw.EncryptedChat) { await this._secretChat.finish(up.chat); } if (up.chat instanceof index_js_1.Raw.EncryptedChatDiscarded) { await this._storage.removeSecretChatById(up.chat.id); } if (up.chat instanceof index_js_1.Raw.EncryptedChatRequested) { await this._secretChat.accept(up.chat); } } else if (up instanceof index_js_1.Raw.UpdateNewEncryptedMessage) { const modUpdate = await this._handleSecretChatUpdate(up); if (modUpdate) { parsed.push(modUpdate); } } else { parsed.push(up); } } update.updates = parsed; } this._handler.forEach((callback) => { return callback(update); }); } return update; } async _handleSecretChatUpdate(update) { const modUpdate = await index_js_1.UpdateSecretChatMessage.generate(update, this._secretChat); if (modUpdate.message instanceof index_js_1.SecretChatMessageService) { const msg = modUpdate.message.message; if (msg && 'action' in msg) { const action = msg.action; if (action instanceof index_js_1.Raw.DecryptedMessageActionRequestKey20) { await this._secretChat.acceptRekeying(modUpdate.message.chatId, action); return false; } if (action instanceof index_js_1.Raw.DecryptedMessageActionAcceptKey20) { await this._secretChat.commitRekeying(modUpdate.message.chatId, action); return false; } if (action instanceof index_js_1.Raw.DecryptedMessageActionCommitKey20) { await this._secretChat.finalRekeying(modUpdate.message.chatId, action); return false; } if (action instanceof index_js_1.Raw.DecryptedMessageActionNoop20) { return false; } if (action instanceof index_js_1.Raw.DecryptedMessageActionNotifyLayer17) { const peer = await this._storage.getSecretChatById(modUpdate.message.chatId); if (peer) { peer.layer = action.layer; if (action.layer < 73) { peer.mtproto = 1; } await peer.update(this._storage); if (action.layer >= 17 && Date.now() / 1000 - peer.created > 15) { await this._secretChat.notifyLayer(modUpdate.message.chatId); } } return false; } if (action instanceof index_js_1.Raw.DecryptedMessageActionSetMessageTTL8) { const peer = await this._storage.getSecretChatById(modUpdate.message.chatId); if (peer) { peer.ttl = action.ttlSeconds; await peer.update(this._storage); } return false; } } } if (modUpdate.message instanceof index_js_1.SecretChatMessage) { const msg = modUpdate.message.message; if (msg instanceof index_js_1.Raw.DecryptedMessageLayer17) { const peer = await this._storage.getSecretChatById(modUpdate.message.chatId); if (peer) { peer.inSeqNo += 1; if (msg.layer >= 17) { peer.layer = msg.layer; } await peer.update(this._storage); if (msg.layer >= 17 && Date.now() / 1000 - peer.created > 15) { await this._secretChat.notifyLayer(modUpdate.message.chatId); } } } } return modUpdate; } addHandler(callback) { this._handler.push(callback); } async fetchPeers(peers) { let isMin = false; const parsedPeers = []; for (const peer of peers) { if (peer.min) { isMin = true; continue; } if (peer instanceof index_js_1.Raw.User) { peer; parsedPeers.push([ peer.id, peer.accessHash ?? BigInt(0), peer.bot ? 'bot' : 'user', peer.username ? [peer.username.toLowerCase()] : peer.usernames && peer.usernames.length ? peer.usernames.map((username) => username.username.toLowerCase()) : undefined, peer.phone ? peer.phone : undefined, ]); } else if (peer instanceof index_js_1.Raw.Chat || peer instanceof index_js_1.Raw.ChatForbidden) { parsedPeers.push([BigInt(-peer.id), BigInt(0), 'group', undefined, undefined]); } else if (peer instanceof index_js_1.Raw.Channel || peer instanceof index_js_1.Raw.ChannelForbidden) { parsedPeers.push([ helpers.getChannelId(peer.id), peer.accessHash ?? BigInt(0), peer.broadcast ? 'channel' : 'supergroup', peer.username ? [peer.username.toLowerCase()] : undefined, undefined, ]); } continue; } await this._storage.updatePeers(parsedPeers); return isMin; } async resolvePeer(peerId) { if (!this._isConnected) { throw new Errors.ClientError.ClientDisconnected(); } if (typeof peerId === 'bigint') { peerId; let peer = await this._storage.getPeerById(peerId); if (peer) { return peer; } else { const type = await helpers.getPeerType(peerId); if (type === 'user') { await this.fetchPeers(await this.invoke(new index_js_1.Raw.users.GetUsers({ id: [ new index_js_1.Raw.InputUser({ userId: peerId, accessHash: BigInt(0), }), ], }))); } else if (type === 'chat') { await this.invoke(new index_js_1.Raw.messages.GetChats({ id: [-peerId], })); } else { await this.invoke(new index_js_1.Raw.channels.GetChannels({ id: [ new index_js_1.Raw.InputChannel({ channelId: helpers.getChannelId(peerId), accessHash: BigInt(0), }), ], })); } peer = await this._storage.getPeerById(peerId); if (!peer) { throw new Errors.Exceptions.BadRequest.PeerIdInvalid(); } return peer; } } else if (typeof peerId === 'string') { peerId; if (peerId === 'self' || peerId === 'me') { return new index_js_1.Raw.InputUserSelf(); } let peer; if (peerId.includes('@')) { peer = await this._storage.getPeerByUsername(peerId.replace('@', '').trim()); if (peer) { return peer; } else { await this.invoke(new index_js_1.Raw.contacts.ResolveUsername({ username: peerId.replace('@', '').trim(), })); peer = await this._storage.getPeerByUsername(peerId.replace('@', '').trim()); if (peer) { return peer; } else { throw new Errors.Exceptions.BadRequest.PeerIdInvalid(); } } } else if (!Number.isNaN(peerId)) { peer = await this._storage.getPeerById(BigInt(peerId)); if (peer) { return peer; } else { const type = await helpers.getPeerType(BigInt(peerId)); if (type === 'user') { await this.fetchPeers(await this.invoke(new index_js_1.Raw.users.GetUsers({ id: [ new index_js_1.Raw.InputUser({ userId: BigInt(peerId), accessHash: BigInt(0), }), ], }))); } else if (type === 'chat') { await this.invoke(new index_js_1.Raw.messages.GetChats({ id: [-BigInt(peerId)], })); } else { await this.invoke(new index_js_1.Raw.channels.GetChannels({ id: [ new index_js_1.Raw.InputChannel({ channelId: helpers.getChannelId(BigInt(peerId)), accessHash: BigInt(0), }), ], })); } peer = await this._storage.getPeerById(BigInt(peerId)); if (!peer) { throw new Errors.Exceptions.BadRequest.PeerIdInvalid(); } return peer; } } else { peer = await this._storage.getPeerByPhoneNumber(peerId); if (peer) { return peer; } else { throw new Errors.Exceptions.BadRequest.PeerIdInvalid(); } } } else { throw new Errors.Exceptions.BadRequest.PeerIdInvalid(); } } startSecretChat(chatId) { return this._secretChat.start(chatId); } destroySecretChat(chatId) { return this._secretChat.destroy(chatId); } saveFile({ source, fileName, fileId, filePart, progress, }) { return Files.upload(this, source, fileName, fileId, filePart, progress); } saveFileStream({ source, fileName, progress, }) { return Files.uploadStream(this, source, fileName, progress); } downloadStream({ file, dcId, limit, offset }) { return Files.downloadStream(this, file, dcId, limit || 0, offset || BigInt(0)); } async download({ file, dcId, limit, offset }) { const pipe = new Files.File(); const stream = await Files.downloadStream(this, file, dcId, limit || 0, offset || BigInt(0)); let resolve; const promise = new Promise((res) => { resolve = res; }); pipe.on('finish', () => { return resolve(pipe.bytes.buffer); }); stream.pipe(pipe); return promise; } [Symbol.for('nodejs.util.inspect.custom')]() { const toPrint = { _: this.constructor.name, }; for (const key in this) { if (Object.prototype.hasOwnProperty.call(this, key)) { const value = this[key]; if (!key.startsWith('_') && value !== undefined && value !== null) { toPrint[key] = value; } } } return toPrint; } [Symbol.for('Deno.customInspect')]() { return String((0, platform_node_js_1.inspect)(this[Symbol.for('nodejs.util.inspect.custom')](), { colors: true })); } toJSON() { const toPrint = { _: this.constructor.name, }; for (const key in this) { if (Object.prototype.hasOwnProperty.call(this, key)) { const value = this[key]; if (!key.startsWith('_') && value !== undefined && value !== null) { if (typeof value === 'bigint') { toPrint[key] = String(value); } else if (Array.isArray(value)) { toPrint[key] = value.map((v) => (typeof v === 'bigint' ? String(v) : v)); } else { toPrint[key] = value; } } } } return toPrint; } toString() { return `[constructor of ${this.constructor.name}] ${JSON.stringify(this, null, 2)}`; } } exports.Client = Client;