Tutoriels

Créer un bot musical Discord en 2026 : le guide complet

Apprenez à créer votre propre bot musical Discord avec Discord.js et discord-player. Tutoriel pas à pas depuis zéro jusqu'au déploiement.

Marie Chen

Marie Chen

Développeuse Discord

9 min de lecture
Créer un bot musical Discord en 2026 : le guide complet

Depuis l’arrêt de Groovy et Rythm en 2021, de nombreux serveurs Discord cherchent des alternatives pour écouter de la musique ensemble. La bonne nouvelle ? Vous pouvez créer votre propre bot musical personnalisé et le garder privé pour votre serveur.

Dans ce tutoriel complet, nous allons créer un bot musical fonctionnel avec Discord.js v14 et discord-player.

Prérequis

Avant de commencer, assurez-vous d’avoir :

  • Node.js 18+ installé sur votre machine
  • Un compte Discord
  • Des connaissances de base en JavaScript
  • Un éditeur de code (VS Code recommandé)

Étape 1 : Créer l’application Discord

1.1 Accéder au portail développeur

  1. Rendez-vous sur Discord Developer Portal
  2. Connectez-vous avec votre compte Discord
  3. Cliquez sur New Application
  4. Donnez un nom à votre bot (ex: “MusicBot”)

1.2 Configurer le bot

  1. Dans le menu latéral, cliquez sur Bot
  2. Cliquez sur Add Bot puis confirmez
  3. Sous Privileged Gateway Intents, activez :
    • Message Content Intent
    • Server Members Intent
  4. Cliquez sur Reset Token et copiez le token (gardez-le secret !)

1.3 Générer le lien d’invitation

  1. Allez dans OAuth2 > URL Generator
  2. Dans Scopes, cochez : bot et applications.commands
  3. Dans Bot Permissions, cochez :
    • Connect
    • Speak
    • Use Voice Activity
    • Send Messages
    • Embed Links
    • Read Message History
  4. Copiez l’URL générée et ouvrez-la pour inviter le bot sur votre serveur

Étape 2 : Initialiser le projet

Créez un nouveau dossier et initialisez le projet :

mkdir discord-music-bot
cd discord-music-bot
npm init -y

Installez les dépendances nécessaires :

npm install discord.js @discordjs/voice discord-player @discord-player/extractor
npm install dotenv

:::tip discord-player gère toute la complexité de la lecture audio. C’est la bibliothèque la plus maintenue pour les bots musicaux en 2026. :::

Étape 3 : Structure du projet

Créez la structure suivante :

discord-music-bot/
├── src/
│   ├── commands/
│   │   ├── play.js
│   │   ├── skip.js
│   │   ├── stop.js
│   │   ├── queue.js
│   │   └── nowplaying.js
│   ├── events/
│   │   └── ready.js
│   └── index.js
├── .env
├── .gitignore
└── package.json

Étape 4 : Configuration

4.1 Fichier .env

Créez le fichier .env à la racine :

DISCORD_TOKEN=votre_token_ici
CLIENT_ID=id_de_votre_application

4.2 Fichier .gitignore

node_modules/
.env

4.3 Mettre à jour package.json

Ajoutez le type module et le script de démarrage :

{
  "name": "discord-music-bot",
  "version": "1.0.0",
  "type": "module",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js"
  }
}

Étape 5 : Fichier principal

Créez src/index.js :

import { Client, GatewayIntentBits, Collection, REST, Routes } from 'discord.js';
import { Player } from 'discord-player';
import { DefaultExtractors } from '@discord-player/extractor';
import { config } from 'dotenv';
import { readdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

config();

const __dirname = dirname(fileURLToPath(import.meta.url));

// Créer le client Discord
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.GuildVoiceStates,
    GatewayIntentBits.MessageContent,
  ],
});

// Initialiser le player
const player = new Player(client, {
  ytdlOptions: {
    quality: 'highestaudio',
    highWaterMark: 1 << 25,
  },
});

// Charger les extracteurs (YouTube, Spotify, SoundCloud, etc.)
await player.extractors.loadMulti(DefaultExtractors);

// Collection pour les commandes
client.commands = new Collection();

// Charger les commandes
const commandsPath = join(__dirname, 'commands');
const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith('.js'));

const commands = [];

for (const file of commandFiles) {
  const filePath = join(commandsPath, file);
  const command = await import(`file://${filePath}`);

  if ('data' in command && 'execute' in command) {
    client.commands.set(command.data.name, command);
    commands.push(command.data.toJSON());
  }
}

// Enregistrer les slash commands
const rest = new REST().setToken(process.env.DISCORD_TOKEN);

try {
  console.log('Enregistrement des commandes slash...');
  await rest.put(
    Routes.applicationCommands(process.env.CLIENT_ID),
    { body: commands }
  );
  console.log('Commandes enregistrées avec succès !');
} catch (error) {
  console.error('Erreur lors de l\'enregistrement des commandes:', error);
}

