import { type DB } from '../db/client.js'; import type { BotSendLogRow } from '../db/schema/bot_send_log.js'; export type PacingField = 'id' | 'whatsapp' | 'category' | 'tenantId' | 'sentAt' | 'date'; export declare const DEFAULT_PACING_COLUMNS: Readonly>; export interface BotSendPacingOptions { db: D1Database | DB; tableName?: string; columnMap?: Partial>; omitColumns?: ReadonlyArray; allowedExtraColumns?: ReadonlyArray; /** * Tenant scope. When set, both `recordSent` writes the value and * `canSend` / `countSentToday` filter by it. `null` (default) = * single-tenant, no filtering. */ tenantId?: string | null; } export interface CanSendOptions { /** * When set, the daily-cap check is scoped to rows with this category. * When unset, the cap counts every row for the user today regardless * of category. (Use a category-scoped cap for "ads ≤3/day"; use a * total cap for "any send ≤4/day".) */ category?: string; /** * Minimum minutes since the last bot-initiated send to this user * (counting every category). `0` or `null` disables the gap check. * Default `60`. */ minGapMinutes?: number | null; /** * Maximum sends per day (scoped to `category` when set). `null` (default) * disables the cap check. */ dailyCap?: number | null; } export interface RecordSentOptions { extraColumns?: Record; } export type BotSendRow = BotSendLogRow; export declare class BotSendPacing { readonly db: DB; readonly tableName: string; readonly columns: Readonly>; readonly omitColumns: ReadonlySet; readonly allowedExtraColumns: ReadonlySet; readonly tenantId: string | null; constructor({ db, tableName, columnMap, omitColumns, allowedExtraColumns, tenantId, }: BotSendPacingOptions); /** * Atomic insert of one sent row. Call AFTER a successful WhatsApp * dispatch — failed sends should not consume the daily cap. */ recordSent(whatsapp: string, category: string, opts?: RecordSentOptions): Promise; /** * @returns true when both configured gates pass. Returns true (allow) * when no gates are configured — the primitive defaults to permissive * so callers explicitly opt into each protection. * * On D1 error, returns true (fail-open). Pacing is a UX-quality concern, * not a safety boundary; a query failure shouldn't block all cron sends. */ canSend(whatsapp: string, opts?: CanSendOptions): Promise; /** * Count of sends today (UTC date), optionally scoped to a category. * Used by the daily-cap check and exposed for dashboards. */ countSentToday(whatsapp: string, category?: string): Promise; /** Map of category → today's send count for a user. */ countByCategoryToday(whatsapp: string): Promise>; /** * Internal: how many sends to `whatsapp` in the last N minutes. * Drives the min-gap check; exposed so apps can use it directly for * custom predicates. */ countSentSince(whatsapp: string, minutes: number): Promise; } //# sourceMappingURL=bot_send_pacing.d.ts.map