feat: more non-iterable versions of methods

This commit is contained in:
alina 🌸 2023-10-02 18:00:00 +03:00
parent 55c4f296fb
commit ea7eabf0be
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
18 changed files with 902 additions and 484 deletions

View file

@ -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')
}
}
}

View file

@ -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

View file

@ -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()
}
```

View file

@ -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)

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -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,
)
}

View 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
}
}

View 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
}
}
}

View 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
}
}

View 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
}
}

View file

@ -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)
}

View file

@ -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)
}

View 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)
}

View file

@ -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,
)
}

View file

@ -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
}
}

View file

@ -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)

View file

@ -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)