feat: add multi-chat routing and bill-specific dashboard countdown
Some checks failed
Test blackout notifier / test (push) Failing after 4s
Hourly blackout check / check (push) Successful in 3s

This commit is contained in:
Meghdad
2026-07-22 02:14:45 +03:30
parent 6c3e146234
commit 42687ee651
12 changed files with 372 additions and 100 deletions

View File

@@ -1,7 +1,8 @@
# Daily Blackout Check
Checks the official SAAPA planned-outage API and sends Persian notifications to
an Eitaa chat when an outage is added, changed, cancelled, or restored.
the Eitaa chats configured for each bill when an outage is added, changed,
cancelled, or restored.
The checker is designed for an hourly Gitea Actions workflow. Its deduplication
state is stored in `state/outages.json` and committed back to this repository.
@@ -9,14 +10,16 @@ state is stored in `state/outages.json` and committed back to this repository.
## Behavior
- Queries each configured bill ID from today through five days ahead.
- Routes each bill's notifications to all of its configured Eitaa chats.
- Uses Tehran time regardless of the runner container's timezone.
- Sends all upcoming outages when starting with empty state.
- Treats an existing outage number with changed time, address, date, reason, or
planned status as an update.
- Reports a future outage as cancelled only after two consecutive successful
checks omit it.
- Records a notification only after Eitaa accepts it. This favors delivery over
perfect deduplication: a failed state push can cause a duplicate next hour.
- Records a notification only after every configured chat accepts it. This
favors delivery over perfect deduplication: if one chat fails, successful
destinations may receive a retry on the next run.
- Rejects malformed responses and snapshots larger than the configured safety
limit without changing that bill's state.
@@ -50,8 +53,7 @@ python -m blackout_notifier check
| --- | --- | --- | --- |
| `BARGHEMAN_TOKEN` | Yes | — | SAAPA bearer token |
| `EITAAYAR_TOKEN` | Yes | — | EitaaYar bot token |
| `CHAT_ID` | Yes | — | Destination Eitaa chat |
| `BILL_IDS` | Yes | — | Comma-separated decimal bill IDs |
| `BILL_CHAT_MAP` | Yes | — | Comma-separated `BILL_ID:CHAT_ID` routes |
| `STATE_FILE` | No | `state/outages.json` | Durable notification state |
| `LOOKAHEAD_DAYS` | No | `5` | Query horizon, from 1 to 30 days |
| `REQUEST_TIMEOUT_SECONDS` | No | `15` | HTTP timeout, from 1 to 120 seconds |
@@ -59,6 +61,18 @@ python -m blackout_notifier check
Shell and Gitea-provided variables take precedence over `.env`.
For example, this sends bill `7356609804429` to chats `10251670` and `20873456`,
while bill `7619314604429` is sent only to chat `30984567`:
```dotenv
BILL_CHAT_MAP=7356609804429:10251670,7356609804429:20873456,7619314604429:30984567
```
Repeat a bill ID with another chat ID to add destinations; duplicate identical
routes are ignored. The old `CHAT_ID` plus `BILL_IDS` configuration is still
accepted when `BILL_CHAT_MAP` is absent, but it sends every bill to the same chat
and is intended only for backward compatibility.
## Gitea deployment
This repository targets Gitea 1.25.x and a Docker-based runner advertising the
@@ -91,8 +105,7 @@ set to **Read and Write**. Add these repository Actions secrets:
| `REGISTRY_TOKEN` | Personal access token with package Read and Write permission |
| `BARGHEMAN_TOKEN` | SAAPA bearer token |
| `EITAAYAR_TOKEN` | EitaaYar bot token |
| `CHAT_ID` | Destination chat ID |
| `BILL_IDS` | Comma-separated bill IDs |
| `BILL_CHAT_MAP` | Comma-separated `BILL_ID:CHAT_ID` routes |
The registry token is used to publish the image and as `container.credentials`
when Gitea Runner pulls the private image before starting a job.
@@ -183,6 +196,16 @@ It requires Bash, `curl`, `jq`, and GNU `date`:
./main.sh
```
On macOS, install the modern Bash and GNU `date` implementations with Homebrew:
```sh
brew install bash coreutils
```
`main.sh` automatically switches from Apple's Bash 3.2 to Homebrew Bash and
adds Homebrew's GNU utilities to `PATH`, so both `./main.sh` and
`bash ./main.sh` work after installation.
The display uses a flicker-free alternate terminal screen and runs until `q` is
pressed (or it is interrupted with `Ctrl+C`). It updates the countdown every
second and refetches the committed state every 10 minutes. Set a different whole
@@ -192,12 +215,22 @@ minute interval with `-i`/`--interval`:
./main.sh --interval 5
```
The dashboard uses Tehran time to show a large `HH : mm : ss` countdown for the outage
currently in progress or the nearest upcoming outage. A compact schedule below it
lists all active current and future events, including emergency outages. The
terminal output uses English-only labels and omits the stored address and reason
text. When there are no qualifying events it prints an `ALL CLEAR` screen and exits
successfully.
Select one bill for the large countdown with `-b`/`--bill-id`:
```sh
./main.sh --bill-id 7356609804429
```
The dashboard uses Tehran time to show a large `HH : mm : ss` countdown for the
selected bill's ongoing or nearest upcoming outage. The compact schedule below
continues to list active current and future events for every bill, including
emergency outages. If the selected bill has no qualifying outage, the countdown
area says so while the full schedule remains visible. Without `--bill-id`, the
countdown retains the original behavior and selects across all bills.
The terminal output uses English-only labels and omits the stored address and
reason text. When there are no qualifying events for any bill it prints an `ALL
CLEAR` screen and exits successfully.
Colors are enabled only when output is attached to a terminal. Set `NO_COLOR` to
disable them explicitly, or set `COLUMNS` to control the rendered width: