feat(bot): add multi-household reminder delivery

This commit is contained in:
2026-03-09 16:50:57 +04:00
parent 12f33e7aea
commit 16f9981fee
27 changed files with 412 additions and 52 deletions

View File

@@ -1,4 +1,4 @@
import { and, eq } from 'drizzle-orm'
import { and, asc, eq } from 'drizzle-orm'
import { createDbClient, schema } from '@household/db'
import { instantToDate, normalizeSupportedLocale, nowInstant } from '@household/domain'
@@ -11,6 +11,7 @@ import {
type HouseholdTelegramChatRecord,
type HouseholdTopicBindingRecord,
type HouseholdTopicRole,
type ReminderTarget,
type RegisterTelegramHouseholdChatResult
} from '@household/ports'
@@ -125,6 +126,27 @@ function toHouseholdMemberRecord(row: {
}
}
function toReminderTarget(row: {
householdId: string
householdName: string
telegramChatId: string
reminderThreadId: string | null
defaultLocale: string
}): ReminderTarget {
const locale = normalizeSupportedLocale(row.defaultLocale)
if (!locale) {
throw new Error(`Unsupported household default locale: ${row.defaultLocale}`)
}
return {
householdId: row.householdId,
householdName: row.householdName,
telegramChatId: row.telegramChatId,
telegramThreadId: row.reminderThreadId,
locale
}
}
export function createDbHouseholdConfigurationRepository(databaseUrl: string): {
repository: HouseholdConfigurationRepository
close: () => Promise<void>
@@ -364,6 +386,35 @@ export function createDbHouseholdConfigurationRepository(databaseUrl: string): {
return rows.map(toHouseholdTopicBindingRecord)
},
async listReminderTargets() {
const rows = await db
.select({
householdId: schema.householdTelegramChats.householdId,
householdName: schema.households.name,
telegramChatId: schema.householdTelegramChats.telegramChatId,
reminderThreadId: schema.householdTopicBindings.telegramThreadId,
defaultLocale: schema.households.defaultLocale
})
.from(schema.householdTelegramChats)
.innerJoin(
schema.households,
eq(schema.householdTelegramChats.householdId, schema.households.id)
)
.leftJoin(
schema.householdTopicBindings,
and(
eq(
schema.householdTopicBindings.householdId,
schema.householdTelegramChats.householdId
),
eq(schema.householdTopicBindings.role, 'reminders')
)
)
.orderBy(asc(schema.householdTelegramChats.telegramChatId), asc(schema.households.name))
return rows.map(toReminderTarget)
},
async upsertHouseholdJoinToken(input) {
const rows = await db
.insert(schema.householdJoinTokens)

View File

@@ -1,3 +1,5 @@
import { and, eq } from 'drizzle-orm'
import { createDbClient, schema } from '@household/db'
import type { ReminderDispatchRepository } from '@household/ports'
@@ -34,6 +36,20 @@ export function createDbReminderDispatchRepository(databaseUrl: string): {
dedupeKey,
claimed: rows.length > 0
}
},
async releaseReminderDispatch(input) {
const dedupeKey = `${input.period}:${input.reminderType}`
await db
.delete(schema.processedBotMessages)
.where(
and(
eq(schema.processedBotMessages.householdId, input.householdId),
eq(schema.processedBotMessages.source, 'scheduler-reminder'),
eq(schema.processedBotMessages.sourceMessageKey, dedupeKey)
)
)
}
}