// devict-events: serves an HTTP API of upcoming OpenMeet events and runs a // background notifier that announces new events and sends a weekly digest. // // Usage: // node --env-file=.env main.ts import { loadConfig, type Config } from "./src/config.ts"; import { newNotifier } from "./src/notifier/notifier.ts"; import { startApi } from "./src/api/api.ts"; import type { Channel } from "./src/notifier/channels/channel.ts"; import { slackChannel } from "./src/notifier/channels/slack.ts"; import { stdoutChannel } from "./src/notifier/channels/stdout.ts"; function buildChannels(cfg: Config): Channel[] { const channels: Channel[] = []; if (cfg.slackToken && cfg.slackChannel) { channels.push(slackChannel(cfg.slackToken, cfg.slackChannel)); } else { console.log("skipping slack channel, incomplete config"); } if (cfg.enableStdout) channels.push(stdoutChannel()); if (channels.length === 0) throw new Error("no notification channels configured"); return channels; } async function run(): Promise { const cfg = loadConfig(); const channels = buildChannels(cfg); const aborter = new AbortController(); for (const sig of ["SIGINT", "SIGTERM"] as const) process.once(sig, () => aborter.abort()); const notifier = await newNotifier({ cfg, aborter, channels }); try { // Serve the HTTP API while the notifier poll loop runs in the background. // Both shut down together on SIGINT/SIGTERM. await Promise.all([startApi(cfg, { aborter }), notifier.start()]); } catch (err) { console.error("fatal:", err instanceof Error ? err.message : err); process.exit(1); } } await run();