aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Discord Bots

Discord Moderation Bot: Ban, Kick and Mute Commands

A Discord moderation bot is the most practical way to keep a growing server under control: it responds to rule violations in seconds, records every action, and hands moderation powers only to the right people. In this guide we build ban, kick and mute (timeout) commands from scratch with discord.js v14. The focus isn't just making the commands work; it's the details that matter on real servers — permission checks, role hierarchy, and audit log reasons.

Required permissions and intents

Moderation commands require the bot to manage members. In the Discord Developer Portal, grant your bot the Ban Members, Kick Members and Moderate Members permissions. Moderate Members is mandatory for timeouts (mutes). You also need to enable the GuildMembers intent in code and toggle on this privileged intent in the portal so you can access member data.

const { Client, GatewayIntentBits } = require('discord.js');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMembers,
  ],
});

In your server settings, the bot's role must sit above the roles of the members it will moderate. Discord won't let it ban someone ranked higher than itself, and we'll enforce that rule in code as well.

Command definition and default permission

When defining slash commands you can limit their default visibility with setDefaultMemberPermissions. This hides the command from members who lack the permission, but it's only a UI filter — you must still run the real security check at runtime yourself.

const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');

const banCommand = new SlashCommandBuilder()
  .setName('ban')
  .setDescription('Bans a member from the server')
  .addUserOption(o =>
    o.setName('member').setDescription('Member to ban').setRequired(true))
  .addStringOption(o =>
    o.setName('reason').setDescription('Reason for the ban'))
  .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers);

Permission and hierarchy checks

For every moderation command you must verify two things: the permission of the person running it and the role hierarchy. discord.js offers ready-made getters that simplify this: member.bannable, member.kickable and member.moderatable. These account for both the bot's permissions and role order. Even so, it's good practice to also check that the moderator running the command ranks above the target member.

function canModerate(interaction, target) {
  const actor = interaction.member;
  // The server owner can do anything
  if (actor.id === interaction.guild.ownerId) return true;
  // Is the moderator above the target?
  return actor.roles.highest.comparePositionTo(target.roles.highest) > 0;
}

If comparePositionTo returns positive, actor ranks higher. Skipping this check could let a moderator ban someone with more authority than them.

Implementing ban, kick and timeout

We collect the commands under a single handler. The key point: the audit log reason, when passed to the relevant method via the reason field, shows up in Discord's audit log. That way the "who did what to whom, and why" question doesn't go unanswered later.

client.on('interactionCreate', async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  const target = interaction.options.getMember('member');
  const reason = interaction.options.getString('reason') ?? 'Not specified';

  if (!target) {
    return interaction.reply({ content: 'Member not found.', ephemeral: true });
  }
  if (!canModerate(interaction, target)) {
    return interaction.reply({ content: 'You cannot moderate this member.', ephemeral: true });
  }

  if (interaction.commandName === 'ban') {
    if (!target.bannable) {
      return interaction.reply({ content: 'I cannot ban this member.', ephemeral: true });
    }
    await target.ban({ reason, deleteMessageSeconds: 60 * 60 * 24 });
    await interaction.reply(`🔨 ${target.user.tag} was banned. Reason: ${reason}`);
  }

  if (interaction.commandName === 'kick') {
    if (!target.kickable) {
      return interaction.reply({ content: 'I cannot kick this member.', ephemeral: true });
    }
    await target.kick(reason);
    await interaction.reply(`👢 ${target.user.tag} was kicked. Reason: ${reason}`);
  }

  if (interaction.commandName === 'mute') {
    if (!target.moderatable) {
      return interaction.reply({ content: 'I cannot timeout this member.', ephemeral: true });
    }
    const minutes = interaction.options.getInteger('minutes') ?? 10;
    await target.timeout(minutes * 60 * 1000, reason);
    await interaction.reply(`🔇 ${target.user.tag} was muted for ${minutes} minutes.`);
  }
});

Note that for mute we don't create a separate role; we use Discord's built-in timeout feature. member.timeout() takes a duration in milliseconds and is capped at 28 days. Pass null as the duration to clear the timeout. The built-in timeout is more reliable than maintaining a separate "muted" role, because the silence still applies even if the user joins new channels.

Don't swallow errors

Network failures, missing permissions, or a member who has already left are common in practice. Wrap the commands in a try/catch and return a meaningful message to the user; an error that silently drops to the console is the most common source of "the bot isn't working" complaints.

try {
  await target.ban({ reason });
  await interaction.reply(`🔨 ${target.user.tag} was banned.`);
} catch (err) {
  console.error(err);
  await interaction.reply({ content: 'The action failed.', ephemeral: true });
}

Best-practice notes

  • Ephemeral replies: Show error and permission messages with ephemeral: true so only the person running the command sees them; it keeps the channel clean.
  • Require a reason: For serious actions, set setRequired(true) to force the moderator to write an explanation.
  • Dedicated log channel: Send every moderation action as an embed to a private log channel too; it's a permanent record on top of the audit log.
  • Protect yourself and the bot: Reject the action if the target is the command runner or the bot itself.

Frequently Asked Questions

Should I set up a separate role for mute?

No. With discord.js v14, using Discord's built-in timeout feature (member.timeout()) is far more robust. A separate "muted" role requires permission overrides in every channel and can leave gaps when a user joins new channels; a timeout applies server-wide automatically.

Why can't my bot ban higher-ranked members?

Discord only lets a bot or member manage members ranked below its own highest role. Move the bot's role above those members in Server Settings → Roles. If the member.bannable getter returns false, the cause is almost always hierarchy.

How do I write a reason to the audit log?

Just pass the reason field to the relevant method: target.ban({ reason: 'Spam' }) or target.timeout(ms, 'Advertising'). Discord attaches that text next to the action in the server's audit log.

Want a secure, permission-aware moderation bot for your server? Let's plan a setup with ban/kick/timeout, an automatic log channel and anti-raid measures together — get in touch with me.

Devamı için