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

Discord Willkommens-Bot: Personalisiertes Canvas-Bild

Ein Discord Willkommens-Bot begrüßt automatisch jedes neue Mitglied, das deinem Server beitritt, doch er tut mehr als nur eine schlichte „Willkommen"-Zeile zu senden: Er erzeugt ein personalisiertes Bild mit dem Avatar und dem Namen des Mitglieds. Eine maßgeschneiderte Grafik hinterlässt einen deutlich stärkeren ersten Eindruck als eine einzeilige Textnachricht. In diesem Tutorial bauen wir mit discord.js und @napi-rs/canvas einen funktionierenden Bot von Grund auf.

Benötigte Pakete und Projekt-Setup

Du brauchst Node.js 18 oder neuer. Initialisiere in einem leeren Ordner das Projekt und installiere zwei Hauptpakete: discord.js, um mit Discord zu kommunizieren, und @napi-rs/canvas, um das Bild zu zeichnen. Letzteres bevorzuge ich, weil es – anders als das ältere node-canvas – keine Systembibliotheken (Cairo) kompilieren muss; es installiert sich auf den meisten Betriebssystemen und VPS sauber.

npm init -y
npm install discord.js @napi-rs/canvas

Erstelle deinen Bot im Discord Developer Portal, hole dir das Token im Reiter „Bot" und aktiviere unter Privileged Gateway Intents unbedingt den Schalter SERVER MEMBERS INTENT. Ohne ihn bekommt der Bot das Beitritts-Ereignis eines Mitglieds nie zu sehen.

Den Client und seine Intents definieren

Wenn du den Client in discord.js v14 erstellst, gibst du über intents an, welche Ereignisse du empfangen möchtest. Um neue Mitglieder zu erfassen, ist der GuildMembers-Intent zwingend erforderlich.

const { Client, GatewayIntentBits, Events, AttachmentBuilder } = require('discord.js');

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

client.once(Events.ClientReady, (c) => {
  console.log(`Angemeldet als ${c.user.tag}`);
});

client.login(process.env.DISCORD_TOKEN);

Das Token in einer Umgebungsvariable (process.env.DISCORD_TOKEN) statt fest im Code zu halten, ist aus Sicherheitsgründen wichtig; falls das Token durchsickert, übernimmt jemand anderes die Kontrolle über deinen Bot.

Das neue Mitglied erfassen: guildMemberAdd

Wenn jemand beitritt, wird das Ereignis GuildMemberAdd ausgelöst. Hier erzeugen wir das Bild und senden es in einen passenden Kanal. Den Kanal über die ID zu finden ist zuverlässiger als über den Namen, aber für das Beispiel zeige ich den Namens-Ansatz.

client.on(Events.GuildMemberAdd, async (member) => {
  const channel = member.guild.channels.cache.find(
    (c) => c.name === 'allgemein'
  );
  if (!channel) return;

  const attachment = await createWelcomeImage(member);

  await channel.send({
    content: `Willkommen an Bord <@${member.id}>! 🎉`,
    files: [attachment],
  });
});

Der Ausdruck <@${member.id}> erwähnt den Nutzer in der Nachricht, damit er eine Benachrichtigung erhält. Die Funktion createWelcomeImage, die die Grafik erzeugt, schreiben wir gleich.

Das Willkommensbild mit Canvas zeichnen

Hier passiert die Magie. Wir erstellen ein Canvas und zeichnen den Hintergrund, den in einen Kreis beschnittenen Avatar und darüber den Benutzernamen. Um den Avatar rund zu machen, verwenden wir eine clip-Maske.

const { createCanvas, loadImage } = require('@napi-rs/canvas');

async function createWelcomeImage(member) {
  const canvas = createCanvas(1024, 450);
  const ctx = canvas.getContext('2d');

  // Hintergrund
  const background = await loadImage('./assets/background.png');
  ctx.drawImage(background, 0, 0, canvas.width, canvas.height);

  // Den Avatar als Kreis zeichnen
  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();

  // Text
  ctx.textAlign = 'center';
  ctx.fillStyle = '#ffffff';
  ctx.font = 'bold 52px Sans';
  ctx.fillText('Willkommen!', 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 haben wir die Avatar-Erweiterung als png angefordert; das standardmäßige WebP kann loadImage manchmal Probleme bereiten. canvas.encode('png') gibt einen Buffer zurück, den wir direkt an AttachmentBuilder übergeben, um ihn zu Discord hochzuladen.

Eine eigene Schriftart verwenden

Die Standardschrift „Sans" funktioniert, aber wenn du eine möchtest, die zu deiner Marke passt, kannst du eine .ttf-Datei registrieren. Tu das einmal oben in der Datei, bevor der Client startet.

const { GlobalFonts } = require('@napi-rs/canvas');

GlobalFonts.registerFromPath('./assets/Poppins-Bold.ttf', 'Poppins');
// Danach:  ctx.font = 'bold 52px Poppins';

Lange Benutzernamen können über den Rand des Bildes hinauslaufen. Eine praktische Lösung ist, die Schriftgröße zu verkleinern, wenn der Name eine bestimmte Länge überschreitet, oder ihn zu kürzen und ein anzuhängen.

Häufige Fehler und Tipps

  • Ereignis wird nie ausgelöst: Meist ist SERVER MEMBERS INTENT im Portal nicht aktiviert oder der GuildMembers-Intent fehlt im Code.
  • Avatar lädt nicht: Fordere die Erweiterung als png an und stelle sicher, dass der Bot Internetzugang hat.
  • Kanal nicht gefunden: Verwende die Kanal-ID statt des Namens (channels.cache.get('KANAL_ID')) — Namen können sich ändern.
  • Live gehen: Um den Bot rund um die Uhr online zu halten, lass ihn auf einem VPS mit einem Prozessmanager wie pm2 laufen.

Häufige Fragen

Brauche ich für diesen Bot wirklich Hosting?

Der Bot muss mit Discord verbunden bleiben, um zu funktionieren, also stoppt er, sobald dein Rechner ausgeht. Ein günstiger VPS reicht, um ihn dauerhaft online zu halten; mit pm2 startet er nach einem Absturz sogar automatisch neu.

Warum @napi-rs/canvas statt node-canvas?

Ihre APIs sind nahezu identisch, aber @napi-rs/canvas bringt vorkompilierte Binärdateien mit, sodass du keine Systemabhängigkeiten wie Cairo installieren musst. Das macht die Einrichtung deutlich einfacher, besonders auf Shared Hosting und über verschiedene Betriebssysteme hinweg.

Kann ich die Mitgliedsnummer auf das Bild setzen?

Ja. Verwende member.guild.memberCount, um die aktuelle Mitgliederzahl zu erhalten, und zeichne sie als Text wie „Du bist unser 250. Mitglied!". Mit derselben ctx.fillText-Logik kannst du beliebig viele dynamische Informationen hinzufügen.

Möchtest du einen maßgeschneiderten Willkommens-Bot für deine Community? Vom visuellen Design bis zu einem automatischen Rollensystem entwickle ich einen Discord-Bot, der auf deine Bedürfnisse zugeschnitten ist. Kontaktiere mich und lass uns über dein Projekt sprechen.

Devamı için