diff --git a/apps/bot/src/i18n/locales/en.ts b/apps/bot/src/i18n/locales/en.ts index 47a5775..cbd64dc 100644 --- a/apps/bot/src/i18n/locales/en.ts +++ b/apps/bot/src/i18n/locales/en.ts @@ -270,7 +270,8 @@ export const enBotTranslations: BotTranslationCatalog = { `I could not read that amount for ${categoryName}. Reply with a number in ${currency}, or send 0 / "skip".`, templateIntro: (currency) => `Fill in the utility amounts below in ${currency}, then send the completed message back in this topic.`, - templateInstruction: 'Use 0 or skip for any category you want to leave empty.', + templateInstruction: + 'For any category you do not want to add, leave it blank, remove the line entirely, or send 0 / "skip".', templateInvalid: 'I could not read any utility amounts from that template. Send the filled template back with at least one amount.', summaryTitle: (period) => `Utility charges for ${period}`, diff --git a/apps/bot/src/i18n/locales/ru.ts b/apps/bot/src/i18n/locales/ru.ts index 8a03e0b..8def867 100644 --- a/apps/bot/src/i18n/locales/ru.ts +++ b/apps/bot/src/i18n/locales/ru.ts @@ -275,7 +275,7 @@ export const ruBotTranslations: BotTranslationCatalog = { templateIntro: (currency) => `Заполните суммы по коммуналке ниже в ${currency}, затем отправьте заполненное сообщение обратно в этот топик.`, templateInstruction: - 'Для любой категории, которую не нужно добавлять, укажите 0 или слово «пропуск».', + 'Для любой категории, которую не нужно добавлять, оставьте поле пустым, удалите строку целиком или укажите 0 / «пропуск».', templateInvalid: 'Не удалось распознать ни одной суммы в этом шаблоне. Отправьте заполненный шаблон хотя бы с одной суммой.', summaryTitle: (period) => `Коммунальные начисления за ${period}`, diff --git a/apps/bot/src/reminder-topic-utilities.test.ts b/apps/bot/src/reminder-topic-utilities.test.ts index 0fa7c22..9416d86 100644 --- a/apps/bot/src/reminder-topic-utilities.test.ts +++ b/apps/bot/src/reminder-topic-utilities.test.ts @@ -375,7 +375,8 @@ describe('registerReminderTopicUtilities', () => { expect(calls[1]).toMatchObject({ method: 'sendMessage', payload: { - text: expect.stringContaining('Electricity:'), + text: expect.stringContaining('
Electricity: \nWater:'), + parse_mode: 'HTML', message_thread_id: 555 } }) @@ -391,6 +392,34 @@ describe('registerReminderTopicUtilities', () => { }) }) + test('treats blank or removed template lines as skipped categories', async () => { + const { bot, calls } = setupBot() + + await bot.handleUpdate(reminderCallbackUpdate(REMINDER_UTILITY_TEMPLATE_CALLBACK) as never) + + calls.length = 0 + await bot.handleUpdate(reminderMessageUpdate('Electricity: 22\nWater: ') as never) + + expect(calls[0]).toMatchObject({ + method: 'sendMessage', + payload: { + text: expect.stringContaining('- Electricity: 22.00 GEL') + } + }) + + calls.length = 0 + await bot.handleUpdate(reminderCallbackUpdate(REMINDER_UTILITY_TEMPLATE_CALLBACK) as never) + calls.length = 0 + await bot.handleUpdate(reminderMessageUpdate('Electricity: 22') as never) + + expect(calls[0]).toMatchObject({ + method: 'sendMessage', + payload: { + text: expect.stringContaining('- Electricity: 22.00 GEL') + } + }) + }) + test('treats expired pending reminder submissions as unavailable', async () => { const { bot, calls, promptRepository } = setupBot() diff --git a/apps/bot/src/reminder-topic-utilities.ts b/apps/bot/src/reminder-topic-utilities.ts index 061b520..3f5696f 100644 --- a/apps/bot/src/reminder-topic-utilities.ts +++ b/apps/bot/src/reminder-topic-utilities.ts @@ -182,20 +182,32 @@ function parseTemplateEntries( return entries.length > 0 ? entries : null } +function escapeHtml(raw: string): string { + return raw.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>') +} + function buildTemplateText( locale: BotLocale, currency: 'GEL' | 'USD', categories: readonly string[] -): string { +): { + text: string + parseMode: 'HTML' +} { const t = getBotTranslations(locale).reminders - return [ - t.templateIntro(currency), - '', - ...categories.map((category) => `${category}: `), - '', - t.templateInstruction - ].join('\n') + const templateLines = categories.map((category) => `${category}: `).join('\n') + + return { + text: [ + escapeHtml(t.templateIntro(currency)), + '', + `
${escapeHtml(templateLines)}`,
+ '',
+ escapeHtml(t.templateInstruction)
+ ].join('\n'),
+ parseMode: 'HTML'
+ }
}
function reminderUtilitySummaryText(
@@ -261,7 +273,10 @@ function buildReminderConfirmationPayload(input: {
async function replyInTopic(
ctx: Context,
text: string,
- replyMarkup?: InlineKeyboardMarkup
+ replyMarkup?: InlineKeyboardMarkup,
+ options?: {
+ parseMode?: 'HTML'
+ }
): Promise