StaffChat

A private chat channel for your server's staff team.

16

StaffChat

StaffChat

never leak One command, one permission node, zero noise. Toggle into staff mode to keep the next conversation off public chat — or quick-fire a single line with `@hello` without ever leaving the global channel. Built so secret conversations never leak through Discord-bridge mods that mirror public chat.

> No hard dependencies — for No hard dependencies. Server-side only. No hard dependencies beyond Fabric API. LuckPerms optional. Drop it in `mods/`, op yourself, and it works.

---

What it does

Three ways to send to the staff channel, all gated by a single permission `staffchat.true`:

- `@<message>` quick prefix alone toggles "staff chat mode" — every regular chat line you type now goes to staff only until you toggle off. A title pulse + a persistent `● Staff Chat Mode` actionbar reminds you you're in stealth mode so you never accidentally leak to global. - `@<message>` quick prefix (or `/sc <message>`) sends a one-off without changing your toggle state. Good for a quick reply mid-game. - `@<message>` quick prefix in normal chat — type `@need help at spawn` and the message routes to staff only. The prefix character is configurable in case `@` clashes with your mention syntax.

Staff who *aren't* in toggle mode still receive every staff message. Toggle is purely a sending convenience — it doesn't gate reception.

Mention pings

Inside any staff message, drop `@<playername>` to ping that staff member: - Their actionbar flashes `★ Mentioned by <you>` - A configurable chime plays (default: experience-orb pickup) - Self-mentions skipped, offline / non-staff names skipped

Sound id, volume, pitch, and actionbar template are all in the config.

Built for privacy

This is a staff channel — leaks would be embarrassing. The mod is architected so:

- The only intended Discord mirror is the built-in webhook. We suppress at `ServerMessageEvents.ALLOW_CHAT_MESSAGE` (return false) before any chat broadcast happens. No `/say`-spam fallback, no whispered-to-self exposure. - The only intended Discord mirror is the built-in webhook. Bridges like Styled Chat, ServerSideUtils, simple-discord-bridge etc. hook the chat-broadcast pipeline. Because we suppress at the ALLOW phase and re-deliver as The only intended Discord mirror is the built-in webhook. (not chat messages), those bridges never observe a staff line. Your secret conversations stay secret. - The only intended Discord mirror is the built-in webhook. Off by default. Set `discord.enabled = true` and a webhook URL when you actually want staff mirrored to a specific Discord channel — at which point YOU control which channel sees it. - The webhook posts with `allowed_mentions.parse = []` so a staff member typing `@everyone` doesn't actually ping the Discord server.

Built-in Discord webhook (opt-in)

Optional. Off by default. Set: ```hocon discord { enabled = true webhook-url = "https://discord.com/api/webhooks/.../..." username-format = "{sender}" avatar-url-format = "https://mc-heads.net/avatar/{uuid}" retries = 1 } ``` Async POST via Java 21's `HttpClient` on a dedicated single-thread pool — never blocks the server tick. Failures retried up to `retries` times; misconfigured webhooks (4xx) auto-suppress for 5 minutes so a stale token doesn't spam your console.

LuckPerms metadata prefixes (soft-dep)

If LuckPerms is present, each sender's `[Prefix]` from LuckPerms metadata is automatically prepended to their name in staff chat. Without LuckPerms, fall back to a config-based map keyed by `staffchat.rank.<id>` permission:

```hocon prefixes { owner = "&c[Owner] " admin = "&4[Admin] " mod = "&9[Mod] " } ```

A player's prefix is the first matching `staffchat.rank.<id>` they hold, in declaration order. Final fallback: blank.

Defensive design

Same paranoid security patterns as our other server mods:

