diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 1df1cc1e..2c6394f5 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -19,6 +19,7 @@ import { answerCallbackQuery } from './methods/bots/answer-callback-query' import { answerInlineQuery } from './methods/bots/answer-inline-query' import { addChatMembers } from './methods/chats/add-chat-members' import { archiveChats } from './methods/chats/archive-chats' +import { banChatMember } from './methods/chats/ban-chat-member' import { createChannel } from './methods/chats/create-channel' import { createGroup } from './methods/chats/create-group' import { createSupergroup } from './methods/chats/create-supergroup' @@ -36,7 +37,9 @@ import { getFullChat } from './methods/chats/get-full-chat' import { getNearbyChats } from './methods/chats/get-nearby-chats' import { iterChatMembers } from './methods/chats/iter-chat-members' import { joinChat } from './methods/chats/join-chat' +import { kickChatMember } from './methods/chats/kick-chat-member' import { leaveChat } from './methods/chats/leave-chat' +import { restrictChatMember } from './methods/chats/restrict-chat-member' import { saveDraft } from './methods/chats/save-draft' import { setChatDefaultPermissions } from './methods/chats/set-chat-default-permissions' import { setChatDescription } from './methods/chats/set-chat-description' @@ -45,6 +48,7 @@ import { setChatTitle } from './methods/chats/set-chat-title' import { setChatUsername } from './methods/chats/set-chat-username' import { setSlowMode } from './methods/chats/set-slow-mode' import { unarchiveChats } from './methods/chats/unarchive-chats' +import { unbanChatMember } from './methods/chats/unban-chat-member' import { addContact } from './methods/contacts/add-contact' import { deleteContacts } from './methods/contacts/delete-contacts' import { getContacts } from './methods/contacts/get-contacts' @@ -596,6 +600,19 @@ export interface TelegramClient extends BaseTelegramClient { * @param chats Chat ID(s), username(s), phone number(s), `"me"` or `"self"` */ archiveChats(chats: MaybeArray): Promise + /** + * Ban a user from a legacy group, a supergroup or a channel. + * They will not be able to re-join the group on their own, + * manual administrator's action is required. + * + * @param chatId Chat ID + * @param userId User ID + * @returns Service message about removed user, if one was generated. + */ + banChatMember( + chatId: InputPeerLike, + userId: InputPeerLike + ): Promise /** * Create a new broadcast channel * @@ -695,7 +712,6 @@ export interface TelegramClient extends BaseTelegramClient { * * @param chatId Chat ID * @param params - */ getChatEventLog( chatId: InputPeerLike, @@ -881,6 +897,15 @@ export interface TelegramClient extends BaseTelegramClient { * or ID of the linked supergroup or channel. */ joinChat(chatId: InputPeerLike): Promise + /** + * Kick a user from a chat. + * + * This effectively bans a user and immediately unbans them. + * + * @param chatId Chat ID + * @param userId User ID + */ + kickChatMember(chatId: InputPeerLike, userId: InputPeerLike): Promise /** * Leave a group chat, supergroup or channel * @@ -888,6 +913,28 @@ export interface TelegramClient extends BaseTelegramClient { * @param clear (default: `false`) Whether to clear history after leaving (only for legacy group chats) */ leaveChat(chatId: InputPeerLike, clear?: boolean): Promise + /** + * Restrict a user in a supergroup. + * + * @param chatId Chat ID + * @param userId User ID + * @param restrictions + * Restrictions for the user. Note that unlike Bot API, this object contains + * the restrictions, and not the permissions, i.e. to + * passing `sendMessages=true` will disallow the user to send messages, + * and passing `{}` (empty object) will lift any restrictions + * @param until + * Date when the user will be unrestricted. + * When `number` is passed, UNIX time in ms is expected. + * If this value is less than 30 seconds or more than 366 days in + * the future, user will be restricted forever. Defaults to `0` (forever) + */ + restrictChatMember( + chatId: InputPeerLike, + userId: InputPeerLike, + restrictions: Omit, + until?: number | Date + ): Promise /** * Save or delete a draft message associated with some chat * @@ -904,24 +951,15 @@ export interface TelegramClient extends BaseTelegramClient { * You must be an administrator in the chat and have appropriate permissions. * * @param chatId Chat ID or username - * @param permissions New default chat permissions - * @example - * ```typescript - * // Completely restrict chat - * await tg.setDefaultChatPermissions('somechat', {}) - * - * // Chat members can only send text, media, stickers and GIFs - * await tg.setDefaultChatPermissions('somechat', { - * canSendMessages: true, - * canSendMedia: true, - * canSendStickers: true, - * canSendGifs: true, - * }) - * ``` + * @param restrictions + * Restrictions for the chat. Note that unlike Bot API, this object contains + * the restrictions, and not the permissions, i.e. to + * passing `sendMessages=true` will disallow the users to send messages, + * and passing `{}` (empty object) will lift any restrictions */ setChatDefaultPermissions( chatId: InputPeerLike, - permissions: InputChatPermissions + restrictions: Omit ): Promise /** * Change chat description @@ -991,6 +1029,35 @@ export interface TelegramClient extends BaseTelegramClient { * @param chats Chat ID(s), username(s), phone number(s), `"me"` or `"self"` */ unarchiveChats(chats: MaybeArray): Promise + + /** + * Unban a user from a supergroup or a channel, + * or remove any restrictions that they have. + * Unbanning does not add the user back to the chat, this + * just allows the user to join the chat again, if they want. + * + * This method acts as a no-op in case a legacy group is passed. + * + * @param chatId Chat ID + * @param userId User ID + */ + unbanChatMember(chatId: InputPeerLike, userId: InputPeerLike): Promise + + /** + * Unban a user from a supergroup or a channel, + * or remove any restrictions that they have. + * Unbanning does not add the user back to the chat, this + * just allows the user to join the chat again, if they want. + * + * This method acts as a no-op in case a legacy group is passed. + * + * @param chatId Chat ID + * @param userId User ID + */ + unrestrictChatMember( + chatId: InputPeerLike, + userId: InputPeerLike + ): Promise /** * Add an existing Telegram user as a contact * @@ -1109,7 +1176,6 @@ export interface TelegramClient extends BaseTelegramClient { * is not considered when sorting. * * @param params Fetch parameters - */ getDialogs(params?: { /** @@ -2605,7 +2671,9 @@ export interface TelegramClient extends BaseTelegramClient { getUsers(id: InputPeerLike): Promise /** * Get information about multiple users. - * You can retrieve up to 200 users at once + * You can retrieve up to 200 users at once. + * + * Note that order is not guaranteed. * * @param ids Users' identifiers. Can be ID, username, phone number, `"me"`, `"self"` or TL object */ @@ -2791,6 +2859,7 @@ export class TelegramClient extends BaseTelegramClient { answerInlineQuery = answerInlineQuery addChatMembers = addChatMembers archiveChats = archiveChats + banChatMember = banChatMember createChannel = createChannel createGroup = createGroup createSupergroup = createSupergroup @@ -2809,7 +2878,9 @@ export class TelegramClient extends BaseTelegramClient { getNearbyChats = getNearbyChats iterChatMembers = iterChatMembers joinChat = joinChat + kickChatMember = kickChatMember leaveChat = leaveChat + restrictChatMember = restrictChatMember saveDraft = saveDraft setChatDefaultPermissions = setChatDefaultPermissions setChatDescription = setChatDescription @@ -2818,6 +2889,8 @@ export class TelegramClient extends BaseTelegramClient { setChatUsername = setChatUsername setSlowMode = setSlowMode unarchiveChats = unarchiveChats + unbanChatMember = unbanChatMember + unrestrictChatMember = unbanChatMember addContact = addContact deleteContacts = deleteContacts getContacts = getContacts diff --git a/packages/client/src/methods/chats/ban-chat-member.ts b/packages/client/src/methods/chats/ban-chat-member.ts new file mode 100644 index 00000000..d1d5f70e --- /dev/null +++ b/packages/client/src/methods/chats/ban-chat-member.ts @@ -0,0 +1,71 @@ +import { TelegramClient } from '../../client' +import { + InputPeerLike, + Message, + MtCuteInvalidPeerTypeError, + MtCuteTypeAssertionError, +} from '../../types' +import { + isInputPeerChannel, + isInputPeerChat, + normalizeToInputChannel, + normalizeToInputPeer, + normalizeToInputUser, +} from '../../utils/peer-utils' + +/** + * Ban a user from a legacy group, a supergroup or a channel. + * They will not be able to re-join the group on their own, + * manual administrator's action is required. + * + * @param chatId Chat ID + * @param userId User ID + * @returns Service message about removed user, if one was generated. + * @internal + */ +export async function banChatMember( + this: TelegramClient, + chatId: InputPeerLike, + userId: InputPeerLike +): Promise { + const chat = normalizeToInputPeer(await this.resolvePeer(chatId)) + const user = normalizeToInputPeer(await this.resolvePeer(userId)) + + let res + if (isInputPeerChannel(chat)) { + res = await this.call({ + _: 'channels.editBanned', + channel: normalizeToInputChannel(chat), + participant: user, + bannedRights: { + _: 'chatBannedRights', + // bans can't be temporary. + untilDate: 0, + viewMessages: true, + }, + }) + } else if (isInputPeerChat(chat)) { + const normUser = normalizeToInputUser(user) + if (!normUser) throw new MtCuteInvalidPeerTypeError(userId, 'user') + + res = await this.call({ + _: 'messages.deleteChatUser', + chatId: chat.chatId, + userId: normUser, + }) + } else throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel') + + try { + return this._findMessageInUpdate(res) + } catch (e) { + if ( + e instanceof MtCuteTypeAssertionError && + e.context === '_findInUpdate (@ .updates[*])' + ) { + // no service message + return null + } + + throw e + } +} diff --git a/packages/client/src/methods/chats/kick-chat-member.ts b/packages/client/src/methods/chats/kick-chat-member.ts new file mode 100644 index 00000000..065b7558 --- /dev/null +++ b/packages/client/src/methods/chats/kick-chat-member.ts @@ -0,0 +1,28 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike } from '../../types' +import { isInputPeerChannel, normalizeToInputPeer } from '../../utils/peer-utils' + +/** + * Kick a user from a chat. + * + * This effectively bans a user and immediately unbans them. + * + * @param chatId Chat ID + * @param userId User ID + * @internal + */ +export async function kickChatMember( + this: TelegramClient, + chatId: InputPeerLike, + userId: InputPeerLike +): Promise { + const chat = normalizeToInputPeer(await this.resolvePeer(chatId)) + const user = normalizeToInputPeer(await this.resolvePeer(userId)) + + await this.banChatMember(chat, user) + + // not needed in case this is a legacy group + if (isInputPeerChannel(chat)) { + await this.unbanChatMember(chat, user) + } +} diff --git a/packages/client/src/methods/chats/restrict-chat-member.ts b/packages/client/src/methods/chats/restrict-chat-member.ts new file mode 100644 index 00000000..7434837d --- /dev/null +++ b/packages/client/src/methods/chats/restrict-chat-member.ts @@ -0,0 +1,52 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike, MtCuteInvalidPeerTypeError } from '../../types' +import { + isInputPeerChannel, + normalizeToInputChannel, + normalizeToInputPeer, +} from '../../utils/peer-utils' +import { tl } from '@mtcute/tl' +import { normalizeDate } from '../../utils/misc-utils' + +/** + * Restrict a user in a supergroup. + * + * @param chatId Chat ID + * @param userId User ID + * @param restrictions + * Restrictions for the user. Note that unlike Bot API, this object contains + * the restrictions, and not the permissions, i.e. to + * passing `sendMessages=true` will disallow the user to send messages, + * and passing `{}` (empty object) will lift any restrictions + * @param until + * Date when the user will be unrestricted. + * When `number` is passed, UNIX time in ms is expected. + * If this value is less than 30 seconds or more than 366 days in + * the future, user will be restricted forever. Defaults to `0` (forever) + * @internal + */ +export async function restrictChatMember( + this: TelegramClient, + chatId: InputPeerLike, + userId: InputPeerLike, + restrictions: Omit, + until?: number | Date +): Promise { + const chat = normalizeToInputPeer(await this.resolvePeer(chatId)) + if (!isInputPeerChannel(chat)) + throw new MtCuteInvalidPeerTypeError(chatId, 'channel') + + const user = normalizeToInputPeer(await this.resolvePeer(userId)) + + const res = await this.call({ + _: 'channels.editBanned', + channel: normalizeToInputChannel(chat), + participant: user, + bannedRights: { + _: 'chatBannedRights', + untilDate: normalizeDate(until) ?? 0, + ...restrictions, + }, + }) + this._handleUpdate(res) +} diff --git a/packages/client/src/methods/chats/set-chat-default-permissions.ts b/packages/client/src/methods/chats/set-chat-default-permissions.ts index 842e2a82..bd87e482 100644 --- a/packages/client/src/methods/chats/set-chat-default-permissions.ts +++ b/packages/client/src/methods/chats/set-chat-default-permissions.ts @@ -1,6 +1,7 @@ import { TelegramClient } from '../../client' import { Chat, InputChatPermissions, InputPeerLike, MtCuteTypeAssertionError } from '../../types' import { normalizeToInputPeer } from '../../utils/peer-utils' +import { tl } from '@mtcute/tl' /** * Change default chat permissions for all members. @@ -8,26 +9,17 @@ import { normalizeToInputPeer } from '../../utils/peer-utils' * You must be an administrator in the chat and have appropriate permissions. * * @param chatId Chat ID or username - * @param permissions New default chat permissions - * @example - * ```typescript - * // Completely restrict chat - * await tg.setDefaultChatPermissions('somechat', {}) - * - * // Chat members can only send text, media, stickers and GIFs - * await tg.setDefaultChatPermissions('somechat', { - * canSendMessages: true, - * canSendMedia: true, - * canSendStickers: true, - * canSendGifs: true, - * }) - * ``` + * @param restrictions + * Restrictions for the chat. Note that unlike Bot API, this object contains + * the restrictions, and not the permissions, i.e. to + * passing `sendMessages=true` will disallow the users to send messages, + * and passing `{}` (empty object) will lift any restrictions * @internal */ export async function setChatDefaultPermissions( this: TelegramClient, chatId: InputPeerLike, - permissions: InputChatPermissions + restrictions: Omit ): Promise { const peer = normalizeToInputPeer(await this.resolvePeer(chatId)) @@ -37,17 +29,7 @@ export async function setChatDefaultPermissions( bannedRights: { _: 'chatBannedRights', untilDate: 0, - sendMessages: !permissions.canSendMessages, - sendMedia: !permissions.canSendMedia, - sendStickers: !permissions.canSendStickers, - sendGifs: !permissions.canSendGifs, - sendGames: !permissions.canSendGames, - sendInline: !permissions.canUseInline, - embedLinks: !permissions.canAddWebPreviews, - sendPolls: !permissions.canSendPolls, - changeInfo: !permissions.canChangeInfo, - inviteUsers: !permissions.canInviteUsers, - pinMessages: !permissions.canPinMessages, + ...restrictions } }) diff --git a/packages/client/src/methods/chats/unban-chat-member.ts b/packages/client/src/methods/chats/unban-chat-member.ts new file mode 100644 index 00000000..10e34a48 --- /dev/null +++ b/packages/client/src/methods/chats/unban-chat-member.ts @@ -0,0 +1,46 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike, MtCuteInvalidPeerTypeError } from '../../types' +import { + isInputPeerChannel, + isInputPeerChat, + normalizeToInputChannel, + normalizeToInputPeer, +} from '../../utils/peer-utils' + +// @alias=unrestrictChatMember +/** + * Unban a user from a supergroup or a channel, + * or remove any restrictions that they have. + * Unbanning does not add the user back to the chat, this + * just allows the user to join the chat again, if they want. + * + * This method acts as a no-op in case a legacy group is passed. + * + * @param chatId Chat ID + * @param userId User ID + * @internal + */ +export async function unbanChatMember( + this: TelegramClient, + chatId: InputPeerLike, + userId: InputPeerLike +): Promise { + const chat = normalizeToInputPeer(await this.resolvePeer(chatId)) + const user = normalizeToInputPeer(await this.resolvePeer(userId)) + + if (isInputPeerChannel(chat)) { + const res = await this.call({ + _: 'channels.editBanned', + channel: normalizeToInputChannel(chat), + participant: user, + bannedRights: { + _: 'chatBannedRights', + untilDate: 0, + }, + }) + + this._handleUpdate(res) + } else if (isInputPeerChat(chat)) { + // no-op // + } else throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel') +}