Skip to main content

Documentation 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.

The anti-spam system runs entirely automatically inside the 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

1

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.
2

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.
3

Third identical message — isolation triggered

When data.count >= 3, checkDuplicate returns true. The bot then:
  1. Fetches the last 10 messages in the channel and bulk-deletes all messages from that user via bulkDelete.
  2. Calls applyScamSanction, which reads the user’s current role list (excluding @everyone and the muted role).
  3. Persists that role list to MariaDB with saveUserRoles(guildId, userId, rolesToSave).
  4. Sends the user a DM warning them that their account has been restricted.
  5. Calls member.roles.set([settings.roleMuted]), replacing all roles with the single configured isolation role.
  6. Posts a public alert in the guild’s configured supportChannel (stored in guild_settings).
  7. Writes a 🛡️ AISLAMIENTO entry to the guild’s audit log channel via sendLog.
4

Counter reset on message change

If the user sends a message with different content at any point, data.count resets to 1 and data.content updates. This means only continuous identical floods trigger isolation — normal conversation is never affected.
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

The config.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:
// config.js
general: {
  minAccountDays: 7,
}
This configuration value is available for access-control checks targeting freshly-created throwaway accounts commonly used in organized raids.

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.js
general: {
  minVerifyMinutes: 1,
}
The 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 inside checkDuplicate: 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:
// config.js
general: {
  spamLimit: 5,        // Reserved — not read by checkDuplicate
  spamInterval: 5000,  // Reserved — not read by checkDuplicate
}
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 the Moderate Members permission can run:
/unmute @user
The /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.
/setup
The wizard stores both values in the guild_settings table as roleMuted and supportChannel. The anti-spam system reads them fresh on every event via getGuildSettings.
Configure the isolation role and support channel with /setup before the bot goes live in a public server. If roleMuted is not set, applyScamSanction will still strip all roles from the user, but they won’t be moved to a restricted channel — which may leave them able to rejoin regular channels after a bot restart.

Build docs developers (and LLMs) love