Jofthomas's picture
Jofthomas HF staff
Upload 4781 files
5c2ed06 verified
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var chat_monitor_exports = {};
__export(chat_monitor_exports, {
Filters: () => Filters,
chatfilter: () => chatfilter,
commands: () => commands,
loginfilter: () => loginfilter,
namefilter: () => namefilter,
nicknamefilter: () => nicknamefilter,
pages: () => pages,
statusfilter: () => statusfilter
});
module.exports = __toCommonJS(chat_monitor_exports);
var import_lib = require("../../lib");
const LEGACY_MONITOR_FILE = "config/chat-plugins/chat-monitor.tsv";
const MONITOR_FILE = "config/chat-plugins/chat-filter.json";
const WRITE_THROTTLE_TIME = 5 * 60 * 1e3;
const EVASION_DETECTION_SUBSTITUTIONS = {
a: ["a", "4", "@", "\xE1", "\xE2", "\xE3", "\xE0", "\u15E9", "A", "\u24D0", "\u24B6", "\u03B1", "\u034F", "\u20B3", "\xE4", "\xC4", "\u13D7", "\u03BB", "\u0394", "\u1E00", "\u13AA", "\u01DF", "\u033E", "\uFF41", "\uFF21", "\u1D00", "\u0250", "\u{1F150}", "\u{1D41A}", "\u{1D400}", "\u{1D622}", "\u{1D608}", "\u{1D656}", "\u{1D63C}", "\u{1D4B6}", "\u{1D4EA}", "\u{1D4D0}", "\u{1D552}", "\u{1D538}", "\u{1D51E}", "\u{1D504}", "\u{1D586}", "\u{1D56C}", "\u{1F130}", "\u{1F170}", "\u{1D49C}", "\u{1D68A}", "\u{1D670}", "\uA34F", "\u0430", "\u{1D4EA}"],
b: ["b", "8", "\u15F7", "B", "\u24D1", "\u24B7", "\u0432", "\u0E3F", "\u1E05", "\u1E04", "\u13F0", "\u03D0", "\u0181", "\u1E03", "\u1E02", "\u026E", "\uFF42", "\uFF22", "\u0299", "\u{1F151}", "\u{1D41B}", "\u{1D401}", "\u{1D623}", "\u{1D609}", "\u{1D657}", "\u{1D63D}", "\u{1D4B7}", "\u{1D4EB}", "\u{1D4D1}", "\u{1D553}", "\u{1D539}", "\u{1D51F}", "\u{1D505}", "\u{1D587}", "\u{1D56D}", "\u{1F131}", "\u{1F171}", "\u{1D435}", "\u10A6", "\u{1D68B}", "\u{1D671}", "\u266D", "b"],
c: ["c", "\xE7", "\u1455", "C", "\u24D2", "\u24B8", "\xA2", "\u034F", "\u20B5", "\u010B", "\u010A", "\u1348", "\u03C2", "\u1E09", "\u1E08", "\u13DF", "\u0188", "\u033E", "\uFF43", "\uFF23", "\u1D04", "\u0254", "\u{1F152}", "\u{1D41C}", "\u{1D402}", "\u{1D624}", "\u{1D60A}", "\u{1D658}", "\u{1D63E}", "\u{1D4B8}", "\u{1D4EC}", "\u{1D4D2}", "\u{1D554}", "\u2102", "\u{1D520}", "\u212D", "\u{1D588}", "\u{1D56E}", "\u{1F132}", "\u{1F172}", "\u{1D49E}", "\u{1D68C}", "\u{1D672}", "\u263E", "\u0441"],
d: ["d", "\u15EA", "D", "\u24D3", "\u24B9", "\u2202", "\u0110", "\u010F", "\u010E", "\u13B4", "\u1E0A", "\u13A0", "\u0256", "\uFF44", "\uFF24", "\u1D05", "\u{1F153}", "\u{1D41D}", "\u{1D403}", "\u{1D625}", "\u{1D60B}", "\u{1D659}", "\u{1D63F}", "\u{1D4B9}", "\u{1D4ED}", "\u{1D4D3}", "\u{1D555}", "\u200B", "\u{1D521}", "\u{1D589}", "\u{1D56F}", "\u{1F133}", "\u{1F173}", "\u{1D49F}", "\u0503", "\u{1D68D}", "\u{1D673}", "\u25D7", "\u217E"],
e: ["e", "3", "\xE9", "\xEA", "E", "\u24D4", "\u24BA", "\u0454", "\u034F", "\u0246", "\u1EC7", "\u1EC6", "\u13CB", "\u03B5", "\u03A3", "\u1E15", "\u1E14", "\u13AC", "\u025B", "\u033E", "\uFF45", "\uFF25", "\u1D07", "\u01DD", "\u{1F154}", "\u{1D41E}", "\u{1D404}", "\u{1D626}", "\u{1D60C}", "\u{1D65A}", "\u{1D640}", "\u212F", "\u{1D4EE}", "\u{1D4D4}", "\u{1D556}", "\u{1D53B}", "\u{1D522}", "\u{1D507}", "\u{1D58A}", "\u{1D570}", "\u{1F134}", "\u{1F174}", "\u{1D452}", "\u{1D438}", "\u04BD", "\u{1D68E}", "\u{1D674}", "\u20AC", "\u0435", "\u0451", "\u{1D4EE}"],
f: ["f", "\u15B4", "F", "\u24D5", "\u24BB", "\u20A3", "\u1E1F", "\u1E1E", "\u13A6", "\u0493", "\u0284", "\uFF46", "\uFF26", "\u025F", "\u{1F155}", "\u{1D41F}", "\u{1D405}", "\u{1D627}", "\u{1D60D}", "\u{1D65B}", "\u{1D641}", "\u{1D4BB}", "\u{1D4EF}", "\u{1D4D5}", "\u{1D557}", "\u{1D53C}", "\u{1D523}", "\u{1D508}", "\u{1D58B}", "\u{1D571}", "\u{1F135}", "\u{1F175}", "\u{1D439}", "\u03DD", "\u{1D68F}", "\u{1D675}", "\u03DC", "f", "\u0191"],
g: ["g", "q", "6", "9", "G", "\u24D6", "\u24BC", "\u034F", "\u20B2", "\u0121", "\u0120", "\u13B6", "\u03D1", "\u1E20", "\u0262", "\u033E", "\uFF47", "\uFF27", "\u0183", "\u{1F156}", "\u{1D420}", "\u{1D406}", "\u{1D628}", "\u{1D60E}", "\u{1D65C}", "\u{1D642}", "\u210A", "\u{1D4F0}", "\u{1D4D6}", "\u{1D558}", "\u{1D53D}", "\u{1D524}", "\u{1D509}", "\u{1D58C}", "\u{1D572}", "\u{1F136}", "\u{1F176}", "\u{1D454}", "\u{1D4A2}", "\u0260", "\u{1D690}", "\u{1D676}", "\u2761", "\u0581", "\u{1D676}", "\u{1D4F0}", "\u050C"],
h: [
"h",
"\u157C",
"H",
"\u24D7",
"\u24BD",
"\u043D",
"\u2C67",
"\u1E27",
"\u1E26",
"\u13C2",
"\u0266",
"\uFF48",
"\uFF28",
"\u029C",
"\u0265",
"\u{1F157}",
"\u{1D421}",
"\u{1D407}",
"\u{1D629}",
"\u{1D60F}",
"\u{1D65D}",
"\u{1D643}",
"\u{1D4BD}",
"\u{1D4F1}",
"\u{1D4D7}",
"\u{1D559}",
"\u{1D53E}",
"\u{1D525}",
"\u{1D50A}",
"\u{1D58D}",
"\u{1D573}",
"\u{1F137}",
"\u{1F177}",
"\u{1D43B}",
"\u050B",
"\u{1D691}",
"\u{1D677}",
"\u2644",
"h"
],
i: ["i", "!", "l", "1", "\xED", "I", "\u24D8", "\u24BE", "\u03B9", "\u034F", "\u0142", "\xEF", "\xCF", "\u13A5", "\u1E2D", "\u1E2C", "\u0268", "\u033E", "\uFF49", "\uFF29", "\u026A", "\u0131", "\u{1F158}", "\u{1D422}", "\u{1D408}", "\u{1D62A}", "\u{1D610}", "\u{1D65E}", "\u{1D644}", "\u{1D4BE}", "\u{1D4F2}", "\u{1D4D8}", "\u{1D55A}", "\u210D", "\u{1D526}", "\u210C", "\u{1D58E}", "\u{1D574}", "\u{1F138}", "\u{1F178}", "\u{1D43C}", "\u{1D692}", "\u{1D678}", "\u2657", "\u0456", "\xA1", "|", "\u{1D4F2}"],
j: ["j", "\u148D", "J", "\u24D9", "\u24BF", "\u05E0", "\u13E0", "\u03F3", "\u029D", "\uFF4A", "\uFF2A", "\u1D0A", "\u027E", "\u{1F159}", "\u{1D423}", "\u{1D409}", "\u{1D62B}", "\u{1D611}", "\u{1D65F}", "\u{1D645}", "\u{1D4BF}", "\u{1D4F3}", "\u{1D4D9}", "\u{1D55B}", "\u200B", "\u{1D527}", "\u{1D58F}", "\u{1D575}", "\u{1F139}", "\u{1F179}", "\u{1D4A5}", "\u{1D693}", "\u{1D679}", "\u266A", "\u0458"],
k: ["k", "K", "\u24DA", "\u24C0", "\u043A", "\u034F", "\u20AD", "\u1E33", "\u1E32", "\u13E6", "\u03BA", "\u0198", "\u04C4", "\u033E", "\uFF4B", "\uFF2B", "\u1D0B", "\u029E", "\u{1F15A}", "\u{1D424}", "\u{1D40A}", "\u{1D62C}", "\u{1D612}", "\u{1D660}", "\u{1D646}", "\u{1D4C0}", "\u{1D4F4}", "\u{1D4DA}", "\u{1D55C}", "\u{1D540}", "\u{1D528}", "\u2111", "\u{1D590}", "\u{1D576}", "\u{1F13A}", "\u{1F17A}", "\u{1D4A6}", "\u0199", "\u{1D694}", "\u{1D67A}", "\u03F0", "k", "\u{1D4F4}"],
l: ["l", "i", "1", "/", "|", "\u14AA", "L", "\u24DB", "\u24C1", "\u2113", "\u2C60", "\u0140", "\u013F", "\u13DD", "\u1E36", "\u13DE", "\u029F", "\uFF4C", "\uFF2C", "\u{1F15B}", "\u{1D425}", "\u{1D40B}", "\u{1D62D}", "\u{1D613}", "\u{1D661}", "\u{1D647}", "\u{1D4C1}", "\u{1D4F5}", "\u{1D4DB}", "\u{1D55D}", "\u{1D541}", "\u{1D529}", "\u200B", "\u{1D591}", "\u{1D577}", "\u{1F13B}", "\u{1F17B}", "\u{1D43F}", "\u0285", "\u{1D695}", "\u{1D67B}", "\u21B3", "\u217C"],
m: [
"m",
"\u15F0",
"M",
"\u24DC",
"\u24C2",
"\u043C",
"\u034F",
"\u20A5",
"\u1E43",
"\u1E42",
"\u13B7",
"\u03FB",
"\u039C",
"\u1E41",
"\u1E40",
"\u028D",
"\u033E",
"\uFF4D",
"\uFF2D",
"\u1D0D",
"\u026F",
"\u{1F15C}",
"\u{1D426}",
"\u{1D40C}",
"\u{1D62E}",
"\u{1D614}",
"\u{1D662}",
"\u{1D648}",
"\u{1D4C2}",
"\u{1D4F6}",
"\u{1D4DC}",
"\u{1D55E}",
"\u{1D542}",
"\u{1D52A}",
"\u{1D50D}",
"\u{1D592}",
"\u{1D578}",
"\u{1F13C}",
"\u{1F17C}",
"\u{1D440}",
"\u0271",
"\u{1D696}",
"\u{1D67C}",
"\u2654",
"\u217F"
],
n: ["n", "\xF1", "\u144E", "N", "\u24DD", "\u24C3", "\u0438", "\u20A6", "\u0144", "\u0143", "\u13C1", "\u03C0", "\u220F", "\u1E46", "\u057C", "\uFF4E", "\uFF2E", "\u0274", "\u{1F15D}", "\u{1D427}", "\u{1D40D}", "\u{1D62F}", "\u{1D615}", "\u{1D663}", "\u{1D649}", "\u{1D4C3}", "\u{1D4F7}", "\u{1D4DD}", "\u{1D55F}", "\u{1D543}", "\u{1D52B}", "\u{1D50E}", "\u{1D593}", "\u{1D579}", "\u{1F13D}", "\u{1F17D}", "\u{1D4A9}", "\u0273", "\u{1D697}", "\u{1D67D}", "\u266B", "\u0578", "\u03B7", "\u{1D67D}", "\u019E", "\u{1D4F7}", "\u039D"],
o: ["o", "0", "\xF3", "\xF4", "\xF5", "\xFA", "O", "\u24DE", "\u24C4", "\u03C3", "\u034F", "\xD8", "\xF6", "\xD6", "\u13A7", "\u0398", "\u1E4F", "\u1E4E", "\u13BE", "\u0585", "\u033E", "\uFF4F", "\uFF2F", "\u1D0F", "\u{1F15E}", "\u{1D428}", "\u{1D40E}", "\u{1D630}", "\u{1D616}", "\u{1D664}", "\u{1D64A}", "\u2134", "\u{1D4F8}", "\u{1D4DE}", "\u{1D560}", "\u{1D544}", "\u{1D52C}", "\u{1D50F}", "\u{1D594}", "\u{1D57A}", "\u{1F13E}", "\u{1F17E}", "\u{1D45C}", "\u{1D4AA}", "\u{1D698}", "\u{1D67E}", "\u2299", "\u03BF"],
p: ["p", "\u146D", "P", "\u24DF", "\u24C5", "\u03C1", "\u20B1", "\u1E57", "\u1E56", "\u13AE", "\u01A4", "\u13E2", "\u0584", "\uFF50", "\uFF30", "\u1D18", "\u{1F15F}", "\u{1D429}", "\u{1D40F}", "\u{1D631}", "\u{1D617}", "\u{1D665}", "\u{1D64B}", "\u{1D4C5}", "\u{1D4F9}", "\u{1D4DF}", "\u{1D561}", "\u2115", "\u{1D52D}", "\u{1D510}", "\u{1D595}", "\u{1D57B}", "\u{1F13F}", "\u{1F17F}", "\u{1D4AB}", "\u{1D699}", "\u{1D67F}", "\u0440"],
q: [
"q",
"\u146B",
"Q",
"\u24E0",
"\u24C6",
"\u034F",
"\u13A4",
"\u03C6",
"\u10B3",
"\u0566",
"\u033E",
"\uFF51",
"\uFF31",
"\u03D9",
"\u01EB",
"\u{1F160}",
"\u{1D42A}",
"\u{1D410}",
"\u{1D632}",
"\u{1D618}",
"\u{1D666}",
"\u{1D64C}",
"\u{1D4C6}",
"\u{1D4FA}",
"\u{1D4E0}",
"\u{1D562}",
"\u200B",
"\u{1D52E}",
"\u{1D511}",
"\u{1D596}",
"\u{1D57C}",
"\u{1F140}",
"\u{1F180}",
"\u{1D4AC}",
"\u{1D69A}",
"\u{1D680}",
"\u262D",
"\u051B"
],
r: ["r", "\u1587", "R", "\u24E1", "\u24C7", "\u044F", "\u2C64", "\u0155", "\u0154", "\u13D2", "\u0433", "\u0393", "\u1E59", "\u1E58", "\u0280", "\uFF52", "\uFF32", "\u0279", "\u{1F161}", "\u{1D42B}", "\u{1D411}", "\u{1D633}", "\u{1D619}", "\u{1D667}", "\u{1D64D}", "\u{1D4C7}", "\u{1D4FB}", "\u{1D4E1}", "\u{1D563}", "\u{1D546}", "\u{1D52F}", "\u{1D512}", "\u{1D597}", "\u{1D57D}", "\u{1F141}", "\u{1F181}", "\u{1D445}", "\u027E", "\u{1D69B}", "\u{1D681}", "\u2608", "r", "\u{1D681}", "\u{1D4FB}"],
s: ["s", "5", "\u1515", "S", "\u24E2", "\u24C8", "\u0455", "\u034F", "\u20B4", "\u1E69", "\u1E68", "\u13D5", "\u0405", "\u1E60", "\u0586", "\u033E", "\uFF53", "\uFF33", "\uA731", "\u{1F162}", "\u{1D42C}", "\u{1D412}", "\u{1D634}", "\u{1D61A}", "\u{1D668}", "\u{1D64E}", "\u{1D4C8}", "\u{1D4FC}", "\u{1D4E2}", "\u{1D564}", "\u2119", "\u{1D530}", "\u{1D513}", "\u{1D598}", "\u{1D57E}", "\u{1F142}", "\u{1F182}", "\u{1D4AE}", "\u0282", "\u{1D69C}", "\u{1D682}", "\u0455", "\u{1D4FC}"],
t: ["t", "+", "T", "\u24E3", "\u24C9", "\u0442", "\u20AE", "\u1E97", "\u1E6E", "\u13D6", "\u03C4", "\u01AC", "\u13C6", "\u0236", "\uFF54", "\uFF34", "\u1D1B", "\u0287", "\u{1F163}", "\u{1D42D}", "\u{1D413}", "\u{1D635}", "\u{1D61B}", "\u{1D669}", "\u{1D64F}", "\u{1D4C9}", "\u{1D4FD}", "\u{1D4E3}", "\u{1D565}", "\u200B", "\u{1D531}", "\u{1D514}", "\u{1D599}", "\u{1D57F}", "\u{1F143}", "\u{1F183}", "\u{1D4AF}", "\u019A", "\u{1D69D}", "\u{1D683}", "\u2602", "t", "\u{1D4FD}"],
u: ["u", "\xFA", "\xFC", "\u144C", "U", "\u24E4", "\u24CA", "\u03C5", "\u034F", "\u0244", "\xDC", "\u13EC", "\u01B1", "\u1E73", "\u1E72", "\u028A", "\u033E", "\uFF55", "\uFF35", "\u1D1C", "\u{1F164}", "\u{1D42E}", "\u{1D414}", "\u{1D636}", "\u{1D61C}", "\u{1D66A}", "\u{1D650}", "\u{1D4CA}", "\u{1D4FE}", "\u{1D4E4}", "\u{1D566}", "\u211A", "\u{1D532}", "\u211C", "\u{1D59A}", "\u{1D580}", "\u{1F144}", "\u{1F184}", "\u{1D4B0}", "\u{1D69E}", "\u{1D684}", "\u260B", "\u057D"],
v: ["v", "\u142F", "V", "\u24E5", "\u24CB", "\u03BD", "\u1E7F", "\u1E7E", "\u13C9", "\u01B2", "\u1E7C", "\u028B", "\uFF56", "\uFF36", "\u1D20", "\u028C", "\u{1F165}", "\u{1D42F}", "\u{1D415}", "\u{1D637}", "\u{1D61D}", "\u{1D66B}", "\u{1D651}", "\u{1D4CB}", "\u{1D4FF}", "\u{1D4E5}", "\u{1D567}", "\u200B", "\u{1D533}", "\u{1D59B}", "\u{1D581}", "\u{1F145}", "\u{1F185}", "\u{1D4B1}", "\u{1D69F}", "\u{1D685}", "\u2713", "\u2174"],
w: ["w", "\u15EF", "W", "\u24E6", "\u24CC", "\u03C9", "\u034F", "\u20A9", "\u1E85", "\u1E84", "\u13C7", "\u0448", "\u0428", "\u1E87", "\u1E86", "\u13B3", "\u0561", "\u033E", "\uFF57", "\uFF37", "\u1D21", "\u028D", "\u{1F166}", "\u{1D430}", "\u{1D416}", "\u{1D638}", "\u{1D61E}", "\u{1D66C}", "\u{1D652}", "\u{1D4CC}", "\u{1D500}", "\u{1D4E6}", "\u{1D568}", "\u211D", "\u{1D534}", "\u{1D516}", "\u{1D59C}", "\u{1D582}", "\u{1F146}", "\u{1F186}", "\u{1D4B2}", "\u026F", "\u{1D6A0}", "\u{1D686}", "\u051D"],
x: ["x", "\u166D", "X", "\u24E7", "\u24CD", "\u03C7", "\u04FE", "\u1E8D", "\u1E8C", "\u1300", "\u03F0", "\u0416", "\u0445", "\u04FC", "\uFF58", "\uFF38", "\u{1F167}", "\u{1D431}", "\u{1D417}", "\u{1D639}", "\u{1D61F}", "\u{1D66D}", "\u{1D653}", "\u{1D4CD}", "\u{1D501}", "\u{1D4E7}", "\u{1D569}", "\u200B", "\u{1D535}", "\u{1D517}", "\u{1D59D}", "\u{1D583}", "\u{1F147}", "\u{1F187}", "\u{1D4B3}", "\u{1D6A1}", "\u{1D687}", "\u2318", "\u0445"],
y: [
"y",
"Y",
"\u24E8",
"\u24CE",
"\u0443",
"\u034F",
"\u024E",
"\xFF",
"\u0178",
"\u13A9",
"\u03C8",
"\u03A8",
"\u1E8F",
"\u1E8E",
"\u13BD",
"\u0447",
"\u028F",
"\u033E",
"\uFF59",
"\uFF39",
"\u028E",
"\u{1F168}",
"\u{1D432}",
"\u{1D418}",
"\u{1D63A}",
"\u{1D620}",
"\u{1D66E}",
"\u{1D654}",
"\u{1D4CE}",
"\u{1D502}",
"\u{1D4E8}",
"\u{1D56A}",
"\u{1D54A}",
"\u{1D536}",
"\u{1D518}",
"\u{1D59E}",
"\u{1D584}",
"\u{1F148}",
"\u{1F188}",
"\u{1D4B4}",
"\u10E7",
"\u{1D6A2}",
"\u{1D688}",
"\u263F",
"\u0443"
],
z: ["z", "\u1614", "Z", "\u24E9", "\u24CF", "\u2C6B", "\u1E93", "\u1E92", "\u135A", "\u13C3", "\u0290", "\uFF5A", "\uFF3A", "\u1D22", "\u{1F169}", "\u{1D433}", "\u{1D419}", "\u{1D63B}", "\u{1D621}", "\u{1D66F}", "\u{1D655}", "\u{1D4CF}", "\u{1D503}", "\u{1D4E9}", "\u{1D56B}", "\u{1D54B}", "\u{1D537}", "\u{1D519}", "\u{1D59F}", "\u{1D585}", "\u{1F149}", "\u{1F189}", "\u{1D4B5}", "\u0225", "\u{1D6A3}", "\u{1D689}", "\u2621", "z", "\u{1D503}"]
};
const filterWords = Chat.filterWords;
const Filters = new class {
constructor() {
this.EVASION_DETECTION_SUBSTITUTIONS = EVASION_DETECTION_SUBSTITUTIONS;
this.EVASION_DETECTION_SUB_STRINGS = {};
for (const letter in EVASION_DETECTION_SUBSTITUTIONS) {
this.EVASION_DETECTION_SUB_STRINGS[letter] = `[${EVASION_DETECTION_SUBSTITUTIONS[letter].join("")}]`;
}
this.load();
}
constructEvasionRegex(str) {
const buf = "\\b" + [...str].map((letter) => (this.EVASION_DETECTION_SUB_STRINGS[letter] || letter) + "+").join("\\.?") + "\\b";
return new RegExp(buf, "iu");
}
generateRegex(word, isEvasion = false, isShortener = false, isReplacement = false) {
try {
if (isEvasion) {
return this.constructEvasionRegex(word);
} else {
return new RegExp(isShortener ? `\\b${word}` : word, isReplacement ? "igu" : "iu");
}
} catch (e) {
throw new Chat.ErrorMessage(
e.message.startsWith("Invalid regular expression: ") ? e.message : `Invalid regular expression: /${word}/: ${e.message}`
);
}
}
stripWordBoundaries(regex) {
return new RegExp(regex.toString().replace("/\\b", "").replace("\\b/iu", ""), "iu");
}
save(force = false) {
(0, import_lib.FS)(MONITOR_FILE).writeUpdate(() => {
const buf = {};
for (const key in Chat.monitors) {
buf[key] = [];
for (const filterWord of filterWords[key]) {
const word = { ...filterWord };
delete word.regex;
buf[key].push(word);
}
}
return JSON.stringify(buf);
}, { throttle: force ? 0 : WRITE_THROTTLE_TIME });
}
add(filterWord) {
if (!filterWord.hits)
filterWord.hits = 0;
const punishment = Chat.monitors[filterWord.list].punishment;
if (!filterWord.regex) {
filterWord.regex = this.generateRegex(
filterWord.word,
punishment === "EVASION",
punishment === "SHORTENER",
!!filterWord.replacement
);
}
if (filterWords[filterWord.list].some((val) => String(val.regex) === String(filterWord.regex))) {
throw new Chat.ErrorMessage(`${filterWord.word} is already added to the ${filterWord.list} list.`);
}
filterWords[filterWord.list].push(filterWord);
this.save(true);
}
load() {
const legacy = (0, import_lib.FS)(LEGACY_MONITOR_FILE);
if (legacy.existsSync()) {
return process.nextTick(() => {
this.loadLegacy();
legacy.renameSync(LEGACY_MONITOR_FILE + ".backup");
Monitor.notice(`Legacy chatfilter data loaded and renamed to a .backup file.`);
});
}
const data = JSON.parse((0, import_lib.FS)(MONITOR_FILE).readIfExistsSync() || "{}");
for (const k in data) {
filterWords[k] = [];
for (const entry of data[k]) {
if (k === "evasion") {
entry.regex = this.constructEvasionRegex(entry.word);
} else {
entry.regex = new RegExp(
k === "shorteners" ? `\\b${entry.word}` : entry.word,
entry.replacement ? "igu" : "iu"
);
}
filterWords[k].push(entry);
}
}
}
loadLegacy() {
let data;
try {
data = (0, import_lib.FS)(LEGACY_MONITOR_FILE).readSync();
} catch (e) {
if (e.code !== "ENOENT")
throw e;
}
if (!data)
return;
const lines = data.split("\n");
loop:
for (const line of lines) {
if (!line || line === "\r")
continue;
const [location, word, punishment, reason, times, ...rest] = line.split(" ").map((param) => param.trim());
if (location === "Location")
continue;
if (!(location && word && punishment))
continue;
for (const key in Chat.monitors) {
if (Chat.monitors[key].location === location && Chat.monitors[key].punishment === punishment) {
const replacement = rest[0];
const publicReason = rest[1];
let regex;
if (punishment === "EVASION") {
regex = Filters.constructEvasionRegex(word);
} else {
regex = new RegExp(punishment === "SHORTENER" ? `\\b${word}` : word, replacement ? "igu" : "iu");
}
const filterWord = { regex, word, hits: parseInt(times) || 0 };
if (reason && reason !== "undefined")
filterWord.reason = reason;
if (publicReason)
filterWord.publicReason = publicReason;
if (replacement)
filterWord.replacement = replacement;
filterWords[key].push(filterWord);
continue loop;
}
}
Monitor.crashlog(new Error("Couldn't find [location, punishment] pair for a filter word"), "The main process", {
location,
word,
punishment,
reason,
times,
rest
});
}
}
}();
Chat.registerMonitor("autolock", {
location: "EVERYWHERE",
punishment: "AUTOLOCK",
label: "Autolock",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, word, reason, publicReason } = line;
const match = regex.exec(lcMessage);
if (match) {
if (isStaff)
return `${message} __[would be locked: ${word}${reason ? ` (${reason})` : ""}]__`;
message = message.replace(/(https?):\/\//g, "$1__:__//");
message = message.replace(/\./g, "__.__");
if (room) {
void Punishments.autolock(
user,
room,
"ChatMonitor",
`Filtered phrase: ${word}`,
`<<${room.roomid}>> ${user.name}: ||\`\`${message}\`\`${reason ? ` __(${reason})__` : ""}||`,
true
);
} else {
this.errorReply(`Please do not say '${match[0]}'${publicReason ? ` ${publicReason}` : ``}.`);
}
return false;
}
}
});
Chat.registerMonitor("publicwarn", {
location: "PUBLIC",
punishment: "WARN",
label: "Filtered in public",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, word, reason, publicReason } = line;
const match = regex.exec(lcMessage);
if (match) {
if (isStaff)
return `${message} __[would be filtered in public: ${word}${reason ? ` (${reason})` : ""}]__`;
this.errorReply(`Please do not say '${match[0]}'${publicReason ? ` ${publicReason}` : ``}.`);
return false;
}
}
});
Chat.registerMonitor("warn", {
location: "EVERYWHERE",
punishment: "WARN",
label: "Filtered",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, word, reason, publicReason } = line;
const match = regex.exec(lcMessage);
if (match) {
if (isStaff)
return `${message} __[would be filtered: ${word}${reason ? ` (${reason})` : ""}]__`;
this.errorReply(`Please do not say '${match[0]}'${publicReason ? ` ${publicReason}` : ``}.`);
return false;
}
}
});
Chat.registerMonitor("evasion", {
location: "EVERYWHERE",
punishment: "EVASION",
label: "Filter Evasion Detection",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, word, reason, publicReason } = line;
let normalizedMessage = lcMessage.normalize("NFKC");
normalizedMessage = normalizedMessage.replace(/[\s-_,.]+/g, ".");
const match = regex.exec(normalizedMessage);
if (match) {
if (match[0] === word && regex.test(message)) {
if (isStaff)
return `${message} __[would be filtered: ${word}${reason ? ` (${reason})` : ""}]__`;
this.errorReply(`Do not say '${word}'.`);
return false;
}
if (isStaff)
return `${message} __[would be locked for filter evading: ${match[0]} (${word})]__`;
message = message.replace(/(https?):\/\//g, "$1__:__//");
if (room) {
void Punishments.autolock(
user,
room,
"FilterEvasionMonitor",
`Evading filter: ${message} (${match[0]} => ${word})`,
`<<${room.roomid}>> ${user.name}: ||\`\`${message}\`\` __(${match[0]} => ${word})__||`,
true
);
} else {
this.errorReply(`Please do not say '${word}'${publicReason ? ` ${publicReason}` : ``}.`);
}
return false;
}
}
});
Chat.registerMonitor("wordfilter", {
location: "EVERYWHERE",
punishment: "FILTERTO",
label: "Filtered to a different phrase",
condition: "notStaff",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, replacement } = line;
let match = regex.exec(message);
while (match) {
let filtered = replacement || "";
if (match[0] === match[0].toUpperCase())
filtered = filtered.toUpperCase();
if (match[0].startsWith(match[0].charAt(0).toUpperCase())) {
filtered = `${filtered ? filtered.charAt(0).toUpperCase() : ""}${filtered.slice(1)}`;
}
message = message.replace(match[0], filtered);
match = regex.exec(message);
}
return message;
}
});
Chat.registerMonitor("namefilter", {
location: "NAMES",
punishment: "WARN",
label: "Filtered in names"
});
Chat.registerMonitor("battlefilter", {
location: "BATTLES",
punishment: "MUTE",
label: "Filtered in battles",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, word, reason, publicReason } = line;
const match = regex.exec(lcMessage);
if (match) {
if (isStaff)
return `${message} __[would be filtered: ${word}${reason ? ` (${reason})` : ""}]__`;
message = message.replace(/(https?):\/\//g, "$1__:__//");
message = message.replace(/\./g, "__.__");
if (room) {
room.mute(user);
this.errorReply(
`You have been muted for using a banned phrase. Please do not say '${match[0]}'${publicReason ? ` ${publicReason}` : ``}.`
);
const text = `[BattleMonitor] <<${room.roomid}>> MUTED: ${user.name}: ${message}${reason ? ` __(${reason})__` : ""}`;
const adminlog = Rooms.get("adminlog");
if (adminlog) {
adminlog.add(`|c|~|${text}`).update();
} else {
Monitor.log(text);
}
void room.uploadReplay(user, this.connection, "forpunishment");
}
return false;
}
}
});
Chat.registerMonitor("shorteners", {
location: "EVERYWHERE",
punishment: "SHORTENER",
label: "URL Shorteners",
condition: "notTrusted",
monitor(line, room, user, message, lcMessage, isStaff) {
const { regex, word, publicReason } = line;
if (regex.test(lcMessage)) {
if (isStaff)
return `${message} __[shortener: ${word}]__`;
this.errorReply(`Please do not use URL shorteners such as '${word}'${publicReason ? ` ${publicReason}` : ``}.`);
return false;
}
}
});
const chatfilter = function(message, user, room) {
let lcMessage = message.replace(/\u039d/g, "N").toLowerCase().replace(/[\u200b\u007F\u00AD\uDB40\uDC00\uDC21]/g, "").replace(/\u03bf/g, "o").replace(/\u043e/g, "o").replace(/\u0430/g, "a").replace(/\u0435/g, "e").replace(/\u039d/g, "e");
lcMessage = lcMessage.replace(/__|\*\*|``|\[\[|\]\]/g, "");
const isStaffRoom = room && (room.persist && room.roomid.endsWith("staff") || room.roomid.startsWith("help-"));
const isStaff = isStaffRoom || user.isStaff || !!(this.pmTarget && this.pmTarget.isStaff);
for (const list in Chat.monitors) {
const { location, condition, monitor } = Chat.monitors[list];
if (!monitor)
continue;
if (location === "BATTLES" && !(room && room.battle && room.battle.challengeType !== "challenge"))
continue;
if (location === "PUBLIC" && room && room.settings.isPrivate === true)
continue;
switch (condition) {
case "notTrusted":
if (user.trusted && !isStaffRoom)
continue;
break;
case "notStaff":
if (isStaffRoom)
continue;
break;
}
for (const line of Chat.filterWords[list]) {
const ret = monitor.call(this, line, room, user, message, lcMessage, isStaff);
if (ret !== void 0 && ret !== message) {
line.hits++;
Filters.save();
}
if (typeof ret === "string") {
message = ret;
} else if (ret === false) {
return false;
}
}
}
return message;
};
const namefilter = (name, user) => {
const id = toID(name);
if (Punishments.namefilterwhitelist.has(id))
return name;
if (Monitor.forceRenames.has(id)) {
if (typeof Monitor.forceRenames.get(id) === "number") {
Monitor.forceRenames.set(id, false);
}
if (!Monitor.forceRenames.get(id)) {
user.trackRename = id;
Monitor.forceRenames.set(id, true);
}
return "";
}
if (id === toID(user.trackRename))
return "";
let lcName = name.replace(/\u039d/g, "N").toLowerCase().replace(/[\u200b\u007F\u00AD]/g, "").replace(/\u03bf/g, "o").replace(/\u043e/g, "o").replace(/\u0430/g, "a").replace(/\u0435/g, "e").replace(/\u039d/g, "e");
lcName = lcName.replace("herapist", "").replace("grape", "").replace("scrape", "");
for (const list in filterWords) {
if (!Chat.monitors[list] || Chat.monitors[list].location === "BATTLES")
continue;
const punishment = Chat.monitors[list].punishment;
for (const line of filterWords[list]) {
const regex = punishment === "EVASION" ? Filters.stripWordBoundaries(line.regex) : line.regex;
if (regex.test(lcName)) {
if (Chat.monitors[list].punishment === "AUTOLOCK") {
void Punishments.autolock(
user,
"staff",
`NameMonitor`,
`inappropriate name: ${name}`,
`using an inappropriate name: ||${name} (from ${user.name})||`,
false,
name
);
}
line.hits++;
Filters.save();
return "";
}
}
}
return name;
};
const loginfilter = (user) => {
if (user.namelocked)
return;
if (user.trackRename) {
const manualForceRename = Monitor.forceRenames.has(toID(user.trackRename));
Rooms.global.notifyRooms(
["staff"],
import_lib.Utils.html`|html|[NameMonitor] Username used: <span class="username">${user.name}</span> ${user.getAccountStatusString()} (${!manualForceRename ? "automatically " : ""}forcerenamed from <span class="username">${user.trackRename}</span>)`
);
user.trackRename = "";
}
const offlineWarn = Punishments.offlineWarns.get(user.id);
if (typeof offlineWarn !== "undefined") {
user.send(`|c|~|/warn You were warned while offline${offlineWarn.length ? `: ${offlineWarn}` : "."}`);
Punishments.offlineWarns.delete(user.id);
}
};
const nicknamefilter = (name, user) => {
let lcName = name.replace(/\u039d/g, "N").toLowerCase().replace(/[\u200b\u007F\u00AD]/g, "").replace(/\u03bf/g, "o").replace(/\u043e/g, "o").replace(/\u0430/g, "a").replace(/\u0435/g, "e").replace(/\u039d/g, "e");
lcName = lcName.replace("herapist", "").replace("grape", "").replace("scrape", "");
for (const list in filterWords) {
if (!Chat.monitors[list])
continue;
if (Chat.monitors[list].location === "BATTLES")
continue;
for (const line of filterWords[list]) {
let { regex, word } = line;
if (Chat.monitors[list].punishment === "EVASION") {
regex = Filters.stripWordBoundaries(regex);
}
const match = regex.exec(lcName);
if (match) {
if (Chat.monitors[list].punishment === "AUTOLOCK") {
void Punishments.autolock(
user,
"staff",
`NameMonitor`,
`inappropriate Pok\xE9mon nickname: ${name}`,
`${user.name} - using an inappropriate Pok\xE9mon nickname: ||${name}||`,
true
);
} else if (Chat.monitors[list].punishment === "EVASION" && match[0] !== lcName) {
void Punishments.autolock(
user,
"staff",
"FilterEvasionMonitor",
`Evading filter in Pok\xE9mon nickname (${name} => ${word})`,
`${user.name}: Pok\xE9mon nicknamed ||\`\`${name} => ${word}\`\`||`,
true
);
}
line.hits++;
Filters.save();
return "";
}
}
}
return name;
};
const statusfilter = (status, user) => {
let lcStatus = status.replace(/\u039d/g, "N").toLowerCase().replace(/[\u200b\u007F\u00AD]/g, "").replace(/\u03bf/g, "o").replace(/\u043e/g, "o").replace(/\u0430/g, "a").replace(/\u0435/g, "e").replace(/\u039d/g, "e");
lcStatus = lcStatus.replace("herapist", "").replace("grape", "").replace("scrape", "");
const impersonationRegex = /\b(?:global|room|upper|senior)?\s*(?:staff|admin|administrator|leader|owner|founder|mod|moderator|driver|voice|operator|sysop|creator)\b/gi;
if (!user.can("lock") && impersonationRegex.test(lcStatus))
return "";
for (const list in filterWords) {
if (!Chat.monitors[list])
continue;
const punishment = Chat.monitors[list].punishment;
for (const line of filterWords[list]) {
const regex = punishment === "EVASION" ? Filters.stripWordBoundaries(line.regex) : line.regex;
if (regex.test(lcStatus)) {
if (punishment === "AUTOLOCK") {
void Punishments.autolock(
user,
"staff",
`NameMonitor`,
`inappropriate status message: ${status}`,
`${user.name} - using an inappropriate status: ||${status}||`,
true
);
}
line.hits++;
Filters.save();
return "";
}
}
}
return status;
};
const pages = {
filters(query, user, connection) {
if (!user.named)
return Rooms.RETRY_AFTER_LOGIN;
this.title = "Filters";
let buf = `<div class="pad ladder"><h2>Filters</h2>`;
if (!user.can("addhtml"))
this.checkCan("lock");
let content = ``;
for (const key in Chat.monitors) {
content += `<tr><th colspan="2"><h3>${Chat.monitors[key].label} <span style="font-size:8pt;">[${key}]</span></h3></tr></th>`;
if (filterWords[key].length) {
content += filterWords[key].map(({ regex, word, reason, publicReason, replacement, hits }) => {
let entry = import_lib.Utils.html`<abbr title="${reason}"><code>${word}</code></abbr>`;
if (publicReason)
entry += import_lib.Utils.html` <small>(public reason: ${publicReason})</small>`;
if (replacement)
entry += import_lib.Utils.html` &rArr; ${replacement}`;
return `<tr><td>${entry}</td><td>${hits}</td></tr>`;
}).join("");
}
}
if (Punishments.namefilterwhitelist.size) {
content += `<tr><th colspan="2"><h3>Whitelisted names</h3></tr></th>`;
for (const [val] of Punishments.namefilterwhitelist) {
content += `<tr><td>${val}</td></tr>`;
}
}
if (!content) {
buf += `<p>There are no filtered words.</p>`;
} else {
buf += `<table>${content}</table>`;
}
buf += `</div>`;
return buf;
}
};
const commands = {
filters: "filter",
filter: {
add(target, room, user) {
this.checkCan("rangeban");
let separator = ",";
if (target.includes("\n")) {
separator = "\n";
} else if (target.includes("/")) {
separator = "/";
}
let [list, ...rest] = target.split(separator);
list = toID(list);
if (!list || !rest.length) {
return this.errorReply(`Syntax: /filter add list ${separator} word ${separator} reason [${separator} optional public reason]`);
}
if (!(list in filterWords)) {
return this.errorReply(`Invalid list: ${list}. Possible options: ${Object.keys(filterWords).join(", ")}`);
}
const filterWord = { list, word: "" };
rest = rest.map((part) => part.trim());
if (Chat.monitors[list].punishment === "FILTERTO") {
[filterWord.word, filterWord.replacement, filterWord.reason, filterWord.publicReason] = rest;
if (!filterWord.replacement) {
return this.errorReply(
`Syntax for word filters: /filter add ${list} ${separator} regex ${separator} reason [${separator} optional public reason]`
);
}
} else {
[filterWord.word, filterWord.reason, filterWord.publicReason] = rest;
}
filterWord.word = filterWord.word.trim();
if (!filterWord.word) {
return this.errorReply(`Invalid word: '${filterWord.word}'.`);
}
Filters.add(filterWord);
const reason = filterWord.reason ? ` (${filterWord.reason})` : "";
if (Chat.monitors[list].punishment === "FILTERTO") {
this.globalModlog(`ADDFILTER`, null, `'${String(filterWord.regex)} => ${filterWord.replacement}' to ${list} list${reason}`);
} else {
this.globalModlog(`ADDFILTER`, null, `'${filterWord.word}' to ${list} list${reason}`);
}
const output = `'${filterWord.word}' was added to the ${list} list.`;
Rooms.get("upperstaff")?.add(output).update();
if (room?.roomid !== "upperstaff")
this.sendReply(output);
},
remove(target, room, user) {
this.checkCan("rangeban");
let [list, ...words] = target.split(target.includes("\n") ? "\n" : ",").map((param) => param.trim());
list = toID(list);
if (!list || !words.length)
return this.errorReply("Syntax: /filter remove list, words");
if (!(list in filterWords)) {
return this.errorReply(`Invalid list: ${list}. Possible options: ${Object.keys(filterWords).join(", ")}`);
}
const notFound = words.filter((val) => !filterWords[list].filter((entry) => entry.word === val).length);
if (notFound.length) {
return this.errorReply(`${notFound.join(", ")} ${Chat.plural(notFound, "are", "is")} not on the ${list} list.`);
}
filterWords[list] = filterWords[list].filter((entry) => !words.includes(entry.word));
this.globalModlog(`REMOVEFILTER`, null, `'${words.join(", ")}' from ${list} list`);
Filters.save(true);
const output = `'${words.join(", ")}' ${Chat.plural(words, "were", "was")} removed from the ${list} list.`;
Rooms.get("upperstaff")?.add(output).update();
if (room?.roomid !== "upperstaff")
this.sendReply(output);
},
"": "view",
list: "view",
view(target, room, user) {
this.parse(`/join view-filters`);
},
help(target, room, user) {
this.parse(`/help filter`);
},
test(target, room, user) {
this.checkCan("lock");
if (room && ["staff", "upperstaff"].includes(room.roomid)) {
this.runBroadcast(true, `!filter test ${target}`);
}
const lcMessage = Chat.stripFormatting(target.replace(/\u039d/g, "N").toLowerCase().replace(/[\u200b\u007F\u00AD\uDB40\uDC00\uDC21]/g, "").replace(/\u03bf/g, "o").replace(/\u043e/g, "o").replace(/\u0430/g, "a").replace(/\u0435/g, "e").replace(/\u039d/g, "e"));
const buf = [];
for (const monitorName in Chat.monitors) {
const monitor = Chat.monitors[monitorName];
for (const line of Chat.filterWords[monitorName]) {
let ret;
if (monitor.monitor) {
ret = monitor.monitor.call(this, line, room, user, target, lcMessage, true);
} else {
ret = line.regex.exec(target)?.[0];
}
if (typeof ret === "string") {
buf.push(`${monitorName}: ${ret}`);
break;
} else if (ret === false) {
buf.push(`${monitorName}: "${target}" would be blocked from being sent.`);
break;
}
}
}
if (buf.length) {
return this.sendReplyBox(Chat.formatText(buf.join("\n"), false, true));
} else {
throw new Chat.ErrorMessage(
`"${target}" doesn't trigger any filters. Check spelling?`
);
}
},
testhelp: [
`/filter test [test string] - Tests whether or not the provided test string would trigger any of the chat monitors.`,
`Requires: % @ ~`
]
},
filterhelp: [
`/filter add list, word, reason[, optional public reason] - Adds a word to the given filter list. Requires: ~`,
`/filter remove list, words - Removes words from the given filter list. Requires: ~`,
`/filter view - Opens the list of filtered words. Requires: % @ ~`,
`/filter test [test string] - Tests whether or not the provided test string would trigger any of the chat monitors. Requires: % @ ~`,
`You may use / instead of , in /filter add if you want to specify a reason that includes commas.`
],
allowname(target, room, user) {
this.checkCan("forcerename");
target = toID(target);
if (!target)
return this.errorReply(`Syntax: /allowname username`);
if (Punishments.namefilterwhitelist.has(target)) {
return this.errorReply(`${target} is already allowed as a username.`);
}
const msg = `${target} was allowed as a username by ${user.name}.`;
const toNotify = ["staff", "upperstaff"];
Rooms.global.notifyRooms(toNotify, `|c|${user.getIdentity()}|/log ${msg}`);
if (!room || !toNotify.includes(room.roomid)) {
this.sendReply(msg);
}
this.globalModlog(`ALLOWNAME`, target);
Monitor.forceRenames.delete(target);
}
};
process.nextTick(() => {
Chat.multiLinePattern.register("/filter (add|remove) ");
});
//# sourceMappingURL=chat-monitor.js.map