feat: refactor some iterable methods to have non-iterable counterpart
This commit is contained in:
parent
59ac74f300
commit
82994408a2
22 changed files with 925 additions and 508 deletions
|
@ -60,6 +60,7 @@ import { getChatMembers } from './methods/chats/get-chat-members'
|
|||
import { getChatPreview } from './methods/chats/get-chat-preview'
|
||||
import { getFullChat } from './methods/chats/get-full-chat'
|
||||
import { getNearbyChats } from './methods/chats/get-nearby-chats'
|
||||
import { iterChatEventLog } from './methods/chats/iter-chat-event-log'
|
||||
import { iterChatMembers } from './methods/chats/iter-chat-members'
|
||||
import { joinChat } from './methods/chats/join-chat'
|
||||
import { kickChatMember } from './methods/chats/kick-chat-member'
|
||||
|
@ -84,10 +85,9 @@ import { createFolder } from './methods/dialogs/create-folder'
|
|||
import { deleteFolder } from './methods/dialogs/delete-folder'
|
||||
import { editFolder } from './methods/dialogs/edit-folder'
|
||||
import { findFolder } from './methods/dialogs/find-folder'
|
||||
import { getDialogs } from './methods/dialogs/get-dialogs'
|
||||
import { getFolders } from './methods/dialogs/get-folders'
|
||||
import { _normalizeInputFolder, getFolders } from './methods/dialogs/get-folders'
|
||||
import { getPeerDialogs } from './methods/dialogs/get-peer-dialogs'
|
||||
import { _parseDialogs } from './methods/dialogs/parse-dialogs'
|
||||
import { iterDialogs } from './methods/dialogs/iter-dialogs'
|
||||
import { setFoldersOrder } from './methods/dialogs/set-folders'
|
||||
import { downloadAsBuffer } from './methods/files/download-buffer'
|
||||
import { downloadToFile } from './methods/files/download-file'
|
||||
|
@ -107,6 +107,8 @@ import { getInviteLinks } 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'
|
||||
import { iterInviteLinkMembers } from './methods/invite-links/iter-invite-link-members'
|
||||
import { iterInviteLinks } from './methods/invite-links/iter-invite-links'
|
||||
import { revokeInviteLink } from './methods/invite-links/revoke-invite-link'
|
||||
import { closePoll } from './methods/messages/close-poll'
|
||||
import { deleteMessages } from './methods/messages/delete-messages'
|
||||
|
@ -198,10 +200,9 @@ import {
|
|||
BotStoppedUpdate,
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
ChatAction,
|
||||
ChatEvent,
|
||||
ChatInviteLink,
|
||||
ChatInviteLinkJoinedMember,
|
||||
ChatInviteLinkMember,
|
||||
ChatJoinRequestUpdate,
|
||||
ChatMember,
|
||||
ChatMemberUpdate,
|
||||
|
@ -216,6 +217,8 @@ import {
|
|||
HistoryReadUpdate,
|
||||
IMessageEntityParser,
|
||||
InlineQuery,
|
||||
InputChatEventFilters,
|
||||
InputDialogFolder,
|
||||
InputFileLike,
|
||||
InputInlineResult,
|
||||
InputMediaLike,
|
||||
|
@ -1123,8 +1126,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
rank?: string,
|
||||
): Promise<void>
|
||||
/**
|
||||
* Get chat event log ("Recent actions" in official
|
||||
* clients).
|
||||
* Get chat event log ("Recent actions" in official clients).
|
||||
*
|
||||
* Only available for supergroups and channels, and
|
||||
* requires (any) administrator rights.
|
||||
|
@ -1171,23 +1173,24 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* and when passing one or more action types,
|
||||
* they will be filtered locally.
|
||||
*/
|
||||
filters?: tl.TypeChannelAdminLogEventsFilter | MaybeArray<Exclude<ChatAction, null>['type']>
|
||||
filters?: InputChatEventFilters
|
||||
|
||||
/**
|
||||
* Limit the number of events returned.
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all events are returned
|
||||
* > Note: when using filters, there will likely be
|
||||
* > less events returned than specified here.
|
||||
* > This limit is only used to limit the number of
|
||||
* > events to fetch from the server.
|
||||
* >
|
||||
* > If you need to limit the number of events
|
||||
* > returned, use {@link iterChatEventLog} instead.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatEvent>
|
||||
): Promise<ChatEvent[]>
|
||||
/**
|
||||
* Get information about a single chat member
|
||||
*
|
||||
|
@ -1277,6 +1280,33 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* @param longitude Longitude of the location
|
||||
*/
|
||||
getNearbyChats(latitude: number, longitude: number): Promise<Chat[]>
|
||||
/**
|
||||
* Iterate over chat event log.
|
||||
*
|
||||
* Small wrapper over {@link getChatEventLog}
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param params
|
||||
*/
|
||||
iterChatEventLog(
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getChatEventLog']>[1] & {
|
||||
/**
|
||||
* Total number of events to return.
|
||||
*
|
||||
* @default Infinity
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, passed as `limit` to {@link getChatEventLog}.
|
||||
* Usually you don't need to touch this.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatEvent>
|
||||
/**
|
||||
* Iterate through chat members
|
||||
*
|
||||
|
@ -1570,17 +1600,35 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* @param params Search parameters. At least one must be set.
|
||||
*/
|
||||
findFolder(params: { title?: string; emoji?: string; id?: number }): Promise<tl.RawDialogFilter | null>
|
||||
/**
|
||||
* Get list of folders.
|
||||
*/
|
||||
getFolders(): Promise<tl.TypeDialogFilter[]>
|
||||
|
||||
_normalizeInputFolder(folder: InputDialogFolder): Promise<tl.TypeDialogFilter>
|
||||
/**
|
||||
* Get dialogs with certain peers.
|
||||
*
|
||||
* @param peers Peers for which to fetch dialogs.
|
||||
*/
|
||||
getPeerDialogs(peers: InputPeerLike): Promise<Dialog>
|
||||
/**
|
||||
* Get dialogs with certain peers.
|
||||
*
|
||||
* @param peers Peers for which to fetch dialogs.
|
||||
*/
|
||||
getPeerDialogs(peers: InputPeerLike[]): Promise<Dialog[]>
|
||||
/**
|
||||
* Iterate over dialogs.
|
||||
*
|
||||
* Note that due to Telegram limitations,
|
||||
* Note that due to Telegram API limitations,
|
||||
* ordering here can only be anti-chronological
|
||||
* (i.e. newest - first), and draft update date
|
||||
* is not considered when sorting.
|
||||
*
|
||||
* @param params Fetch parameters
|
||||
*/
|
||||
getDialogs(params?: {
|
||||
iterDialogs(params?: {
|
||||
/**
|
||||
* Offset message date used as an anchor for pagination.
|
||||
*/
|
||||
|
@ -1599,7 +1647,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
/**
|
||||
* Limits the number of dialogs to be received.
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all dialogs are fetched, ignored when `pinned=only`
|
||||
* @default `Infinity`, i.e. all dialogs are fetched
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
|
@ -1607,7 +1655,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* Chunk size which will be passed to `messages.getDialogs`.
|
||||
* You shouldn't usually care about this.
|
||||
*
|
||||
* Defaults to 100.
|
||||
* @default 100.
|
||||
*/
|
||||
chunkSize?: number
|
||||
|
||||
|
@ -1621,11 +1669,11 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* `keep`, which will return pinned dialogs
|
||||
* ordered by date among other non-pinned dialogs.
|
||||
*
|
||||
* Defaults to `include`.
|
||||
*
|
||||
* > **Note**: When using `include` mode with folders,
|
||||
* > pinned dialogs will only be fetched if all offset
|
||||
* > parameters are unset.
|
||||
*
|
||||
* @default `include`.
|
||||
*/
|
||||
pinned?: 'include' | 'exclude' | 'only' | 'keep'
|
||||
|
||||
|
@ -1636,11 +1684,13 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* `exclude` them from the list, or `only`
|
||||
* return archived dialogs
|
||||
*
|
||||
* Defaults to `exclude`, ignored for folders since folders
|
||||
* Ignored for folders, since folders
|
||||
* themselves contain information about archived chats.
|
||||
*
|
||||
* > **Note**: when fetching `only` pinned dialogs
|
||||
* > passing `keep` will act as passing `only`
|
||||
* > **Note**: when `pinned=only`, `archived=keep` will act as `only`
|
||||
* > because of Telegram API limitations.
|
||||
*
|
||||
* @default `exclude`
|
||||
*/
|
||||
archived?: 'keep' | 'exclude' | 'only'
|
||||
|
||||
|
@ -1663,9 +1713,9 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* When a folder with given ID or title is not found,
|
||||
* {@link MtArgumentError} is thrown
|
||||
*
|
||||
* By default fetches from "All" folder
|
||||
* @default <empty> (fetches from "All" folder)
|
||||
*/
|
||||
folder?: string | number | tl.RawDialogFilter
|
||||
folder?: InputDialogFolder
|
||||
|
||||
/**
|
||||
* Additional filtering for the dialogs.
|
||||
|
@ -1676,24 +1726,6 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
filter?: Partial<Omit<tl.RawDialogFilter, '_' | 'id' | 'title'>>
|
||||
}): AsyncIterableIterator<Dialog>
|
||||
/**
|
||||
* Get list of folders.
|
||||
*/
|
||||
getFolders(): Promise<tl.TypeDialogFilter[]>
|
||||
/**
|
||||
* Get dialogs with certain peers.
|
||||
*
|
||||
* @param peers Peers for which to fetch dialogs.
|
||||
*/
|
||||
getPeerDialogs(peers: InputPeerLike): Promise<Dialog>
|
||||
/**
|
||||
* Get dialogs with certain peers.
|
||||
*
|
||||
* @param peers Peers for which to fetch dialogs.
|
||||
*/
|
||||
getPeerDialogs(peers: InputPeerLike[]): Promise<Dialog[]>
|
||||
|
||||
_parseDialogs(res: tl.messages.TypeDialogs | tl.messages.TypePeerDialogs): Dialog[]
|
||||
/**
|
||||
* Reorder folders
|
||||
*
|
||||
|
@ -1943,10 +1975,22 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
link?: string
|
||||
|
||||
/**
|
||||
* Maximum number of users to return (by default returns all)
|
||||
* Maximum number of users to return
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Offset request/join date used as an anchor for pagination.
|
||||
*/
|
||||
offsetDate?: Date | number
|
||||
|
||||
/**
|
||||
* Offset user used as an anchor for pagination
|
||||
*/
|
||||
offsetUser?: tl.TypeInputUser
|
||||
|
||||
/**
|
||||
* Whether to get users who have requested to join
|
||||
* the chat but weren't accepted yet
|
||||
|
@ -1955,13 +1999,13 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
|
||||
/**
|
||||
* Search for a user in the pending join requests list
|
||||
* (only works if {@link requested} is true)
|
||||
* (if passed, {@link requested} is assumed to be true)
|
||||
*
|
||||
* Doesn't work when {@link link} is set (Telegram limitation)
|
||||
*/
|
||||
requestedSearch?: string
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLinkJoinedMember>
|
||||
): Promise<ArrayWithTotal<ChatInviteLinkMember>>
|
||||
/**
|
||||
* Get detailed information about an invite link
|
||||
*
|
||||
|
@ -1982,8 +2026,14 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
getInviteLinks(
|
||||
chatId: InputPeerLike,
|
||||
adminId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Only return this admin's links.
|
||||
*
|
||||
* @default `"self"`
|
||||
*/
|
||||
admin?: InputPeerLike
|
||||
|
||||
/**
|
||||
* Whether to fetch revoked invite links
|
||||
*/
|
||||
|
@ -1991,18 +2041,22 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
|
||||
/**
|
||||
* Limit the number of invite links to be fetched.
|
||||
* By default, all links are fetched.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Size of chunks which are fetched. Usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
* Offset date used as an anchor for pagination.
|
||||
*/
|
||||
chunkSize?: number
|
||||
offsetDate?: Date | number
|
||||
|
||||
/**
|
||||
* Offset link used as an anchor for pagination
|
||||
*/
|
||||
offsetLink?: string
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLink>
|
||||
): Promise<ArrayWithTotal<ChatInviteLink>>
|
||||
/**
|
||||
* Get primary invite link of a chat
|
||||
*
|
||||
|
@ -2025,6 +2079,60 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* @param action Whether to approve or deny the join request
|
||||
*/
|
||||
hideJoinRequest(peer: InputPeerLike, user: InputPeerLike, action: 'approve' | 'deny'): Promise<void>
|
||||
/**
|
||||
* Iterate over users who have joined
|
||||
* the chat with the given invite link.
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param params Additional params
|
||||
*/
|
||||
iterInviteLinkMembers(
|
||||
chatId: InputPeerLike,
|
||||
params: Parameters<TelegramClient['getInviteLinkMembers']>[1] & {
|
||||
/**
|
||||
* Maximum number of users to return
|
||||
*
|
||||
* @default `Infinity`, i.e. all users are fetched
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size which will be passed to `messages.getChatInviteImporters`.
|
||||
* You shouldn't usually care about this.
|
||||
*
|
||||
* @default 100.
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLinkMember>
|
||||
/**
|
||||
* Iterate over invite links created by some administrator in the chat.
|
||||
*
|
||||
* As an administrator you can only get your own links
|
||||
* (i.e. `adminId = "self"`), as a creator you can get
|
||||
* any other admin's links.
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param adminId Admin who created the links
|
||||
* @param params
|
||||
*/
|
||||
iterInviteLinks(
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getInviteLinks']>[1] & {
|
||||
/**
|
||||
* Limit the number of invite links to be fetched.
|
||||
* By default, all links are fetched.
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Size of chunks which are fetched. Usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLink>
|
||||
/**
|
||||
* Revoke an invite link.
|
||||
*
|
||||
|
@ -3921,6 +4029,7 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
getChat = getChat
|
||||
getFullChat = getFullChat
|
||||
getNearbyChats = getNearbyChats
|
||||
iterChatEventLog = iterChatEventLog
|
||||
iterChatMembers = iterChatMembers
|
||||
joinChat = joinChat
|
||||
kickChatMember = kickChatMember
|
||||
|
@ -3946,10 +4055,10 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
deleteFolder = deleteFolder
|
||||
editFolder = editFolder
|
||||
findFolder = findFolder
|
||||
getDialogs = getDialogs
|
||||
getFolders = getFolders
|
||||
_normalizeInputFolder = _normalizeInputFolder
|
||||
getPeerDialogs = getPeerDialogs
|
||||
_parseDialogs = _parseDialogs
|
||||
iterDialogs = iterDialogs
|
||||
setFoldersOrder = setFoldersOrder
|
||||
downloadAsBuffer = downloadAsBuffer
|
||||
downloadToFile = downloadToFile
|
||||
|
@ -3969,6 +4078,8 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
getPrimaryInviteLink = getPrimaryInviteLink
|
||||
hideAllJoinRequests = hideAllJoinRequests
|
||||
hideJoinRequest = hideJoinRequest
|
||||
iterInviteLinkMembers = iterInviteLinkMembers
|
||||
iterInviteLinks = iterInviteLinks
|
||||
revokeInviteLink = revokeInviteLink
|
||||
closePoll = closePoll
|
||||
deleteMessages = deleteMessages
|
||||
|
|
|
@ -17,10 +17,9 @@ import {
|
|||
BotStoppedUpdate,
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
ChatAction,
|
||||
ChatEvent,
|
||||
ChatInviteLink,
|
||||
ChatInviteLinkJoinedMember,
|
||||
ChatInviteLinkMember,
|
||||
ChatJoinRequestUpdate,
|
||||
ChatMember,
|
||||
ChatMemberUpdate,
|
||||
|
@ -35,6 +34,8 @@ import {
|
|||
HistoryReadUpdate,
|
||||
IMessageEntityParser,
|
||||
InlineQuery,
|
||||
InputChatEventFilters,
|
||||
InputDialogFolder,
|
||||
InputFileLike,
|
||||
InputInlineResult,
|
||||
InputMediaLike,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { assertNever, MaybeArray, tl } from '@mtcute/core'
|
||||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { ChatAction, ChatEvent, InputPeerLike, PeersIndex } from '../../types'
|
||||
import { ChatEvent, InputPeerLike, PeersIndex } from '../../types'
|
||||
import { InputChatEventFilters, normalizeChatEventFilters } from '../../types/peers/chat-event/filters'
|
||||
import { normalizeToInputChannel, normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
* Get chat event log ("Recent actions" in official
|
||||
* clients).
|
||||
* Get chat event log ("Recent actions" in official clients).
|
||||
*
|
||||
* Only available for supergroups and channels, and
|
||||
* requires (any) administrator rights.
|
||||
|
@ -22,7 +22,7 @@ import { normalizeToInputChannel, normalizeToInputUser } from '../../utils/peer-
|
|||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function* getChatEventLog(
|
||||
export async function getChatEventLog(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: {
|
||||
|
@ -57,165 +57,62 @@ export async function* getChatEventLog(
|
|||
* and when passing one or more action types,
|
||||
* they will be filtered locally.
|
||||
*/
|
||||
filters?: tl.TypeChannelAdminLogEventsFilter | MaybeArray<Exclude<ChatAction, null>['type']>
|
||||
filters?: InputChatEventFilters
|
||||
|
||||
/**
|
||||
* Limit the number of events returned.
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all events are returned
|
||||
* > Note: when using filters, there will likely be
|
||||
* > less events returned than specified here.
|
||||
* > This limit is only used to limit the number of
|
||||
* > events to fetch from the server.
|
||||
* >
|
||||
* > If you need to limit the number of events
|
||||
* > returned, use {@link iterChatEventLog} instead.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatEvent> {
|
||||
): Promise<ChatEvent[]> {
|
||||
if (!params) params = {}
|
||||
|
||||
const channel = normalizeToInputChannel(await this.resolvePeer(chatId), chatId)
|
||||
|
||||
let current = 0
|
||||
let maxId = params.maxId ?? Long.ZERO
|
||||
const minId = params.minId ?? Long.ZERO
|
||||
const query = params.query ?? ''
|
||||
const { maxId = Long.ZERO, minId = Long.ZERO, query = '', limit = 100, users, filters } = params
|
||||
|
||||
const total = params.limit || Infinity
|
||||
const chunkSize = Math.min(params.chunkSize ?? 100, total)
|
||||
|
||||
const admins: tl.TypeInputUser[] | undefined = params.users ?
|
||||
await this.resolvePeerMany(params.users, normalizeToInputUser) :
|
||||
const admins: tl.TypeInputUser[] | undefined = users ?
|
||||
await this.resolvePeerMany(users, normalizeToInputUser) :
|
||||
undefined
|
||||
|
||||
let serverFilter: tl.Mutable<tl.TypeChannelAdminLogEventsFilter> | undefined = undefined
|
||||
let localFilter: Record<string, true> | undefined = undefined
|
||||
const { serverFilter, localFilter } = normalizeChatEventFilters(filters)
|
||||
|
||||
if (params.filters) {
|
||||
if (typeof params.filters === 'string' || Array.isArray(params.filters)) {
|
||||
let input = params.filters
|
||||
if (!Array.isArray(input)) input = [input]
|
||||
const res = await this.call({
|
||||
_: 'channels.getAdminLog',
|
||||
channel,
|
||||
q: query,
|
||||
eventsFilter: serverFilter,
|
||||
admins,
|
||||
maxId,
|
||||
minId,
|
||||
limit,
|
||||
})
|
||||
|
||||
serverFilter = {
|
||||
_: 'channelAdminLogEventsFilter',
|
||||
}
|
||||
localFilter = {}
|
||||
if (!res.events.length) return []
|
||||
|
||||
input.forEach((type) => {
|
||||
localFilter![type] = true
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
switch (type) {
|
||||
case 'user_joined':
|
||||
serverFilter!.join = true
|
||||
break
|
||||
case 'user_left':
|
||||
serverFilter!.leave = true
|
||||
break
|
||||
case 'user_invited':
|
||||
serverFilter!.invite = true
|
||||
break
|
||||
case 'title_changed':
|
||||
case 'description_changed':
|
||||
case 'linked_chat_changed':
|
||||
case 'location_changed':
|
||||
case 'photo_changed':
|
||||
case 'username_changed':
|
||||
case 'stickerset_changed':
|
||||
serverFilter!.info = true
|
||||
break
|
||||
case 'invites_toggled':
|
||||
case 'history_toggled':
|
||||
case 'signatures_toggled':
|
||||
case 'def_perms_changed':
|
||||
serverFilter!.settings = true
|
||||
break
|
||||
case 'msg_pinned':
|
||||
serverFilter!.pinned = true
|
||||
break
|
||||
case 'msg_edited':
|
||||
case 'poll_stopped':
|
||||
serverFilter!.edit = true
|
||||
break
|
||||
case 'msg_deleted':
|
||||
serverFilter!.delete = true
|
||||
break
|
||||
case 'user_perms_changed':
|
||||
serverFilter!.ban = true
|
||||
serverFilter!.unban = true
|
||||
serverFilter!.kick = true
|
||||
serverFilter!.unkick = true
|
||||
break
|
||||
case 'user_admin_perms_changed':
|
||||
serverFilter!.promote = true
|
||||
serverFilter!.demote = true
|
||||
break
|
||||
case 'slow_mode_changed':
|
||||
case 'ttl_changed':
|
||||
// not documented so idk, enable both
|
||||
serverFilter!.settings = true
|
||||
serverFilter!.info = true
|
||||
break
|
||||
case 'call_started':
|
||||
case 'call_ended':
|
||||
serverFilter!.groupCall = true
|
||||
break
|
||||
case 'call_setting_changed':
|
||||
// not documented so idk, enable all
|
||||
serverFilter!.groupCall = true
|
||||
serverFilter!.settings = true
|
||||
serverFilter!.info = true
|
||||
break
|
||||
case 'user_joined_invite':
|
||||
// not documented so idk, enable all
|
||||
serverFilter!.join = true
|
||||
serverFilter!.invite = true
|
||||
serverFilter!.invites = true
|
||||
break
|
||||
case 'invite_deleted':
|
||||
case 'invite_edited':
|
||||
case 'invite_revoked':
|
||||
serverFilter!.invites = true
|
||||
break
|
||||
default:
|
||||
assertNever(type)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
serverFilter = params.filters
|
||||
const results: ChatEvent[] = []
|
||||
|
||||
for (const evt of res.events) {
|
||||
const parsed = new ChatEvent(this, evt, peers)
|
||||
|
||||
if (localFilter && (!parsed.action || !localFilter[parsed.action.type])) {
|
||||
continue
|
||||
}
|
||||
|
||||
results.push(parsed)
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const res = await this.call({
|
||||
_: 'channels.getAdminLog',
|
||||
channel,
|
||||
q: query,
|
||||
eventsFilter: serverFilter,
|
||||
admins,
|
||||
maxId,
|
||||
minId,
|
||||
limit: Math.min(chunkSize, total - current),
|
||||
})
|
||||
|
||||
if (!res.events.length) break
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
const last = res.events[res.events.length - 1]
|
||||
maxId = last.id
|
||||
|
||||
for (const evt of res.events) {
|
||||
const parsed = new ChatEvent(this, evt, peers)
|
||||
|
||||
if (localFilter && (!parsed.action || !localFilter[parsed.action.type])) {
|
||||
continue
|
||||
}
|
||||
|
||||
current += 1
|
||||
yield parsed
|
||||
|
||||
if (current >= total) break
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
|
82
packages/client/src/methods/chats/iter-chat-event-log.ts
Normal file
82
packages/client/src/methods/chats/iter-chat-event-log.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { ChatEvent, InputPeerLike } from '../../types'
|
||||
import { normalizeChatEventFilters } from '../../types/peers/chat-event/filters'
|
||||
import { normalizeToInputChannel, normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
* Iterate over chat event log.
|
||||
*
|
||||
* Small wrapper over {@link getChatEventLog}
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterChatEventLog(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getChatEventLog']>[1] & {
|
||||
/**
|
||||
* Total number of events to return.
|
||||
*
|
||||
* @default Infinity
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size, passed as `limit` to {@link getChatEventLog}.
|
||||
* Usually you don't need to touch this.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatEvent> {
|
||||
if (!params) params = {}
|
||||
|
||||
const channel = normalizeToInputChannel(await this.resolvePeer(chatId), chatId)
|
||||
|
||||
const { minId = Long.ZERO, query = '', limit = Infinity, chunkSize = 100, users, filters } = params
|
||||
|
||||
const admins: tl.TypeInputUser[] | undefined = users ?
|
||||
await this.resolvePeerMany(users, normalizeToInputUser) :
|
||||
undefined
|
||||
|
||||
const { serverFilter, localFilter } = normalizeChatEventFilters(filters)
|
||||
|
||||
let current = 0
|
||||
let maxId = params.maxId ?? Long.ZERO
|
||||
|
||||
for (;;) {
|
||||
const chunk = await this.getChatEventLog(channel, {
|
||||
minId,
|
||||
maxId,
|
||||
query,
|
||||
limit: localFilter ? chunkSize : Math.min(limit - current, chunkSize),
|
||||
// provide already resolved users to avoid resolving them again
|
||||
users: admins,
|
||||
// local filters may mess with pagination
|
||||
filters: { serverFilter },
|
||||
})
|
||||
|
||||
if (!chunk.length) break
|
||||
|
||||
const last = chunk[chunk.length - 1]
|
||||
maxId = last.id
|
||||
|
||||
for (const item of chunk) {
|
||||
if (localFilter && (!item.action || !localFilter[item.action.type])) {
|
||||
continue
|
||||
}
|
||||
|
||||
current += 1
|
||||
yield item
|
||||
|
||||
if (current >= limit) break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
import { MtArgumentError, tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { InputDialogFolder } from '../../types'
|
||||
|
||||
/**
|
||||
* Get list of folders.
|
||||
|
@ -11,3 +12,28 @@ export async function getFolders(this: TelegramClient): Promise<tl.TypeDialogFil
|
|||
_: 'messages.getDialogFilters',
|
||||
})
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function _normalizeInputFolder(
|
||||
this: TelegramClient,
|
||||
folder: InputDialogFolder,
|
||||
): Promise<tl.TypeDialogFilter> {
|
||||
if (typeof folder === 'string' || typeof folder === 'number') {
|
||||
const folders = await this.getFolders()
|
||||
const found = folders.find((it) => {
|
||||
if (it._ === 'dialogFilterDefault') {
|
||||
return folder === 0
|
||||
}
|
||||
|
||||
return it.id === folder || it.title === folder
|
||||
})
|
||||
|
||||
if (!found) {
|
||||
throw new MtArgumentError(`Could not find folder ${folder}`)
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
return folder
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export async function getPeerDialogs(
|
|||
),
|
||||
})
|
||||
|
||||
const dialogs = this._parseDialogs(res)
|
||||
const dialogs = Dialog.parseTlDialogs(this, res)
|
||||
|
||||
return isSingle ? dialogs[0] : dialogs
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { MtArgumentError, tl } from '@mtcute/core'
|
||||
import { MtUnsupportedError, tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { Dialog } from '../../types'
|
||||
import { Dialog, InputDialogFolder } from '../../types'
|
||||
import { normalizeDate } from '../../utils/misc-utils'
|
||||
|
||||
/**
|
||||
* Iterate over dialogs.
|
||||
*
|
||||
* Note that due to Telegram limitations,
|
||||
* Note that due to Telegram API limitations,
|
||||
* ordering here can only be anti-chronological
|
||||
* (i.e. newest - first), and draft update date
|
||||
* is not considered when sorting.
|
||||
|
@ -17,7 +17,7 @@ import { normalizeDate } from '../../utils/misc-utils'
|
|||
* @param params Fetch parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* getDialogs(
|
||||
export async function* iterDialogs(
|
||||
this: TelegramClient,
|
||||
params?: {
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ export async function* getDialogs(
|
|||
/**
|
||||
* Limits the number of dialogs to be received.
|
||||
*
|
||||
* Defaults to `Infinity`, i.e. all dialogs are fetched, ignored when `pinned=only`
|
||||
* @default `Infinity`, i.e. all dialogs are fetched
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
|
@ -46,7 +46,7 @@ export async function* getDialogs(
|
|||
* Chunk size which will be passed to `messages.getDialogs`.
|
||||
* You shouldn't usually care about this.
|
||||
*
|
||||
* Defaults to 100.
|
||||
* @default 100.
|
||||
*/
|
||||
chunkSize?: number
|
||||
|
||||
|
@ -60,11 +60,11 @@ export async function* getDialogs(
|
|||
* `keep`, which will return pinned dialogs
|
||||
* ordered by date among other non-pinned dialogs.
|
||||
*
|
||||
* Defaults to `include`.
|
||||
*
|
||||
* > **Note**: When using `include` mode with folders,
|
||||
* > pinned dialogs will only be fetched if all offset
|
||||
* > parameters are unset.
|
||||
*
|
||||
* @default `include`.
|
||||
*/
|
||||
pinned?: 'include' | 'exclude' | 'only' | 'keep'
|
||||
|
||||
|
@ -75,11 +75,13 @@ export async function* getDialogs(
|
|||
* `exclude` them from the list, or `only`
|
||||
* return archived dialogs
|
||||
*
|
||||
* Defaults to `exclude`, ignored for folders since folders
|
||||
* Ignored for folders, since folders
|
||||
* themselves contain information about archived chats.
|
||||
*
|
||||
* > **Note**: when fetching `only` pinned dialogs
|
||||
* > passing `keep` will act as passing `only`
|
||||
* > **Note**: when `pinned=only`, `archived=keep` will act as `only`
|
||||
* > because of Telegram API limitations.
|
||||
*
|
||||
* @default `exclude`
|
||||
*/
|
||||
archived?: 'keep' | 'exclude' | 'only'
|
||||
|
||||
|
@ -102,9 +104,9 @@ export async function* getDialogs(
|
|||
* When a folder with given ID or title is not found,
|
||||
* {@link MtArgumentError} is thrown
|
||||
*
|
||||
* By default fetches from "All" folder
|
||||
* @default <empty> (fetches from "All" folder)
|
||||
*/
|
||||
folder?: string | number | tl.RawDialogFilter
|
||||
folder?: InputDialogFolder
|
||||
|
||||
/**
|
||||
* Additional filtering for the dialogs.
|
||||
|
@ -118,49 +120,59 @@ export async function* getDialogs(
|
|||
): AsyncIterableIterator<Dialog> {
|
||||
if (!params) params = {}
|
||||
|
||||
// fetch folder if needed
|
||||
let filters: tl.TypeDialogFilter | undefined
|
||||
const { limit = Infinity, chunkSize = 100, folder, filter, pinned = 'include' } = params
|
||||
|
||||
if (typeof params.folder === 'string' || typeof params.folder === 'number') {
|
||||
const folders = await this.getFolders()
|
||||
const found = folders.find((it) => {
|
||||
if (it._ === 'dialogFilterDefault') {
|
||||
return params!.folder === 0
|
||||
}
|
||||
let { offsetId = 0, offsetPeer = { _: 'inputPeerEmpty' }, archived = 'exclude' } = params
|
||||
|
||||
return it.id === params!.folder || it.title === params!.folder
|
||||
})
|
||||
let offsetDate = normalizeDate(params.offsetDate) ?? 0
|
||||
|
||||
if (!found) {
|
||||
throw new MtArgumentError(`Could not find folder ${params.folder}`)
|
||||
}
|
||||
let localFilters_: tl.TypeDialogFilter | undefined
|
||||
|
||||
filters = found as tl.RawDialogFilter
|
||||
} else {
|
||||
filters = params.folder
|
||||
if (folder) {
|
||||
localFilters_ = await this._normalizeInputFolder(folder)
|
||||
}
|
||||
|
||||
if (params.filter) {
|
||||
if (filters) {
|
||||
filters = {
|
||||
...filters,
|
||||
...params.filter,
|
||||
if (filter) {
|
||||
if (localFilters_ && localFilters_._ !== 'dialogFilterDefault') {
|
||||
localFilters_ = {
|
||||
...localFilters_,
|
||||
...filter,
|
||||
}
|
||||
} else {
|
||||
filters = {
|
||||
_: 'dialogFilterDefault',
|
||||
localFilters_ = {
|
||||
_: 'dialogFilter',
|
||||
id: 0,
|
||||
title: '',
|
||||
pinnedPeers: [],
|
||||
includePeers: [],
|
||||
excludePeers: [],
|
||||
...params.filter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localFilters_?._ === 'dialogFilterDefault') {
|
||||
localFilters_ = undefined
|
||||
}
|
||||
|
||||
if (localFilters_?._ === 'dialogFilterChatlist') {
|
||||
throw new MtUnsupportedError('Shared chat folders are not supported yet')
|
||||
}
|
||||
|
||||
const localFilters = localFilters_
|
||||
|
||||
if (localFilters) {
|
||||
archived = localFilters.excludeArchived ? 'exclude' : 'keep'
|
||||
}
|
||||
|
||||
const fetchPinnedDialogsFromFolder = async (): Promise<tl.messages.RawPeerDialogs | null> => {
|
||||
if (!filters || filters._ === 'dialogFilterDefault' || !filters.pinnedPeers.length) {
|
||||
if (!localFilters || !localFilters.pinnedPeers.length) {
|
||||
return null
|
||||
}
|
||||
const res = await this.call({
|
||||
_: 'messages.getPeerDialogs',
|
||||
peers: filters.pinnedPeers.map((peer) => ({
|
||||
_: 'inputDialogPeer',
|
||||
peers: localFilters.pinnedPeers.map((peer) => ({
|
||||
_: 'inputDialogPeer' as const,
|
||||
peer,
|
||||
})),
|
||||
})
|
||||
|
@ -170,17 +182,10 @@ export async function* getDialogs(
|
|||
return res
|
||||
}
|
||||
|
||||
const pinned = params.pinned ?? 'include'
|
||||
let archived = params.archived ?? 'exclude'
|
||||
|
||||
if (filters) {
|
||||
archived = filters._ !== 'dialogFilterDefault' && filters.excludeArchived ? 'exclude' : 'keep'
|
||||
}
|
||||
|
||||
if (pinned === 'only') {
|
||||
let res
|
||||
|
||||
if (filters) {
|
||||
if (localFilters) {
|
||||
res = await fetchPinnedDialogsFromFolder()
|
||||
} else {
|
||||
res = await this.call({
|
||||
|
@ -188,38 +193,36 @@ export async function* getDialogs(
|
|||
folderId: archived === 'exclude' ? 0 : 1,
|
||||
})
|
||||
}
|
||||
if (res) yield* this._parseDialogs(res)
|
||||
if (res) yield* Dialog.parseTlDialogs(this, res, limit)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let current = 0
|
||||
const total = params.limit ?? Infinity
|
||||
const chunkSize = Math.min(params.chunkSize ?? 100, total)
|
||||
|
||||
let offsetId = params.offsetId ?? 0
|
||||
let offsetDate = normalizeDate(params.offsetDate) ?? 0
|
||||
let offsetPeer = params.offsetPeer ?? { _: 'inputPeerEmpty' }
|
||||
|
||||
if (filters && filters._ !== 'dialogFilterDefault' && filters.pinnedPeers.length && pinned === 'include') {
|
||||
if (
|
||||
localFilters?.pinnedPeers.length &&
|
||||
pinned === 'include' &&
|
||||
offsetId === 0 &&
|
||||
offsetDate === 0 &&
|
||||
offsetPeer._ === 'inputPeerEmpty'
|
||||
) {
|
||||
const res = await fetchPinnedDialogsFromFolder()
|
||||
|
||||
if (res) {
|
||||
const dialogs = this._parseDialogs(res)
|
||||
const dialogs = Dialog.parseTlDialogs(this, res, limit)
|
||||
|
||||
for (const d of dialogs) {
|
||||
yield d
|
||||
|
||||
if (++current >= total) return
|
||||
if (++current >= limit) return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if pinned is `only`, this wouldn't be reached
|
||||
// if pinned is `exclude`, we want to exclude them
|
||||
// if pinned is `include`, we already yielded them, so we also want to exclude them
|
||||
// if pinned is `keep`, we want to keep them
|
||||
const filterFolder = filters ? Dialog.filterFolder(filters, pinned !== 'keep') : undefined
|
||||
const filterFolder = localFilters ? Dialog.filterFolder(localFilters, pinned !== 'keep') : undefined
|
||||
|
||||
let folderId
|
||||
|
||||
|
@ -232,7 +235,8 @@ export async function* getDialogs(
|
|||
}
|
||||
|
||||
for (;;) {
|
||||
const dialogs = this._parseDialogs(
|
||||
const dialogs = Dialog.parseTlDialogs(
|
||||
this,
|
||||
await this.call({
|
||||
_: 'messages.getDialogs',
|
||||
excludePinned: params.pinned === 'exclude',
|
||||
|
@ -240,8 +244,7 @@ export async function* getDialogs(
|
|||
offsetDate,
|
||||
offsetId,
|
||||
offsetPeer,
|
||||
|
||||
limit: chunkSize,
|
||||
limit: filterFolder ? chunkSize : Math.min(limit - current, chunkSize),
|
||||
hash: Long.ZERO,
|
||||
}),
|
||||
)
|
||||
|
@ -250,13 +253,13 @@ export async function* getDialogs(
|
|||
const last = dialogs[dialogs.length - 1]
|
||||
offsetPeer = last.chat.inputPeer
|
||||
offsetId = last.raw.topMessage
|
||||
offsetDate = normalizeDate(last.lastMessage?.date) ?? 0
|
||||
offsetDate = normalizeDate(last.lastMessage.date)!
|
||||
|
||||
for (const d of dialogs) {
|
||||
if (filterFolder && !filterFolder(d)) continue
|
||||
|
||||
yield d
|
||||
if (++current >= total) return
|
||||
if (++current >= limit) return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import { getMarkedPeerId, tl } from '@mtcute/core'
|
||||
import { assertTypeIsNot } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { Dialog, PeersIndex } from '../../types'
|
||||
|
||||
/** @internal */
|
||||
export function _parseDialogs(
|
||||
this: TelegramClient,
|
||||
res: tl.messages.TypeDialogs | tl.messages.TypePeerDialogs,
|
||||
): Dialog[] {
|
||||
assertTypeIsNot('parseDialogs', res, 'messages.dialogsNotModified')
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
const messages: Record<number, tl.TypeMessage> = {}
|
||||
res.messages.forEach((msg) => {
|
||||
if (!msg.peerId) return
|
||||
|
||||
messages[getMarkedPeerId(msg.peerId)] = msg
|
||||
})
|
||||
|
||||
return res.dialogs
|
||||
.filter((it) => it._ === 'dialog')
|
||||
.map((it) => new Dialog(this, it as tl.RawDialog, peers, messages))
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { ChatInviteLinkJoinedMember, InputPeerLike, PeersIndex, User } from '../../types'
|
||||
import { ArrayWithTotal, ChatInviteLinkMember, InputPeerLike, PeersIndex } from '../../types'
|
||||
import { makeArrayWithTotal, normalizeDate } from '../../utils'
|
||||
|
||||
/**
|
||||
* Iterate over users who have joined
|
||||
|
@ -11,7 +12,7 @@ import { ChatInviteLinkJoinedMember, InputPeerLike, PeersIndex, User } from '../
|
|||
* @param params Additional params
|
||||
* @internal
|
||||
*/
|
||||
export async function* getInviteLinkMembers(
|
||||
export async function getInviteLinkMembers(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params: {
|
||||
|
@ -21,10 +22,22 @@ export async function* getInviteLinkMembers(
|
|||
link?: string
|
||||
|
||||
/**
|
||||
* Maximum number of users to return (by default returns all)
|
||||
* Maximum number of users to return
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Offset request/join date used as an anchor for pagination.
|
||||
*/
|
||||
offsetDate?: Date | number
|
||||
|
||||
/**
|
||||
* Offset user used as an anchor for pagination
|
||||
*/
|
||||
offsetUser?: tl.TypeInputUser
|
||||
|
||||
/**
|
||||
* Whether to get users who have requested to join
|
||||
* the chat but weren't accepted yet
|
||||
|
@ -33,59 +46,36 @@ export async function* getInviteLinkMembers(
|
|||
|
||||
/**
|
||||
* Search for a user in the pending join requests list
|
||||
* (only works if {@link requested} is true)
|
||||
* (if passed, {@link requested} is assumed to be true)
|
||||
*
|
||||
* Doesn't work when {@link link} is set (Telegram limitation)
|
||||
*/
|
||||
requestedSearch?: string
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLinkJoinedMember> {
|
||||
): Promise<ArrayWithTotal<ChatInviteLinkMember>> {
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
const limit = params.limit ?? Infinity
|
||||
let current = 0
|
||||
const { limit = 100, link, requestedSearch, requested = Boolean(requestedSearch) } = params
|
||||
|
||||
let offsetDate = 0
|
||||
let offsetUser: tl.TypeInputUser = { _: 'inputUserEmpty' }
|
||||
const { offsetUser = { _: 'inputUserEmpty' } } = params
|
||||
|
||||
for (;;) {
|
||||
// for some reason ts needs annotation, idk
|
||||
const res: tl.RpcCallReturn['messages.getChatInviteImporters'] = await this.call({
|
||||
_: 'messages.getChatInviteImporters',
|
||||
limit: Math.min(100, limit - current),
|
||||
peer,
|
||||
link: params.link,
|
||||
requested: params.requested,
|
||||
q: params.requestedSearch,
|
||||
offsetDate,
|
||||
offsetUser,
|
||||
})
|
||||
const offsetDate = normalizeDate(params.offsetDate) ?? 0
|
||||
|
||||
if (!res.importers.length) break
|
||||
const res = await this.call({
|
||||
_: 'messages.getChatInviteImporters',
|
||||
limit,
|
||||
peer,
|
||||
link,
|
||||
requested,
|
||||
q: requestedSearch,
|
||||
offsetDate,
|
||||
offsetUser,
|
||||
})
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
const last = res.importers[res.importers.length - 1]
|
||||
offsetDate = last.date
|
||||
offsetUser = {
|
||||
_: 'inputUser',
|
||||
userId: last.userId,
|
||||
accessHash: (peers.user(last.userId) as tl.RawUser).accessHash!,
|
||||
}
|
||||
|
||||
for (const it of res.importers) {
|
||||
const user = new User(this, peers.user(it.userId))
|
||||
|
||||
yield {
|
||||
user,
|
||||
date: new Date(it.date * 1000),
|
||||
isPendingRequest: it.requested!,
|
||||
bio: it.about,
|
||||
approvedBy: it.approvedBy,
|
||||
}
|
||||
}
|
||||
|
||||
current += res.importers.length
|
||||
if (current >= limit) break
|
||||
}
|
||||
return makeArrayWithTotal(
|
||||
res.importers.map((it) => new ChatInviteLinkMember(this, it, peers)),
|
||||
res.count,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
import { assertTypeIsNot } from '@mtcute/core/utils'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types'
|
||||
import { ArrayWithTotal, ChatInviteLink, InputPeerLike, PeersIndex } from '../../types'
|
||||
import { makeArrayWithTotal, normalizeDate } from '../../utils'
|
||||
import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
|
@ -17,11 +15,17 @@ import { normalizeToInputUser } from '../../utils/peer-utils'
|
|||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function* getInviteLinks(
|
||||
export async function getInviteLinks(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
adminId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Only return this admin's links.
|
||||
*
|
||||
* @default `"self"`
|
||||
*/
|
||||
admin?: InputPeerLike
|
||||
|
||||
/**
|
||||
* Whether to fetch revoked invite links
|
||||
*/
|
||||
|
@ -29,55 +33,43 @@ export async function* getInviteLinks(
|
|||
|
||||
/**
|
||||
* Limit the number of invite links to be fetched.
|
||||
* By default, all links are fetched.
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Size of chunks which are fetched. Usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
* Offset date used as an anchor for pagination.
|
||||
*/
|
||||
chunkSize?: number
|
||||
offsetDate?: Date | number
|
||||
|
||||
/**
|
||||
* Offset link used as an anchor for pagination
|
||||
*/
|
||||
offsetLink?: string
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLink> {
|
||||
): Promise<ArrayWithTotal<ChatInviteLink>> {
|
||||
if (!params) params = {}
|
||||
|
||||
let current = 0
|
||||
const total = params.limit || Infinity
|
||||
const chunkSize = Math.min(params.chunkSize ?? 100, total)
|
||||
const { revoked = false, limit = Infinity, admin } = params
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
const admin = normalizeToInputUser(await this.resolvePeer(adminId), adminId)
|
||||
const offsetDate = normalizeDate(params.offsetDate)
|
||||
const offsetLink = params.offsetLink
|
||||
|
||||
let offsetDate: number | undefined = undefined
|
||||
let offsetLink: string | undefined = undefined
|
||||
const res = await this.call({
|
||||
_: 'messages.getExportedChatInvites',
|
||||
peer: await this.resolvePeer(chatId),
|
||||
revoked,
|
||||
adminId: admin ? normalizeToInputUser(await this.resolvePeer(admin), admin) : { _: 'inputUserSelf' },
|
||||
limit,
|
||||
offsetDate,
|
||||
offsetLink,
|
||||
})
|
||||
|
||||
for (;;) {
|
||||
const res: tl.RpcCallReturn['messages.getExportedChatInvites'] = await this.call({
|
||||
_: 'messages.getExportedChatInvites',
|
||||
peer,
|
||||
adminId: admin,
|
||||
limit: Math.min(chunkSize, total - current),
|
||||
offsetDate,
|
||||
offsetLink,
|
||||
})
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
if (!res.invites.length) break
|
||||
|
||||
const peers = PeersIndex.from(res)
|
||||
|
||||
const last = res.invites[res.invites.length - 1]
|
||||
|
||||
assertTypeIsNot('getInviteLinks', last, 'chatInvitePublicJoinRequests')
|
||||
offsetDate = last.date
|
||||
offsetLink = last.link
|
||||
|
||||
for (const it of res.invites) {
|
||||
yield new ChatInviteLink(this, it, peers)
|
||||
}
|
||||
|
||||
current += res.invites.length
|
||||
if (current >= total) break
|
||||
}
|
||||
return makeArrayWithTotal(
|
||||
res.invites.map((it) => new ChatInviteLink(this, it, peers)),
|
||||
res.count,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { ChatInviteLinkMember, InputPeerLike } from '../../types'
|
||||
import { normalizeToInputUser } from '../../utils'
|
||||
|
||||
/**
|
||||
* Iterate over users who have joined
|
||||
* the chat with the given invite link.
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param params Additional params
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterInviteLinkMembers(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params: Parameters<TelegramClient['getInviteLinkMembers']>[1] & {
|
||||
/**
|
||||
* Maximum number of users to return
|
||||
*
|
||||
* @default `Infinity`, i.e. all users are fetched
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Chunk size which will be passed to `messages.getChatInviteImporters`.
|
||||
* You shouldn't usually care about this.
|
||||
*
|
||||
* @default 100.
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLinkMember> {
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
const { limit = Infinity, chunkSize = 100, link, requestedSearch, requested = Boolean(requestedSearch) } = params
|
||||
|
||||
let { offsetDate, offsetUser = { _: 'inputUserEmpty' } } = params
|
||||
|
||||
let current = 0
|
||||
|
||||
for (;;) {
|
||||
const items = await this.getInviteLinkMembers(peer, {
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
link,
|
||||
requested,
|
||||
requestedSearch,
|
||||
offsetDate,
|
||||
offsetUser,
|
||||
})
|
||||
|
||||
if (!items.length) break
|
||||
|
||||
const last = items[items.length - 1]
|
||||
offsetDate = last.date
|
||||
offsetUser = normalizeToInputUser(last.user.inputPeer)
|
||||
|
||||
for (const it of items) {
|
||||
yield it
|
||||
|
||||
if (++current >= limit) return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { ChatInviteLink, InputPeerLike } from '../../types'
|
||||
|
||||
/**
|
||||
* Iterate over invite links created by some administrator in the chat.
|
||||
*
|
||||
* As an administrator you can only get your own links
|
||||
* (i.e. `adminId = "self"`), as a creator you can get
|
||||
* any other admin's links.
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param adminId Admin who created the links
|
||||
* @param params
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterInviteLinks(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getInviteLinks']>[1] & {
|
||||
/**
|
||||
* Limit the number of invite links to be fetched.
|
||||
* By default, all links are fetched.
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Size of chunks which are fetched. Usually not needed.
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
chunkSize?: number
|
||||
},
|
||||
): AsyncIterableIterator<ChatInviteLink> {
|
||||
if (!params) params = {}
|
||||
|
||||
const { revoked = false, limit = Infinity, chunkSize = 100, admin } = params
|
||||
|
||||
let { offsetDate, offsetLink } = params
|
||||
|
||||
let current = 0
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
const adminResolved = admin ? await this.resolvePeer(admin) : ({ _: 'inputUserSelf' } as const)
|
||||
|
||||
for (;;) {
|
||||
const links = await this.getInviteLinks(peer, {
|
||||
admin: adminResolved,
|
||||
revoked,
|
||||
limit: Math.min(chunkSize, limit - current),
|
||||
offsetDate,
|
||||
offsetLink,
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,19 @@
|
|||
import { getMarkedPeerId, tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { makeInspectable } from '../../utils'
|
||||
import { assertTypeIsNot, hasValueAtKey, makeInspectable } from '../../utils'
|
||||
import { MtMessageNotFoundError } from '../errors'
|
||||
import { Chat, PeersIndex } from '../peers'
|
||||
import { DraftMessage } from './draft-message'
|
||||
import { Message } from './message'
|
||||
|
||||
/**
|
||||
* Type used as an input for a folder in client methods
|
||||
*
|
||||
* You can pass folder object, id or title
|
||||
*/
|
||||
export type InputDialogFolder = string | number | tl.RawDialogFilter
|
||||
|
||||
/**
|
||||
* A dialog.
|
||||
*
|
||||
|
@ -21,6 +28,40 @@ export class Dialog {
|
|||
readonly _messages: Record<number, tl.TypeMessage>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Parse a list of dialogs from a TL object
|
||||
*
|
||||
* @param client Client instance
|
||||
* @param dialogs TL object
|
||||
* @param limit Maximum number of dialogs to parse
|
||||
*/
|
||||
static parseTlDialogs(
|
||||
client: TelegramClient,
|
||||
dialogs: tl.messages.TypeDialogs | tl.messages.TypePeerDialogs,
|
||||
limit?: number,
|
||||
): Dialog[] {
|
||||
assertTypeIsNot('parseDialogs', dialogs, 'messages.dialogsNotModified')
|
||||
|
||||
const peers = PeersIndex.from(dialogs)
|
||||
|
||||
const messages: Record<number, tl.TypeMessage> = {}
|
||||
dialogs.messages.forEach((msg) => {
|
||||
if (!msg.peerId) return
|
||||
|
||||
messages[getMarkedPeerId(msg.peerId)] = msg
|
||||
})
|
||||
|
||||
const arr = dialogs.dialogs
|
||||
.filter(hasValueAtKey('_', 'dialog'))
|
||||
.map((it) => new Dialog(client, it, peers, messages))
|
||||
|
||||
if (limit) {
|
||||
return arr.slice(0, limit)
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* Find pinned dialogs from a list of dialogs
|
||||
*
|
||||
|
@ -183,7 +224,7 @@ export class Dialog {
|
|||
/**
|
||||
* The latest message sent in this chat
|
||||
*/
|
||||
get lastMessage(): Message | null {
|
||||
get lastMessage(): Message {
|
||||
if (!this._lastMessage) {
|
||||
const cid = this.chat.id
|
||||
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import { tl, toggleChannelIdMark } from '@mtcute/core'
|
||||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { makeInspectable } from '../../utils'
|
||||
import { Photo } from '../media'
|
||||
import { Message } from '../messages'
|
||||
import { ChatInviteLink } from './chat-invite-link'
|
||||
import { ChatLocation } from './chat-location'
|
||||
import { ChatMember } from './chat-member'
|
||||
import { ChatPermissions } from './chat-permissions'
|
||||
import { PeersIndex } from './index'
|
||||
import { User } from './user'
|
||||
import { PeersIndex, TelegramClient, toggleChannelIdMark } from '../../..'
|
||||
import { Photo } from '../../media'
|
||||
import { Message } from '../../messages'
|
||||
import { ChatInviteLink } from '../chat-invite-link'
|
||||
import { ChatLocation } from '../chat-location'
|
||||
import { ChatMember } from '../chat-member'
|
||||
import { ChatPermissions } from '../chat-permissions'
|
||||
|
||||
/** A user has joined the group (in the case of big groups, info of the user that has joined isn't shown) */
|
||||
export interface ChatActionUserJoined {
|
||||
|
@ -321,7 +318,28 @@ export type ChatAction =
|
|||
| ChatActionTtlChanged
|
||||
| null
|
||||
|
||||
function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): ChatAction {
|
||||
/** @internal */
|
||||
export function _actionFromTl(
|
||||
e: tl.TypeChannelAdminLogEventAction,
|
||||
client: TelegramClient,
|
||||
peers: PeersIndex,
|
||||
): ChatAction {
|
||||
// todo - MTQ-78
|
||||
// channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long
|
||||
// channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
|
||||
// todo - MTQ-57
|
||||
// channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string>
|
||||
// todo - MTQ-77
|
||||
// channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
|
||||
// channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
// channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic
|
||||
// channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
// channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic
|
||||
// todo - MTQ-72
|
||||
// channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
||||
// channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions
|
||||
// channelAdminLogEventActionToggleAntiSpam#64f36dfc new_value:Bool = ChannelAdminLogEventAction;
|
||||
|
||||
switch (e._) {
|
||||
case 'channelAdminLogEventActionParticipantJoin':
|
||||
return { type: 'user_joined' }
|
||||
|
@ -346,8 +364,8 @@ function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): C
|
|||
case 'channelAdminLogEventActionChangePhoto':
|
||||
return {
|
||||
type: 'photo_changed',
|
||||
old: new Photo(this.client, e.prevPhoto as tl.RawPhoto),
|
||||
new: new Photo(this.client, e.newPhoto as tl.RawPhoto),
|
||||
old: new Photo(client, e.prevPhoto as tl.RawPhoto),
|
||||
new: new Photo(client, e.newPhoto as tl.RawPhoto),
|
||||
}
|
||||
case 'channelAdminLogEventActionToggleInvites':
|
||||
return {
|
||||
|
@ -364,37 +382,37 @@ function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): C
|
|||
case 'channelAdminLogEventActionUpdatePinned':
|
||||
return {
|
||||
type: 'msg_pinned',
|
||||
message: new Message(this.client, e.message, this._peers),
|
||||
message: new Message(client, e.message, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionEditMessage':
|
||||
return {
|
||||
type: 'msg_edited',
|
||||
old: new Message(this.client, e.prevMessage, this._peers),
|
||||
new: new Message(this.client, e.newMessage, this._peers),
|
||||
old: new Message(client, e.prevMessage, peers),
|
||||
new: new Message(client, e.newMessage, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionDeleteMessage':
|
||||
return {
|
||||
type: 'msg_deleted',
|
||||
message: new Message(this.client, e.message, this._peers),
|
||||
message: new Message(client, e.message, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionParticipantLeave':
|
||||
return { type: 'user_left' }
|
||||
case 'channelAdminLogEventActionParticipantInvite':
|
||||
return {
|
||||
type: 'user_invited',
|
||||
member: new ChatMember(this.client, e.participant, this._peers),
|
||||
member: new ChatMember(client, e.participant, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionParticipantToggleBan':
|
||||
return {
|
||||
type: 'user_perms_changed',
|
||||
old: new ChatMember(this.client, e.prevParticipant, this._peers),
|
||||
new: new ChatMember(this.client, e.newParticipant, this._peers),
|
||||
old: new ChatMember(client, e.prevParticipant, peers),
|
||||
new: new ChatMember(client, e.newParticipant, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionParticipantToggleAdmin':
|
||||
return {
|
||||
type: 'user_admin_perms_changed',
|
||||
old: new ChatMember(this.client, e.prevParticipant, this._peers),
|
||||
new: new ChatMember(this.client, e.newParticipant, this._peers),
|
||||
old: new ChatMember(client, e.prevParticipant, peers),
|
||||
new: new ChatMember(client, e.newParticipant, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionChangeStickerSet':
|
||||
return {
|
||||
|
@ -417,7 +435,7 @@ function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): C
|
|||
case 'channelAdminLogEventActionStopPoll':
|
||||
return {
|
||||
type: 'poll_stopped',
|
||||
message: new Message(this.client, e.message, this._peers),
|
||||
message: new Message(client, e.message, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionChangeLinkedChat':
|
||||
return {
|
||||
|
@ -428,8 +446,8 @@ function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): C
|
|||
case 'channelAdminLogEventActionChangeLocation':
|
||||
return {
|
||||
type: 'location_changed',
|
||||
old: e.prevValue._ === 'channelLocationEmpty' ? null : new ChatLocation(this.client, e.prevValue),
|
||||
new: e.newValue._ === 'channelLocationEmpty' ? null : new ChatLocation(this.client, e.newValue),
|
||||
old: e.prevValue._ === 'channelLocationEmpty' ? null : new ChatLocation(client, e.prevValue),
|
||||
new: e.newValue._ === 'channelLocationEmpty' ? null : new ChatLocation(client, e.newValue),
|
||||
}
|
||||
case 'channelAdminLogEventActionToggleSlowMode':
|
||||
return {
|
||||
|
@ -460,23 +478,23 @@ function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): C
|
|||
case 'channelAdminLogEventActionParticipantJoinByInvite':
|
||||
return {
|
||||
type: 'user_joined_invite',
|
||||
link: new ChatInviteLink(this.client, e.invite, this._peers),
|
||||
link: new ChatInviteLink(client, e.invite, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionExportedInviteDelete':
|
||||
return {
|
||||
type: 'invite_deleted',
|
||||
link: new ChatInviteLink(this.client, e.invite, this._peers),
|
||||
link: new ChatInviteLink(client, e.invite, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionExportedInviteRevoke':
|
||||
return {
|
||||
type: 'invite_revoked',
|
||||
link: new ChatInviteLink(this.client, e.invite, this._peers),
|
||||
link: new ChatInviteLink(client, e.invite, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionExportedInviteEdit':
|
||||
return {
|
||||
type: 'invite_edited',
|
||||
old: new ChatInviteLink(this.client, e.prevInvite, this._peers),
|
||||
new: new ChatInviteLink(this.client, e.newInvite, this._peers),
|
||||
old: new ChatInviteLink(client, e.prevInvite, peers),
|
||||
new: new ChatInviteLink(client, e.newInvite, peers),
|
||||
}
|
||||
case 'channelAdminLogEventActionChangeHistoryTTL':
|
||||
return {
|
||||
|
@ -488,43 +506,3 @@ function _actionFromTl(this: ChatEvent, e: tl.TypeChannelAdminLogEventAction): C
|
|||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatEvent {
|
||||
constructor(
|
||||
readonly client: TelegramClient,
|
||||
readonly raw: tl.TypeChannelAdminLogEvent,
|
||||
readonly _peers: PeersIndex,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Event ID.
|
||||
*
|
||||
* Event IDs are generated in direct chronological order
|
||||
* (i.e. newer events have bigger event ID)
|
||||
*/
|
||||
get id(): tl.Long {
|
||||
return this.raw.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Date of the event
|
||||
*/
|
||||
get date(): Date {
|
||||
return new Date(this.raw.date * 1000)
|
||||
}
|
||||
|
||||
private _actor?: User
|
||||
/**
|
||||
* Actor of the event
|
||||
*/
|
||||
get actor(): User {
|
||||
return (this._actor ??= new User(this.client, this._peers.user(this.raw.userId)))
|
||||
}
|
||||
|
||||
private _action?: ChatAction
|
||||
get action(): ChatAction {
|
||||
return (this._action ??= _actionFromTl.call(this, this.raw.action))
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(ChatEvent)
|
123
packages/client/src/types/peers/chat-event/filters.ts
Normal file
123
packages/client/src/types/peers/chat-event/filters.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
import { assertNever, MaybeArray, tl } from '@mtcute/core'
|
||||
|
||||
import { ChatAction } from './actions'
|
||||
|
||||
export interface ChatEventFilters {
|
||||
serverFilter?: tl.TypeChannelAdminLogEventsFilter
|
||||
localFilter?: Record<string, true>
|
||||
}
|
||||
|
||||
export type InputChatEventFilters =
|
||||
| ChatEventFilters
|
||||
| tl.TypeChannelAdminLogEventsFilter
|
||||
| MaybeArray<Exclude<ChatAction, null>['type']>
|
||||
| undefined
|
||||
|
||||
/** @internal */
|
||||
export function normalizeChatEventFilters(input: InputChatEventFilters): ChatEventFilters {
|
||||
if (!input) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (typeof input === 'string' || Array.isArray(input)) {
|
||||
if (!Array.isArray(input)) input = [input]
|
||||
|
||||
const serverFilter: tl.Mutable<tl.TypeChannelAdminLogEventsFilter> = {
|
||||
_: 'channelAdminLogEventsFilter',
|
||||
}
|
||||
const localFilter: Record<string, true> = {}
|
||||
|
||||
input.forEach((type) => {
|
||||
localFilter[type] = true
|
||||
|
||||
switch (type) {
|
||||
case 'user_joined':
|
||||
serverFilter.join = true
|
||||
break
|
||||
case 'user_left':
|
||||
serverFilter.leave = true
|
||||
break
|
||||
case 'user_invited':
|
||||
serverFilter.invite = true
|
||||
break
|
||||
case 'title_changed':
|
||||
case 'description_changed':
|
||||
case 'linked_chat_changed':
|
||||
case 'location_changed':
|
||||
case 'photo_changed':
|
||||
case 'username_changed':
|
||||
case 'stickerset_changed':
|
||||
serverFilter.info = true
|
||||
break
|
||||
case 'invites_toggled':
|
||||
case 'history_toggled':
|
||||
case 'signatures_toggled':
|
||||
case 'def_perms_changed':
|
||||
serverFilter.settings = true
|
||||
break
|
||||
case 'msg_pinned':
|
||||
serverFilter.pinned = true
|
||||
break
|
||||
case 'msg_edited':
|
||||
case 'poll_stopped':
|
||||
serverFilter.edit = true
|
||||
break
|
||||
case 'msg_deleted':
|
||||
serverFilter.delete = true
|
||||
break
|
||||
case 'user_perms_changed':
|
||||
serverFilter.ban = true
|
||||
serverFilter.unban = true
|
||||
serverFilter.kick = true
|
||||
serverFilter.unkick = true
|
||||
break
|
||||
case 'user_admin_perms_changed':
|
||||
serverFilter.promote = true
|
||||
serverFilter.demote = true
|
||||
break
|
||||
case 'slow_mode_changed':
|
||||
case 'ttl_changed':
|
||||
// not documented so idk, enable both
|
||||
serverFilter.settings = true
|
||||
serverFilter.info = true
|
||||
break
|
||||
case 'call_started':
|
||||
case 'call_ended':
|
||||
serverFilter.groupCall = true
|
||||
break
|
||||
case 'call_setting_changed':
|
||||
// not documented so idk, enable all
|
||||
serverFilter.groupCall = true
|
||||
serverFilter.settings = true
|
||||
serverFilter.info = true
|
||||
break
|
||||
case 'user_joined_invite':
|
||||
// not documented so idk, enable all
|
||||
serverFilter.join = true
|
||||
serverFilter.invite = true
|
||||
serverFilter.invites = true
|
||||
break
|
||||
case 'invite_deleted':
|
||||
case 'invite_edited':
|
||||
case 'invite_revoked':
|
||||
serverFilter.invites = true
|
||||
break
|
||||
default:
|
||||
assertNever(type)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
serverFilter,
|
||||
localFilter,
|
||||
}
|
||||
}
|
||||
|
||||
if ('_' in input) {
|
||||
return {
|
||||
serverFilter: input,
|
||||
}
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
50
packages/client/src/types/peers/chat-event/index.ts
Normal file
50
packages/client/src/types/peers/chat-event/index.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../../client'
|
||||
import { makeInspectable } from '../../../utils'
|
||||
import { PeersIndex } from '../peers-index'
|
||||
import { User } from '../user'
|
||||
import { _actionFromTl, ChatAction } from './actions'
|
||||
|
||||
export * from './actions'
|
||||
export { InputChatEventFilters } from './filters'
|
||||
|
||||
export class ChatEvent {
|
||||
constructor(
|
||||
readonly client: TelegramClient,
|
||||
readonly raw: tl.TypeChannelAdminLogEvent,
|
||||
readonly _peers: PeersIndex,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Event ID.
|
||||
*
|
||||
* Event IDs are generated in direct chronological order
|
||||
* (i.e. newer events have bigger event ID)
|
||||
*/
|
||||
get id(): tl.Long {
|
||||
return this.raw.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Date of the event
|
||||
*/
|
||||
get date(): Date {
|
||||
return new Date(this.raw.date * 1000)
|
||||
}
|
||||
|
||||
private _actor?: User
|
||||
/**
|
||||
* Actor of the event
|
||||
*/
|
||||
get actor(): User {
|
||||
return (this._actor ??= new User(this.client, this._peers.user(this.raw.userId)))
|
||||
}
|
||||
|
||||
private _action?: ChatAction
|
||||
get action(): ChatAction {
|
||||
return (this._action ??= _actionFromTl(this.raw.action, this.client, this._peers))
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(ChatEvent)
|
59
packages/client/src/types/peers/chat-invite-link-member.ts
Normal file
59
packages/client/src/types/peers/chat-invite-link-member.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { tl } from '@mtcute/core'
|
||||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { makeInspectable } from '../../utils'
|
||||
import { PeersIndex } from './peers-index'
|
||||
import { User } from './user'
|
||||
|
||||
export class ChatInviteLinkMember {
|
||||
constructor(readonly client: TelegramClient, readonly raw: tl.RawChatInviteImporter, readonly _peers: PeersIndex) {}
|
||||
|
||||
private _user?: User
|
||||
/**
|
||||
* User who joined the chat
|
||||
*/
|
||||
get user(): User {
|
||||
return (this._user ??= new User(this.client, this._peers.user(this.raw.userId)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Date when the user joined the chat
|
||||
*/
|
||||
get date(): Date {
|
||||
return new Date(this.raw.date * 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this user currently has a pending join request
|
||||
* (and is actually not a member yet)
|
||||
*/
|
||||
get isPendingRequest(): boolean {
|
||||
return this.raw.requested!
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the participant joined by importing a chat folder deep link
|
||||
*/
|
||||
get isViaChatlist(): boolean {
|
||||
return this.raw.requested!
|
||||
}
|
||||
|
||||
/**
|
||||
* For users with pending requests, contains bio of the user that requested to join
|
||||
*/
|
||||
get bio(): string | null {
|
||||
return this.raw.about ?? null
|
||||
}
|
||||
|
||||
private _approvedBy?: User
|
||||
/**
|
||||
* The administrator that approved the join request of the user
|
||||
*/
|
||||
get approvedBy(): User | null {
|
||||
if (!this.raw.approvedBy) return null
|
||||
|
||||
return (this._approvedBy ??= new User(this.client, this._peers.user(this.raw.approvedBy)))
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(ChatInviteLinkMember)
|
|
@ -6,32 +6,6 @@ import { makeInspectable } from '../../utils'
|
|||
import { PeersIndex } from './index'
|
||||
import { User } from './user'
|
||||
|
||||
export interface ChatInviteLinkJoinedMember {
|
||||
/**
|
||||
* User who joined the chat
|
||||
*/
|
||||
user: User
|
||||
|
||||
/**
|
||||
* Date when the user joined the chat
|
||||
*/
|
||||
date: Date
|
||||
|
||||
/**
|
||||
* Whether the user currently has a pending join request
|
||||
*/
|
||||
isPendingRequest: boolean
|
||||
/**
|
||||
* For users with pending requests,
|
||||
* contains bio of the user that requested to join
|
||||
*/
|
||||
bio?: string
|
||||
/**
|
||||
* The administrator that approved the join request of the user
|
||||
*/
|
||||
approvedBy?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* An invite link
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@ import { tl } from '@mtcute/core'
|
|||
export * from './chat'
|
||||
export * from './chat-event'
|
||||
export * from './chat-invite-link'
|
||||
export * from './chat-invite-link-member'
|
||||
export * from './chat-location'
|
||||
export * from './chat-member'
|
||||
export * from './chat-permissions'
|
||||
|
|
|
@ -2,7 +2,7 @@ import { getBarePeerId, tl } from '@mtcute/core'
|
|||
|
||||
import { TelegramClient } from '../../client'
|
||||
import { makeInspectable } from '../../utils'
|
||||
import { ChatInviteLinkJoinedMember, PeersIndex, User } from '../peers'
|
||||
import { PeersIndex, User } from '../peers'
|
||||
|
||||
/**
|
||||
* This update is sent when a user requests to join a chat
|
||||
|
@ -68,17 +68,6 @@ export class ChatJoinRequestUpdate {
|
|||
await this.client.hideJoinRequest(this.chatId, id, action)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all pending join requests for this chat
|
||||
*/
|
||||
fetchAll(params?: { limit?: number; search?: string }): AsyncIterableIterator<ChatInviteLinkJoinedMember> {
|
||||
return this.client.getInviteLinkMembers(this.chatId, {
|
||||
limit: params?.limit,
|
||||
requested: true,
|
||||
requestedSearch: params?.search,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(ChatJoinRequestUpdate)
|
||||
|
|
|
@ -18,14 +18,10 @@ export async function resolveMaybeDynamic<T>(val: MaybeDynamic<T>): Promise<T> {
|
|||
}
|
||||
|
||||
export function makeArrayWithTotal<T>(arr: T[], total: number): ArrayWithTotal<T> {
|
||||
Object.defineProperty(arr, 'total', {
|
||||
value: total,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
})
|
||||
const a = arr as ArrayWithTotal<T>
|
||||
a.total = total
|
||||
|
||||
return arr as ArrayWithTotal<T>
|
||||
return a
|
||||
}
|
||||
|
||||
export function extractChannelIdFromUpdate(upd: tl.TypeUpdate): number | undefined {
|
||||
|
|
|
@ -44,7 +44,7 @@ export function hasPresentKey<K extends string | number | symbol>(k: K) {
|
|||
* files[0].imageUrl // TS will know this is present, because already it excluded the other union members.
|
||||
* ```
|
||||
*/
|
||||
export function hasValueAtKey<K extends string | number | symbol, V>(k: K, v: V) {
|
||||
export function hasValueAtKey<const K extends string | number | symbol, const V>(k: K, v: V) {
|
||||
return function <T> (a: T & { [k in K]: unknown }): a is T & { [k in K]: V } {
|
||||
return a[k] === v
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue