The core Discord bot for "La Taverne Dorée du Gnome" - Featuring AI conversations, music streaming, League of Legends integration, RPG economy system, and casino games.
The bot is built with Discord.js v14 and TypeScript in strict mode, providing 30 slash commands across 6 major categories. It integrates with multiple external APIs (Mistral AI, OpenAI Whisper, Riot Games, OpenLibrary) and uses MongoDB for persistent data storage.
Key Stats:
- 30 slash commands with full TypeScript typing
- 239 unit tests with 100% pass rate
- 6 service modules for background operations
- MongoDB integration for XP, economy, and user data
- Voice support for music streaming and AI voice conversations
- Node.js v22.17.0 or higher
- MongoDB (local or Atlas connection)
- FFmpeg (for audio processing)
- YT-DLP (for YouTube/SoundCloud streaming)
- Discord Bot Token with necessary permissions
# Install dependencies
npm install
# Copy environment template
cp .env.example .env
# Edit .env with your tokens (see Configuration section)
# Build TypeScript
npm run build
# Deploy slash commands to Discord
npm run deploy
# Start the bot
npm run dev # Development mode (ts-node with hot reload)
npm start # Production mode (compiled JavaScript)npm test # Run all 239 tests
npm run test:watch # Watch mode with auto-reload
npm run test:coverage # Generate coverage reportCreate a .env file in the /bot/ directory with the following variables:
# Discord Configuration (Required)
DISCORD_TOKEN=your_discord_bot_token
DISCORD_CLIENT_ID=your_discord_client_id
DISCORD_GUILD_ID=your_guild_id_for_testing
# AI Services (Required for AI commands)
MISTRAL_API_KEY=your_mistral_api_key # For /mistral and /conversation
OPENAI_API_KEY=your_openai_api_key # For /listen (Whisper STT)
# Gaming APIs (Required for LoL commands)
RIOT_GAMES_API_KEY=your_riot_api_key # For /lol-* commands
# Database (Required)
MONGODB_URI=mongodb://localhost:27017/gnome
# Optional: Backend Casino Integration
CASINO_API_URL=http://localhost:3001 # If using backend casino serverUse .env.example as a template with all available options.
| Command | Description | Cooldown | Deferred |
|---|---|---|---|
/mistral <prompt> |
Single-shot question to Mistral AI | 5s | ✅ |
/conversation <prompt> |
Start 15-minute thread conversation with AI | 5s | ✅ |
/listen start|stop |
Voice-to-voice AI conversation (Whisper STT + TTS) | 10s | ❌ |
Features:
- Custom Personality: "Le Gnome" - sarcastic gaming assistant
- Thread History: Conversation context maintained from Discord thread messages
- Voice Support: Real-time transcription and TTS responses in voice channels
| Command | Description | Support |
|---|---|---|
/play <query|url> |
Play music from search or URL | YouTube, SoundCloud |
/playlist <url> |
Load entire playlist | YouTube, SoundCloud |
/pause |
Pause current playback | - |
/resume |
Resume paused music | - |
/skip |
Skip to next song | - |
/stop |
Stop music and disconnect | - |
/queue [page] |
Show paginated queue (10 songs/page) | - |
/nowplaying |
Display current song with thumbnail | - |
/loop |
Toggle loop mode for current song | - |
Implementation:
- Service:
services/musicService.tsmanages queue and playback - Audio Library:
play-dlfor YouTube/SoundCloud streaming - Voice:
@discordjs/voicefor Discord voice channel integration - YT-DLP: External dependency for YouTube extraction
| Command | Description | API |
|---|---|---|
/lol-stats <player> <tag> |
Player profile + ranked stats | Riot API (EUW) |
/lol-matches <player> <tag> [count] |
Match history (paginated) | Match-v5 |
/lol-lastgame <player> <tag> |
Last game with AI analysis | Match-v5 + Mistral |
/lol-rotation |
Weekly free champion rotation | Champion-v3 |
Features:
- Regional Support: EUW platform with Europe routing
- Data Dragon: Static champion data and images
- AI Analysis: Mistral-powered performance insights for last game
- Rich Embeds: Color-coded by rank tier with champion icons
| Command | Description | Rewards |
|---|---|---|
/rank [user] |
Display level and XP progress | 5-15 XP per message |
/balance [user] |
Show coins, XP, and level | Level × 50 coins on level-up |
/leaderboard |
Server top 10 by level/XP | - |
/daily |
Daily coin reward | 50 + (level × 10) coins |
/give <user> <amount> |
Transfer coins to another user | - |
XP System:
- Service:
services/xpTracking.tshandles message tracking - Rate: 5-15 XP per message with 60-second cooldown
- Level Formula:
level = floor(XP / 100) - Level-up Bonus: User receives
level × 50coins on level-up - Database: MongoDB
userscollection viadatabase/db.ts
| Command | Description | Min Bet | RTP |
|---|---|---|---|
/slots <bet> |
Slot machine with emoji symbols | 10 coins | ~85% |
/blackjack <bet> |
Classic blackjack vs dealer | 10 coins | ~99% |
/roulette <bet> <type> |
European roulette | 10 coins | ~97% |
/dice <bet> |
Two-dice rolling game | 10 coins | ~90% |
Note: These are individual Discord commands. The multiplayer casino with RPG classes is implemented in the /backend/ and /frontend/ packages (see Backend README).
| Command | Description |
|---|---|
/birthday set <day> <month> |
Register birthday (auto-announces) |
/birthday check [user] |
View user's birthday |
/birthday remove |
Delete your birthday |
/birthday list |
Show all upcoming birthdays |
/book <theme> [count] |
Get book recommendations from OpenLibrary |
/ping |
Check bot latency |
/echo <message> |
Repeat a message |
/help |
Display all available commands |
Birthday Service:
- Cron Job:
services/birthdayChecker.tschecks daily at midnight - Auto-announce: Posts birthday messages in configured channel
- Database: MongoDB
birthdayscollection
bot/
├── commands/ # 30 slash command files
│ ├── mistral.ts # AI single-shot interaction
│ ├── conversation.ts # Thread-based AI chat
│ ├── listen.ts # Voice-to-voice AI
│ ├── play.ts # Music player entry
│ ├── lol-stats.ts # League of Legends stats
│ ├── balance.ts # Economy system
│ └── ... # 24 more commands
├── services/ # Background services
│ ├── musicService.ts # Queue + playback manager
│ ├── xpTracking.ts # Message XP tracking
│ ├── birthdayChecker.ts # Daily birthday cron
│ └── ytdlpService.ts # YT-DLP wrapper
├── database/ # MongoDB ODM
│ └── db.ts # Mongoose schemas (User, Birthday)
├── types/ # TypeScript interfaces
│ └── index.ts # Command, MusicQueue types
├── __tests__/ # 27 test files (239 tests)
├── __mocks__/ # Test mocks for APIs
├── index.ts # Bot entry point
├── deploy-commands.ts # Slash command registration
├── package.json # Dependencies
├── tsconfig.json # TypeScript configuration
└── vitest.config.ts # Test configuration
Main Bot (index.ts):
- Loads all commands from
/commands/directory - Registers event handlers for
interactionCreateandmessageCreate - Manages cooldown system with
client.cooldownsCollection - Tracks XP for messages via
xpTracking.ts - Handles errors with centralized logging
Command Deployment (deploy-commands.ts):
- Standalone script to register/update slash commands with Discord API
- Run after adding/modifying commands:
npm run deploy - Uses REST API to push command definitions to Discord
All commands follow this TypeScript structure:
import { SlashCommandBuilder, CommandInteraction } from 'discord.js';
interface Command {
data: SlashCommandBuilder;
execute: (interaction: CommandInteraction) => Promise<void>;
cooldown?: number; // Optional cooldown in seconds (default: 3)
defer?: boolean; // Auto-defer for long operations
}
export const command: Command = {
data: new SlashCommandBuilder()
.setName('commandname')
.setDescription('Command description in French'),
async execute(interaction: CommandInteraction): Promise<void> {
// Command logic with full type safety
await interaction.reply('Response');
},
cooldown: 5,
defer: true // Triggers automatic deferReply() + "Done!" message
};User Schema (database/db.ts):
{
userId: string; // Discord user ID
username: string; // Discord username
xp: number; // Total experience points
level: number; // Calculated level
coins: number; // Virtual currency
lastDaily: Date; // Last /daily claim timestamp
}Birthday Schema:
{
userId: string; // Discord user ID
day: number; // Day of month (1-31)
month: number; // Month (1-12)
year?: number; // Optional year
}The bot uses Vitest for unit testing with the following setup:
- 239 tests across 27 test files
- 100% pass rate in latest run
- Mocked dependencies: Discord.js interactions, external APIs
- Coverage: 83% of critical commands tested
# Run all tests
npm test
# Watch mode (auto-reload on file changes)
npm run test:watch
# Generate coverage report
npm run test:coverage
# Run specific test file
npm test -- mistral.test.tsTests are located in __tests__/ with naming pattern <command>.test.ts:
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { command as mistralCommand } from '../commands/mistral';
describe('Mistral Command', () => {
it('should reply with AI response', async () => {
const mockInteraction = createMockInteraction({
options: { getString: vi.fn().mockReturnValue('test prompt') }
});
await mistralCommand.execute(mockInteraction);
expect(mockInteraction.deferReply).toHaveBeenCalled();
expect(mockInteraction.editReply).toHaveBeenCalledWith(
expect.stringContaining('response')
);
});
});Mocks are defined in __mocks__/ and __tests__/setup.ts:
// Mock Mistral API
vi.mock('undici', () => ({
request: vi.fn().mockResolvedValue({
body: {
json: () => ({
choices: [{ message: { content: 'Mocked AI response' } }]
})
}
})
}));
// Mock Discord voice
vi.mock('@discordjs/voice', () => ({
joinVoiceChannel: vi.fn(),
createAudioPlayer: vi.fn(),
createAudioResource: vi.fn()
}));| Category | Commands | Tested | Coverage |
|---|---|---|---|
| AI | 3 | 3 | ✅ 100% |
| Music | 8 | 3 | 🟡 37% |
| League of Legends | 4 | 4 | ✅ 100% |
| Economy | 5 | 5 | ✅ 100% |
| Casino | 4 | 4 | ✅ 100% |
| Utilities | 6 | 3 | 🟡 50% |
| Total | 30 | 22 | 73% |
- Endpoint:
https://api.mistral.ai/v1/chat/completions - Model:
mistral-small-latest - Usage:
/mistral,/conversation,/lol-lastgame - System Prompt: "Le Gnome" personality defined in command files
- Endpoint:
https://api.openai.com/v1/audio/transcriptions - Model:
whisper-1 - Usage:
/listenfor voice-to-text transcription - Formats: Opus, WebM, MP3, WAV
- Platform: EUW1 (Europe West)
- Routing: Europe region for match data
- Endpoints:
- Account-v1: Riot ID → PUUID
- Summoner-v4: Summoner data
- League-v4: Ranked stats
- Match-v5: Match history
- Champion-v3: Free rotation
- Data Dragon: Static champion images and data
- Endpoint:
https://openlibrary.org/search.json - Usage:
/bookcommand for recommendations - Data: Book metadata, authors, covers
- Driver: Mongoose ODM v8.19.2
- Collections:
users,birthdays - Connection: Via
MONGODB_URIenvironment variable
- Create command file in
commands/:
// commands/mycommand.ts
import { SlashCommandBuilder, CommandInteraction } from 'discord.js';
export const command = {
data: new SlashCommandBuilder()
.setName('mycommand')
.setDescription('My command description')
.addStringOption(option =>
option.setName('input')
.setDescription('Input description')
.setRequired(true)
),
async execute(interaction: CommandInteraction) {
const input = interaction.options.getString('input');
await interaction.reply(`You said: ${input}`);
},
cooldown: 5
};- Create test file in
__tests__/:
// __tests__/mycommand.test.ts
import { describe, it, expect, vi } from 'vitest';
import { command } from '../commands/mycommand';
describe('MyCommand', () => {
it('should echo user input', async () => {
const mockInteraction = {
options: { getString: vi.fn().mockReturnValue('test') },
reply: vi.fn()
};
await command.execute(mockInteraction);
expect(mockInteraction.reply).toHaveBeenCalledWith('You said: test');
});
});- Test and deploy:
npm test # Verify tests pass
npm run build # Compile TypeScript
npm run deploy # Register with Discord
npm run dev # Restart botCooldowns are managed automatically by index.ts:
- Default: 3 seconds
- Custom: Set
cooldownproperty in command export - Storage:
client.cooldownsCollection (in-memory) - Display: Discord timestamp format in ephemeral messages
For long-running operations (API calls), use the defer pattern:
export const command = {
// ... command definition
async execute(interaction: CommandInteraction) {
await interaction.deferReply(); // Shows "Bot is thinking..."
// Long operation (API call, processing, etc.)
const result = await fetchExternalAPI();
await interaction.editReply(result);
},
defer: true // Optional: auto-adds "Done!" message
};For voice features (music, /listen):
import { joinVoiceChannel, createAudioPlayer } from '@discordjs/voice';
// Join voice channel
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: guild.id,
adapterCreator: guild.voiceAdapterCreator,
selfDeaf: false // Required for receiving audio
});
// Receive audio (for /listen)
const receiver = connection.receiver;
receiver.speaking.on('start', (userId) => {
const audioStream = receiver.subscribe(userId);
// Process audio stream
});Bot doesn't respond to commands:
- Verify bot has
applications.commandsscope - Check Discord token is valid in
.env - Run
npm run deployto register commands - Restart bot with
npm run dev
MongoDB connection errors:
- Verify MongoDB is running:
mongod --version - Check
MONGODB_URIformat in.env - Ensure MongoDB service is started
Voice commands not working:
- Install FFmpeg:
sudo apt install ffmpeg - Install YT-DLP:
pip install yt-dlp - Verify bot has
CONNECTandSPEAKpermissions - Check voice channel is not full
API integration failures:
- Verify API keys in
.env - Check API rate limits (especially Riot Games)
- Review logs for specific error messages
The bot logs to console with different levels:
console.log('[INFO]', 'Normal operation');
console.error('[ERROR]', 'Critical error');
console.warn('[WARN]', 'Warning message');For production, consider using a logging library like winston.
{
"discord.js": "^14.15.2", // Discord API wrapper
"@discordjs/voice": "^0.19.0", // Voice channel support
"mongoose": "^8.19.2", // MongoDB ODM
"play-dl": "^1.9.7", // YouTube/SoundCloud streaming
"libsodium-wrappers": "^0.7.15", // Voice encryption (pure JS)
"node-cron": "^4.2.1", // Scheduled tasks
"undici": "^6.18.1", // HTTP client
"dotenv": "^16.4.7" // Environment variables
}{
"typescript": "^5.9.3", // TypeScript compiler
"vitest": "^3.2.4", // Test framework
"ts-node": "^10.9.2", // TypeScript execution
"@types/node": "^22.12.7" // Node.js type definitions
}ISC License - See root repository LICENSE file
- Backend Casino API - Express.js server for multiplayer casino
- Frontend Discord Activity - React UI for casino interface
- Testing Guide - Comprehensive testing documentation
- RPG System - Character classes and casino bonuses
- Main README - Complete project overview
Developed with ❤️ for "La Taverne Dorée du Gnome"