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 INTENTportalda açık değildir veyaGuildMembersniyeti koda eklenmemiştir. - Avatar yüklenmiyor: Uzantıyı
pngolarak 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
pm2gibi 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.