e0ba54f2c3c6330156645b936288b4cc01c1cca8
Joel Discord Bot
A Discord bot with AI-powered responses and message tracking.
Quick Start
# Install dependencies
bun install
# Set environment variables
cp .env.example .env
# Edit .env with your tokens
# Run in development mode
bun run dev
# Run in production
bun run start
Project Structure
See src/README.md for detailed architecture documentation.
src/
├── core/ # Bot client, config, logging
├── commands/ # Slash commands
├── events/ # Discord event handlers
├── features/ # Feature modules (Joel AI, message logging)
├── services/ # External services (AI providers)
├── database/ # Database schema and repositories
└── utils/ # Shared utilities
Environment Variables
| Variable | Description |
|---|---|
DISCORD_TOKEN |
Discord bot token |
DISCORD_CLIENT_ID |
Discord application client ID |
DISCORD_CLIENT_SECRET |
Discord application client secret |
OPENROUTER_API_KEY |
OpenRouter API key for AI |
FAL_API_KEY or FAL_KEY |
Fal API key for image generation |
AI_CLASSIFICATION_FALLBACK_MODELS |
Comma-separated fallback model IDs for classification requests |
KLIPY_API_KEY |
Klipy API key for GIF search (optional) |
ELEVENLABS_API_KEY |
ElevenLabs API key for voiceover |
ELEVENLABS_VOICE_ID |
Default ElevenLabs voice ID (optional) |
ELEVENLABS_MODEL |
ElevenLabs model ID (default: eleven_multilingual_v2) |
DATABASE_PATH |
SQLite file path (default: ./data/db.sqlite3) |
WEB_PORT |
Port for web dashboard (default: 3000) |
WEB_BASE_URL |
Base URL for web dashboard |
SESSION_SECRET |
Secret for session encryption |
Data Persistence
- Bot options and other runtime state are stored in SQLite.
- Keep
DATABASE_PATHon persistent storage so updates/redeploys do not reset settings. - Backward compatibility: if
DATABASE_PATHis unset andsrc/database/db.sqlite3exists, it is used automatically. - Recommended one-time migration:
mkdir -p data && cp src/database/db.sqlite3 data/db.sqlite3
- Docker example:
docker run -d \
--name joel-discord \
-v joel_data:/data \
--env-file .env \
your-image:latest
Docker
Build the image:
docker build -t ghcr.io/your-org/joel-bot:latest .
Run it locally:
docker run -d \
--name joel-bot \
-p 3000:3000 \
-v joel_data:/data \
--env-file .env \
ghcr.io/your-org/joel-bot:latest
The container now starts in production mode and does not enable the CSS file watcher.
k3s
The repo includes starter manifests in k8s/deployment.yaml, k8s/secret.example.yaml, and k8s/ingress.example.yaml.
Typical flow:
# Build and push the image
docker build -t ghcr.io/your-org/joel-bot:latest .
docker push ghcr.io/your-org/joel-bot:latest
# Create your secret manifest from the example and fill in real values
cp k8s/secret.example.yaml k8s/secret.yaml
# Apply namespace, PVC, deployment, and service
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/secret.yaml
# Optional if you want the web UI exposed through Traefik
kubectl apply -f k8s/ingress.example.yaml
Notes:
- Update the image in
k8s/deployment.yamlto your registry path. - Set
WEB_BASE_URLin the secret to the public HTTPS URL you expose through ingress. - The deployment mounts
/dataon a PVC and stores SQLite at/data/db.sqlite3. - k3s usually provisions the PVC automatically via the default
local-pathstorage class.
Scripts
| Script | Description |
|---|---|
bun run dev |
Development with hot reload |
bun run start |
Production start |
bun run build |
Build for production |
bun run db:generate |
Generate DB migrations |
bun run db:migrate |
Run DB migrations |
bun run db:studio |
Open Drizzle Studio |
bun run typecheck |
Type check without emit |
Languages
TypeScript
82.1%
CSS
11%
JavaScript
6%
Nix
0.4%
Dockerfile
0.3%
Other
0.2%