Créer un bot Discord complet avec Discord.js v14 : Le guide ultime 2025 (slash commands, modération, base de données)
Tutoriel complet pour créer un bot Discord professionnel avec Discord.js v14 : slash commands, système de modération, base de données SQLite, déploiement et bonnes pratiques. Du débutant au bot en production.
Créer un bot Discord moderne : Le tutoriel complet 2025
Discord compte plus de 500 millions d'utilisateurs en 2025, et les bots sont devenus indispensables pour gérer les serveurs, automatiser des tâches et créer des expériences interactives.
Dans ce tutoriel ultra-complet, vous allez apprendre à créer un bot Discord professionnel de A à Z avec Discord.js v14, incluant :
- ✅ Slash commands modernes (remplaçant les commandes en préfixe)
- ✅ Système de modération (kick, ban, mute, warnings)
- ✅ Base de données SQLite pour sauvegarder les données
- ✅ Déploiement en production (Railway, Render, ou VPS)
- ✅ Bonnes pratiques de sécurité et gestion d'erreurs
Que vous soyez débutant ou développeur expérimenté, ce guide vous accompagnera étape par étape. Prêt à coder ? 🚀
Prérequis : Ce qu'il vous faut avant de commencer
Connaissances techniques
- JavaScript ES6+ : Promises, async/await, arrow functions
- Node.js : Installation et bases de npm
- Ligne de commande : Commandes basiques (cd, mkdir, etc.)
Outils à installer
# Vérifier que Node.js est installé (version 18+ recommandée)
node -v
# Si pas installé, télécharger depuis https://nodejs.org/
# Installer un éditeur de code (VS Code recommandé)
# Créer un compte Discord (si pas déjà fait)
Pourquoi Discord.js v14 ?
Discord.js v14 (2023-2025) introduit :
- Support natif des slash commands (obligatoire depuis 2022)
- Builders pour les embeds et composants (syntaxe moderne)
- Performance améliorée (moins de RAM, plus rapide)
- TypeScript natif (types inclus par défaut)
Partie 1 : Créer l'application Discord et récupérer le token
Étape 1 : Créer une application sur Discord Developer Portal
- Aller sur discord.com/developers/applications
- Cliquer sur "New Application"
- Donner un nom à votre bot (ex: "MonBot")
- Accepter les conditions et créer
Étape 2 : Récupérer le token du bot
- Aller dans l'onglet "Bot" (menu de gauche)
- Cliquer sur "Reset Token" et copier le token
- ⚠️ IMPORTANT : Ne JAMAIS partager ce token publiquement
- Activer les Privileged Gateway Intents :
- ✅ PRESENCE INTENT
- ✅ SERVER MEMBERS INTENT
- ✅ MESSAGE CONTENT INTENT
Étape 3 : Inviter le bot sur votre serveur
- Onglet "OAuth2" → "URL Generator"
- Cocher "bot" et "applications.commands"
- Permissions recommandées :
- Send Messages, Embed Links, Attach Files
- Kick Members, Ban Members, Manage Messages
- Manage Roles (pour le système de mute)
- Copier l'URL générée et ouvrir dans le navigateur
- Sélectionner votre serveur de test et autoriser
Partie 2 : Initialiser le projet Node.js
Créer la structure du projet
mkdir mon-bot-discord
cd mon-bot-discord
npm init -y
npm install discord.js dotenv better-sqlite3
Structure de fichiers recommandée
mon-bot-discord/
├── commands/ # Fichiers des commandes
│ ├── ping.js
│ ├── kick.js
│ └── ban.js
├── events/ # Gestionnaires d'événements
│ ├── ready.js
│ └── interactionCreate.js
├── database/ # Gestion de la base de données
│ └── database.js
├── .env # Variables d'environnement (TOKEN)
├── index.js # Point d'entrée principal
└── deploy-commands.js # Script pour enregistrer les slash commands
Créer le fichier .env
# .env
TOKEN=VOTRE_TOKEN_ICI
CLIENT_ID=VOTRE_APPLICATION_ID
⚠️ Ajouter .env au .gitignore !
Partie 3 : Coder le bot de base (connexion Discord)
index.js : Point d'entrée principal
// index.js
require('dotenv').config();
const { Client, GatewayIntentBits, Collection } = require('discord.js');
const fs = require('fs');
// Créer le client Discord avec les intents nécessaires
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMembers
]
});
// Collection pour stocker les commandes
client.commands = new Collection();
// Charger les fichiers de commandes
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.data.name, command);
}
// Charger les événements
const eventFiles = fs.readdirSync('./events').filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const event = require(`./events/${file}`);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
}
// Se connecter à Discord
client.login(process.env.TOKEN);
events/ready.js : Événement de connexion réussie
// events/ready.js
module.exports = {
name: 'ready',
once: true,
execute(client) {
console.log(`✅ Bot connecté en tant que ${client.user.tag}`);
console.log(`📊 Présent sur ${client.guilds.cache.size} serveur(s)`);
// Définir l'activité du bot
client.user.setActivity('Gérer le serveur', { type: 'WATCHING' });
}
};
events/interactionCreate.js : Gérer les slash commands
// events/interactionCreate.js
module.exports = {
name: 'interactionCreate',
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
} catch (error) {
console.error(`Erreur lors de l'exécution de ${interaction.commandName}:`, error);
await interaction.reply({
content: '❌ Une erreur est survenue lors de l'exécution de cette commande.',
ephemeral: true
});
}
}
};
Partie 4 : Créer vos premières slash commands
Commande /ping : Vérifier la latence du bot
// commands/ping.js
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Affiche la latence du bot'),
async execute(interaction) {
const sent = await interaction.reply({
content: 'Pinging...',
fetchReply: true
});
const latency = sent.createdTimestamp - interaction.createdTimestamp;
interaction.editReply(`🏓 Pong! Latence : ${latency}ms | API : ${Math.round(interaction.client.ws.ping)}ms`);
}
};
Commande /kick : Expulser un membre
// commands/kick.js
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('kick')
.setDescription('Expulse un membre du serveur')
.addUserOption(option =>
option.setName('membre')
.setDescription('Le membre à expulser')
.setRequired(true))
.addStringOption(option =>
option.setName('raison')
.setDescription('Raison de l'expulsion'))
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers),
async execute(interaction) {
const target = interaction.options.getUser('membre');
const reason = interaction.options.getString('raison') || 'Aucune raison fournie';
const member = interaction.guild.members.cache.get(target.id);
if (!member) {
return interaction.reply({ content: '❌ Membre introuvable.', ephemeral: true });
}
if (!member.kickable) {
return interaction.reply({ content: '❌ Je ne peux pas expulser ce membre.', ephemeral: true });
}
try {
await member.kick(reason);
await interaction.reply(`✅ ${target.tag} a été expulsé. Raison : ${reason}`);
} catch (error) {
console.error(error);
await interaction.reply({ content: '❌ Erreur lors de l'expulsion.', ephemeral: true });
}
}
};
Commande /ban : Bannir un membre
// commands/ban.js
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ban')
.setDescription('Bannit un membre du serveur')
.addUserOption(option =>
option.setName('membre')
.setDescription('Le membre à bannir')
.setRequired(true))
.addStringOption(option =>
option.setName('raison')
.setDescription('Raison du bannissement'))
.addIntegerOption(option =>
option.setName('supprimer-messages')
.setDescription('Supprimer les messages des X derniers jours (0-7)')
.setMinValue(0)
.setMaxValue(7))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
const target = interaction.options.getUser('membre');
const reason = interaction.options.getString('raison') || 'Aucune raison fournie';
const deleteMessageDays = interaction.options.getInteger('supprimer-messages') || 0;
const member = interaction.guild.members.cache.get(target.id);
if (member && !member.bannable) {
return interaction.reply({ content: '❌ Je ne peux pas bannir ce membre.', ephemeral: true });
}
try {
await interaction.guild.members.ban(target.id, {
deleteMessageSeconds: deleteMessageDays * 86400,
reason: reason
});
await interaction.reply(`🔨 ${target.tag} a été banni. Raison : ${reason}`);
} catch (error) {
console.error(error);
await interaction.reply({ content: '❌ Erreur lors du bannissement.', ephemeral: true });
}
}
};
Partie 5 : Enregistrer les slash commands auprès de Discord
deploy-commands.js : Script d'enregistrement
// deploy-commands.js
require('dotenv').config();
const { REST, Routes } = require('discord.js');
const fs = require('fs');
const commands = [];
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);
(async () => {
try {
console.log(`🔄 Enregistrement de ${commands.length} commande(s)...`);
// Enregistrement global (prend 1h pour se propager)
const data = await rest.put(
Routes.applicationCommands(process.env.CLIENT_ID),
{ body: commands }
);
console.log(`✅ ${data.length} commande(s) enregistrée(s) avec succès !`);
} catch (error) {
console.error('❌ Erreur lors de l'enregistrement :', error);
}
})();
Lancer l'enregistrement
node deploy-commands.js
# Puis démarrer le bot
node index.js
Testez /ping, /kick, /ban sur votre serveur Discord ! 🎉
Partie 6 : Ajouter une base de données SQLite
Pourquoi une base de données ?
Pour sauvegarder :
- Logs de modération (qui a banni qui et quand)
- Système de points/niveaux
- Configurations personnalisées par serveur
database/database.js : Connexion SQLite
// database/database.js
const Database = require('better-sqlite3');
const db = new Database('bot.db');
// Créer la table des logs de modération
db.exec(`
CREATE TABLE IF NOT EXISTS modlogs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT NOT NULL,
user_id TEXT NOT NULL,
moderator_id TEXT NOT NULL,
action TEXT NOT NULL,
reason TEXT,
timestamp INTEGER NOT NULL
)
`);
// Fonction pour ajouter un log
function addModLog(guildId, userId, moderatorId, action, reason) {
const stmt = db.prepare(`
INSERT INTO modlogs (guild_id, user_id, moderator_id, action, reason, timestamp)
VALUES (?, ?, ?, ?, ?, ?)
`);
stmt.run(guildId, userId, moderatorId, action, reason, Date.now());
}
// Fonction pour récupérer les logs d'un utilisateur
function getUserLogs(guildId, userId) {
const stmt = db.prepare('SELECT * FROM modlogs WHERE guild_id = ? AND user_id = ? ORDER BY timestamp DESC');
return stmt.all(guildId, userId);
}
module.exports = { db, addModLog, getUserLogs };
Intégrer les logs dans les commandes
// Ajouter en haut de commands/ban.js
const { addModLog } = require('../database/database');
// Après le ban réussi, ajouter :
addModLog(
interaction.guild.id,
target.id,
interaction.user.id,
'BAN',
reason
);
Créer une commande /historique
// commands/historique.js
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
const { getUserLogs } = require('../database/database');
module.exports = {
data: new SlashCommandBuilder()
.setName('historique')
.setDescription('Affiche l'historique de modération d'un membre')
.addUserOption(option =>
option.setName('membre')
.setDescription('Le membre à vérifier')
.setRequired(true)),
async execute(interaction) {
const target = interaction.options.getUser('membre');
const logs = getUserLogs(interaction.guild.id, target.id);
if (logs.length === 0) {
return interaction.reply({ content: '✅ Aucune sanction enregistrée.', ephemeral: true });
}
const embed = new EmbedBuilder()
.setTitle(`📋 Historique de ${target.tag}`)
.setColor('Orange')
.setDescription(logs.slice(0, 10).map(log => {
const date = new Date(log.timestamp).toLocaleString('fr-FR');
return `**${log.action}** par <@${log.moderator_id}> le ${date}\nRaison : ${log.reason || 'Non spécifiée'}`;
}).join('\n\n'))
.setFooter({ text: `Total : ${logs.length} sanction(s)` });
await interaction.reply({ embeds: [embed] });
}
};
Partie 7 : Déployer le bot en production
Option 1 : Hébergement sur Railway (Gratuit avec GitHub Student Pack)
- Créer un compte sur railway.app
- Connecter votre dépôt GitHub
- Ajouter les variables d'environnement (TOKEN, CLIENT_ID)
- Déployer automatiquement à chaque push
Option 2 : Hébergement sur Render (Gratuit)
- Créer un compte sur render.com
- Créer un nouveau "Web Service"
- Commande de build :
npm install - Commande de démarrage :
node index.js
Option 3 : VPS (DigitalOcean, OVH, etc.)
# Installer Node.js sur le serveur
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install nodejs
# Cloner le projet
git clone https://github.com/votre-repo/mon-bot-discord.git
cd mon-bot-discord
npm install
# Utiliser PM2 pour garder le bot en ligne
npm install -g pm2
pm2 start index.js --name mon-bot
pm2 startup
pm2 save
Garder le bot actif 24/7
Sur Railway/Render : Automatique (ils s'occupent de tout)
Sur VPS : Utiliser PM2 (redémarre automatiquement en cas de crash)
Partie 8 : Aller plus loin - Fonctionnalités avancées
Boutons et menus déroulants
const { ButtonBuilder, ButtonStyle, ActionRowBuilder } = require('discord.js');
const button = new ButtonBuilder()
.setCustomId('confirm-ban')
.setLabel('Confirmer le bannissement')
.setStyle(ButtonStyle.Danger);
const row = new ActionRowBuilder().addComponents(button);
await interaction.reply({ content: 'Êtes-vous sûr ?', components: [row] });
Système de niveaux et XP
Créer une table users avec colonnes xp et level, incrémenter à chaque message.
Commandes de musique (YouTube)
Utiliser @discordjs/voice + ytdl-core pour lire de la musique.
API externes (météo, traduction, etc.)
const axios = require('axios');
const response = await axios.get('https://api.openweathermap.org/data/2.5/weather?q=Paris&appid=YOUR_KEY');
const weather = response.data;
Bonnes pratiques et sécurité
✅ Faire
- Utiliser des variables d'environnement (.env) pour les secrets
- Valider TOUTES les entrées utilisateurs
- Gérer les erreurs avec try/catch
- Limiter les permissions du bot au strict nécessaire
- Logger les actions importantes (bans, kicks, etc.)
❌ Ne PAS faire
- Commit le fichier .env sur GitHub (risque de vol de token)
- Donner la permission "Administrator" au bot (trop dangereux)
- Stocker des mots de passe en clair dans la BDD
- Exécuter du code arbitraire fourni par les utilisateurs (eval)
Ressources pour continuer votre apprentissage
Documentation officielle
- Discord.js Guide : discordjs.guide (LE guide de référence)
- Documentation API : discord.js.org
- Discord Developer Portal : discord.com/developers/docs
Communautés et support
- Discord officiel Discord.js : Support en temps réel
- Reddit r/discordapp : Discussions et partages de bots
- GitHub Discussions : Pour signaler des bugs
Exemples de bots open-source
- MEE6 (propriétaire mais inspirant)
- Dyno Bot (système de modération complet)
- GitHub : discord-bot-templates (templates gratuits)
Conclusion : Votre bot est prêt à conquérir Discord !
Félicitations ! Vous avez maintenant un bot Discord complet et fonctionnel avec :
- ✅ Slash commands modernes (ping, kick, ban, historique)
- ✅ Base de données SQLite pour les logs
- ✅ Gestion d'erreurs robuste
- ✅ Prêt pour le déploiement en production
Les possibilités sont infinies : système de niveaux, économie virtuelle, mini-jeux, intégration d'APIs tierces, etc.
Mon conseil : Commencez petit, ajoutez des fonctionnalités progressivement, et écoutez les retours de votre communauté. Les meilleurs bots Discord sont ceux qui répondent à un besoin réel de leurs utilisateurs.
Prêt à lancer votre bot sur 1000+ serveurs ? Le code complet de ce tutoriel est disponible sur GitHub. Forkez, améliorez, et partagez vos créations ! 🤖💜