π Summary
A self-hosted toolchain that syncs Discord channels to local storage, exposes a REST API for reading and sending messages, and notifies you when something new happens β fully automated on a cron schedule.
Built to solve a simple need: don’t miss messages across 12+ channels, even when you’re not watching Discord. It runs 24/7 inside a Docker container, syncing every 5 minutes, and only pings you when there’s actually something new.
π― Goals
- β Automatically sync messages from all Discord channels to local JSONL files
- β Expose a REST API to read, search, and send messages programmatically
- β Fire notifications only on new messages β no spam, no noise
- β Run 24/7 with zero manual intervention (Docker + cron)
π§° Stack
- Python + Flask β REST API (health, channels, messages CRUD)
- JSONL β lightweight append-only message storage (one file per channel)
- Discord API (self-bot) β message reading and sending
- Docker β containerized deployment with docker-compose
- Hermes Agent cron β 5-minute watchdog cycle for new message detection
π§ How it works
Discord API βββ main.py (sync loop) βββ chatlogs/messages_in_*.jsonl
β
βΌ
app.py (Flask API)
βββββββββββββββββββββββ
β GET /health β
β GET /channels β
β GET /channels/X/ β
β messages β
β POST /channels/X/ β
β send β
βββββββββββββββββββββββ
β
watchdog (cron/5min)
ββ notifies on new messages
- Sync loop (
main.py) polls Discord every 5 minutes and appends new messages tomessages_in_{channel}.jsonl - Flask API (
app.py) serves these files over HTTP β you can curl for the latest messages, or send replies via POST - Watchdog (cron job) compares last message IDs against stored state; if something is new, it alerts you directly β otherwise stays silent (zero token cost)
π Lessons Learned
- Append-only storage is underrated. JSONL gives you fault-tolerant, grep-able message history without a database. If the container crashes, you lose nothing.
- Watchdog silence matters as much as alerts. A cron job that only speaks when there’s news is more useful than one that says “nothing new” every 5 minutes. The
no_agent: Truepattern (script runs, stdout decides if anything gets delivered) saves tokens and attention. - Apostrophes are the enemy of inline curl. Learned the hard way: always write your JSON payload to a temp file and use
-d @fileinstead of-d '...'. Shell eats single quotes faster than you think. - Stateful vs stateless matters even in simple tools. The watchdog stores last-seen message IDs per channel in a state file β without it, every run would re-notify old messages.