Rework alert cooldown model

Content alerts (unfulfilled, pending, tautulli-no-matches) now have
zero cooldown on manual close — they reopen immediately on the next
refresh if the condition still exists. Closing is an acknowledgment
of the current state, not a suppression of future alerts.

User-behavior alerts (ghost, watchrate) keep a cooldown (7 days) so
a single manual close isn't immediately undone by the next refresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 11:38:05 -04:00
parent 8fe61cdeb8
commit 6fa246d3c4
2 changed files with 28 additions and 23 deletions

View File

@@ -16,19 +16,19 @@ import {
const DATA_DIR = join(process.cwd(), "data");
const DB_PATH = join(DATA_DIR, "alerts.json");
// Cooldown days applied on MANUAL close — suppresses re-opening for this long.
// Auto-resolved alerts have no cooldown and can reopen immediately if the
// condition returns.
// Cooldown days applied on MANUAL close.
// 0 = no cooldown: content alerts reopen immediately on the next refresh if
// the condition still exists. Closing is an acknowledgment, not a suppression.
// >0 = user-behavior alerts: suppress re-opening for this many days so a single
// acknowledgment isn't immediately undone by the next refresh.
const COOLDOWN: Record<string, number> = {
unfulfilled: 3,
pending: 3,
ghost: 14,
watchrate: 14,
declined: 14,
"tautulli-no-matches": 1,
"dark-library": 30,
unfulfilled: 0,
pending: 0,
ghost: 7,
watchrate: 7,
"tautulli-no-matches": 0,
};
const DEFAULT_COOLDOWN = 7;
const DEFAULT_COOLDOWN = 0;
interface Store {
nextId: number;
@@ -205,13 +205,17 @@ export function closeAlert(id: number): Alert | null {
if (!alert) return null;
const cooldownDays = COOLDOWN[alert.category] ?? DEFAULT_COOLDOWN;
const suppressUntil = new Date();
suppressUntil.setDate(suppressUntil.getDate() + cooldownDays);
let suppressedUntil: string | null = null;
if (cooldownDays > 0) {
const d = new Date();
d.setDate(d.getDate() + cooldownDays);
suppressedUntil = d.toISOString();
}
alert.status = "closed";
alert.closeReason = "manual";
alert.closedAt = new Date().toISOString();
alert.suppressedUntil = suppressUntil.toISOString();
alert.suppressedUntil = suppressedUntil;
save(store);
return toAlert(alert);
}