fix(bot): shorten payment topic callback payloads

This commit is contained in:
2026-03-11 11:46:25 +04:00
parent cd3b364125
commit 9553ca342a
2 changed files with 76 additions and 82 deletions

View File

@@ -338,11 +338,11 @@ describe('registerConfiguredPaymentTopicIngestion', () => {
[ [
{ {
text: 'Подтвердить оплату', text: 'Подтвердить оплату',
callback_data: expect.stringContaining('payment_topic:confirm:10002:') callback_data: expect.stringMatching(/^payment_topic:confirm:[^:]+$/)
}, },
{ {
text: 'Отменить', 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({ expect(await promptRepository.getPendingAction('-10012345', '10002')).toMatchObject({
action: 'payment_topic_confirmation' 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 () => { test('asks for clarification and resolves follow-up answers in the same payments topic', async () => {
@@ -448,7 +455,7 @@ describe('registerConfiguredPaymentTopicIngestion', () => {
calls.length = 0 calls.length = 0
await bot.handleUpdate( await bot.handleUpdate(
paymentCallbackUpdate(`payment_topic:confirm:10002:${proposalId ?? 'missing'}`) as never paymentCallbackUpdate(`payment_topic:confirm:${proposalId ?? 'missing'}`) as never
) )
expect(paymentConfirmationService.submitted).toEqual([ expect(paymentConfirmationService.submitted).toEqual([

View File

@@ -216,11 +216,7 @@ function parsePaymentTopicConfirmationPayload(
} }
} }
function paymentProposalReplyMarkup( function paymentProposalReplyMarkup(locale: BotLocale, proposalId: string) {
locale: BotLocale,
senderTelegramUserId: string,
proposalId: string
) {
const t = getBotTranslations(locale).payments const t = getBotTranslations(locale).payments
return { return {
@@ -228,11 +224,11 @@ function paymentProposalReplyMarkup(
[ [
{ {
text: t.confirmButton, text: t.confirmButton,
callback_data: `${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}${senderTelegramUserId}:${proposalId}` callback_data: `${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}${proposalId}`
}, },
{ {
text: t.cancelButton, 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 { ): void {
bot.callbackQuery( bot.callbackQuery(
new RegExp(`^${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}(\\d+):([^:]+)$`), new RegExp(`^${PAYMENT_TOPIC_CONFIRM_CALLBACK_PREFIX}([^:]+)$`),
async (ctx) => { async (ctx) => {
if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') { if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') {
return return
} }
const actorTelegramUserId = ctx.from?.id?.toString() const actorTelegramUserId = ctx.from?.id?.toString()
const ownerTelegramUserId = ctx.match[1] const proposalId = ctx.match[1]
const proposalId = ctx.match[2] if (!actorTelegramUserId || !proposalId) {
if (!actorTelegramUserId || !ownerTelegramUserId || !proposalId) {
return return
} }
const locale = await resolveTopicLocale(ctx, householdConfigurationRepository) const locale = await resolveTopicLocale(ctx, householdConfigurationRepository)
const t = getBotTranslations(locale).payments const t = getBotTranslations(locale).payments
if (actorTelegramUserId !== ownerTelegramUserId) {
await ctx.answerCallbackQuery({
text: t.notYourProposal,
show_alert: true
})
return
}
const pending = await promptRepository.getPendingAction( const pending = await promptRepository.getPendingAction(
ctx.chat.id.toString(), ctx.chat.id.toString(),
actorTelegramUserId actorTelegramUserId
@@ -313,6 +300,14 @@ export function registerConfiguredPaymentTopicIngestion(
return return
} }
if (payload.senderTelegramUserId !== actorTelegramUserId) {
await ctx.answerCallbackQuery({
text: t.notYourProposal,
show_alert: true
})
return
}
const paymentService = paymentServiceForHousehold(payload.householdId) const paymentService = paymentServiceForHousehold(payload.householdId)
const result = await paymentService.submit({ const result = await paymentService.submit({
...payload, ...payload,
@@ -348,31 +343,20 @@ export function registerConfiguredPaymentTopicIngestion(
} }
) )
bot.callbackQuery( bot.callbackQuery(new RegExp(`^${PAYMENT_TOPIC_CANCEL_CALLBACK_PREFIX}([^:]+)$`), async (ctx) => {
new RegExp(`^${PAYMENT_TOPIC_CANCEL_CALLBACK_PREFIX}(\\d+):([^:]+)$`),
async (ctx) => {
if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') { if (ctx.chat?.type !== 'group' && ctx.chat?.type !== 'supergroup') {
return return
} }
const actorTelegramUserId = ctx.from?.id?.toString() const actorTelegramUserId = ctx.from?.id?.toString()
const ownerTelegramUserId = ctx.match[1] const proposalId = ctx.match[1]
const proposalId = ctx.match[2] if (!actorTelegramUserId || !proposalId) {
if (!actorTelegramUserId || !ownerTelegramUserId || !proposalId) {
return return
} }
const locale = await resolveTopicLocale(ctx, householdConfigurationRepository) const locale = await resolveTopicLocale(ctx, householdConfigurationRepository)
const t = getBotTranslations(locale).payments const t = getBotTranslations(locale).payments
if (actorTelegramUserId !== ownerTelegramUserId) {
await ctx.answerCallbackQuery({
text: t.notYourProposal,
show_alert: true
})
return
}
const pending = await promptRepository.getPendingAction( const pending = await promptRepository.getPendingAction(
ctx.chat.id.toString(), ctx.chat.id.toString(),
actorTelegramUserId actorTelegramUserId
@@ -390,6 +374,14 @@ export function registerConfiguredPaymentTopicIngestion(
return return
} }
if (payload.senderTelegramUserId !== actorTelegramUserId) {
await ctx.answerCallbackQuery({
text: t.notYourProposal,
show_alert: true
})
return
}
await promptRepository.clearPendingAction(ctx.chat.id.toString(), actorTelegramUserId) await promptRepository.clearPendingAction(ctx.chat.id.toString(), actorTelegramUserId)
await ctx.answerCallbackQuery({ await ctx.answerCallbackQuery({
text: t.cancelled text: t.cancelled
@@ -402,8 +394,7 @@ export function registerConfiguredPaymentTopicIngestion(
} }
}) })
} }
} })
)
bot.on('message', async (ctx, next) => { bot.on('message', async (ctx, next) => {
const candidate = toCandidateFromContext(ctx) const candidate = toCandidateFromContext(ctx)
@@ -517,11 +508,7 @@ export function registerConfiguredPaymentTopicIngestion(
await replyToPaymentMessage( await replyToPaymentMessage(
ctx, ctx,
t.proposal(proposal.payload.kind, amount.toMajorString(), amount.currency), t.proposal(proposal.payload.kind, amount.toMajorString(), amount.currency),
paymentProposalReplyMarkup( paymentProposalReplyMarkup(locale, proposal.payload.proposalId)
locale,
record.senderTelegramUserId,
proposal.payload.proposalId
)
) )
} }
} catch (error) { } catch (error) {