import type { UsageCounter } from '../usage/usage_counter.js'; import type { QuietHours } from '../util/quiet_hours.js'; export interface RateCappedDispatcherOptions { counter: UsageCounter; /** * Feature key — partitions the `feature_usage` table. Pick something * unique per channel ('reactive_ad', 'tip', 'upsell_nudge'). */ feature: string; /** Maximum impressions per calendar day. */ dailyMax: number; /** * Minimum gap (seconds) between two dispatches to the same user. Set 0 * to disable. Defaults to 0. */ minGapSeconds?: number; /** Optional quiet-hours guard. Skips dispatch entirely when inside. */ quietHours?: QuietHours | null; } export type DispatchReason = 'sent' | 'quiet_hours' | 'min_gap' | 'daily_cap' | 'send_failed' | 'no_whatsapp'; export interface DispatchResult { sent: boolean; reason: DispatchReason; } export type SendFn = () => Promise | boolean | void; export declare class RateCappedDispatcher { readonly counter: UsageCounter; readonly feature: string; readonly dailyMax: number; readonly minGapSeconds: number; readonly quietHours: QuietHours | null; constructor({ counter, feature, dailyMax, minGapSeconds, quietHours }: RateCappedDispatcherOptions); /** * Check the gap + cap, run `send`, record an impression on success. * Never throws — failures surface in the `reason` field. * * `send` returns truthy to mean "delivered". A void return is treated as * delivered. A thrown error is caught and treated as `send_failed` (no * impression recorded, no propagation to the caller). */ tryDispatch(whatsapp: string, send: SendFn, key?: string | null): Promise; /** ms-since-epoch of the last dispatch for this user, or null. */ lastDispatchedAt(whatsapp: string): Promise; } //# sourceMappingURL=rate_capped_dispatcher.d.ts.map