aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Discord-bots

Discord-muziekbot bouwen met discord.js en Lavalink

Een Discord-muziekbot schrijven lijkt op het eerste gezicht eenvoudig — "ga een kanaal in, speel een nummer" — maar het wordt serieus zodra je solide wachtrijbeheer, ononderbroken audiostreaming en een architectuur toevoegt die op veel servers tegelijk draait. In deze gids bouwen we een moderne bot in twee lagen: discord.js voor de botlogica, en een aparte audioserver, Lavalink, voor decodering en streaming. We behandelen ook kort de directe aanpak met @discordjs/voice voor kleinere bots.

Waarom Lavalink? Het verschil met @discordjs/voice

@discordjs/voice is de officiële bibliotheek waarmee je bot een spraakkanaal kan binnenkomen en een ruwe PCM/Opus-stream kan versturen. Op zichzelf is dat genoeg om muziek af te spelen: je haalt een streambron op (bijvoorbeeld ytdl-core of play-dl), zet die met createAudioResource om in een resource en speelt die af via een AudioPlayer. Het probleem met deze aanpak is schaalbaarheid: het decoderen en transcoderen van audio belast hetzelfde Node-proces waarin je bot draait zwaar.

Lavalink is een zelfstandige, op Java gebaseerde audionode die dit zware werk overneemt. Je bot vertelt Lavalink via een WebSocket "speel dit nummer in dit kanaal", en Lavalink regelt alle decodering en streaming. De voordelen:

  • De CPU-belasting wordt van de bot afgehaald; één node kan honderden servers bedienen.
  • Ingebouwde plugin-ondersteuning voor YouTube, SoundCloud, Bandcamp, Twitch en HTTP-bronnen.
  • Sessiecontinuïteit en robuuste herverbinding, zelfs als de bot opnieuw opstart.

Voor een kleine hobbybot op één server is @discordjs/voice ruim voldoende. Als je 24/7 op veel servers wilt afspelen, is Lavalink vrijwel verplicht.

Het project opzetten en de bot registreren

Initialiseer eerst een Node.js-project (Node 18+ aanbevolen) en installeer de benodigde pakketten:

mkdir muziek-bot && cd muziek-bot
npm init -y
npm install discord.js shoukaku dotenv

Hier is shoukaku een volwassen clientbibliotheek die tussen discord.js en Lavalink praat (alternatieven: lavalink-client, dat actief onderhouden wordt in plaats van erela.js). Maak een applicatie aan in het Discord Developer Portal, pak een bot-token en zet die in een .env-bestand. Geef bij het uitnodigen van de bot de scopes bot en applications.commands plus de spraakrechten Connect en Speak.

De Lavalink-node draaien

Lavalink wordt geleverd als een uitvoerbare JAR en vereist Java 17+. Download de officiële release en zet er een application.yml naast:

server:
  port: 2333
lavalink:
  server:
    password: "een-sterk-wachtwoord"

Start het vervolgens met java -jar Lavalink.jar. Bij actuele versies moet je mogelijk een aparte plugin (zoals youtube-source) configureren voor de YouTube-bron; controleer de opstartlogs om te zien welke bronnen geladen zijn. Maak dit proces in productie persistent met systemd of Docker.

De bot met Lavalink verbinden

Met Shoukaku stel je de node-verbinding en een basisclient zo in:

const { Client, GatewayIntentBits } = require('discord.js');
const { Shoukaku, Connectors } = require('shoukaku');
require('dotenv').config();

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildVoiceStates,
  ],
});

const nodes = [{
  name: 'main',
  url: 'localhost:2333',
  auth: process.env.LAVALINK_PASSWORD,
}];

const shoukaku = new Shoukaku(new Connectors.DiscordJS(client), nodes);
shoukaku.on('error', (_, err) => console.error(err));

client.login(process.env.DISCORD_TOKEN);

Let op: de intent GuildVoiceStates is vereist om spraakstatussen te volgen. Zonder die intent kan de bot niet zien in welk kanaal een gebruiker zit.

