Phase 7: notify and send via Apprise (Email + WhatsApp)
The morning briefs go out through the house Apprise API on `.35:8210`: one brief per boy and one combined brief to Jo and Sam (FR-9, FR-14). The service names an Apprise tag (`harry_email`, `joanna_whatsapp`, ...) and never holds an address, a phone number or an SMTP password (AGENTS.md §10 rule 3). - internal/apprise: keyed and stateless client, tag filtering, and error redaction so nothing sensitive reaches a log, a row or a screen. - internal/brief: builds the per-boy and the combined brief, delivers on email and WhatsApp, records every attempt in `notifications` (migration 004 adds scope, channel, tag, ok and error) and fails loudly. - Send panel (§5 screen 4): recipients, channels, an exact preview, Send now, plus the Send action on a stored draft (FR-10). The dashboard shows a Morning briefs banner and the Sends list. - POST /notify/morning: the bearer-token endpoint the 6:00am n8n schedule calls. 207 when a channel or a scan failed; `dry_run=1` checks the wiring without messaging the family. - scripts/morning-brief.sh: scan both children, then send. It is the n8n job; deploy/n8n-morning-brief.json is the ready-to-import workflow (06:00 daily). - FR-12: a failed scan is never "nothing due" -- the brief says so, and the run exits non-zero. A failed delivery is recorded with no sent_at. - The long no-due-date tail is capped in the sent brief so it stays brief (§10 rule 6); overdue work is never capped. Verified live: 8 deliveries recorded (4 email, 4 WhatsApp) with HTTP 200 and sent_at, and the endpoint answers 401/200/207 as documented. Evidence in docs/phase-7-notify-notes.md. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
40
README.md
40
README.md
@@ -4,8 +4,29 @@ A school service for Harry and Finn: **Compass** for the daily schedule and
|
||||
notices, **Teams** for homework and assignments. It stores what is due per child,
|
||||
puts overdue work first, and tells the family before work is late.
|
||||
|
||||
> Build spec: [`AGENTS.md`](AGENTS.md). This repository is at **Phase 6** (the
|
||||
> calendar write). See §11 of the spec for the phase order.
|
||||
> Build spec: [`AGENTS.md`](AGENTS.md). This repository is at **Phase 7** (notify
|
||||
> and send via Apprise). See §11 of the spec for the phase order.
|
||||
|
||||
## Status — Phase 7
|
||||
|
||||
Phase 7 builds and sends the morning briefs through the house **Apprise** API on
|
||||
`.35:8210` (FR-9, FR-14): one brief per boy, plus one combined brief to Jo and
|
||||
Sam, on **Email and WhatsApp**. Every attempt is recorded in `notifications` and
|
||||
shown on the dashboard, so a failed send is loud rather than silent (FR-12).
|
||||
`POST /notify/morning` is the endpoint the 6:00am n8n schedule calls. See
|
||||
[`docs/phase-7-notify-notes.md`](docs/phase-7-notify-notes.md).
|
||||
|
||||
```bash
|
||||
make notify # send the briefs from the latest scan
|
||||
make preview # print the briefs, send nothing
|
||||
make morning-brief # scan both children, then send (the n8n job)
|
||||
|
||||
# Delivery log: per recipient per channel, plus the scan state.
|
||||
docker exec compass-teams-nhs /app/school notify status
|
||||
```
|
||||
|
||||
The destinations live in the Apprise configuration, not here: this service only
|
||||
names a tag such as `harry_email` (AGENTS.md §10 rule 3).
|
||||
|
||||
## Status — Phase 6
|
||||
|
||||
@@ -77,9 +98,8 @@ Built and deployed on `.13`:
|
||||
- Its own container and port **8792**, behind Caddy at
|
||||
`school.lab.audasmedia.com.au`.
|
||||
|
||||
Not built yet (later phases): notifications and the Hub API. The calendar write
|
||||
is Phase 6. The service itself does **not** scan the school; n8n triggers the
|
||||
scan later. Teams (FR-15) is a separate source.
|
||||
Not built yet (later phases): the Hub API. The service itself does **not** scan
|
||||
the school; n8n triggers the scan. Teams (FR-15) is a separate source.
|
||||
|
||||
### Fail loudly (FR-12)
|
||||
|
||||
@@ -98,11 +118,13 @@ internal/config/ environment configuration
|
||||
internal/store/ SQLite: schema, migrations, queries
|
||||
internal/parse/ HTML -> structured items (tasks, messages, events)
|
||||
internal/llm/ OmniRoute client (importance classification only)
|
||||
internal/apprise/ Apprise API client (tag-based, redacts errors)
|
||||
internal/brief/ build + deliver the morning briefs (FR-9, FR-14)
|
||||
internal/calendar/ Home Assistant calendar client + the idempotent sync
|
||||
internal/auth/ session login (signed cookies, CSRF)
|
||||
internal/web/ HTTP handlers, templates, static assets
|
||||
deploy/ Caddy snippet, deployment notes, verify script
|
||||
scripts/ Compass fetchers, the parse wrapper and the calendar push
|
||||
deploy/ Caddy snippet, the n8n workflow, deployment notes, verify script
|
||||
scripts/ Compass fetchers, the parse and morning-brief wrappers, the calendar push
|
||||
environment.d/ env example (the real file is gitignored)
|
||||
```
|
||||
|
||||
@@ -117,8 +139,8 @@ docker compose up -d --build
|
||||
Generate secrets with the built image:
|
||||
|
||||
```bash
|
||||
docker run --rm --entrypoint /app/school compass-teams-nhs/school:0.4.0-phase6 gen-secret
|
||||
docker run --rm --entrypoint /app/school compass-teams-nhs/school:0.4.0-phase6 hash-password 'the-password'
|
||||
docker run --rm --entrypoint /app/school compass-teams-nhs/school:0.5.0-phase7 gen-secret
|
||||
docker run --rm --entrypoint /app/school compass-teams-nhs/school:0.5.0-phase7 hash-password 'the-password'
|
||||
```
|
||||
|
||||
Note: Docker Compose interpolates `$VAR` inside `env_file`. A bcrypt hash
|
||||
|
||||
Reference in New Issue
Block a user