# Chat Manager
**Ranks in chat, tags in the scoreboard, a full chat log and a Discord relay — in one plugin.**
Give your owners, admins and VIPs a coloured prefix in chat and a matching tag in the TAB scoreboard,
mirror everything into a Discord channel, and keep a searchable log of every message. Players get a
menu to tune their own look. Every last string is configurable — you never touch code.
---
## What it looks like
### In chat
You write this:
```json
"owner": {
"ChatPrefix": "{DARKRED}[OWNER]",
"ChatNameColor": "{LIGHTRED}",
"ChatMessageColor": "{WHITE}",
"ScoreboardTag": "[OW]"
}
```
Players see this — prefix, name and the message body each in their own colour:
```
[OWNER] illusion: hey everyone
[ADMIN] SomeGuy: rotating B
(T) [VIP] Player: two left in site
*DEAD* [VIP] Player: he's behind you
```
The whole layout is yours: reorder it, change the separator, add a team colour that follows whoever
is talking. Want *every* player styled, not just staff? One setting does that.
### In the scoreboard
```
[OW] illusion 22 4
[AD] SomeGuy 17 9
[VIP] Player 8 12
```
### In Discord
Names link straight to the Steam profile. Ranks get their own badge. The message sits on its own
quoted line so it never gets lost:
> 💬 👑 **illusion** 🔴 `18:42:07`
> > hey, rotating B
>
> 🛡️ ⭐ **SomeGuy** 🔵 💀 `18:42:19`
> > two left in site
### In your logs
A dated file per day, plus the same line in your server console:
```
[2026-09-03 18:42:07] [ALL] [owner] illusion (76561198012345678 | 1.2.3.4) [CT/ALIVE]: hey everyone
[2026-09-03 18:42:19] [TEAM] [vip] SomeGuy (76561198087654321 | 5.6.7.8) [T/DEAD]: two left in site
```
---
## Features
**Ranks and tags**
- Coloured chat prefix, name colour and message colour per rank
- Matching scoreboard tag — set the rank once, both follow
- Assign ranks by **admin flag/group**, **SteamID** or **player name** (with wildcards)
- Priorities decide who wins when a player matches several ranks
- `{TEAMCOLOR}` colours text red for T and blue for CT, following the speaker
**Chat logging**
- Daily rotating log file with timestamp, rank, name, SteamID, IP, team and message
- The same line in your server console, in your own layout
- Every field and label is configurable; IPs can be turned off
**Discord relay**
- Mirrors chat into any channel through a webhook
- Clickable Steam profile links, rank badges and team/alive emoji
- IPs hidden by default, batched posts that respect Discord's rate limits
- Players can't ping `@everyone` or format your channel — both are blocked
**For your players**
- `!cm` opens a menu where a player can switch off their own prefix, colours or scoreboard tag
- Their choice is remembered between reconnects, maps and restarts
- Players can **only** hide their own styling — nobody can pick a prefix or touch anyone else
**For your staff**
- `!clearchat` / `!cc` wipes the chat with a configurable "cleared by X" announcement
- Live config reload — no restart
- Warns you in console if another plugin is fighting over the same chat line or scoreboard tag
---
## Commands
| Command | Also works as | Who can use it |
| --- | --- | --- |
| `css_cm` | `!cm` `/cm` | `@css/reservation` by default — or everyone, your choice |
| `css_clearchat` | `!clearchat` `/clearchat` | `@css/ban` by default |
| `css_cc` | `!cc` `/cc` | same as above |
| `css_chat_reload` | `!chat_reload` | `@css/root` |
| `css_chat_debug` | `!chat_debug` | `@css/root` — diagnostics if a tag ever looks stuck |
Every permission is configurable. **Leave one empty and that command is open to everyone.**
---
## Requirements
- CS2 server with [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp/releases)
- [MenuManagerCS2](https://github.com/NickFox007/MenuManagerCS2) — **optional**, only for the `!cm`
menu. Everything else works without it.
## Installation
1. Copy the files into `addons/counterstrikesharp/plugins/ChatManager/`
2. Restart the server, or run `css_plugins load ChatManager`
3. Edit `addons/counterstrikesharp/configs/plugins/ChatManager/ChatManager.json`
4. Run `css_chat_reload` — no restart needed
That's it. The config generates itself on first load with sensible defaults, and the plugin prints
the exact paths to your console so you always know where things are.
---
## Configuration
Everything lives in **one JSON file**. Ranks are defined once and referenced by name:
```json
"Tags": {
"owner": { "Priority": 100, "ChatPrefix": "{DARKRED}[OWNER]", "ScoreboardTag": "[OW]" },
"vip": { "Priority": 40, "ChatPrefix": "{GOLD}[VIP]", "ScoreboardTag": "[VIP]" }
},
"Assignments": {
"Permissions": { "@css/root": "owner", "#css/vip": "vip" },
"SteamIds": { "76561198012345678": "owner" },
"Names": { "*[BG]*": "vip" }
}
```
A player who matches several ranks gets the highest `Priority`. Anything pointing at a rank that
doesn't exist is skipped with a clear warning — a typo never takes your server down.
### Colours
Drop these anywhere a colour is allowed. Tokens on the same row are the same colour.
| Token | Colour |
| --- | --- |
| `{DEFAULT}` `{WHITE}` | White — the normal chat colour |
| `{DARKRED}` | Dark red |
| `{GREEN}` | Green |
| `{OLIVE}` | Olive |
| `{LIME}` | Lime |
| `{RED}` | Red |
| `{GREY}` `{GRAY}` | Grey |
| `{YELLOW}` | Yellow |
| `{SILVER}` `{BLUEGREY}` | Silver / blue-grey |
| `{BLUE}` `{LIGHTBLUE}` | Light blue |
| `{DARKBLUE}` | Dark blue |
| `{PURPLE}` `{MAGENTA}` | Purple |
| `{LIGHTRED}` | Light red |
| `{GOLD}` `{ORANGE}` | Orange-gold |
| **`{TEAMCOLOR}`** | **Red for T, blue for CT — follows the speaker** |
Watch out: `{BLUE}` is the *light* blue, `{DARKBLUE}` is the dark one, and there is no separate
orange — `{GOLD}` is it.
### Layout
`ChatFormat` decides the whole line. The default is:
```
{DEAD}{PREFIX} {NAME}{DEFAULT}: {MESSAGE}
```
`{PREFIX}` `{NAME}` `{MESSAGE}` `{TEAM}` `{DEAD}` — arrange them however you like. Team chat has its
own format. The log file, the console and Discord each have their own too, with tokens for the time,
SteamID, IP, team, rank and more.
📖 **[Full configuration reference →](CONFIGURATION.md)** — every setting explained, plus the Discord
tokens, the personalisation menu, conflict warnings and troubleshooting.
---
## Good to know
Written plainly, because you should know before you buy:
- **It restyles chat by replacing the message.** That is how every plugin of this kind works, and it
has one side effect: a player somebody muted in the scoreboard is still visible to them, because a
plugin-printed line doesn't consult that mute list. If your server needs client mutes to hold, run
the plugin with chat tags off and use the scoreboard tags and logging instead.
- **If you already run a gag/mute plugin**, load order decides who wins. Test it — it takes a minute,
and the fix is one setting either way.
- **If another plugin also writes scoreboard tags** (rank, Faceit level…), the two will fight. The
plugin detects this and tells you in console instead of leaving you guessing.
- **Blocked messages may still be logged.** If another plugin blocks a message after this one has
seen it, it can still reach your log and Discord.
## Performance
Built for a full server. Chat lines are pre-compiled when the config loads, not rebuilt per message.
Log and Discord writes are handed to background tasks, so the game thread never waits on your disk or
on Discord. If Discord goes down or the disk falls behind, messages are dropped and counted — the
server never stalls. Clearing chat is spread over several frames so 64 players don't cost you a tick.
---
## Full example config
A complete, working config with everything filled in:
```json
{
"Enabled": true,
"ChatTagsEnabled": true,
"ScoreboardTagsEnabled": true,
"DefaultTag": "player",
"NameMatchEnabled": true,
"PermissionMatchMode": "inherit",
"IgnoredChatPrefixes": [ "!", "/", ".", "@" ],
"MaxMessageLength": 256,
"ChatFormat": "{DEAD}{PREFIX} {NAME}{DEFAULT}: {MESSAGE}",
"TeamChatFormat": "{DEAD}{TEAM}{PREFIX} {NAME}{DEFAULT}: {MESSAGE}",
"DeadTag": "{GREY}*DEAD*{DEFAULT} ",
"TeamTagT": "{GREY}(T){DEFAULT} ",
"TeamTagCT": "{GREY}(CT){DEFAULT} ",
"TeamTagSpectator": "{GREY}(SPEC){DEFAULT} ",
"TeamColorT": "{RED}",
"TeamColorCT": "{BLUE}",
"TeamColorSpectator": "{GREY}",
"RestrictDeadTeamChat": false,
"ScoreboardRefreshInterval": 10.0,
"ReapplyTagsOnRoundStart": true,
"ClearClanTagWhenUntagged": false,
"ForceScoreboardRefresh": true,
"EmptyScoreboardTag": " ",
"ChatLog": {
"Enabled": true,
"LogToFile": true,
"LogToConsole": true,
"LogCommands": false,
"LogUntaggedPlayers": true,
"IncludeIp": true,
"UseUtcTime": false,
"Directory": "addons/counterstrikesharp/logs/ChatManager",
"FileNameFormat": "chat_{DATE}.log",
"DateFormat": "yyyy-MM-dd",
"TimestampFormat": "yyyy-MM-dd HH:mm:ss",
"FileFormat": "[{TIME}] [{CHANNEL}] [{TAG}] {NAME} ({STEAMID} | {IP}) [{TEAM}/{STATE}]: {MESSAGE}",
"ConsoleFormat": "[ChatManager] [{TIME}] [{CHANNEL}] [{TAG}] {NAME} ({STEAMID} | {IP}): {MESSAGE}",
"ChannelAllLabel": "ALL",
"ChannelTeamLabel": "TEAM",
"AliveLabel": "ALIVE",
"DeadLabel": "DEAD",
"SpectatorLabel": "SPEC",
"TerroristLabel": "T",
"CounterTerroristLabel": "CT",
"UntaggedLabel": "-",
"UnknownValueLabel": "unknown",
"HiddenIpLabel": "hidden",
"MaxQueuedLines": 4096
},
"Discord": {
"Enabled": true,
"WebhookUrl": "https://discord.com/api/webhooks/000000000/xxxxxxxxxxxxxxxx",
"Username": "ChatManager",
"AvatarUrl": "",
"MessageFormat": "{CHANNELEMOJI} {TAGEMOJI} **{PROFILE}** {TEAMEMOJI}{STATEEMOJI} `{TIME}`\n> {MESSAGE}",
"ChannelAllEmoji": "💬",
"ChannelTeamEmoji": "🛡️",
"TeamTEmoji": "🔴",
"TeamCTEmoji": "🔵",
"SpectatorEmoji": "👁️",
"AliveEmoji": "",
"DeadEmoji": "💀",
"SteamProfileUrl": "https://steamcommunity.com/profiles/{STEAMID}",
"IncludeIp": false,
"LogCommands": false,
"LogUntaggedPlayers": true,
"TimestampFormat": "HH:mm:ss",
"UseUtcTime": false,
"BatchIntervalSeconds": 3.0,
"MaxQueuedMessages": 500,
"SuppressEmbeds": true,
"EscapeMarkdown": true
},
"Personalization": {
"Enabled": true,
"Permission": "@css/reservation",
"StorageFile": "addons/counterstrikesharp/data/ChatManager/preferences.json",
"SaveIntervalSeconds": 30.0,
"DefaultShowPrefix": true,
"DefaultShowNameColor": true,
"DefaultShowMessageColor": true,
"DefaultShowScoreboardTag": true,
"MenuTitle": "Chat settings",
"OptionPrefix": "Chat prefix: {STATE}",
"OptionNameColor": "Coloured name: {STATE}",
"OptionMessageColor": "Coloured message: {STATE}",
"OptionScoreboardTag": "Scoreboard tag: {STATE}",
"OptionReset": "Turn everything back on",
"StateOn": "ON",
"StateOff": "OFF",
"NoPermissionMessage": "{RED}You do not have access to the chat settings menu.",
"MenuUnavailableMessage": "{RED}The menu system is unavailable - MenuManager is not installed.",
"DisabledMessage": "{RED}Chat personalisation is disabled on this server."
},
"ClearChat": {
"Enabled": true,
"Permission": "@css/ban",
"Lines": 60,
"LinesPerFrame": 10,
"AnnounceMessage": "{GREEN}[ChatManager]{DEFAULT} Chat was cleared by {LIGHTRED}{NAME}{DEFAULT}.",
"ConsoleName": "Console",
"NoPermissionMessage": "{RED}You do not have access to this command.",
"DiscordMessage": ":broom: Chat was cleared by **{NAME}**"
},
"ConflictDetection": {
"Enabled": true,
"WarnOnKnownPlugins": true,
"WarnOnClanTagDrift": true,
"ClanTagDriftThreshold": 5,
"KnownPrefixPlugins": [
"tags", "prefix", "chatcolor", "chatcolour", "simpleadmin", "k4-system", "k4system",
"ranks", "faceit", "clantag", "scoreboard"
]
},
"Tags": {
"owner": {
"Priority": 100,
"ChatPrefix": "{DARKRED}[OWNER]",
"ChatNameColor": "{LIGHTRED}",
"ChatMessageColor": "{WHITE}",
"ScoreboardTag": "[OW]",
"DiscordEmoji": "👑",
"ChatPrefixEnabled": true,
"ScoreboardTagEnabled": true
},
"admin": {
"Priority": 80,
"ChatPrefix": "{RED}[ADMIN]",
"ChatNameColor": "{LIGHTRED}",
"ChatMessageColor": "{WHITE}",
"ScoreboardTag": "[AD]",
"DiscordEmoji": "🛠️",
"ChatPrefixEnabled": true,
"ScoreboardTagEnabled": true
},
"moderator": {
"Priority": 60,
"ChatPrefix": "{BLUE}[MOD]",
"ChatNameColor": "{LIGHTBLUE}",
"ChatMessageColor": "{WHITE}",
"ScoreboardTag": "[MD]",
"DiscordEmoji": "🔧",
"ChatPrefixEnabled": true,
"ScoreboardTagEnabled": true
},
"vip": {
"Priority": 40,
"ChatPrefix": "{GOLD}[VIP]",
"ChatNameColor": "{YELLOW}",
"ChatMessageColor": "{WHITE}",
"ScoreboardTag": "[VIP]",
"DiscordEmoji": "⭐",
"ChatPrefixEnabled": true,
"ScoreboardTagEnabled": true
},
"player": {
"Priority": 0,
"ChatPrefix": "{GREY}[PLAYER]",
"ChatNameColor": "{WHITE}",
"ChatMessageColor": "{GREY}",
"ScoreboardTag": "",
"ChatPrefixEnabled": true,
"ScoreboardTagEnabled": true
}
},
"Assignments": {
"Permissions": {
"@css/root": "owner",
"@css/ban": "admin",
"@css/kick": "moderator",
"@css/reservation": "vip",
"#css/vip": "vip"
},
"SteamIds": {
"76561198012345678": "owner",
"76561198087654321": "vip"
},
"Names": {
"ExactNickname": "vip",
"MyClan|*": "moderator",
"*[BG]*": "vip"
}
},
"ConfigVersion": 7
}
```