398 lines
20 KiB
Markdown
398 lines
20 KiB
Markdown
# Excommunicado
|
||
|
||
**Excommunicado** is a Discord moderation bot designed to combat channel flooding and spam without resorting to bans or kicks.
|
||
|
||
In **stealth mode**, offending users **do not realize** they have been moved. They continue to see the entire server normally. Their public messages are silently deleted and forwarded to the shared **XCOM Lounge** room, where selected staff members can also be present to converse with them. Normal members never see the spam or the moderated room. This creates the illusion of a normal experience while containing the violation.
|
||
|
||
## Table of Contents
|
||
- [Features](#features)
|
||
- [Philosophy & Use Cases](#philosophy--use-cases)
|
||
- [Requirements](#requirements)
|
||
- [Installation & Quick Start](#installation--quick-start)
|
||
- [Detailed Command Reference](#detailed-command-reference)
|
||
- [Configuration](#configuration)
|
||
- [How Isolation Works (Technical)](#how-isolation-works-technical)
|
||
- [Architecture & Code Structure](#architecture--code-structure)
|
||
- [Customization](#customization)
|
||
- [Troubleshooting](#troubleshooting)
|
||
- [Security Best Practices](#security-best-practices)
|
||
- [Extending the Bot](#extending-the-bot)
|
||
- [License](#license)
|
||
|
||
## Features
|
||
- **Slash commands only** (modern Discord UX)
|
||
- `/xcom-setup` – one-time server initialization
|
||
- `/xcom @user [reason]` – excommunicate a user (manual or via auto-detection)
|
||
- `/xcom-release @user` – restore full access (the `[XCOM]` mark remains)
|
||
- `/xcom-unmark @user` – remove the permanent `[XCOM]` nickname mark
|
||
- `/xcom-vow <text>` – let an isolated user post a vow of redemption to staff
|
||
- `/xcom-witness @staff @target` – add a staff member to the shared lounge
|
||
- `/xcom-absolve @user` – release a user early once they earn enough points
|
||
- `/xcom-vote @user up|down` – community vote to excommunicate (or defend) a user
|
||
- `/xcom-votes @user` – staff view of a user's current vote tally
|
||
- `/xcom-keywords` – admin panel to curate auto-score keywords/phrases
|
||
- `/xcom-list` – view all currently XCOM users with reasons and timestamps
|
||
- **Automatic flood protection** – detects ≥5 messages within 8 seconds and auto-excommunicates
|
||
- **Community voting** – members up/down-vote a user; reaching a net **+10** (with quorum) auto-XCOMs them; tallies reset weekly
|
||
- **Keyword auto-scoring** – admin-curated phrases (e.g. cracked-software/warez trading) add weighted points to a sender's score and can auto-XCOM critical abuse even when no admin is online
|
||
- **Absolution Points** – users earn points for good behavior in the xcom-lounge; staff can absolve early
|
||
- **Shared hidden channel** `xcom-lounge` with 5-second slowmode
|
||
- **Persistent state** – atomic, corruption-safe JSON storage survives bot restarts
|
||
- **Mention-safe forwarding** – relocated messages never ping (`@everyone`/`@here`/roles are neutralized)
|
||
- **Rich embeds + DM notifications** for offenders and staff logs
|
||
- **Graceful permission handling** and role-hierarchy safety checks
|
||
- Zero bans/kicks – users stay in the server
|
||
|
||
## Philosophy & Use Cases
|
||
Excommunicado solves the "ban vs. mute" dilemma for flooders:
|
||
- Bans encourage alt accounts.
|
||
- Timeouts/mutes still allow the user to see the server and feel excluded.
|
||
- Excommunication gives the user a dedicated space to talk to moderators while completely removing their ability to disrupt public channels.
|
||
|
||
Ideal for:
|
||
- High-traffic community servers
|
||
- Gaming clans with strict chat rules
|
||
- Support servers where users sometimes vent excessively
|
||
- Any server that values retention over punishment
|
||
|
||
## Requirements
|
||
- Python ≥ 3.10
|
||
- discord.py ≥ 2.4.0 + python-dotenv
|
||
- Discord bot with **privileged intents** enabled:
|
||
- Server Members Intent
|
||
- Message Content Intent
|
||
- Bot permissions on invite: `Manage Roles`, `Manage Channels`, `Send Messages`, `Embed Links`, `Read Message History` (Administrator recommended for initial setup)
|
||
- Role hierarchy: Bot role must sit **above** the `Excommunicado` role
|
||
|
||
## Installation & Quick Start
|
||
|
||
### Docker Deployment (Recommended)
|
||
|
||
1. Make sure you have Docker and Docker Compose installed.
|
||
2. Copy `.env.example` → `.env` and fill in your token.
|
||
3. Build and run the container:
|
||
|
||
```powershell
|
||
docker compose up -d --build
|
||
```
|
||
|
||
The bot runs as a non-root user with healthchecks and log rotation.
|
||
|
||
To view logs:
|
||
```powershell
|
||
docker compose logs -f excommunicado
|
||
```
|
||
|
||
To stop:
|
||
```powershell
|
||
docker compose down
|
||
```
|
||
|
||
---
|
||
|
||
### Manual Installation (Non-Docker)
|
||
|
||
### 1. Prepare the project
|
||
```
|
||
cd ~/Excommunicado # or your chosen folder
|
||
```
|
||
|
||
### 2. Install dependencies
|
||
```
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### 3. Configure environment
|
||
cp `.env.example` → `.env` and populate:
|
||
```env
|
||
DISCORD_TOKEN=your_token_here
|
||
GUILD_ID=123456789012345678 # optional – speeds up command registration during development
|
||
LOG_CHANNEL_ID=987654321098765432 # optional – receives audit embeds
|
||
```
|
||
|
||
### 4. Create & invite the bot (Discord Developer Portal)
|
||
1. https://discord.com/developers/applications → New Application → Bot
|
||
2. Enable the two privileged intents under the Bot tab
|
||
3. Generate a token (copy to `.env`)
|
||
4. OAuth2 → URL Generator → `bot` + `applications.commands` scopes + required permissions
|
||
5. Invite the bot to your target guild
|
||
|
||
### 5. Run the bot
|
||
```powershell
|
||
python bot.py
|
||
```
|
||
|
||
You should see:
|
||
```
|
||
✅ Logged in as Excommunicado#1234 ...
|
||
Excommunicado bot is ready (XCOM stealth isolation mode).
|
||
[COG] Moderation (Excommunicado) loaded.
|
||
```
|
||
|
||
### 6. Run `/xcom-setup` in Discord
|
||
(Requires Manage Server permission)
|
||
|
||
This creates:
|
||
- `Excommunicado` role (dark red color)
|
||
- `xcom-lounge` text channel (hidden, slowmode 5s)
|
||
|
||
### 7. Manual role lockdown (critical for older non-stealth setups)
|
||
After setup, the `Excommunicado` role is only a marker. Do **not** deny View on public channels (stealth mode keeps the user seeing the full server). The `xcom-lounge` is already hidden from @everyone.
|
||
|
||
### 8. Test the flow
|
||
```
|
||
/xcom @SomeSpammer Flooding with repeated messages
|
||
```
|
||
The user is stealthily XCOM'd. Their messages are forwarded to the shared room. They receive a DM and a staff log is sent (if configured).
|
||
|
||
## Detailed Command Reference
|
||
|
||
### `/xcom-setup`
|
||
- **Permission**: Manage Server
|
||
- **Purpose**: Idempotent creation of role + channel
|
||
- **Output**: Embed explaining next manual steps
|
||
|
||
### `/xcom <user> [reason]`
|
||
- **Permission**: Manage Roles
|
||
- **Parameters**:
|
||
- `user` (required) – target member
|
||
- `reason` (optional) – default "Flooding the channel"
|
||
- **Effects**:
|
||
- Assigns `Excommunicado` role
|
||
- Applies the permanent `[XCOM]` nickname mark (best-effort; needs Manage Nicknames)
|
||
- Records in `data/xcom_config.json`
|
||
- Posts welcome embed in `xcom-lounge`
|
||
- DMs the user
|
||
- Logs to staff channel (if set)
|
||
- **Role-hierarchy safety**: refuses to XCOM the server owner, a member whose top role is equal to or higher than the invoker's, or a member ranked above the bot — each returns a clear ephemeral error instead of failing silently.
|
||
- **Cooldown**: max 5 uses per 15 seconds per moderator (per guild) to prevent accidental mass-spam.
|
||
- **Edge cases**: Already under XCOM, bot, self-target, missing hierarchy → clear ephemeral error
|
||
|
||
### `/xcom-release <user>`
|
||
- **Permission**: Manage Roles
|
||
- **Effects**: Removes role + record. The `[XCOM]` nickname mark **remains** until an admin runs `/xcom-unmark`.
|
||
|
||
### `/xcom-unmark <user>`
|
||
- **Permission**: Manage Roles
|
||
- **Effects**: Removes the permanent `[XCOM]` nickname prefix.
|
||
|
||
### `/xcom-vow <text>`
|
||
- **Permission**: None (user must be under XCOM)
|
||
- **Effects**: Posts the user's vow in the xcom-lounge for staff to see.
|
||
|
||
### `/xcom-witness @staff @target`
|
||
- **Permission**: Manage Roles
|
||
- **Effects**: Gives the staff member the Excommunicado role so they can join the shared lounge.
|
||
|
||
### `/xcom-absolve @user`
|
||
- **Permission**: Manage Roles
|
||
- **Cooldown**: max 5 uses per 15 seconds per moderator (per guild)
|
||
- **Effects**: If the user has ≥10 absolution points (earned by positive participation in the lounge), removes the role early. The mark remains.
|
||
|
||
### `/xcom-list`
|
||
- **Permission**: Manage Roles
|
||
- **Output**: Embed listing up to 15 users with reason + date (truncated if more)
|
||
|
||
### `/xcom-vote <user> <up|down>`
|
||
- **Permission**: None (any member) — light cooldown of 10 uses / 60s per member
|
||
- **Effects**: Casts a weighted community vote on the target's running score.
|
||
- `up` = excommunicate, `down` = defend.
|
||
- Admins/moderators count **±2**; regular members **±1**.
|
||
- **One vote per voter per target per 24h** (re-voting replaces your previous vote).
|
||
- When the **net score ≥ 10** *and* at least **5 distinct voters** have participated, the user is automatically XCOM'd and the tally is cleared.
|
||
- Tallies **reset weekly**.
|
||
- **Guardrails**: cannot vote on yourself, bots, the server owner, staff/moderators, or anyone ranked above the bot. Confirmations are ephemeral (preserves stealth, avoids public pile-on).
|
||
|
||
### `/xcom-votes <user>`
|
||
- **Permission**: Manage Roles
|
||
- **Output**: Ephemeral embed showing the target's current score, distinct-voter count, threshold, and quorum, with a breakdown of points from human votes vs. keyword flags.
|
||
|
||
### `/xcom-keywords`
|
||
- **Permission**: Manage Server (admin)
|
||
- **Effects**: Opens an **interactive ephemeral panel** to curate the auto-score keyword/phrase list:
|
||
- **Add / Edit** — a modal to enter a phrase and its score weight (1–100). Re-adding an existing phrase updates its weight.
|
||
- **Remove** — a modal to delete a phrase.
|
||
- **Refresh** — re-render the current list.
|
||
- **How it works**: any non-staff message containing a listed phrase (case-insensitive substring match) adds that phrase's weight to the sender's community score. When the score reaches the XCOM threshold, the user is auto-isolated — **no quorum required**, since this path is meant to capture abuse when no admin is online. Staff, the server owner, and members ranked above the bot are immune.
|
||
|
||
All commands are ephemeral where possible to keep chat clean.
|
||
|
||
## Configuration
|
||
|
||
### Environment Variables
|
||
| Variable | Required | Description |
|
||
|-------------------|----------|------------------------------------------|
|
||
| `DISCORD_TOKEN` | Yes | Bot token |
|
||
| `GUILD_ID` | No | Guild for faster slash command sync |
|
||
| `LOG_CHANNEL_ID` | No | Channel that receives action embeds |
|
||
|
||
### Tunable Constants (in `bot.py`)
|
||
```python
|
||
FLOOD_THRESHOLD = 5 # messages
|
||
FLOOD_WINDOW_SECONDS = 8 # within this many seconds
|
||
```
|
||
A user who sends `FLOOD_THRESHOLD` messages within `FLOOD_WINDOW_SECONDS` is auto-excommunicated. Change and restart to adjust sensitivity.
|
||
|
||
### Community Vote Constants (in `cogs/moderation.py`)
|
||
```python
|
||
VOTE_XCOM_THRESHOLD = 10 # net score required to auto-XCOM a user
|
||
VOTE_ADMIN_WEIGHT = 2 # admins / moderators count double
|
||
VOTE_USER_WEIGHT = 1 # regular members
|
||
VOTE_COOLDOWN_HOURS = 24 # one vote per voter per target per 24h
|
||
VOTE_RESET_DAYS = 7 # tallies reset weekly
|
||
VOTE_MIN_UNIQUE_VOTERS = 5 # quorum: distinct voters required before XCOM fires
|
||
```
|
||
|
||
**Recommended guardrails (already enforced):**
|
||
- **Quorum** (`VOTE_MIN_UNIQUE_VOTERS`) prevents a handful of admins (±2 each) or a tiny brigade from instantly triggering an XCOM — a genuine community consensus is required.
|
||
- **Staff immunity** — moderators/admins and the server owner can't be vote-targeted.
|
||
- **Hierarchy check** — the bot refuses votes it couldn't enforce.
|
||
- **Ephemeral results** keep the target unaware (stealth) and discourage public harassment campaigns.
|
||
- **Auto-clear on trigger** stops a target from being re-XCOM'd on the very next vote.
|
||
|
||
Tune `VOTE_XCOM_THRESHOLD` and `VOTE_MIN_UNIQUE_VOTERS` together to match your server size — larger servers should raise both.
|
||
|
||
### Keyword Auto-Scoring
|
||
Admins curate the phrase list live via `/xcom-keywords` (no restart needed). Each phrase has an integer **weight**; a matching message adds that weight to the sender's community score (the same score `/xcom-vote` feeds). Notes:
|
||
- Matching is **case-insensitive substring**, so curate carefully — this is a cybersecurity channel where some terms are legitimate in context. Prefer specific phrases (`"selling cracked"`) over broad single words (`"crack"`).
|
||
- Multiple distinct phrases matched in one message stack their weights.
|
||
- A high weight (e.g. `10`) makes a single message enough to XCOM instantly; low weights (`1`–`3`) require repeated abuse.
|
||
- Keyword points are part of the weekly-resetting score and are shown separately in `/xcom-votes`.
|
||
- The keyword path **bypasses the human voter quorum** by design, but still respects staff/owner immunity and bot role hierarchy.
|
||
|
||
### Data File
|
||
`data/xcom_config.json` (auto-created). Writes are **atomic** (written to a temp file, then `os.replace`d) and the file is **cached in memory**, so the bot does not re-read disk on every message:
|
||
```json
|
||
{
|
||
"123456789": {
|
||
"xcom_role_id": 111111111,
|
||
"xcom_channel_id": 222222222,
|
||
"isolated_users": {
|
||
"333333333": {
|
||
"reason": "Auto-detected flooding...",
|
||
"timestamp": "2026-06-08T12:34:56.789012+00:00",
|
||
"absolution_points": 0,
|
||
"marked": true
|
||
}
|
||
},
|
||
"votes": {
|
||
"444444444": {
|
||
"week_start": "2026-06-08T00:00:00+00:00",
|
||
"voters": {
|
||
"555555555": { "direction": 1, "weight": 2, "timestamp": "2026-06-08T12:00:00+00:00" }
|
||
},
|
||
"keyword_points": 3
|
||
}
|
||
},
|
||
"keywords": {
|
||
"selling cracked software": {
|
||
"weight": 5,
|
||
"added_by": 555555555,
|
||
"added_at": "2026-06-08T10:00:00+00:00",
|
||
"display": "selling cracked software"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
- `absolution_points` – earned by positive participation in the lounge; `/xcom-absolve` needs ≥10.
|
||
- `marked` – whether the permanent `[XCOM]` nickname mark is applied.
|
||
- `votes` – per-target community-vote tallies; each voter's `direction` (±1) × `weight` is summed, plus `keyword_points` from auto-scoring, into the net score; the whole record is discarded once `week_start` is older than a week.
|
||
- `keywords` – admin-curated auto-score phrases (managed via `/xcom-keywords`).
|
||
|
||
If the file is ever corrupted, the bot backs it up to `xcom_config.json.corrupt` and starts fresh instead of crashing.
|
||
|
||
## How Isolation Works (Technical) — Stealth Mode
|
||
1. On `/xcom-setup`, the bot creates:
|
||
- `Excommunicado` role (marker only)
|
||
- `xcom-lounge` channel (hidden from @everyone, visible to Excommunicado role holders)
|
||
2. When a user is excommunicated (manually or automatically):
|
||
- They receive the `Excommunicado` role (grants access to the shared room)
|
||
- **No view permissions are removed** from public channels — the user continues to see the full server and does not realize anything changed.
|
||
3. On every message the isolated user sends outside `xcom-lounge`:
|
||
- The message is **instantly deleted** (public never sees the spam)
|
||
- The content is forwarded to the shared `xcom-lounge` room **with all mentions neutralized** (no `@everyone`/`@here`/role pings) and **truncated** to Discord's 2000-character limit
|
||
- Staff members who also hold the `Excommunicado` role can see and reply in the same room
|
||
4. The offender experiences normal server browsing + "their messages just go to the moderated lounge where staff are"
|
||
5. `/xcom-release` removes the role — full normal behavior is restored instantly
|
||
|
||
This design ensures the violation holder shares the room with real staff members while never realizing they have been isolated.
|
||
|
||
## Architecture & Code Structure
|
||
```
|
||
Excommunicado/
|
||
├── bot.py # Entry point, flood detector (on_message), ExcommunicadoBot class
|
||
├── requirements.txt
|
||
├── .env.example
|
||
├── README.md # This file (full documentation)
|
||
├── utils/
|
||
│ ├── config.py # JSON load/save helpers + isolated user CRUD
|
||
│ └── __init__.py
|
||
├── cogs/
|
||
│ ├── moderation.py # All slash commands + helper functions
|
||
│ └── __init__.py
|
||
└── data/
|
||
└── xcom_config.json
|
||
```
|
||
|
||
Key flows:
|
||
- `on_message` → rate-limit deque → auto call to `get_or_create_*` + `add_roles`
|
||
- Commands → permission checks → role/channel helpers → config persistence → DM + embed
|
||
|
||
## Customization
|
||
- Change role name/color in `get_or_create_xcom_role`
|
||
- Change channel name/slowmode in `get_or_create_xcom_channel`
|
||
- Add more commands by creating additional cogs
|
||
- Integrate with existing mod bots via webhooks or shared log channel
|
||
|
||
## Troubleshooting
|
||
- **"Missing Permissions" on role assign** → Bot role not high enough in hierarchy
|
||
- **Commands not appearing** → Restart bot after `GUILD_ID` change; wait 1h for global sync
|
||
- **Isolated user messages still appear publicly** → The stealth suppression logic in on_message is not triggering (check that the user has the Excommunicado role or is in the isolated_users JSON)
|
||
- **Auto-detection too sensitive** → Increase `FLOOD_WINDOW_SECONDS` or threshold
|
||
- **JSON corrupted** → The bot auto-recovers: the bad file is moved to `data/xcom_config.json.corrupt` and a fresh one is created. Restore from the backup if you need the old data.
|
||
- **`[XCOM]` mark not applied** → Bot is missing `Manage Nicknames`, or the target outranks the bot (role assignment still succeeds; the mark is best-effort)
|
||
|
||
## Security Best Practices
|
||
- Never commit `.env` or the token (the provided `.gitignore` already excludes `.env`, `.env.*`, and `data/`)
|
||
- Use a dedicated bot account (not your personal account)
|
||
- Give the bot the minimum permissions required
|
||
- Regularly audit `data/xcom_config.json` for old entries
|
||
- Give trusted staff the `Excommunicado` role so they can share the moderated room with XCOM users and respond naturally
|
||
|
||
### Built-in hardening
|
||
- **No mention abuse**: forwarded messages are sent with mentions disabled, so an isolated user cannot make the bot ping `@everyone`, `@here`, or any role.
|
||
- **Length-safe forwarding**: relocated messages are truncated to Discord's 2000-character limit; image/sticker-only messages forward a placeholder instead of failing.
|
||
- **Atomic persistence**: config writes can't half-complete and corrupt state; a corrupt file is quarantined automatically.
|
||
- **Role-hierarchy enforcement**: `/xcom` will not target the owner, equal/higher-ranked members, or members above the bot.
|
||
- **Command cooldowns**: `/xcom` and `/xcom-absolve` are rate-limited (5 per 15s per moderator) and surface a friendly "slow down" message instead of erroring.
|
||
- **Runs as non-root** in Docker with healthchecks and log rotation.
|
||
|
||
## Extending the Bot
|
||
Example: add a `/xcom-stats` command counting total excommunications.
|
||
|
||
1. Add new `@app_commands.command` in `moderation.py`
|
||
2. Use `get_isolated_users` to aggregate data
|
||
3. Register in `setup()`
|
||
|
||
The modular cog design makes extension trivial.
|
||
|
||
## License
|
||
Internal moderation tool. Free to modify and use within your communities.
|
||
|
||
---
|
||
|
||
**Excommunicado** – Excommunicate the flood, keep the member.
|
||
```
|
||
(End of full documentation)
|
||
|
||
Made to keep your server clean without destroying community members.
|
||
```
|
||
|
||
## Usage Examples
|
||
- `/xcom @Spammer42 Spamming links and flooding`
|
||
- `/xcom-list`
|
||
- `/xcom-release @Spammer42`
|
||
|
||
The user is now under XCOM in the shared moderated room — problem contained.
|