feat: add tomorrow weather brief notifications

This commit is contained in:
zv
2026-06-12 22:10:22 +02:00
parent 3309e03acb
commit 7c3706c3f6
15 changed files with 414 additions and 15 deletions

View File

@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
const DEFAULT_APP_URL = "http://127.0.0.1:3000";
const DEFAULT_WARNING_INTERVAL_MINUTES = 5;
const DEFAULT_MORNING_BRIEF_TIME = "07:00";
const DEFAULT_TOMORROW_BRIEF_TIME = "18:00";
const LOOP_INTERVAL_MS = 30_000;
const initialEnvKeys = new Set(Object.keys(process.env));
@@ -12,10 +13,12 @@ loadEnvFile(".env.local", true);
const appUrl = normalizeAppUrl(process.env.WTR_APP_URL ?? process.env.NEXT_PUBLIC_APP_URL ?? DEFAULT_APP_URL);
const cronSecret = process.env.NOTIFICATIONS_CRON_SECRET ?? "";
const warningIntervalMinutes = readPositiveNumber(process.env.NOTIFICATIONS_WARNING_INTERVAL_MINUTES, DEFAULT_WARNING_INTERVAL_MINUTES);
const morningBriefTime = normalizeTime(process.env.NOTIFICATIONS_MORNING_BRIEF_TIME ?? DEFAULT_MORNING_BRIEF_TIME);
const morningBriefTime = normalizeTime(process.env.NOTIFICATIONS_MORNING_BRIEF_TIME ?? DEFAULT_MORNING_BRIEF_TIME, DEFAULT_MORNING_BRIEF_TIME);
const tomorrowBriefTime = normalizeTime(process.env.NOTIFICATIONS_TOMORROW_BRIEF_TIME ?? DEFAULT_TOMORROW_BRIEF_TIME, DEFAULT_TOMORROW_BRIEF_TIME);
let lastWarningCheckAt = 0;
let lastMorningBriefDate = "";
let lastTomorrowBriefDate = "";
let isRunningTick = false;
function loadEnvFile(path, overrideLoadedValues) {
@@ -43,8 +46,8 @@ function readPositiveNumber(value, fallback) {
return Number.isFinite(number) && number > 0 ? number : fallback;
}
function normalizeTime(value) {
return /^\d{2}:\d{2}$/.test(value) ? value : DEFAULT_MORNING_BRIEF_TIME;
function normalizeTime(value, fallback) {
return /^\d{2}:\d{2}$/.test(value) ? value : fallback;
}
function getWarsawDateParts(date = new Date()) {
@@ -113,8 +116,12 @@ async function tick() {
const warsawTime = getWarsawDateParts();
if (isAtOrAfterTime(warsawTime.time, morningBriefTime) && lastMorningBriefDate !== warsawTime.dateKey) {
const sentBrief = await callNotificationEndpointSafely("/api/notifications/daily-brief");
if (!sentBrief) return;
lastMorningBriefDate = warsawTime.dateKey;
if (sentBrief) lastMorningBriefDate = warsawTime.dateKey;
}
if (isAtOrAfterTime(warsawTime.time, tomorrowBriefTime) && lastTomorrowBriefDate !== warsawTime.dateKey) {
const sentBrief = await callNotificationEndpointSafely("/api/notifications/tomorrow-brief");
if (sentBrief) lastTomorrowBriefDate = warsawTime.dateKey;
}
} catch (error) {
console.error(formatWorkerError(error));
@@ -124,6 +131,6 @@ async function tick() {
}
console.log(`wtr. notification worker: ${appUrl}`);
console.log(`Warnings every ${warningIntervalMinutes} min, morning brief at ${morningBriefTime} Europe/Warsaw.`);
console.log(`Warnings every ${warningIntervalMinutes} min, morning brief at ${morningBriefTime}, tomorrow brief at ${tomorrowBriefTime} Europe/Warsaw.`);
void tick();
setInterval(tick, LOOP_INTERVAL_MS);