dograma
Version:
NodeJS/Browser MTProto API Telegram client library,
458 lines (457 loc) • 16.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports._selfId = exports._getInputNotify = exports._getInputDialog = exports._getPeer = exports.getPeerId = exports._getEntityFromString = exports.getInputEntity = exports.getEntity = exports.isUserAuthorized = exports.isBot = exports.getMe = exports.invoke = void 0;
const tl_1 = require("../tl");
const Utils_1 = require("../Utils");
const Helpers_1 = require("../Helpers");
const __1 = require("../");
const big_integer_1 = __importDefault(require("big-integer"));
const Logger_1 = require("../extensions/Logger");
// UserMethods {
// region Invoking Telegram request
/** @hidden */
async function invoke(client, request, sender) {
if (request.classType !== "request") {
throw new Error("You can only invoke MTProtoRequests");
}
if (!sender) {
sender = client._sender;
}
if (sender == undefined) {
throw new Error("Cannot send requests while disconnected. You need to call .connect()");
}
await request.resolve(client, __1.utils);
client._lastRequest = new Date().getTime();
let attempt;
for (attempt = 0; attempt < client._requestRetries; attempt++) {
try {
const promise = sender.send(request);
const result = await promise;
client.session.processEntities(result);
client._entityCache.add(result);
return result;
}
catch (e) {
if (e instanceof __1.errors.ServerError ||
e.errorMessage === "RPC_CALL_FAIL" ||
e.errorMessage === "RPC_MCGET_FAIL") {
client._log.warn(`Telegram is having internal issues ${e.constructor.name}`);
await (0, Helpers_1.sleep)(2000);
}
else if (e instanceof __1.errors.FloodWaitError ||
e instanceof __1.errors.FloodTestPhoneWaitError) {
if (e.seconds <= client.floodSleepThreshold) {
client._log.info(`Sleeping for ${e.seconds}s on flood wait (Caused by ${request.className})`);
await (0, Helpers_1.sleep)(e.seconds * 1000);
}
else {
throw e;
}
}
else if (e instanceof __1.errors.PhoneMigrateError ||
e instanceof __1.errors.NetworkMigrateError ||
e instanceof __1.errors.UserMigrateError) {
client._log.info(`Phone migrated to ${e.newDc}`);
const shouldRaise = e instanceof __1.errors.PhoneMigrateError ||
e instanceof __1.errors.NetworkMigrateError;
if (shouldRaise && (await client.isUserAuthorized())) {
throw e;
}
await client._switchDC(e.newDc);
}
else {
throw e;
}
}
}
throw new Error(`Request was unsuccessful ${attempt} time(s)`);
}
exports.invoke = invoke;
/** @hidden */
async function getMe(client, inputPeer = false) {
if (inputPeer && client._selfInputPeer) {
return client._selfInputPeer;
}
const me = (await client.invoke(new tl_1.Api.users.GetUsers({ id: [new tl_1.Api.InputUserSelf()] })))[0];
client._bot = me.bot;
if (!client._selfInputPeer) {
client._selfInputPeer = __1.utils.getInputPeer(me, false);
}
return inputPeer ? client._selfInputPeer : me;
}
exports.getMe = getMe;
/** @hidden */
async function isBot(client) {
if (client._bot === undefined) {
const me = await client.getMe();
if (me) {
return !(me instanceof tl_1.Api.InputPeerUser) ? me.bot : undefined;
}
}
return client._bot;
}
exports.isBot = isBot;
/** @hidden */
async function isUserAuthorized(client) {
try {
await client.invoke(new tl_1.Api.updates.GetState());
return true;
}
catch (e) {
return false;
}
}
exports.isUserAuthorized = isUserAuthorized;
/** @hidden */
async function getEntity(client, entity) {
const single = !(0, Helpers_1.isArrayLike)(entity);
let entityArray = [];
if ((0, Helpers_1.isArrayLike)(entity)) {
entityArray = entity;
}
else {
entityArray.push(entity);
}
const inputs = [];
for (const x of entityArray) {
if (typeof x === "string") {
const valid = (0, Utils_1.parseID)(x);
if (valid) {
inputs.push(await client.getInputEntity(valid));
}
else {
inputs.push(x);
}
}
else {
inputs.push(await client.getInputEntity(x));
}
}
const lists = new Map([
[Helpers_1._EntityType.USER, []],
[Helpers_1._EntityType.CHAT, []],
[Helpers_1._EntityType.CHANNEL, []],
]);
for (const x of inputs) {
try {
lists.get((0, Helpers_1._entityType)(x)).push(x);
}
catch (e) { }
}
let users = lists.get(Helpers_1._EntityType.USER);
let chats = lists.get(Helpers_1._EntityType.CHAT);
let channels = lists.get(Helpers_1._EntityType.CHANNEL);
if (users.length) {
users = await client.invoke(new tl_1.Api.users.GetUsers({
id: users,
}));
}
if (chats.length) {
const chatIds = chats.map((x) => x.chatId);
chats = (await client.invoke(new tl_1.Api.messages.GetChats({ id: chatIds }))).chats;
}
if (channels.length) {
channels = (await client.invoke(new tl_1.Api.channels.GetChannels({ id: channels }))).chats;
}
const idEntity = new Map();
for (const user of users) {
idEntity.set((0, Utils_1.getPeerId)(user), user);
}
for (const channel of channels) {
idEntity.set((0, Utils_1.getPeerId)(channel), channel);
}
for (const chat of chats) {
idEntity.set((0, Utils_1.getPeerId)(chat), chat);
}
const result = [];
for (const x of inputs) {
if (typeof x === "string") {
result.push(await _getEntityFromString(client, x));
}
else if (!(x instanceof tl_1.Api.InputPeerSelf)) {
result.push(idEntity.get((0, Utils_1.getPeerId)(x)));
}
else {
for (const [key, u] of idEntity.entries()) {
if (u instanceof tl_1.Api.User && u.self) {
result.push(u);
break;
}
}
}
}
return single ? result[0] : result;
}
exports.getEntity = getEntity;
/** @hidden */
async function getInputEntity(client, peer) {
// Short-circuit if the input parameter directly maps to an InputPeer
try {
return __1.utils.getInputPeer(peer);
// eslint-disable-next-line no-empty
}
catch (e) { }
// Next in priority is having a peer (or its ID) cached in-memory
try {
if (typeof peer == "string") {
const valid = (0, Utils_1.parseID)(peer);
if (valid) {
const res = client._entityCache.get(peer);
if (res) {
return res;
}
}
}
if (typeof peer === "number" ||
typeof peer === "bigint" ||
big_integer_1.default.isInstance(peer)) {
const res = client._entityCache.get(peer.toString());
if (res) {
return res;
}
}
// 0x2d45687 == crc32(b'Peer')
if (typeof peer == "object" &&
!big_integer_1.default.isInstance(peer) &&
peer.SUBCLASS_OF_ID === 0x2d45687) {
const res = client._entityCache.get(__1.utils.getPeerId(peer));
if (res) {
return res;
}
}
// eslint-disable-next-line no-empty
}
catch (e) { }
// Then come known strings that take precedence
if (typeof peer == "string") {
if (["me", "this", "self"].includes(peer)) {
return new tl_1.Api.InputPeerSelf();
}
}
// No InputPeer, cached peer, or known string. Fetch from disk cache
try {
if (peer != undefined) {
return client.session.getInputEntity(peer);
}
// eslint-disable-next-line no-empty
}
catch (e) { }
// Only network left to try
if (typeof peer === "string") {
return __1.utils.getInputPeer(await _getEntityFromString(client, peer));
}
// If we're a bot and the user has messaged us privately users.getUsers
// will work with accessHash = 0. Similar for channels.getChannels.
// If we're not a bot but the user is in our contacts, it seems to work
// regardless. These are the only two special-cased requests.
if (typeof peer === "number") {
peer = (0, Helpers_1.returnBigInt)(peer);
}
peer = __1.utils.getPeer(peer);
if (peer instanceof tl_1.Api.PeerUser) {
const users = await client.invoke(new tl_1.Api.users.GetUsers({
id: [
new tl_1.Api.InputUser({
userId: peer.userId,
accessHash: big_integer_1.default.zero,
}),
],
}));
if (users.length && !(users[0] instanceof tl_1.Api.UserEmpty)) {
// If the user passed a valid ID they expect to work for
// channels but would be valid for users, we get UserEmpty.
// Avoid returning the invalid empty input peer for that.
//
// We *could* try to guess if it's a channel first, and if
// it's not, work as a chat and try to validate it through
// another request, but that becomes too much work.
return __1.utils.getInputPeer(users[0]);
}
}
else if (peer instanceof tl_1.Api.PeerChat) {
return new tl_1.Api.InputPeerChat({
chatId: peer.chatId,
});
}
else if (peer instanceof tl_1.Api.PeerChannel) {
try {
const channels = await client.invoke(new tl_1.Api.channels.GetChannels({
id: [
new tl_1.Api.InputChannel({
channelId: peer.channelId,
accessHash: big_integer_1.default.zero,
}),
],
}));
return __1.utils.getInputPeer(channels.chats[0]);
}
catch (e) {
if (client._log.canSend(Logger_1.LogLevel.ERROR)) {
console.error(e);
}
}
}
throw new Error(`Could not find the input entity for ${JSON.stringify(peer)}.
Please read https://` +
"docs.telethon.dev/en/latest/concepts/entities.html to" +
" find out more details.");
}
exports.getInputEntity = getInputEntity;
/** @hidden */
async function _getEntityFromString(client, string) {
const phone = __1.utils.parsePhone(string);
if (phone) {
try {
const result = await client.invoke(new tl_1.Api.contacts.GetContacts({
hash: big_integer_1.default.zero,
}));
if (!(result instanceof tl_1.Api.contacts.ContactsNotModified)) {
for (const user of result.users) {
if (user instanceof tl_1.Api.User && user.phone === phone) {
return user;
}
}
}
}
catch (e) {
if (e.errorMessage === "BOT_METHOD_INVALID") {
throw new Error("Cannot get entity by phone number as a " +
"bot (try using integer IDs, not strings)");
}
throw e;
}
}
const id = __1.utils.parseID(string);
if (id != undefined) {
return getInputEntity(client, id);
}
else if (["me", "this"].includes(string.toLowerCase())) {
return client.getMe();
}
else {
const { username, isInvite } = __1.utils.parseUsername(string);
if (isInvite) {
const invite = await client.invoke(new tl_1.Api.messages.CheckChatInvite({
hash: username,
}));
if (invite instanceof tl_1.Api.ChatInvite) {
throw new Error("Cannot get entity from a channel (or group) " +
"that you are not part of. Join the group and retry");
}
else if (invite instanceof tl_1.Api.ChatInviteAlready) {
return invite.chat;
}
}
else if (username) {
try {
const result = await client.invoke(new tl_1.Api.contacts.ResolveUsername({ username: username }));
const pid = __1.utils.getPeerId(result.peer, false);
if (result.peer instanceof tl_1.Api.PeerUser) {
for (const x of result.users) {
if ((0, Helpers_1.returnBigInt)(x.id).equals((0, Helpers_1.returnBigInt)(pid))) {
return x;
}
}
}
else {
for (const x of result.chats) {
if ((0, Helpers_1.returnBigInt)(x.id).equals((0, Helpers_1.returnBigInt)(pid))) {
return x;
}
}
}
}
catch (e) {
if (e.errorMessage === "USERNAME_NOT_OCCUPIED") {
throw new Error(`No user has "${username}" as username`);
}
throw e;
}
}
}
throw new Error(`Cannot find any entity corresponding to "${string}"`);
}
exports._getEntityFromString = _getEntityFromString;
/** @hidden */
async function getPeerId(client, peer, addMark = true) {
if (typeof peer == "string") {
const valid = (0, Utils_1.parseID)(peer);
if (valid) {
return __1.utils.getPeerId(peer, addMark);
}
else {
peer = await client.getInputEntity(peer);
}
}
if (typeof peer == "number" ||
typeof peer == "bigint" ||
big_integer_1.default.isInstance(peer)) {
return __1.utils.getPeerId(peer, addMark);
}
if (peer.SUBCLASS_OF_ID == 0x2d45687 || peer.SUBCLASS_OF_ID == 0xc91c90b6) {
peer = await client.getInputEntity(peer);
}
if (peer instanceof tl_1.Api.InputPeerSelf) {
peer = await client.getMe(true);
}
return __1.utils.getPeerId(peer, addMark);
}
exports.getPeerId = getPeerId;
/** @hidden */
async function _getPeer(client, peer) {
if (!peer) {
return undefined;
}
const [i, cls] = __1.utils.resolveId((0, Helpers_1.returnBigInt)(await client.getPeerId(peer)));
return new cls({
userId: i,
channelId: i,
chatId: i,
});
}
exports._getPeer = _getPeer;
/** @hidden */
async function _getInputDialog(client, dialog) {
try {
if (dialog.SUBCLASS_OF_ID == 0xa21c9795) {
// crc32(b'InputDialogPeer')
dialog.peer = await client.getInputEntity(dialog.peer);
return dialog;
}
else if (dialog.SUBCLASS_OF_ID == 0xc91c90b6) {
//crc32(b'InputPeer')
return new tl_1.Api.InputDialogPeer({
peer: dialog,
});
}
}
catch (e) { }
return new tl_1.Api.InputDialogPeer({
peer: dialog,
});
}
exports._getInputDialog = _getInputDialog;
/** @hidden */
async function _getInputNotify(client, notify) {
try {
if (notify.SUBCLASS_OF_ID == 0x58981615) {
if (notify instanceof tl_1.Api.InputNotifyPeer) {
notify.peer = await client.getInputEntity(notify.peer);
}
return notify;
}
}
catch (e) { }
return new tl_1.Api.InputNotifyPeer({
peer: await client.getInputEntity(notify),
});
}
exports._getInputNotify = _getInputNotify;
/** @hidden */
function _selfId(client) {
return client._selfInputPeer ? client._selfInputPeer.userId : undefined;
}
exports._selfId = _selfId;