Moderation
Everything on this page lives in modules/moderation.yml, except mutes, which are
commands rather than config.
The whole file
Section titled “The whole file”cooldown: 1scooldown-affect-pm: false
max-length: chars: 0 affect-pm: true
anti-repeat: enabled: false affect-pm: false window-seconds: 30 similarity: enabled: false threshold: 0.85 history: 3
anti-flood: enabled: false affect-pm: true max-run: 8 max-word-length: 30
filter: enabled: false affect-pm: true mode: censor censor-char: "*" words: [] words-file: "" patterns: [] normalize: leetspeak: true diacritics: true separators: true repeated: true
links: enabled: false affect-pm: true mode: block ips: true whitelist: - youtube.com - youtu.be
slowmode: enabled: false
history: enabled: false affect-pm: false keep-days: 14 entries: 15
chat-clear-lines: 100Every check ships off. A fresh server does not filter, rate limit or slow anything until you
flip the enabled under the check you want, and /voxen reload picks it up.
Private messages
Section titled “Private messages”Every check on this page is written for public chat first. Each one has its own affect-pm
switch that decides whether /msg and /r go through it as well, so you pick what belongs
in a private conversation and what does not.
| Key | Default | Applies to PM |
|---|---|---|
cooldown-affect-pm |
false |
the delay between messages |
max-length.affect-pm |
true |
the character limit |
anti-repeat.affect-pm |
false |
exact and similar repeats |
anti-flood.affect-pm |
true |
character runs and mashed keys |
filter.affect-pm |
true |
blocked words and patterns |
links.affect-pm |
true |
links and IP addresses |
history.affect-pm |
false |
writing the message to the chat log |
The defaults say no to the two rate limits and yes to the content checks. A conversation sends “ok” twice and answers within a second, which is not spam, but a slur or an advertised address is just as unwelcome in a PM as in chat.
The bypass permissions are the same nodes as in chat, so voxen.bypass.filter covers both.
Cooldowns are shared with chat rather than counted separately, so switching
cooldown-affect-pm on means a PM also delays the next public message. Mutes have their own
switch, respect-mutes in modules/private-messages.yml.
With history.affect-pm on, private messages land in the same chat_log table under the
channel name @pm, so /voxen history <player> shows them alongside public messages. Only
the sender’s server writes the row.
Message length
Section titled “Message length”max-length: chars: 0 affect-pm: truechars: 0 is off, which is the default. Anything above that rejects messages longer than
that many characters before formatting, counted on what the player typed. The
message-too-long message gets <max>.
Clients cap chat input at 256 characters anyway, so this mostly matters for messages that arrive through a command or another plugin.
Cooldown and anti-repeat
Section titled “Cooldown and anti-repeat”| Key | Meaning |
|---|---|
cooldown |
minimum delay between any two messages per player, empty disables it |
anti-repeat.enabled |
blocks the same message twice within window-seconds |
anti-repeat.similarity.enabled |
also blocks messages that are only slightly changed |
anti-repeat.similarity.threshold |
how alike two messages must be, 0.0 to 1.0 |
anti-repeat.similarity.history |
how many recent messages each new one is compared against |
With similarity off, only an exact repeat counts. With it on, hi there! right after
hi there is caught too. Lower the threshold to catch more, at the cost of the occasional
false positive on short messages. voxen.bypass.spam skips the check entirely,
voxen.bypass.cooldown skips the delay.
Channels can have their own cooldown on top of the global one, which is the usual way to
slow down a trade channel without slowing down everything.
The chat-repeat message gets <threshold>, the configured percentage, so players can see
how close is too close. The same value is available anywhere else as
%voxen_similarity_threshold% or <voxen_similarity_threshold>.
Anti-flood
Section titled “Anti-flood”| Key | Meaning |
|---|---|
anti-flood.enabled |
rejects messages that are only noise |
anti-flood.max-run |
how many identical characters in a row are still allowed |
anti-flood.max-word-length |
longest allowed word, 0 turns the check off |
This is the one that stops aaaaaaaaaaaaaaaaaaaa and a wall of mashed keys. Two checks,
both cheap and both aimed at message shape, not content:
- a run of more than
max-runof the same character.helloooooandaaaaaaaastill pass at the default of 8,aaaaaaaaadoes not. - a single word longer than
max-word-length. Normal sentences have spaces, keyboard mash does not. Words containing.,:or/are skipped so links keep going to the link filter, where the whitelist can let them through.
voxen.bypass.spam skips it, same as anti-repeat.
Word filter
Section titled “Word filter”| Key | Meaning |
|---|---|
filter.mode |
block rejects the message, censor masks matches with censor-char |
filter.words |
blocked words, matched case-insensitively |
filter.words-file |
extra word list file, one word per line, # starts a comment |
filter.patterns |
blocked regex patterns |
filter.normalize.leetspeak |
matches digit and symbol swaps, so d4rn and $hoot hit |
filter.normalize.diacritics |
strips accents, so dąrn and shoöt hit |
filter.normalize.separators |
ignores spacing and punctuation, so d.a.r.n hits |
filter.normalize.repeated |
collapses runs of the same letter, so daaarrn hits |
words-file is a path relative to the plugin folder, which keeps a long list out of the
YAML:
filter: words-file: "blocked-words.txt"repeated also collapses the words you listed, so daarn in the list and darn in a
message match each other. The price is the occasional false positive on words that legitimately
double a letter, since pass is compared as pas. Turn it off if that bites.
Endings are not handled for you
Section titled “Endings are not handled for you”Matching is on substrings, not whole words, so a listed word catches anything containing
it: darn also hits darned and goddarn. It does not work backwards, though, so a full
form like darned misses the plain darn. List the stem and every ending follows.
filter: words: - darn # darn, darned, darningThis matters most in inflected languages, where one word has a dozen endings. One stem covers them all.
In censor mode, players with voxen.filter.toggle can run /filter to see the
uncensored original. block mode rejects messages before they exist, so there is nothing
to reveal.
Custom font glyphs
Section titled “Custom font glyphs”glyphs: enabled: false mode: "censor" affects-private-messages: trueNexo, Oraxen and ItemsAdder let players write :name: and turn it into a glyph from the
resource pack, each checking its own permission before it substitutes. That check only covers
the substitution. A player who copies the finished character out of chat and pastes it back is
not substituting anything, so the glyph renders whatever those permissions say.
Voxen closes that by looking at the characters themselves: anything in the Unicode private use
areas, which is where all of those plugins put their glyphs, is removed unless the player holds
voxen.chat.glyphs. mode: block refuses the whole message instead of cleaning it, and
affects-private-messages covers /msg as well.
Staff with voxen.filter.toggle see the original text, the same as with the word filter, so
nothing is hidden from the people reviewing it.
Link filter
Section titled “Link filter”| Key | Meaning |
|---|---|
links.enabled |
the link and IP filter |
links.mode |
block rejects the message, censor masks the link |
links.ips |
also catches bare IPv4 addresses like 1.2.3.4:25565 |
links.obfuscated |
also catches play,server,pl, where a comma or apostrophe stands in for the dot |
links.whitelist |
domains that stay allowed, subdomains included |
obfuscated covers the usual dodge of writing play,server,pl or play'server'pl instead
of dots. To keep normal sentences out of it, a match has to end in something that really
looks like a domain ending (com, pl, gg, xyz and the rest of the common list) and
start with at least three characters, so hello, world and no,it isn't pass. The
whitelist applies here too.
It runs before the word filter and has its own bypass, voxen.bypass.links, so staff
can post links while everyone else cannot. Addresses count with or without a scheme, so
example.com/thing is caught. A whitelist entry matches the host and its subdomains:
youtube.com also allows www.youtube.com and m.youtube.com.
Slowmode
Section titled “Slowmode”/voxen slowmode global 10s/voxen slowmode global offRaises a channel’s cooldown at runtime. It never makes a channel faster than the
cooldown in its own file, it only raises it. voxen.bypass.cooldown skips it like any
other cooldown.
-
Mute someone, optionally on one channel only:
/voxen mute Steve 2h global spamming/voxen mute Steve permanent all -
Check what is active:
/voxen mutes/voxen muteinfo Steve -
Lift it:
/voxen unmute Steve all
Durations are 10m, 2h, 7d or permanent. A raw UUID works instead of a name, which
covers players who have never joined this server. voxen.mute.exempt makes someone
unmutable.
/voxen mutechat silences everyone at once, /voxen mutechannel <channel> silences one
channel. Both have their own bypass nodes.
On a network, mutes are broadcast to every server the moment they are set. See Cross-server chat.
Chat history
Section titled “Chat history”history: enabled: false keep-days: 14 entries: 15Off by default. When enabled, messages are written to the chat_log table after they pass
the filter, together with the channel and the server-id they came from, so on a network
every server writes into the same log if they share a database.
/voxen history <player> shows the newest entries messages. Rows older than
keep-days are deleted on startup; 0 keeps them forever.
Chat clear
Section titled “Chat clear”chat-clear-lines is how many blank lines /voxen chatclear pushes. Players with
voxen.mod.chatclear.exempt keep their chat visible during a global clear, so staff do not
lose context. /voxen chatclear <player> clears one person.