// Gérer les interactions
client.on('interactionCreate', async interaction => {
  if (!interaction.isChatInputCommand()) return;

  const command = client.commands.get(interaction.commandName);
  if (!command) return;

  try {
    await command.execute(interaction, player);
  } catch (error) {
    console.error(error);
    const content = { content: 'Une erreur est survenue !', ephemeral: true };
    if (interaction.replied || interaction.deferred) {
      await interaction.followUp(content);
    } else {
      await interaction.reply(content);
    }
  }
});

// Événements du player
player.events.on('playerStart', (queue, track) => {
  queue.metadata.channel.send({
    embeds: [{
      color: 0x1DB954,
      title: '🎵 Lecture en cours',
      description: `**[${track.title}](${track.url})**`,
      thumbnail: { url: track.thumbnail },
      fields: [
        { name: 'Durée', value: track.duration, inline: true },
        { name: 'Demandé par', value: track.requestedBy.toString(), inline: true },
      ],
    }],
  });
});

player.events.on('emptyQueue', queue => {
  queue.metadata.channel.send('✅ File d\'attente terminée, je quitte le salon vocal.');
});

player.events.on('error', (queue, error) => {
  console.error('Erreur player:', error);
});

// Quand le bot est prêt
client.once('ready', () => {
  console.log(`✅ Connecté en tant que ${client.user.tag}`);
  client.user.setActivity('de la musique 🎵', { type: 2 }); // "Écoute"
});

// Connexion
client.login(process.env.DISCORD_TOKEN);

Étape 6 : Les commandes

6.1 Commande Play

Créez src/commands/play.js :

import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
import { useMainPlayer } from 'discord-player';

export const data = new SlashCommandBuilder()
  .setName('play')
  .setDescription('Joue une musique depuis YouTube, Spotify ou SoundCloud')
  .addStringOption(option =>
    option
      .setName('query')
      .setDescription('Nom de la musique ou URL')
      .setRequired(true)
  );

export async function execute(interaction, player) {
  const query = interaction.options.getString('query');
  const voiceChannel = interaction.member.voice.channel;

  if (!voiceChannel) {
    return interaction.reply({
      content: '❌ Vous devez être dans un salon vocal !',
      ephemeral: true,
    });
  }

  await interaction.deferReply();

  try {
    const { track } = await player.play(voiceChannel, query, {
      nodeOptions: {
        metadata: {
          channel: interaction.channel,
          requestedBy: interaction.user,
        },
        leaveOnEmpty: true,
        leaveOnEmptyCooldown: 60000,
        leaveOnEnd: true,
        leaveOnEndCooldown: 60000,
      },
      requestedBy: interaction.user,
    });

    const embed = new EmbedBuilder()
      .setColor(0x1DB954)
      .setTitle('🎵 Ajouté à la file d\'attente')
      .setDescription(`**[${track.title}](${track.url})**`)
      .setThumbnail(track.thumbnail)
      .addFields(
        { name: 'Durée', value: track.duration, inline: true },
        { name: 'Artiste', value: track.author || 'Inconnu', inline: true }
      );

    await interaction.editReply({ embeds: [embed] });
  } catch (error) {
    console.error(error);
    await interaction.editReply({
      content: '❌ Impossible de jouer cette musique. Vérifiez le lien ou réessayez.',
    });
  }
}

6.2 Commande Skip

Créez src/commands/skip.js :

import { SlashCommandBuilder } from 'discord.js';
import { useQueue } from 'discord-player';

export const data = new SlashCommandBuilder()
  .setName('skip')
  .setDescription('Passe à la musique suivante');

export async function execute(interaction) {
  const queue = useQueue(interaction.guildId);

  if (!queue || !queue.isPlaying()) {
    return interaction.reply({
      content: '❌ Aucune musique en cours de lecture.',
      ephemeral: true,
    });
  }

  const currentTrack = queue.currentTrack;
  queue.node.skip();

  await interaction.reply({
    content: `⏭️ **${currentTrack.title}** a été passée.`,
  });
}

6.3 Commande Stop

Créez src/commands/stop.js :

import { SlashCommandBuilder } from 'discord.js';
import { useQueue } from 'discord-player';

export const data = new SlashCommandBuilder()
  .setName('stop')
  .setDescription('Arrête la musique et vide la file d\'attente');

export async function execute(interaction) {
  const queue = useQueue(interaction.guildId);

  if (!queue) {
    return interaction.reply({
      content: '❌ Aucune musique en cours de lecture.',
      ephemeral: true,
    });
  }

  queue.delete();

  await interaction.reply({
    content: '⏹️ Musique arrêtée et file d\'attente vidée.',
  });
}

6.4 Commande Queue

Créez src/commands/queue.js :

import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
import { useQueue } from 'discord-player';

