feat: more non-iterable versions of methods
This commit is contained in:
parent
55c4f296fb
commit
ea7eabf0be
18 changed files with 902 additions and 484 deletions
|
@ -33,6 +33,7 @@ async function addSingleMethod(state, fileName) {
|
|||
const fileFullText = await fs.promises.readFile(fileName, 'utf-8')
|
||||
const program = ts.createSourceFile(path.basename(fileName), fileFullText, ts.ScriptTarget.ES2018, true)
|
||||
const relPath = path.relative(targetDir, fileName).replace(/\\/g, '/') // replace path delim to unix
|
||||
const module = `./${relPath.replace(/\.ts$/, '')}`
|
||||
|
||||
state.files[relPath] = fileFullText
|
||||
|
||||
|
@ -53,6 +54,7 @@ async function addSingleMethod(state, fileName) {
|
|||
|
||||
for (const stmt of program.statements) {
|
||||
const isCopy = checkForFlag(stmt, '@copy')
|
||||
const isTypeExported = checkForFlag(stmt, '@exported')
|
||||
|
||||
if (stmt.kind === ts.SyntaxKind.ImportDeclaration) {
|
||||
if (!isCopy) continue
|
||||
|
@ -159,19 +161,6 @@ async function addSingleMethod(state, fileName) {
|
|||
)
|
||||
}
|
||||
|
||||
const returnsExported = (
|
||||
stmt.body ?
|
||||
ts.getLeadingCommentRanges(fileFullText, stmt.body.pos + 2) ||
|
||||
(stmt.statements &&
|
||||
stmt.statements.length &&
|
||||
ts.getLeadingCommentRanges(fileFullText, stmt.statements[0].pos)) ||
|
||||
[] :
|
||||
[]
|
||||
)
|
||||
.map((range) => fileFullText.substring(range.pos, range.end))
|
||||
.join('\n')
|
||||
.includes('@returns-exported')
|
||||
|
||||
// overloads
|
||||
const isOverload = !stmt.body
|
||||
|
||||
|
@ -193,20 +182,11 @@ async function addSingleMethod(state, fileName) {
|
|||
hasOverloads: hasOverloads[name] && !isOverload,
|
||||
})
|
||||
|
||||
const module = `./${relPath.replace(/\.ts$/, '')}`
|
||||
|
||||
if (!(module in state.imports)) {
|
||||
state.imports[module] = new Set()
|
||||
}
|
||||
|
||||
state.imports[module].add(name)
|
||||
|
||||
if (returnsExported) {
|
||||
let returnType = stmt.type.getText()
|
||||
let m = returnType.match(/^Promise<(.+)>$/)
|
||||
if (m) returnType = m[1]
|
||||
state.imports[module].add(returnType)
|
||||
}
|
||||
}
|
||||
} else if (stmt.kind === ts.SyntaxKind.InterfaceDeclaration) {
|
||||
if (isCopy) {
|
||||
|
@ -217,8 +197,22 @@ async function addSingleMethod(state, fileName) {
|
|||
continue
|
||||
}
|
||||
|
||||
const isExported = (stmt.modifiers || []).find((mod) => mod.kind === ts.SyntaxKind.ExportKeyword)
|
||||
|
||||
if (isTypeExported) {
|
||||
if (!isExported) {
|
||||
throwError(stmt, fileName, 'Exported interfaces must be exported')
|
||||
}
|
||||
|
||||
if (!(module in state.imports)) {
|
||||
state.imports[module] = new Set()
|
||||
}
|
||||
|
||||
state.imports[module].add(stmt.name.escapedText)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!checkForFlag(stmt, '@extension')) continue
|
||||
const isExported = (stmt.modifiers || []).find((mod) => mod.kind === 92 /* ExportKeyword */)
|
||||
|
||||
if (isExported) {
|
||||
throwError(isExported, fileName, 'Extension interfaces must not be imported')
|
||||
|
@ -233,8 +227,22 @@ async function addSingleMethod(state, fileName) {
|
|||
code: member.getText(),
|
||||
})
|
||||
}
|
||||
} else if (stmt.kind === ts.SyntaxKind.TypeAliasDeclaration && isTypeExported) {
|
||||
const isExported = (stmt.modifiers || []).find((mod) => mod.kind === ts.SyntaxKind.ExportKeyword)
|
||||
|
||||
if (!isExported) {
|
||||
throwError(stmt, fileName, 'Exported type aliases must be exported')
|
||||
}
|
||||
|
||||
if (!(module in state.imports)) {
|
||||
state.imports[module] = new Set()
|
||||
}
|
||||
|
||||
state.imports[module].add(stmt.name.escapedText)
|
||||
} else if (isCopy) {
|
||||
state.copy.push({ from: relPath, code: stmt.getFullText().trim() })
|
||||
} else if (isTypeExported) {
|
||||
throwError(stmt, fileName, 'Only functions and interfaces can be exported')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ import { editInviteLink } from './methods/invite-links/edit-invite-link'
|
|||
import { exportInviteLink } from './methods/invite-links/export-invite-link'
|
||||
import { getInviteLink } from './methods/invite-links/get-invite-link'
|
||||
import { getInviteLinkMembers } from './methods/invite-links/get-invite-link-members'
|
||||
import { getInviteLinks } from './methods/invite-links/get-invite-links'
|
||||
import { getInviteLinks, GetInviteLinksOffset } from './methods/invite-links/get-invite-links'
|
||||
import { getPrimaryInviteLink } from './methods/invite-links/get-primary-invite-link'
|
||||
import { hideAllJoinRequests } from './methods/invite-links/hide-all-join-requests'
|
||||
import { hideJoinRequest } from './methods/invite-links/hide-join-request'
|
||||
|
@ -118,19 +118,23 @@ import { editMessage } from './methods/messages/edit-message'
|
|||
import { _findMessageInUpdate } from './methods/messages/find-in-update'
|
||||
import { forwardMessages } from './methods/messages/forward-messages'
|
||||
import { _getDiscussionMessage, getDiscussionMessage } from './methods/messages/get-discussion-message'
|
||||
import { getHistory } from './methods/messages/get-history'
|
||||
import { getHistory, GetHistoryOffset } from './methods/messages/get-history'
|
||||
import { getMessageGroup } from './methods/messages/get-message-group'
|
||||
import { getMessageReactions } from './methods/messages/get-message-reactions'
|
||||
import { getMessages } from './methods/messages/get-messages'
|
||||
import { getMessagesUnsafe } from './methods/messages/get-messages-unsafe'
|
||||
import { getReactionUsers } from './methods/messages/get-reaction-users'
|
||||
import { getReactionUsers, GetReactionUsersOffset } from './methods/messages/get-reaction-users'
|
||||
import { getScheduledMessages } from './methods/messages/get-scheduled-messages'
|
||||
import { iterHistory } from './methods/messages/iter-history'
|
||||
import { iterReactionUsers } from './methods/messages/iter-reaction-users'
|
||||
import { iterSearchGlobal } from './methods/messages/iter-search-global'
|
||||
import { iterSearchMessages } from './methods/messages/iter-search-messages'
|
||||
import { _parseEntities } from './methods/messages/parse-entities'
|
||||
import { pinMessage } from './methods/messages/pin-message'
|
||||
import { readHistory } from './methods/messages/read-history'
|
||||
import { readReactions } from './methods/messages/read-reactions'
|
||||
import { searchGlobal } from './methods/messages/search-global'
|
||||
import { searchMessages } from './methods/messages/search-messages'
|
||||
import { searchGlobal, SearchGlobalOffset } from './methods/messages/search-global'
|
||||
import { searchMessages, SearchMessagesOffset } from './methods/messages/search-messages'
|
||||
import { sendCopy } from './methods/messages/send-copy'
|
||||
import { sendMedia } from './methods/messages/send-media'
|
||||
import { sendMediaGroup } from './methods/messages/send-media-group'
|
||||
|
@ -183,6 +187,7 @@ import { deleteProfilePhotos } from './methods/users/delete-profile-photos'
|
|||
import { getCommonChats } from './methods/users/get-common-chats'
|
||||
import { getMe } from './methods/users/get-me'
|
||||
import { getMyUsername } from './methods/users/get-my-username'
|
||||
import { getProfilePhoto } from './methods/users/get-profile-photo'
|
||||
import { getProfilePhotos } from './methods/users/get-profile-photos'
|
||||
import { getUsers } from './methods/users/get-users'
|
||||
import { iterProfilePhotos } from './methods/users/iter-profile-photos'
|
||||
|
@ -2055,16 +2060,11 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
limit?: number
|
||||
|
||||
/**
|
||||
* Offset date used as an anchor for pagination.
|
||||
* Offset for pagination.
|
||||
*/
|
||||
offsetDate?: Date | number
|
||||
|
||||
/**
|
||||
* Offset link used as an anchor for pagination
|
||||
*/
|
||||
offsetLink?: string
|
||||
offset?: GetInviteLinksOffset
|
||||
},
|
||||
): Promise<ArrayPaginated<ChatInviteLink, { date: number; link: string }>>
|
||||
): Promise<ArrayPaginated<ChatInviteLink, GetInviteLinksOffset>>
|
||||
/**
|
||||
* Get primary invite link of a chat
|
||||
*
|
||||
|
@ -2512,7 +2512,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
getDiscussionMessage(peer: InputPeerLike, message: number): Promise<Message | null>
|
||||
/**
|
||||
* Iterate through a chat history sequentially.
|
||||
* Get chat history.
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional fetch parameters
|
||||
|
@ -2523,61 +2523,58 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* By default, no limit is applied and all messages
|
||||
* are returned.
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Sequential number of the first message to be returned.
|
||||
* Defaults to 0 (most recent message).
|
||||
*
|
||||
* Negative values are also accepted and are useful
|
||||
* in case you set `offsetId` or `offsetDate`.
|
||||
* Offset for pagination
|
||||
*/
|
||||
offset?: number
|
||||
offset?: GetHistoryOffset
|
||||
|
||||
/**
|
||||
* Pass a message identifier as an offset to retrieve
|
||||
* only older messages starting from that message
|
||||
* Additional offset from {@link offset}, in resulting messages.
|
||||
*
|
||||
* This can be used for advanced use cases, like:
|
||||
* - Loading 20 messages newer than message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -20, limit = 20`
|
||||
* - Loading 20 messages around message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -10, limit = 20`
|
||||
*
|
||||
* @default `0` (disabled)
|
||||
*/
|
||||
offsetId?: number
|
||||
|
||||
addOffset?: number
|
||||
|
||||
/**
|
||||
* Minimum message ID to return
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
minId?: number
|
||||
|
||||
/**
|
||||
* Maximum message ID to return.
|
||||
*
|
||||
* > *Seems* to work the same as {@link offsetId}
|
||||
* Unless {@link addOffset} is used, this will work the same as {@link offset}.
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
maxId?: number
|
||||
|
||||
/**
|
||||
* Pass a date (`Date` or Unix time in ms) as an offset to retrieve
|
||||
* only older messages starting from that date.
|
||||
*/
|
||||
offsetDate?: number | Date
|
||||
|
||||
/**
|
||||
* Pass `true` to retrieve messages in reversed order (from older to recent)
|
||||
* Whether to retrieve messages in reversed order (from older to recent),
|
||||
* starting from {@link offset} (inclusive).
|
||||
*
|
||||
* > **Note**: Using `reverse=true` requires you to pass offset from which to start
|
||||
* > fetching the messages "downwards". If you call `getHistory` with `reverse=true`
|
||||
* > and without any offset, it will return an empty array.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
reverse?: boolean
|
||||
|
||||
/**
|
||||
* Chunk size. Usually you shouldn't care about this.
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message>
|
||||
): Promise<ArrayPaginated<Message, GetHistoryOffset>>
|
||||
/**
|
||||
* Get all messages inside of a message group
|
||||
*
|
||||
|
@ -2678,20 +2675,18 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
emoji?: InputReaction
|
||||
|
||||
/**
|
||||
* Limit the number of events returned.
|
||||
* Limit the number of users returned.
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all events are returned
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
* Offset for pagination
|
||||
*/
|
||||
chunkSize?: number
|
||||
offset?: GetReactionUsersOffset
|
||||
},
|
||||
): AsyncIterableIterator<PeerReaction>
|
||||
): Promise<ArrayPaginated<PeerReaction, GetReactionUsersOffset>>
|
||||
/**
|
||||
* Get a single scheduled message in chat by its ID
|
||||
*
|
||||
|
@ -2709,6 +2704,111 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* @param messageIds Scheduled messages IDs
|
||||
*/
|
||||
getScheduledMessages(chatId: InputPeerLike, messageIds: number[]): Promise<(Message | null)[]>
|
||||
/**
|
||||
* Iterate over chat history. Wrapper over {@link getHistory}
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional fetch parameters
|
||||
*/
|
||||
iterHistory(
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getHistory']>[1] & {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* @default Infinity, i.e. all messages
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size. Usually you shouldn't care about this.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message>
|
||||
/**
|
||||
* Iterate over users who have reacted to the message.
|
||||
*
|
||||
* Wrapper over {@link getReactionUsers}.
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param messageId Message ID
|
||||
* @param params
|
||||
*/
|
||||
iterReactionUsers(
|
||||
chatId: InputPeerLike,
|
||||
messageId: number,
|
||||
params?: Parameters<TelegramClient['getReactionUsers']>[2] & {
|
||||
/**
|
||||
* Limit the number of events returned.
|
||||
*
|
||||
* @default `Infinity`, i.e. all events are returned
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, usually not needed.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<PeerReaction>
|
||||
/**
|
||||
* Search for messages globally from all of your chats.
|
||||
*
|
||||
* Iterable version of {@link searchGlobal}
|
||||
*
|
||||
* **Note**: Due to Telegram limitations, you can only get up to ~10000 messages
|
||||
*
|
||||
* @param params Search parameters
|
||||
*/
|
||||
iterSearchGlobal(
|
||||
params?: Parameters<TelegramClient['searchGlobal']>[0] & {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* @default `Infinity`, i.e. all messages are returned
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, which will be passed as `limit` parameter
|
||||
* for `messages.search`. Usually you shouldn't care about this.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message>
|
||||
/**
|
||||
* Search for messages inside a specific chat
|
||||
*
|
||||
* Iterable version of {@link searchMessages}
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional search parameters
|
||||
*/
|
||||
iterSearchMessages(
|
||||
params?: Parameters<TelegramClient['searchMessages']>[0] & {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* @default `Infinity`, i.e. all messages are returned
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, which will be passed as `limit` parameter
|
||||
* for `messages.search`. Usually you shouldn't care about this.
|
||||
*
|
||||
* @default `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message>
|
||||
|
||||
_parseEntities(
|
||||
text?: string | FormattedString<string>,
|
||||
|
@ -2752,14 +2852,14 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
/**
|
||||
* Text query string. Use `"@"` to search for mentions.
|
||||
*
|
||||
* Defaults to `""` (empty string)
|
||||
* @default `""` (empty string)
|
||||
*/
|
||||
query?: string
|
||||
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* By default, no limit is applied and all messages are returned
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
|
@ -2772,59 +2872,77 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
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`
|
||||
* Offset data used for pagination
|
||||
*/
|
||||
chunkSize?: number
|
||||
}): AsyncIterableIterator<Message>
|
||||
offset?: SearchGlobalOffset
|
||||
|
||||
/**
|
||||
* Only return messages newer than this date
|
||||
*/
|
||||
minDate?: Date | number
|
||||
|
||||
/**
|
||||
* Only return messages older than this date
|
||||
*/
|
||||
maxDate?: Date | number
|
||||
}): Promise<ArrayPaginated<Message, SearchGlobalOffset>>
|
||||
/**
|
||||
* Search for messages inside a specific chat
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional search parameters
|
||||
*/
|
||||
searchMessages(
|
||||
chatId: InputPeerLike,
|
||||
params?: {
|
||||
searchMessages(params?: {
|
||||
/**
|
||||
* Text query string. Required for text-only messages,
|
||||
* optional for media.
|
||||
*
|
||||
* Defaults to `""` (empty string)
|
||||
* @default `""` (empty string)
|
||||
*/
|
||||
query?: string
|
||||
|
||||
/**
|
||||
* Offset ID for the search. Only messages earlier than this
|
||||
* ID will be returned.
|
||||
* Chat where to search for messages.
|
||||
*
|
||||
* Defaults to `0` (for the latest message).
|
||||
* When empty, will search across common message box (i.e. private messages and legacy chats)
|
||||
*/
|
||||
offsetId?: number
|
||||
chatId?: InputPeerLike
|
||||
|
||||
/**
|
||||
* Offset from the {@link offsetId}. Only used for the
|
||||
* first chunk
|
||||
* Offset ID for the search. Only messages earlier than this ID will be returned.
|
||||
*
|
||||
* Defaults to `0` (for the same message as {@link offsetId}).
|
||||
* @default `0` (starting from the latest message).
|
||||
*/
|
||||
offset?: number
|
||||
offset?: SearchMessagesOffset
|
||||
|
||||
/**
|
||||
* Additional offset from {@link offset}, in resulting messages.
|
||||
*
|
||||
* This can be used for advanced use cases, like:
|
||||
* - Loading 20 results newer than message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -20, limit = 20`
|
||||
* - Loading 20 results around message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -10, limit = 20`
|
||||
*
|
||||
* When {@link offset} is not set, this will be relative to the last message
|
||||
*
|
||||
* @default `0` (disabled)
|
||||
*/
|
||||
addOffset?: number
|
||||
|
||||
/**
|
||||
* Minimum message ID to return
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
minId?: number
|
||||
|
||||
/**
|
||||
* Maximum message ID to return.
|
||||
*
|
||||
* > *Seems* to work the same as {@link offsetId}
|
||||
* Unless {@link addOffset} is used, this will work the same as {@link offset}.
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
maxId?: number
|
||||
|
||||
|
@ -2850,7 +2968,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* By default, no limit is applied and all messages are returned
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
|
@ -2863,21 +2981,12 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
filter?: tl.TypeMessagesFilter
|
||||
|
||||
/**
|
||||
* Search for messages sent by a specific user.
|
||||
* Search only for messages sent by a specific user.
|
||||
*
|
||||
* Pass their marked ID, username, phone or `"me"` or `"self"`
|
||||
* You can pass their marked ID, username, phone or `"me"` or `"self"`
|
||||
*/
|
||||
fromUser?: InputPeerLike
|
||||
|
||||
/**
|
||||
* 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<Message>
|
||||
}): Promise<ArrayPaginated<Message, SearchMessagesOffset>>
|
||||
/**
|
||||
* Copy a message (i.e. send the same message,
|
||||
* but do not forward it).
|
||||
|
@ -3743,6 +3852,14 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*
|
||||
*/
|
||||
getMyUsername(): string | null
|
||||
/**
|
||||
* Get a single profile picture of a user by its ID
|
||||
*
|
||||
* @param userId User ID, username, phone number, `"me"` or `"self"`
|
||||
* @param photoId ID of the photo to fetch
|
||||
* @param params
|
||||
*/
|
||||
getProfilePhoto(userId: InputPeerLike, photoId: tl.Long): Promise<Photo>
|
||||
/**
|
||||
* Get a list of profile pictures of a user
|
||||
*
|
||||
|
@ -3755,18 +3872,18 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
/**
|
||||
* Offset from which to fetch.
|
||||
*
|
||||
* Defaults to `0`
|
||||
* @default `0`
|
||||
*/
|
||||
offset?: number
|
||||
|
||||
/**
|
||||
* Maximum number of items to fetch (up to 100)
|
||||
*
|
||||
* Defaults to `100`
|
||||
* @default `100`
|
||||
*/
|
||||
limit?: number
|
||||
},
|
||||
): Promise<Photo[]>
|
||||
): Promise<ArrayPaginated<Photo, number>>
|
||||
/**
|
||||
* Get information about a single user.
|
||||
*
|
||||
|
@ -3790,33 +3907,20 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
iterProfilePhotos(
|
||||
userId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Offset from which to fetch.
|
||||
*
|
||||
* Defaults to `0`
|
||||
*/
|
||||
offset?: number
|
||||
|
||||
params?: Parameters<TelegramClient['getProfilePhotos']>[1] & {
|
||||
/**
|
||||
* Maximum number of items to fetch
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all items are fetched
|
||||
* @default `Infinity`, i.e. all items are fetched
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Size of chunks which are fetched. Usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
|
||||
/**
|
||||
* If set, the method will return only photos
|
||||
* with IDs less than the set one
|
||||
*/
|
||||
maxId?: tl.Long
|
||||
},
|
||||
): AsyncIterableIterator<Photo>
|
||||
/**
|
||||
|
@ -4095,6 +4199,10 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
getMessages = getMessages
|
||||
getReactionUsers = getReactionUsers
|
||||
getScheduledMessages = getScheduledMessages
|
||||
iterHistory = iterHistory
|
||||
iterReactionUsers = iterReactionUsers
|
||||
iterSearchGlobal = iterSearchGlobal
|
||||
iterSearchMessages = iterSearchMessages
|
||||
_parseEntities = _parseEntities
|
||||
pinMessage = pinMessage
|
||||
readHistory = readHistory
|
||||
|
@ -4151,6 +4259,7 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
getCommonChats = getCommonChats
|
||||
getMe = getMe
|
||||
getMyUsername = getMyUsername
|
||||
getProfilePhoto = getProfilePhoto
|
||||
getProfilePhotos = getProfilePhotos
|
||||
getUsers = getUsers
|
||||
iterProfilePhotos = iterProfilePhotos
|
||||
|
|
|
@ -63,18 +63,18 @@ function _initializeAwesomeExtension(this: TelegramClient) {
|
|||
}
|
||||
```
|
||||
|
||||
## `@returns-exported`
|
||||
## `@exported`
|
||||
|
||||
Used as a first statement inside an exported function's body to indicate that this method returns an object of type
|
||||
which is exported from the same file.
|
||||
Used as a first statement inside an exported function's body to indicate that
|
||||
this exported type should be imported from the client
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
// @exported
|
||||
export type FooOrBar = Foo | Bar
|
||||
|
||||
export function getFooOrBar(this: TelegramClient): FooOrBar {
|
||||
// @returns-exported
|
||||
return new Foo()
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { ArrayPaginated, ChatInviteLink, InputPeerLike, PeersIndex } from '../../types'
|
||||
import { makeArrayPaginated, normalizeDate } from '../../utils'
|
||||
import { makeArrayPaginated } from '../../utils'
|
||||
import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
||||
// @exported
|
||||
export interface GetInviteLinksOffset {
|
||||
date: number
|
||||
link: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get invite links created by some administrator in the chat.
|
||||
*
|
||||
|
@ -39,22 +45,14 @@ export async function getInviteLinks(
|
|||
limit?: number
|
||||
|
||||
/**
|
||||
* Offset date used as an anchor for pagination.
|
||||
* Offset for pagination.
|
||||
*/
|
||||
offsetDate?: Date | number
|
||||
|
||||
/**
|
||||
* Offset link used as an anchor for pagination
|
||||
*/
|
||||
offsetLink?: string
|
||||
offset?: GetInviteLinksOffset
|
||||
},
|
||||
): Promise<ArrayPaginated<ChatInviteLink, { date: number; link: string }>> {
|
||||
): Promise<ArrayPaginated<ChatInviteLink, GetInviteLinksOffset>> {
|
||||
if (!params) params = {}
|
||||
|
||||
const { revoked = false, limit = Infinity, admin } = params
|
||||
|
||||
const offsetDate = normalizeDate(params.offsetDate)
|
||||
const offsetLink = params.offsetLink
|
||||
const { revoked = false, limit = Infinity, admin, offset } = params
|
||||
|
||||
const res = await this.call({
|
||||
_: 'messages.getExportedChatInvites',
|
||||
|
@ -62,8 +60,8 @@ export async function getInviteLinks(
|
|||
revoked,
|
||||
adminId: admin ? normalizeToInputUser(await this.resolvePeer(admin), admin) : { _: 'inputUserSelf' },
|
||||
limit,
|
||||
offsetDate,
|
||||
offsetLink,
|
||||
offsetDate: offset?.date,
|
||||
offsetLink: offset?.link,
|
||||
})
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
|
|
@ -35,7 +35,7 @@ export async function* iterInviteLinks(
|
|||
|
||||
const { revoked = false, limit = Infinity, chunkSize = 100, admin } = params
|
||||
|
||||
let { offsetDate, offsetLink } = params
|
||||
let { offset } = params
|
||||
|
||||
let current = 0
|
||||
|
||||
|
@ -47,21 +47,18 @@ export async function* iterInviteLinks(
|
|||
admin: adminResolved,
|
||||
revoked,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
offsetDate,
|
||||
offsetLink,
|
||||
offset,
|
||||
})
|
||||
|
||||
if (!links.length) return
|
||||
|
||||
const last = links[links.length - 1]
|
||||
|
||||
offsetDate = last.date
|
||||
offsetLink = last.link
|
||||
|
||||
for (const link of links) {
|
||||
yield link
|
||||
|
||||
if (++current >= limit) break
|
||||
}
|
||||
|
||||
if (!links.next) return
|
||||
offset = links.next
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,105 +1,111 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { tl } from '@mtcute/core'
|
||||
import { assertTypeIsNot } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Message, PeersIndex } from '../../types'
|
||||
import { normalizeDate } from '../../utils/misc-utils'
|
||||
import { ArrayPaginated, InputPeerLike, Message, PeersIndex } from '../../types'
|
||||
import { makeArrayPaginated } from '../../utils'
|
||||
|
||||
// @exported
|
||||
export interface GetHistoryOffset {
|
||||
id: number
|
||||
date: number
|
||||
}
|
||||
|
||||
const defaultOffset: GetHistoryOffset = {
|
||||
id: 0,
|
||||
date: 0,
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through a chat history sequentially.
|
||||
* Get chat history.
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional fetch parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* getHistory(
|
||||
export async function getHistory(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* By default, no limit is applied and all messages
|
||||
* are returned.
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Sequential number of the first message to be returned.
|
||||
* Defaults to 0 (most recent message).
|
||||
*
|
||||
* Negative values are also accepted and are useful
|
||||
* in case you set `offsetId` or `offsetDate`.
|
||||
* Offset for pagination
|
||||
*/
|
||||
offset?: number
|
||||
offset?: GetHistoryOffset
|
||||
|
||||
/**
|
||||
* Pass a message identifier as an offset to retrieve
|
||||
* only older messages starting from that message
|
||||
* Additional offset from {@link offset}, in resulting messages.
|
||||
*
|
||||
* This can be used for advanced use cases, like:
|
||||
* - Loading 20 messages newer than message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -20, limit = 20`
|
||||
* - Loading 20 messages around message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -10, limit = 20`
|
||||
*
|
||||
* @default `0` (disabled)
|
||||
*/
|
||||
offsetId?: number
|
||||
|
||||
addOffset?: number
|
||||
|
||||
/**
|
||||
* Minimum message ID to return
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
minId?: number
|
||||
|
||||
/**
|
||||
* Maximum message ID to return.
|
||||
*
|
||||
* > *Seems* to work the same as {@link offsetId}
|
||||
* Unless {@link addOffset} is used, this will work the same as {@link offset}.
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
maxId?: number
|
||||
|
||||
/**
|
||||
* Pass a date (`Date` or Unix time in ms) as an offset to retrieve
|
||||
* only older messages starting from that date.
|
||||
*/
|
||||
offsetDate?: number | Date
|
||||
|
||||
/**
|
||||
* Pass `true` to retrieve messages in reversed order (from older to recent)
|
||||
* Whether to retrieve messages in reversed order (from older to recent),
|
||||
* starting from {@link offset} (inclusive).
|
||||
*
|
||||
* > **Note**: Using `reverse=true` requires you to pass offset from which to start
|
||||
* > fetching the messages "downwards". If you call `getHistory` with `reverse=true`
|
||||
* > and without any offset, it will return an empty array.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
reverse?: boolean
|
||||
|
||||
/**
|
||||
* Chunk size. Usually you shouldn't care about this.
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message> {
|
||||
): Promise<ArrayPaginated<Message, GetHistoryOffset>> {
|
||||
if (!params) params = {}
|
||||
|
||||
let current = 0
|
||||
const total = params.limit || Infinity
|
||||
const limit = Math.min(params.chunkSize || 100, total)
|
||||
const {
|
||||
limit = 100,
|
||||
offset: { id: offsetId = 0, date: offsetDate = 0 } = defaultOffset,
|
||||
addOffset = 0,
|
||||
minId = 0,
|
||||
maxId = 0,
|
||||
reverse = false,
|
||||
} = params
|
||||
|
||||
const minId = params.minId || 0
|
||||
const maxId = params.maxId || 0
|
||||
const addOffsetAdjusted = addOffset + (reverse ? -limit : 0)
|
||||
|
||||
let offsetId = params.offsetId ?? (params.reverse && !params.offsetDate ? 1 : 0)
|
||||
const offsetDate = normalizeDate(params.offsetDate) || 0
|
||||
const baseOffset = -(params.reverse ? limit : 0)
|
||||
let addOffset = (params.offset ? params.offset * (params.reverse ? -1 : 1) : 0) + baseOffset
|
||||
|
||||
// resolve peer once and pass an InputPeer afterwards
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
for (;;) {
|
||||
const res = await this.call({
|
||||
_: 'messages.getHistory',
|
||||
peer,
|
||||
offsetId,
|
||||
offsetDate,
|
||||
addOffset,
|
||||
limit: Math.min(limit, total - current),
|
||||
addOffset: addOffsetAdjusted,
|
||||
limit,
|
||||
maxId,
|
||||
minId,
|
||||
hash: Long.ZERO,
|
||||
|
@ -108,19 +114,17 @@ export async function* getHistory(
|
|||
assertTypeIsNot('getHistory', res, 'messages.messagesNotModified')
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
const msgs = res.messages.filter((msg) => msg._ !== 'messageEmpty').map((msg) => new Message(this, msg, peers))
|
||||
|
||||
if (!msgs.length) break
|
||||
if (reverse) msgs.reverse()
|
||||
|
||||
if (params.reverse) msgs.reverse()
|
||||
const last = msgs[msgs.length - 1]
|
||||
const next = last ?
|
||||
{
|
||||
id: last.id + (reverse ? 1 : 0),
|
||||
date: last.raw.date,
|
||||
} :
|
||||
undefined
|
||||
|
||||
offsetId = msgs[msgs.length - 1].id + (params.reverse ? 1 : 0)
|
||||
addOffset = baseOffset
|
||||
|
||||
yield* msgs
|
||||
current += msgs.length
|
||||
|
||||
if (current >= total) break
|
||||
}
|
||||
return makeArrayPaginated(msgs, (res as tl.messages.RawMessagesSlice).count ?? msgs.length, next)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, InputReaction, normalizeInputReaction, PeerReaction, PeersIndex } from '../../types'
|
||||
import {
|
||||
ArrayPaginated,
|
||||
InputPeerLike,
|
||||
InputReaction,
|
||||
normalizeInputReaction,
|
||||
PeerReaction,
|
||||
PeersIndex,
|
||||
} from '../../types'
|
||||
import { makeArrayPaginated } from '../../utils'
|
||||
|
||||
// @exported
|
||||
export type GetReactionUsersOffset = string
|
||||
|
||||
/**
|
||||
* Get users who have reacted to the message.
|
||||
|
@ -11,7 +20,7 @@ import { InputPeerLike, InputReaction, normalizeInputReaction, PeerReaction, Pee
|
|||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function* getReactionUsers(
|
||||
export async function getReactionUsers(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
messageId: number,
|
||||
|
@ -22,54 +31,40 @@ export async function* getReactionUsers(
|
|||
emoji?: InputReaction
|
||||
|
||||
/**
|
||||
* Limit the number of events returned.
|
||||
* Limit the number of users returned.
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all events are returned
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
* Offset for pagination
|
||||
*/
|
||||
chunkSize?: number
|
||||
offset?: GetReactionUsersOffset
|
||||
},
|
||||
): AsyncIterableIterator<PeerReaction> {
|
||||
): Promise<ArrayPaginated<PeerReaction, GetReactionUsersOffset>> {
|
||||
if (!params) params = {}
|
||||
|
||||
const { limit = 100, offset, emoji } = params
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
let current = 0
|
||||
let offset: string | undefined = undefined
|
||||
const total = params.limit || Infinity
|
||||
const chunkSize = Math.min(params.chunkSize ?? 100, total)
|
||||
const reaction = normalizeInputReaction(emoji)
|
||||
|
||||
const reaction = normalizeInputReaction(params.emoji)
|
||||
|
||||
for (;;) {
|
||||
const res: tl.RpcCallReturn['messages.getMessageReactionsList'] = await this.call({
|
||||
const res = await this.call({
|
||||
_: 'messages.getMessageReactionsList',
|
||||
peer,
|
||||
id: messageId,
|
||||
reaction,
|
||||
limit: Math.min(chunkSize, total - current),
|
||||
limit,
|
||||
offset,
|
||||
})
|
||||
|
||||
if (!res.reactions.length) break
|
||||
|
||||
offset = res.nextOffset
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
for (const reaction of res.reactions) {
|
||||
const parsed = new PeerReaction(this, reaction, peers)
|
||||
|
||||
current += 1
|
||||
yield parsed
|
||||
|
||||
if (current >= total) break
|
||||
}
|
||||
}
|
||||
return makeArrayPaginated(
|
||||
res.reactions.map((it) => new PeerReaction(this, it, peers)),
|
||||
res.count,
|
||||
res.nextOffset,
|
||||
)
|
||||
}
|
||||
|
|
60
packages/client/src/methods/messages/iter-history.ts
Normal file
60
packages/client/src/methods/messages/iter-history.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Message } from '../../types'
|
||||
|
||||
/**
|
||||
* Iterate over chat history. Wrapper over {@link getHistory}
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional fetch parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterHistory(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getHistory']>[1] & {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* @default Infinity, i.e. all messages
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size. Usually you shouldn't care about this.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message> {
|
||||
if (!params) params = {}
|
||||
|
||||
const { limit = Infinity, chunkSize = 100, minId = 0, maxId = 0, reverse = false } = params
|
||||
|
||||
let { offset, addOffset = 0 } = params
|
||||
let current = 0
|
||||
|
||||
// resolve peer once and pass an InputPeer afterwards
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
for (;;) {
|
||||
const res = await this.getHistory(peer, {
|
||||
offset,
|
||||
addOffset,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
maxId,
|
||||
minId,
|
||||
reverse,
|
||||
})
|
||||
|
||||
for (const msg of res) {
|
||||
yield msg
|
||||
|
||||
if (++current >= limit) return
|
||||
}
|
||||
|
||||
if (!res.next) return
|
||||
offset = res.next
|
||||
addOffset = 0
|
||||
}
|
||||
}
|
60
packages/client/src/methods/messages/iter-reaction-users.ts
Normal file
60
packages/client/src/methods/messages/iter-reaction-users.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, normalizeInputReaction, PeerReaction } from '../../types'
|
||||
|
||||
/**
|
||||
* Iterate over users who have reacted to the message.
|
||||
*
|
||||
* Wrapper over {@link getReactionUsers}.
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param messageId Message ID
|
||||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterReactionUsers(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
messageId: number,
|
||||
params?: Parameters<TelegramClient['getReactionUsers']>[2] & {
|
||||
/**
|
||||
* Limit the number of events returned.
|
||||
*
|
||||
* @default `Infinity`, i.e. all events are returned
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, usually not needed.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<PeerReaction> {
|
||||
if (!params) params = {}
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
const { limit = Infinity, chunkSize = 100 } = params
|
||||
|
||||
let current = 0
|
||||
let { offset } = params
|
||||
|
||||
const reaction = normalizeInputReaction(params.emoji)
|
||||
|
||||
for (;;) {
|
||||
const res = await this.getReactionUsers(peer, messageId, {
|
||||
emoji: reaction,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
offset,
|
||||
})
|
||||
|
||||
offset = res.next
|
||||
|
||||
for (const reaction of res) {
|
||||
yield reaction
|
||||
|
||||
if (++current >= limit) break
|
||||
}
|
||||
}
|
||||
}
|
65
packages/client/src/methods/messages/iter-search-global.ts
Normal file
65
packages/client/src/methods/messages/iter-search-global.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { Message, SearchFilters } from '../../types'
|
||||
import { normalizeDate } from '../../utils'
|
||||
|
||||
/**
|
||||
* Search for messages globally from all of your chats.
|
||||
*
|
||||
* Iterable version of {@link searchGlobal}
|
||||
*
|
||||
* **Note**: Due to Telegram limitations, you can only get up to ~10000 messages
|
||||
*
|
||||
* @param params Search parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterSearchGlobal(
|
||||
this: TelegramClient,
|
||||
params?: Parameters<TelegramClient['searchGlobal']>[0] & {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* @default `Infinity`, i.e. all messages are returned
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, which will be passed as `limit` parameter
|
||||
* for `messages.search`. Usually you shouldn't care about this.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message> {
|
||||
if (!params) params = {}
|
||||
|
||||
const { query = '', filter = SearchFilters.Empty, limit = Infinity, chunkSize = 100 } = params
|
||||
|
||||
const minDate = normalizeDate(params.minDate) ?? 0
|
||||
const maxDate = normalizeDate(params.maxDate) ?? 0
|
||||
|
||||
let { offset } = params
|
||||
let current = 0
|
||||
|
||||
for (;;) {
|
||||
const res = await this.searchGlobal({
|
||||
query,
|
||||
filter,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
minDate,
|
||||
maxDate,
|
||||
offset,
|
||||
})
|
||||
|
||||
if (!res.length) return
|
||||
|
||||
for (const msg of res) {
|
||||
yield msg
|
||||
|
||||
if (++current >= limit) return
|
||||
}
|
||||
|
||||
if (!res.next) return
|
||||
offset = res.next
|
||||
}
|
||||
}
|
82
packages/client/src/methods/messages/iter-search-messages.ts
Normal file
82
packages/client/src/methods/messages/iter-search-messages.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { Message, SearchFilters } from '../../types'
|
||||
import { normalizeDate } from '../../utils/misc-utils'
|
||||
|
||||
/**
|
||||
* Search for messages inside a specific chat
|
||||
*
|
||||
* Iterable version of {@link searchMessages}
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
|
||||
* @param params Additional search parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterSearchMessages(
|
||||
this: TelegramClient,
|
||||
params?: Parameters<TelegramClient['searchMessages']>[0] & {
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* @default `Infinity`, i.e. all messages are returned
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, which will be passed as `limit` parameter
|
||||
* for `messages.search`. Usually you shouldn't care about this.
|
||||
*
|
||||
* @default `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<Message> {
|
||||
if (!params) params = {}
|
||||
|
||||
const {
|
||||
query = '',
|
||||
chatId = { _: 'inputPeerEmpty' },
|
||||
minId = 0,
|
||||
maxId = 0,
|
||||
threadId,
|
||||
limit = Infinity,
|
||||
chunkSize = 100,
|
||||
filter = SearchFilters.Empty,
|
||||
} = params
|
||||
|
||||
const minDate = normalizeDate(params.minDate) ?? 0
|
||||
const maxDate = normalizeDate(params.maxDate) ?? 0
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
const fromUser = params.fromUser ? await this.resolvePeer(params.fromUser) : undefined
|
||||
|
||||
let { offset, addOffset } = params
|
||||
let current = 0
|
||||
|
||||
for (;;) {
|
||||
const res = await this.searchMessages({
|
||||
query,
|
||||
chatId: peer,
|
||||
offset,
|
||||
addOffset,
|
||||
minId,
|
||||
maxId,
|
||||
threadId,
|
||||
filter,
|
||||
fromUser,
|
||||
minDate,
|
||||
maxDate,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
})
|
||||
|
||||
if (!res.length) break
|
||||
|
||||
for (const msg of res) {
|
||||
yield msg
|
||||
|
||||
if (++current >= limit) return
|
||||
}
|
||||
|
||||
if (!res.next) break
|
||||
offset = res.next
|
||||
addOffset = undefined
|
||||
}
|
||||
}
|
|
@ -2,7 +2,21 @@ import { tl } from '@mtcute/core'
|
|||
import { assertTypeIsNot } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { Message, PeersIndex, SearchFilters } from '../../types'
|
||||
import { ArrayPaginated, Message, PeersIndex, SearchFilters } from '../../types'
|
||||
import { makeArrayPaginated, normalizeDate } from '../../utils'
|
||||
|
||||
// @exported
|
||||
export interface SearchGlobalOffset {
|
||||
rate: number
|
||||
peer: tl.TypeInputPeer
|
||||
id: number
|
||||
}
|
||||
|
||||
const defaultOffset: SearchGlobalOffset = {
|
||||
rate: 0,
|
||||
peer: { _: 'inputPeerEmpty' },
|
||||
id: 0,
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for messages globally from all of your chats
|
||||
|
@ -12,20 +26,20 @@ import { Message, PeersIndex, SearchFilters } from '../../types'
|
|||
* @param params Search parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* searchGlobal(
|
||||
export async function searchGlobal(
|
||||
this: TelegramClient,
|
||||
params?: {
|
||||
/**
|
||||
* Text query string. Use `"@"` to search for mentions.
|
||||
*
|
||||
* Defaults to `""` (empty string)
|
||||
* @default `""` (empty string)
|
||||
*/
|
||||
query?: string
|
||||
|
||||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* By default, no limit is applied and all messages are returned
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
|
@ -38,54 +52,59 @@ export async function* searchGlobal(
|
|||
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`
|
||||
* Offset data used for pagination
|
||||
*/
|
||||
chunkSize?: number
|
||||
offset?: SearchGlobalOffset
|
||||
|
||||
/**
|
||||
* Only return messages newer than this date
|
||||
*/
|
||||
minDate?: Date | number
|
||||
|
||||
/**
|
||||
* Only return messages older than this date
|
||||
*/
|
||||
maxDate?: Date | number
|
||||
},
|
||||
): AsyncIterableIterator<Message> {
|
||||
): Promise<ArrayPaginated<Message, SearchGlobalOffset>> {
|
||||
if (!params) params = {}
|
||||
|
||||
let current = 0
|
||||
const {
|
||||
query = '',
|
||||
filter = SearchFilters.Empty,
|
||||
limit = 100,
|
||||
offset: { rate: offsetRate, peer: offsetPeer, id: offsetId } = defaultOffset,
|
||||
} = params
|
||||
|
||||
const total = params.limit || Infinity
|
||||
const limit = Math.min(params.chunkSize || 100, total)
|
||||
const minDate = normalizeDate(params.minDate) ?? 0
|
||||
const maxDate = normalizeDate(params.maxDate) ?? 0
|
||||
|
||||
let offsetRate = 0
|
||||
let offsetPeer = { _: 'inputPeerEmpty' } as tl.TypeInputPeer
|
||||
let offsetId = 0
|
||||
|
||||
for (;;) {
|
||||
const res: tl.RpcCallReturn['messages.searchGlobal'] = await this.call({
|
||||
const res = await this.call({
|
||||
_: 'messages.searchGlobal',
|
||||
q: params.query || '',
|
||||
filter: params.filter || SearchFilters.Empty,
|
||||
minDate: 0,
|
||||
maxDate: 0,
|
||||
q: query,
|
||||
filter,
|
||||
minDate,
|
||||
maxDate,
|
||||
offsetId,
|
||||
offsetRate,
|
||||
offsetPeer: offsetPeer,
|
||||
limit: Math.min(limit, total - current),
|
||||
offsetPeer,
|
||||
limit,
|
||||
})
|
||||
|
||||
assertTypeIsNot('searchGlobal', res, 'messages.messagesNotModified')
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
const msgs = res.messages.filter((msg) => msg._ !== 'messageEmpty').map((msg) => new Message(this, msg, peers))
|
||||
|
||||
if (!msgs.length) break
|
||||
|
||||
const last = msgs[msgs.length - 1]
|
||||
offsetRate = (res as tl.messages.RawMessagesSlice).nextRate ?? last.raw.date
|
||||
offsetPeer = last.chat.inputPeer
|
||||
offsetId = last.id
|
||||
|
||||
yield* msgs
|
||||
const next = last ?
|
||||
{
|
||||
rate: (res as tl.messages.RawMessagesSlice).nextRate ?? last.raw.date,
|
||||
peer: last.chat.inputPeer,
|
||||
id: last.id,
|
||||
} :
|
||||
undefined
|
||||
|
||||
current += msgs.length
|
||||
if (current >= total) break
|
||||
}
|
||||
return makeArrayPaginated(msgs, (res as tl.messages.RawMessagesSlice).count ?? msgs.length, next)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@ import { tl } from '@mtcute/core'
|
|||
import { assertTypeIsNot } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Message, PeersIndex, SearchFilters } from '../../types'
|
||||
import { normalizeDate } from '../../utils/misc-utils'
|
||||
import { ArrayPaginated, InputPeerLike, Message, PeersIndex, SearchFilters } from '../../types'
|
||||
import { makeArrayPaginated, normalizeDate } from '../../utils/misc-utils'
|
||||
|
||||
// @exported
|
||||
export type SearchMessagesOffset = number
|
||||
|
||||
/**
|
||||
* Search for messages inside a specific chat
|
||||
|
@ -14,47 +17,59 @@ import { normalizeDate } from '../../utils/misc-utils'
|
|||
* @param params Additional search parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* searchMessages(
|
||||
export async function searchMessages(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Text query string. Required for text-only messages,
|
||||
* optional for media.
|
||||
*
|
||||
* Defaults to `""` (empty string)
|
||||
* @default `""` (empty string)
|
||||
*/
|
||||
query?: string
|
||||
|
||||
/**
|
||||
* Offset ID for the search. Only messages earlier than this
|
||||
* ID will be returned.
|
||||
* Chat where to search for messages.
|
||||
*
|
||||
* Defaults to `0` (for the latest message).
|
||||
* When empty, will search across common message box (i.e. private messages and legacy chats)
|
||||
*/
|
||||
offsetId?: number
|
||||
chatId?: InputPeerLike
|
||||
|
||||
/**
|
||||
* Offset from the {@link offsetId}. Only used for the
|
||||
* first chunk
|
||||
* Offset ID for the search. Only messages earlier than this ID will be returned.
|
||||
*
|
||||
* Defaults to `0` (for the same message as {@link offsetId}).
|
||||
* @default `0` (starting from the latest message).
|
||||
*/
|
||||
offset?: number
|
||||
offset?: SearchMessagesOffset
|
||||
|
||||
/**
|
||||
* Additional offset from {@link offset}, in resulting messages.
|
||||
*
|
||||
* This can be used for advanced use cases, like:
|
||||
* - Loading 20 results newer than message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -20, limit = 20`
|
||||
* - Loading 20 results around message with ID `MSGID`:
|
||||
* `offset = MSGID, addOffset = -10, limit = 20`
|
||||
*
|
||||
* When {@link offset} is not set, this will be relative to the last message
|
||||
*
|
||||
* @default `0` (disabled)
|
||||
*/
|
||||
addOffset?: number
|
||||
|
||||
/**
|
||||
* Minimum message ID to return
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
minId?: number
|
||||
|
||||
/**
|
||||
* Maximum message ID to return.
|
||||
*
|
||||
* > *Seems* to work the same as {@link offsetId}
|
||||
* Unless {@link addOffset} is used, this will work the same as {@link offset}.
|
||||
*
|
||||
* Defaults to `0` (disabled).
|
||||
* @default `0` (disabled).
|
||||
*/
|
||||
maxId?: number
|
||||
|
||||
|
@ -80,7 +95,7 @@ export async function* searchMessages(
|
|||
/**
|
||||
* Limits the number of messages to be retrieved.
|
||||
*
|
||||
* By default, no limit is applied and all messages are returned
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
|
@ -93,70 +108,57 @@ export async function* searchMessages(
|
|||
filter?: tl.TypeMessagesFilter
|
||||
|
||||
/**
|
||||
* Search for messages sent by a specific user.
|
||||
* Search only for messages sent by a specific user.
|
||||
*
|
||||
* Pass their marked ID, username, phone or `"me"` or `"self"`
|
||||
* You can pass their marked ID, username, phone or `"me"` or `"self"`
|
||||
*/
|
||||
fromUser?: InputPeerLike
|
||||
|
||||
/**
|
||||
* 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<Message> {
|
||||
): Promise<ArrayPaginated<Message, SearchMessagesOffset>> {
|
||||
if (!params) params = {}
|
||||
|
||||
let current = 0
|
||||
let offsetId = params.offsetId || 0
|
||||
let offset = params.offset || 0
|
||||
const {
|
||||
query = '',
|
||||
chatId = { _: 'inputPeerEmpty' },
|
||||
offset = 0,
|
||||
addOffset = 0,
|
||||
minId = 0,
|
||||
maxId = 0,
|
||||
threadId,
|
||||
limit = 100,
|
||||
filter = SearchFilters.Empty,
|
||||
} = params
|
||||
|
||||
const minDate = normalizeDate(params.minDate) ?? 0
|
||||
const maxDate = normalizeDate(params.maxDate) ?? 0
|
||||
const minId = params.minId ?? 0
|
||||
const maxId = params.maxId ?? 0
|
||||
|
||||
const total = params.limit || Infinity
|
||||
const limit = Math.min(params.chunkSize || 100, total)
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
const fromUser = (params.fromUser ? await this.resolvePeer(params.fromUser) : null) || undefined
|
||||
const fromUser = params.fromUser ? await this.resolvePeer(params.fromUser) : undefined
|
||||
|
||||
for (;;) {
|
||||
const res: tl.RpcCallReturn['messages.search'] = await this.call({
|
||||
const res = await this.call({
|
||||
_: 'messages.search',
|
||||
peer,
|
||||
q: params.query || '',
|
||||
filter: params.filter || SearchFilters.Empty,
|
||||
q: query,
|
||||
filter,
|
||||
minDate,
|
||||
maxDate,
|
||||
offsetId,
|
||||
addOffset: offset,
|
||||
limit: Math.min(limit, total - current),
|
||||
offsetId: offset,
|
||||
addOffset,
|
||||
limit,
|
||||
minId,
|
||||
maxId,
|
||||
fromId: fromUser,
|
||||
topMsgId: threadId,
|
||||
hash: Long.ZERO,
|
||||
})
|
||||
|
||||
assertTypeIsNot('searchMessages', res, 'messages.messagesNotModified')
|
||||
|
||||
// for successive chunks, we need to reset the offset
|
||||
offset = 0
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
const msgs = res.messages.filter((msg) => msg._ !== 'messageEmpty').map((msg) => new Message(this, msg, peers))
|
||||
|
||||
if (!msgs.length) break
|
||||
const last = msgs[msgs.length - 1]
|
||||
const next = last ? last.id : undefined
|
||||
|
||||
offsetId = res.messages[res.messages.length - 1].id
|
||||
yield* msgs
|
||||
|
||||
current += msgs.length
|
||||
if (current >= total) break
|
||||
}
|
||||
return makeArrayPaginated(msgs, (res as tl.messages.RawMessagesSlice).count ?? msgs.length, next)
|
||||
}
|
||||
|
|
29
packages/client/src/methods/users/get-profile-photo.ts
Normal file
29
packages/client/src/methods/users/get-profile-photo.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
import { assertTypeIs } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Photo } from '../../types'
|
||||
import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
* Get a single profile picture of a user by its ID
|
||||
*
|
||||
* @param userId User ID, username, phone number, `"me"` or `"self"`
|
||||
* @param photoId ID of the photo to fetch
|
||||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function getProfilePhoto(this: TelegramClient, userId: InputPeerLike, photoId: tl.Long): Promise<Photo> {
|
||||
const res = await this.call({
|
||||
_: 'photos.getUserPhotos',
|
||||
userId: normalizeToInputUser(await this.resolvePeer(userId), userId),
|
||||
offset: -1,
|
||||
limit: 1,
|
||||
maxId: photoId,
|
||||
})
|
||||
|
||||
const photo = res.photos[0]
|
||||
assertTypeIs('getProfilePhotos', photo, 'photo')
|
||||
|
||||
return new Photo(this, photo)
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { tl } from '@mtcute/core'
|
||||
import { assertTypeIs } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Photo } from '../../types'
|
||||
import { ArrayPaginated, InputPeerLike, Photo } from '../../types'
|
||||
import { makeArrayPaginated } from '../../utils'
|
||||
import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
|
@ -20,27 +22,37 @@ export async function getProfilePhotos(
|
|||
/**
|
||||
* Offset from which to fetch.
|
||||
*
|
||||
* Defaults to `0`
|
||||
* @default `0`
|
||||
*/
|
||||
offset?: number
|
||||
|
||||
/**
|
||||
* Maximum number of items to fetch (up to 100)
|
||||
*
|
||||
* Defaults to `100`
|
||||
* @default `100`
|
||||
*/
|
||||
limit?: number
|
||||
},
|
||||
): Promise<Photo[]> {
|
||||
): Promise<ArrayPaginated<Photo, number>> {
|
||||
if (!params) params = {}
|
||||
|
||||
const { offset = 0, limit = 100 } = params
|
||||
|
||||
const res = await this.call({
|
||||
_: 'photos.getUserPhotos',
|
||||
userId: normalizeToInputUser(await this.resolvePeer(userId), userId),
|
||||
offset: params.offset ?? 0,
|
||||
limit: params.limit ?? 100,
|
||||
offset,
|
||||
limit,
|
||||
maxId: Long.ZERO,
|
||||
})
|
||||
|
||||
return res.photos.map((it) => new Photo(this, it as tl.RawPhoto))
|
||||
return makeArrayPaginated(
|
||||
res.photos.map((it) => {
|
||||
assertTypeIs('getProfilePhotos', it, 'photo')
|
||||
|
||||
return new Photo(this, it)
|
||||
}),
|
||||
(res as tl.photos.RawPhotosSlice).count ?? res.photos.length,
|
||||
offset + res.photos.length,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Photo } from '../../types'
|
||||
import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
@ -16,66 +12,44 @@ import { normalizeToInputUser } from '../../utils/peer-utils'
|
|||
export async function* iterProfilePhotos(
|
||||
this: TelegramClient,
|
||||
userId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Offset from which to fetch.
|
||||
*
|
||||
* Defaults to `0`
|
||||
*/
|
||||
offset?: number
|
||||
|
||||
params?: Parameters<TelegramClient['getProfilePhotos']>[1] & {
|
||||
/**
|
||||
* Maximum number of items to fetch
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all items are fetched
|
||||
* @default `Infinity`, i.e. all items are fetched
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Size of chunks which are fetched. Usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
|
||||
/**
|
||||
* If set, the method will return only photos
|
||||
* with IDs less than the set one
|
||||
*/
|
||||
maxId?: tl.Long
|
||||
},
|
||||
): AsyncIterableIterator<Photo> {
|
||||
if (!params) params = {}
|
||||
|
||||
const peer = normalizeToInputUser(await this.resolvePeer(userId), userId)
|
||||
|
||||
let offset = params.offset || 0
|
||||
const { limit = Infinity, chunkSize = 100 } = params
|
||||
|
||||
let { offset } = params
|
||||
let current = 0
|
||||
const total = params.limit || Infinity
|
||||
|
||||
const limit = Math.min(params.chunkSize || 100, total)
|
||||
|
||||
const maxId = params.maxId || Long.ZERO
|
||||
|
||||
for (;;) {
|
||||
const res = await this.call({
|
||||
_: 'photos.getUserPhotos',
|
||||
userId: peer,
|
||||
limit: Math.min(limit, total - current),
|
||||
const res = await this.getProfilePhotos(peer, {
|
||||
offset,
|
||||
maxId,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
})
|
||||
|
||||
if (!res.photos.length) break
|
||||
for (const it of res) {
|
||||
yield it
|
||||
|
||||
offset += res.photos.length
|
||||
|
||||
for (const it of res.photos) {
|
||||
yield new Photo(this, it as tl.RawPhoto)
|
||||
if (++current >= limit) return
|
||||
}
|
||||
|
||||
current += res.photos.length
|
||||
|
||||
if (current >= total) break
|
||||
if (!res.next) return
|
||||
offset = res.next
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,13 @@ export class Photo extends FileLocation {
|
|||
this.type = 'photo'
|
||||
}
|
||||
|
||||
/**
|
||||
* Photo ID
|
||||
*/
|
||||
get id(): tl.Long {
|
||||
return this.raw.id
|
||||
}
|
||||
|
||||
/** Date this photo was sent */
|
||||
get date(): Date {
|
||||
return new Date(this.raw.date * 1000)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { getMarkedPeerId, tl } from '@mtcute/core'
|
||||
import { assertTypeIs } from '@mtcute/core/utils'
|
||||
|
||||
|
@ -10,7 +12,7 @@ import { PeersIndex, User } from '../peers'
|
|||
*
|
||||
* Either a `string` with a unicode emoji, or a `tl.Long` for a custom emoji
|
||||
*/
|
||||
export type InputReaction = string | tl.Long
|
||||
export type InputReaction = string | tl.Long | tl.TypeReaction
|
||||
|
||||
export function normalizeInputReaction(reaction?: InputReaction | null): tl.TypeReaction {
|
||||
if (typeof reaction === 'string') {
|
||||
|
@ -18,11 +20,13 @@ export function normalizeInputReaction(reaction?: InputReaction | null): tl.Type
|
|||
_: 'reactionEmoji',
|
||||
emoticon: reaction,
|
||||
}
|
||||
} else if (reaction) {
|
||||
} else if (Long.isLong(reaction)) {
|
||||
return {
|
||||
_: 'reactionCustomEmoji',
|
||||
documentId: reaction,
|
||||
}
|
||||
} else if (reaction) {
|
||||
return reaction
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -139,13 +143,6 @@ export class MessageReactions {
|
|||
(reaction) => new PeerReaction(this.client, reaction, this._peers),
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users who reacted to this message
|
||||
*/
|
||||
getUsers(params?: Parameters<TelegramClient['getReactionUsers']>[2]): AsyncIterableIterator<PeerReaction> {
|
||||
return this.client.getReactionUsers(this.messageId, this.chatId, params)
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(MessageReactions)
|
||||
|
|
Loading…
Reference in a new issue