mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 17:24:02 +00:00
feat(bot): add guided private prompts
This commit is contained in:
@@ -2,3 +2,4 @@ export { createDbAnonymousFeedbackRepository } from './anonymous-feedback-reposi
|
||||
export { createDbFinanceRepository } from './finance-repository'
|
||||
export { createDbHouseholdConfigurationRepository } from './household-config-repository'
|
||||
export { createDbReminderDispatchRepository } from './reminder-dispatch-repository'
|
||||
export { createDbTelegramPendingActionRepository } from './telegram-pending-action-repository'
|
||||
|
||||
144
packages/adapters-db/src/telegram-pending-action-repository.ts
Normal file
144
packages/adapters-db/src/telegram-pending-action-repository.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
|
||||
import { createDbClient, schema } from '@household/db'
|
||||
import type {
|
||||
TelegramPendingActionRecord,
|
||||
TelegramPendingActionRepository,
|
||||
TelegramPendingActionType
|
||||
} from '@household/ports'
|
||||
|
||||
function parsePendingActionType(raw: string): TelegramPendingActionType {
|
||||
if (raw === 'anonymous_feedback') {
|
||||
return raw
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected telegram pending action type: ${raw}`)
|
||||
}
|
||||
|
||||
function mapPendingAction(row: {
|
||||
telegramUserId: string
|
||||
telegramChatId: string
|
||||
action: string
|
||||
payload: unknown
|
||||
expiresAt: Date | null
|
||||
}): TelegramPendingActionRecord {
|
||||
return {
|
||||
telegramUserId: row.telegramUserId,
|
||||
telegramChatId: row.telegramChatId,
|
||||
action: parsePendingActionType(row.action),
|
||||
payload:
|
||||
row.payload && typeof row.payload === 'object' && !Array.isArray(row.payload)
|
||||
? (row.payload as Record<string, unknown>)
|
||||
: {},
|
||||
expiresAt: row.expiresAt
|
||||
}
|
||||
}
|
||||
|
||||
export function createDbTelegramPendingActionRepository(databaseUrl: string): {
|
||||
repository: TelegramPendingActionRepository
|
||||
close: () => Promise<void>
|
||||
} {
|
||||
const { db, queryClient } = createDbClient(databaseUrl, {
|
||||
max: 5,
|
||||
prepare: false
|
||||
})
|
||||
|
||||
const repository: TelegramPendingActionRepository = {
|
||||
async upsertPendingAction(input) {
|
||||
const rows = await db
|
||||
.insert(schema.telegramPendingActions)
|
||||
.values({
|
||||
telegramUserId: input.telegramUserId,
|
||||
telegramChatId: input.telegramChatId,
|
||||
action: input.action,
|
||||
payload: input.payload,
|
||||
expiresAt: input.expiresAt,
|
||||
updatedAt: new Date()
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: [
|
||||
schema.telegramPendingActions.telegramChatId,
|
||||
schema.telegramPendingActions.telegramUserId
|
||||
],
|
||||
set: {
|
||||
action: input.action,
|
||||
payload: input.payload,
|
||||
expiresAt: input.expiresAt,
|
||||
updatedAt: new Date()
|
||||
}
|
||||
})
|
||||
.returning({
|
||||
telegramUserId: schema.telegramPendingActions.telegramUserId,
|
||||
telegramChatId: schema.telegramPendingActions.telegramChatId,
|
||||
action: schema.telegramPendingActions.action,
|
||||
payload: schema.telegramPendingActions.payload,
|
||||
expiresAt: schema.telegramPendingActions.expiresAt
|
||||
})
|
||||
|
||||
const row = rows[0]
|
||||
if (!row) {
|
||||
throw new Error('Pending action upsert did not return a row')
|
||||
}
|
||||
|
||||
return mapPendingAction(row)
|
||||
},
|
||||
|
||||
async getPendingAction(telegramChatId, telegramUserId) {
|
||||
const now = new Date()
|
||||
const rows = await db
|
||||
.select({
|
||||
telegramUserId: schema.telegramPendingActions.telegramUserId,
|
||||
telegramChatId: schema.telegramPendingActions.telegramChatId,
|
||||
action: schema.telegramPendingActions.action,
|
||||
payload: schema.telegramPendingActions.payload,
|
||||
expiresAt: schema.telegramPendingActions.expiresAt
|
||||
})
|
||||
.from(schema.telegramPendingActions)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.telegramPendingActions.telegramChatId, telegramChatId),
|
||||
eq(schema.telegramPendingActions.telegramUserId, telegramUserId)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
const row = rows[0]
|
||||
if (!row) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (row.expiresAt && row.expiresAt.getTime() <= now.getTime()) {
|
||||
await db
|
||||
.delete(schema.telegramPendingActions)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.telegramPendingActions.telegramChatId, telegramChatId),
|
||||
eq(schema.telegramPendingActions.telegramUserId, telegramUserId)
|
||||
)
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return mapPendingAction(row)
|
||||
},
|
||||
|
||||
async clearPendingAction(telegramChatId, telegramUserId) {
|
||||
await db
|
||||
.delete(schema.telegramPendingActions)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.telegramPendingActions.telegramChatId, telegramChatId),
|
||||
eq(schema.telegramPendingActions.telegramUserId, telegramUserId)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
repository,
|
||||
close: async () => {
|
||||
await queryClient.end({ timeout: 5 })
|
||||
}
|
||||
}
|
||||
}
|
||||
13
packages/db/drizzle/0007_sudden_murmur.sql
Normal file
13
packages/db/drizzle/0007_sudden_murmur.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE "telegram_pending_actions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"telegram_user_id" text NOT NULL,
|
||||
"telegram_chat_id" text NOT NULL,
|
||||
"action" text NOT NULL,
|
||||
"payload" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"expires_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "telegram_pending_actions_chat_user_unique" ON "telegram_pending_actions" USING btree ("telegram_chat_id","telegram_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "telegram_pending_actions_user_action_idx" ON "telegram_pending_actions" USING btree ("telegram_user_id","action");
|
||||
@@ -50,6 +50,13 @@
|
||||
"when": 1773015092441,
|
||||
"tag": "0006_marvelous_nehzno",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "7",
|
||||
"when": 1773051000000,
|
||||
"tag": "0007_sudden_murmur",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -107,6 +107,32 @@ export const householdPendingMembers = pgTable(
|
||||
})
|
||||
)
|
||||
|
||||
export const telegramPendingActions = pgTable(
|
||||
'telegram_pending_actions',
|
||||
{
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
telegramUserId: text('telegram_user_id').notNull(),
|
||||
telegramChatId: text('telegram_chat_id').notNull(),
|
||||
action: text('action').notNull(),
|
||||
payload: jsonb('payload')
|
||||
.default(sql`'{}'::jsonb`)
|
||||
.notNull(),
|
||||
expiresAt: timestamp('expires_at', { withTimezone: true }),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
|
||||
},
|
||||
(table) => ({
|
||||
chatUserUnique: uniqueIndex('telegram_pending_actions_chat_user_unique').on(
|
||||
table.telegramChatId,
|
||||
table.telegramUserId
|
||||
),
|
||||
userActionIdx: index('telegram_pending_actions_user_action_idx').on(
|
||||
table.telegramUserId,
|
||||
table.action
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
export const members = pgTable(
|
||||
'members',
|
||||
{
|
||||
|
||||
@@ -35,3 +35,9 @@ export type {
|
||||
SettlementSnapshotLineRecord,
|
||||
SettlementSnapshotRecord
|
||||
} from './finance'
|
||||
export {
|
||||
TELEGRAM_PENDING_ACTION_TYPES,
|
||||
type TelegramPendingActionRecord,
|
||||
type TelegramPendingActionRepository,
|
||||
type TelegramPendingActionType
|
||||
} from './telegram-pending-actions'
|
||||
|
||||
20
packages/ports/src/telegram-pending-actions.ts
Normal file
20
packages/ports/src/telegram-pending-actions.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export const TELEGRAM_PENDING_ACTION_TYPES = ['anonymous_feedback'] as const
|
||||
|
||||
export type TelegramPendingActionType = (typeof TELEGRAM_PENDING_ACTION_TYPES)[number]
|
||||
|
||||
export interface TelegramPendingActionRecord {
|
||||
telegramUserId: string
|
||||
telegramChatId: string
|
||||
action: TelegramPendingActionType
|
||||
payload: Record<string, unknown>
|
||||
expiresAt: Date | null
|
||||
}
|
||||
|
||||
export interface TelegramPendingActionRepository {
|
||||
upsertPendingAction(input: TelegramPendingActionRecord): Promise<TelegramPendingActionRecord>
|
||||
getPendingAction(
|
||||
telegramChatId: string,
|
||||
telegramUserId: string
|
||||
): Promise<TelegramPendingActionRecord | null>
|
||||
clearPendingAction(telegramChatId: string, telegramUserId: string): Promise<void>
|
||||
}
|
||||
Reference in New Issue
Block a user