Bir discord log botu, sunucunda yaşanan olayları sessizce kaydeden ve moderasyon ekibine sağlam bir denetim kaydı sunan en değerli araçlardan biridir. Kim hangi mesajı sildi, kim sunucudan ayrıldı, birine ne zaman hangi rol verildi — bütün bunları kalıcı bir kanalda görmek, tartışmaları "ben yapmadım" seviyesinden kanıta dayalı bir düzene taşır. Bu yazıda discord.js v14 ile mesaj silme, üye giriş/çıkış ve rol değişimlerini loglayan pratik bir bot kuracağız.
Discord log botu ne işe yarar?
Discord'un yerleşik denetim kaydı (audit log) yalnızca belli yönetici eylemlerini, sınırlı süreyle tutar ve mesaj içeriklerini göstermez. Kendi log botunu yazdığında ise neyi, nasıl ve nereye kaydedeceğine sen karar verirsin. Tipik kullanım senaryoları şunlardır:
- Mesaj denetimi: silinen ve düzenlenen mesajların içeriğini saklamak.
- Üye hareketi: katılım ve ayrılışları, hesap yaşıyla birlikte izlemek (raid tespiti için değerli).
- Yetki takibi: rol verme/alma işlemlerini ve bunu kimin yaptığını görmek.
Bütün bu olayları tek bir #loglar kanalına, okunaklı embed'ler hâlinde göndereceğiz. Embed'leri renk koduyla ayırmak (silme için kırmızı, katılım için yeşil, rol için mor) bir bakışta olay türünü anlamanı sağlar. Bu kanalı yalnızca moderatörlerin görebileceği şekilde kısıtlamak da iyi bir alışkanlıktır; loglar denetim amaçlıdır, herkese açık olmaları gerekmez.
Gerekli intents ve partial yapılandırması
Loglama, ayrıcalıklı (privileged) intent'lere ihtiyaç duyar. GuildMembers üye olaylarını, MessageContent ise mesaj içeriğini görmek için zorunludur. Bu ikisini Discord Developer Portal'daki uygulama ayarlarından da etkinleştirmen gerekir. Ayrıca önbellekte olmayan eski mesajların olaylarını yakalamak için partials tanımlamalısın:
// index.js
const { Client, GatewayIntentBits, Partials } = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
partials: [Partials.Message, Partials.Channel, Partials.GuildMember],
});
const LOG_CHANNEL_ID = '123456789012345678';
client.login(process.env.TOKEN);
Tekrarı azaltmak için tüm olaylarda kullanacağımız küçük bir yardımcı fonksiyon tanımlayalım:
const { EmbedBuilder } = require('discord.js');
function log(guild, embed) {
const channel = guild.channels.cache.get(LOG_CHANNEL_ID);
if (channel) channel.send({ embeds: [embed] }).catch(() => {});
}
Mesaj silme ve düzenlemeyi loglama
Mesaj silmeyi Events.MessageDelete olayıyla yakalarız. Önemli bir sınırlama vardır: bot başlamadan önce gönderilmiş ve önbellekte olmayan bir mesaj silinirse, olay yalnızca kısmi (partial) gelir ve içeriği kurtaramazsın — çünkü mesaj artık Discord'da yoktur. Bu yüzden partial mesajları zarifçe geçeriz:
const { Events } = require('discord.js');
client.on(Events.MessageDelete, async (message) => {
if (message.partial) return; // önbellekte yoksa içerik alınamaz
if (message.author?.bot) return;
log(message.guild, new EmbedBuilder()
.setColor(0xED4245)
.setAuthor({ name: message.author.tag, iconURL: message.author.displayAvatarURL() })
.setDescription(
`🗑️ **Mesaj silindi** — <#${message.channel.id}>\n` +
(message.content || '*İçerik yok*'),
)
.setTimestamp());
});
Düzenlemeleri Events.MessageUpdate ile dinleriz. Bir mesaja link önizlemesi (embed) yüklendiğinde de bu olay tetiklenir; gereksiz log üretmemek için içerik gerçekten değişmediyse erken çıkarız:
client.on(Events.MessageUpdate, async (oldMsg, newMsg) => {
if (oldMsg.partial || newMsg.author?.bot) return;
if (oldMsg.content === newMsg.content) return; // sadece embed yüklendi
log(newMsg.guild, new EmbedBuilder()
.setColor(0xFEE75C)
.setAuthor({ name: newMsg.author.tag, iconURL: newMsg.author.displayAvatarURL() })
.setDescription(`✏️ **Mesaj düzenlendi** — [Mesaja git](${newMsg.url})`)
.addFields(
{ name: 'Önce', value: (oldMsg.content || '—').slice(0, 1024) },
{ name: 'Sonra', value: (newMsg.content || '—').slice(0, 1024) },
)
.setTimestamp());
});
Embed alanları en fazla 1024 karakter taşıyabildiği için slice(0, 1024) ile kırparız; aksi halde uzun mesajlar gönderimi başarısız kılar.
Üye giriş ve çıkışlarını loglama
Katılımlar Events.GuildMemberAdd, ayrılışlar ise Events.GuildMemberRemove ile gelir. Yeni katılan üyenin hesabının ne zaman açıldığını <t:...:R> biçimiyle göreceli zaman damgası olarak göstermek, çok yeni hesaplardan oluşan bir raid dalgasını fark etmeni kolaylaştırır:
client.on(Events.GuildMemberAdd, (member) => {
const created = Math.floor(member.user.createdTimestamp / 1000);
log(member.guild, new EmbedBuilder()
.setColor(0x57F287)
.setAuthor({ name: member.user.tag, iconURL: member.user.displayAvatarURL() })
.setDescription(`📥 **${member} sunucuya katıldı**`)
.addFields({ name: 'Hesap yaşı', value: `<t:${created}:R>` })
.setTimestamp());
});
client.on(Events.GuildMemberRemove, (member) => {
log(member.guild, new EmbedBuilder()
.setColor(0xED4245)
.setAuthor({ name: member.user.tag, iconURL: member.user.displayAvatarURL() })
.setDescription(`📤 **${member.user.tag} sunucudan ayrıldı**`)
.setTimestamp());
});
Rol değişimlerini loglama
Rol değişimleri ayrı bir olay değildir; üye güncellemesinin (Events.GuildMemberUpdate) bir parçasıdır. Eklenen ve kaldırılan rolleri bulmak için eski ve yeni üyenin rol koleksiyonlarını karşılaştırırız:
client.on(Events.GuildMemberUpdate, (oldMember, newMember) => {
const oldRoles = oldMember.roles.cache;
const newRoles = newMember.roles.cache;
const added = newRoles.filter((r) => !oldRoles.has(r.id));
const removed = oldRoles.filter((r) => !newRoles.has(r.id));
if (added.size === 0 && removed.size === 0) return; // sadece takma ad vb. değişti
const lines = [];
if (added.size) lines.push(`➕ Eklenen: ${added.map((r) => r).join(', ')}`);
if (removed.size) lines.push(`➖ Alınan: ${removed.map((r) => r).join(', ')}`);
log(newMember.guild, new EmbedBuilder()
.setColor(0x5865F2)
.setAuthor({ name: newMember.user.tag, iconURL: newMember.user.displayAvatarURL() })
.setDescription(`🎭 **Rol değişimi** — ${newMember}\n${lines.join('\n')}`)
.setTimestamp());
});
Burada filter ile iki koleksiyon arasındaki farkı alıyoruz: yeni listede olup eskide olmayan roller eklenmiş, eskide olup yenide olmayanlar ise alınmıştır. Rol nesnesini doğrudan şablon dizgisine koymak onu otomatik olarak <@&rolId> biçiminde bir bahsetmeye (mention) dönüştürür, böylece log mesajında rol adı tıklanabilir ve renkli görünür.
İşlemi kimin yaptığını da görmek istersen, denetim kaydını sorgulayabilirsin. Bunun için botun ViewAuditLog iznine sahip olması gerekir:
const { AuditLogEvent } = require('discord.js');
const audit = await newMember.guild.fetchAuditLogs({
type: AuditLogEvent.MemberRoleUpdate, limit: 1,
});
const entry = audit.entries.first();
const executor = entry?.target.id === newMember.id ? entry.executor : null;
Sık Sorulan Sorular
Neden silinen bazı mesajların içeriği boş geliyor?
Çünkü o mesaj botun önbelleğinde yoktu. discord.js yalnızca çalışırken gördüğü mesajları önbelleğe alır; bot başlamadan önce gönderilmiş eski bir mesaj silinirse içeriği geri getirilemez. Bu, kötü kod değil Discord API'sinin bir kısıtıdır. Daha kapsamlı geçmiş istiyorsan mesajları kendi veritabanında saklaman gerekir.
Bot çevrimdışıyken olan olayları loglayabilir miyim?
Hayır. Gateway olayları yalnızca bot bağlıyken canlı yayınlanır; çevrimdışı geçen sürede olanlar kaçar. Bu yüzden log botunu PM2 gibi bir araçla 7/24 ayakta tutmak ve error olaylarını yakalamak önemlidir.
Birden fazla olay türünü ayrı kanallara ayırabilir miyim?
Evet. Tek bir LOG_CHANNEL_ID yerine olay türüne göre farklı kanal kimlikleri tanımlayıp log() fonksiyonuna hedef kanalı parametre olarak geçebilirsin; bu, mesaj loglarını üye loglarından ayrı tutmana yarar.
Sunucun için güvenilir bir loglama altyapısı mı istiyorsun? Moderasyon ihtiyaçlarına göre uçtan uca özelleştirilmiş bir Discord botu tasarlayıp kurabilirim. Projeni konuşmak için benimle iletişime geç.