Bir discord bot event dinleyicisi yazmak, botunuzu statik bir komut makinesinden gerçek zamanlı tepki veren bir uygulamaya dönüştüren adımdır. Discord, sunucuda olup biten her şeyi — bir mesajın gönderilmesi, bir üyenin katılması, bir tepkinin eklenmesi — kalıcı bir WebSocket bağlantısı üzerinden botunuza olay (event) olarak iletir. Bu yazıda olay akışının nasıl çalıştığını, gateway intent'lerinin neden zorunlu olduğunu ve sağlam bir event listener yapısını nasıl kuracağınızı discord.js üzerinden adım adım anlatıyorum.
Gateway nedir ve event akışı nasıl işler?
Discord botları aslında iki farklı kanaldan konuşur. Bir mesaj göndermek, kanal oluşturmak gibi eylemler REST API üzerinden HTTP istekleriyle yapılır. Buna karşılık sunucuda olan biteni dinlemek Gateway adı verilen kalıcı bir WebSocket bağlantısı üzerinden gerçekleşir.
Akış şöyle ilerler: botunuz başlatıldığında Gateway'e bağlanır ve token'ı ile kimlik doğrular (IDENTIFY). Discord, bağlantının canlı kaldığından emin olmak için düzenli aralıklarla heartbeat bekler. Bağlantı kurulduktan sonra sunucularda bir şey olduğunda Discord, botunuza bir olay paketi (örneğin MESSAGE_CREATE) gönderir. Kütüphane bu ham paketi yakalar, çözümler ve sizin kaydettiğiniz dinleyici fonksiyonunu tetikler.
Bu yüzden event tabanlı bir bot asla "soru sorup cevap beklemez"; tam tersine, sürekli açık bir hat üzerinden olayları itme (push) mantığıyla alır. Botun mimarisini de bu gerçeğe göre kurmak gerekir.
Intent'ler: neyi dinleyebileceğinizi belirleyen anahtar
2020'den itibaren Discord, botların hangi olay türlerini alacağını Gateway Intents ile sınırladı. Intent, "ben şu kategorideki olayları almak istiyorum" diyen bir izin bayrağıdır. İstemediğiniz olayları almazsanız hem ağ trafiği hem de bellek kullanımı düşer; daha önemlisi, ilgisiz verilere erişiminiz olmaz.
Intent'leri açıkça belirtmezseniz ilgili olaylar botunuza hiç ulaşmaz. En sık yaşanan "neden mesaj olayım tetiklenmiyor?" sorununun cevabı genelde eksik intent'tir. Bazı intent'ler ise privileged (ayrıcalıklı) kabul edilir ve hem Discord Developer Portal'da açılmaları hem de kodda belirtilmeleri gerekir:
GuildMembers— üye katılma/ayrılma, üye listesi (privileged).MessageContent— mesajların metin içeriğine erişim (privileged).GuildPresences— çevrimiçi durumu ve aktivite (privileged).
100'den fazla sunucuda bulunan botlarda privileged intent'ler için Discord'dan doğrulama (verification) almanız gerekir. Bu yüzden botunuzu gerçekten ihtiyaç duyduğu intent'lerle sınırlı tutmak hem iyi bir pratik hem de ölçeklenme için zorunluluktur.
İlk event listener'ı kurmak
discord.js v14 ile minimal bir bot iskeleti şöyle görünür. Client nesnesini gerekli intent'lerle oluşturur, ardından olaylara dinleyici bağlarız:
const { Client, GatewayIntentBits, Events } = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
// Bağlantı hazır olduğunda bir kez tetiklenir
client.once(Events.ClientReady, (c) => {
console.log(`Giriş yapıldı: ${c.user.tag}`);
});
// Her yeni mesajda tetiklenir
client.on(Events.MessageCreate, (message) => {
if (message.author.bot) return; // botların mesajlarını yok say
if (message.content === '!ping') {
message.reply('Pong!');
}
});
client.login(process.env.DISCORD_TOKEN);
Burada iki önemli ayrım var. client.once dinleyiciyi yalnızca bir kez çalıştırır — ClientReady gibi yaşam döngüsünde bir defa olan olaylar için idealdir. client.on ise her olayda yeniden tetiklenir. Ayrıca message.author.bot kontrolü, botların birbirini tetikleyerek sonsuz döngüye girmesini önler; bu, gözden kaçan ama kritik bir savunmadır.
Sık kullanılan olaylar ve doğru kullanım
Gerçek bir bot tek bir olaya değil, birkaçına birden tepki verir. En sık çalışacağınız olaylar şunlardır:
GuildMemberAdd— yeni üye katıldığında karşılama mesajı veya otomatik rol için (GuildMembersintent'i gerekir).InteractionCreate— slash komutları, butonlar ve menüler için modern ve önerilen yol.MessageReactionAdd— tepki tabanlı rol verme (reaction roles) sistemleri için.GuildCreate— bot yeni bir sunucuya eklendiğinde, kurulum yapmak için.
Modern botlarda komut mantığını MessageCreate yerine InteractionCreate ve slash komutları üzerine kurmak gerekir; çünkü MessageContent ayrıcalıklı bir intent'tir ve Discord uzun vadede mesaj içeriğine dayalı komutları teşvik etmiyor.
Olay yapısını ölçeklenebilir tutmak
Tüm dinleyicileri tek bir dosyaya yığmak, bot büyüdükçe yönetilemez hale gelir. Sağlam bir yaklaşım, her olayı kendi dosyasına koyan bir event handler kurmaktır. Her dosya olayın adını ve çalıştırılacak fonksiyonu dışa aktarır:
// events/messageCreate.js
const { Events } = require('discord.js');
module.exports = {
name: Events.MessageCreate,
once: false,
execute(message) {
if (message.author.bot) return;
// ... mantık
},
};
// dinleyicileri otomatik yükleyen kısım
const fs = require('node:fs');
const path = require('node:path');
const eventsPath = path.join(__dirname, 'events');
const files = fs.readdirSync(eventsPath).filter((f) => f.endsWith('.js'));
for (const file of files) {
const event = require(path.join(eventsPath, file));
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
}
Bu yapı sayesinde yeni bir olay eklemek için sadece events/ klasörüne bir dosya bırakmanız yeterli olur. Kod okunabilir kalır, her olayın sorumluluğu ayrışır ve hata ayıklamak çok daha kolaylaşır.
Sık yapılan hatalar
Event listener'larda en çok karşılaşılan tuzaklar şunlardır: (1) Intent eksikliği — olayın tetiklenmemesinin bir numaralı sebebi. (2) Bağlanmadan önce çok fazla olay dinlemek yerine, gerçekten ihtiyaç duyduklarınızı seçmek. (3) Dinleyici içinde ağ isteklerini await ile beklerken hataları yakalamamak; tek bir yakalanmamış hata botu çökertebilir. Dinleyicileri try/catch ile sarmak ve botu bir process manager (örneğin PM2) altında çalıştırmak, üretim ortamında kararlılığı belirgin şekilde artırır.
Sık Sorulan Sorular
Intent'i koda eklemek tek başına yeterli mi?
Privileged intent'ler için hayır. MessageContent, GuildMembers ve GuildPresences intent'lerini hem kodda belirtmeli hem de Discord Developer Portal'daki bot ayarlarından açmalısınız. İkisinden biri eksikse olaylar gelmez.
client.on ile client.once arasındaki fark nedir?
client.on dinleyiciyi her olay tekrarında çalıştırır; client.once ise yalnızca ilk seferinde çalıştırıp dinleyiciyi otomatik kaldırır. ClientReady gibi bir kez gerçekleşen olaylar için once doğru seçimdir.
Mesaj içeriği boş geliyor, neden?
Büyük olasılıkla MessageContent intent'i eksiktir. Bu ayrıcalıklı intent olmadan message.content boş gelir; sadece botun mention edildiği veya DM olan mesajlarda içerik dolu olur.
Botunuz için sağlam bir event mimarisi mi kurmak istiyorsunuz? Gateway intent'lerinden ölçeklenebilir handler yapısına kadar Discord botlarınızı baştan sona geliştiriyorum. Projenizi konuşmak için benimle iletişime geçin.