export const data = new SlashCommandBuilder()
  .setName('queue')
  .setDescription('Affiche la file d\'attente');

export async function execute(interaction) {
  const queue = useQueue(interaction.guildId);

  if (!queue || !queue.isPlaying()) {
    return interaction.reply({
      content: '❌ Aucune musique en cours de lecture.',
      ephemeral: true,
    });
  }

  const currentTrack = queue.currentTrack;
  const tracks = queue.tracks.toArray();

  const embed = new EmbedBuilder()
    .setColor(0x1DB954)
    .setTitle('📜 File d\'attente')
    .setDescription(
      `**En cours :**\n🎵 [${currentTrack.title}](${currentTrack.url}) - ${currentTrack.duration}`
    );

  if (tracks.length > 0) {
    const trackList = tracks
      .slice(0, 10)
      .map((track, i) => `${i + 1}. [${track.title}](${track.url}) - ${track.duration}`)
      .join('\n');

    embed.addFields({
      name: `À suivre (${tracks.length} titre${tracks.length > 1 ? 's' : ''})`,
      value: trackList,
    });

    if (tracks.length > 10) {
      embed.setFooter({ text: `Et ${tracks.length - 10} autres titres...` });
    }
  }

  await interaction.reply({ embeds: [embed] });
}

6.5 Commande Now Playing

Créez src/commands/nowplaying.js :

import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
import { useQueue } from 'discord-player';

export const data = new SlashCommandBuilder()
  .setName('nowplaying')
  .setDescription('Affiche la musique en cours');

export async function execute(interaction) {
  const queue = useQueue(interaction.guildId);

  if (!queue || !queue.isPlaying()) {
    return interaction.reply({
      content: '❌ Aucune musique en cours de lecture.',
      ephemeral: true,
    });
  }

  const track = queue.currentTrack;
  const progress = queue.node.createProgressBar();

  const embed = new EmbedBuilder()
    .setColor(0x1DB954)
    .setTitle('🎵 En cours de lecture')
    .setDescription(`**[${track.title}](${track.url})**`)
    .setThumbnail(track.thumbnail)
    .addFields(
      { name: 'Artiste', value: track.author || 'Inconnu', inline: true },
      { name: 'Durée', value: track.duration, inline: true },
      { name: 'Demandé par', value: track.requestedBy.toString(), inline: true },
      { name: 'Progression', value: progress || 'N/A' }
    );

  await interaction.reply({ embeds: [embed] });
}

Étape 7 : Lancer le bot

Démarrez votre bot :

npm start

Si tout fonctionne, vous devriez voir :

Enregistrement des commandes slash...
Commandes enregistrées avec succès !
✅ Connecté en tant que MusicBot#1234

Étape 8 : Tester

  1. Rejoignez un salon vocal sur votre serveur
  2. Tapez /play suivi du nom d’une musique ou d’une URL YouTube
  3. Le bot devrait rejoindre et jouer la musique !

Commandes supplémentaires

Voici quelques idées pour enrichir votre bot :

CommandeDescription
/pauseMet en pause la lecture
/resumeReprend la lecture
/volumeAjuste le volume (0-100)
/shuffleMélange la file d’attente
/loopActive/désactive la répétition
/lyricsAffiche les paroles

Sources audio supportées

Avec @discord-player/extractor, votre bot peut lire :

  • YouTube (vidéos et playlists)
  • YouTube Music
  • Spotify (nécessite conversion)
  • SoundCloud
  • Apple Music
  • Vimeo
  • Et bien d’autres…

:::warning Important : YouTube peut bloquer les bots qui font trop de requêtes. Pour un usage intensif, envisagez d’utiliser des proxies ou de vous conformer aux conditions d’utilisation de YouTube. :::

Dépannage

Le bot ne joue pas de son

  1. Vérifiez que FFmpeg est installé sur votre système
  2. Installez-le si nécessaire : npm install ffmpeg-static
  3. Vérifiez que le bot a les permissions “Connect” et “Speak”

Erreur “Cannot find module”

Assurez-vous d’avoir "type": "module" dans votre package.json pour utiliser les imports ES6.

Le bot se déconnecte tout seul

C’est normal ! Par défaut, le bot quitte après 60 secondes d’inactivité. Vous pouvez modifier leaveOnEmptyCooldown et leaveOnEndCooldown dans les options.

Conclusion

Vous avez maintenant un bot musical Discord fonctionnel ! Ce bot est privé et vous appartient entièrement, contrairement aux bots publics qui peuvent être supprimés à tout moment.

Pour aller plus loin :

  • Ajoutez une base de données pour sauvegarder les playlists
  • Créez un système de DJ avec rôles
  • Intégrez un dashboard web pour la configuration

Dans un prochain article, nous verrons comment héberger ce bot pour qu’il reste en ligne 24h/24.

#discord #bot #musique #nodejs #discord.js
Partager :

Articles similaires

Commentaires