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 { 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
|
||||||
*
|
*
|
||||||
|
|
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