A personal order-tracking dashboard: harvests purchase/shipping emails from
Gmail and iCloud over IMAP, cross-references Discord cook-bot checkout
successes, and keeps shipment status live via a carrier tracking API.
Runs locally (FastAPI + SQLite + APScheduler backend, Vite + React +
Tailwind frontend), viewed at http://localhost:8000.
Scope for v1: purchases + tracking only. Sales/profit tracking
(StockX/GOAT/eBay) is a future phase — the data model leaves room for it
(orders.disposition) but nothing is built on it yet.
hypetrack/
├── backend/ FastAPI + SQLAlchemy/SQLite + APScheduler
│ ├── app.py FastAPI app; serves the API and, once built, frontend/dist
│ ├── config.py ~/.config/hypetrack/config loader (chmod-600 enforced)
│ ├── models.py / db.py SQLAlchemy schema + engine
│ ├── ingest/ IMAP client, email sync, Discord bot
│ ├── parsing/ classifier, extractors, retailer parsers, LLM fallback
│ ├── tracking/ Ship24 provider + polling
│ ├── matching.py order <-> Discord success <-> shipment linking
│ └── scheduler.py APScheduler jobs (email sync/15min, tracking/4h) + Discord task
└── frontend/ Vite + React + Tailwind — dashboard/detail/review/settings
Every external credential is optional. Missing one disables that feature with a banner in the UI — the app always boots and shows whatever data it has.
- Python 3 (tested against the system
/usr/bin/python33.9.6 — the codebase is written to stay 3.9-compatible). - Node.js + npm, only to build the frontend. If Node isn't installed,
the backend and JSON API still run fully;
/returns a short JSON message telling you the UI isn't built instead of the dashboard.
Real config lives at ~/.config/hypetrack/config, chmod 600, KEY=value
lines. The app creates a template there automatically on first boot if
the file doesn't exist yet (migrating ICLOUD_EMAIL/ICLOUD_APP_PASSWORD
from ~/.config/icloud/credentials if present). See config.example in
this repo for the full list of keys and comments. The app never
overwrites an existing config file — edit it directly to fill in values.
A commented-out line (# GMAIL_APP_PASSWORD=) is treated as "not set";
uncomment and fill it in to enable that feature.
Gmail IMAP requires a 16-character app password, not your real password
(and won't work at all if 2-Step Verification isn't on — turn it on
first at myaccount.google.com/security if needed):
- Go to https://myaccount.google.com/apppasswords.
- Sign in, name the app password something like "HypeTrack", click Create.
- Copy the 16-character password (spaces don't matter).
- In
~/.config/hypetrack/config, set:GMAIL_EMAIL=nixmendiola@gmail.com GMAIL_APP_PASSWORD=<the 16-character password> - Also confirm IMAP is enabled: Gmail → Settings → Forwarding and POP/IMAP → Enable IMAP.
- Go to https://discord.com/developers/applications → New Application → name it (e.g. "HypeTrack Bot").
- Bot tab → Add Bot. Under Privileged Gateway Intents, enable Message Content Intent (required to read cook-bot embeds).
- Copy the bot token (Bot tab → Reset Token if needed) into
DISCORD_BOT_TOKENin the config file. - OAuth2 → URL Generator: scope
bot, permissions Read Messages/View Channels and Read Message History (no write permissions needed — this bot is read-only by design). Open the generated URL and invite it to your server. - In Discord, enable Developer Mode (User Settings → Advanced), right-click
the channel the cook bot posts successes to, "Copy Channel ID", and set
DISCORD_CHANNEL_IDin the config file.
- Sign up at https://www.ship24.com/tracking-api (free tier available).
- Generate an API key from the dashboard.
- Set in the config file:
(17track could be added later behind the same
TRACKING_API_KEY=<your key> TRACKING_PROVIDER=ship24TrackingProviderinterface inbackend/tracking/provider.pyif preferred.)
Get a key at https://console.anthropic.com/ → Settings → API Keys, set
ANTHROPIC_API_KEY in the config file. Without it, only the deterministic
parsers (Tiers 0-2) run; unrecognized emails land in the review queue
instead of being auto-extracted.
cd hypetrack
./run.shThis creates the venv and installs backend dependencies on first run,
applies Alembic migrations, and starts uvicorn on
http://localhost:8000. Ctrl-C to stop.
To build the frontend (requires Node):
cd frontend
npm install
npm run buildThen restart ./run.sh — backend/app.py mounts frontend/dist/ and
serves it at / once it exists.
For frontend development with hot reload instead of a static build:
cd frontend && npm install && npm run devThis starts Vite's dev server (typically :5173) which proxies /api/*
to the backend — run ./run.sh in another terminal first.
The scheduler runs an email sync automatically 15 minutes after startup and every 15 minutes thereafter, and a tracking poll every 4 hours. To force an immediate sync (e.g. right after first setup), either click Sync now in the header once the UI is built, or:
curl -X POST http://localhost:8000/api/syncThe first sync per account does a ~3 month backfill; subsequent syncs are incremental (tracked per-account/folder via IMAP UID watermarks).
./venv/bin/pip install -r backend/requirements-dev.txt # adds pytest
./venv/bin/python -m pytest backend/tests/ -vUnit tests cover the classifier, regex extractors, all four retailer parsers (against synthetic fixture HTML), the LLM fallback and Ship24 tracking provider (both against mocked clients — no live keys needed to test them), Discord embed parsing (three field-naming conventions, against synthetic embed JSON), matching, and the config loader's chmod-600 refusal.
- IMAP: every
select()call passesreadonly=True; every fetch usesBODY.PEEK[...]— the app never marks mail read, moves, or deletes anything. (grep -rn "readonly=True" backend/ingest/imap_client.pyto verify — there's exactly oneselect()call site.) - Discord: the bot only reads message/embed history — it never sends, reacts, or deletes.
- Secrets: never logged, never committed.
backend/data/,*.db, and any config/credential paths are gitignored —git grep -i "password\|token\|api_key"before committing to double-check.
- Sales/profit tracking (StockX/GOAT/eBay) — only the
orders.dispositioncolumn exists, unused. - Auth, multi-user, web deployment — see notes below.
- Writing to mailboxes or Discord.
- Parsing a mailbox's full history — only the bounded candidate search window (subject keywords / known retailer domains, ~3 months).
Not needed to run this locally — kept here for when/if that day comes:
- Database: SQLAlchemy + Alembic are already in place specifically so
swapping SQLite for Postgres is a
DATABASE_URLchange inbackend/db.pyplus running the existing migrations against the new database — no model/query rewrites expected. - Secrets: swap the
~/.config/hypetrack/configfile loader for environment variables (backend/config.py'sConfigclass already reads from a plain dict —os.environwould work as-is). - Process model: the Discord bot and APScheduler jobs currently run inside the FastAPI process; a multi-instance web deployment would need to pull them into a separate worker process (or use a leader-election lock) so jobs don't run once per instance.
- Auth: none exists today — this is a single-user local app. Add a reverse proxy with basic auth or a real auth layer before exposing it to the internet.
- Containerization: a
Dockerfileisn't included yet; the natural shape is a single image runninguvicorn backend.app:app, built frontend baked into the image or served from a CDN/static host separately.