From 29535d6bbc8d4e9957f55ab9429e9e35063c5fa6 Mon Sep 17 00:00:00 2001 From: yiekheng Date: Sun, 10 May 2026 13:23:04 +0800 Subject: [PATCH] docs: refresh README for web-first state of the world (P3/T24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status section reflected the post-Plan-1 world where pairing went through Telegram. Reality has long since moved on: - Plans 1, 2, and 3 are all complete. - The Telegram bot is gone (removed in P3/T4). - The web app at wabot.04080616.xyz is the primary surface. - The recurrence picker, multi-message stack, swipe rows, archive flow, HEIC fallback, and PWA install all shipped. Other updates: - Quick-start no longer mentions Telegram tokens — replaced with the URL the operator opens to use the app. - Architecture paragraph swapped grammy mention for the LISTEN/ NOTIFY consumer + SSE shape. - Pointed at the new design spec (2026-05-09-web-app-design.md) and manual test runbook. - Layout / scripts tables trimmed: dropped the publish.sh and link-account.sh stubs that were never implemented. - "Deferred" section lists honest gaps (media library, E2E tests, auth, multi-operator). Test counts called out (249 web + 31 shared + 26 bot = 306) so the status claim is verifiable in a single grep. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 125 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 8b933d7..5fa79d9 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,87 @@ # cm WhatsApp Reminder Bot -Self-hosted WhatsApp reminder bot. Pairs multiple WhatsApp accounts via Telegram-delivered QR codes and sends scheduled reminders to groups. +Self-hosted WhatsApp reminder bot. Pair multiple WhatsApp accounts via +a browser-based PWA, schedule recurring reminders to groups, and watch +the run history all from a phone home-screen icon. ## Status -**Plan 1 complete.** Foundation, DB schema, and Telegram-driven WhatsApp pairing are working end-to-end. Reminder scheduling, the web dashboard, and production deploy are upcoming plans (`docs/superpowers/plans/`). +**Plans 1, 2, and 3 complete.** The web app at `wabot.04080616.xyz` is +the primary control surface; the Telegram bot has been removed. What's working today: -- Single-operator Telegram bot with a whitelist + audit log of every command. -- BotFather-style menu navigation: `/menu` opens a single message that edits in place as you navigate. -- Pair a new WhatsApp account with `/menu` → 📡 Pair New → reply with a label. QR is delivered to Telegram and refreshed in place as it expires. -- Browse paired accounts with 📒 Accounts. Tap an account → see groups, send a test text message, or unpair. -- Group sync runs at pairing and on every Baileys `groups.upsert` / `groups.update` event, plus a manual 🔄 Refresh button. Removed groups are pruned automatically. -- Auto-reconnect on transient drops; restart-survival via Baileys `useMultiFileAuthState` (no QR rescan needed across container restarts as long as WhatsApp hasn't logged the device out). +- **Self-hosted Next.js 16 PWA** — installable on a phone home screen. + Mobile-first single-row header with a slide-out drawer; desktop + sidebar. +- **Live QR pairing** — server-side Baileys session feeds the QR + payload directly into the browser via Server-Sent Events. Scan, + see "✅ Connected" within seconds, auto-redirect. +- **Multi-account, multi-group reminders** — 5-step wizard + (Account → Message → When → Groups → Review) plus per-section edit + pages so you don't have to walk the wizard end-to-end to fix one + field. Active recurrence picker covers Daily / Weekly / Monthly / + Yearly with multi-rule support and per-rule fire-time pickers; the + rendered description reads as plain English ("Every week on Mon, + Wed, Fri at 09:00") not raw cron. +- **Multi-message stacks** — a reminder can carry multiple ordered + parts (text + media), fired in sequence with a 1.5 s gap. Media + files swap at any time from the Edit Message page. +- **Smart media handling** — per-kind WhatsApp size caps (5 MB image, + 16 MB video/audio, 100 MB document). HEIC photos and `.mov` videos + fall back to the document delivery path so they reach the recipient + as a downloadable file instead of failing silently. +- **Swipe-to-act rows** — on mobile, swipe a reminder or activity + row left for Delete or right for Pause/Restart/Archive. iOS-Mail + style. +- **Activity tab** — last 200 runs with status filters (Success / + Partial / Failed / Skipped) plus an Archived tab. Archive a noisy + run to keep the main list readable; restore later. Hard-delete + always available. Run history survives a reminder deletion. +- **Auto-reconnect on transient drops; restart-survival via Baileys + session persistence.** Pair once, the device stays linked across + container restarts. +- **All actions audited.** Reminder run history queryable from the + UI; per-run target results (sent / failed / skipped) preserved + even when the underlying group is removed. + +Test count: **249 web + 31 shared + 26 bot = 306** passing. ## Host requirements -Only Docker. No host Node, pnpm, or any other language toolchain — everything runs in containers via the long-lived `tools` service. +Only Docker. No host Node, pnpm, or any other language toolchain — +everything runs in containers via the long-lived `tools` sidecar. ## Architecture in one paragraph -Two app containers and one external dependency. `bot` (Node.js) holds the live Baileys WhatsApp sessions, the grammy Telegram bot, and (in plan 2) a pg-boss scheduler. `web` (Next.js, plan 3) is stateless UI + API. `tools` is a long-running Node 22 + pnpm sidecar used for installs/tests/typechecks/migrations so the host doesn't need a Node toolchain. Postgres lives external at `192.168.0.210` in a `wabot` database. All cross-service communication goes through Postgres (`LISTEN/NOTIFY` for events, table writes for state). +Two app containers and one external dependency. `bot` (Node.js) holds +the live Baileys WhatsApp sessions, the pg-boss scheduler, and a +Postgres `LISTEN bot.command` consumer. `web` (Next.js 16 App Router ++ React 19) is stateless UI: Server Components for reads, Server +Actions for mutations, an SSE endpoint for live updates, +`@serwist/next` for the PWA shell. `tools` is a long-running +Node 22 + pnpm sidecar used for installs / tests / typechecks / +migrations so the host doesn't need a Node toolchain. Postgres lives +external at `192.168.0.210` in a `wabot` database. All cross-service +communication goes through Postgres (`LISTEN/NOTIFY` for events, +table writes for state). -Full design spec: [`docs/superpowers/specs/2026-05-03-whatsapp-bot-design.md`](docs/superpowers/specs/2026-05-03-whatsapp-bot-design.md) +Full design spec: +[`docs/superpowers/specs/2026-05-09-web-app-design.md`](docs/superpowers/specs/2026-05-09-web-app-design.md) ## Quick start (dev) -Prerequisites: Docker, the `wabot` database + `waBot` role on `192.168.0.210` (with a `pg_hba.conf` line permitting `192.168.0.0/24`), and a Telegram bot token from `@BotFather`. +Prerequisites: Docker, the `wabot` database + `waBot` role on +`192.168.0.210` (with a `pg_hba.conf` line permitting +`192.168.0.0/24`). ```bash # 1. Configure env cp envs/.env.example .env.development -# edit .env.development: real DATABASE_URL, TELEGRAM_BOT_TOKEN, your TG user ID +# edit .env.development: real DATABASE_URL, plus the LAN host to expose scripts/gen_auth_secret.sh --write -# 2. Bring up the tools container, install deps +# 2. Bring up the stack, install deps NO_SUDO=1 scripts/dev.sh up NO_SUDO=1 scripts/dev.sh pnpm install @@ -43,39 +89,62 @@ NO_SUDO=1 scripts/dev.sh pnpm install NO_SUDO=1 scripts/db.sh migrate NO_SUDO=1 scripts/db.sh seed -# 4. Watch the bot service -NO_SUDO=1 scripts/dev.sh logs bot +# 4. Open the web app +# Local: http://localhost:9000 +# LAN: http://:9000 (e.g. http://192.168.0.253:9000) +# Public: https://wabot.04080616.xyz (whatever your reverse proxy serves) ``` -In Telegram, message your dev bot `/menu`, tap **📡 Pair New**, reply with a label, scan the QR. +Pair an account: `/accounts` → "New Account" → enter a label → +"Pair WhatsApp" → scan the QR with WhatsApp's "Linked Devices". -`NO_SUDO=1` is the right setting if your user is in the `docker` group (the default for this repo). Drop it if you need `sudo docker`. +PWA install: phone Chrome → menu → "Install App" / "Add to Home +Screen". Launches fullscreen. + +`NO_SUDO=1` is the right setting if your user is in the `docker` +group (the default for this repo). Drop it if you need `sudo docker`. + +## Manual test runbook + +End-to-end checks that unit tests can't cover (live Baileys, +WhatsApp delivery, swipe gestures): +[`docs/superpowers/specs/manual-test-web.md`](docs/superpowers/specs/manual-test-web.md). ## Layout -- `apps/bot/` — Node service: Baileys WhatsApp + grammy Telegram + (later) pg-boss scheduler -- `apps/web/` — Next.js dashboard (plan 3) +- `apps/bot/` — Baileys WhatsApp + pg-boss scheduler + LISTEN/NOTIFY + command consumer +- `apps/web/` — Next.js 16 App Router PWA - `packages/db/` — Drizzle schema and migrations -- `packages/shared/` — cross-app helpers (rrule, media paths, timezones) +- `packages/shared/` — cross-app helpers (rrule, media paths, + timezones, WhatsApp media classifier) - `docs/superpowers/specs/` — design specs and manual test runbooks - `docs/superpowers/plans/` — implementation plans -- `docker/` — Dockerfiles (`tools.Dockerfile`, `bot.Dockerfile`, `web.Dockerfile` placeholder) -- `scripts/` — `dev.sh`, `db.sh`, `gen_auth_secret.sh`, plus stubs for plans 2/4 +- `docker/` — Dockerfiles (`tools.Dockerfile`, `bot.Dockerfile`, + `web.Dockerfile`) +- `scripts/` — `dev.sh`, `db.sh`, `gen_auth_secret.sh` ## Scripts -All `pnpm`/`tsx`/`drizzle-kit` invocations run inside the `tools` container, so no host Node is needed. +All `pnpm`/`tsx`/`drizzle-kit` invocations run inside the `tools` +container, so no host Node is needed. | Script | Purpose | |---|---| | `scripts/dev.sh up\|down\|logs\|status\|build\|exec\|pnpm\|shell\|restart-bot` | Stack lifecycle and tools-container shell | | `scripts/db.sh migrate\|generate\|studio\|seed\|reset` | Drizzle migration helper | | `scripts/gen_auth_secret.sh [--write]` | Generate `AUTH_SECRET` (host-only, no Node needed) | -| `scripts/publish.sh` | Push to Gitea registry — implemented in plan 4 | -| `scripts/link-account.sh` | CLI pairing without Telegram — implemented in plan 2 | Set `NO_SUDO=1` if your user is in the docker group (recommended). -## Next plan +## Deferred -`docs/superpowers/plans/-reminder-scheduling.md` — pg-boss, reminder CRUD via Telegram, fire-reminder handler, sender (text/image/video), retry policy, run history. +- **Standalone media library** browser (currently media is uploaded + per-reminder). +- **E2E browser tests** (Playwright) on the swipe and pairing flows. +- **Auth** (passkeys / email-password) — bring back if URL exposure + becomes a concern. Today the app trusts whatever's in front of the + reverse proxy. +- **Multi-operator** — schema supports `operator_id` on every row, + but the seed runs as a single operator and there's no /signup or + invite flow yet.