diff --git a/apps/bot/src/payment-topic-ingestion.test.ts b/apps/bot/src/payment-topic-ingestion.test.ts index d0e60c8..88f2dfb 100644 --- a/apps/bot/src/payment-topic-ingestion.test.ts +++ b/apps/bot/src/payment-topic-ingestion.test.ts @@ -338,11 +338,11 @@ describe('registerConfiguredPaymentTopicIngestion', () => { [ { text: 'Подтвердить оплату', - callback_data: expect.stringContaining('payment_topic:confirm:10002:') + callback_data: expect.stringMatching(/^payment_topic:confirm:[^:]+$/) }, { text: 'Отменить', - callback_data: expect.stringContaining('payment_topic:cancel:10002:') + callback_data: expect.stringMatching(/^payment_topic:cancel:[^:]+$/) } ] ] @@ -352,6 +352,13 @@ describe('registerConfiguredPaymentTopicIngestion', () => { expect(await promptRepository.getPendingAction('-10012345', '10002')).toMatchObject({ action: 'payment_topic_confirmation' }) + const proposalId = ( + (await promptRepository.getPendingAction('-10012345', '10002'))?.payload as { + proposalId?: string + } | null + )?.proposalId + expect(`payment_topic:confirm:${proposalId ?? ''}`.length).toBeLessThanOrEqual(64) + expect(`payment_topic:cancel:${proposalId ?? ''}`.length).toBeLessThanOrEqual(64) }) test('asks for clarification and resolves follow-up answers in the same payments topic', async () => { @@ -448,7 +455,7 @@ describe('registerConfiguredPaymentTopicIngestion', () => { calls.length = 0 await bot.handleUpdate( - paymentCallbackUpdate(`payment_topic:confirm:10002:${proposalId ?? 'missing'}`) as never + paymentCallbackUpdate(`payment_topic:confirm:${proposalId ?? 'missing'}`) as never ) expect(paymentConfirmationService.submitted).toEqual([ diff --git a/apps/bot/src/payment-topic-ingestion.ts b/apps/bot/src/payment-topic-ingestion.ts index 7b98de1..faa0a2c 100644 --- a/apps/bot/src/payment-topic-ingestion.ts +++ b/apps/bot/src/payment-topic-ingestion.ts @@ -216,11 +216,7 @@ function parsePaymentTopicConfirmationPayload( } } -function paymentProposalReplyMarkup( - locale: BotLocale, - senderTelegramUserId: string, - proposalId: string -) { +function paymentProposalReplyMarkup(locale: BotLocale, proposalId: string) { const t = getBotTranslations(locale).payments return { @@ -228,11 +224,11 @@ function paymentProposalReplyMarkup( [ { text: t.confirmButton, - callback_data: `${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}${senderTelegramUserId}:${proposalId}` + callback_data: `${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}${proposalId}` }, { text: t.cancelButton, - callback_data: `${PAYMENT_TOPIC_CANCEL_CALLBACK_PREFIX}${senderTelegramUserId}:${proposalId}` + callback_data: `${PAYMENT_TOPIC_CANCEL_CALLBACK_PREFIX}${proposalId}` } ] ] @@ -272,30 +268,21 @@ export function registerConfiguredPaymentTopicIngestion( } = {} ): void { bot.callbackQuery( - new RegExp(`^${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}(\\d+):([^:]+)$`), + new RegExp(`^${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}([^:]+)$`), async (ctx) => { if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') { return } const actorTelegramUserId = ctx.from?.id?.toString() - const ownerTelegramUserId = ctx.match[1] - const proposalId = ctx.match[2] - if (!actorTelegramUserId || !ownerTelegramUserId || !proposalId) { + const proposalId = ctx.match[1] + if (!actorTelegramUserId || !proposalId) { return } const locale = await resolveTopicLocale(ctx, householdConfigurationRepository) const t = getBotTranslations(locale).payments - if (actorTelegramUserId !== ownerTelegramUserId) { - await ctx.answerCallbackQuery({ - text: t.notYourProposal, - show_alert: true - }) - return - } - const pending = await promptRepository.getPendingAction( ctx.chat.id.toString(), actorTelegramUserId @@ -313,6 +300,14 @@ export function registerConfiguredPaymentTopicIngestion( return } + if (payload.senderTelegramUserId !== actorTelegramUserId) { + await ctx.answerCallbackQuery({ + text: t.notYourProposal, + show_alert: true + }) + return + } + const paymentService = paymentServiceForHousehold(payload.householdId) const result = await paymentService.submit({ ...payload, @@ -348,62 +343,58 @@ export function registerConfiguredPaymentTopicIngestion( } ) - bot.callbackQuery( - new RegExp(`^${PAYMENT_TOPIC_CANCEL_CALLBACK_PREFIX}(\\d+):([^:]+)$`), - async (ctx) => { - if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') { - return - } - - const actorTelegramUserId = ctx.from?.id?.toString() - const ownerTelegramUserId = ctx.match[1] - const proposalId = ctx.match[2] - if (!actorTelegramUserId || !ownerTelegramUserId || !proposalId) { - return - } - - const locale = await resolveTopicLocale(ctx, householdConfigurationRepository) - const t = getBotTranslations(locale).payments - - if (actorTelegramUserId !== ownerTelegramUserId) { - await ctx.answerCallbackQuery({ - text: t.notYourProposal, - show_alert: true - }) - return - } - - const pending = await promptRepository.getPendingAction( - ctx.chat.id.toString(), - actorTelegramUserId - ) - const payload = - pending?.action === PAYMENT_TOPIC_CONFIRMATION_ACTION - ? parsePaymentTopicConfirmationPayload(pending.payload) - : null - - if (!payload || payload.proposalId !== proposalId) { - await ctx.answerCallbackQuery({ - text: t.proposalUnavailable, - show_alert: true - }) - return - } - - await promptRepository.clearPendingAction(ctx.chat.id.toString(), actorTelegramUserId) - await ctx.answerCallbackQuery({ - text: t.cancelled - }) - - if (ctx.msg) { - await ctx.editMessageText(t.cancelled, { - reply_markup: { - inline_keyboard: [] - } - }) - } + bot.callbackQuery(new RegExp(`^${PAYMENT_TOPIC_CANCEL_CALLBACK_PREFIX}([^:]+)$`), async (ctx) => { + if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') { + return } - ) + + const actorTelegramUserId = ctx.from?.id?.toString() + const proposalId = ctx.match[1] + if (!actorTelegramUserId || !proposalId) { + return + } + + const locale = await resolveTopicLocale(ctx, householdConfigurationRepository) + const t = getBotTranslations(locale).payments + + const pending = await promptRepository.getPendingAction( + ctx.chat.id.toString(), + actorTelegramUserId + ) + const payload = + pending?.action === PAYMENT_TOPIC_CONFIRMATION_ACTION + ? parsePaymentTopicConfirmationPayload(pending.payload) + : null + + if (!payload || payload.proposalId !== proposalId) { + await ctx.answerCallbackQuery({ + text: t.proposalUnavailable, + show_alert: true + }) + return + } + + if (payload.senderTelegramUserId !== actorTelegramUserId) { + await ctx.answerCallbackQuery({ + text: t.notYourProposal, + show_alert: true + }) + return + } + + await promptRepository.clearPendingAction(ctx.chat.id.toString(), actorTelegramUserId) + await ctx.answerCallbackQuery({ + text: t.cancelled + }) + + if (ctx.msg) { + await ctx.editMessageText(t.cancelled, { + reply_markup: { + inline_keyboard: [] + } + }) + } + }) bot.on('message', async (ctx, next) => { const candidate = toCandidateFromContext(ctx) @@ -517,11 +508,7 @@ export function registerConfiguredPaymentTopicIngestion( await replyToPaymentMessage( ctx, t.proposal(proposal.payload.kind, amount.toMajorString(), amount.currency), - paymentProposalReplyMarkup( - locale, - record.senderTelegramUserId, - proposal.payload.proposalId - ) + paymentProposalReplyMarkup(locale, proposal.payload.proposalId) ) } } catch (error) {