3.1 KiB
Lunch Notifier
Single-file Flask app that scrapes the daily Mariposa (Kyrene school
district) lunch menu and texts it to a list of phone numbers via
email-to-SMS carrier gateways (e.g. 4805551234@vtext.com for Verizon).
Runs as one Docker container on the user's Unraid home server.
Architecture
app.py— everything: Flask web UI/API, APScheduler-style job scheduling (schedulelibrary), and the email-to-SMS send logic (smtplibvia Gmail, app password auth). The HTML/CSS/JS frontend is embedded as a Python string constant (HTML) rather than a separate template file — no build step, no template engine.scraper.py— fetches and parses the school's published lunch menu.scheduler.py— day-of-week/time scheduling helpers used byapp.py.- Settings (phone numbers, send time, days, vacation date ranges) persist
to
/data/settings.json, not environment variables — env vars (PHONE_NUMBER,RUN_TIME,RUN_DAYS) are defaults used only the very first time, before a settings file exists. - Vacation ranges suppress sends without disabling the schedule — checked
fresh on every run via
is_on_vacation().
Local development
pip install -r requirements.txt
python3 app.py
Needs GMAIL_USER and GMAIL_APP_PASSWORD env vars to actually send
(a Gmail app password, not the account password — required since Gmail
disallows plain SMTP auth with the real password). Visit
http://localhost:2323.
Deployment
Target: Unraid server, container path /mnt/user/appdata/lunch-notifier,
built from Dockerfile/docker-compose.yml in this repo. Container name
Lunch-Notifier, port 2323. No reverse proxy — accessed directly via
http://<server-ip>:2323 on the LAN.
Deploy mechanism: Forgejo push-to-deploy, same pattern as the other
three projects on this box (card-grader, n64-tracker, arr-summary) —
see card-grader's CLAUDE.md for the full pipeline mechanics (runner
config, HTTPS-not-SSH git auth, known tradeoffs). In short: push to main
on the forgejo remote (https://git.hippofam.com/ninja_hippo/lunch-notifier.git)
triggers .forgejo/workflows/deploy.yml, which syncs into
/mnt/user/appdata/lunch-notifier (excluding .git, .env, data/) and
runs docker compose up -d --build.
Persistent state, never touched by deploys: data/settings.json (the
volume-mounted /data inside the container) holds phone numbers, schedule,
and vacation ranges — this is live user data, not deploy-managed. .env
holds GMAIL_USER/GMAIL_APP_PASSWORD/PHONE_NUMBER etc. and is
server-only, gitignored, never committed.
Migration note (2026-08-25): this container originally used a Docker
named volume (lunch-notifier_lunch-settings) instead of a bind mount.
That meant Unraid's Appdata Backup plugin — which only knows about
/mnt/user/appdata and /mnt/cache/appdata as backup sources — silently
skipped it entirely; the settings data was never included in any backup.
Migrated to a bind mount (./data:/data) with the existing settings copied
over first and verified intact before the old named volume was deleted.