fix(bot): harden webhook processing and purchase defaults

This commit is contained in:
2026-03-11 03:25:16 +04:00
parent dc09a07e21
commit ac5f11f8da
12 changed files with 674 additions and 148 deletions

View File

@@ -1,5 +1,6 @@
export { createDbAnonymousFeedbackRepository } from './anonymous-feedback-repository'
export { createDbFinanceRepository } from './finance-repository'
export { createDbHouseholdConfigurationRepository } from './household-config-repository'
export { createDbProcessedBotMessageRepository } from './processed-bot-message-repository'
export { createDbReminderDispatchRepository } from './reminder-dispatch-repository'
export { createDbTelegramPendingActionRepository } from './telegram-pending-action-repository'

View File

@@ -0,0 +1,58 @@
import { and, eq } from 'drizzle-orm'
import { createDbClient, schema } from '@household/db'
import type { ProcessedBotMessageRepository } from '@household/ports'
export function createDbProcessedBotMessageRepository(databaseUrl: string): {
repository: ProcessedBotMessageRepository
close: () => Promise<void>
} {
const { db, queryClient } = createDbClient(databaseUrl, {
max: 3,
prepare: false
})
const repository: ProcessedBotMessageRepository = {
async claimMessage(input) {
const rows = await db
.insert(schema.processedBotMessages)
.values({
householdId: input.householdId,
source: input.source,
sourceMessageKey: input.sourceMessageKey,
payloadHash: input.payloadHash ?? null
})
.onConflictDoNothing({
target: [
schema.processedBotMessages.householdId,
schema.processedBotMessages.source,
schema.processedBotMessages.sourceMessageKey
]
})
.returning({ id: schema.processedBotMessages.id })
return {
claimed: rows.length > 0
}
},
async releaseMessage(input) {
await db
.delete(schema.processedBotMessages)
.where(
and(
eq(schema.processedBotMessages.householdId, input.householdId),
eq(schema.processedBotMessages.source, input.source),
eq(schema.processedBotMessages.sourceMessageKey, input.sourceMessageKey)
)
)
}
}
return {
repository,
close: async () => {
await queryClient.end({ timeout: 5 })
}
}
}

View File

@@ -6,6 +6,12 @@ export {
type ReminderTarget,
type ReminderType
} from './reminders'
export type {
ClaimProcessedBotMessageInput,
ClaimProcessedBotMessageResult,
ProcessedBotMessageRepository,
ReleaseProcessedBotMessageInput
} from './processed-bot-messages'
export {
HOUSEHOLD_TOPIC_ROLES,
type HouseholdConfigurationRepository,

View File

@@ -0,0 +1,21 @@
export interface ClaimProcessedBotMessageInput {
householdId: string
source: string
sourceMessageKey: string
payloadHash?: string | null
}
export interface ClaimProcessedBotMessageResult {
claimed: boolean
}
export interface ReleaseProcessedBotMessageInput {
householdId: string
source: string
sourceMessageKey: string
}
export interface ProcessedBotMessageRepository {
claimMessage(input: ClaimProcessedBotMessageInput): Promise<ClaimProcessedBotMessageResult>
releaseMessage(input: ReleaseProcessedBotMessageInput): Promise<void>
}