Eve is a Discord bot for small-to-mid community servers (~20-100 members) that turns event announcements into organized threads with one click. When someone posts an event in a watched channel and mentions a configured role, Eve parses the message, shows the poster an editable preview, and on confirmation creates a thread, a Discord Scheduled Event, and links everything together. It also tracks emoji reactions to auto-pull interested users into the thread.
- One-click event creation — Post a message mentioning the trigger role, click the setup button, and Eve handles the rest
- Smart event parsing — Uses spaCy NLP (dependency parsing + NER) and parsedatetime to extract dates, times, titles, and locations from natural language
- Ephemeral setup flow — Only the event creator and mods see the setup UI; the channel stays clean
- Discord Scheduled Events — Automatically creates a linked scheduled event with countdown
- Reaction tracking — React with the RSVP emoji and get auto-added to the event thread (works even if you reacted before the event was created)
- "Interested" tracking — Click "Interested" on the Discord Scheduled Event and get auto-added to the thread
- Image support — Auto-detects images from the original message for the scheduled event cover
- Voice channel events — Support for both address-based and voice channel locations
- Interactive setup wizard — Configure channels, role, timezone, and emoji in one screen
All commands are subcommands under /eve and require the ManageGuild permission.
| Command | Description |
|---|---|
/eve setup |
Interactive setup wizard |
/eve channel add #channel |
Register an event channel |
/eve channel remove #channel |
Unregister an event channel |
/eve channel list |
Show all watched channels |
/eve role set @role |
Set the trigger role |
/eve emoji set |
Set the default RSVP emoji |
/eve timezone set |
Set the server default timezone (25 major timezones) |
/eve status |
Show current configuration |
- Someone posts an event message in a watched channel, mentioning the trigger role
- Eve replies with a detection message that pings the poster: "@user and mods can set this up"
- The poster (or a mod) clicks the setup button — the detection message is deleted and a private preview appears with parsed event details
- They can edit the title, date, time, location, timezone, and image
- On confirm, Eve creates a Discord Scheduled Event, a thread on the original message, and links them together. If event or thread creation fails, the entire flow fails with a clear error message
- Reactions with the RSVP emoji auto-add users to the thread (including any reactions placed before the event was created)
- Users who click "Interested" on the Discord Scheduled Event are also auto-added to the thread
Right-click any message → Apps → Create Event to create an event manually. Eve shows the same private preview and setup flow — no public "Event detected" message is posted. Works on any message in the server, regardless of channel or role.
- When a tracked event message is deleted and the scheduled event still exists, Eve posts: "A tracked event message was deleted — Clean up?"
- Click "Clean Up" — the prompt is deleted and a delete modal opens showing event details, with an optional thread deletion checkbox
- Click "Dismiss" to remove tracking but keep the thread and scheduled event intact
- The cleanup prompt auto-deletes after 24 hours if no action is taken
- If the scheduled event was already deleted before the message, Eve silently cleans up the database — no prompt needed
Right-click a tracked event message → Apps → Delete Event. A modal appears showing event details and links as read-only text, a thread deletion checkbox, and a confirmation checkbox. On confirm, Eve deletes the scheduled event, the original message, optionally the thread, and removes tracking from the database. Only the event creator or users with Manage Events permission can use this.
- Docker and Docker Compose (recommended), or Node.js 22+ and Python 3.12+
- A Discord bot token with the Message Content privileged intent enabled
- Create a Discord application at https://discord.com/developers/applications
- Create a bot and enable the Message Content privileged intent
- Generate an invite link with these permissions:
- Send Messages
- Create Public Threads
- Manage Threads
- Add Reactions
- Read Message History
- Manage Events
- Use External Emojis
Eve is two containers: the bot, and an NLP sidecar that parses event details out of message text. The bot will not start until the sidecar reports healthy.
git clone https://github.com/UmushiUmushi/eve.git
cd eve
cp .env.example .env # fill in DISCORD_TOKEN and DISCORD_CLIENT_ID
docker compose up -d --wait--wait blocks until both containers pass their health checks, so the command
fails loudly rather than leaving you to discover a broken stack later. First
build takes a few minutes — the sidecar downloads a spaCy language model.
docker-compose.prod.yml pulls eve and eve-nlp from GitHub Container
Registry, so the server never needs a source checkout.
It does not declare a Watchtower container,
on the assumption that one already runs on the host. Both services are labelled
com.centurylinklabs.watchtower.enable=true, so they are picked up whether or
not that instance uses --label-enable. Since both declare real health checks,
Watchtower waits for a replacement to pass its check before retiring the
container it replaces.
# On the server: only these two files plus .env are needed
curl -O https://raw.githubusercontent.com/UmushiUmushi/eve/main/docker-compose.prod.yml
cp .env.example .env # fill in your token
docker compose -f docker-compose.prod.yml up -d --waitdocker compose ps # both services should read (healthy)
docker compose logs -f eve # structured JSON logs
docker compose exec eve node dist/health-check.jsThen, in Discord:
- Run
/eve setupand pick a watched channel, a trigger role and a timezone - Post a message in that channel mentioning the trigger role, e.g.
Game night tomorrow at 7pm at Central Park @Events - Eve should reply with a detection prompt within a second or two
If nothing happens, check the logs — Eve reports at startup whether the NLP service is reachable, and logs every parse failure with the guild and message ID.
git clone https://github.com/UmushiUmushi/eve.git
cd eve
cp .env.example .env
# Set NLP_SERVICE_URL=http://localhost:24000 for a locally-run sidecar
# Terminal 1 — the NLP service
cd nlp-service
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m spacy download en_core_web_sm
uvicorn main:app --port 24000
# Terminal 2 — the bot
npm install
npm run build
npm start
better-sqlite3is a native module compiled against a specific Node ABI. If you seeNODE_MODULE_VERSIONerrors, runnpm rebuild better-sqlite3. The repo pins a Node version in.nvmrc;nvm useavoids the problem.
| Variable | Required | Default | Description |
|---|---|---|---|
DISCORD_TOKEN |
Yes | — | Bot token from the Discord developer portal |
DISCORD_CLIENT_ID |
Yes | — | Application ID, used for command registration |
NLP_SERVICE_URL |
No | http://eve-nlp:24000 |
URL of the NLP sidecar |
DISCORD_DEV_GUILD_ID |
No | — | Register commands to one guild for instant updates while developing |
LOG_LEVEL |
No | info |
Minimum level to emit; each includes everything more severe. trace, debug, info, warn, error, fatal — left is more output |
EVE_DB_PATH |
No | ./data/eve.db |
SQLite location. Tests use :memory: |
EVE_HEARTBEAT_PATH |
No | ./data/heartbeat.json |
Liveness file read by the health check |
EVE_DEBUG |
No | — | Set on the sidecar to expose its /debug parse inspector |
Slash commands are registered automatically on bot startup.
The database is a single SQLite file in the eve-data volume. It runs in WAL
mode, so copying the .db file alone can capture a torn state — use SQLite's
backup command:
docker compose exec eve node -e "
const db = require('better-sqlite3')('/app/data/eve.db');
db.backup('/app/data/backup.db').then(() => console.log('done'));
"
docker compose cp eve:/app/data/backup.db ./eve-backup.dbnpm run dev # hot-reload development server
npm run build # type-check and compile
npm test # unit tests
npm run test:coverage # unit tests with coverage thresholds
npm run lint # lint
npm run check # lint + build + coverage, the same gates CI runs
cd nlp-service && python -m pytest # NLP service testsSchema lives in src/database/migrations/ as plain SQL, applied automatically
at startup before the bot connects to Discord.
npm run db:status # what's applied and what's pending
npm run db:migrate # apply pending migrations
npm run db:rollback # reverse the most recent migrationMigrations are checksummed by their SQL. Never edit one that has been applied — add a new one. Never delete one; the runner refuses to start if a recorded migration has vanished from the code.
- TypeScript with discord.js v14
- SQLite via better-sqlite3 (no external database needed), with SQL migrations
- pino for structured JSON logging
- Python FastAPI sidecar for NLP processing
- spaCy for NER and dependency parsing (title/location extraction)
- parsedatetime for structured date/time resolution
- Vitest and pytest, with coverage thresholds enforced in CI
- Docker multi-stage builds, health checks on both services
- Pluggable LLM-based event parser (local Ollama, OpenAI, Anthropic)
- Recurring events
- Event reminders
- Web dashboard
- Multi-role support
- Event templates
- Multi-day events
- Edit event flow
- 'Remove from thread' handlers