A full-featured Discord moderation bot built with discord.js v14. Supports multiple servers, with per-server configuration managed via /setup slash commands. Includes moderation commands, auto-moderation, and comprehensive logging.
| Command | Description | Permission Required |
|---|---|---|
/ban |
Ban a member (with optional message deletion) | Ban Members |
/kick |
Kick a member | Kick Members |
/mute |
Timeout a member (1 min – 7 days) | Moderate Members |
/unmute |
Remove a timeout | Moderate Members |
/warn |
Issue a warning (stored persistently) | Moderate Members |
/warnings |
View or clear a member's warnings | Moderate Members |
/purge |
Bulk delete up to 100 messages (optional: filter by user; skips messages older than 14 days) | Manage Messages |
/setup |
Configure the bot for this server | Administrator |
| Subcommand | Description |
|---|---|
/setup log-channel |
Set the channel where mod-log messages are sent |
/setup add-bad-words |
Add words to this server's banned list (on top of the global list) |
/setup clear-bad-words |
Remove all server-specific banned words (global list unchanged) |
/setup view-bad-words |
Show global and server-specific banned word lists separately |
/setup block-links |
Enable or disable link blocking for this server |
/setup spam-threshold |
Set how many messages in 5 seconds triggers spam detection |
/setup view |
Show the full current configuration for this server |
- Bad word filter — Deletes messages containing words from the global list or the server's extra list. Matching is evasion-resistant:
- Leet-speak normalization (
@→a,3→e,$→s,ph→f, etc.) - Diacritic stripping (
fück→fuck,slür→slur) - Zero-width / invisible Unicode removal (common copy-paste evasion)
- Fuzzy symbol substitution (
f*ck,f.u.c.k,h-a-t-eall match)
- Leet-speak normalization (
- Link blocker — Optionally blocks all HTTP/HTTPS URLs (per server)
- Spam detection — Mutes users who send too many messages too fast; escalating timeouts (1 min, 5 min, 1 hour)
- Moderator bypass — Members with the
Manage Messagespermission are exempt from all auto-mod checks
- Member joins (flags accounts less than 7 days old)
- Member leaves (shows roles held)
- Message edits (before/after)
- Message deletes
- All moderation actions (ban, kick, mute, warn, auto-mod, purge)
- Go to discord.com/developers/applications
- Click New Application, give it a name, and click Create
- Go to the Bot tab on the left sidebar
- Click Add Bot → Yes, do it!
- Under Privileged Gateway Intents, enable:
- Server Members Intent
- Message Content Intent
- Click Save Changes
- Click Reset Token and copy your Bot Token — save it for later
- Go to the OAuth2 → URL Generator tab
- Under Scopes, check:
botandapplications.commands - Under Bot Permissions, check:
Ban MembersKick MembersModerate Members(for timeouts)Manage MessagesSend MessagesEmbed LinksRead Message HistoryView Channels
- Copy the generated URL and open it in your browser to invite the bot
- Repeat for each additional server
Enable Developer Mode in Discord:
User Settings → Advanced → Developer Mode → ON
Then find your Client ID on the Discord Developer Portal under your App → OAuth2.
# Install dependencies
npm install
# Copy the example env file
cp .env.example .envOpen .env and fill in the two required values:
BOT_TOKEN=your_bot_token_here
CLIENT_ID=your_client_id_hereThe remaining variables are optional global defaults that apply to all servers. Per-server settings are configured with /setup after the bot starts (see Step 6).
# Words banned in every server (server admins can add more with /setup add-bad-words)
BAD_WORDS=badword1,badword2,badword3
# Global defaults — can be overridden per server with /setup
BLOCK_LINKS=false
SPAM_THRESHOLD=5Commands are deployed globally and will be available in every server the bot joins.
npm run deployYou should see:
Registering 8 slash command(s) globally...
Slash commands registered globally. Changes may take up to 1 hour to appear in all servers.
Global commands can take up to 1 hour to propagate after the first deploy. You only need to re-run this when you add or remove commands.
npm startYou should see:
[CMD] Loaded: ban
[CMD] Loaded: kick
...
Logged in as YourBot#0000
When the bot joins a server, it will automatically DM the server owner with setup instructions. You can also run these commands yourself at any time:
-
Set a log channel (required for logging to work):
/setup log-channel #mod-log -
Add server-specific banned words (optional — global words from
.envare always active):/setup add-bad-words words:slur1,slur2 -
Adjust other settings as needed:
/setup block-links enabled:true /setup spam-threshold amount:4 -
Check your configuration at any time:
/setup view /setup view-bad-words
Per-server settings are saved to data/guild-configs.json on the host machine.
| Variable | Required | Description |
|---|---|---|
BOT_TOKEN |
Yes | Your bot token from the Discord Developer Portal |
CLIENT_ID |
Yes | Your application's client ID |
BAD_WORDS |
No | Comma-separated words banned in all servers |
BLOCK_LINKS |
No | true or false — global default (default: false) |
SPAM_THRESHOLD |
No | Message count before spam triggers — global default (default: 5) |
| Key | Description |
|---|---|
logChannelId |
Channel ID where mod-log embeds are sent |
extraBadWords |
Words banned only in this server, in addition to the global list |
blockLinks |
Overrides the global BLOCK_LINKS default for this server |
spamThreshold |
Overrides the global SPAM_THRESHOLD default for this server |
discord-mod-bot/
├── src/
│ ├── index.js # Bot entry point
│ ├── deploy-commands.js # Global slash command registration
│ ├── commands/
│ │ ├── ban.js
│ │ ├── kick.js
│ │ ├── mute.js
│ │ ├── unmute.js
│ │ ├── warn.js
│ │ ├── warnings.js
│ │ ├── purge.js
│ │ └── setup.js # Per-server configuration command
│ ├── events/
│ │ ├── ready.js
│ │ ├── interactionCreate.js
│ │ ├── guildCreate.js # Alerts server owner on bot join
│ │ ├── guildMemberAdd.js
│ │ ├── guildMemberRemove.js
│ │ ├── messageCreate.js # Auto-mod engine
│ │ ├── messageUpdate.js
│ │ └── messageDelete.js
│ └── utils/
│ ├── logger.js # Per-guild log channel embed sender
│ ├── guildConfig.js # Per-guild config read/write utility
│ └── warnings.js # Warning persistence (JSON)
├── data/
│ ├── warnings.json # Auto-created on first /warn
│ └── guild-configs.json # Auto-created on first /setup
├── .env # Your config (never commit this!)
├── .env.example # Safe template to share
├── package.json
└── README.md
If the bot is already running in one or more servers and you are pulling the latest code, follow these steps. The guildCreate event only fires when the bot first joins a server — existing servers will not receive the automatic setup DM.
git pull
npm installRemove GUILD_ID and LOG_CHANNEL_ID — they are no longer used. Keep everything else.
BOT_TOKEN=... # keep
CLIENT_ID=... # keep
# GUILD_ID=... # remove — no longer used
# LOG_CHANNEL_ID=... # remove — set per-server with /setup log-channel instead
BAD_WORDS=... # keep — applies globally to all servers
BLOCK_LINKS=... # keep — global default, can be overridden per server
SPAM_THRESHOLD=... # keep — global default, can be overridden per serverThis registers the new /setup command. Run it once while the bot is stopped.
npm run deployGlobal commands can take up to 1 hour to appear in existing servers.
npm startIf you are using pm2:
pm2 restart allAll moderation commands continue to work immediately, but logging will be silent until you set a log channel. Run this in each server:
/setup log-channel #your-mod-log-channel
Your existing warning history in data/warnings.json carries over automatically — no action needed there.
This project is licensed under the MIT License.
- Never share your
.envfile or commit it to Git. Add it to.gitignore. - The bot needs a role higher than the roles of users it moderates in each server's role hierarchy.
- Warnings are stored in
data/warnings.json— back this up if you want to preserve history. - Per-server config is stored in
data/guild-configs.json— include this in any backups.