The anti-spam system runs entirely automatically inside theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Capinetta-RP/capinetta-discord-bot/llms.txt
Use this file to discover all available pages before exploring further.
messageCreate event handler. It monitors every message sent in a guild, identifies flood patterns by tracking consecutive identical messages per user, and responds with a graduated sequence of isolation, bulk deletion, and audit logging — all without requiring any moderator intervention. Combined with new-account rejection and a verification cooldown panel, the system forms a multi-layered defense against raids, bot accounts, and coordinated spam attacks. Because guild settings are stored per-server in MariaDB, every guild the bot is in runs its own independent anti-spam state.
How It Works
Message tracking via consecutiveMap
Every incoming message is evaluated by the
checkDuplicate function in messageCreate.js. The bot maintains a per-user in-memory consecutiveMap keyed by user ID. Each entry stores the last message content and a consecutive repetition counter.First and second repetition — counter increments
When a message matches the previous one exactly, the counter increments. On the first and second repetitions the counter reaches
1 and 2 respectively. No public action is taken yet, but the internal state is updated so the third match triggers the response immediately.Third identical message — isolation triggered
When
data.count >= 3, checkDuplicate returns true. The bot then:- Fetches the last 10 messages in the channel and bulk-deletes all messages from that user via
bulkDelete. - Calls
applyScamSanction, which reads the user’s current role list (excluding@everyoneand the muted role). - Persists that role list to MariaDB with
saveUserRoles(guildId, userId, rolesToSave). - Sends the user a DM warning them that their account has been restricted.
- Calls
member.roles.set([settings.roleMuted]), replacing all roles with the single configured isolation role. - Posts a public alert in the guild’s configured
supportChannel(stored inguild_settings). - Writes a
🛡️ AISLAMIENTOentry to the guild’s audit log channel viasendLog.
Anti-spam operates independently per guild. Each server has its own
consecutiveMap state and its own database records, making the system fully multiserver-compatible.New Account Rejection
Theconfig.general.minAccountDays field (default: 7) defines the minimum account age threshold for new members. When a member joins, the guildMemberAdd event fires and can be extended to enforce this limit before granting any roles or sending the welcome card:
Verification Cooldown
The/set-verify command posts a persistent button panel to a designated channel. When a user clicks the verification button, the bot enforces a mandatory waiting period before granting the verified role:
config.general.minVerifyMinutes value (default: 1 minute) means automated scripts clicking the button immediately will be denied. Only users who wait out the cooldown receive the verified role. The verification channel ID is read from config.general.verifyChannel (env var: GENERAL_VERIFY_CHANNEL), and the role granted is config.general.roleUser (GENERAL_ROLE_USER).
Spam Detection Threshold
The flood detection threshold is hardcoded insidecheckDuplicate: isolation triggers when data.count >= 3, meaning three consecutive identical messages from the same user. The config.js file also defines spamLimit and spamInterval values that are available for future use or custom extensions:
The
consecutiveMap used in checkDuplicate is an in-memory structure on the bot process. It resets on bot restart. The DB-persisted data (saved roles) is unaffected by restarts — /unmute will still restore roles correctly after a reboot.Recovery: Restoring Roles After Isolation
Once the situation has been reviewed, any moderator with theModerate Members permission can run:
/unmute command calls getUserRoles(guildId, userId) to retrieve the JSON array of role IDs saved before isolation. It then calls member.roles.set(savedRoles) to atomically restore all original roles. After a successful restore, clearUserRoles(guildId, userId) removes the saved state from the database to prevent stale data.
If no saved roles are found (e.g., the user was never isolated by the bot, or the DB was manually cleared), the command returns an ephemeral error rather than silently doing nothing.
Isolation Role Configuration
The isolation role (roleMuted) and the support alert channel (supportChannel) are configured per-guild. Run /setup to set them interactively via the setup wizard before going live.
guild_settings table as roleMuted and supportChannel. The anti-spam system reads them fresh on every event via getGuildSettings.
