diff --git a/src/routes/bot/editMessageReplyMarkup.ts b/src/routes/bot/editMessageReplyMarkup.ts new file mode 100644 index 0000000..897e2be --- /dev/null +++ b/src/routes/bot/editMessageReplyMarkup.ts @@ -0,0 +1,10 @@ +import { handle } from './utils'; +import type { Route } from '../route'; + +export const editMessageReplyMarkup: Route = (app, telegramServer) => { + handle(app, '/bot:token/editMessageReplyMarkup', (req, res, _next) => { + telegramServer.editMessageReplyMarkup(req.body); + const data = {ok: true, result: null}; + res.sendResult(data); + }); +}; diff --git a/src/routes/bot/index.ts b/src/routes/bot/index.ts index a1f6992..4a835b0 100644 --- a/src/routes/bot/index.ts +++ b/src/routes/bot/index.ts @@ -4,6 +4,7 @@ import { getMe } from './getMe'; import { answerCallbackQuery } from './answerCallbackQuery'; import { sendMessage } from './sendMessage'; import { editMessageText } from './editMessageText'; +import { editMessageReplyMarkup } from './editMessageReplyMarkup'; import { setWebhook } from './setWebhook'; import { deleteWebhook } from './deleteWebhook'; import { unknownMethod } from './unknownMethod'; @@ -14,6 +15,7 @@ export const botRoutes = [ answerCallbackQuery, getMe, editMessageText, + editMessageReplyMarkup, sendMessage, setWebhook, deleteWebhook, diff --git a/src/telegramServer.ts b/src/telegramServer.ts index 8701e38..dd6d548 100644 --- a/src/telegramServer.ts +++ b/src/telegramServer.ts @@ -41,6 +41,7 @@ interface StoredUpdate { type BotIncommingMessage = Params<'sendMessage', never>[0]; type BotEditTextIncommingMessage = Params<'editMessageText', never>[0]; +type BotEditReplyMarkupIncommingMessage = Params<'editMessageReplyMarkup', never>[0]; type RawIncommingMessage = { reply_markup?: string | object; @@ -217,9 +218,27 @@ export class TelegramServer extends EventEmitter { } } + editMessageReplyMarkup(rawMessage: BotEditReplyMarkupIncommingMessage) { + const message = TelegramServer.normalizeMessage(rawMessage); + // only InlineKeyboardMarkup is allowed in response + if (message.reply_markup && 'inline_keyboard' in message.reply_markup) { + const update = this.storage.botMessages.find( + (u) =>( + String(u.messageId) === String(message.message_id) + && String(u.message.chat_id) === String(message.chat_id) + ), + ); + if (update) { + update.message = {...update.message, ...message }; + this.emit('EditedMessageReplyMarkup'); + } + } + } + async waitBotEdits() { return new Promise((resolve) => { this.once('EditedMessageText', () => resolve()); + this.once('EditedMessageReplyMarkup', () => resolve()); }); } diff --git a/src/test/generic.test.ts b/src/test/generic.test.ts index a75e09d..ce486a6 100644 --- a/src/test/generic.test.ts +++ b/src/test/generic.test.ts @@ -326,6 +326,62 @@ describe('Telegram Server', () => { await server.stop(); }); + it('should handle reply markup editing', async () => { + const { server, client } = await getServerAndClient(token); + const bot = new TelegramBot(token, {baseApiUrl: server.config.apiURL, polling: true}); + bot.onText(/\/start/, (msg) => { + const chatId = msg.from!.id; + bot.sendMessage(chatId, 'Greetings', { + reply_markup: { + inline_keyboard: [ + [{ + text: 'Button', + callback_data: 'button', + }], + ], + }, + }); + }); + bot.on('callback_query', (query) => { + if (query.data === 'edit_markup') { + bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [{ + text: 'EditedButton', + callback_data: 'edited_button', + }], + ], + }, + {chat_id: query.message!.chat.id, message_id: query.message!.message_id}, + ); + } + }); + await client.sendCommand(client.makeCommand('/start')); + const startUpdates = await client.getUpdates(); + const botReply = startUpdates.result[0]; + assert.exists(botReply); + const replyMarkup = botReply.message.reply_markup; + if (!isInlineKeyboard(replyMarkup)) { + assert.fail('Wrong keyboard type in request'); + } + assert.equal(replyMarkup.inline_keyboard[0][0].text, 'Button'); + + const cb = client.makeCallbackQuery('edit_markup', {message: {message_id: botReply.messageId}}); + await client.sendCallback(cb); + await server.waitBotEdits(); + const allUpdates = await client.getUpdatesHistory(); + const targetUpdate = allUpdates.find((update) => update.messageId === botReply.messageId); + assert.isTrue(!!targetUpdate && 'message' in targetUpdate); + const replyMarkupEdited = (targetUpdate as StoredBotUpdate).message.reply_markup; + if (!isInlineKeyboard(replyMarkupEdited)) { + assert.fail('Wrong keyboard type in stored update'); + } + assert.equal(replyMarkupEdited.inline_keyboard[0][0].text, 'EditedButton'); + await bot.stopPolling(); + await server.stop(); + }); + it('should remove messages on storeTimeout', async () => { const { server, client } = await getServerAndClient(token, { storeTimeout: 1,