using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using HarmonyLib;
using Steamworks;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ClientChatFilter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ClientChatFilter")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("15a11270-54ce-4852-b435-2dc748a93ba6")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("wilson.textchatfilter", "Text Chat Filter", "1.0.0")]
public class LobbyChatPatchPlugin : BaseUnityPlugin
{
private void Awake()
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
string name = ((BaseUnityPlugin)this).Info.Metadata.Name;
Debug.Log((object)("[" + name + "] loaded successfully."));
new Harmony("wilson.textchatfilter").PatchAll();
}
}
[HarmonyPatch(typeof(MainMenuManager), "OnLobbyChatMsg")]
internal class Patch_OnLobbyChatMsg
{
private static bool Prefix(MainMenuManager __instance, LobbyChatMsg_t callback)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
byte[] array = new byte[4096];
CSteamID val = default(CSteamID);
EChatEntryType val2 = default(EChatEntryType);
int lobbyChatEntry = SteamMatchmaking.GetLobbyChatEntry((CSteamID)callback.m_ulSteamIDLobby, (int)callback.m_iChatID, ref val, array, array.Length, ref val2);
string @string = Encoding.UTF8.GetString(array, 0, lobbyChatEntry);
string friendPersonaName = SteamFriends.GetFriendPersonaName(val);
string text = ChatFilter.ReplaceBannedWords(@string);
Debug.Log((object)("[" + friendPersonaName + "] says: " + text));
GameObject val3 = Object.Instantiate<GameObject>(__instance.ChatMessagePrefab);
((TMP_Text)val3.GetComponent<SetChatMessage>().msgtext).text = "[" + friendPersonaName + "]: " + text;
val3.transform.SetParent(__instance.ChatListContent.transform);
val3.transform.localScale = Vector3.one;
FieldInfo field = typeof(MainMenuManager).GetField("ChatMessages", BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
{
(field.GetValue(__instance) as List<GameObject>)?.Add(val3);
}
Canvas.ForceUpdateCanvases();
__instance.scrollRect.verticalNormalizedPosition = 0f;
return false;
}
}
public static class ChatFilter
{
public static class BannedWordsList
{
public static readonly HashSet<string> Words = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Ass", "A-rab", "ABC", "ABCD", "Abbie", "Abeed", "Abo", "Africoon", "Africoon-Americoon", "Africoonia",
"Afro engineering", "Ah Chah", "Ali Baba", "Alligator bait", "AmeriKKKunt", "Americoon", "Americunt", "Ang mo", "Ape", "Arabush",
"Aravush", "Armo", "Aseng", "Ashke-Nazi", "Assburgers", "Aunt-Jemima", "Ayrab", "Bakra", "Balija", "Beaner",
"Beaney", "Bluegum", "Boche", "Bog-Irish", "Bogtrotter", "Bohunk", "Boonga", "Bootlip", "Bougnoule", "Bounty-bar",
"Bozgor", "Brit", "Buckra", "Buddhahead", "Bule", "Burr-head", "Burrhead", "Cabbage-Eater", "Camel-Jockey", "Carcamano",
"Chankoro", "Charlie", "Chernozhopy", "Chinaman", "Chink", "Choc-ice", "Chug", "Ciapaty-or-ciapak", "Cina", "Cokin",
"Coon-ass", "Dego", "Dhoti", "Dothead", "Eskimo", "Fritzie", "Golliwog", "Goyim", "Greaseball", "Greaser",
"Gringo", "Gyopo", "Gypsy", "Half-breed", "Hunky", "Hymie", "Iceberg Fuckers", "Indognesial", "Indonesial", "Jigaboo",
"Laowai", "Latinx", "Leb", "Lebo", "Mangal", "Moolinyan", "Moon-Cricket", "Moskal", "Mountain-Turk", "Munt",
"Mzungu", "Norte", "Oreo", "Oriental", "Oven-Dodger", "Paleface", "Peckerwood", "Petrol-sniffer", "Piefke", "Polentone",
"Polock", "Porch-monkey", "Pshek", "Redskin", "Sheboon", "Spearchucker", "Spic", "Swamp-Guinea", "Tacohead", "Timber-nigger",
"Towel-head", "Turco-Albanian", "Twinkie", "Uncle-Tom", "Vatnik", "Wetback", "White-trash", "Wigger", "Wuhan-virus", "Yuon",
"Zhyd", "aboriginal", "addict", "addicted", "addicts", "af", "alligatorbait", "anal", "analannie", "analsex",
"anchor-baby", "antifaggot", "antinigger", "aperest", "apesault", "arab", "arabs", "areola", "arse", "arsehole",
"asian", "assbagger", "assblaster", "assclown", "asscowboy", "asses", "assfuck", "assfucker", "asshole", "assholes",
"asshore", "assjockey", "asskisser", "assklown", "asslick", "asslicker", "asslover", "assman", "assmonkey", "assmunch",
"assmuncher", "asspacker", "asspirate", "asspuppies", "assranger", "asswhore", "asswipe", "backdoorman", "badfuck", "ballgag",
"balllicker", "ballsack", "banging", "barelylegal", "bastard", "bazongas", "bazooms", "beaner", "beatoff", "beatyourmeat",
"beaver", "beef curtains", "bellend", "bestial", "bigass", "bigbastard", "bigbutt", "bitch", "bitches", "bitchez",
"bitchslap", "black", "blackman", "blacks", "blumpkin", "bogan", "bollock", "bombing", "bondage", "boob",
"booby", "boong", "boonga", "bootycall", "bountybar", "brea5t", "breastman", "buggery", "bullcrap", "bulldike",
"bulldyke", "bullshit", "bumblefuck", "bumfuck", "bung", "bunghole", "butchbabes", "butchdike", "butchdyke", "butt-bang",
"butt-fuck", "butt-fucker", "butt-fuckers", "buttbang", "buttface", "buttfuck", "buttfucker", "buttfuckers", "buttman", "buttmunch",
"buttpirate", "cakewalk", "cameljockey", "cancer", "carpet-muncher", "carpetmuncher", "carruth", "catfucker", "cawk", "chav",
"cheesedick", "chinamen", "choad", "chode", "cisgender", "clamdigger", "clamdiver", "clit", "clunge", "cock",
"cockcowboy", "cockknob", "cocklover", "cockrider", "cocksmith", "cocksmoker", "cocksucer", "cocksucker", "cocksucking", "cocktease",
"coloured", "cooly", "coon", "cooter", "cornhole", "crackwhore", "crotchmonkey", "crotchrot", "cum", "cumbubble",
"cumfest", "cummer", "cumming", "cumquat", "cumqueen", "cumshot", "cumskin", "cumsock", "cunilingus", "cunnilingus",
"cunt", "cunteyed", "cuntfucker", "cuntlicker", "datnigga", "deepthroat", "defecate", "demon", "destroy", "deth",
"devilworshipper", "dick", "dickbrain", "dickforbrains", "dickless", "dicklick", "dicklicker", "diddle", "dike", "dixiedyke",
"doggiestyle", "doggystyle", "dong", "doo-doo", "doodoo", "doofus", "dope", "douche", "douchebag", "dripdick",
"dullard", "dwarf", "effing", "ejaculation", "erection", "escort", "faeces", "fag", "fagot", "fairies",
"fairy", "fannyfucker", "farting", "fastfuck", "fat", "fatass", "fatfuck", "fatfucker", "fatso", "fckcum",
"feces", "felatio", "femsplaining", "fisting", "fml", "frogess", "ftw", "fuck", "fuckity-bye", "fucktardedly",
"fucktardedness", "full-blood", "fuuck", "fuzzy-headed", "gangsta", "goddam", "goddamned", "goldenshower", "gonzagas", "groe",
"handicapped", "handjob", "hobo", "hoes", "homobangers", "homosexual", "hooters", "hore", "horney", "husky",
"hussy", "idiot", "illegal", "incest", "itch", "jackoff", "jew", "jigg", "jigga", "jiggabo",
"jigger", "jiggy", "jihad", "jijjiboo", "jism", "jiz", "jizjuice", "jizm", "jizzim", "jizzum",
"knockers", "kummer", "kumming", "kumquat", "kunilingus", "kunt", "ky", "kyke", "laid", "looney",
"minge", "minger", "molester", "molestor", "moneyshot", "mooncricket", "moron", "mothafucka", "motherfucker", "motherfucking",
"motherfuckings", "muff", "muffdive", "munter", "nigger", "niggerhole", "niggers", "niggles", "niggling", "nigglings",
"nizzle", "nlgger", "nlggor", "niggar", "nigr", "nigar", "nirgar", "nofuckingway", "nookey", "nookie",
"nooner", "orgasim", "orgasm", "orgies", "orgy", "osama", "pansy", "pecker", "pee-pee", "peehole",
"peepshow", "penis", "period", "perv", "phonesex", "phuk", "phuked", "phuking", "phukking", "phungky",
"phuq", "pi55", "picaninny", "piccaninny", "poon", "poontang", "pooper", "pooping", "poorwhitetrash", "porchmonkey",
"porn", "pornflick", "pornking", "porno", "prick", "prickhead", "pros", "prostitute", "pu55i", "pu55y",
"pubiclice", "pud", "punani", "queer", "retard", "retarded", "sexslave", "sextogo", "sexwhore", "shag",
"sheepfucker", "shinola", "shit", "shit for brains", "shitcan", "shitdick", "shiteater", "shitface", "shitfit", "shitforbrains",
"shitfuck", "shitfull", "shithapens", "shithappens", "shithead", "shitnigger", "shits", "slanteye", "slav", "slavedriver",
"sleezeball", "slimeball", "slut", "sluts", "slutt", "sniggers", "snowback", "snownigger", "spaghettinigger", "spankthemonkey",
"spermbag", "spermhearder", "spermherder", "spick", "spitter", "stiffy", "strapon", "stupidfuck", "stupidfucker", "suckdick",
"suckme", "suckmyass", "suckmydick", "suckmytit", "suckoff", "swastika", "tatas", "titbitnipply", "titfuck", "titfucker",
"trannie", "tranny", "trans-testicle", "transgendered", "trisexual", "troid", "trots", "turd", "twat", "twink",
"twobitwhore", "unfuckable", "welfare queen", "whiskeydick", "whitelist", "whitetrash", "whitewashing", "whop", "willy", "wokeness",
"wtf", "yambag", "zhid", "zhidovka", "zhydovka", "zigabo", "fuk", "fukc", "fuc"
};
}
private class TrieNode
{
public Dictionary<char, TrieNode> Children = new Dictionary<char, TrieNode>();
public bool IsEnd;
}
private static readonly TrieNode root;
static ChatFilter()
{
root = new TrieNode();
foreach (string word in BannedWordsList.Words)
{
AddBannedWord(word);
}
}
private static bool IsIsolatedWord(string input, int start, int end)
{
bool num = start == 0 || !char.IsLetter(input[start - 1]);
bool flag = end + 1 >= input.Length || !char.IsLetter(input[end + 1]);
return num && flag;
}
public static void AddBannedWord(string word)
{
TrieNode trieNode = root;
string text = NormalizeForFilter(word);
foreach (char key in text)
{
if (!trieNode.Children.ContainsKey(key))
{
trieNode.Children[key] = new TrieNode();
}
trieNode = trieNode.Children[key];
}
trieNode.IsEnd = true;
}
public static string ReplaceBannedWords(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
StringBuilder stringBuilder = new StringBuilder();
int num = 0;
while (num < input.Length)
{
if (!char.IsLetterOrDigit(input[num]))
{
stringBuilder.Append(input[num]);
num++;
continue;
}
int num2 = MatchObfuscated(input, num);
if (num2 > 0)
{
if (stringBuilder.Length > 0 && !char.IsWhiteSpace(stringBuilder[stringBuilder.Length - 1]))
{
stringBuilder.Append(' ');
}
stringBuilder.Append("bad");
int num3 = num + num2;
if (num3 < input.Length && !char.IsWhiteSpace(input[num3]))
{
stringBuilder.Append(' ');
}
num += num2;
}
else
{
stringBuilder.Append(input[num]);
num++;
}
}
return stringBuilder.ToString();
}
private static int MatchObfuscated(string input, int start)
{
StringBuilder stringBuilder = new StringBuilder();
int num = -1;
int num2 = 0;
for (int i = start; i < input.Length; i++)
{
if (num2 >= 40)
{
break;
}
char c = input[i];
if (!char.IsLetterOrDigit(c))
{
continue;
}
char c2 = NormalizeChar(c);
if (stringBuilder.Length == 0 || c2 != stringBuilder[stringBuilder.Length - 1])
{
stringBuilder.Append(c2);
}
string text = stringBuilder.ToString();
if (SearchTrie(text))
{
bool num3 = text.Length <= 3;
bool flag = start == 0 || !char.IsLetter(input[start - 1]);
bool flag2 = i + 1 >= input.Length || !char.IsLetter(input[i + 1]);
if (!num3 || (flag && flag2))
{
num = i;
}
}
num2++;
}
if (num < start)
{
return 0;
}
return num - start + 1;
}
private static char NormalizeChar(char c)
{
c = char.ToLowerInvariant(c);
switch (c)
{
case '!':
case '1':
case '|':
return 'i';
case '3':
return 'e';
case '4':
case '@':
return 'a';
case '$':
case '5':
return 's';
case '6':
return 'g';
case '7':
return 't';
case '8':
return 'b';
case '0':
return 'o';
case '9':
return 'g';
default:
return c;
}
}
private static bool SearchTrie(string word)
{
TrieNode trieNode = root;
foreach (char key in word)
{
if (!trieNode.Children.ContainsKey(key))
{
return false;
}
trieNode = trieNode.Children[key];
}
return trieNode.IsEnd;
}
private static string NormalizeForFilter(string input)
{
input = input.ToLowerInvariant().Replace("1", "i").Replace("!", "i")
.Replace("|", "i")
.Replace("3", "e")
.Replace("4", "a")
.Replace("@", "a")
.Replace("5", "s")
.Replace("6", "g")
.Replace("$", "s")
.Replace("7", "t")
.Replace("8", "b")
.Replace("0", "o")
.Replace("9", "g");
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
char c = input[i];
if (char.IsLetter(c) && (i == 0 || c != input[i - 1]))
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString();
}
}