// ntfy-style priority scale (docs/migration-ntfy.md) — reused here so a // value already familiar to anyone migrating from ntfy behaves the way // they'd expect: "min"/1 means "log it, don't buzz the phone." const PRIORITY_ALIASES: Record = { min: 1, low: 2, default: 3, high: 4, urgent: 5, max: 5, } const DEFAULT_PRIORITY = 3 const MIN_PRIORITY_NO_PUSH = 1 export function resolvePriority(raw: string | null | undefined): number { if (!raw) return DEFAULT_PRIORITY const trimmed = raw.trim().toLowerCase() // The `in` check above guarantees the key exists. if (trimmed in PRIORITY_ALIASES) return PRIORITY_ALIASES[trimmed]! const parsed = Number(trimmed) return Number.isFinite(parsed) ? parsed : DEFAULT_PRIORITY } // "min" priority notifications are stored and shown in-app like any other // — they just don't trigger a push, so a noisy automation can stay in its // normal topic instead of needing a dedicated quiet topic. export function isQuietPriority(raw: string | null | undefined): boolean { return resolvePriority(raw) <= MIN_PRIORITY_NO_PUSH } // Household-wide "quiet hours" window (Admin > System) — true when `now` // falls inside [start, end) in the given IANA timezone. Handles a window // that wraps past midnight (e.g. 23:00-07:00) by checking OR instead of // AND once start > end. export function isWithinQuietHours(start: string, end: string, timezone: string, now = new Date()): boolean { const parts = new Intl.DateTimeFormat('en-GB', { timeZone: timezone, hour: '2-digit', minute: '2-digit', hourCycle: 'h23', }).formatToParts(now) const hour = Number(parts.find((p) => p.type === 'hour')?.value ?? '0') const minute = Number(parts.find((p) => p.type === 'minute')?.value ?? '0') const nowMinutes = hour * 60 + minute const [startH, startM] = start.split(':').map(Number) const [endH, endM] = end.split(':').map(Number) const startMinutes = (startH ?? 0) * 60 + (startM ?? 0) const endMinutes = (endH ?? 0) * 60 + (endM ?? 0) if (startMinutes === endMinutes) return false if (startMinutes < endMinutes) { return nowMinutes >= startMinutes && nowMinutes < endMinutes } return nowMinutes >= startMinutes || nowMinutes < endMinutes }