feat(client): getChatMembers and iterChatMembers methods
This commit is contained in:
parent
ce3f694eb0
commit
14dc62e912
3 changed files with 296 additions and 0 deletions
|
@ -23,9 +23,11 @@ import { deleteChatPhoto } from './methods/chats/delete-chat-photo'
|
|||
import { deleteGroup } from './methods/chats/delete-group'
|
||||
import { deleteHistory } from './methods/chats/delete-history'
|
||||
import { getChatMember } from './methods/chats/get-chat-member'
|
||||
import { getChatMembers } from './methods/chats/get-chat-members'
|
||||
import { getChatPreview } from './methods/chats/get-chat-preview'
|
||||
import { getChat } from './methods/chats/get-chat'
|
||||
import { getFullChat } from './methods/chats/get-full-chat'
|
||||
import { iterChatMembers } from './methods/chats/iter-chat-members'
|
||||
import { joinChat } from './methods/chats/join-chat'
|
||||
import { leaveChat } from './methods/chats/leave-chat'
|
||||
import { setChatDefaultPermissions } from './methods/chats/set-chat-default-permissions'
|
||||
|
@ -479,6 +481,62 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
): Promise<ChatMember> {
|
||||
return getChatMember.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Get a chunk of members of some chat.
|
||||
*
|
||||
* You can retrieve up to 200 members at once
|
||||
*
|
||||
* @param chatId Chat ID or username
|
||||
* @param params Additional parameters
|
||||
*/
|
||||
getChatMembers(
|
||||
chatId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Search query to filter members by their display names and usernames
|
||||
* Defaults to `''` (empty string)
|
||||
*
|
||||
* > **Note**: Only used for these values of `filter`:
|
||||
* > `all`, `banned`, `restricted`, `contacts`
|
||||
*/
|
||||
query?: string
|
||||
|
||||
/**
|
||||
* Sequential number of the first member to be returned.
|
||||
*/
|
||||
offset?: number
|
||||
|
||||
/**
|
||||
* Maximum number of members to be retrieved. Defaults to `200`
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Type of the query. Can be:
|
||||
* - `all`: get all members
|
||||
* - `banned`: get only banned members
|
||||
* - `restricted`: get only restricted members
|
||||
* - `bots`: get only bots
|
||||
* - `recent`: get recent members
|
||||
* - `admins`: get only administrators (and creator)
|
||||
* - `contacts`: get only contacts
|
||||
* - `mention`: get users that can be mentioned ([learn more](https://mt.tei.su/tl/class/channelParticipantsMentions))
|
||||
*
|
||||
* Only used for channels and supergroups. Defaults to `recent`
|
||||
*/
|
||||
type?:
|
||||
| 'all'
|
||||
| 'banned'
|
||||
| 'restricted'
|
||||
| 'bots'
|
||||
| 'recent'
|
||||
| 'admins'
|
||||
| 'contacts'
|
||||
| 'mention'
|
||||
}
|
||||
): Promise<ChatMember[]> {
|
||||
return getChatMembers.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Get preview information about a private chat.
|
||||
*
|
||||
|
@ -513,6 +571,30 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
getFullChat(chatId: InputPeerLike): Promise<Chat> {
|
||||
return getFullChat.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Iterate through chat members
|
||||
*
|
||||
* This method is a small wrapper over {@link getChatMembers},
|
||||
* which also handles duplicate entries (i.e. does not yield
|
||||
* the same member twice)
|
||||
*
|
||||
* @param chatId Chat ID or username
|
||||
* @param params Additional parameters
|
||||
*/
|
||||
iterChatMembers(
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getChatMembers']>[1] & {
|
||||
/**
|
||||
* Chunk size, which will be passed as `limit` parameter
|
||||
* to {@link getChatMembers}. Usually you shouldn't care about this.
|
||||
*
|
||||
* Defaults to `200`
|
||||
*/
|
||||
chunkSize?: number
|
||||
}
|
||||
): AsyncIterableIterator<ChatMember> {
|
||||
return iterChatMembers.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Join a channel or supergroup
|
||||
*
|
||||
|
|
147
packages/client/src/methods/chats/get-chat-members.ts
Normal file
147
packages/client/src/methods/chats/get-chat-members.ts
Normal file
|
@ -0,0 +1,147 @@
|
|||
import {
|
||||
ChatMember,
|
||||
InputPeerLike,
|
||||
MtCuteInvalidPeerTypeError,
|
||||
} from '../../types'
|
||||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
createUsersChatsIndex,
|
||||
normalizeToInputChannel,
|
||||
normalizeToInputPeer,
|
||||
} from '../../utils/peer-utils'
|
||||
import { assertTypeIs } from '../../utils/type-assertion'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
/**
|
||||
* Get a chunk of members of some chat.
|
||||
*
|
||||
* You can retrieve up to 200 members at once
|
||||
*
|
||||
* @param chatId Chat ID or username
|
||||
* @param params Additional parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function getChatMembers(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: {
|
||||
/**
|
||||
* Search query to filter members by their display names and usernames
|
||||
* Defaults to `''` (empty string)
|
||||
*
|
||||
* > **Note**: Only used for these values of `filter`:
|
||||
* > `all`, `banned`, `restricted`, `contacts`
|
||||
*/
|
||||
query?: string
|
||||
|
||||
/**
|
||||
* Sequential number of the first member to be returned.
|
||||
*/
|
||||
offset?: number
|
||||
|
||||
/**
|
||||
* Maximum number of members to be retrieved. Defaults to `200`
|
||||
*/
|
||||
limit?: number
|
||||
|
||||
/**
|
||||
* Type of the query. Can be:
|
||||
* - `all`: get all members
|
||||
* - `banned`: get only banned members
|
||||
* - `restricted`: get only restricted members
|
||||
* - `bots`: get only bots
|
||||
* - `recent`: get recent members
|
||||
* - `admins`: get only administrators (and creator)
|
||||
* - `contacts`: get only contacts
|
||||
* - `mention`: get users that can be mentioned ([learn more](https://mt.tei.su/tl/class/channelParticipantsMentions))
|
||||
*
|
||||
* Only used for channels and supergroups. Defaults to `recent`
|
||||
*/
|
||||
type?:
|
||||
| 'all'
|
||||
| 'banned'
|
||||
| 'restricted'
|
||||
| 'bots'
|
||||
| 'recent'
|
||||
| 'admins'
|
||||
| 'contacts'
|
||||
| 'mention'
|
||||
}
|
||||
): Promise<ChatMember[]> {
|
||||
if (!params) params = {}
|
||||
|
||||
const chat = normalizeToInputPeer(await this.resolvePeer(chatId))
|
||||
if (chat._ === 'inputPeerUser')
|
||||
throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
||||
|
||||
if (chat._ === 'inputPeerChat') {
|
||||
const res = await this.call({
|
||||
_: 'messages.getFullChat',
|
||||
chatId: chat.chatId,
|
||||
})
|
||||
|
||||
assertTypeIs(
|
||||
'getChatMember (@ messages.getFullChat)',
|
||||
res.fullChat,
|
||||
'chatFull'
|
||||
)
|
||||
|
||||
let members =
|
||||
res.fullChat.participants._ === 'chatParticipantsForbidden'
|
||||
? []
|
||||
: res.fullChat.participants.participants
|
||||
|
||||
if (params.offset) members = members.slice(params.offset)
|
||||
if (params.limit) members = members.slice(0, params.limit)
|
||||
|
||||
const { users } = createUsersChatsIndex(res)
|
||||
|
||||
return members.map((m) => new ChatMember(this, m, users))
|
||||
}
|
||||
|
||||
if (chat._ === 'inputPeerChannel') {
|
||||
const q = params.query?.toLowerCase() ?? ''
|
||||
const type = params.type ?? 'recent'
|
||||
|
||||
let filter: tl.TypeChannelParticipantsFilter
|
||||
if (type === 'all') {
|
||||
filter = { _: 'channelParticipantsSearch', q }
|
||||
} else if (type === 'banned') {
|
||||
filter = { _: 'channelParticipantsKicked', q }
|
||||
} else if (type === 'restricted') {
|
||||
filter = { _: 'channelParticipantsBanned', q }
|
||||
} else if (type === 'mention') {
|
||||
filter = { _: 'channelParticipantsMentions', q }
|
||||
} else if (type === 'bots') {
|
||||
filter = { _: 'channelParticipantsBots' }
|
||||
} else if (type === 'recent') {
|
||||
filter = { _: 'channelParticipantsRecent' }
|
||||
} else if (type === 'admins') {
|
||||
filter = { _: 'channelParticipantsAdmins' }
|
||||
} else if (type === 'contacts') {
|
||||
filter = { _: 'channelParticipantsContacts', q }
|
||||
} else {
|
||||
return type as never
|
||||
}
|
||||
|
||||
const res = await this.call({
|
||||
_: 'channels.getParticipants',
|
||||
channel: normalizeToInputChannel(chat)!,
|
||||
filter,
|
||||
offset: params.offset ?? 0,
|
||||
limit: params.limit ?? 200,
|
||||
hash: 0,
|
||||
})
|
||||
|
||||
assertTypeIs(
|
||||
'getChatMembers (@ channels.getParticipants)',
|
||||
res,
|
||||
'channels.channelParticipants'
|
||||
)
|
||||
|
||||
const { users } = createUsersChatsIndex(res)
|
||||
return res.participants.map(i => new ChatMember(this, i, users))
|
||||
}
|
||||
|
||||
throw new Error('should not happen')
|
||||
}
|
67
packages/client/src/methods/chats/iter-chat-members.ts
Normal file
67
packages/client/src/methods/chats/iter-chat-members.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { ChatMember, InputPeerLike } from '../../types'
|
||||
|
||||
/**
|
||||
* Iterate through chat members
|
||||
*
|
||||
* This method is a small wrapper over {@link getChatMembers},
|
||||
* which also handles duplicate entries (i.e. does not yield
|
||||
* the same member twice)
|
||||
*
|
||||
* @param chatId Chat ID or username
|
||||
* @param params Additional parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function* iterChatMembers(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
params?: Parameters<TelegramClient['getChatMembers']>[1] & {
|
||||
/**
|
||||
* Chunk size, which will be passed as `limit` parameter
|
||||
* to {@link getChatMembers}. Usually you shouldn't care about this.
|
||||
*
|
||||
* Defaults to `200`
|
||||
*/
|
||||
chunkSize?: number
|
||||
}
|
||||
): AsyncIterableIterator<ChatMember> {
|
||||
if (!params) params = {}
|
||||
|
||||
let current = 0
|
||||
let total = params.limit || Infinity
|
||||
const limit = Math.min(params.chunkSize ?? 200, total)
|
||||
let offset = params.offset ?? 0
|
||||
|
||||
const yielded = new Set()
|
||||
const chat = await this.resolvePeer(chatId)
|
||||
|
||||
for (;;) {
|
||||
const members = await this.getChatMembers(chat, {
|
||||
offset,
|
||||
limit,
|
||||
query: params.query,
|
||||
type: params.type
|
||||
})
|
||||
|
||||
if (!members.length) break
|
||||
|
||||
if (chat._ === 'inputPeerChat') {
|
||||
total = members.length
|
||||
}
|
||||
|
||||
offset += members.length
|
||||
|
||||
for (const m of members) {
|
||||
const uid = m.user.id
|
||||
|
||||
// handle duplicates
|
||||
if (yielded.has(uid)) continue
|
||||
yielded.add(uid)
|
||||
|
||||
yield m
|
||||
|
||||
current += 1
|
||||
if (current >= total) return
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue