Discord · Guide

How to Make a Discord Moderation Bot

Updated October 11, 2026 6 min read

On this page
  1. Start with Discord AutoMod
  2. What a custom moderation bot adds
  3. Permissions, role order and intents
  4. Moderation commands in Python (discord.py)
  5. A timeout command in JavaScript (discord.js)
  6. Logging what happens
  7. Keep the bot online
  8. FAQ

A busy Discord server needs moderation that never sleeps: spam filters, quick timeouts, a way to clear a flood of messages and a record of who did what. Discord gives you part of that for free with AutoMod, and a small bot of your own covers the rest.

This guide starts with AutoMod, because it may be all you need, then builds moderation commands in Python (discord.py) or JavaScript (discord.js) and explains the permissions, intents and logs that make them work.

Start with Discord AutoMod

AutoMod is built into Discord and runs on Discord's side, so it works even when no bot is online. Open Server Settings → AutoMod; you need the Manage Server permission. It offers:

  • Custom word filters with your own keywords, wildcards and regular expressions.
  • Commonly flagged words: ready lists for profanity, sexual content and slurs.
  • Mention spam: act when a message mentions too many people at once, up to a limit you set.
  • Spam content: Discord's own detection of suspected spam.
  • Member profile filters: keywords checked against usernames and profiles.

For each rule you choose what happens: block the message, send an alert to a moderators' channel, or time the member out. You can exempt roles and channels. Members with Manage Server or Administrator are never filtered.

If your problem is spam, slurs and mass mentions, set up AutoMod first and see if that is enough. A custom bot pays off when you want commands your moderators can use, your own rules and a proper log.

What a custom moderation bot adds

  • Slash commands for moderators: /timeout, /purge, /slowmode, kick and ban, with a reason recorded every time.
  • A mod log channel with joins, leaves, deleted messages and every action your moderators take.
  • Your own rules: warnings that add up to a timeout, new accounts held back, links allowed only in some channels.
  • Everything in one place, tuned to your community instead of a public bot's defaults.

If you have never made a bot, first follow how to make a Discord bot to create the application, get the token and run a first /ping command. The code below builds on it.

Permissions, role order and intents

Invite the bot with only the permissions its commands use:

ActionPermission
Time out a member (up to 28 days)Moderate Members
Kick a memberKick Members
Ban a memberBan Members
Delete messagesManage Messages and Read Message History
Set slow modeManage Channels

Role order matters. A bot can only act on members whose highest role is below the bot's own highest role. In Server Settings → Roles, drag the bot's role above the roles it should moderate. Nobody can time out the server owner or an Administrator.

Intents. Slash commands need no privileged intent. Two are useful for moderation: Server Members for join and leave logs, and Message Content if the bot reads messages to filter them itself. Turn them on under Bot → Privileged Gateway Intents in the Developer Portal. Since June 2026, Discord reviews these only for apps that reach more than 10,000 users; below that, the switch is enough.

Moderation commands in Python (discord.py)

With discord.py 2.7 installed (pip install discord.py), this bot.py adds /timeout, /purge and /slowmode:

import os
from datetime import timedelta
from typing import Optional

import discord
from discord import app_commands

class Bot(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())
        self.tree = app_commands.CommandTree(self)

    async def setup_hook(self):
        await self.tree.sync()

bot = Bot()

@bot.tree.command(description="Time out a member")
@app_commands.guild_only()
@app_commands.default_permissions(moderate_members=True)
async def timeout(interaction: discord.Interaction, member: discord.Member,
                  minutes: app_commands.Range[int, 1, 40320], reason: Optional[str] = None):
    await member.timeout(timedelta(minutes=minutes), reason=reason)
    await interaction.response.send_message(f"{member.mention} is timed out for {minutes} min.", ephemeral=True)

@bot.tree.command(description="Delete recent messages in this channel")
@app_commands.guild_only()
@app_commands.default_permissions(manage_messages=True)
async def purge(interaction: discord.Interaction, count: app_commands.Range[int, 1, 100]):
    await interaction.response.defer(ephemeral=True)
    deleted = await interaction.channel.purge(limit=count)
    await interaction.followup.send(f"Deleted {len(deleted)} messages.", ephemeral=True)

@bot.tree.command(description="Set slow mode in this channel")
@app_commands.guild_only()
@app_commands.default_permissions(manage_channels=True)
async def slowmode(interaction: discord.Interaction, seconds: app_commands.Range[int, 0, 21600]):
    await interaction.channel.edit(slowmode_delay=seconds)
    await interaction.response.send_message(f"Slow mode is now {seconds} s.", ephemeral=True)

