@tgsnake/core
Version:
Pure Telegram MTProto library for nodejs
462 lines (461 loc) • 19 kB
JavaScript
"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;