/** * Default per-account send rate, mirroring `BOT_MAX_SEND_PER_MINUTE` * in the bot env. The web bundle hardcodes this — operators who tune * the bot env are expected to redeploy web with the matching value. */ export const ASSUMED_RATE_PER_MINUTE = 40; const ETA_BUFFER = 1.15; /** * Pure ETA helper. Given a target count and a fire time, returns the * estimated duration in whole minutes and the projected finish * timestamp. * * Calculation: * ceil((targetCount / ratePerMinute) * 1.15) minutes * estimatedFinishAt = fireAt + that many minutes * * Floor of 1 minute when targetCount > 0 (anything non-zero takes at * least a minute to feel real). Returns 0 minutes when targetCount * is zero — the run is a no-op. */ export function estimateRunDuration(opts: { targetCount: number; ratePerMinute?: number; fireAt: Date; }): { durationMinutes: number; estimatedFinishAt: Date } { const rate = opts.ratePerMinute ?? ASSUMED_RATE_PER_MINUTE; if (rate <= 0) throw new Error("ratePerMinute must be > 0"); if (opts.targetCount <= 0) { return { durationMinutes: 0, estimatedFinishAt: new Date(opts.fireAt) }; } const raw = (opts.targetCount / rate) * ETA_BUFFER; const durationMinutes = Math.max(1, Math.ceil(raw)); const estimatedFinishAt = new Date( opts.fireAt.getTime() + durationMinutes * 60_000, ); return { durationMinutes, estimatedFinishAt }; }