feat(client): getChatMembers and iterChatMembers methods

This commit is contained in:
teidesu 2021-04-14 19:43:05 +03:00
parent ce3f694eb0
commit 14dc62e912
3 changed files with 296 additions and 0 deletions

View file

@ -23,9 +23,11 @@ import { deleteChatPhoto } from './methods/chats/delete-chat-photo'
import { deleteGroup } from './methods/chats/delete-group' import { deleteGroup } from './methods/chats/delete-group'
import { deleteHistory } from './methods/chats/delete-history' import { deleteHistory } from './methods/chats/delete-history'
import { getChatMember } from './methods/chats/get-chat-member' 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 { getChatPreview } from './methods/chats/get-chat-preview'
import { getChat } from './methods/chats/get-chat' import { getChat } from './methods/chats/get-chat'
import { getFullChat } from './methods/chats/get-full-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 { joinChat } from './methods/chats/join-chat'
import { leaveChat } from './methods/chats/leave-chat' import { leaveChat } from './methods/chats/leave-chat'
import { setChatDefaultPermissions } from './methods/chats/set-chat-default-permissions' import { setChatDefaultPermissions } from './methods/chats/set-chat-default-permissions'
@ -479,6 +481,62 @@ export class TelegramClient extends BaseTelegramClient {
): Promise<ChatMember> { ): Promise<ChatMember> {
return getChatMember.apply(this, arguments) 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. * Get preview information about a private chat.
* *
@ -513,6 +571,30 @@ export class TelegramClient extends BaseTelegramClient {
getFullChat(chatId: InputPeerLike): Promise<Chat> { getFullChat(chatId: InputPeerLike): Promise<Chat> {
return getFullChat.apply(this, arguments) 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 * Join a channel or supergroup
* *

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

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