Public
A personal app to remind of important events in life
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

Reminders

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/.

Files

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

Environment

Set these in the Val Town UI.

VariableRequiredPurpose
ADMIN_USERNAMEyesVal Town username allowed to see the admin UI
ADMIN_TOKENyesBearer token for direct API routes
OAUTH_STATE_ENCRYPTION_KEYyesRandom secret for Val Town OAuth state cookies
TELEGRAM_BOT_TOKENyesTelegram BotFather token
TELEGRAM_CHAT_IDyesDestination chat or channel id
LOOKAHEAD_DAYSnoDays to scan, default 8
DRY_RUNnotrue 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.

Admin UI

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, and send-telegram actions

Schema

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, email
  • events: 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.

API

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.

Local Validation

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

Cron Type Check

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

Remote Commands

These mutate Val Town remotely and require explicit approval for the turn:

  • vtx new
  • vtx link
  • vtx push
  • vtx sync
  • vtx apply-types
  • raw mutating vt commands