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

Discord Hoş Geldin Botu: Canvas ile Karşılama Görseli

Bir Discord hoş geldin botu, sunucuna katılan her yeni üyeye otomatik bir mesaj atan ama bununla kalmayıp ona özel, üzerinde avatarının ve adının yer aldığı bir karşılama görseli üreten bottur. Düz bir "aramıza hoş geldin" mesajı yerine kişiselleştirilmiş bir görsel, topluluğun ilk izlenimini ciddi şekilde güçlendirir. Bu yazıda discord.js ve @napi-rs/canvas kullanarak sıfırdan çalışan bir bot kuracağız.

Gerekli paketler ve proje kurulumu

Node.js 18 veya üzeri yüklü olmalı. Boş bir klasörde projeyi başlatıp iki ana paketi kuruyoruz: Discord ile konuşmak için discord.js, görsel çizmek için ise @napi-rs/canvas. İkincisini tercih etmemin sebebi, eski node-canvas paketinin aksine sistem kütüphanesi (Cairo) derlemesi gerektirmemesi; çoğu işletim sisteminde ve VPS'te sorunsuz kurulur.

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

Botunu Discord Developer Portal üzerinden oluştur, "Bot" sekmesinden token'ını al ve Privileged Gateway Intents altında SERVER MEMBERS INTENT seçeneğini mutlaka aç. Bu olmadan bot, üye katılma olayını hiç görmez.

İstemciyi ve niyetleri (intents) tanımlamak

discord.js v14 ile istemciyi oluştururken hangi olayları almak istediğimizi intents ile belirtiriz. Yeni üyeleri yakalamak için GuildMembers niyeti şarttır.

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

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

client.once(Events.ClientReady, (c) => {
  console.log(`Giriş yapıldı: ${c.user.tag}`);
});

client.login(process.env.DISCORD_TOKEN);

Token'ı koda gömmek yerine bir ortam değişkeninde (process.env.DISCORD_TOKEN) tutmak güvenlik açısından önemlidir; token sızarsa botunun kontrolü başkasına geçer.

Yeni üyeyi yakalamak: guildMemberAdd

Biri sunucuya katıldığında GuildMemberAdd olayı tetiklenir. Burada görseli üretip uygun bir kanala göndereceğiz. Kanalı isimle bulmak yerine ID ile bulmak daha sağlamdır, ama örnek olsun diye isimle gösteriyorum.

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

  const attachment = await createWelcomeImage(member);

  await channel.send({
    content: `Aramıza hoş geldin <@${member.id}>! 🎉`,
    files: [attachment],
  });
});

<@${member.id}> ifadesi mesajda kullanıcıyı etiketler (mention), böylece bildirim alır. Görseli üreten createWelcomeImage fonksiyonunu birazdan yazacağız.

Canvas ile karşılama görseli çizmek

Asıl sihir burada. Bir tuval (canvas) oluşturuyor, üzerine arka planı, daire içine kırpılmış avatarı ve kullanıcı adını çiziyoruz. Avatarı yuvarlak göstermek için bir clip maskesi kullanıyoruz.

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

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

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

  // Avatarı yuvarlak çiz
  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();

  // Metinler
  ctx.textAlign = 'center';
  ctx.fillStyle = '#ffffff';
  ctx.font = 'bold 52px Sans';
  ctx.fillText('Hoş Geldin!', 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' });
}

Burada displayAvatarURL ile uzantıyı png seçtik; varsayılan WebP bazı durumlarda loadImage ile sorun çıkarabiliyor. canvas.encode('png') bize bir buffer döndürür ve bunu doğrudan AttachmentBuilder'a vererek Discord'a yüklüyoruz.

Özel font kullanmak

Varsayılan "Sans" fontu işe yarar ama markana uygun bir font istiyorsan .ttf dosyasını kaydedebilirsin. Bunu istemci başlatılmadan, dosyanın en üstünde bir kez yapmak yeterli.

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

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

Uzun kullanıcı adları görselin kenarından taşabilir. Pratik bir çözüm, ad belli bir uzunluğu aşıyorsa font boyutunu küçültmek veya adı kısaltıp sonuna eklemektir.

Yaygın hatalar ve ipuçları

  • Olay hiç tetiklenmiyor: Genellikle SERVER MEMBERS INTENT portalda açık değildir veya GuildMembers niyeti koda eklenmemiştir.
  • Avatar yüklenmiyor: Uzantıyı png olarak isteyin; ayrıca botun internet erişimi olduğundan emin olun.
  • Kanal bulunamıyor: İsim yerine kanal ID'si kullanın (channels.cache.get('KANAL_ID')) — isimler değişebilir.
  • Yayına alma: Botu 7/24 ayakta tutmak için bir VPS'te pm2 gibi bir süreç yöneticisiyle çalıştırın.

Sık Sorulan Sorular

Bu bot için sunucu (hosting) şart mı?

Bot çalıştığı sürece Discord'a bağlı kalması gerekir, yani bilgisayarın kapanınca durur. Sürekli çevrimiçi olması için ucuz bir VPS yeterlidir; pm2 ile çökse bile otomatik yeniden başlar.

node-canvas yerine neden @napi-rs/canvas?

İkisinin API'si neredeyse aynıdır, ancak @napi-rs/canvas önceden derlenmiş ikili dosyalarla gelir; Cairo gibi sistem bağımlılıkları kurmanız gerekmez. Bu, özellikle paylaşımlı sunucularda ve farklı işletim sistemlerinde kurulumu çok kolaylaştırır.

Görsele kaçıncı üye olduğunu da ekleyebilir miyim?

Evet. member.guild.memberCount ile o anki üye sayısını alıp görsele "Sen 250. üyemizsin!" gibi bir metin olarak çizebilirsin. Aynı ctx.fillText mantığıyla istediğin kadar dinamik bilgi ekleyebilirsin.

Topluluğun için özel bir hoş geldin botu mu istiyorsun? Görsel tasarımdan rol verme sistemine kadar ihtiyacına uygun bir Discord botu geliştirebilirim. Benimle iletişime geç ve projeni konuşalım.

Devamı için