Building a Discord Moderation Bot: AutoMod and Custom Rules

Published on | Updated

How to build moderation features for your Discord bot. Covers auto-moderation, word filters, spam detection, logging, and integration with Discord's built-in AutoMod.

Discord bot hosting

Server moderation is the most common reason communities build custom bots. While Discord's built-in AutoMod handles basics, custom bots provide the flexibility that growing communities need.

Discord's Built-in AutoMod

Before building custom moderation, use Discord's native AutoMod:

  • Keyword filtering
  • Spam detection
  • Mention spam protection
  • Link blocking

Configure through Server Settings → Safety Setup → AutoMod.

Custom AutoMod Features

Word Filter

const bannedWords = ['badword1', 'badword2'];
const bannedPatterns = [/discord\.gg\/[a-zA-Z0-9]+/i]; // Block invite links

client.on('messageCreate', async message => {
    if (message.author.bot) return;
    
    const content = message.content.toLowerCase();
    
    // Check banned words
    if (bannedWords.some(word => content.includes(word))) {
        await message.delete();
        await message.channel.send({
            content: `${message.author}, that word isn't allowed here.`,
            allowedMentions: { users: [message.author.id] }
        });
        await logModAction(message.guild, message.author, 'automod', 'Banned word detected');
        return;
    }
    
    // Check patterns
    if (bannedPatterns.some(pattern => pattern.test(message.content))) {
        await message.delete();
        return;
    }
});

Spam Detection

const messageCache = new Map();

client.on('messageCreate', async message => {
    if (message.author.bot) return;
    
    const key = message.author.id;
    const now = Date.now();
    
    if (!messageCache.has(key)) {
        messageCache.set(key, []);
    }
    
    const userMessages = messageCache.get(key);
    userMessages.push(now);
    
    // Keep only messages from last 5 seconds
    const recent = userMessages.filter(t => now - t < 5000);
    messageCache.set(key, recent);
    
    // More than 5 messages in 5 seconds = spam
    if (recent.length > 5) {
        await message.member.timeout(60000, 'Spam detection');
        await message.channel.send(`${message.author} has been muted for spam.`);
    }
});

Raid Protection

Detect mass joins:

const joinCache = [];

client.on('guildMemberAdd', async member => {
    const now = Date.now();
    joinCache.push({ id: member.id, time: now });
    
    // Clean old entries
    while (joinCache.length > 0 && now - joinCache[0].time > 10000) {
        joinCache.shift();
    }
    
    // More than 10 joins in 10 seconds = potential raid
    if (joinCache.length > 10) {
        // Enable verification level
        await member.guild.setVerificationLevel(4); // VERY_HIGH
        // Alert admins
        const alertChannel = member.guild.channels.cache.find(c => c.name === 'mod-alerts');
        if (alertChannel) {
            await alertChannel.send('@here Potential raid detected! Verification level increased.');
        }
    }
});

Warning System

// Track warnings in database
async function warnUser(guildId, userId, moderatorId, reason) {
    const warning = await db.prepare(`
        INSERT INTO warnings (guild_id, user_id, moderator_id, reason)
        VALUES (?, ?, ?, ?)
    `).run(guildId, userId, moderatorId, reason);
    
    // Check total warnings
    const count = db.prepare(
        'SELECT COUNT(*) as count FROM warnings WHERE guild_id = ? AND user_id = ?'
    ).get(guildId, userId).count;
    
    // Auto-escalation
    if (count >= 3) {
        // Auto-timeout after 3 warnings
        const member = await guild.members.fetch(userId);
        await member.timeout(3600000, 'Accumulated 3 warnings');
    }
    
    return { warningId: warning.lastInsertRowid, totalWarnings: count };
}

Logging

Mod Log Channel

async function logModAction(guild, user, action, reason, moderator) {
    const logChannel = guild.channels.cache.find(c => c.name === 'mod-log');
    if (!logChannel) return;
    
    const embed = {
        color: action === 'ban' ? 0xFF0000 : action === 'warn' ? 0xFFAA00 : 0x00FF00,
        title: `${action.toUpperCase()}`,
        fields: [
            { name: 'User', value: `${user.tag} (${user.id})`, inline: true },
            { name: 'Moderator', value: moderator ? moderator.tag : 'AutoMod', inline: true },
            { name: 'Reason', value: reason || 'No reason provided' }
        ],
        timestamp: new Date().toISOString()
    };
    
    await logChannel.send({ embeds: [embed] });
}

Audit Trail

Log every moderation action to the database for accountability and appeals:

INSERT INTO mod_actions (guild_id, target_id, moderator_id, action, reason, timestamp)
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP);

Deployment

A moderation bot needs true 24/7 uptime, because it can't protect your server while it is offline. Space-Node's Discord bot hosting keeps the bot running around the clock and restarts it automatically if it crashes. Our free Starter plan is not available right now, so the entry plan is Growth: €0.50 a month for 512 MB RAM and 10 GB NVMe storage, billed €3.00 every 6 months plus a one-time €0.50 setup fee on the 6 month and 1 year terms.

For larger communities (1000+ members), look at Pro: 1 GB RAM and 20 GB NVMe for €1.00 a month, billed €6.00 every 6 months plus the same setup fee. The extra RAM helps with caching member data, and the extra storage gives the audit trail room to grow.