
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.