- 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 works:
What does not work:
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.
Config.json
REST API
Some screenshots,
NetModerator
ArbiterServer
In-game
Dashboard
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.
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
| Command | Description |
| turn_netm on | Enables the NetModerator filter at runtime |
| turn_netm off | Disables the NetModerator filter at runtime |
| reload_netm | Reloads 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
| Method | Path | Description |
| GET | /api/stats | Stats + active/inactive word counts |
| GET | /api/search | Search chat history (sender, name, chatType, dateFrom, dateTo) |
| GET | /api/chat/{CHANNEL} | Live messages for a channel (e.g. /api/chat/NORMAL) |
| POST | /api/words | Add / 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/reload | Reload banned words from DB into memory |
| POST | /api/words/import | Bulk import — body is XML (AbuseLetterList.xml) or plain text ((AbuseLetterList.txt)) |
| POST | /api/moderators | Add 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
ArbiterServer
In-game
Dashboard
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.

