diff --git a/packages/core/src/highlevel/client.ts b/packages/core/src/highlevel/client.ts index 21766c29..44cf9e9d 100644 --- a/packages/core/src/highlevel/client.ts +++ b/packages/core/src/highlevel/client.ts @@ -89,6 +89,7 @@ import { importContacts } from './methods/contacts/import-contacts.js' import { createFolder } from './methods/dialogs/create-folder.js' import { deleteFolder } from './methods/dialogs/delete-folder.js' import { editFolder } from './methods/dialogs/edit-folder.js' +import { findDialogs } from './methods/dialogs/find-dialogs.js' import { findFolder } from './methods/dialogs/find-folder.js' import { getFolders } from './methods/dialogs/get-folders.js' import { getPeerDialogs } from './methods/dialogs/get-peer-dialogs.js' @@ -1039,6 +1040,18 @@ export interface TelegramClient extends ITelegramClient { */ timeout?: number + /** + * Whether to "fire and forget" this request, + * in which case the promise will resolve as soon + * as the request is sent with an empty response. + * + * Useful for interacting with bots that don't correctly + * answer to callback queries and the request always times out. + * + * **Note**: any errors will be silently ignored. + */ + fireAndForget?: boolean + /** * Whether this is a "play game" button */ @@ -2156,6 +2169,18 @@ export interface TelegramClient extends ITelegramClient { /** Modification to be applied to this folder */ modification: Partial> }): Promise + + /** + * Try to find a dialog (dialogs) with a given peer (peers) by their ID, username or phone number. + * + * This might be an expensive call, as it will potentially iterate over all + * dialogs to find the one with the given peer + * + * **Available**: 👤 users only + * + * @throws {MtPeerNotFoundError} If a dialog with any of the given peers was not found + */ + findDialogs(peers: MaybeArray): Promise /** * Find a folder by its parameter. * @@ -5242,6 +5267,8 @@ export interface TelegramClient extends ITelegramClient { * while also normalizing and removing * peers that can't be normalized to that type. * + * If a peer was not found, it will be skipped. + * * Uses async pool internally, with a concurrent limit of 8 * * @param peerIds Peer Ids @@ -5254,11 +5281,13 @@ export interface TelegramClient extends ITelegramClient { /** * Get multiple `InputPeer`s at once. * + * If a peer was not found, `null` will be returned instead + * * Uses async pool internally, with a concurrent limit of 8 * * @param peerIds Peer Ids */ - resolvePeerMany(peerIds: InputPeerLike[]): Promise + resolvePeerMany(peerIds: InputPeerLike[]): Promise<(tl.TypeInputPeer | null)[]> /** * Get the `InputPeer` of a known peer id. @@ -5692,6 +5721,9 @@ TelegramClient.prototype.deleteFolder = function (...args) { TelegramClient.prototype.editFolder = function (...args) { return editFolder(this._client, ...args) } +TelegramClient.prototype.findDialogs = function (...args) { + return findDialogs(this._client, ...args) +} TelegramClient.prototype.findFolder = function (...args) { return findFolder(this._client, ...args) } diff --git a/packages/core/src/highlevel/methods.ts b/packages/core/src/highlevel/methods.ts index d2c6d68f..016ae892 100644 --- a/packages/core/src/highlevel/methods.ts +++ b/packages/core/src/highlevel/methods.ts @@ -81,6 +81,7 @@ export { importContacts } from './methods/contacts/import-contacts.js' export { createFolder } from './methods/dialogs/create-folder.js' export { deleteFolder } from './methods/dialogs/delete-folder.js' export { editFolder } from './methods/dialogs/edit-folder.js' +export { findDialogs } from './methods/dialogs/find-dialogs.js' export { findFolder } from './methods/dialogs/find-folder.js' export { getFolders } from './methods/dialogs/get-folders.js' export { getPeerDialogs } from './methods/dialogs/get-peer-dialogs.js' diff --git a/packages/core/src/highlevel/methods/dialogs/find-dialogs.ts b/packages/core/src/highlevel/methods/dialogs/find-dialogs.ts new file mode 100644 index 00000000..5478c2b8 --- /dev/null +++ b/packages/core/src/highlevel/methods/dialogs/find-dialogs.ts @@ -0,0 +1,107 @@ +import { tl } from '@mtcute/tl' + +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' +import { MtPeerNotFoundError } from '../../types/errors.js' +import { Dialog } from '../../types/messages/dialog.js' +import { resolvePeerMany } from '../users/resolve-peer-many.js' +import { getPeerDialogs } from './get-peer-dialogs.js' +import { iterDialogs } from './iter-dialogs.js' + +// @available=user +/** + * Try to find a dialog (dialogs) with a given peer (peers) by their ID, username or phone number. + * + * This might be an expensive call, as it will potentially iterate over all + * dialogs to find the one with the given peer + * + * @throws {MtPeerNotFoundError} If a dialog with any of the given peers was not found + */ +export async function findDialogs(client: ITelegramClient, peers: MaybeArray): Promise { + if (!Array.isArray(peers)) peers = [peers] + const resolved = await resolvePeerMany(client, peers) + + // now we need to split `peers` into two parts: ids that we could resolve and those we couldn't + // those that we couldn't we'll iterate over all dialogs and try to find them by username/id + // those that we could we'll use getPeerDialogs + + // id -> idx + const notFoundIds = new Map() + // username -> idx + const notFoundUsernames = new Map() + let notFoundCount = 0 + + const foundInputPeers: tl.TypeInputPeer[] = [] + const foundIdxToOriginalIdx = new Map() + + for (let i = 0; i < peers.length; i++) { + const input = peers[i] + const resolvedPeer = resolved[i] + + if (!resolvedPeer) { + if (typeof input === 'number') { + notFoundIds.set(input, i) + } else { + notFoundUsernames.set(input, i) + } + + notFoundCount += 1 + + continue + } + + foundInputPeers.push(resolvedPeer) + foundIdxToOriginalIdx.set(foundInputPeers.length - 1, i) + } + + const dialogs = await getPeerDialogs(client, foundInputPeers) + + if (foundInputPeers.length === peers.length) { + return dialogs + } + + const ret = new Array(peers.length) + + // populate found dialogs + for (const [idx, origIdx] of foundIdxToOriginalIdx) { + ret[origIdx] = dialogs[idx] + } + + // now we need to iterate over all dialogs and try to find the rest + for await (const dialog of iterDialogs(client, { + archived: 'keep', + })) { + const chat = dialog.chat + + const idxById = notFoundIds.get(chat.id) + + if (idxById !== undefined) { + ret[idxById] = dialog + notFoundIds.delete(chat.id) + notFoundCount -= 1 + } + + if (notFoundCount === 0) break + + if (!chat.username) continue + + const idxByUsername = notFoundUsernames.get(chat.username) + + if (idxByUsername !== undefined) { + ret[idxByUsername] = dialog + notFoundUsernames.delete(chat.username) + notFoundCount -= 1 + } + + if (notFoundCount === 0) break + } + + // if we still have some dialogs that we couldn't find, fail + + if (notFoundCount > 0) { + const notFound = [...notFoundIds.keys(), ...notFoundUsernames.keys()] + throw new MtPeerNotFoundError(`Could not find dialogs with peers: ${notFound.join(', ')}`) + } + + return ret +}