diff --git a/apps/bot/src/household-setup.test.ts b/apps/bot/src/household-setup.test.ts index 80e6d20..1a1bbf7 100644 --- a/apps/bot/src/household-setup.test.ts +++ b/apps/bot/src/household-setup.test.ts @@ -810,11 +810,11 @@ describe('registerHouseholdSetupCommands', () => { ], [ { - text: 'Create purchases', + text: 'Create purchases topic', callback_data: 'setup_topic:create:purchase' }, { - text: 'Bind purchases', + text: 'Bind purchases topic', callback_data: 'setup_topic:bind:purchase' } ] @@ -1060,15 +1060,24 @@ describe('registerHouseholdSetupCommands', () => { expect(await promptRepository.getPendingAction('-100123456', '123456')).toMatchObject({ action: 'setup_topic_binding', payload: { - role: 'payments' + role: 'payments', + setupMessageId: 91 } }) calls.length = 0 await bot.handleUpdate(topicMessageUpdate('hello from payments', 444) as never) - expect(calls).toHaveLength(2) + expect(calls).toHaveLength(3) expect(calls[1]).toMatchObject({ + method: 'editMessageText', + payload: { + chat_id: -100123456, + message_id: 91, + text: expect.stringContaining('- payments: bound to thread 444') + } + }) + expect(calls[2]).toMatchObject({ method: 'sendMessage', payload: { chat_id: -100123456, diff --git a/apps/bot/src/household-setup.ts b/apps/bot/src/household-setup.ts index 4b81398..d337329 100644 --- a/apps/bot/src/household-setup.ts +++ b/apps/bot/src/household-setup.ts @@ -321,12 +321,20 @@ function isHouseholdTopicRole(value: string): value is HouseholdTopicRole { function parseSetupBindPayload(payload: Record): { role: HouseholdTopicRole + setupMessageId?: number } | null { - return typeof payload.role === 'string' && isHouseholdTopicRole(payload.role) - ? { - role: payload.role - } - : null + if (typeof payload.role !== 'string' || !isHouseholdTopicRole(payload.role)) { + return null + } + + return { + role: payload.role, + ...(typeof payload.setupMessageId === 'number' && Number.isInteger(payload.setupMessageId) + ? { + setupMessageId: payload.setupMessageId + } + : {}) + } } export function buildJoinMiniAppUrl( @@ -527,6 +535,22 @@ export function registerHouseholdSetupCommands(options: { return } + if (payload.setupMessageId && options.householdConfigurationRepository) { + const reply = await buildSetupReplyForHousehold({ + ctx, + locale, + household: result.household, + created: false + }) + + await ctx.api.editMessageText( + Number(telegramChatId), + payload.setupMessageId, + reply.text, + 'reply_markup' in reply ? { reply_markup: reply.reply_markup } : {} + ) + } + await ctx.reply( bindTopicSuccessMessage( locale, @@ -1027,7 +1051,12 @@ export function registerHouseholdSetupCommands(options: { telegramChatId, action: SETUP_BIND_TOPIC_ACTION, payload: { - role + role, + ...(ctx.msg + ? { + setupMessageId: ctx.msg.message_id + } + : {}) }, expiresAt: nowInstant().add({ milliseconds: SETUP_BIND_TOPIC_TTL_MS }) }) diff --git a/apps/bot/src/i18n/locales/en.ts b/apps/bot/src/i18n/locales/en.ts index 9974095..79fcff4 100644 --- a/apps/bot/src/i18n/locales/en.ts +++ b/apps/bot/src/i18n/locales/en.ts @@ -60,8 +60,8 @@ export const enBotTranslations: BotTranslationCatalog = { setupTopicsHeading: 'Topic setup:', setupTopicBound: (role, topic) => `- ${role}: bound to ${topic}`, setupTopicMissing: (role) => `- ${role}: not configured`, - setupTopicCreateButton: (role) => `Create ${role}`, - setupTopicBindButton: (role) => `Bind ${role}`, + setupTopicCreateButton: (role) => `Create ${role} topic`, + setupTopicBindButton: (role) => `Bind ${role} topic`, setupTopicCreateFailed: 'I could not create that topic. Check bot admin permissions and forum settings.', setupTopicCreateForbidden: @@ -260,8 +260,20 @@ export const enBotTranslations: BotTranslationCatalog = { payments: { topicMissing: 'Payments topic is not configured for this household yet. Ask an admin to run /bind_payments_topic.', + proposal: (kind, amount, currency) => + `I can record this ${kind === 'rent' ? 'rent' : 'utilities'} payment: ${amount} ${currency}. Confirm or cancel below.`, + clarification: + 'I could not confirm this payment yet. Please clarify whether this was rent or utilities and include the amount/currency if needed.', + unsupportedCurrency: + 'I can only record payments in the household settlement currency for this topic right now.', + noBalance: 'There is no payable balance for that payment type right now.', + confirmButton: 'Confirm payment', + cancelButton: 'Cancel', recorded: (kind, amount, currency) => `Recorded ${kind === 'rent' ? 'rent' : 'utilities'} payment: ${amount} ${currency}`, + cancelled: 'Payment proposal cancelled.', + proposalUnavailable: 'This payment proposal is no longer available.', + notYourProposal: 'Only the original sender can confirm or cancel this payment.', savedForReview: 'Saved this payment confirmation for review.', duplicate: 'This payment confirmation was already processed.' } diff --git a/apps/bot/src/i18n/locales/ru.ts b/apps/bot/src/i18n/locales/ru.ts index 51422c5..f97deb4 100644 --- a/apps/bot/src/i18n/locales/ru.ts +++ b/apps/bot/src/i18n/locales/ru.ts @@ -62,8 +62,8 @@ export const ruBotTranslations: BotTranslationCatalog = { setupTopicsHeading: 'Настройка топиков:', setupTopicBound: (role, topic) => `- ${role}: привязан к ${topic}`, setupTopicMissing: (role) => `- ${role}: не настроен`, - setupTopicCreateButton: (role) => `Создать ${role}`, - setupTopicBindButton: (role) => `Привязать ${role}`, + setupTopicCreateButton: (role) => `Создать топик для ${role}`, + setupTopicBindButton: (role) => `Привязать топик для ${role}`, setupTopicCreateFailed: 'Не удалось создать этот топик. Проверьте права бота и включённые форум-топики в группе.', setupTopicCreateForbidden: @@ -263,8 +263,20 @@ export const ruBotTranslations: BotTranslationCatalog = { payments: { topicMissing: 'Для этого дома ещё не настроен топик оплат. Попросите админа выполнить /bind_payments_topic.', + proposal: (kind, amount, currency) => + `Я могу записать эту оплату ${kind === 'rent' ? 'аренды' : 'коммуналки'}: ${amount} ${currency}. Подтвердите или отмените ниже.`, + clarification: + 'Пока не могу подтвердить эту оплату. Уточните, это аренда или коммуналка, и при необходимости напишите сумму и валюту.', + unsupportedCurrency: + 'Сейчас я могу записывать оплаты в этом топике только в валюте расчётов по дому.', + noBalance: 'Сейчас для этого типа оплаты нет суммы к подтверждению.', + confirmButton: 'Подтвердить оплату', + cancelButton: 'Отменить', recorded: (kind, amount, currency) => `Оплата ${kind === 'rent' ? 'аренды' : 'коммуналки'} сохранена: ${amount} ${currency}`, + cancelled: 'Предложение оплаты отменено.', + proposalUnavailable: 'Это предложение оплаты уже недоступно.', + notYourProposal: 'Подтвердить или отменить эту оплату может только отправитель сообщения.', savedForReview: 'Это подтверждение оплаты сохранено на проверку.', duplicate: 'Это подтверждение оплаты уже было обработано.' } diff --git a/apps/bot/src/i18n/types.ts b/apps/bot/src/i18n/types.ts index 269b413..6a062c2 100644 --- a/apps/bot/src/i18n/types.ts +++ b/apps/bot/src/i18n/types.ts @@ -249,6 +249,15 @@ export interface BotTranslationCatalog { payments: { topicMissing: string recorded: (kind: 'rent' | 'utilities', amount: string, currency: string) => string + proposal: (kind: 'rent' | 'utilities', amount: string, currency: string) => string + clarification: string + unsupportedCurrency: string + noBalance: string + confirmButton: string + cancelButton: string + cancelled: string + proposalUnavailable: string + notYourProposal: string savedForReview: string duplicate: string }