Een Discord welkomstbot begroet automatisch elk nieuw lid dat je server binnenkomt, maar hij doet meer dan een kale "welkom"-regel sturen: hij genereert een gepersonaliseerde afbeelding met de avatar en naam van het lid. Een afbeelding op maat maakt een veel sterkere eerste indruk dan een tekstregel. In deze tutorial bouwen we vanaf nul een werkende bot met discord.js en @napi-rs/canvas.
Benodigde pakketten en projectopzet
Je hebt Node.js 18 of nieuwer nodig. Initialiseer in een lege map het project en installeer twee hoofdpakketten: discord.js om met Discord te praten en @napi-rs/canvas om de afbeelding te tekenen. Ik geef de voorkeur aan dat laatste omdat het, anders dan het oudere node-canvas, geen systeembibliotheken (Cairo) hoeft te compileren — het installeert probleemloos op de meeste besturingssystemen en VPS'en.
npm init -y
npm install discord.js @napi-rs/canvas
Maak je bot aan in het Discord Developer Portal, pak het token uit het tabblad "Bot" en zet onder Privileged Gateway Intents beslist de schakelaar SERVER MEMBERS INTENT aan. Zonder die optie ziet de bot de join-gebeurtenis van een lid nooit.
De client en zijn intents definiëren
Wanneer je de client maakt in discord.js v14, geef je via intents aan welke gebeurtenissen je wilt ontvangen. Om nieuwe leden op te vangen is de GuildMembers-intent verplicht.
const { Client, GatewayIntentBits, Events, AttachmentBuilder } = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
],
});
client.once(Events.ClientReady, (c) => {
console.log(`Ingelogd als ${c.user.tag}`);
});
client.login(process.env.DISCORD_TOKEN);
Het token in een omgevingsvariabele bewaren (process.env.DISCORD_TOKEN) in plaats van het hard te coderen is belangrijk voor de veiligheid; lekt het token, dan krijgt iemand anders de controle over je bot.
Het nieuwe lid opvangen: guildMemberAdd
Wanneer iemand binnenkomt, wordt de GuildMemberAdd-gebeurtenis getriggerd. Hier genereren we de afbeelding en sturen we hem naar een geschikt kanaal. Het kanaal op ID vinden is betrouwbaarder dan op naam, maar voor het voorbeeld toon ik de naam-aanpak.
client.on(Events.GuildMemberAdd, async (member) => {
const channel = member.guild.channels.cache.find(
(c) => c.name === 'algemeen'
);
if (!channel) return;
const attachment = await createWelcomeImage(member);
await channel.send({
content: `Welkom aan boord <@${member.id}>! 🎉`,
files: [attachment],
});
});
De uitdrukking <@${member.id}> tagt de gebruiker in het bericht, zodat die een melding krijgt. De functie createWelcomeImage die de afbeelding maakt, schrijven we hierna.
De welkomstafbeelding tekenen met Canvas
Hier gebeurt de magie. We maken een canvas en tekenen de achtergrond, de tot een cirkel bijgesneden avatar en daarbovenop de gebruikersnaam. Om de avatar rond te maken, gebruiken we een clip-masker.
const { createCanvas, loadImage } = require('@napi-rs/canvas');
async function createWelcomeImage(member) {
const canvas = createCanvas(1024, 450);
const ctx = canvas.getContext('2d');
// Achtergrond
const background = await loadImage('./assets/background.png');
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
// De avatar als cirkel tekenen
const avatarURL = member.user.displayAvatarURL({
extension: 'png',
size: 256,
});
const avatar = await loadImage(avatarURL);
ctx.save();
ctx.beginPath();
ctx.arc(512, 150, 100, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(avatar, 412, 50, 200, 200);
ctx.restore();
// Tekst
ctx.textAlign = 'center';
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 52px Sans';
ctx.fillText('Welkom!', 512, 320);
ctx.font = '36px Sans';
ctx.fillText(member.user.username, 512, 375);
const buffer = await canvas.encode('png');
return new AttachmentBuilder(buffer, { name: 'welcome.png' });
}
Hier hebben we de avatar-extensie als png opgevraagd; het standaard WebP kan loadImage soms in de war brengen. canvas.encode('png') geeft een buffer terug, die we rechtstreeks aan AttachmentBuilder doorgeven om naar Discord te uploaden.
Een aangepast lettertype gebruiken
Het standaard "Sans"-lettertype werkt, maar wil je er een die bij je merk past, dan kun je een .ttf-bestand registreren. Doe dit één keer bovenaan het bestand, voordat de client start.
const { GlobalFonts } = require('@napi-rs/canvas');
GlobalFonts.registerFromPath('./assets/Poppins-Bold.ttf', 'Poppins');
// Daarna: ctx.font = 'bold 52px Poppins';
Lange gebruikersnamen kunnen over de rand van de afbeelding lopen. Een praktische oplossing is de lettergrootte te verkleinen wanneer de naam een bepaalde lengte overschrijdt, of de naam in te korten met een … erachter.
Veelgemaakte fouten en tips
- Gebeurtenis triggert nooit: meestal staat
SERVER MEMBERS INTENTniet aan in het portal, of ontbreekt deGuildMembers-intent in je code. - Avatar laadt niet: vraag de extensie als
pngop en zorg dat de bot internettoegang heeft. - Kanaal niet gevonden: gebruik de kanaal-ID in plaats van de naam (
channels.cache.get('KANAAL_ID')) — namen kunnen veranderen. - Live gaan: draai de bot op een VPS met een procesmanager zoals
pm2om hem 24/7 online te houden.
Veelgestelde vragen
Heb ik echt hosting nodig voor deze bot?
De bot moet verbonden blijven met Discord om te werken, dus hij stopt zodra je computer uitgaat. Een goedkope VPS is genoeg om hem permanent online te houden; met pm2 herstart hij zelfs automatisch na een crash.
Waarom @napi-rs/canvas in plaats van node-canvas?
Hun API's zijn vrijwel identiek, maar @napi-rs/canvas wordt geleverd met voorgecompileerde binaries, dus je hoeft geen systeemafhankelijkheden zoals Cairo te installeren. Dat maakt de installatie veel eenvoudiger, vooral op gedeelde hosting en op verschillende besturingssystemen.
Kan ik het lidnummer op de afbeelding zetten?
Ja. Gebruik member.guild.memberCount om het huidige aantal leden op te halen en teken het als tekst zoals "Jij bent ons 250e lid!". Met dezelfde ctx.fillText-logica kun je zoveel dynamische info toevoegen als je wilt.
Wil je een welkomstbot op maat voor jouw community? Van het visuele ontwerp tot een automatisch rollensysteem, ik bouw een Discord-bot afgestemd op jouw wensen. Neem contact op en laten we het over je project hebben.