- Audit log — a staff member demoted between two messages silently drops off the recipient list with no leaking error. - Audit log strips control chars and the raw section-sign so nobody injects malformed Minecraft formatting; caps message length at `chat.max-length` (default 256); rejects "all formatting, no content" payloads. - Audit log — sliding 30-second window, default 10 messages per window, configurable. Sender gets a "slow down" reply, message dropped server-side, audit log records the rate-limit event. - Audit log uses explicit-only permission resolution — OPs don't auto-bypass unless LuckPerms specifically grants it. - Audit log — every staff send appends to `logs/staffchat-audit.log` with an 8-char base36 transaction id, timestamp, sender UUID + name, sanitized content, recipient count, and outcome (DELIVERED / RATE_LIMITED / SANITIZED / DROPPED). Atomic appends, useful for incident review.

Permissions

| Node | Default | Purpose | |---|---|---| | `staffchat.true` | OP 4 fallback | Send + receive in the staff channel | | `staffchat.bypass.rate-limit` | false (explicit only) | Ignore the rate limit | | `staffchat.admin.reload` | OP 3 | `/staffchat reload` | | `staffchat.admin.list` | OP 3 | `/staffchat list` | | `staffchat.rank.<id>` | — | Fallback prefix tier when LuckPerms is absent |

Without a permissions plugin: `/op <name>` is enough — OP 4 satisfies the `staffchat.true` fallback.

With LuckPerms: `lp group default permission set staffchat.true false` then grant per-rank.

Commands

``` /staffchat Toggle staff chat mode (staffchat.true) /staffchat <message> One-off send (no toggle change) (staffchat.true) /staffchat reload Reload config from disk (staffchat.admin.reload) /staffchat list List currently-online staff (staffchat.admin.list) /sc Alias for /staffchat toggle /sc <message> Alias for /staffchat <message> ```

Configuration

`config/staffchat/config.conf` is HOCON. Notable knobs:

```hocon staffchat { chat { format = "&8[&cSTAFF&8] &f{prefix}&7{sender}&8: &f{message}" quick-prefix = "@" # set to "" to disable max-length = 256 toggle-indicator = "&c● Staff Chat Mode" toggle-on-title = "&c&lStaff Chat ON" toggle-off-title = "&7&lStaff Chat OFF" } mentions { enabled = true sound-id = "minecraft:entity.experience_orb.pickup" sound-volume = 0.7 sound-pitch = 1.2 actionbar-text = "&e★ Mentioned by {sender}" } discord { enabled = false webhook-url = "" username-format = "{sender}" avatar-url-format = "https://mc-heads.net/avatar/{uuid}" retries = 1 } prefixes { ... } security { rate-limit-per-30s = 10 log-to-file = true log-file = "logs/staffchat-audit.log" log-to-console = false } persist-toggled-state = true message-locale = "auto" messages {} } ```

Edit by hand and `/staffchat reload`, or hot-reload to pick up edits live without bouncing the server.

i18n

English bundled. Drop `config/staffchat/lang/<locale>.json` to add any other language without rebuilding the jar. Loader prefers config-dir overrides over bundled resources, with `en_us` as the canonical fallback.

Requirements

Hard dependencies: - Java 21 - Java 21 - Java 21 - Java 21

Bundled inside the jar — no extra installs: - Fabric Permissions API - SpongePowered Configurate HOCON - Typesafe Config

Optional soft dependency: - LuckPerms — for `[Prefix]` metadata in staff messages.

What it intentionally is *not*

- Not client-side — the optional Discord webhook only mirrors the staff channel. It does not bridge global chat to Discord. Use a real chat-bridge mod for that. - Not client-side — no /mute, /kick, /ban. Use vanilla or a moderation mod. - Not client-side — one staff channel. If you want admin / mod / builder split channels, that's planned for v1.1. - Not client-side — runs server-side only, players need nothing installed.

Target audience

Server operators who want a quick, reliable, Discord-isolated way for their staff to talk in-game without spinning up Slack, a side voice channel, or a moderation suite they don't need. Drop the jar in `mods/`, op yourself, type `/staffchat` — done.

---

Author: MIT Author: souljagger

ADS