bot.run(os.environ["DISCORD_TOKEN"])

default_permissions hides each command from members without that permission. Server admins can change who sees it under Server Settings → Integrations. The limits in the code are Discord's: 40,320 minutes is 28 days, slow mode goes up to 6 hours.

A timeout command in JavaScript (discord.js)

With discord.js 14 (npm install discord.js), the same /timeout looks like this. moderatable checks the role order and the bot's permission before trying:

const { Client, Events, GatewayIntentBits, InteractionContextType, MessageFlags,
  PermissionFlagsBits, REST, Routes, SlashCommandBuilder } = require('discord.js');

const commands = [
  new SlashCommandBuilder()
    .setName('timeout').setDescription('Time out a member')
    .addUserOption((o) => o.setName('member').setDescription('Member').setRequired(true))
    .addIntegerOption((o) => o.setName('minutes').setDescription('Minutes').setMinValue(1).setMaxValue(40320).setRequired(true))
    .addStringOption((o) => o.setName('reason').setDescription('Reason'))
    .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers)
    .setContexts(InteractionContextType.Guild)
    .toJSON(),
];
const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.once(Events.ClientReady, async (c) => {
  await new REST().setToken(process.env.DISCORD_TOKEN).put(Routes.applicationCommands(c.user.id), { body: commands });
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand() || interaction.commandName !== 'timeout') return;
  const member = interaction.options.getMember('member');
  const minutes = interaction.options.getInteger('minutes');
  if (!member?.moderatable) {
    return interaction.reply({ content: 'I cannot time out that member.', flags: MessageFlags.Ephemeral });
  }
  await member.timeout(minutes * 60_000, interaction.options.getString('reason') ?? undefined);
  await interaction.reply({ content: `${member} is timed out for ${minutes} min.`, flags: MessageFlags.Ephemeral });
});

client.login(process.env.DISCORD_TOKEN);

Kick and ban follow the same pattern with member.kick(reason) and member.ban({ reason, deleteMessageSeconds }). A ban can also delete up to 7 days of the member's messages.

Logging what happens

  • Always pass a reason. The libraries send it to Discord's audit log (Server Settings → Audit Log), which keeps entries for 45 days.
  • Post every action to a private mod log channel as well: who acted, on whom, why and for how long. Unlike the audit log, it keeps the history as long as you like.
  • Joins and leaves need the Server Members intent; log the account's creation date to spot fresh throwaway accounts.
  • Deleted and edited messages can only be logged with their text if the bot has the Message Content intent.

Keep in mind the bulk-delete limit: Discord deletes up to 100 messages per call and refuses messages older than two weeks, so /purge is for fresh spam, not for clearing out an old channel.

Keep the bot online

AutoMod keeps working without you, but your commands and logs stop the moment the bot's process stops. A moderation bot running on a home PC is offline exactly when the PC sleeps, and raids rarely wait for office hours.

On HytHost Discord bot hosting you upload the project, pick the runtime (Node.js, Python, Java, Go, C#, Bun or Lavalink) and keep the token in a startup variable instead of in the code. The panel starts the bot and restarts it after a crash. The full setup is in how to host a Discord bot 24/7.

A moderation bot only works while it runs: HytHost Discord bot hosting supports Python, Node.js and 5 more runtimes, from €0.89/mo.

See bot hosting plans
FAQ

Frequently asked questions

For many servers AutoMod is enough: it blocks keywords, flagged words, mention spam and suspected spam, and can time members out. Add a bot when your moderators need commands such as purge and slow mode, your own rules, or a full log of actions.

Only those its commands use: Moderate Members for timeouts, Kick Members, Ban Members, Manage Messages with Read Message History for deleting messages, and Manage Channels for slow mode. Avoid giving it Administrator.

Usually role order: the bot can only act on members whose highest role is below its own, so move the bot's role higher in Server Settings. The server owner and members with Administrator cannot be timed out at all.

Not in bulk. Discord's bulk delete accepts up to 100 messages at a time and only messages newer than two weeks. Older messages can only be deleted one by one, which is slow.

Only if it reads messages itself, for example to filter words or to log deleted message text. Slash commands, timeouts, bans and purges work without it.

Host your moderation bot

HytHost Discord bot hosting runs Python, Node.js, Java, Go, C#, Bun or Lavalink, restarts the bot after a crash and starts at €0.89/mo.

Live chat 8 AM – 11 PM (Chișinău time) · tickets answered during business hours

Was this guide helpful?