Decompiled source of UniversalChatFilter v1.0.2

plugins/UniversalChatFilter/UniversalChatFilter.dll

Decompiled a day ago
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>());
		}
	}
}