Discord Bot Auto-Role and Verification Systems in 2026

Published on

Automated verification prevents bots and raiders while auto-role systems streamline new member onboarding. Here's how to implement both correctly.

Discord bot hosting

Discord raid attacks (waves of bot accounts joining to spam, mass-ping, or post malicious content) are a constant threat for growing servers. A verification system filters genuine users from automated accounts. Auto-role makes the legitimate path frictionless.

Simple Reaction Role Verification

The simplest approach: new members must react to a rules message before getting the Member role and channel access.

// On guild member join: put them in #welcome (only channel they see)
client.on('guildMemberAdd', async (member) => {
    const welcomeChannel = member.guild.channels.cache.get(WELCOME_CHANNEL_ID);
    await welcomeChannel.send(
        `Welcome ${member}! React with ✅ to verify you've read the rules and gain access.`
    );
});

// On reaction: grant Member role
client.on('messageReactionAdd', async (reaction, user) => {
    if (user.bot) return;
    if (reaction.message.channelId !== WELCOME_CHANNEL_ID) return;
    if (reaction.emoji.name !== '✅') return;
    
    const member = await reaction.message.guild.members.fetch(user.id);
    const memberRole = reaction.message.guild.roles.cache.get(MEMBER_ROLE_ID);
    await member.roles.add(memberRole);
});

CAPTCHA Verification

For servers frequently targeted by bot attacks, upgrade to CAPTCHA verification using discord-captcha:

const captcha = require('@haileybot/captcha-generator');

client.on('guildMemberAdd', async (member) => {
    const cap = new captcha();
    
    // DM the user a CAPTCHA image
    try {
        const dm = await member.createDM();
        await dm.send({
            content: `Welcome to ${member.guild.name}! Type the code shown to verify:`,
            files: [cap.JPEGStream]
        });
        
        // Wait for their response
        const filter = m => m.author.id === member.id;
        const response = await dm.awaitMessages({ filter, max: 1, time: 120000 });
        
        if (response.first().content === cap.value) {
            await member.roles.add(MEMBER_ROLE_ID);
            await dm.send('✅ Verified! Welcome to the server.');
        } else {
            await member.kick('Failed CAPTCHA verification');
        }
    } catch (e) {
        // DMs disabled - fall back to channel verification
    }
});

Auto-Role Without Verification (Fast Tracks)

For servers that want automatic role assignment on join (no verification):

client.on('guildMemberAdd', async (member) => {
    const defaultRole = member.guild.roles.cache.get(DEFAULT_ROLE_ID);
    if (defaultRole) await member.roles.add(defaultRole);
    
    // Auto-assign roles based on account age
    const accountAge = Date.now() - member.user.createdTimestamp;
    if (accountAge > 30 * 24 * 3600 * 1000) {  // 30 days old
        const trustedRole = member.guild.roles.cache.get(TRUSTED_ROLE_ID);
        await member.roles.add(trustedRole);
    }
});

Account age filtering silently blocks most new bot accounts (created fresh for raids) from getting server access.

If member.roles.add() fails with DiscordAPIError[50013]: Missing Permissions, the role usually sits above the bot's own highest role. Our Missing Permissions (50013) fix shows how to check and fix the role order.

Run your Discord moderation bot on Space-Node