- Katılım
- 9 Şub 2024
- Mesajlar
- 946
- Tepkime puanı
- 1,780
- Puanları
- 93
jhaeger
Moderasyon Botu Yapımı: Ban, Kick, Timeout, Uyarı (Warn) Sistemi
discord.js v14 ile slash komutlu moderasyon botu
discord.js v14 ile slash komutlu moderasyon botu
Selamlar SroCave Ailesi!
Bu rehberde sunucunuzu yönetmeyi kolaylaştıracak bir moderasyon botu yapacağız. Botumuzda şu komutlar olacak:
/ban: Kullanıcıyı sunucudan yasaklar/kick: Kullanıcıyı sunucudan atar/timeoutve/timeout-kaldir: Kullanıcıyı susturur / susturmayı kaldırır/warn: Kullanıcıya uyarı verir (3 uyarıda otomatik timeout)/uyarilar: Kullanıcının uyarılarını listeler/uyarilari-sil: Kullanıcının uyarılarını temizler
Not: Slash komutlarının temelini bilmiyorsanız önce şu konuya göz atın: Discord Botlarında Slash Commands (/) Sistemi Nasıl Kurulur?
Gerekli Kurulum
Node.js 18 veya üzeri gereklidir. Proje klasörünüzde:
Bash:
npm init -y
npm install discord.js better-sqlite3 dotenv
Proje klasörüne
.env dosyası oluşturup şunları yazın:
Kod:
TOKEN=BOT_TOKENINIZ
CLIENT_ID=BOT_UYGULAMA_IDSI
GUILD_ID=SUNUCU_IDSI
Uyarı:
.envdosyanızı asla başkalarıyla paylaşmayın ve.gitignoreiçine ekleyin. Token güvenliği için: Discord Bot Token'ı Nedir? Güvenli Nasıl Saklanır?
Botun İhtiyacı Olan Yetkiler
Botu sunucuya davet ederken (OAuth2 > URL Generator) şu yetkileri verin:- Ban Members :
/baniçin - Kick Members :
/kickiçin - Moderate Members :
/timeoutve/warniçin - Scope olarak
botveapplications.commands
Çok Önemli: Botun rolü, işlem yapacağı üyelerin rollerinden daha yukarıda olmalıdır. Sunucu Ayarları > Roller bölümünden bot rolünü yukarı taşıyın. Aksi halde
Missing Permissionshatası alırsınız.
Tam Bot Kodu (index.js)
JavaScript:
require('dotenv').config();
const {
Client, GatewayIntentBits, REST, Routes, SlashCommandBuilder,
PermissionFlagsBits, EmbedBuilder, MessageFlags
} = require('discord.js');
const Database = require('better-sqlite3');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
// ---------- Veritabanı ----------
const db = new Database('moderasyon.db');
db.exec(`
CREATE TABLE IF NOT EXISTS warns (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT,
user_id TEXT,
mod_id TEXT,
reason TEXT,
date TEXT
)
`);
// ---------- Yardımcı Fonksiyonlar ----------
const kullaniciSecenegi = o => o.setName('kullanici').setDescription('Hedef kullanıcı').setRequired(true);
const sebepSecenegi = o => o.setName('sebep').setDescription('İşlem sebebi');
const bilgi = (baslik, renk, aciklama) =>
new EmbedBuilder().setTitle(baslik).setColor(renk).setDescription(aciklama).setTimestamp();
const hataGonder = (interaction, mesaj) =>
interaction.reply({ content: `❌ ${mesaj}`, flags: MessageFlags.Ephemeral });
// Hedef üyeye işlem yapılabilir mi? (hiyerarşi kontrolü)
function kontrol(interaction, uye) {
if (uye.id === interaction.user.id) return 'Kendinize işlem uygulayamazsınız.';
if (uye.id === interaction.guild.ownerId) return 'Sunucu sahibine işlem uygulanamaz.';
if (uye.id === interaction.client.user.id) return 'Bota işlem uygulayamazsınız.';
const sahipMi = interaction.user.id === interaction.guild.ownerId;
if (!sahipMi && uye.roles.highest.position >= interaction.member.roles.highest.position) {
return 'Sizden üst veya eşit roldeki birine işlem uygulayamazsınız.';
}
return null;
}
// ---------- Komut Tanımları ----------
const commands = [
new SlashCommandBuilder().setName('ban').setDescription('Kullanıcıyı sunucudan yasaklar')
.addUserOption(kullaniciSecenegi).addStringOption(sebepSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
new SlashCommandBuilder().setName('kick').setDescription('Kullanıcıyı sunucudan atar')
.addUserOption(kullaniciSecenegi).addStringOption(sebepSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers),
new SlashCommandBuilder().setName('timeout').setDescription('Kullanıcıyı belirli süre susturur')
.addUserOption(kullaniciSecenegi)
.addIntegerOption(o => o.setName('dakika').setDescription('Süre (dakika, en fazla 40320)')
.setRequired(true).setMinValue(1).setMaxValue(40320))
.addStringOption(sebepSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
new SlashCommandBuilder().setName('timeout-kaldir').setDescription('Kullanıcının susturmasını kaldırır')
.addUserOption(kullaniciSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
new SlashCommandBuilder().setName('warn').setDescription('Kullanıcıya uyarı verir')
.addUserOption(kullaniciSecenegi).addStringOption(sebepSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
new SlashCommandBuilder().setName('uyarilar').setDescription('Kullanıcının uyarılarını listeler')
.addUserOption(kullaniciSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
new SlashCommandBuilder().setName('uyarilari-sil').setDescription('Kullanıcının tüm uyarılarını siler')
.addUserOption(kullaniciSecenegi)
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
].map(c => c.toJSON());
// ---------- Komutları Discord'a Kaydet ----------
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);
client.once('ready', async () => {
console.log(`Bot giriş yaptı: ${client.user.tag}`);
try {
await rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
{ body: commands }
);
console.log('Slash komutlar yüklendi!');
} catch (error) {
console.error(error);
}
});
// ---------- Komutları Çalıştır ----------
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
const { commandName, options, guild } = interaction;
const hedef = options.getUser('kullanici');
const sebep = options.getString('sebep') || 'Sebep belirtilmedi';
const uye = hedef ? await guild.members.fetch(hedef.id).catch(() => null) : null;
try {
// ----- BAN -----
if (commandName === 'ban') {
if (uye) {
const hata = kontrol(interaction, uye);
if (hata) return hataGonder(interaction, hata);
if (!uye.bannable) return hataGonder(interaction, 'Bu kullanıcıyı banlayamam. Bot rolümü yukarı taşıyın.');
}
await hedef.send(`🔨 **${guild.name}** sunucusundan yasaklandınız.\nSebep: ${sebep}`).catch(() => {});
await guild.members.ban(hedef, { reason: `${interaction.user.tag}: ${sebep}` });
return interaction.reply({
embeds: [bilgi('🔨 Kullanıcı Yasaklandı', 0xED4245,
`**Kullanıcı:** ${hedef.tag}\n**Yetkili:** ${interaction.user}\n**Sebep:** ${sebep}`)]
});
}
// Bundan sonraki komutlar için kullanıcı sunucuda olmalı
if (['kick', 'timeout', 'timeout-kaldir', 'warn'].includes(commandName)) {
if (!uye) return hataGonder(interaction, 'Bu kullanıcı sunucuda bulunamadı.');
const hata = kontrol(interaction, uye);
if (hata) return hataGonder(interaction, hata);
}
// ----- KICK -----
if (commandName === 'kick') {
if (!uye.kickable) return hataGonder(interaction, 'Bu kullanıcıyı atamam. Bot rolümü yukarı taşıyın.');
await hedef.send(`👢 **${guild.name}** sunucusundan atıldınız.\nSebep: ${sebep}`).catch(() => {});
await uye.kick(`${interaction.user.tag}: ${sebep}`);
return interaction.reply({
embeds: [bilgi('👢 Kullanıcı Atıldı', 0xFEE75C,
`**Kullanıcı:** ${hedef.tag}\n**Yetkili:** ${interaction.user}\n**Sebep:** ${sebep}`)]
});
}
// ----- TIMEOUT -----
if (commandName === 'timeout') {
const dakika = options.getInteger('dakika');
if (!uye.moderatable) return hataGonder(interaction, 'Bu kullanıcıyı susturamam. Bot rolümü yukarı taşıyın.');
await uye.timeout(dakika * 60 * 1000, `${interaction.user.tag}: ${sebep}`);
return interaction.reply({
embeds: [bilgi('🔇 Kullanıcı Susturuldu', 0xFEE75C,
`**Kullanıcı:** ${hedef}\n**Süre:** ${dakika} dakika\n**Yetkili:** ${interaction.user}\n**Sebep:** ${sebep}`)]
});
}
// ----- TIMEOUT KALDIR -----
if (commandName === 'timeout-kaldir') {
if (!uye.moderatable) return hataGonder(interaction, 'Bu kullanıcıya işlem yapamam.');
await uye.timeout(null);
return interaction.reply({
embeds: [bilgi('🔊 Susturma Kaldırıldı', 0x57F287, `**Kullanıcı:** ${hedef}\n**Yetkili:** ${interaction.user}`)]
});
}
// ----- WARN -----
if (commandName === 'warn') {
db.prepare('INSERT INTO warns (guild_id, user_id, mod_id, reason, date) VALUES (?, ?, ?, ?, ?)')
.run(guild.id, hedef.id, interaction.user.id, sebep, new Date().toLocaleString('tr-TR'));
const sayi = db.prepare('SELECT COUNT(*) AS c FROM warns WHERE guild_id = ? AND user_id = ?')
.get(guild.id, hedef.id).c;
await hedef.send(`⚠️ **${guild.name}** sunucusunda uyarı aldınız.\nSebep: ${sebep}\nToplam uyarı: ${sayi}`).catch(() => {});
let ek = '';
if (sayi >= 3 && uye.moderatable) {
await uye.timeout(60 * 60 * 1000, '3 uyarıya ulaştı (otomatik)');
ek = '\n\n🔇 **3 uyarıya ulaştığı için 1 saat susturuldu.**';
}
return interaction.reply({
embeds: [bilgi('⚠️ Uyarı Verildi', 0xFEE75C,
`**Kullanıcı:** ${hedef}\n**Yetkili:** ${interaction.user}\n**Sebep:** ${sebep}\n**Toplam Uyarı:** ${sayi}${ek}`)]
});
}
// ----- UYARILAR -----
if (commandName === 'uyarilar') {
const liste = db.prepare('SELECT * FROM warns WHERE guild_id = ? AND user_id = ? ORDER BY id DESC LIMIT 10')
.all(guild.id, hedef.id);
if (liste.length === 0) {
return interaction.reply({ embeds: [bilgi('📋 Uyarılar', 0x57F287, `${hedef} kullanıcısının hiç uyarısı yok.`)] });
}
const metin = liste.map(w => `**#${w.id}** • ${w.reason}\n└ <@${w.mod_id}> • ${w.date}`).join('\n\n');
return interaction.reply({
embeds: [bilgi(`📋 ${hedef.username} - Son ${liste.length} Uyarı`, 0x5865F2, metin)]
});
}
// ----- UYARILARI SİL -----
if (commandName === 'uyarilari-sil') {
const sonuc = db.prepare('DELETE FROM warns WHERE guild_id = ? AND user_id = ?').run(guild.id, hedef.id);
return interaction.reply({
embeds: [bilgi('🧹 Uyarılar Temizlendi', 0x57F287, `${hedef} kullanıcısının **${sonuc.changes}** uyarısı silindi.`)]
});
}
} catch (error) {
console.error(error);
const mesaj = { content: '❌ Bir hata oluştu. Bot yetkilerini ve rol sırasını kontrol edin.', flags: MessageFlags.Ephemeral };
if (interaction.replied || interaction.deferred) interaction.followUp(mesaj);
else interaction.reply(mesaj);
}
});
client.login(process.env.TOKEN);
Botu başlatmak için:
Bash:
node index.js
Kod Açıklaması
- setDefaultMemberPermissions: Komutu sadece ilgili yetkiye sahip kişilerin görmesini ve kullanmasını sağlar.
- kontrol(): Kendinize, sunucu sahibine veya üst rolündeki birine işlem yapılmasını engeller.
- bannable / kickable / moderatable: Botun o kişiye işlem yapıp yapamayacağını söyler (rol sırası kontrolü).
- hedef.send(): Kullanıcıya özel mesaj atar. DM'leri kapalıysa hata vermemesi için
.catch(() => {})kullanılır. - better-sqlite3: Uyarıları
moderasyon.dbdosyasında kalıcı olarak saklar. - flags: MessageFlags.Ephemeral: Hata mesajını sadece komutu kullanan kişiye gösterir.
Örnek Kullanım
Kod:
/ban kullanici:@Ahmet sebep:Reklam yapmak
/kick kullanici:@Ahmet sebep:Kurallara uymamak
/timeout kullanici:@Ahmet dakika:30 sebep:Spam
/timeout-kaldir kullanici:@Ahmet
/warn kullanici:@Ahmet sebep:Küfür
/uyarilar kullanici:@Ahmet
/uyarilari-sil kullanici:@Ahmet
Sık Karşılaşılan Hatalar
- Missing Permissions: Bot rolü hedefin rolünden aşağıda veya botta gerekli yetki yok.
- Komutlar görünmüyor: Botu
applications.commandsscope'u ile davet etmemiş olabilirsiniz veyaGUILD_IDyanlıştır. - Cannot find module 'better-sqlite3':
npm install better-sqlite3komutunu çalıştırın. - Unknown Interaction: Bot 3 saniye içinde cevap veremedi, işlem uzunsa
interaction.deferReply()kullanın.
Hata çözümleri için ayrıntılı konumuz: Discord Bot Hataları ve Çözümleri
Geliştirme Fikirleri
- Moderasyon işlemlerini bir log kanalına göndermek
- Uyarı eşiğini (3) ve otomatik ceza süresini ayarlanabilir yapmak
/temizlekomutu ile toplu mesaj silmek/unbankomutu eklemek- Uyarılara tarih bazlı otomatik sona erme eklemek
Faydalı Kaynaklar
Sonuç
Artık ban, kick, timeout ve kalıcı uyarı sistemiyle çalışan, rol hiyerarşisini de kontrol eden bir moderasyon botunuz var. Yetkileri doğru verdiğiniz sürece sunucunuzu güvenle yönetebilirsiniz.Not: Botunuzu geliştirirken kodlarınızı yedeklemeyi ve veritabanı dosyasını güvenli tutmayı unutmayın!
Sorularınızı aşağıya yazabilirsiniz, herkese kolay gelsin!





