Bir Discord botu büyüdükçe en sık karşılaşılan sorunlardan biri komut taşmasıdır: bir kullanıcı aynı komutu saniyede onlarca kez tetikler, bot da her seferinde cevap verir. Sağlam bir discord bot cooldown sistemi tam burada devreye girer; her kullanıcıya bir komutu yeniden çalıştırması için kısa bir bekleme süresi tanır ve hem botunuzu hem de Discord'un API limitlerini korur. Bu rehberde cooldown mantığını sıfırdan kurup, discord.js ile çalışan gerçek bir örnek yazacağız ve spam'a karşı ek katmanlar ekleyeceğiz.
Neden cooldown ve spam koruması gerekir?
Cooldown sadece bir "kullanıcıyı yavaşlatma" özelliği değildir; botunuzun istikrarı için bir savunma hattıdır. Başlıca nedenler:
- API rate limit'leri: Discord, botların gönderebileceği istek sayısını sınırlar. Aşırı mesaj/yanıt gönderirseniz
429 Too Many Requestscevabı alır ve geçici olarak engellenirsiniz. - Kaynak tüketimi: Veritabanı sorgusu, harici API çağrısı veya görsel üretimi gibi pahalı komutlar spam'le sunucunuzu kilitleyebilir.
- Kötüye kullanım: Ekonomi, çekiliş veya ödül komutlarında cooldown olmazsa kullanıcılar sistemi suistimal eder.
- Kanal kirliliği: Tekrar eden bot yanıtları kanalları okunmaz hale getirir.
Cooldown mantığı: zaman damgasını saklamak
Tüm cooldown sistemlerinin temelinde tek bir fikir vardır: bir kullanıcının komutu en son ne zaman çalıştırdığını saklamak ve aradan yeterli süre geçip geçmediğini kontrol etmek. Sözde kod şöyle işler:
- Kullanıcı komutu çalıştırır.
- O kullanıcı için kayıtlı bir zaman damgası var mı diye bak.
- Varsa ve
(şimdi - kayıtlı zaman) < cooldownise: reddet ve kalan süreyi söyle. - Aksi halde: komutu çalıştır ve yeni zaman damgasını kaydet.
Saklama yeri için küçük botlarda bellek (bir Map) yeterlidir. Bot yeniden başlatıldığında cooldown'ların sıfırlanmasını istemiyorsanız Redis veya bir veritabanı kullanın.
discord.js ile komut bazlı cooldown sistemi
discord.js v14'te slash komutları için yaygın ve resmi olarak önerilen yaklaşım, her komut için ayrı bir Collection tutmaktır. Collection, JavaScript'in Map sınıfını genişletir. Komut dosyanıza isteğe bağlı bir cooldown alanı ekleyebilirsiniz:
// commands/ping.js
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
cooldown: 5, // saniye
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Botun gecikmesini gösterir'),
async execute(interaction) {
await interaction.reply(`Pong! ${interaction.client.ws.ping}ms`);
},
};
Asıl kontrol, interactionCreate olayında komut çalıştırılmadan önce yapılır:
// events/interactionCreate.js
const { Events, Collection } = require('discord.js');
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
const { cooldowns } = interaction.client;
if (!cooldowns.has(command.data.name)) {
cooldowns.set(command.data.name, new Collection());
}
const now = Date.now();
const timestamps = cooldowns.get(command.data.name);
const cooldownAmount = (command.cooldown ?? 3) * 1000;
if (timestamps.has(interaction.user.id)) {
const expiration = timestamps.get(interaction.user.id) + cooldownAmount;
if (now < expiration) {
const expiredTimestamp = Math.round(expiration / 1000);
return interaction.reply({
content: `Bu komutu tekrar kullanmak icin bekle. Yeniden <t:${expiredTimestamp}:R> kullanabilirsin.`,
ephemeral: true,
});
}
}
timestamps.set(interaction.user.id, now);
setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
}
},
};
Burada client.cooldowns kümesini başta bir kez tanımlamayı unutmayın: bot istemcinizi kurarken client.cooldowns = new Collection(); satırını ekleyin. <t:...:R> Discord'un göreli zaman damgası biçimidir ve kullanıcıya "3 saniye sonra" gibi otomatik güncellenen bir metin gösterir.
Kullanıcı, sunucu ve global cooldown türleri
Yukarıdaki örnek kullanıcı başına cooldown uygular ki en yaygın ihtiyaç budur. Ancak farklı senaryolar farklı anahtarlar gerektirir:
- Kullanıcı başına: Anahtar olarak
interaction.user.idkullanın. Kişisel komutlar (profil, bakiye) için idealdir. - Sunucu başına: Anahtar olarak
interaction.guildIdkullanın. Tüm sunucuyu etkileyen komutlarda (duyuru, toplu işlem) mantıklıdır. - Kanal başına:
interaction.channelIdile belirli kanallarda hız sınırı koyun. - Global: Sabit bir anahtar (örn. komut adı) kullanın; pahalı bir harici API'yi koruyan komutlar için.
Sadece anahtar olarak hangi alanı sakladığınızı değiştirerek aynı mantığı yeniden kullanabilirsiniz. Birden çok türü birleştirmek için bileşik anahtar oluşturun: `${interaction.guildId}-${interaction.user.id}`.
Discord'un kendi rate limit'leri (429) ve discord.js
Kendi cooldown'unuz, kullanıcıların botu spamlamasını engeller; ama botun kendisi de Discord API'sine karşı limitlere tabidir. İyi haber: discord.js'in altında çalışan @discordjs/rest katmanı, global ve route bazlı rate limit'leri otomatik yönetir, istekleri kuyruğa alır ve 429 durumunda bekler. Yine de bilmeniz gereken noktalar var:
- Bir döngü içinde yüzlerce mesaj göndermeyin; bunun yerine işlemleri toplu hale getirin veya araya gecikme koyun.
- Geçici sohbet yanıtlarında (yalnızca komutu çalıştıran kişinin gördüğü)
ephemeral: truekullanmak kanal kirliliğini ve gereksiz mesaj trafiğini azaltır. - Uzun süren işlemlerde önce
interaction.deferReply()çağırın; aksi halde 3 saniyelik etkileşim zaman aşımına takılırsınız. - Genel rate limit'e takılırsanız
rateLimitedolayını dinleyerek loglayabilir ve istek hacminizi düşürebilirsiniz.
Spam'a karşı ek önlemler
Cooldown ilk savunma hattıdır; agresif kötüye kullanıma karşı şu katmanları ekleyin:
- Kademeli ceza: Cooldown'a tekrar tekrar takılan kullanıcının bekleme süresini geçici olarak artırın.
- İzleme listesi: Belirli bir eşik üstünde komut deneyen kullanıcıyı kısa süre yok sayın (basit bir hız sınırlayıcı).
- İzin kontrolü: Yöneticileri cooldown'dan muaf tutmak için
interaction.memberPermissionskontrolü ekleyin. - Kalıcı saklama: Ödül/ekonomi komutlarında cooldown'u Redis veya veritabanında tutun ki bot yeniden başladığında suistimal edilmesin.
Sık Sorulan Sorular
Cooldown verilerini bellekte tutmak güvenli mi?
Küçük ve orta ölçekli botlar için bellek (Collection/Map) yeterli ve hızlıdır. Ancak bot her yeniden başladığında sıfırlanır. Çekiliş, ödül veya ekonomi gibi suistimale açık komutlarda cooldown'u Redis veya bir veritabanında kalıcı saklamanızı öneririm.
discord.js rate limit'leri benim için hallediyorsa neden ayrıca cooldown yazayım?
İkisi farklı amaca hizmet eder. discord.js'in REST katmanı botun Discord'a gönderdiği istekleri düzenler. Sizin cooldown'unuz ise kullanıcıların botu ne sıklıkta tetikleyebileceğini kontrol eder; mantık spam'ı, kaynak tüketimini ve oyun mekaniği suistimalini engeller.
discord.py için de aynı mantık geçerli mi?
Evet, prensip aynıdır: son kullanım zamanını saklayıp farkı kontrol edersiniz. discord.py'de ayrıca yerleşik @commands.cooldown dekoratörü ve BucketType seçenekleri vardır; bu da kullanıcı/sunucu/kanal bazlı cooldown'u tek satırda tanımlamanızı sağlar.
Botunuz spam altında mı eziliyor? Komut cooldown'ları, rate limit yönetimi ve kötüye kullanıma dayanıklı bir mimari ile botunuzu sağlamlaştırmanıza yardımcı olabilirim. Benimle iletişime geçin ve projenizi konuşalım.