UNPKG

grammy-edit-or-reply

Version:

Exports utilities for grammy to edit a message or reply to a message based on the context

183 lines (141 loc) 6.46 kB
# Grammy Edit or Reply Provides a plugin for grammy to easily **edit** a message or **reply** to a message based on the context. This plugin aims to greatly simplify the way you write code by unifying handlers for **commands**, **callbacks**, and **inline mode**. It can seamlessly edit to and from: - 📝 Text messages - 🖼️ Photos - 🎞️ Animations - 📹 Videos - 📄 Documents - 🎵 Audio ```typescript function makeMediaGallery(selectedIdx?: number) { const keyboard: InlineKeyboardButton[][] = [ myMedias.map((x, i) => ({ text: prettyMediaTypes[x.type], callback_data: `media_${i}`, })), ]; const media = selectedIdx !== undefined ? myMedias.at(selectedIdx) : undefined; if (media) { return { text: `Media id: <code>${media.media}</code>`, parse_mode: 'HTML', media, keyboard, } satisfies MessageDataMedia; } return { text: 'Pick the media from the options', keyboard, } satisfies MessageData; } // generate inlineQueryResults based on the message data bot.inlineQuery(/.*/, async (ctx) => { const results = myMedias.map((x, i) => ({ ...makeInlineResult(makeMediaGallery(i)), id: `media-${i}`, title: `Send ${x.type} ${prettyMediaTypes[x.type]}`, })); await ctx.answerInlineQuery(results); }); // handle the command bot.command('start', async (ctx) => { await ctx.editOrReply(makeMediaGallery()); }); // and also the callback query (including in inline mode) bot.callbackQuery(/media_(\d+)/, async (ctx) => { const selectedIdx = Number(ctx.match[1]); await ctx.editOrReply(makeMediaGallery(selectedIdx)); }); ``` > [!TIP] > You can run this example yourself, the complete code is present in `examples/gallery.ts`, simply run `$ deno run --allow-net examples/gallery.ts` after building with `$ npm run build`. When editing from a text message to one containing media, the previous message will be deleted and the new one with the media will be sent. The same behavior also happens when replacing a message containing media with one without media. If an error occurs, no message is deleted. You can import the `editOrReply` function to use the same functionality when a `Context` object is unavailable. ## Installation and Setup ```bash npm install grammy-edit-or-reply # or yarn add grammy-edit-or-reply ``` You can then add the middleware as follows: ```typescript // extend your context type MyContext = Context & EditOrReplyFlavor; const bot = new Bot<MyContext>('12345:ABCDE'); // register the middleware bot.use(editOrReplyMiddleware()); ``` ## Unified Message Data Interface This library offers a simple unified interface for defining messages and inline messages, both for sending and editing, with and without media, see `MessageData` for all the options. ```typescript function getMenuMessage(ctx: MyContext) { return { text: 'This is a very complete example of the things that edit-or-reply can do for you!', media: { type: 'animation', media: 'file-id-here', }, has_spoiler: true, show_caption_above_media: true, // only inline keyboards are supported since other kinds // of keyboards can't be passed to message-editing endpoints keyboard: [[{ text: 'Hello World', url: 'https://example.com' }]], disable_notification: true, protect_content: true, reply_parameters: ctx.msgId ? { message_id: ctx.msgId } : undefined, entities: [ { offset: 10, length: 4, type: 'bold' }, { offset: 51, length: 13, type: 'text_link', url: 'https://github.com/rayz1065/grammy-edit-or-reply', }, ], // explicitly set parse_mode to undefined if you're using the parseMode plugin, // otherwise entities will not work! parse_mode: undefined, // using `satisfies` helps keep the type narrow } satisfies MessageData; } ``` This allows you to describe messages in a method-agnostic way, `editOrReply` will then be tasked to pick the right one out of the **11** available and correctly structure the data to call it. ## Return type The return type depends on the method used, most of them will return the message, except for inline methods that return `true`. A simple type check (or assertion if you're sure no inline method will be used) will allow you to access the message's data. ```typescript bot.command('start', async (ctx) => { const result = await ctx.editOrReply(getMenuMessage(ctx)); // notice how inline-mode methods can return True assert(typeof result !== 'boolean'); console.log(result.message_id); }); ``` ## Generating Inline Query Results This plugin can also help you generate inline query results out of your messages through the `makeInlineResult` function, with a few caveats: - `InputFile`s are not supported in `sendInlineQuery`, the type of media must therefore be narrowed down in case it's too broad using `messageDataHasNoInputFile`. - Permitting URLs as media would require adding a lot of extra metadata (like thumbnails and mime types). In order to keep message definition simple **only file ids are allowed**; - sending an audio by file id can [run into issues with metadata](https://github.com/telegraf/telegraf/issues/884#issuecomment-582278542), even though the file is already stored on Telegram's server. Audios are therefore sent as documents, changing the appearance of the inline result, but not that of the sent message. ```typescript const messageData = getMenuMessage(); assert(messageDataHasNoInputFile(messageData)); // if necessary const result = { ...makeInlineResult(getMenuMessage()), id: `0`, title: `Main menu`, }; ``` ## How it works Under the hood `editOrReply` uses a function called `getMessageInfo` to determine the type of the message in the current context, three types are possible: - `OldMessageInfoChat`, only the chat is available - `OldMessageInfoChatMessage`, the chat and a message to edit are available - `OldMessageInfoInline`, an inline message is available `OldMessageInfoChatMessage` and `OldMessageInfoInline` further specify whether the previous message has a media by using `getMessageMediaInfo` on the message, if the message is not available (inline mode or inaccessible message) a guess will be made. Call `ctx.getMessageInfo` directly if you want more control over the guess and pass the result to `ctx.editOrReply`. ## License Grammy Edit or Reply is available under the **MIT License**.