De afspeelwachtrij bouwen

De echte muziekervaring wordt bepaald door de "wachtrij": gelijktijdige verzoeken op een rij zetten, automatisch doorgaan na een nummer en vertrekken als het kanaal leegloopt. Houd per server een aparte wachtrij aan. Een eenvoudige play-flow:

async function play(interaction, query) {
  const node = shoukaku.options.nodeResolver(shoukaku.nodes);
  const result = await node.rest.resolve(`ytsearch:${query}`);
  if (!result || !result.data.length) {
    return interaction.reply('Geen resultaten gevonden.');
  }
  const track = result.data[0];

  const player = await shoukaku.joinVoiceChannel({
    guildId: interaction.guildId,
    channelId: interaction.member.voice.channelId,
    shardId: 0,
  });

  player.playTrack({ track: { encoded: track.encoded } });
  return interaction.reply(`Nu speelt: **${track.info.title}**`);
}

In een echte bot verpak je dit in een wachtrijklasse: bij de gebeurtenis player.on('end', ...) haal je het volgende nummer uit de array en speel je het af, en als de array leeg is verlaat je het kanaal met shoukaku.leaveVoiceChannel na een uitsteltijd. De wachtrij in het geheugen bewaren als een Map<guildId, Queue> is voor de meeste bots genoeg.

Slash-commando's toevoegen

Moderne Discord-bots gebruiken slash-commando's in plaats van teksttriggers. Registreer de commando's één keer via REST en verwerk ze in de interactionCreate-gebeurtenis:

const { SlashCommandBuilder, REST, Routes } = require('discord.js');

const commands = [
  new SlashCommandBuilder()
    .setName('play')
    .setDescription('Speel een nummer af')
    .addStringOption(o =>
      o.setName('query').setDescription('Naam van het nummer of URL').setRequired(true)),
].map(c => c.toJSON());

const rest = new REST().setToken(process.env.DISCORD_TOKEN);
await rest.put(
  Routes.applicationCommands(process.env.CLIENT_ID),
  { body: commands },
);

Voeg skip, queue, pause, stop en volume op dezelfde manier toe. Elk commando behalve play leest en werkt de wachtrijstatus bij; door je wachtrijbeheer in één module te centraliseren wordt de code veel makkelijker te onderhouden.

Veelvoorkomende valkuilen

  • Bot komt het kanaal binnen maar er is geen geluid: meestal kan de Lavalink-node de bron niet resolven. Controleer de node-logs en of de bronplugin geladen is.
  • "Used disallowed intents": je bent vergeten de betreffende intents in het developer portal in te schakelen.
  • Bot uitgenodigd maar commando's verschijnen niet: globale commando's hebben tijd nodig om door te komen; tijdens de ontwikkeling werkt registreren op een specifieke server (guild) direct.

Veelgestelde vragen

Is Lavalink verplicht, of is alleen discord.js genoeg?

Voor één server met af en toe afspelen is @discordjs/voice plus een bron als play-dl genoeg. Als je continu op veel servers afspeelt, biedt Lavalink een veel stabielere ervaring omdat het de CPU-belasting van de bot afhaalt.

Moet ik op iets letten qua auteursrecht?

Audiobronnen en de gebruiksvoorwaarden van platforms veranderen na verloop van tijd. Zorg bij een commercieel of grootschalig project dat de inhoud die je afspeelt voldoet aan de voorwaarden van het betreffende platform, en geef waar mogelijk de voorkeur aan gelicentieerde API's.

Waar moet ik de bot hosten?

Draai voor lage latentie de bot en de Lavalink-node op een VPS in dezelfde regio. Gratis platforms die "in slaap vallen" zijn ongeschikt voor muziekbots; je hebt een server nodig die continu draait.

Wil je je eigen muziekbot? Voor een op maat gemaakte bot, van begin tot eind opgebouwd met wachtrijbeheer, slash-commando's en Lavalink-infrastructuur, neem contact met me op.

Devamı için