SQLite-backed birthday and anniversary reminders for Val Town.
This val is intentionally small: one HTTP admin UI/API, one daily cron checker, pure date/message helpers, and inline Telegram delivery.
This repo owns the app-specific reminder port. The sibling uApps repo owns the shared vtx wrapper
plus reusable Val Town templates/examples such as $HOME/minion/uApps/vals/_template/ and
$HOME/minion/uApps/vals/null-css/.
reminders/ ├── lib/ │ ├── admin/ │ │ ├── ai.ts # GPT command endpoint helpers │ │ ├── auth.ts # Val Town OAuth + bearer token admin auth │ │ ├── csv.ts # CSV schema import parser │ │ ├── ui-client.ts # browser script for admin actions │ │ └── ui.tsx # Preact SSR + null-css admin UI │ ├── db.ts # SQLite schema + CRUD helpers │ ├── dates.ts # pure look-ahead date calculations │ ├── events.ts # SQLite schema + event persistence helpers │ ├── messages.ts # pure Telegram message formatting │ ├── telegram.ts # Bot API fetch helper │ └── types.ts # Person and reminder event types ├── reminder-api.http.ts # HTTP admin UI, CRUD, CSV, and AI API ├── reminder-check.cron.ts # daily reminder check ├── deno.json # Deno/Val Town typecheck config ├── val.yml # Val Town metadata └── README.md
Set these in the Val Town UI.
| Variable | Required | Purpose |
|---|---|---|
| ADMIN_USERNAME | yes | Val Town username allowed to see the admin UI |
| ADMIN_TOKEN | yes | Bearer token for direct API routes |
| OAUTH_STATE_ENCRYPTION_KEY | yes | Random secret for Val Town OAuth state cookies |
| TELEGRAM_BOT_TOKEN | yes | Telegram BotFather token |
| TELEGRAM_CHAT_ID | yes | Destination chat or channel id |
| LOOKAHEAD_DAYS | no | Days to scan, default 8 |
| DRY_RUN | no | true skips Telegram delivery |
OAUTH_STATE_ENCRYPTION_KEY is required by Val Town std/oauth; use a random secret such as
openssl rand -base64 32. Without it, OAuth can still log a missing-env warning before login state
can be protected.
The AI endpoint uses Val Town's std/openai integration and does not need an OpenAI API key.
Visit / or /admin and sign in with Val Town. The UI renders only for ADMIN_USERNAME. Direct
API clients can use ADMIN_TOKEN.
The page supports:
- listing, creating, editing, and deleting people
- CSV upload with columns
name,nickName,birthdate,anniversary,isAccurate,relation,email; the UI includes a downloadable sample - AI commands that can dry-run or apply
save-row,update-row, andsend-telegramactions
People are identity/contact records. Events are dated facts attached to a person. The legacy
birthdate and anniversary inputs remain for compatibility, but writes also normalize them into
events rows:
people:name,nick_name,relation,emailevents:person_id,kind,label,date,calendar,is_accurate,source,external_id,notes
This keeps current birthday/anniversary reminders working while leaving room for custom events, calendar export, imported external IDs, and richer event metadata.
All JSON API routes require either the admin Val Town browser session,
Authorization: Bearer <ADMIN_TOKEN>, or X-Admin-Token: <ADMIN_TOKEN>.
# list people curl "$VAL_URL/people" \ -H "authorization: Bearer $ADMIN_TOKEN" # create person curl -X POST "$VAL_URL/people" \ -H "authorization: Bearer $ADMIN_TOKEN" \ -H "content-type: application/json" \ -d '{"name":"Ada Lovelace","birthdate":"1815-12-10","relation":"history"}' # update person curl -X PUT "$VAL_URL/people/1" \ -H "authorization: Bearer $ADMIN_TOKEN" \ -H "content-type: application/json" \ -d '{"nickName":"Ada","isAccurate":true}' # delete person curl -X DELETE "$VAL_URL/people/1" \ -H "authorization: Bearer $ADMIN_TOKEN" # import CSV curl -X POST "$VAL_URL/people/import-csv" \ -H "authorization: Bearer $ADMIN_TOKEN" \ -H "content-type: application/json" \ -d '{"csv":"name,nickName,birthdate,anniversary,isAccurate,relation,email\nAda,,1815-12-10,,true,history,"}' # AI command curl -X POST "$VAL_URL/ai" \ -H "authorization: Bearer $ADMIN_TOKEN" \ -H "content-type: application/json" \ -d '{"query":"Dry-run adding Ada Lovelace with birthday 1815-12-10","dryRun":true}'
Person fields: id, name, nickName, birthdate, anniversary, isAccurate, relation,
email.
Dates are stored as YYYY-MM-DD.
Leap-day birthdays and anniversaries are reminded on Feb 28 in non-leap years.
Use the uApps vtx wrapper. Local discovery and validation are safe. Source the toolbox shell first
so the uApps plugin exposes vtx.
[[ -n "$ZSH_VERSION" && -z "$TBX_SHELL_INSTALLED" ]] && source "$HOME/.zshrc" 2>/dev/null || true cd "$TOOLBOX/exp/vals/reminders" vtx ls vtx map vtx check vtx env
This checks the cron entrypoint without sending Telegram messages. A real dry run requires invoking
the linked Val Town val with DRY_RUN=true or ?dryRun=1.
cd "$TOOLBOX/exp/vals/reminders" DRY_RUN=true LOOKAHEAD_DAYS=8 deno check --config deno.json reminder-check.cron.ts
These mutate Val Town remotely and require explicit approval for the turn:
vtx newvtx linkvtx pushvtx syncvtx apply-types- raw mutating
vtcommands