diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 92d394a6..0eb480e2 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -24,6 +24,7 @@ import { getHistory } from './methods/messages/get-history' import { getMessages } from './methods/messages/get-messages' import { iterHistory } from './methods/messages/iter-history' import { _parseEntities } from './methods/messages/parse-entities' +import { searchGlobal } from './methods/messages/search-global' import { searchMessages } from './methods/messages/search-messages' import { sendPhoto } from './methods/messages/send-photo' import { sendText } from './methods/messages/send-text' @@ -591,6 +592,46 @@ export class TelegramClient extends BaseTelegramClient { ): Promise<[string, tl.TypeMessageEntity[] | undefined]> { return _parseEntities.apply(this, arguments) } + /** + * Search for messages globally from all of your chats + * + * **Note**: Due to Telegram limitations, you can only get up to ~10000 messages + * + * @param params Search parameters + */ + searchGlobal(params?: { + /** + * Text query string. Use `"@"` to search for mentions. + * + * Defaults to `""` (empty string) + */ + query?: string + + /** + * Limits the number of messages to be retrieved. + * + * By default, no limit is applied and all messages are returned + */ + limit?: number + + /** + * Filter the results using some filter. + * Defaults to {@link SearchFilters.Empty} (i.e. will return all messages) + * + * @link SearchFilters + */ + filter?: tl.TypeMessagesFilter + + /** + * Chunk size, which will be passed as `limit` parameter + * for `messages.search`. Usually you shouldn't care about this. + * + * Defaults to `100` + */ + chunkSize?: number + }): AsyncIterableIterator { + return searchGlobal.apply(this, arguments) + } /** * Search for messages inside a specific chat * diff --git a/packages/client/src/methods/messages/search-global.ts b/packages/client/src/methods/messages/search-global.ts new file mode 100644 index 00000000..f43e0199 --- /dev/null +++ b/packages/client/src/methods/messages/search-global.ts @@ -0,0 +1,98 @@ +import { TelegramClient } from '../../client' +import { Message, MtCuteTypeAssertionError } from '../../types' +import { tl } from '@mtcute/tl' +import { createUsersChatsIndex } from '../../utils/peer-utils' +import { SearchFilters } from '../../types' + +/** + * Search for messages globally from all of your chats + * + * **Note**: Due to Telegram limitations, you can only get up to ~10000 messages + * + * @param params Search parameters + * @internal + */ +export async function* searchGlobal( + this: TelegramClient, + params?: { + /** + * Text query string. Use `"@"` to search for mentions. + * + * Defaults to `""` (empty string) + */ + query?: string + + /** + * Limits the number of messages to be retrieved. + * + * By default, no limit is applied and all messages are returned + */ + limit?: number + + /** + * Filter the results using some filter. + * Defaults to {@link SearchFilters.Empty} (i.e. will return all messages) + * + * @link SearchFilters + */ + filter?: tl.TypeMessagesFilter + + /** + * Chunk size, which will be passed as `limit` parameter + * for `messages.search`. Usually you shouldn't care about this. + * + * Defaults to `100` + */ + chunkSize?: number + } +): AsyncIterableIterator { + if (!params) params = {} + + let current = 0 + + const total = params.limit || Infinity + const limit = Math.min(params.chunkSize || 100, total) + + let offsetDate = 0 + let offsetPeer = { _: 'inputPeerEmpty' } as tl.TypeInputPeer + let offsetId = 0 + + for (;;) { + const res = await this.call({ + _: 'messages.searchGlobal', + q: params.query || '', + filter: params.filter || SearchFilters.Empty, + minDate: 0, + maxDate: 0, + offsetId, + offsetRate: offsetDate, + offsetPeer: offsetPeer, + limit: Math.min(limit, total - current), + }) + + if (res._ === 'messages.messagesNotModified') + throw new MtCuteTypeAssertionError( + 'searchMessages', + '!messages.messagesNotModified', + res._ + ) + + const { users, chats } = createUsersChatsIndex(res) + + const msgs = res.messages.map( + (msg) => new Message(this, msg, users, chats) + ) + + if (!msgs.length) break + + const last = msgs[msgs.length - 1] + offsetDate = last.raw.date + offsetPeer = last.chat.inputPeer + offsetId = last.id + + yield* msgs + + current += msgs.length + if (current >= total) break + } +} diff --git a/packages/client/src/methods/messages/search-messages.ts b/packages/client/src/methods/messages/search-messages.ts index 6ea16da3..ff20fa71 100644 --- a/packages/client/src/methods/messages/search-messages.ts +++ b/packages/client/src/methods/messages/search-messages.ts @@ -88,7 +88,7 @@ export async function* searchMessages( maxDate: 0, offsetId: 0, addOffset: offset, - limit, + limit: Math.min(limit, total - current), minId: 0, maxId: 0, fromId: fromUser,