From 75dc961d659520a1ad835cb69753b77fa04219e1 Mon Sep 17 00:00:00 2001 From: teidesu Date: Sun, 11 Apr 2021 00:37:12 +0300 Subject: [PATCH] feat(client): createGroup, deleteGroup, deleteHistory and leaveChat methods --- packages/client/src/client.ts | 60 +++++++++++++++++++ .../client/src/methods/chats/create-group.ts | 46 ++++++++++++++ .../client/src/methods/chats/delete-group.ts | 30 ++++++++++ .../src/methods/chats/delete-history.ts | 33 ++++++++++ .../client/src/methods/chats/leave-chat.ts | 39 ++++++++++++ packages/client/src/types/errors.ts | 3 + 6 files changed, 211 insertions(+) create mode 100644 packages/client/src/methods/chats/create-group.ts create mode 100644 packages/client/src/methods/chats/delete-group.ts create mode 100644 packages/client/src/methods/chats/delete-history.ts create mode 100644 packages/client/src/methods/chats/leave-chat.ts diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index bf418d1c..3d054a26 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -16,12 +16,16 @@ import { start } from './methods/auth/start' import { addChatMembers } from './methods/chats/add-chat-members' import { archiveChats } from './methods/chats/archive-chats' import { createChannel } from './methods/chats/create-channel' +import { createGroup } from './methods/chats/create-group' import { createSupergroup } from './methods/chats/create-supergroup' import { deleteChannel } from './methods/chats/delete-channel' +import { deleteGroup } from './methods/chats/delete-group' +import { deleteHistory } from './methods/chats/delete-history' import { getChatPreview } from './methods/chats/get-chat-preview' import { getChat } from './methods/chats/get-chat' import { getFullChat } from './methods/chats/get-full-chat' import { joinChat } from './methods/chats/join-chat' +import { leaveChat } from './methods/chats/leave-chat' import { unarchiveChats } from './methods/chats/unarchive-chats' import { downloadAsBuffer } from './methods/files/download-buffer' import { downloadToFile } from './methods/files/download-file' @@ -366,6 +370,23 @@ export class TelegramClient extends BaseTelegramClient { createChannel(title: string, description?: string): Promise { return createChannel.apply(this, arguments) } + /** + * Create a legacy group chat + * + * If you want to create a supergroup, use {@link createSupergroup} + * instead. + * + * @param title Group title + * @param users + * User(s) to be invited in the group (ID(s), username(s) or phone number(s)). + * Due to Telegram limitations, you can't create a legacy group with yourself. + */ + createGroup( + title: string, + users: MaybeArray + ): Promise { + return createGroup.apply(this, arguments) + } /** * Create a new supergroup * @@ -393,6 +414,36 @@ export class TelegramClient extends BaseTelegramClient { deleteSupergroup(chatId: InputPeerLike): Promise { return deleteChannel.apply(this, arguments) } + /** + * Delete a legacy group chat for all members + * + * @param chatId Chat ID + */ + deleteGroup(chatId: InputPeerLike): Promise { + return deleteGroup.apply(this, arguments) + } + /** + * Delete communication history (for private chats + * and legacy groups) + * + * @param chat Chat or user ID, username, phone number, `"me"` or `"self"` + * @param mode + * (default: `'delete'`) + * Deletion mode. Can be: + * - `delete`: delete messages (only for yourself) + * - `clear`: delete messages (only for yourself) + * - `revoke`: delete messages for all users + * - I'm not sure what's the difference between `delete` and `clear`, + * but they are in fact different flags in TL object. + * @param maxId (default: `0`) Maximum ID of message to delete. Defaults to 0 (remove all messages) + */ + deleteHistory( + chat: InputPeerLike, + mode?: 'delete' | 'clear' | 'revoke', + maxId?: number + ): Promise { + return deleteHistory.apply(this, arguments) + } /** * Get preview information about a private chat. * @@ -437,6 +488,15 @@ export class TelegramClient extends BaseTelegramClient { joinChat(chatId: InputPeerLike): Promise { return joinChat.apply(this, arguments) } + /** + * Leave a group chat, supergroup or channel + * + * @param chatId Chat ID or username + * @param clear (default: `false`) Whether to clear history after leaving (only for legacy group chats) + */ + leaveChat(chatId: InputPeerLike, clear?: boolean): Promise { + return leaveChat.apply(this, arguments) + } /** * Unarchive one or more chats * diff --git a/packages/client/src/methods/chats/create-group.ts b/packages/client/src/methods/chats/create-group.ts new file mode 100644 index 00000000..b6796a97 --- /dev/null +++ b/packages/client/src/methods/chats/create-group.ts @@ -0,0 +1,46 @@ +import { TelegramClient } from '../../client' +import { MaybeArray } from '@mtcute/core' +import { Chat, InputPeerLike, MtCuteTypeAssertionError } from '../../types' +import { normalizeToInputUser } from '../../utils/peer-utils' +import { tl } from '@mtcute/tl' + +/** + * Create a legacy group chat + * + * If you want to create a supergroup, use {@link createSupergroup} + * instead. + * + * @param title Group title + * @param users + * User(s) to be invited in the group (ID(s), username(s) or phone number(s)). + * Due to Telegram limitations, you can't create a legacy group with yourself. + * @internal + */ +export async function createGroup( + this: TelegramClient, + title: string, + users: MaybeArray +): Promise { + if (!Array.isArray(users)) users = [users] + + const peers = (await Promise.all( + (users as InputPeerLike[]) + .map(u => this.resolvePeer(u).then(normalizeToInputUser)) + )).filter(Boolean) as tl.TypeInputUser[] + + const res = await this.call({ + _: 'messages.createChat', + title, + users: peers + }) + + if (!(res._ === 'updates' || res._ === 'updatesCombined')) { + throw new MtCuteTypeAssertionError( + 'createChannel (@ channels.createChannel)', + 'updates | updatesCombined', + res._ + ) + } + + return new Chat(this, res.chats[0]) +} diff --git a/packages/client/src/methods/chats/delete-group.ts b/packages/client/src/methods/chats/delete-group.ts new file mode 100644 index 00000000..8fdb8a3e --- /dev/null +++ b/packages/client/src/methods/chats/delete-group.ts @@ -0,0 +1,30 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike, MtCuteInvalidPeerTypeError } from '../../types' +import { normalizeToInputPeer } from '../../utils/peer-utils' + +/** + * Delete a legacy group chat for all members + * + * @param chatId Chat ID + * @internal + */ +export async function deleteGroup( + this: TelegramClient, + chatId: InputPeerLike +): Promise { + const chat = normalizeToInputPeer(await this.resolvePeer(chatId)) + if (chat._ !== 'inputPeerChat') + throw new MtCuteInvalidPeerTypeError(chatId, 'chat') + + await this.call({ + _: 'messages.deleteChatUser', + revokeHistory: true, + chatId: chat.chatId, + userId: { _: 'inputUserSelf' }, + }) + + await this.call({ + _: 'messages.deleteChat', + chatId: chat.chatId, + }) +} diff --git a/packages/client/src/methods/chats/delete-history.ts b/packages/client/src/methods/chats/delete-history.ts new file mode 100644 index 00000000..11510612 --- /dev/null +++ b/packages/client/src/methods/chats/delete-history.ts @@ -0,0 +1,33 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike } from '../../types' +import { normalizeToInputPeer } from '../../utils/peer-utils' + +/** + * Delete communication history (for private chats + * and legacy groups) + * + * @param chat Chat or user ID, username, phone number, `"me"` or `"self"` + * @param mode + * Deletion mode. Can be: + * - `delete`: delete messages (only for yourself) + * - `clear`: delete messages (only for yourself) + * - `revoke`: delete messages for all users + * - I'm not sure what's the difference between `delete` and `clear`, + * but they are in fact different flags in TL object. + * @param maxId Maximum ID of message to delete. Defaults to 0 (remove all messages) + * @internal + */ +export async function deleteHistory( + this: TelegramClient, + chat: InputPeerLike, + mode: 'delete' | 'clear' | 'revoke' = 'delete', + maxId = 0 +): Promise { + await this.call({ + _: 'messages.deleteHistory', + justClear: mode === 'clear', + revoke: mode === 'revoke', + peer: normalizeToInputPeer(await this.resolvePeer(chat)), + maxId + }) +} diff --git a/packages/client/src/methods/chats/leave-chat.ts b/packages/client/src/methods/chats/leave-chat.ts new file mode 100644 index 00000000..32b4cf78 --- /dev/null +++ b/packages/client/src/methods/chats/leave-chat.ts @@ -0,0 +1,39 @@ +import { InputPeerLike, MtCuteInvalidPeerTypeError } from '../../types' +import { TelegramClient } from '../../client' +import { + normalizeToInputChannel, + normalizeToInputPeer, +} from '../../utils/peer-utils' + +/** + * Leave a group chat, supergroup or channel + * + * @param chatId Chat ID or username + * @param clear Whether to clear history after leaving (only for legacy group chats) + * @internal + */ +export async function leaveChat( + this: TelegramClient, + chatId: InputPeerLike, + clear = false +): Promise { + const chat = await this.resolvePeer(chatId) + const input = normalizeToInputPeer(chat) + + if (input._ === 'inputPeerChannel') { + await this.call({ + _: 'channels.leaveChannel', + channel: normalizeToInputChannel(chat)!, + }) + } else if (input._ === 'inputPeerChat') { + await this.call({ + _: 'messages.deleteChatUser', + chatId: input.chatId, + userId: { _: 'inputUserSelf' }, + }) + + if (clear) { + await this.deleteHistory(input) + } + } else throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel') +} diff --git a/packages/client/src/types/errors.ts b/packages/client/src/types/errors.ts index 914580fe..28cac29e 100644 --- a/packages/client/src/types/errors.ts +++ b/packages/client/src/types/errors.ts @@ -26,6 +26,9 @@ export class MtCuteUnsupportedError extends MtCuteError {} /** * Server returned something of an unexpected type. + * + * This is usually a problem on library side. + * Feel free to open an issue about this! */ export class MtCuteTypeAssertionError extends MtCuteError { /**