NetModerator

Joined
Mar 29, 2019
Messages
1,497
Reaction score
3,148
Hey guys, i'm back with another piece of the puzzle the "NetModerator", use or not is up to you.

A C# .NET 8 replacement for the original NetModerator microservice used by TERA's ArbiterServer. It handles chat filtering over raw TCP and exposes a web dashboard for live monitoring.

What It Does​

ArbiterServer connects to NetModerator over raw TCP and forwards chat messages before broadcasting them. NetModerator checks each message against a banned-word list, censors matches (e.g. **** → ****), and returns the result. It also persists chat history to MySQL and exposes a browser dashboard.

What works:

  • Chat filtering (EvaluateChat) — censors banned words, returns censored text
  • Banned word management (add / deactivate / permanently delete / bulk import)
  • Moderator bypass — moderators are never filtered
  • Chat history stored in MySQL, viewable per-channel in the dashboard
  • Live dashboard with 3-second auto-refresh at http://<DashboardHost>:<DashboardPort>/
  • REST API for word and moderator management

What does not work:

  • EvaluateString (character names, guild names, servant names) — ArbiterServer's callback always uses the original string and discards filtered_string from the response. Filtering these fields is a no-op by design in the binary. Blocking still works (returning an error code).

Enable / Disable in ArbiterServer​

In DeploymentConfig.xml inside <ArbiterServerConfig>:

Code:
<!-- Enable -->
<NetModerator use="true" ip="127.0.0.1" port="12345" timeout="1000" threadNum="8" sessionNum="8" />

Code:
<!-- Disable --> 
<NetModerator use="false" ip="127.0.0.1" port="12345" timeout="1000" threadNum="8" sessionNum="8" />


Note: When the service is unreachable with use="true", ArbiterServer silently allows all text through — it never blocks players due to service downtime.

NetModeratorConfig.xml

ArbiterServer requires a NetModeratorConfig.xml file in its root folder (same directory as ArbiterServer*.exe). This file defines which chat types and string fields are evaluated. Without it, ArbiterServer will crash on startup.

In-Game / TextAdmin Commands​


CommandDescription
turn_netm onEnables the NetModerator filter at runtime
turn_netm offDisables the NetModerator filter at runtime
reload_netmReloads the NetModerator configuration from file

Config.json

Code:
{
  "Host": "127.0.0.1",       // TCP host ArbiterServer connects to
  "Port": 12345,              // TCP port ArbiterServer connects to
  "DashboardHost": "127.0.0.1", // Dashboard HTTP bind address
  "DashboardPort": 8765,      // Dashboard HTTP port
  "ReloadInterval": 300,      // Seconds between auto-reloads of banned words from DB
  "HistoryLimit": 2000,       // Max messages kept in memory per channel
  "LogLevel": "info",         // Logging verbosity: debug | info | warn | error
  "Database": { //Replace all database values with your own values
    "Host": "127.0.0.1",
    "Port": 3306,
    "User": "netmod", 
    "Password": "netmod",
    "Database": "netmoderator",
    "Charset": "utf8mb4"
  }
}

Dashboard​

Open http://<DashboardHost>:<DashboardPort>/ in a browser.

  • Chat tabs — one per channel (NORMAL, GUILD, WHISPER, etc.), auto-refreshes every 3 seconds
  • Search — search chat history by user ID, name, channel, and date range
  • Banned Words — active/inactive dual list; import from AbuseLetterList .xml or .txt (one word per line)
  • Moderators — manage moderator account IDs (moderators bypass all filtering)

REST API

MethodPathDescription
GET/api/statsStats + active/inactive word counts
GET/api/searchSearch chat history (sender, name, chatType, dateFrom, dateTo)
GET/api/chat/{CHANNEL}Live messages for a channel (e.g. /api/chat/NORMAL)
POST/api/wordsAdd / reactivate a banned word ({"word":"..."})
DELETE/api/words/{word}Deactivate a word (sets active=0)
DELETE/api/words/permanent/{word}Hard-delete a word from the database
POST/api/words/reloadReload banned words from DB into memory
POST/api/words/importBulk import — body is XML (AbuseLetterList.xml) or plain text ((AbuseLetterList.txt))
POST/api/moderatorsAdd a moderator ({"accountId":"..."})
DELETE/api/moderators/{accountId}Remove a moderator


Database Schema​

Four tables are auto-created on startup:

  • chat_history — all chat messages with sender, level, channel, filtered/censored flags
  • banned_words — word list with active flag (0 = inactive, 1 = active)
  • moderators — account IDs exempt from filtering
  • violations — records every filtered/blocked message (sender, name, word matched, timestamp)

Some screenshots,

NetModerator

Archgeus - NetModerator - RaGEZONE Forums


ArbiterServer

Archgeus - NetModerator - RaGEZONE Forums


In-game

Archgeus - NetModerator - RaGEZONE Forums


Archgeus - NetModerator - RaGEZONE Forums


Dashboard

Archgeus - NetModerator - RaGEZONE Forums


Note 2: If you disabled the netmoderator, but some words now appear censored as #$%& instead of *****
R:\ That's your AbuseLetterList in action plus InputRestriction, some versions (like 31.04) have 2 AbuseLetterList files, one in the DataCenter and the other one in a resource container (DownloadedResources) so the words that you are watching censored comes from one of those files.

Why in Development section?

Because i don't know how is the behavior in scale.

How i come with this ****?

Just being curious since there are a lot of things that didn´t come with the server leaks, and... why not...

Reverse engineered ArbiterServer using IDA Pro and Claude Sonnet.


Cheers.
 

Attachments

Back