using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Mirror;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using UniversalChatFilter.Commands;
using UniversalChatFilter.Core;
using UniversalChatFilter.Filters;
using UniversalChatFilter.Integration;
using UniversalChatFilter.Patches;
using UniversalChatFilter.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("atl_chatFilter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("atl_chatFilter")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("1228286a-1b5e-4d8a-b8f2-1323f308e5a4")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace UniversalChatFilter
{
public class FilterResult
{
public bool IsFiltered { get; set; }
public string OriginalMessage { get; set; }
public string DisplayMessage { get; set; }
public string MatchedWord { get; set; }
}
public static class FilterManager
{
private const int MAX_CACHED_MESSAGES = 100;
private static readonly Dictionary<int, string> _originalMessages = new Dictionary<int, string>();
private static readonly Queue<int> _cacheOrder = new Queue<int>();
private static int _messageIdCounter = 0;
private static ContainsFilter _containsFilter;
private static ExactMatchFilter _exactMatchFilter;
private static RegexFilter _regexFilter;
public static void Initialize()
{
_containsFilter = new ContainsFilter();
_exactMatchFilter = new ExactMatchFilter();
_regexFilter = new RegexFilter();
Plugin.Log.LogInfo((object)"FilterManager initialized");
}
public static FilterResult ProcessMessage(string message)
{
FilterResult filterResult = new FilterResult
{
OriginalMessage = message,
DisplayMessage = message,
IsFiltered = false
};
if (!FilterConfig.EnableFilter.Value)
{
return filterResult;
}
string[] personalWordArray = FilterConfig.GetPersonalWordArray();
if (personalWordArray.Length == 0)
{
return filterResult;
}
try
{
bool value = FilterConfig.CaseSensitive.Value;
IWordFilter currentFilter = GetCurrentFilter();
bool flag = FilterConfig.FilterListMode.Value == FilterMode.Whitelist;
string[] exceptionWordArray = FilterConfig.GetExceptionWordArray();
bool flag2 = false;
string text = null;
string[] array = personalWordArray;
foreach (string text2 in array)
{
if (currentFilter.IsMatch(message, text2, value) && !IsExceptionMatch(message, text2, exceptionWordArray, value))
{
flag2 = true;
text = text2;
break;
}
}
if (flag ? (!flag2) : flag2)
{
filterResult.IsFiltered = true;
filterResult.MatchedWord = text ?? (flag ? "[no whitelist match]" : "");
filterResult.DisplayMessage = FilterConfig.BlockedDisplayText.Value;
CacheOriginalMessage(message);
string text3 = (flag ? "whitelist" : "blacklist");
Plugin.Log.LogDebug((object)("Message filtered (" + text3 + ", matched: '" + filterResult.MatchedWord + "')"));
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Filter bypassed due to error: " + ex.Message));
filterResult.IsFiltered = false;
filterResult.DisplayMessage = message;
}
return filterResult;
}
public static bool CheckMessageContainsFilterWord(string message, out string matchedWord)
{
matchedWord = null;
if (string.IsNullOrEmpty(message))
{
return false;
}
string[] personalWordArray = FilterConfig.GetPersonalWordArray();
bool value = FilterConfig.CaseSensitive.Value;
IWordFilter currentFilter = GetCurrentFilter();
string[] array = personalWordArray;
foreach (string text in array)
{
if (currentFilter.IsMatch(message, text, value))
{
matchedWord = text;
return true;
}
}
return false;
}
private static bool IsExceptionMatch(string message, string filterWord, string[] exceptionWords, bool caseSensitive)
{
if (exceptionWords == null || exceptionWords.Length == 0)
{
return false;
}
StringComparison comparisonType = (caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
foreach (string text in exceptionWords)
{
if (text.IndexOf(filterWord, comparisonType) >= 0 && message.IndexOf(text, comparisonType) >= 0)
{
return true;
}
}
return false;
}
private static IWordFilter GetCurrentFilter()
{
return FilterConfig.MatchMode.Value switch
{
FilterMatchMode.ExactWord => _exactMatchFilter,
FilterMatchMode.Regex => _regexFilter,
_ => _containsFilter,
};
}
private static int CacheOriginalMessage(string message)
{
if (_originalMessages.Count >= 100)
{
int key = _cacheOrder.Dequeue();
_originalMessages.Remove(key);
}
int num = _messageIdCounter++;
_originalMessages[num] = message;
_cacheOrder.Enqueue(num);
return num;
}
public static bool TryGetOriginalMessage(int messageId, out string originalMessage)
{
return _originalMessages.TryGetValue(messageId, out originalMessage);
}
public static void ClearCache()
{
_originalMessages.Clear();
_cacheOrder.Clear();
}
}
[BepInPlugin("com.eleen.atlyss.universalchatfilter", "Universal Chat Filter", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
private const string OLD_PLUGIN_GUID = "com.atlyss.chatfilter";
private Harmony _harmony;
private ChatBehaviour _lastChatBehaviour = null;
private FilterSettingsWindow _settingsWindow;
public static Plugin Instance { get; private set; }
public static ManualLogSource Log { get; private set; }
private void Awake()
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
MigrateOldConfig();
FilterConfig.Initialize(((BaseUnityPlugin)this).Config);
FilterManager.Initialize();
_harmony = new Harmony("com.eleen.atlyss.universalchatfilter");
_harmony.PatchAll();
EasySettingsIntegration.Initialize();
_settingsWindow = ((Component)this).gameObject.AddComponent<FilterSettingsWindow>();
Log.LogInfo((object)"Universal Chat Filter v1.0.0 loaded! Press F11 for settings.");
}
private void MigrateOldConfig()
{
try
{
string configPath = Paths.ConfigPath;
string text = Path.Combine(configPath, "com.atlyss.chatfilter.cfg");
string text2 = Path.Combine(configPath, "com.eleen.atlyss.universalchatfilter.cfg");
if (!File.Exists(text2) && File.Exists(text))
{
File.Copy(text, text2);
Log.LogInfo((object)"Migrated config from 'com.atlyss.chatfilter.cfg' to 'com.eleen.atlyss.universalchatfilter.cfg'");
}
}
catch (Exception ex)
{
Log.LogWarning((object)("Config migration failed: " + ex.Message));
}
}
private void Update()
{
ChatBehaviour current = ChatBehaviour._current;
if ((Object)(object)current != (Object)null && (Object)(object)current != (Object)(object)_lastChatBehaviour)
{
Log.LogInfo((object)"[Plugin] ChatBehaviour changed, attaching click handler...");
FilterClickHandler.AttachToChat();
_lastChatBehaviour = current;
}
else if ((Object)(object)current == (Object)null && (Object)(object)_lastChatBehaviour != (Object)null)
{
_lastChatBehaviour = null;
FilterClickHandler.ResetAttachState();
}
}
private void OnDestroy()
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "com.eleen.atlyss.universalchatfilter";
public const string PLUGIN_NAME = "Universal Chat Filter";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace UniversalChatFilter.UI
{
public class FilterClickHandler : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
{
private TMP_Text _textComponent;
private Camera _camera;
private Canvas _canvas;
private static bool _isAttached;
private void Awake()
{
}
private void Start()
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Invalid comparison between Unknown and I4
_textComponent = ((Component)this).GetComponent<TMP_Text>();
_canvas = ((Component)this).GetComponentInParent<Canvas>();
if ((Object)(object)_canvas != (Object)null)
{
if ((int)_canvas.renderMode == 0)
{
_camera = null;
}
else
{
_camera = _canvas.worldCamera ?? Camera.main;
}
}
else
{
_camera = Camera.main;
}
}
public static void AttachToChat()
{
if (_isAttached)
{
return;
}
try
{
if ((Object)(object)ChatBehaviour._current == (Object)null || (Object)(object)ChatBehaviour._current._chatAssets == (Object)null)
{
return;
}
TextMeshProUGUI chatText = ChatBehaviour._current._chatAssets._chatText;
if ((Object)(object)chatText == (Object)null)
{
return;
}
if ((Object)(object)((Component)chatText).GetComponent<FilterClickHandler>() != (Object)null)
{
_isAttached = true;
return;
}
FilterClickHandler filterClickHandler = ((Component)chatText).gameObject.AddComponent<FilterClickHandler>();
((Graphic)chatText).raycastTarget = true;
Canvas componentInParent = ((Component)chatText).GetComponentInParent<Canvas>();
if ((Object)(object)componentInParent != (Object)null)
{
GraphicRaycaster component = ((Component)componentInParent).GetComponent<GraphicRaycaster>();
if ((Object)(object)component == (Object)null)
{
((Component)componentInParent).gameObject.AddComponent<GraphicRaycaster>();
}
}
_isAttached = true;
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogError((object)("[FilterClickHandler] Failed to attach: " + ex.Message));
}
}
}
public static void ResetAttachState()
{
_isAttached = false;
}
public void OnPointerClick(PointerEventData eventData)
{
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_textComponent == (Object)null)
{
_textComponent = ((Component)this).GetComponent<TMP_Text>();
if ((Object)(object)_textComponent == (Object)null)
{
return;
}
}
int num = TMP_TextUtilities.FindIntersectingLink(_textComponent, Vector2.op_Implicit(eventData.position), _camera);
if (num != -1)
{
TMP_LinkInfo linkInfo = _textComponent.textInfo.linkInfo[num];
string linkID = ((TMP_LinkInfo)(ref linkInfo)).GetLinkID();
if (linkID.StartsWith("filter_") && int.TryParse(linkID.Substring(7), out var result))
{
ToggleFilteredMessage(result, linkInfo);
}
}
}
private void ToggleFilteredMessage(int messageId, TMP_LinkInfo linkInfo)
{
try
{
if (ChatDisplayPatch.TryToggleMessage(messageId, out var newDisplayText, out var _))
{
ReplaceFilteredLink(messageId, newDisplayText);
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("[FilterClickHandler] Toggle failed: " + ex.Message));
}
}
}
private void ReplaceFilteredLink(int messageId, string newText)
{
try
{
if ((Object)(object)_textComponent == (Object)null)
{
return;
}
string text = _textComponent.text;
string value = $"<link=\"filter_{messageId}\">";
int num = text.IndexOf(value);
if (num != -1)
{
int num2 = text.IndexOf("</link>", num);
if (num2 != -1)
{
num2 += "</link>".Length;
string text2 = text.Substring(0, num);
string text3 = text.Substring(num2);
_textComponent.text = text2 + newText + text3;
_textComponent.ForceMeshUpdate(false, false);
}
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("[FilterClickHandler] Replace failed: " + ex.Message));
}
}
}
}
public class FilterSettingsWindow : MonoBehaviour
{
private static FilterSettingsWindow _instance;
private bool _isVisible = false;
private Rect _windowRect = new Rect(100f, 100f, 450f, 650f);
private Vector2 _scrollPosition = Vector2.zero;
private string _personalWords = "";
private string _exceptionWords = "";
private string _serverWords = "";
private string _serverExceptionWords = "";
private string _blockedDisplayText = "";
private bool _isWaitingForKey = false;
private KeyCode _currentToggleKey = (KeyCode)292;
public static FilterSettingsWindow Instance => _instance;
private void Awake()
{
_instance = this;
LoadCurrentValues();
ParseToggleKey();
}
private void ParseToggleKey()
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
try
{
_currentToggleKey = (KeyCode)Enum.Parse(typeof(KeyCode), FilterConfig.ToggleKey.Value, ignoreCase: true);
}
catch
{
_currentToggleKey = (KeyCode)292;
}
}
private void Update()
{
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: 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_003d: Invalid comparison between Unknown and I4
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Invalid comparison between Unknown and I4
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Invalid comparison between Unknown and I4
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Invalid comparison between Unknown and I4
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
if (_isWaitingForKey)
{
foreach (KeyCode value in Enum.GetValues(typeof(KeyCode)))
{
KeyCode val = value;
if ((int)val != 0 && (int)val != 323 && (int)val != 324 && (int)val != 325 && Input.GetKeyDown(val))
{
if ((int)val == 27)
{
_isWaitingForKey = false;
}
else
{
_currentToggleKey = val;
FilterConfig.ToggleKey.Value = ((object)(KeyCode)(ref val)).ToString();
_isWaitingForKey = false;
}
break;
}
}
return;
}
if (Input.GetKeyDown(_currentToggleKey))
{
Toggle();
}
if (_isVisible && Input.GetKeyDown((KeyCode)27))
{
Hide();
}
}
public void Toggle()
{
_isVisible = !_isVisible;
if (_isVisible)
{
LoadCurrentValues();
}
}
public void Show()
{
_isVisible = true;
LoadCurrentValues();
}
public void Hide()
{
_isVisible = false;
}
private void LoadCurrentValues()
{
_personalWords = FilterConfig.PersonalWordList.Value;
_exceptionWords = FilterConfig.ExceptionWordList.Value;
_serverWords = FilterConfig.ServerWordList.Value;
_serverExceptionWords = FilterConfig.ServerExceptionWordList.Value;
_blockedDisplayText = FilterConfig.BlockedDisplayText.Value;
}
private void OnGUI()
{
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Expected O, but got Unknown
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
if (_isVisible)
{
if (_isWaitingForKey)
{
GUI.Box(new Rect((float)(Screen.width / 2 - 150), (float)(Screen.height / 2 - 30), 300f, 60f), "");
GUI.Label(new Rect((float)(Screen.width / 2 - 140), (float)(Screen.height / 2 - 20), 280f, 40f), "Press any key to set...\n(ESC to cancel)", GetCenteredStyle());
}
GUI.skin.window.fontSize = 14;
GUI.skin.label.fontSize = 12;
GUI.skin.textField.fontSize = 12;
GUI.skin.button.fontSize = 12;
GUI.skin.toggle.fontSize = 12;
_windowRect = GUI.Window(9999, _windowRect, new WindowFunction(DrawWindow), $"Chat Filter Settings ({_currentToggleKey})");
}
}
private GUIStyle GetCenteredStyle()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
GUIStyle val = new GUIStyle(GUI.skin.label);
val.alignment = (TextAnchor)4;
val.fontSize = 16;
return val;
}
private void DrawWindow(int windowId)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_0794: Unknown result type (might be due to invalid IL or missing references)
_scrollPosition = GUILayout.BeginScrollView(_scrollPosition, Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUILayout.Label($"Toggle Key: {_currentToggleKey}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) });
if (GUILayout.Button("Change", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
{
_isWaitingForKey = true;
}
if (GUILayout.Button("Reset (F11)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }))
{
_currentToggleKey = (KeyCode)292;
FilterConfig.ToggleKey.Value = "F11";
}
GUILayout.EndHorizontal();
GUILayout.Space(10f);
GUILayout.Label("═══ Personal Filter ═══", GetHeaderStyle(), Array.Empty<GUILayoutOption>());
GUILayout.Space(5f);
bool flag = GUILayout.Toggle(FilterConfig.EnableFilter.Value, " Enable Filter", Array.Empty<GUILayoutOption>());
if (flag != FilterConfig.EnableFilter.Value)
{
FilterConfig.EnableFilter.Value = flag;
}
bool flag2 = GUILayout.Toggle(FilterConfig.PlaySound.Value, " Play Sound", Array.Empty<GUILayoutOption>());
if (flag2 != FilterConfig.PlaySound.Value)
{
FilterConfig.PlaySound.Value = flag2;
}
bool flag3 = GUILayout.Toggle(FilterConfig.CaseSensitive.Value, " Case Sensitive", Array.Empty<GUILayoutOption>());
if (flag3 != FilterConfig.CaseSensitive.Value)
{
FilterConfig.CaseSensitive.Value = flag3;
}
GUILayout.Space(10f);
GUILayout.Label("Apply to Channels:", Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
bool flag4 = GUILayout.Toggle(FilterConfig.FilterGlobalChat.Value, " Global (G)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
if (flag4 != FilterConfig.FilterGlobalChat.Value)
{
FilterConfig.FilterGlobalChat.Value = flag4;
}
bool flag5 = GUILayout.Toggle(FilterConfig.FilterZoneChat.Value, " Zone (Z)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
if (flag5 != FilterConfig.FilterZoneChat.Value)
{
FilterConfig.FilterZoneChat.Value = flag5;
}
bool flag6 = GUILayout.Toggle(FilterConfig.FilterPartyChat.Value, " Party (P)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
if (flag6 != FilterConfig.FilterPartyChat.Value)
{
FilterConfig.FilterPartyChat.Value = flag6;
}
GUILayout.EndHorizontal();
GUILayout.Space(10f);
GUILayout.Label("Match Mode:", Array.Empty<GUILayoutOption>());
DrawEnumButtons<FilterMatchMode>(FilterConfig.MatchMode);
GUILayout.Space(5f);
GUILayout.Label("Filter Action:", Array.Empty<GUILayoutOption>());
DrawEnumButtons<FilterAction>(FilterConfig.PersonalFilterAction);
GUILayout.Space(5f);
GUILayout.Label("Filter List Mode:", Array.Empty<GUILayoutOption>());
DrawEnumButtons<FilterMode>(FilterConfig.FilterListMode);
GUILayout.Space(10f);
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUILayout.Label("Replacement Char:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) });
DrawReplacementCharButtons();
GUILayout.EndHorizontal();
GUILayout.Space(10f);
GUILayout.Label("Filter Words (comma separated):", Array.Empty<GUILayoutOption>());
_personalWords = GUILayout.TextArea(_personalWords, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(60f) });
GUILayout.Space(5f);
GUILayout.Label("Exception Words (e.g., 'class,pass,glass' for 'ass' filter):", Array.Empty<GUILayoutOption>());
_exceptionWords = GUILayout.TextArea(_exceptionWords, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) });
GUILayout.Space(5f);
GUILayout.Label("Blocked Display Text (max 100 chars):", Array.Empty<GUILayoutOption>());
_blockedDisplayText = GUILayout.TextArea(_blockedDisplayText, 100, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) });
GUILayout.Space(20f);
GUILayout.Label("═══ Server Filter (Host Only) ═══", GetHeaderStyle(), Array.Empty<GUILayoutOption>());
GUILayout.Space(5f);
bool flag7 = GUILayout.Toggle(FilterConfig.EnableServerFilter.Value, " Enable Server Filter", Array.Empty<GUILayoutOption>());
if (flag7 != FilterConfig.EnableServerFilter.Value)
{
FilterConfig.EnableServerFilter.Value = flag7;
}
bool flag8 = GUILayout.Toggle(FilterConfig.LogFilteredMessages.Value, " Log Filtered Messages", Array.Empty<GUILayoutOption>());
if (flag8 != FilterConfig.LogFilteredMessages.Value)
{
FilterConfig.LogFilteredMessages.Value = flag8;
}
bool flag9 = GUILayout.Toggle(FilterConfig.NotifySender.Value, " Notify Sender", Array.Empty<GUILayoutOption>());
if (flag9 != FilterConfig.NotifySender.Value)
{
FilterConfig.NotifySender.Value = flag9;
}
GUILayout.Space(5f);
GUILayout.Label("Apply Server Filter to Channels:", Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
bool flag10 = GUILayout.Toggle(FilterConfig.ServerFilterGlobalChat.Value, " Global (G)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
if (flag10 != FilterConfig.ServerFilterGlobalChat.Value)
{
FilterConfig.ServerFilterGlobalChat.Value = flag10;
}
bool flag11 = GUILayout.Toggle(FilterConfig.ServerFilterZoneChat.Value, " Zone (Z)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
if (flag11 != FilterConfig.ServerFilterZoneChat.Value)
{
FilterConfig.ServerFilterZoneChat.Value = flag11;
}
bool flag12 = GUILayout.Toggle(FilterConfig.ServerFilterPartyChat.Value, " Party (P)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
if (flag12 != FilterConfig.ServerFilterPartyChat.Value)
{
FilterConfig.ServerFilterPartyChat.Value = flag12;
}
GUILayout.EndHorizontal();
GUILayout.Space(5f);
GUILayout.Label("Server Filter Action:", Array.Empty<GUILayoutOption>());
DrawEnumButtons<FilterAction>(FilterConfig.ServerFilterAction);
GUILayout.Space(10f);
GUILayout.Label("Server Filter Words (comma separated):", Array.Empty<GUILayoutOption>());
_serverWords = GUILayout.TextArea(_serverWords, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(60f) });
GUILayout.Space(5f);
GUILayout.Label("Server Exception Words:", Array.Empty<GUILayoutOption>());
_serverExceptionWords = GUILayout.TextArea(_serverExceptionWords, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) });
GUILayout.Space(20f);
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
ApplySettings();
}
if (GUILayout.Button("Reset to Defaults", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
ResetToDefaults();
}
if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
Hide();
}
GUILayout.EndHorizontal();
GUILayout.EndScrollView();
GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 25f));
}
private GUIStyle GetHeaderStyle()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
GUIStyle val = new GUIStyle(GUI.skin.label);
val.fontStyle = (FontStyle)1;
val.fontSize = 14;
return val;
}
private void DrawEnumButtons<T>(ConfigEntry<T> config) where T : Enum
{
Array values = Enum.GetValues(typeof(T));
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
foreach (T item in values)
{
GUIStyle val2 = (config.Value.Equals(item) ? GetSelectedButtonStyle() : GUI.skin.button);
if (GUILayout.Button(item.ToString(), val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(25f) }))
{
config.Value = item;
}
}
GUILayout.EndHorizontal();
}
private void DrawReplacementCharButtons()
{
string[] array = new string[5] { "#", "█", "●", "■", "X" };
string[] array2 = array;
foreach (string text in array2)
{
GUIStyle val = ((FilterConfig.ReplacementChar.Value == text) ? GetSelectedButtonStyle() : GUI.skin.button);
if (GUILayout.Button(text, val, (GUILayoutOption[])(object)new GUILayoutOption[2]
{
GUILayout.Width(35f),
GUILayout.Height(25f)
}))
{
FilterConfig.ReplacementChar.Value = text;
}
}
}
private GUIStyle GetSelectedButtonStyle()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
GUIStyle val = new GUIStyle(GUI.skin.button);
val.normal.textColor = Color.green;
val.fontStyle = (FontStyle)1;
return val;
}
private void ApplySettings()
{
FilterConfig.PersonalWordList.Value = _personalWords;
FilterConfig.ExceptionWordList.Value = _exceptionWords;
FilterConfig.ServerWordList.Value = _serverWords;
FilterConfig.ServerExceptionWordList.Value = _serverExceptionWords;
FilterConfig.BlockedDisplayText.Value = _blockedDisplayText;
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)"[FilterSettings] Settings applied.");
}
}
private void ResetToDefaults()
{
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
FilterConfig.EnableFilter.Value = true;
FilterConfig.PlaySound.Value = true;
FilterConfig.CaseSensitive.Value = false;
FilterConfig.MatchMode.Value = FilterMatchMode.Contains;
FilterConfig.PersonalFilterAction.Value = FilterAction.Replace;
FilterConfig.FilterListMode.Value = FilterMode.Blacklist;
FilterConfig.PersonalWordList.Value = "";
FilterConfig.ExceptionWordList.Value = "";
FilterConfig.BlockedDisplayText.Value = "[Filtered]";
FilterConfig.ToggleKey.Value = "F11";
_currentToggleKey = (KeyCode)292;
FilterConfig.FilterGlobalChat.Value = true;
FilterConfig.FilterZoneChat.Value = true;
FilterConfig.FilterPartyChat.Value = true;
FilterConfig.EnableServerFilter.Value = false;
FilterConfig.LogFilteredMessages.Value = true;
FilterConfig.NotifySender.Value = true;
FilterConfig.ServerFilterAction.Value = FilterAction.Replace;
FilterConfig.ServerWordList.Value = "";
FilterConfig.ServerExceptionWordList.Value = "";
FilterConfig.ServerFilterGlobalChat.Value = true;
FilterConfig.ServerFilterZoneChat.Value = true;
FilterConfig.ServerFilterPartyChat.Value = true;
LoadCurrentValues();
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)"[FilterSettings] Settings reset to defaults.");
}
}
}
}
namespace UniversalChatFilter.Patches
{
[HarmonyPatch(typeof(ChatBehaviour), "UserCode_Cmd_SendChatMessage__String__ChatChannel")]
public static class ChatBroadcastPatch
{
private static readonly ContainsFilter _containsFilter = new ContainsFilter();
private static readonly ExactMatchFilter _exactMatchFilter = new ExactMatchFilter();
private static readonly RegexFilter _regexFilter = new RegexFilter();
[HarmonyPrefix]
private static bool Prefix(ChatBehaviour __instance, ref string _message, ChatChannel _chatChannel)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (!FilterConfig.EnableServerFilter.Value)
{
return true;
}
if (!NetworkServer.active)
{
return true;
}
if (!FilterConfig.IsServerChannelFilterEnabled(_chatChannel))
{
return true;
}
try
{
FilterResult filterResult = CheckServerFilter(_message);
if (filterResult.IsFiltered)
{
if (FilterConfig.LogFilteredMessages.Value)
{
LogFilteredMessage(__instance, _message, filterResult.MatchedWord);
}
return HandleFilterAction(__instance, ref _message, filterResult);
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("[ServerFilter] Error: " + ex.Message));
}
}
return true;
}
private static FilterResult CheckServerFilter(string message)
{
FilterResult filterResult = new FilterResult
{
OriginalMessage = message,
IsFiltered = false
};
string[] serverWordArray = FilterConfig.GetServerWordArray();
if (serverWordArray.Length == 0)
{
return filterResult;
}
bool value = FilterConfig.CaseSensitive.Value;
IWordFilter filter = GetFilter();
string[] serverExceptionWordArray = FilterConfig.GetServerExceptionWordArray();
string[] array = serverWordArray;
foreach (string text in array)
{
if (!string.IsNullOrEmpty(text) && filter.IsMatch(message, text, value) && !IsExceptionMatch(message, text, serverExceptionWordArray, value))
{
filterResult.IsFiltered = true;
filterResult.MatchedWord = text;
break;
}
}
return filterResult;
}
private static bool IsExceptionMatch(string message, string filterWord, string[] exceptionWords, bool caseSensitive)
{
if (exceptionWords == null || exceptionWords.Length == 0)
{
return false;
}
StringComparison comparisonType = (caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
foreach (string text in exceptionWords)
{
if (text.IndexOf(filterWord, comparisonType) >= 0 && message.IndexOf(text, comparisonType) >= 0)
{
return true;
}
}
return false;
}
private static IWordFilter GetFilter()
{
return FilterConfig.MatchMode.Value switch
{
FilterMatchMode.ExactWord => _exactMatchFilter,
FilterMatchMode.Regex => _regexFilter,
_ => _containsFilter,
};
}
private static bool HandleFilterAction(ChatBehaviour instance, ref string message, FilterResult filterResult)
{
switch (FilterConfig.ServerFilterAction.Value)
{
case FilterAction.BlockAndNotify:
if (FilterConfig.NotifySender.Value)
{
instance.Target_RecieveMessage("Your message was blocked by the server filter.");
}
return false;
case FilterAction.Replace:
return true;
case FilterAction.SilentBlock:
return false;
case FilterAction.LogOnly:
return true;
default:
return true;
}
}
private static string ReplaceFilteredWords(string message, string matchedWord)
{
if (string.IsNullOrEmpty(matchedWord))
{
return message;
}
char c = ((!string.IsNullOrEmpty(FilterConfig.ReplacementChar.Value)) ? FilterConfig.ReplacementChar.Value[0] : '#');
string text = new string(c, matchedWord.Length);
if (!FilterConfig.CaseSensitive.Value)
{
for (int num = message.IndexOf(matchedWord, StringComparison.OrdinalIgnoreCase); num >= 0; num = message.IndexOf(matchedWord, num + text.Length, StringComparison.OrdinalIgnoreCase))
{
message = message.Substring(0, num) + text + message.Substring(num + matchedWord.Length);
}
return message;
}
return message.Replace(matchedWord, text);
}
private static void LogFilteredMessage(ChatBehaviour instance, string message, string matchedWord)
{
try
{
string text = Traverse.Create((object)instance).Field("_player").GetValue<Player>()?._nickname ?? "Unknown";
string text2 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string text3 = "[" + text2 + "] [" + text + "] Filtered (matched: '" + matchedWord + "'): " + message;
string text4 = Path.Combine(Paths.ConfigPath, "ChatFilter_Log.txt");
if (File.Exists(text4))
{
FileInfo fileInfo = new FileInfo(text4);
if (fileInfo.Length > 1048576)
{
string text5 = Path.Combine(Paths.ConfigPath, "ChatFilter_Log_old.txt");
if (File.Exists(text5))
{
File.Delete(text5);
}
File.Move(text4, text5);
}
}
File.AppendAllText(text4, text3 + Environment.NewLine);
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)("Filtered message from " + text + ": matched '" + matchedWord + "'"));
}
}
catch (Exception ex)
{
ManualLogSource log2 = Plugin.Log;
if (log2 != null)
{
log2.LogWarning((object)("Failed to log filtered message: " + ex.Message));
}
}
}
}
[HarmonyPatch(typeof(ChatBehaviour))]
public static class ChatBubblePatch
{
private static FieldInfo _chatTextMeshField;
[HarmonyPatch("UserCode_Rpc_RecieveChatMessage__String__Boolean__ChatChannel")]
[HarmonyPostfix]
private static void Postfix(ChatBehaviour __instance, string message, bool _isEmoteMessage, ChatChannel _chatChannel)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
if (_isEmoteMessage || !FilterConfig.EnableFilter.Value || !FilterConfig.IsChannelFilterEnabled(_chatChannel))
{
return;
}
string[] personalWordArray = FilterConfig.GetPersonalWordArray();
if (personalWordArray.Length == 0)
{
return;
}
try
{
FilterResult filterResult = FilterManager.ProcessMessage(message);
if (!filterResult.IsFiltered)
{
return;
}
if (_chatTextMeshField == null)
{
_chatTextMeshField = typeof(ChatBehaviour).GetField("_chatTextMesh", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (_chatTextMeshField != null)
{
object? value = _chatTextMeshField.GetValue(__instance);
TextMeshPro val = (TextMeshPro)((value is TextMeshPro) ? value : null);
if ((Object)(object)val != (Object)null)
{
((TMP_Text)val).text = ApplyBubbleFilter(message, filterResult);
}
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("[ChatBubble] Filter error: " + ex.Message));
}
}
}
private static string ApplyBubbleFilter(string message, FilterResult result)
{
switch (FilterConfig.PersonalFilterAction.Value)
{
case FilterAction.Replace:
return ChatDisplayPatch.ReplaceFilteredWords(message, result.MatchedWord);
case FilterAction.BlockAndNotify:
case FilterAction.SilentBlock:
return FilterConfig.BlockedDisplayText.Value;
case FilterAction.LogOnly:
return message;
default:
return message;
}
}
}
[HarmonyPatch(typeof(ChatBehaviour), "Send_ChatMessage")]
public static class ChatCommandPatch
{
[HarmonyPrefix]
private static bool Prefix(ChatBehaviour __instance, ref string _message)
{
try
{
if (FilterCommandHandler.TryHandleCommand(_message, out var response))
{
if (!string.IsNullOrEmpty(response))
{
__instance.New_ChatMessage(response);
}
ClearChatInput(__instance);
return false;
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Command processing failed: " + ex.Message));
}
}
return true;
}
private static void ClearChatInput(ChatBehaviour instance)
{
try
{
if ((Object)(object)instance._chatAssets != (Object)null && (Object)(object)instance._chatAssets._chatInput != (Object)null)
{
instance._chatAssets._chatInput.text = string.Empty;
instance._chatAssets._chatInput.DeactivateInputField();
}
instance._focusedInChat = false;
}
catch
{
}
}
}
[HarmonyPatch(typeof(ChatBehaviour), "New_ChatMessage")]
public static class ChatDisplayPatch
{
public class FilteredMessageData
{
public string OriginalMessage { get; set; }
public string FilteredDisplay { get; set; }
public bool IsRevealed { get; set; }
}
private static readonly Dictionary<int, FilteredMessageData> _filteredMessages = new Dictionary<int, FilteredMessageData>();
private static int _messageCounter = 0;
private const int MAX_CACHED = 100;
[HarmonyPrefix]
private static bool Prefix(ref string _message)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
if (!FilterConfig.EnableFilter.Value)
{
return true;
}
ChatChannel channel = DetectChatChannel(_message);
if (!FilterConfig.IsChannelFilterEnabled(channel))
{
return true;
}
string[] personalWordArray = FilterConfig.GetPersonalWordArray();
if (personalWordArray.Length == 0)
{
return true;
}
try
{
FilterResult filterResult = FilterManager.ProcessMessage(_message);
if (filterResult.IsFiltered)
{
return HandleFilterAction(ref _message, filterResult);
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("[ClientFilter] Error: " + ex.Message));
}
}
return true;
}
private static ChatChannel DetectChatChannel(string message)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: 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)
if (message.Contains("(G)"))
{
return (ChatChannel)0;
}
if (message.Contains("(Z)"))
{
return (ChatChannel)2;
}
if (message.Contains("(P)"))
{
return (ChatChannel)1;
}
return (ChatChannel)0;
}
private static bool HandleFilterAction(ref string message, FilterResult result)
{
switch (FilterConfig.PersonalFilterAction.Value)
{
case FilterAction.Replace:
message = ReplaceFilteredWords(message, result.MatchedWord);
return true;
case FilterAction.BlockAndNotify:
{
int messageId = CacheFilteredMessage(message, result.DisplayMessage);
message = FormatBlockedMessage(messageId);
return true;
}
case FilterAction.SilentBlock:
return false;
case FilterAction.LogOnly:
return true;
default:
return true;
}
}
public static string ReplaceFilteredWords(string message, string matchedWord)
{
if (string.IsNullOrEmpty(matchedWord))
{
return message;
}
char c = ((!string.IsNullOrEmpty(FilterConfig.ReplacementChar.Value)) ? FilterConfig.ReplacementChar.Value[0] : '#');
string text = new string(c, matchedWord.Length);
if (!FilterConfig.CaseSensitive.Value)
{
for (int num = message.IndexOf(matchedWord, StringComparison.OrdinalIgnoreCase); num >= 0; num = message.IndexOf(matchedWord, num + text.Length, StringComparison.OrdinalIgnoreCase))
{
message = message.Substring(0, num) + text + message.Substring(num + matchedWord.Length);
}
}
else
{
message = message.Replace(matchedWord, text);
}
return message;
}
private static string FormatBlockedMessage(int messageId)
{
string value = FilterConfig.BlockedDisplayText.Value;
return $"<link=\"filter_{messageId}\">{value} [Click to reveal]</link>";
}
private static int CacheFilteredMessage(string original, string filtered)
{
if (_filteredMessages.Count >= 100)
{
int key = _messageCounter - 100;
if (_filteredMessages.ContainsKey(key))
{
_filteredMessages.Remove(key);
}
}
int messageCounter = _messageCounter;
_filteredMessages[messageCounter] = new FilteredMessageData
{
OriginalMessage = original,
FilteredDisplay = filtered,
IsRevealed = false
};
_messageCounter++;
return messageCounter;
}
public static bool TryToggleMessage(int messageId, out string newDisplayText, out bool isNowRevealed)
{
newDisplayText = null;
isNowRevealed = false;
if (!_filteredMessages.TryGetValue(messageId, out var value))
{
return false;
}
value.IsRevealed = !value.IsRevealed;
isNowRevealed = value.IsRevealed;
if (value.IsRevealed)
{
newDisplayText = $"<link=\"filter_{messageId}\">{value.OriginalMessage} [Click to hide]</link>";
}
else
{
string value2 = FilterConfig.BlockedDisplayText.Value;
newDisplayText = $"<link=\"filter_{messageId}\">{value2} [Click to reveal]</link>";
}
return true;
}
public static bool TryGetMessageData(int messageId, out FilteredMessageData data)
{
return _filteredMessages.TryGetValue(messageId, out data);
}
public static void ClearCache()
{
_filteredMessages.Clear();
_messageCounter = 0;
}
}
[HarmonyPatch(typeof(ChatBehaviour), "Rpc_RecieveChatMessage")]
public static class ServerRpcFilterPatch
{
private static readonly ContainsFilter _containsFilter = new ContainsFilter();
private static readonly ExactMatchFilter _exactMatchFilter = new ExactMatchFilter();
private static readonly RegexFilter _regexFilter = new RegexFilter();
[HarmonyPrefix]
[HarmonyPriority(800)]
private static void Prefix(ref string message, bool _isEmoteMessage, ChatChannel _chatChannel)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
if (_isEmoteMessage || !NetworkServer.active || !FilterConfig.EnableServerFilter.Value || !FilterConfig.IsServerChannelFilterEnabled(_chatChannel))
{
return;
}
try
{
FilterResult filterResult = CheckServerFilter(message);
if (filterResult.IsFiltered)
{
switch (FilterConfig.ServerFilterAction.Value)
{
case FilterAction.Replace:
message = ReplaceFilteredWords(message, filterResult.MatchedWord);
break;
case FilterAction.BlockAndNotify:
message = FilterConfig.BlockedDisplayText.Value;
break;
case FilterAction.SilentBlock:
message = "";
break;
case FilterAction.LogOnly:
break;
}
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("[ServerRpcFilter] Error: " + ex.Message));
}
}
}
private static FilterResult CheckServerFilter(string message)
{
FilterResult filterResult = new FilterResult
{
OriginalMessage = message,
IsFiltered = false
};
string[] serverWordArray = FilterConfig.GetServerWordArray();
if (serverWordArray.Length == 0)
{
return filterResult;
}
bool value = FilterConfig.CaseSensitive.Value;
IWordFilter filter = GetFilter();
string[] serverExceptionWordArray = FilterConfig.GetServerExceptionWordArray();
string[] array = serverWordArray;
foreach (string text in array)
{
if (!string.IsNullOrEmpty(text) && filter.IsMatch(message, text, value) && !IsExceptionMatch(message, text, serverExceptionWordArray, value))
{
filterResult.IsFiltered = true;
filterResult.MatchedWord = text;
break;
}
}
return filterResult;
}
private static bool IsExceptionMatch(string message, string filterWord, string[] exceptionWords, bool caseSensitive)
{
if (exceptionWords == null || exceptionWords.Length == 0)
{
return false;
}
StringComparison comparisonType = (caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
foreach (string text in exceptionWords)
{
if (text.IndexOf(filterWord, comparisonType) >= 0 && message.IndexOf(text, comparisonType) >= 0)
{
return true;
}
}
return false;
}
private static IWordFilter GetFilter()
{
return FilterConfig.MatchMode.Value switch
{
FilterMatchMode.ExactWord => _exactMatchFilter,
FilterMatchMode.Regex => _regexFilter,
_ => _containsFilter,
};
}
private static string ReplaceFilteredWords(string message, string matchedWord)
{
if (string.IsNullOrEmpty(matchedWord))
{
return message;
}
char c = ((!string.IsNullOrEmpty(FilterConfig.ReplacementChar.Value)) ? FilterConfig.ReplacementChar.Value[0] : '#');
string text = new string(c, matchedWord.Length);
if (!FilterConfig.CaseSensitive.Value)
{
for (int num = message.IndexOf(matchedWord, StringComparison.OrdinalIgnoreCase); num >= 0; num = message.IndexOf(matchedWord, num + text.Length, StringComparison.OrdinalIgnoreCase))
{
message = message.Substring(0, num) + text + message.Substring(num + matchedWord.Length);
}
return message;
}
return message.Replace(matchedWord, text);
}
}
}
namespace UniversalChatFilter.Integration
{
public static class EasySettingsIntegration
{
private static bool _initialized;
private static bool _isAvailable;
private static Assembly _easySettingsAssembly;
private static Type _settingsType;
private static Type _settingsTabType;
public static bool IsAvailable => _isAvailable;
public static void Initialize()
{
if (_initialized)
{
return;
}
_initialized = true;
try
{
_easySettingsAssembly = FindEasySettingsAssembly();
if (_easySettingsAssembly == null)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)"EasySettings not found. Using config file only.");
}
return;
}
_settingsType = _easySettingsAssembly.GetType("Nessie.ATLYSS.EasySettings.Settings");
_settingsTabType = _easySettingsAssembly.GetType("Nessie.ATLYSS.EasySettings.SettingsTab");
if (_settingsType == null || _settingsTabType == null)
{
ManualLogSource log2 = Plugin.Log;
if (log2 != null)
{
log2.LogWarning((object)"EasySettings types not found.");
}
return;
}
RegisterOnInitialized();
_isAvailable = true;
ManualLogSource log3 = Plugin.Log;
if (log3 != null)
{
log3.LogInfo((object)"EasySettings integration enabled.");
}
}
catch (Exception ex)
{
ManualLogSource log4 = Plugin.Log;
if (log4 != null)
{
log4.LogWarning((object)("EasySettings integration failed: " + ex.Message));
}
_isAvailable = false;
}
}
private static Assembly FindEasySettingsAssembly()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
if (assembly.GetName().Name == "EasySettings")
{
return assembly;
}
}
return null;
}
private static void RegisterOnInitialized()
{
PropertyInfo property = _settingsType.GetProperty("OnInitialized", BindingFlags.Static | BindingFlags.Public);
if (property == null)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)"Settings.OnInitialized not found.");
}
return;
}
object value = property.GetValue(null);
if (value != null)
{
MethodInfo method = value.GetType().GetMethod("AddListener");
if (!(method == null))
{
Type typeFromHandle = typeof(UnityAction);
Delegate @delegate = Delegate.CreateDelegate(typeFromHandle, typeof(EasySettingsIntegration), "OnEasySettingsInitialized");
method.Invoke(value, new object[1] { @delegate });
}
}
}
public static void OnEasySettingsInitialized()
{
try
{
SetupSettingsUI();
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)"EasySettings UI configured.");
}
}
catch (Exception ex)
{
ManualLogSource log2 = Plugin.Log;
if (log2 != null)
{
log2.LogError((object)("Failed to setup EasySettings UI: " + ex.Message));
}
}
}
private static void SetupSettingsUI()
{
object obj = TryGetModerationTab();
if (obj == null)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)"Using default ModTab (Moderation tab not available).");
}
obj = GetModTab();
}
if (obj == null)
{
ManualLogSource log2 = Plugin.Log;
if (log2 != null)
{
log2.LogWarning((object)"No settings tab available.");
}
return;
}
AddHeader(obj, "═══ Chat Filter - Personal ═══");
AddToggle(obj, "Enable Filter", FilterConfig.EnableFilter);
AddToggle(obj, "Play Filter Sound", FilterConfig.PlaySound);
AddDropdown<FilterMatchMode>(obj, "Match Mode", FilterConfig.MatchMode);
AddDropdown<FilterAction>(obj, "Personal Filter Action", FilterConfig.PersonalFilterAction);
AddDropdown<FilterMode>(obj, "Filter List Mode", FilterConfig.FilterListMode);
AddTextField(obj, "Filter Words (comma separated)", FilterConfig.PersonalWordList, "word1, word2, ...");
AddTextField(obj, "Blocked Display Text", FilterConfig.BlockedDisplayText, "[Filtered]");
AddHeader(obj, "═══ Chat Filter - Server (Host Only) ═══");
AddToggle(obj, "Enable Server Filter", FilterConfig.EnableServerFilter);
AddDropdown<FilterAction>(obj, "Filter Action", FilterConfig.ServerFilterAction);
AddTextField(obj, "Server Filter Words", FilterConfig.ServerWordList, "word1, word2, ...");
AddToggle(obj, "Log Filtered Messages", FilterConfig.LogFilteredMessages);
AddToggle(obj, "Notify Sender", FilterConfig.NotifySender);
}
private static object TryGetModerationTab()
{
try
{
MethodInfo method = _settingsType.GetMethod("GetOrAddCustomTab", BindingFlags.Static | BindingFlags.Public);
if (method == null)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogDebug((object)"GetOrAddCustomTab method not found.");
}
return null;
}
object obj = method.Invoke(null, new object[1] { "Moderation" });
if (obj != null)
{
ManualLogSource log2 = Plugin.Log;
if (log2 != null)
{
log2.LogInfo((object)"Using Moderation tab for Chat Filter settings.");
}
}
return obj;
}
catch (Exception ex)
{
ManualLogSource log3 = Plugin.Log;
if (log3 != null)
{
log3.LogDebug((object)("Failed to get Moderation tab: " + ex.Message));
}
return null;
}
}
private static object GetModTab()
{
try
{
return _settingsType.GetProperty("ModTab", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
}
catch
{
return null;
}
}
private static void AddHeader(object settingsTab, string label)
{
try
{
_settingsTabType.GetMethod("AddHeader", new Type[1] { typeof(string) })?.Invoke(settingsTab, new object[1] { label });
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogDebug((object)("AddHeader failed: " + ex.Message));
}
}
}
private static void AddToggle(object settingsTab, string label, ConfigEntry<bool> config)
{
try
{
_settingsTabType.GetMethod("AddToggle", new Type[2]
{
typeof(string),
typeof(ConfigEntry<bool>)
})?.Invoke(settingsTab, new object[2] { label, config });
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogDebug((object)("AddToggle failed: " + ex.Message));
}
}
}
private static void AddTextField(object settingsTab, string label, ConfigEntry<string> config, string placeholder)
{
try
{
_settingsTabType.GetMethod("AddTextField", new Type[3]
{
typeof(string),
typeof(ConfigEntry<string>),
typeof(string)
})?.Invoke(settingsTab, new object[3] { label, config, placeholder });
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogDebug((object)("AddTextField failed: " + ex.Message));
}
}
}
private static void AddDropdown<T>(object settingsTab, string label, ConfigEntry<T> config) where T : struct
{
try
{
MethodInfo[] methods = _settingsTabType.GetMethods();
MethodInfo[] array = methods;
foreach (MethodInfo methodInfo in array)
{
if (methodInfo.Name == "AddDropdown" && methodInfo.IsGenericMethod)
{
MethodInfo methodInfo2 = methodInfo.MakeGenericMethod(typeof(T));
ParameterInfo[] parameters = methodInfo2.GetParameters();
if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string))
{
methodInfo2.Invoke(settingsTab, new object[2] { label, config });
break;
}
}
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogDebug((object)("AddDropdown failed: " + ex.Message));
}
}
}
}
}
namespace UniversalChatFilter.Filters
{
public class ContainsFilter : IWordFilter
{
public bool IsMatch(string message, string filterWord, bool caseSensitive)
{
if (string.IsNullOrEmpty(message) || string.IsNullOrEmpty(filterWord))
{
return false;
}
StringComparison comparisonType = (caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
return message.IndexOf(filterWord, comparisonType) >= 0;
}
}
public class ExactMatchFilter : IWordFilter
{
public bool IsMatch(string message, string filterWord, bool caseSensitive)
{
if (string.IsNullOrEmpty(message) || string.IsNullOrEmpty(filterWord))
{
return false;
}
RegexOptions options = ((!caseSensitive) ? RegexOptions.IgnoreCase : RegexOptions.None);
string pattern = "\\b" + Regex.Escape(filterWord) + "\\b";
return Regex.IsMatch(message, pattern, options);
}
}
public interface IWordFilter
{
bool IsMatch(string message, string filterWord, bool caseSensitive);
}
public class RegexFilter : IWordFilter
{
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(100.0);
public bool IsMatch(string message, string filterPattern, bool caseSensitive)
{
if (string.IsNullOrEmpty(message) || string.IsNullOrEmpty(filterPattern))
{
return false;
}
try
{
RegexOptions options = ((!caseSensitive) ? RegexOptions.IgnoreCase : RegexOptions.None);
Regex regex = new Regex(filterPattern, options, RegexTimeout);
return regex.IsMatch(message);
}
catch (RegexMatchTimeoutException)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Regex timeout for pattern: " + filterPattern));
}
return false;
}
catch (ArgumentException)
{
ManualLogSource log2 = Plugin.Log;
if (log2 != null)
{
log2.LogWarning((object)("Invalid regex pattern: " + filterPattern));
}
return false;
}
}
}
}
namespace UniversalChatFilter.Core
{
public enum FilterMatchMode
{
Contains,
ExactWord,
Regex
}
public enum FilterAction
{
BlockAndNotify,
Replace,
SilentBlock,
LogOnly
}
public enum FilterMode
{
Blacklist,
Whitelist
}
public static class FilterConfig
{
public static ConfigEntry<bool> EnableFilter { get; private set; }
public static ConfigEntry<bool> PlaySound { get; private set; }
public static ConfigEntry<string> BlockedDisplayText { get; private set; }
public static ConfigEntry<FilterMode> FilterListMode { get; private set; }
public static ConfigEntry<string> ReplacementChar { get; private set; }
public static ConfigEntry<string> ToggleKey { get; private set; }
public static ConfigEntry<bool> FilterGlobalChat { get; private set; }
public static ConfigEntry<bool> FilterZoneChat { get; private set; }
public static ConfigEntry<bool> FilterPartyChat { get; private set; }
public static ConfigEntry<string> PersonalWordList { get; private set; }
public static ConfigEntry<string> ExceptionWordList { get; private set; }
public static ConfigEntry<bool> CaseSensitive { get; private set; }
public static ConfigEntry<FilterMatchMode> MatchMode { get; private set; }
public static ConfigEntry<FilterAction> PersonalFilterAction { get; private set; }
public static ConfigEntry<bool> EnableServerFilter { get; private set; }
public static ConfigEntry<string> ServerWordList { get; private set; }
public static ConfigEntry<string> ServerExceptionWordList { get; private set; }
public static ConfigEntry<FilterAction> ServerFilterAction { get; private set; }
public static ConfigEntry<bool> LogFilteredMessages { get; private set; }
public static ConfigEntry<bool> NotifySender { get; private set; }
public static ConfigEntry<bool> ServerFilterGlobalChat { get; private set; }
public static ConfigEntry<bool> ServerFilterZoneChat { get; private set; }
public static ConfigEntry<bool> ServerFilterPartyChat { get; private set; }
public static void Initialize(ConfigFile config)
{
EnableFilter = config.Bind<bool>("General", "EnableFilter", true, "Enable chat filter (default: enabled)");
PlaySound = config.Bind<bool>("General", "PlaySound", true, "Play sound when a message is filtered");
BlockedDisplayText = config.Bind<string>("General", "BlockedDisplayText", "[Filtered]", "Text displayed for filtered messages");
FilterListMode = config.Bind<FilterMode>("General", "FilterListMode", FilterMode.Blacklist, "Blacklist: filter messages containing listed words. Whitelist: filter messages NOT containing listed words.");
ReplacementChar = config.Bind<string>("General", "ReplacementChar", "#", "Character used to replace filtered words (e.g., '#', '█', '●'). Note: '*' may cause issues with markdown parsing in some mods.");
ToggleKey = config.Bind<string>("General", "ToggleKey", "F11", "Key to open/close the settings window (e.g., F11, F10, F9)");
FilterGlobalChat = config.Bind<bool>("Channels", "FilterGlobalChat", true, "Apply filter to Global chat (G)");
FilterZoneChat = config.Bind<bool>("Channels", "FilterZoneChat", true, "Apply filter to Zone chat (Z)");
FilterPartyChat = config.Bind<bool>("Channels", "FilterPartyChat", true, "Apply filter to Party chat (P)");
PersonalWordList = config.Bind<string>("Client", "PersonalWordList", "", "Comma-separated list of words to filter (personal)");
CaseSensitive = config.Bind<bool>("Client", "CaseSensitive", false, "Case-sensitive matching");
MatchMode = config.Bind<FilterMatchMode>("Client", "MatchMode", FilterMatchMode.Contains, "Filter matching mode");
PersonalFilterAction = config.Bind<FilterAction>("Client", "PersonalFilterAction", FilterAction.Replace, "Action for personal filter: Replace (replace with specified char), BlockAndNotify (show [Filtered] message), SilentBlock (hide completely)");
ExceptionWordList = config.Bind<string>("Client", "ExceptionWordList", "", "Comma-separated list of exception words (e.g., 'class,pass,glass' to exclude from 'ass' filter)");
EnableServerFilter = config.Bind<bool>("Server", "EnableServerFilter", false, "Enable server-wide filter (host only)");
ServerWordList = config.Bind<string>("Server", "ServerWordList", "", "Comma-separated list of words to filter (server-wide)");
ServerFilterAction = config.Bind<FilterAction>("Server", "FilterAction", FilterAction.Replace, "Action to take when a message is filtered");
LogFilteredMessages = config.Bind<bool>("Server", "LogFilteredMessages", true, "Log filtered messages to file");
NotifySender = config.Bind<bool>("Server", "NotifySender", true, "Notify sender when their message is filtered");
ServerExceptionWordList = config.Bind<string>("Server", "ServerExceptionWordList", "", "Comma-separated list of exception words for server filter");
ServerFilterGlobalChat = config.Bind<bool>("ServerChannels", "ServerFilterGlobalChat", true, "Apply server filter to Global chat (G)");
ServerFilterZoneChat = config.Bind<bool>("ServerChannels", "ServerFilterZoneChat", true, "Apply server filter to Zone chat (Z)");
ServerFilterPartyChat = config.Bind<bool>("ServerChannels", "ServerFilterPartyChat", true, "Apply server filter to Party chat (P)");
}
public static bool IsChannelFilterEnabled(ChatChannel channel)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Expected I4, but got Unknown
return (int)channel switch
{
0 => FilterGlobalChat.Value,
2 => FilterZoneChat.Value,
1 => FilterPartyChat.Value,
_ => true,
};
}
public static bool IsServerChannelFilterEnabled(ChatChannel channel)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Expected I4, but got Unknown
return (int)channel switch
{
0 => ServerFilterGlobalChat.Value,
2 => ServerFilterZoneChat.Value,
1 => ServerFilterPartyChat.Value,
_ => true,
};
}
public static string[] GetPersonalWordArray()
{
if (string.IsNullOrWhiteSpace(PersonalWordList.Value))
{
return new string[0];
}
return (from w in PersonalWordList.Value.Split(new char[1] { ',' })
select w.Trim() into w
where !string.IsNullOrEmpty(w)
select w).ToArray();
}
public static string[] GetServerWordArray()
{
if (string.IsNullOrWhiteSpace(ServerWordList.Value))
{
return new string[0];
}
return (from w in ServerWordList.Value.Split(new char[1] { ',' })
select w.Trim() into w
where !string.IsNullOrEmpty(w)
select w).ToArray();
}
public static string[] GetExceptionWordArray()
{
if (string.IsNullOrWhiteSpace(ExceptionWordList.Value))
{
return new string[0];
}
return (from w in ExceptionWordList.Value.Split(new char[1] { ',' })
select w.Trim() into w
where !string.IsNullOrEmpty(w)
select w).ToArray();
}
public static string[] GetServerExceptionWordArray()
{
if (string.IsNullOrWhiteSpace(ServerExceptionWordList.Value))
{
return new string[0];
}
return (from w in ServerExceptionWordList.Value.Split(new char[1] { ',' })
select w.Trim() into w
where !string.IsNullOrEmpty(w)
select w).ToArray();
}
public static void AddPersonalWord(string word)
{
List<string> list = GetPersonalWordArray().ToList();
if (!list.Contains<string>(word, StringComparer.OrdinalIgnoreCase))
{
list.Add(word);
PersonalWordList.Value = string.Join(", ", list);
}
}
public static bool RemovePersonalWord(string word)
{
List<string> list = GetPersonalWordArray().ToList();
bool flag = list.RemoveAll((string w) => string.Equals(w, word, StringComparison.OrdinalIgnoreCase)) > 0;
if (flag)
{
PersonalWordList.Value = string.Join(", ", list);
}
return flag;
}
public static void ClearPersonalWords()
{
PersonalWordList.Value = "";
}
public static void AddServerWord(string word)
{
List<string> list = GetServerWordArray().ToList();
if (!list.Contains<string>(word, StringComparer.OrdinalIgnoreCase))
{
list.Add(word);
ServerWordList.Value = string.Join(", ", list);
}
}
public static bool RemoveServerWord(string word)
{
List<string> list = GetServerWordArray().ToList();
bool flag = list.RemoveAll((string w) => string.Equals(w, word, StringComparison.OrdinalIgnoreCase)) > 0;
if (flag)
{
ServerWordList.Value = string.Join(", ", list);
}
return flag;
}
public static void ClearServerWords()
{
ServerWordList.Value = "";
}
}
}
namespace UniversalChatFilter.Commands
{
public static class FilterCommandHandler
{
private const string CMD_PREFIX = "/filter";
public static bool TryHandleCommand(string message, out string response)
{
response = null;
if (string.IsNullOrWhiteSpace(message))
{
return false;
}
string text = message.Trim();
if (!text.StartsWith("/filter", StringComparison.OrdinalIgnoreCase))
{
return false;
}
string[] args = text.Substring("/filter".Length).Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
response = ProcessCommand(args);
return true;
}
private static string ProcessCommand(string[] args)
{
if (args.Length == 0)
{
return GetHelpText();
}
string text = args[0].ToLowerInvariant();
switch (text)
{
case "help":
case "?":
return GetHelpText();
case "on":
return EnableFilter(enable: true);
case "off":
return EnableFilter(enable: false);
case "add":
return AddWord(args);
case "remove":
case "del":
return RemoveWord(args);
case "list":
return ListWords();
case "clear":
return ClearWords();
case "mode":
return ToggleFilterMode(args);
case "server":
return ProcessServerCommand(args);
default:
return "<color=red>Unknown command: " + text + ". Use '/filter help' for help.</color>";
}
}
private static string EnableFilter(bool enable)
{
FilterConfig.EnableFilter.Value = enable;
string text = (enable ? "enabled" : "disabled");
return "<color=yellow>Chat filter " + text + ".</color>";
}
private static string AddWord(string[] args)
{
if (args.Length < 2)
{
return "<color=red>Usage: /filter add <word></color>";
}
string text = string.Join(" ", args, 1, args.Length - 1);
FilterConfig.AddPersonalWord(text);
return "<color=green>Added '" + text + "' to filter list.</color>";
}
private static string RemoveWord(string[] args)
{
if (args.Length < 2)
{
return "<color=red>Usage: /filter remove <word></color>";
}
string text = string.Join(" ", args, 1, args.Length - 1);
if (FilterConfig.RemovePersonalWord(text))
{
return "<color=green>Removed '" + text + "' from filter list.</color>";
}
return "<color=red>'" + text + "' not found in filter list.</color>";
}
private static string ListWords()
{
string[] personalWordArray = FilterConfig.GetPersonalWordArray();
FilterMode value = FilterConfig.FilterListMode.Value;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"<color=yellow>Filter mode: {value}</color>");
if (personalWordArray.Length == 0)
{
stringBuilder.AppendLine("Filter list is empty.");
return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
}
stringBuilder.AppendLine("<color=yellow>Filter words:</color>");
for (int i = 0; i < personalWordArray.Length; i++)
{
stringBuilder.AppendLine($" {i + 1}. {personalWordArray[i]}");
}
return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
}
private static string ToggleFilterMode(string[] args)
{
if (args.Length < 2)
{
FilterMode value = FilterConfig.FilterListMode.Value;
return $"<color=yellow>Current mode: {value}. Use '/filter mode blacklist' or '/filter mode whitelist'.</color>";
}
switch (args[1].ToLowerInvariant())
{
case "blacklist":
case "black":
FilterConfig.FilterListMode.Value = FilterMode.Blacklist;
return "<color=green>Filter mode set to Blacklist (filter messages containing listed words).</color>";
case "whitelist":
case "white":
FilterConfig.FilterListMode.Value = FilterMode.Whitelist;
return "<color=green>Filter mode set to Whitelist (filter messages NOT containing listed words).</color>";
default:
return "<color=red>Invalid mode. Use 'blacklist' or 'whitelist'.</color>";
}
}
private static string ClearWords()
{
FilterConfig.ClearPersonalWords();
return "<color=yellow>Filter list cleared.</color>";
}
private static string ProcessServerCommand(string[] args)
{
if (!IsHost())
{
return "<color=red>Server filter commands are host-only.</color>";
}
if (args.Length < 2)
{
return GetServerHelpText();
}
switch (args[1].ToLowerInvariant())
{
case "on":
return EnableServerFilter(enable: true);
case "off":
return EnableServerFilter(enable: false);
case "add":
return AddServerWord(args);
case "remove":
case "del":
return RemoveServerWord(args);
case "list":
return ListServerWords();
default:
return GetServerHelpText();
}
}
private static bool IsHost()
{
try
{
Player val = Object.FindObjectOfType<Player>();
return (Object)(object)val != (Object)null && val._isHostPlayer;
}
catch
{
return false;
}
}
private static string EnableServerFilter(bool enable)
{
FilterConfig.EnableServerFilter.Value = enable;
string text = (enable ? "enabled" : "disabled");
return "<color=yellow>Server filter " + text + ".</color>";
}
private static string AddServerWord(string[] args)
{
if (args.Length < 3)
{
return "<color=red>Usage: /filter server add <word></color>";
}
string text = string.Join(" ", args, 2, args.Length - 2);
FilterConfig.AddServerWord(text);
return "<color=green>Added '" + text + "' to server filter list.</color>";
}
private static string RemoveServerWord(string[] args)
{
if (args.Length < 3)
{
return "<color=red>Usage: /filter server remove <word></color>";
}
string text = string.Join(" ", args, 2, args.Length - 2);
if (FilterConfig.RemoveServerWord(text))
{
return "<color=green>Removed '" + text + "' from server filter list.</color>";
}
return "<color=red>'" + text + "' not found in server filter list.</color>";
}
private static string ListServerWords()
{
string[] serverWordArray = FilterConfig.GetServerWordArray();
if (serverWordArray.Length == 0)
{
return "<color=yellow>Server filter list is empty.</color>";
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("<color=yellow>Server filter words:</color>");
for (int i = 0; i < serverWordArray.Length; i++)
{
stringBuilder.AppendLine($" {i + 1}. {serverWordArray[i]}");
}
return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
}
private static string GetHelpText()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("<color=yellow>=== Chat Filter Commands ===</color>");
stringBuilder.AppendLine("/filter on - Enable filter");
stringBuilder.AppendLine("/filter off - Disable filter");
stringBuilder.AppendLine("/filter add <word> - Add word to filter");
stringBuilder.AppendLine("/filter remove <word> - Remove word");
stringBuilder.AppendLine("/filter list - Show filter list");
stringBuilder.AppendLine("/filter clear - Clear all filters");
stringBuilder.AppendLine("/filter mode <black/white> - Set filter mode");
stringBuilder.AppendLine("/filter server - Server commands (host)");
return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
}
private static string GetServerHelpText()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("<color=yellow>=== Server Filter Commands (Host) ===</color>");
stringBuilder.AppendLine("/filter server on - Enable server filter");
stringBuilder.AppendLine("/filter server off - Disable server filter");
stringBuilder.AppendLine("/filter server add <word> - Add word");
stringBuilder.AppendLine("/filter server remove <word> - Remove word");
stringBuilder.AppendLine("/filter server list - Show server filter list");
return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
}
}
}