System overview
Volvox.Bot is a Node.js 22 application built on discord.js v14. It connects to Discord’s gateway, handles events through a module system, persists state in PostgreSQL, caches hot data in Redis, and exposes a REST + WebSocket API consumed by a Next.js web dashboard.Components
Entry point — src/index.js
src/index.js is the single entry point for the bot process. On startup it:
- Imports the Sentry SDK first (before all other modules) so it can instrument them.
- Initialises the database pool via
initDb()and Redis viainitRedis(). - Loads configuration from PostgreSQL (falling back to
config.json) vialoadConfig(). - Registers config change listeners for hot-reload.
- Starts background services: conversation cleanup, triage, tempban scheduler, warning expiry, announcement scheduler, and GitHub feed.
- Loads all slash commands from
src/commands/into adiscord.jsCollection. - Calls
client.login()to authenticate with Discord. - Starts the REST API server on
BOT_API_PORT(default3001).
discord.js client is initialised with all required gateway intents and a global allowedMentions: { parse: ['users'] } guard that prevents the bot from ever producing @everyone or @here pings, even if AI-generated content contains them.
Feature modules — src/modules/
Each module owns a discrete feature area and exports start/stop lifecycle functions called from src/index.js.
| Module | Responsibility |
|---|---|
modules/ai.js | Per-channel conversation history, Claude completions, feedback tracking |
modules/triage.js | Two-step message classifier (fast Haiku pass → Sonnet responder) |
modules/moderation.js | Warn/kick/ban/tempban/timeout, tempban scheduler |
modules/warningEngine.js | Warning expiry scheduler, escalation evaluation |
modules/events.js | Central event handler registration (messageCreate, interactionCreate, etc.) |
modules/config.js | Config load/save, live /config command integration |
modules/welcome.js | Dynamic welcome messages with template variable substitution |
modules/scheduler.js | Scheduled announcement delivery |
modules/botStatus.js | Rotating bot presence messages |
modules/memory.js | Long-term user memory via mem0 |
modules/githubFeed.js | GitHub event feed to a Discord channel |
modules/roleMenuTemplates.js | Built-in reaction-role menu template seeding |
modules/optout.js | Per-user opt-out preferences for memory features |
Slash commands — src/commands/
Each file in src/commands/ exports a data object (a discord.js SlashCommandBuilder) and an execute(interaction) async function. Commands are loaded at startup by src/utils/loadCommands.js and registered with Discord’s API by src/deploy-commands.js.
Command access is controlled by the permissions.allowedCommands map in config.json, which maps command names to one of "everyone", "moderator", or "admin".
REST API + WebSocket — src/api/
An Express 5 server starts after the bot logs in. It provides:
/api/v1/health— health check endpoint (also used by the DockerHEALTHCHECKdirective)/api/v1/config— read and write bot configuration (gated byBOT_API_SECRET)/api/v1/audit-log— paginated audit log with filtering/api/v1/analytics— message activity, command usage, AI feedback metrics/api/v1/logs— streamed log access via WebSocket/api/v1/auth/discord— Discord OAuth2 flow for dashboard login
src/api/utils/configAllowlist.js. Keys not on the allowlist are rejected to prevent arbitrary config mutation from the dashboard.
Web dashboard — web/
A Next.js application that communicates with the bot’s REST API using a shared BOT_API_SECRET. It provides:
- Dashboard overview — server stats, bot health, recent activity
- Configuration editor — live config editing backed by
/api/v1/config - Analytics — message activity, voice time, AI feedback charts, PDF export
- Audit log — complete mod action history with CSV/JSON export and WebSocket streaming
- Conversation viewer — browse and search AI conversation history
web/src/lib/page-titles.ts using createPageMetadata().
Data stores
| Store | Purpose |
|---|---|
| PostgreSQL 17 | Config, conversation history, audit log, analytics, scheduled announcements, warnings, restart tracking |
| Redis 7 | Config cache, Discord API response cache, rate-limit counters, session storage |
REDIS_URL is not set. PostgreSQL is required for persistence; without DATABASE_URL, the bot runs from config.json only with no state persistence.
Event flow
Every Discord event follows the same path through the system:modules/events.js registers all discord.js event listeners and routes each event to the appropriate module handler. Slash command interactions (interactionCreate) are handled by looking up the command name in client.commands and calling its execute() function.
AI chat data flow
When a user mentions the bot in a channel:messageCreatefires →events.jsroutes to the AI handler inmodules/ai.js.- The handler checks whether the channel is on the
ai.blockedChannelIdslist. If so, it silently ignores the message. - It loads the per-channel conversation history (up to
ai.historyLengthmessages, withinai.historyTTLDays). - Optionally, up to
memory.maxContextMemorieslong-term memories are retrieved from mem0 and prepended to the context. - The assembled context is sent to Claude using the Anthropic SDK. The system prompt is defined in
config.ai.systemPrompt. - The response is streamed back to Discord and appended to the conversation history.
- The updated history is persisted to PostgreSQL.
- If
ai.feedback.enabledistrue, thumbs-up/down reaction listeners are registered on the response message and feedback is recorded for dashboard analytics.
Moderation data flow
For the AI triage / auto-moderation path:- Every non-bot message in monitored channels is buffered by
modules/triage.js. - On each tick (
triage.defaultIntervalms), buffered messages are sent to the classifier model (triage.classifyModel, defaultclaude-haiku-4-5) with a low per-message budget (triage.classifyBudget). - If the classifier flags a message as needing a response or moderation action, it is passed to the responder model (
triage.respondModel, defaultclaude-sonnet-4-6). - The responder either posts a reply in the channel or triggers a moderation action (warn, delete, flag to
triage.moderationLogChannel). - All moderation actions are written to the audit log in PostgreSQL and optionally posted to
moderation.alertChannelId.
/warn, /ban, /kick, etc.):
- The slash command handler validates the executor’s permissions against
permissions.allowedCommands. - Protected roles (
moderation.protectRoles) are checked — admins and mods cannot be actioned. - The action is executed on Discord (kick, ban, timeout, etc.).
- A DM notification is sent to the target user if
moderation.dmNotifications.<action>istrue. - The event is written to the PostgreSQL audit log and streamed to active WebSocket connections in the dashboard.
Project structure
src
index.js
deploy-commands.js
db.js
redis.js
logger.js
sentry.js
modules
ai.js
triage.js
events.js
moderation.js
config.js
welcome.js
commands
api
utils
web
migrations
config.json
docker-compose.yml
Dockerfile
package.json