Telegram alert bot with staged rollout

Baguio Outage Radar

A Telegram bot that tells each subscriber only about the power interruptions that affect the areas they saved, built from a public outage data feed and restored with a dry-run-first rollout.

Status
Live in a controlled, owner-only stage since November 2026. Wider release not started. Private.
Source
Source: private

The problem

Power interruption notices are published for whole feeders and towns, in text that mentions many place names. Residents and property managers only care about their own area.

The goal was a small service that reads the public outage feed, matches it to the areas each subscriber saved, and sends a timely message, without ever spamming or missing an alert after a crash.

The approach

A Node.js and Express service polls the regional electric cooperative’s public JSON feeds for actual and scheduled outages every five minutes and turns each record into a row with a time window, feeder, area, cause and place names.

A place-name dictionary and whole-phrase matching connect rows to each subscriber’s saved areas. A Telegram bot (long polling) handles subscribe, status, test and unsubscribe commands, plus admin commands.

A password-protected admin dashboard manages subscribers. A separate parser turns advisory PDFs or pasted text into rows, outside the automatic alert path.

It runs as a hardened systemd service on a Linux VPS, from versioned release directories behind a symlink.

Architecture

Baguio Outage Radar architectureA poller reads the public outage feed and builds rows. A matcher compares them with subscriber areas and sends alerts through the Telegram bot, with a dry-run switch and an allowlist in front of sending. State is stored in files. The service runs under systemd.Public outage feedJSON, polled every 5 minPoller + row builderplace-name dictionaryMatcherwhole-phrase, per subscriberSend gatedry run + allowlistTelegram botlong pollingState filesatomic writes, sent logsystemd servicehardened, restarts on failure
Solid outline: component. Amber fill: control or guard. Dashed outline: external input or output. Dotted outline: deliberately blocked.
Text alternative: the flow in words
  1. Every five minutes the poller reads the public feed and builds outage rows.
  2. The matcher compares each new row with the areas every subscriber saved.
  3. The send gate applies the dry-run switch and the recipient allowlist.
  4. The Telegram bot delivers the alert. The outage is only marked as seen after delivery.
  5. State lives in atomically written files. systemd restarts the service if it fails.

Key technical decisions

  • Dry run is the default

    Unless explicitly switched off, no Telegram traffic is sent. Would-be messages go to a local outbox file that can be reviewed first.

  • Staged rollout with an allowlist

    Executed in order: dry run on the server, a 24-hour soak, a build with a recipient allowlist, then live for the owner only. The activation script runs its checks, rolls back automatically on failure and starts a 48-hour monitor. Wider release has separate written conditions and has not started.

  • Never lose or repeat an alert

    An outage is marked as seen only after its alerts were sent. A per-subscriber sent log prevents repeats, and the first start on a new server records existing outages silently.

  • Crash, then let systemd restart

    Failures exit the process and the service manager restarts it. State files are written atomically, and poll failures raise an admin alert and a recovery notice.

  • Rollback and backups built in

    Install and rollback scripts switch between release directories, a data backup script keeps a rolling set, and rollback can optionally restore a data backup.

Safety and security notes

  • The restoration fixed a set of defects found in the recovered original, each with a regression test: unauthenticated admin routes, an open payment webhook, unescaped user text in messages, and over-eager word-level matching.
  • The service listens on loopback only, compares the dashboard password in constant time and keeps the admin closed when no password is set.
  • systemd hardening limits writes to the data directory: no new privileges, strict protection of the system and home directories, private temp and devices.
  • No tokens, hosts, addresses or subscriber information appear here. Sanitised summary only.

Limits and honest caveats

  • Not open to the public. The original ran as a live service during June 2026; the restored version has only been live for the owner so far, and wider release is a separate decision.
  • Matching quality depends on the feed’s free text, which is why the dry-run outbox is reviewed before going live.