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