diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 0eb480e2..817de0c2 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -19,6 +19,7 @@ import { downloadAsIterable } from './methods/files/download-iterable' import { downloadAsStream } from './methods/files/download-stream' import { uploadFile } from './methods/files/upload-file' import { deleteMessages } from './methods/messages/delete-messages' +import { editMessage } from './methods/messages/edit-message' import { _findMessageInUpdate } from './methods/messages/find-in-update' import { getHistory } from './methods/messages/get-history' import { getMessages } from './methods/messages/get-messages' @@ -435,8 +436,57 @@ export class TelegramClient extends BaseTelegramClient { ): Promise { return deleteMessages.apply(this, arguments) } + /** + * Edit message text and/or reply markup. + * + * @param chatId ID of the chat, its username, phone or `"me"` or `"self"` + * @param message Message or its ID + * @param params + */ + editMessage( + chatId: InputPeerLike, + message: number | Message, + params: { + /** + * New message text + */ + text?: string - protected _findMessageInUpdate(res: tl.TypeUpdates): Message { + /** + * Parse mode to use to parse entities before sending + * the message. Defaults to current default parse mode (if any). + * + * Passing `null` will explicitly disable formatting. + */ + parseMode?: string | null + + /** + * List of formatting entities to use instead of parsing via a + * parse mode. + * + * **Note:** Passing this makes the method ignore {@link parseMode} + */ + entities?: tl.TypeMessageEntity[] + + /** + * Whether to disable links preview in this message + */ + disableWebPreview?: boolean + + /** + * For bots: inline or reply markup or an instruction + * to hide a reply keyboard or to force a reply. + */ + replyMarkup?: ReplyMarkup + } + ): Promise { + return editMessage.apply(this, arguments) + } + + protected _findMessageInUpdate( + res: tl.TypeUpdates, + isEdit = false + ): Message { return _findMessageInUpdate.apply(this, arguments) } /** diff --git a/packages/client/src/methods/messages/edit-message.ts b/packages/client/src/methods/messages/edit-message.ts new file mode 100644 index 00000000..8068fdf7 --- /dev/null +++ b/packages/client/src/methods/messages/edit-message.ts @@ -0,0 +1,69 @@ +import { TelegramClient } from '../../client' +import { BotKeyboard, InputPeerLike, Message, ReplyMarkup } from '../../types' +import { tl } from '@mtcute/tl' +import { normalizeToInputPeer } from '../../utils/peer-utils' + +/** + * Edit message text and/or reply markup. + * + * @param chatId ID of the chat, its username, phone or `"me"` or `"self"` + * @param message Message or its ID + * @param params + * @internal + */ +export async function editMessage( + this: TelegramClient, + chatId: InputPeerLike, + message: number | Message, + params: { + /** + * New message text + */ + text?: string + + /** + * Parse mode to use to parse entities before sending + * the message. Defaults to current default parse mode (if any). + * + * Passing `null` will explicitly disable formatting. + */ + parseMode?: string | null + + /** + * List of formatting entities to use instead of parsing via a + * parse mode. + * + * **Note:** Passing this makes the method ignore {@link parseMode} + */ + entities?: tl.TypeMessageEntity[] + + /** + * Whether to disable links preview in this message + */ + disableWebPreview?: boolean + + /** + * For bots: inline or reply markup or an instruction + * to hide a reply keyboard or to force a reply. + */ + replyMarkup?: ReplyMarkup + } +): Promise { + const [content, entities] = await this._parseEntities( + params.text, + params.parseMode, + params.entities + ) + + const res = await this.call({ + _: 'messages.editMessage', + id: typeof message === 'number' ? message : message.id, + peer: normalizeToInputPeer(await this.resolvePeer(chatId)), + noWebpage: params.disableWebPreview, + replyMarkup: BotKeyboard._convertToTl(params.replyMarkup), + message: content, + entities, + }) + + return this._findMessageInUpdate(res, true) as any +} diff --git a/packages/client/src/methods/messages/find-in-update.ts b/packages/client/src/methods/messages/find-in-update.ts index 389d4f8f..426c4daf 100644 --- a/packages/client/src/methods/messages/find-in-update.ts +++ b/packages/client/src/methods/messages/find-in-update.ts @@ -6,7 +6,8 @@ import { createUsersChatsIndex } from '../../utils/peer-utils' /** @internal */ export function _findMessageInUpdate( this: TelegramClient, - res: tl.TypeUpdates + res: tl.TypeUpdates, + isEdit = false ): Message { if (!(res._ === 'updates' || res._ === 'updatesCombined')) throw new MtCuteTypeAssertionError( @@ -17,9 +18,14 @@ export function _findMessageInUpdate( for (const u of res.updates) { if ( - u._ === 'updateNewMessage' || - u._ === 'updateNewChannelMessage' || - u._ === 'updateNewScheduledMessage' + isEdit && ( + u._ === 'updateEditMessage' || + u._ === 'updateEditChannelMessage' + ) || !isEdit && ( + u._ === 'updateNewMessage' || + u._ === 'updateNewChannelMessage' || + u._ === 'updateNewScheduledMessage' + ) ) { const { users, chats } = createUsersChatsIndex(res) diff --git a/packages/client/src/types/messages/message.ts b/packages/client/src/types/messages/message.ts index 0fd90ef2..90c1389f 100644 --- a/packages/client/src/types/messages/message.ts +++ b/packages/client/src/types/messages/message.ts @@ -802,6 +802,35 @@ export class Message { delete(revoke = false): Promise { return this.client.deleteMessages(this.chat.inputPeer, this.id, revoke) } + + /** + * Edit this message's text and/or reply markup + * + * @see TelegramClient.editMessage + */ + edit(params: Parameters[2]): Promise { + return this.client.editMessage(this.chat.inputPeer, this.id, params) + } + + /** + * Edit message text and optionally reply markup. + * + * Convenience method that just wraps {@link edit}, + * passing positional `text` as object field. + * + * @param text New message text + * @param params Additional parameters + * @see TelegramClient.editMessage + */ + editText( + text: string, + params?: Omit[2], 'text'> + ): Promise { + return this.edit({ + text, + ...(params || {}) + }) + } } makeInspectable(Message, ['empty', 'isScheduled'], ['link'])