Decompiled source of Hush v0.1.13

AndrewLin.Hush.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using Alpha;
using Alpha.Core.Command;
using Alpha.Core.Util;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Hush.Core;
using Hush.Core.Commands;
using Hush.Patches;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using PurrNet;
using PurrNet.Packing;
using PurrNet.Transports;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AndrewLin")]
[assembly: AssemblyConfiguration("Publish")]
[assembly: AssemblyDescription("Hush: A mod for On-Together with in-game utility commands including scheduled hushers (/hushmein, /hushlocalin, /hushglobalin), and more. Use /hushhelp")]
[assembly: AssemblyFileVersion("0.1.13.0")]
[assembly: AssemblyInformationalVersion("0.1.13+40efb38c878f46f938474f9d25618c3ddcd1f984")]
[assembly: AssemblyProduct("AndrewLin.Hush")]
[assembly: AssemblyTitle("AndrewLin.Hush")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/andrewlimforfun/ot-mods")]
[assembly: AssemblyVersion("0.1.13.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace Hush
{
	public static class BuildInfo
	{
		public const string Version = "0.1.13";
	}
	[BepInPlugin("com.andrewlin.ontogether.hush", "Hush", "0.1.13")]
	public class HushPlugin : BaseUnityPlugin
	{
		private static ManualLogSource? _logger;

		private static readonly ConcurrentQueue<Action> _mainThreadQueue = new ConcurrentQueue<Action>();

		public const string ModGUID = "com.andrewlin.ontogether.hush";

		public const string ModName = "Hush";

		public const string ModVersion = "0.1.13";

		public static ConfigEntry<bool>? EnableFeature { get; private set; }

		public static ConfigEntry<bool>? ShowCommand { get; private set; }

		public static ConfigEntry<FilterAction>? FilterActionConfig { get; private set; }

		public static ConfigEntry<string>? CensorCharConfig { get; private set; }

		public static ConfigEntry<string>? FilterConfigPathConfig { get; private set; }

		public static ChatFilterManager? FilterManager { get; private set; }

		public static PlayerMuteManager? MuteManager { get; private set; }

		public static BanManager? BanManager { get; private set; }

		public static bool VerboseLogging
		{
			get
			{
				return HushSettings.VerboseLogging;
			}
			set
			{
				HushSettings.VerboseLogging = value;
			}
		}

		public static string FilterConfigPath => FilterConfigPathConfig?.Value ?? Path.Combine(Paths.ConfigPath, "com.andrewlin.ontogether.hush.filter.json");

		public static string MutesConfigPath => Path.Combine(Paths.ConfigPath, "com.andrewlin.ontogether.hush.mutes.json");

		public static void SaveFilter()
		{
			if (FilterManager == null)
			{
				return;
			}
			try
			{
				FilterManager.Save(FilterConfigPath);
			}
			catch (Exception ex)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("Failed to save filter config: " + ex.Message));
				}
			}
		}

		public static void SaveMutes()
		{
			if (MuteManager == null)
			{
				return;
			}
			try
			{
				MuteManager.Save(MutesConfigPath);
			}
			catch (Exception ex)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("Failed to save mutes: " + ex.Message));
				}
			}
		}

		public static void RunOnMainThread(Action action)
		{
			ConfigEntry<bool>? enableFeature = EnableFeature;
			if (enableFeature != null && enableFeature.Value)
			{
				_mainThreadQueue.Enqueue(action);
			}
		}

		private void Awake()
		{
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			_logger = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hush v0.1.13 is loaded!");
			InitConfig();
			FilterManager = new ChatFilterManager();
			FilterManager.Load(FilterConfigPath);
			if (FilterActionConfig != null)
			{
				FilterManager.Action = FilterActionConfig.Value;
				FilterActionConfig.SettingChanged += delegate
				{
					if (FilterManager != null)
					{
						FilterManager.Action = FilterActionConfig.Value;
					}
				};
			}
			if (CensorCharConfig != null)
			{
				if (CensorCharConfig.Value.Length == 1)
				{
					FilterManager.CensorChar = CensorCharConfig.Value[0];
				}
				CensorCharConfig.SettingChanged += delegate
				{
					if (FilterManager != null && CensorCharConfig.Value.Length == 1)
					{
						FilterManager.CensorChar = CensorCharConfig.Value[0];
					}
				};
			}
			MuteManager = new PlayerMuteManager();
			MuteManager.Load(MutesConfigPath);
			BanManager = new BanManager();
			Harmony val = new Harmony("com.andrewlin.ontogether.hush");
			val.PatchAll(typeof(TextChannelManagerPatch));
			ChatCommandManager commandManager = AlphaPlugin.CommandManager;
			if (commandManager != null)
			{
				commandManager.Register((IChatCommand)(object)new HushToggleCommand());
			}
			ChatCommandManager commandManager2 = AlphaPlugin.CommandManager;
			if (commandManager2 != null)
			{
				commandManager2.Register((IChatCommand)(object)new HushAddWordCommand());
			}
			ChatCommandManager commandManager3 = AlphaPlugin.CommandManager;
			if (commandManager3 != null)
			{
				commandManager3.Register((IChatCommand)(object)new HushRemoveWordCommand());
			}
			ChatCommandManager commandManager4 = AlphaPlugin.CommandManager;
			if (commandManager4 != null)
			{
				commandManager4.Register((IChatCommand)(object)new HushAddPatternCommand());
			}
			ChatCommandManager commandManager5 = AlphaPlugin.CommandManager;
			if (commandManager5 != null)
			{
				commandManager5.Register((IChatCommand)(object)new HushRemovePatternCommand());
			}
			ChatCommandManager commandManager6 = AlphaPlugin.CommandManager;
			if (commandManager6 != null)
			{
				commandManager6.Register((IChatCommand)(object)new HushGetWordsCommand());
			}
			ChatCommandManager commandManager7 = AlphaPlugin.CommandManager;
			if (commandManager7 != null)
			{
				commandManager7.Register((IChatCommand)(object)new HushGetPatternsCommand());
			}
			ChatCommandManager commandManager8 = AlphaPlugin.CommandManager;
			if (commandManager8 != null)
			{
				commandManager8.Register((IChatCommand)(object)new HushFilterActionCommand());
			}
			ChatCommandManager commandManager9 = AlphaPlugin.CommandManager;
			if (commandManager9 != null)
			{
				commandManager9.Register((IChatCommand)(object)new HushCensorCharCommand());
			}
			ChatCommandManager commandManager10 = AlphaPlugin.CommandManager;
			if (commandManager10 != null)
			{
				commandManager10.Register((IChatCommand)(object)new HushLoadFilterCommand());
			}
			ChatCommandManager commandManager11 = AlphaPlugin.CommandManager;
			if (commandManager11 != null)
			{
				commandManager11.Register((IChatCommand)(object)new HushMuteCommand());
			}
			ChatCommandManager commandManager12 = AlphaPlugin.CommandManager;
			if (commandManager12 != null)
			{
				commandManager12.Register((IChatCommand)(object)new HushUnmuteCommand());
			}
			ChatCommandManager commandManager13 = AlphaPlugin.CommandManager;
			if (commandManager13 != null)
			{
				commandManager13.Register((IChatCommand)(object)new HushTempMuteCommand());
			}
			ChatCommandManager commandManager14 = AlphaPlugin.CommandManager;
			if (commandManager14 != null)
			{
				commandManager14.Register((IChatCommand)(object)new HushGetMutesCommand());
			}
			ChatCommandManager commandManager15 = AlphaPlugin.CommandManager;
			if (commandManager15 != null)
			{
				commandManager15.Register((IChatCommand)(object)new HushDelegateAddCommand());
			}
			ChatCommandManager commandManager16 = AlphaPlugin.CommandManager;
			if (commandManager16 != null)
			{
				commandManager16.Register((IChatCommand)(object)new HushDelegateRemoveCommand());
			}
			ChatCommandManager commandManager17 = AlphaPlugin.CommandManager;
			if (commandManager17 != null)
			{
				commandManager17.Register((IChatCommand)(object)new HushDelegateListCommand());
			}
			ChatCommandManager commandManager18 = AlphaPlugin.CommandManager;
			if (commandManager18 != null)
			{
				commandManager18.Register((IChatCommand)(object)new HushVersionCommand());
			}
			ChatCommandManager commandManager19 = AlphaPlugin.CommandManager;
			if (commandManager19 != null)
			{
				commandManager19.Register((IChatCommand)(object)new HushLogVerboseCommand());
			}
			ChatCommandManager commandManager20 = AlphaPlugin.CommandManager;
			if (commandManager20 != null)
			{
				commandManager20.Register((IChatCommand)(object)new HushBanCommand());
			}
			ChatCommandManager commandManager21 = AlphaPlugin.CommandManager;
			if (commandManager21 != null)
			{
				commandManager21.Register((IChatCommand)(object)new HushBanOfflineCommand());
			}
			ChatCommandManager commandManager22 = AlphaPlugin.CommandManager;
			if (commandManager22 != null)
			{
				commandManager22.Register((IChatCommand)(object)new HushUnbanCommand());
			}
			ChatCommandManager commandManager23 = AlphaPlugin.CommandManager;
			if (commandManager23 != null)
			{
				commandManager23.Register((IChatCommand)(object)new HushGetBansCommand());
			}
		}

		private void InitConfig()
		{
			EnableFeature = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableFeature", true, "Enable or disable the mod feature.");
			ShowCommand = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowCommand", false, "Show the command in chat when used.");
			FilterActionConfig = ((BaseUnityPlugin)this).Config.Bind<FilterAction>("Filter", "Action", FilterAction.Censor, "How the filter handles matched words: Censor (replace with asterisks) or Block (suppress entire message).");
			CensorCharConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Filter", "CensorChar", "*", "Character used to replace matched words when in Censor mode.");
			FilterConfigPathConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Filter", "ConfigPath", Path.Combine(Paths.ConfigPath, "com.andrewlin.ontogether.hush.filter.json"), "Path to the filter word list JSON file.");
		}

		private void Update()
		{
			ConfigEntry<bool>? enableFeature = EnableFeature;
			if (enableFeature == null || !enableFeature.Value)
			{
				return;
			}
			MuteManager?.Tick();
			Action result;
			while (_mainThreadQueue.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Main thread action failed: " + ex.Message));
				}
			}
		}

		private void OnDestroy()
		{
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "AndrewLin.Hush";

		public const string PLUGIN_NAME = "AndrewLin.Hush";

		public const string PLUGIN_VERSION = "0.1.13";
	}
}
namespace Hush.Patches
{
	[HarmonyPatch(typeof(TextChannelManager))]
	public static class TextChannelManagerPatch
	{
		private static readonly ManualLogSource _log = Logger.CreateLogSource("Hush.TCMP");

		[HarmonyPatch("HandleRPCGenerated_0")]
		[HarmonyPrefix]
		public static bool HandleRPCGenerated_0_Prefix(BitPacker stream, ref RPCPacket packet, RPCInfo info, bool asServer)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0491: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Unknown result type (might be due to invalid IL or missing references)
			if (!asServer)
			{
				return true;
			}
			BitPacker val = BitPackerPool.Get(packet.data);
			byte[] bytes = null;
			Packer<byte[]>.Read(val, ref bytes);
			byte[] array = null;
			Packer<byte[]>.Read(val, ref array);
			bool flag = false;
			Packer<bool>.Read(val, ref flag);
			Vector3 val2 = default(Vector3);
			Packer<Vector3>.Read(val, ref val2);
			string text = null;
			Packer<string>.Read(val, ref text);
			val.Dispose();
			string @string = Encoding.Unicode.GetString(array);
			string text2 = ChatUtils.CleanTMPTags(@string);
			if (HushSettings.VerboseLogging)
			{
				_log.LogDebug((object)("[Server] HandleRPCGenerated_0: intercept " + text2 + " (" + text + ")"));
			}
			if (HushPlugin.MuteManager == null)
			{
				_log.LogWarning((object)"[Server] MuteManager is null - mute check skipped");
			}
			else
			{
				if (HushPlugin.MuteManager.IsMuted(text))
				{
					ChatUtils.AddGlobalNotification("Muted message from " + @string + " (" + text + ").");
					_log.LogInfo((object)("[Server] Blocked message from muted player " + text2 + " (" + text + ")."));
					return false;
				}
				if (HushSettings.VerboseLogging)
				{
					_log.LogDebug((object)("[Server] " + text2 + " (" + text + ") is not muted, proceeding"));
				}
			}
			string string2 = Encoding.Unicode.GetString(bytes);
			if (string2.Trim().StartsWith("hush:", StringComparison.Ordinal))
			{
				PlayerMuteManager? muteManager = HushPlugin.MuteManager;
				if (muteManager != null && muteManager.IsDelegate(text))
				{
					_log.LogInfo((object)("[Relay] Relay accepted from delegate " + text2 + " (" + text + "): " + string2));
					try
					{
						ExecuteRelay(string2, text);
					}
					catch (Exception arg)
					{
						_log.LogError((object)$"[Relay] Unhandled exception executing relay from {text2} ({text}): {arg}");
					}
				}
				else if (PlayerLists.IsAdmin(text))
				{
					_log.LogInfo((object)("[Relay] Relay accepted from admin " + text2 + ": " + string2));
					try
					{
						ExecuteRelay(string2, text);
					}
					catch (Exception arg2)
					{
						_log.LogError((object)$"[Relay] Unhandled exception executing relay from admin {text2} ({text}): {arg2}");
					}
				}
				else
				{
					_log.LogWarning((object)("[Relay] Relay rejected from non-delegate " + text2 + " (" + text + "): " + string2));
				}
				return false;
			}
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager == null || !filterManager.Enabled)
			{
				return true;
			}
			FilterResult filterResult = filterManager.Apply(string2);
			if (filterResult.WasBlocked)
			{
				ChatUtils.AddGlobalNotification("Filter blocked message from " + @string + " (" + text + ").");
				_log.LogInfo((object)("[Server] Blocked message from " + text2 + " (" + text + "): \"" + string2 + "\""));
				return false;
			}
			if (!filterResult.WasModified)
			{
				return true;
			}
			_log.LogInfo((object)("[Server] Censored message from " + text2 + " (" + text + ")."));
			byte[] bytes2 = Encoding.Unicode.GetBytes(filterResult.Text);
			BitPacker val3 = BitPackerPool.Get(false);
			Packer<byte[]>.Write(val3, bytes2);
			Packer<byte[]>.Write(val3, array);
			Packer<bool>.Write(val3, flag);
			Packer<Vector3>.Write(val3, val2);
			Packer<string>.Write(val3, text);
			int positionInBytes = val3.positionInBytes;
			byte[] array2 = new byte[positionInBytes];
			Array.Copy(val3.buffer, 0, array2, 0, positionInBytes);
			val3.Dispose();
			packet.data = new ByteData(array2, 0, positionInBytes);
			return true;
		}

		private static void ExecuteRelay(string payload, string senderSteamId)
		{
			PlayerMuteManager mutes = HushPlugin.MuteManager;
			if (mutes != null)
			{
				RelayExecutor relayExecutor = new RelayExecutor(mutes, (Func<string, string, bool>)((string id, string name) => HushPlugin.BanManager?.Ban(id, name) ?? false), (Func<string, bool>)((string id) => mutes.Unmute(id)), (Func<string, string?>)delegate(string id)
				{
					PlayerDetail obj = PlayerUtils.FindPlayerBySteamID(id);
					return (obj != null) ? obj.UserNameClean : null;
				}, (Func<string, string?>)((string q) => PlayerUtils.FindPlayerByQuery(q)?.SteamID), (Action<string>)ChatUtils.AddGlobalNotification, (Action)HushPlugin.SaveMutes, _log);
				relayExecutor.Execute(payload, senderSteamId);
			}
		}

		private static string FormatDuration(TimeSpan ts)
		{
			return DurationFormatter.Format(ts);
		}

		[HarmonyPatch("OnChannelMessageReceived")]
		[HarmonyPrefix]
		public static bool OnChannelMessageReceived_Prefix(string message, string playerID)
		{
			if (string.IsNullOrEmpty(message))
			{
				return true;
			}
			if (message.Trim().StartsWith("hush:", StringComparison.Ordinal))
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				if (i != null && ((NetworkIdentity)i).isServer)
				{
					_log.LogWarning((object)("[Relay] Backup path triggered — relay escaped RPC intercept. sender=(" + playerID + "): " + message));
					try
					{
						ExecuteRelay(message, playerID);
					}
					catch (Exception arg)
					{
						_log.LogError((object)$"[Relay] Backup path unhandled exception from ({playerID}): {arg}");
					}
				}
			}
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager == null || !filterManager.Enabled)
			{
				return true;
			}
			FilterResult filterResult = filterManager.Apply(message);
			if (filterResult.WasBlocked)
			{
				_log.LogInfo((object)("Suppressed notification for blocked message: " + message));
			}
			return !filterResult.WasBlocked;
		}

		[HarmonyPatch("AddMessageUI")]
		[HarmonyPrefix]
		public static bool AddMessageUI_Prefix(ref string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager == null || !filterManager.Enabled)
			{
				return true;
			}
			FilterResult filterResult = filterManager.Apply(text);
			if (filterResult.WasBlocked)
			{
				_log.LogInfo((object)("Blocked message in UI: " + filterResult.Text));
				return false;
			}
			if (filterResult.WasModified)
			{
				_log.LogInfo((object)("Censored message in UI: " + filterResult.Text));
				text = filterResult.Text;
			}
			return true;
		}
	}
}
namespace Hush.Core
{
	public class BanManager
	{
		private readonly ManualLogSource _log = Logger.CreateLogSource("Hush.BM");

		public bool IsBanned(string steamId)
		{
			return MonoSingleton<DataManager>.I.BanData.BanServerPlayers.Contains(steamId);
		}

		public bool Ban(string steamId, string displayName)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			BanData banData = MonoSingleton<DataManager>.I.BanData;
			if (banData.BanServerPlayers.Contains(steamId))
			{
				return false;
			}
			banData.BanServerPlayers.Add(steamId);
			banData.BanServerPlayerNicks.Add(displayName);
			MonoSingleton<DataManager>.I.SaveBanData();
			_log.LogInfo((object)("Banned: " + displayName + " (" + steamId + ")"));
			PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I;
			TextChannelManager i2 = NetworkSingleton<TextChannelManager>.I;
			if ((Object)(object)i != (Object)null && (Object)(object)i2 != (Object)null)
			{
				int num = i.PlayerSteamIDs.IndexOf(steamId);
				if (num >= 0)
				{
					i2.MainPlayerController.BanRPC(i.PlayerIDs[num], true, default(RPCInfo));
				}
			}
			return true;
		}

		public bool BanOffline(string steamId, string displayName)
		{
			BanData banData = MonoSingleton<DataManager>.I.BanData;
			if (banData.BanServerPlayers.Contains(steamId))
			{
				return false;
			}
			banData.BanServerPlayers.Add(steamId);
			banData.BanServerPlayerNicks.Add(displayName);
			MonoSingleton<DataManager>.I.SaveBanData();
			_log.LogInfo((object)("Offline banned: " + displayName + " (" + steamId + ")"));
			return true;
		}

		public bool Unban(string steamId)
		{
			BanData banData = MonoSingleton<DataManager>.I.BanData;
			int num = banData.BanServerPlayers.IndexOf(steamId);
			if (num < 0)
			{
				return false;
			}
			string text = banData.BanServerPlayerNicks[num];
			banData.BanServerPlayers.RemoveAt(num);
			banData.BanServerPlayerNicks.RemoveAt(num);
			MonoSingleton<DataManager>.I.SaveBanData();
			_log.LogInfo((object)("Unbanned: " + text + " (" + steamId + ")"));
			return true;
		}

		public List<(string SteamId, string Nick)> GetBans()
		{
			BanData banData = MonoSingleton<DataManager>.I.BanData;
			List<(string, string)> list = new List<(string, string)>(banData.BanServerPlayers.Count);
			for (int i = 0; i < banData.BanServerPlayers.Count; i++)
			{
				list.Add((banData.BanServerPlayers[i], banData.BanServerPlayerNicks[i]));
			}
			return list;
		}
	}
	public enum FilterAction
	{
		Censor,
		Block
	}
	public readonly struct FilterResult
	{
		public readonly bool WasModified;

		public readonly bool WasBlocked;

		public readonly string Text;

		private FilterResult(string text, bool wasModified, bool wasBlocked)
		{
			Text = text;
			WasModified = wasModified;
			WasBlocked = wasBlocked;
		}

		public static FilterResult Unchanged(string text)
		{
			return new FilterResult(text, wasModified: false, wasBlocked: false);
		}

		public static FilterResult Censored(string text)
		{
			return new FilterResult(text, wasModified: true, wasBlocked: false);
		}

		public static FilterResult Blocked()
		{
			return new FilterResult(string.Empty, wasModified: false, wasBlocked: true);
		}
	}
	public class ChatFilterManager
	{
		private class FilterConfig
		{
			public List<string>? Words { get; set; }

			public List<string>? Patterns { get; set; }
		}

		private readonly ManualLogSource _log = Logger.CreateLogSource("Hush.CFM");

		private readonly HashSet<string> _blockedWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _rawPatterns = new HashSet<string>(StringComparer.Ordinal);

		private Regex? _pattern;

		public FilterAction Action { get; set; } = FilterAction.Censor;


		public char CensorChar { get; set; } = '*';


		public bool Enabled { get; set; } = true;


		public int Count => _blockedWords.Count + _rawPatterns.Count;

		public bool Add(string word)
		{
			if (string.IsNullOrWhiteSpace(word))
			{
				return false;
			}
			if (!_blockedWords.Add(word.Trim()))
			{
				return false;
			}
			_log.LogInfo((object)("Added word: \"" + word.Trim() + "\""));
			RebuildPattern();
			return true;
		}

		public bool Remove(string word)
		{
			if (string.IsNullOrWhiteSpace(word))
			{
				return false;
			}
			if (!_blockedWords.Remove(word.Trim()))
			{
				return false;
			}
			_log.LogInfo((object)("Removed word: \"" + word.Trim() + "\""));
			RebuildPattern();
			return true;
		}

		public bool AddPattern(string regexPattern)
		{
			if (string.IsNullOrWhiteSpace(regexPattern))
			{
				return false;
			}
			string text = regexPattern.Trim();
			try
			{
				new Regex(text);
			}
			catch (ArgumentException ex)
			{
				_log.LogWarning((object)("Rejected invalid regex \"" + text + "\": " + ex.Message));
				return false;
			}
			if (!_rawPatterns.Add(text))
			{
				return false;
			}
			_log.LogInfo((object)("Added pattern: \"" + text + "\""));
			RebuildPattern();
			return true;
		}

		public bool RemovePattern(string regexPattern)
		{
			if (string.IsNullOrWhiteSpace(regexPattern))
			{
				return false;
			}
			string text = regexPattern.Trim();
			if (!_rawPatterns.Remove(text))
			{
				return false;
			}
			_log.LogInfo((object)("Removed pattern: \"" + text + "\""));
			RebuildPattern();
			return true;
		}

		public void Clear()
		{
			int count = Count;
			_blockedWords.Clear();
			_rawPatterns.Clear();
			_pattern = null;
			_log.LogInfo((object)$"Filter cleared ({count} entries removed).");
		}

		public string[] GetWords()
		{
			string[] array = new string[_blockedWords.Count];
			_blockedWords.CopyTo(array);
			return array;
		}

		public string[] GetPatterns()
		{
			string[] array = new string[_rawPatterns.Count];
			_rawPatterns.CopyTo(array);
			return array;
		}

		public FilterResult Apply(string message)
		{
			if (!Enabled || string.IsNullOrEmpty(message))
			{
				return FilterResult.Unchanged(message);
			}
			Regex pattern = _pattern;
			FilterAction action = Action;
			char censorChar = CensorChar;
			if (pattern == null)
			{
				return FilterResult.Unchanged(message);
			}
			if (action == FilterAction.Block)
			{
				if (pattern.IsMatch(message))
				{
					return FilterResult.Blocked();
				}
				return FilterResult.Unchanged(message);
			}
			string text = pattern.Replace(message, (Match match) => new string(censorChar, match.Value.Length));
			if (string.Equals(text, message, StringComparison.Ordinal))
			{
				return FilterResult.Unchanged(message);
			}
			return FilterResult.Censored(text);
		}

		public void Save(string filePath)
		{
			FilterConfig filterConfig = new FilterConfig
			{
				Words = new List<string>(_blockedWords),
				Patterns = new List<string>(_rawPatterns)
			};
			string contents = JsonConvert.SerializeObject((object)filterConfig, (Formatting)1);
			File.WriteAllText(filePath, contents, Encoding.UTF8);
			if (HushSettings.VerboseLogging)
			{
				_log.LogInfo((object)$"Saved filter config ({_blockedWords.Count} words, {_rawPatterns.Count} patterns) to: {filePath}");
			}
		}

		public void Load(string filePath)
		{
			if (!File.Exists(filePath))
			{
				if (HushSettings.VerboseLogging)
				{
					_log.LogInfo((object)"No filter config file found, starting fresh.");
				}
				return;
			}
			FilterConfig filterConfig;
			try
			{
				string text = File.ReadAllText(filePath, Encoding.UTF8);
				filterConfig = JsonConvert.DeserializeObject<FilterConfig>(text);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Failed to parse filter config: " + ex.Message));
				return;
			}
			if (filterConfig == null)
			{
				return;
			}
			_blockedWords.Clear();
			_rawPatterns.Clear();
			if (filterConfig.Words != null)
			{
				foreach (string word in filterConfig.Words)
				{
					if (!string.IsNullOrWhiteSpace(word))
					{
						_blockedWords.Add(word.Trim());
					}
				}
			}
			if (filterConfig.Patterns != null)
			{
				foreach (string pattern in filterConfig.Patterns)
				{
					if (!string.IsNullOrWhiteSpace(pattern))
					{
						string text2 = pattern.Trim();
						try
						{
							new Regex(text2);
							_rawPatterns.Add(text2);
						}
						catch (ArgumentException)
						{
							_log.LogWarning((object)("Skipped invalid regex in config: \"" + text2 + "\""));
						}
					}
				}
			}
			RebuildPattern();
			if (HushSettings.VerboseLogging)
			{
				_log.LogInfo((object)$"Loaded filter config: {_blockedWords.Count} words, {_rawPatterns.Count} patterns.");
			}
		}

		private void RebuildPattern()
		{
			bool flag = _blockedWords.Count > 0;
			bool flag2 = _rawPatterns.Count > 0;
			if (!flag && !flag2)
			{
				_pattern = null;
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			if (flag)
			{
				stringBuilder.Append("\\b(?:");
				bool flag3 = true;
				foreach (string blockedWord in _blockedWords)
				{
					if (!flag3)
					{
						stringBuilder.Append('|');
					}
					stringBuilder.Append(Regex.Escape(blockedWord));
					flag3 = false;
				}
				stringBuilder.Append(")\\b");
			}
			foreach (string rawPattern in _rawPatterns)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append('|');
				}
				stringBuilder.Append("(?:");
				stringBuilder.Append(rawPattern);
				stringBuilder.Append(')');
			}
			_pattern = new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			if (HushSettings.VerboseLogging)
			{
				_log.LogInfo((object)$"Pattern rebuilt: {Count} active entries.");
			}
		}
	}
	public static class DurationFormatter
	{
		public static string Format(TimeSpan ts)
		{
			if (ts.TotalSeconds < 60.0)
			{
				return $"{(int)ts.TotalSeconds}s";
			}
			if (ts.TotalMinutes < 60.0)
			{
				return $"{(int)ts.TotalMinutes}m";
			}
			if (ts.TotalHours < 24.0)
			{
				string text = $"{ts.Hours}h";
				return (ts.Minutes > 0) ? $"{text} {ts.Minutes}m" : text;
			}
			return $"{(int)ts.TotalDays}d";
		}
	}
	public static class HushSettings
	{
		public static bool VerboseLogging { get; set; }
	}
	public class PlayerMuteManager
	{
		private class MuteConfig
		{
			public List<string>? PermaMuted { get; set; }

			public Dictionary<string, string>? TimedMutes { get; set; }

			public List<string>? Delegates { get; set; }
		}

		private readonly ManualLogSource _log = Logger.CreateLogSource("Hush.PMM");

		private readonly HashSet<string> _permaMuted = new HashSet<string>(StringComparer.Ordinal);

		private readonly Dictionary<string, DateTime> _timedMutes = new Dictionary<string, DateTime>(StringComparer.Ordinal);

		private readonly HashSet<string> _delegates = new HashSet<string>(StringComparer.Ordinal);

		public bool Mute(string steamId)
		{
			string text = DisplayId(steamId);
			if (_timedMutes.Remove(steamId) && HushSettings.VerboseLogging)
			{
				_log.LogDebug((object)("Mute: removed existing timed mute for " + text));
			}
			if (!_permaMuted.Add(steamId))
			{
				if (HushSettings.VerboseLogging)
				{
					_log.LogDebug((object)("Mute: " + text + " was already permanently muted (no-op)"));
				}
				return false;
			}
			_log.LogInfo((object)("Permanently muted: " + text));
			return true;
		}

		public bool MuteFor(string steamId, TimeSpan duration)
		{
			string text = DisplayId(steamId);
			if (duration <= TimeSpan.Zero)
			{
				_log.LogWarning((object)$"MuteFor: invalid duration {duration} for {text} — ignored");
				return false;
			}
			if (_permaMuted.Remove(steamId) && HushSettings.VerboseLogging)
			{
				_log.LogDebug((object)("MuteFor: removed existing permanent mute for " + text));
			}
			bool flag = _timedMutes.ContainsKey(steamId);
			_timedMutes[steamId] = DateTime.UtcNow + duration;
			if (flag && HushSettings.VerboseLogging)
			{
				_log.LogDebug((object)("MuteFor: extended/replaced existing timed mute for " + text));
			}
			_log.LogInfo((object)$"Timed muted: {text} until {_timedMutes[steamId]:u} ({duration.TotalSeconds:0}s)");
			return true;
		}

		public bool Unmute(string steamId)
		{
			bool flag = _permaMuted.Remove(steamId);
			bool flag2 = _timedMutes.Remove(steamId);
			if (flag || flag2)
			{
				_log.LogInfo((object)$"Unmuted: {DisplayId(steamId)} (wasPerma={flag}, wasTimed={flag2})");
				return true;
			}
			if (HushSettings.VerboseLogging)
			{
				_log.LogDebug((object)("Unmute: " + DisplayId(steamId) + " was not muted (no-op)"));
			}
			return false;
		}

		public bool IsMuted(string steamId)
		{
			if (_permaMuted.Contains(steamId))
			{
				if (HushSettings.VerboseLogging)
				{
					_log.LogDebug((object)("IsMuted: " + DisplayId(steamId) + " → true (permanent)"));
				}
				return true;
			}
			if (_timedMutes.TryGetValue(steamId, out var value))
			{
				bool flag = value > DateTime.UtcNow;
				if (HushSettings.VerboseLogging)
				{
					_log.LogDebug((object)$"IsMuted: {DisplayId(steamId)} → {flag} (timed, expires {value:u}, remaining {(value - DateTime.UtcNow).TotalSeconds:0}s)");
				}
				return flag;
			}
			if (HushSettings.VerboseLogging)
			{
				_log.LogDebug((object)("IsMuted: " + DisplayId(steamId) + " → false (not in either list)"));
			}
			return false;
		}

		public bool IsDelegate(string steamId)
		{
			return _delegates.Contains(steamId);
		}

		public bool AddDelegate(string steamId)
		{
			if (!_delegates.Add(steamId))
			{
				return false;
			}
			_log.LogInfo((object)("Added mute delegate: " + DisplayId(steamId)));
			return true;
		}

		public bool RemoveDelegate(string steamId)
		{
			if (!_delegates.Remove(steamId))
			{
				return false;
			}
			_log.LogInfo((object)("Removed mute delegate: " + DisplayId(steamId)));
			return true;
		}

		public IReadOnlyCollection<string> GetDelegates()
		{
			return _delegates;
		}

		public void Tick()
		{
			if (_timedMutes.Count == 0)
			{
				return;
			}
			DateTime now = DateTime.UtcNow;
			List<string> list = (from kv in _timedMutes
				where kv.Value <= now
				select kv.Key).ToList();
			foreach (string item in list)
			{
				_timedMutes.Remove(item);
				string text = DisplayId(item);
				ChatUtils.AddGlobalNotification("Timed mute expired for player " + text + ".");
				_log.LogInfo((object)("Timed mute expired: " + text));
			}
		}

		public List<(string SteamId, DateTime? Expiry)> GetMutes()
		{
			List<(string, DateTime?)> list = new List<(string, DateTime?)>();
			foreach (string item in _permaMuted)
			{
				list.Add((item, null));
			}
			DateTime utcNow = DateTime.UtcNow;
			foreach (KeyValuePair<string, DateTime> timedMute in _timedMutes)
			{
				if (timedMute.Value > utcNow)
				{
					list.Add((timedMute.Key, timedMute.Value));
				}
			}
			return list;
		}

		public void Save(string filePath)
		{
			MuteConfig muteConfig = new MuteConfig
			{
				PermaMuted = new List<string>(_permaMuted),
				TimedMutes = _timedMutes.Where<KeyValuePair<string, DateTime>>((KeyValuePair<string, DateTime> kv) => kv.Value > DateTime.UtcNow).ToDictionary((KeyValuePair<string, DateTime> kv) => kv.Key, (KeyValuePair<string, DateTime> kv) => kv.Value.ToString("o")),
				Delegates = new List<string>(_delegates)
			};
			string contents = JsonConvert.SerializeObject((object)muteConfig, (Formatting)1);
			File.WriteAllText(filePath, contents, Encoding.UTF8);
			if (HushSettings.VerboseLogging)
			{
				_log.LogInfo((object)("Saved mute config to: " + filePath));
			}
		}

		public void Load(string filePath)
		{
			if (!File.Exists(filePath))
			{
				if (HushSettings.VerboseLogging)
				{
					_log.LogInfo((object)"No mute config found, starting fresh.");
				}
				return;
			}
			MuteConfig muteConfig;
			try
			{
				string text = File.ReadAllText(filePath, Encoding.UTF8);
				muteConfig = JsonConvert.DeserializeObject<MuteConfig>(text);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Failed to parse mute config: " + ex.Message));
				return;
			}
			if (muteConfig == null)
			{
				return;
			}
			_permaMuted.Clear();
			_timedMutes.Clear();
			_delegates.Clear();
			if (muteConfig.PermaMuted != null)
			{
				foreach (string item in muteConfig.PermaMuted)
				{
					if (!string.IsNullOrWhiteSpace(item))
					{
						_permaMuted.Add(item.Trim());
					}
				}
			}
			if (muteConfig.TimedMutes != null)
			{
				DateTime utcNow = DateTime.UtcNow;
				foreach (KeyValuePair<string, string> timedMute in muteConfig.TimedMutes)
				{
					if (DateTime.TryParse(timedMute.Value, null, DateTimeStyles.RoundtripKind, out var result) && result > utcNow)
					{
						_timedMutes[timedMute.Key] = result;
					}
				}
			}
			if (muteConfig.Delegates != null)
			{
				foreach (string @delegate in muteConfig.Delegates)
				{
					if (!string.IsNullOrWhiteSpace(@delegate))
					{
						_delegates.Add(@delegate.Trim());
					}
				}
			}
			if (HushSettings.VerboseLogging)
			{
				_log.LogInfo((object)$"Loaded mute config: {_permaMuted.Count} permanent, {_timedMutes.Count} timed, {_delegates.Count} delegates.");
			}
		}

		private string DisplayId(string steamId)
		{
			try
			{
				PlayerDetail val = PlayerUtils.FindPlayerBySteamID(steamId);
				return (val != null) ? (val.UserName + " (" + steamId + ")") : steamId;
			}
			catch
			{
				return steamId;
			}
		}
	}
	public class RelayExecutor
	{
		private readonly PlayerMuteManager _mutes;

		private readonly Func<string, string, bool> _ban;

		private readonly Func<string, bool> _unmute;

		private readonly Func<string, string?> _resolveName;

		private readonly Func<string, string?> _resolveQuery;

		private readonly Action<string> _notify;

		private readonly Action _saveMutes;

		private readonly ManualLogSource _log;

		public RelayExecutor(PlayerMuteManager mutes, Func<string, string, bool> ban, Func<string, bool> unmute, Func<string, string?> resolveName, Func<string, string?> resolveQuery, Action<string> notify, Action saveMutes, ManualLogSource log)
		{
			_mutes = mutes;
			_ban = ban;
			_unmute = unmute;
			_resolveName = resolveName;
			_resolveQuery = resolveQuery;
			_notify = notify;
			_saveMutes = saveMutes;
			_log = log;
		}

		public bool Execute(string text, string senderSteamId)
		{
			if (text == null || !text.StartsWith("hush:", StringComparison.Ordinal))
			{
				_log.LogWarning((object)("[Relay] Missing 'hush:' prefix from " + senderSteamId + ": " + text));
				return false;
			}
			string payload = text.Substring("hush:".Length);
			ParsedRelayCommand parsedRelayCommand = RelayParser.Parse(payload);
			if (!parsedRelayCommand.IsValid)
			{
				_log.LogWarning((object)("[Relay] " + parsedRelayCommand.Error + " from " + senderSteamId));
				return false;
			}
			string text2 = _resolveName(senderSteamId) ?? senderSteamId;
			string text3;
			string text4;
			if (SteamUtils.IsSteamID(parsedRelayCommand.TargetSteamId))
			{
				text3 = parsedRelayCommand.TargetSteamId;
				text4 = _resolveName(text3);
			}
			else
			{
				string text5 = _resolveQuery(parsedRelayCommand.TargetSteamId);
				if (text5 == null)
				{
					_log.LogWarning((object)("[Relay] Could not resolve player query \"" + parsedRelayCommand.TargetSteamId + "\" from " + senderSteamId + "."));
					return false;
				}
				text3 = text5;
				text4 = _resolveName(text3);
				_log.LogInfo((object)("[Relay] Resolved query \"" + parsedRelayCommand.TargetSteamId + "\" → " + (text4 ?? text3) + " (" + text3 + ")"));
			}
			string text6 = ((text4 != null) ? (text4 + " (" + text3 + ")") : text3);
			switch (parsedRelayCommand.Type)
			{
			case RelayCommandType.TimedMute:
			{
				_mutes.MuteFor(text3, TimeSpan.FromSeconds(parsedRelayCommand.DurationSeconds));
				_saveMutes();
				string text7 = DurationFormatter.Format(TimeSpan.FromSeconds(parsedRelayCommand.DurationSeconds));
				_notify("Hush: delegate " + text2 + " muted " + text6 + " for " + text7 + ".");
				_log.LogInfo((object)$"[Relay] {text2} ({senderSteamId}) muted {text6} for {parsedRelayCommand.DurationSeconds}s.");
				return true;
			}
			case RelayCommandType.Ban:
				if (_ban(text3, text4 ?? text3))
				{
					_notify("Hush: delegate " + text2 + " banned " + text6 + ".");
					_log.LogInfo((object)("[Relay] " + text2 + " (" + senderSteamId + ") banned " + text6 + "."));
				}
				return true;
			case RelayCommandType.Unmute:
				if (_unmute(text3))
				{
					_saveMutes();
					_notify("Hush: delegate " + text2 + " unmuted " + text6 + ".");
					_log.LogInfo((object)("[Relay] " + text2 + " (" + senderSteamId + ") unmuted " + text6 + "."));
				}
				return true;
			default:
				_log.LogWarning((object)("[Relay] Unhandled command type from " + senderSteamId + "."));
				return false;
			}
		}
	}
	public enum RelayCommandType
	{
		TimedMute,
		Ban,
		Unmute,
		Unknown
	}
	public readonly struct ParsedRelayCommand
	{
		public readonly bool IsValid;

		public readonly RelayCommandType Type;

		public readonly string TargetSteamId;

		public readonly int DurationSeconds;

		public readonly string? Error;

		private ParsedRelayCommand(RelayCommandType type, string targetSteamId, int durationSeconds)
		{
			IsValid = true;
			Type = type;
			TargetSteamId = targetSteamId;
			DurationSeconds = durationSeconds;
			Error = null;
		}

		private ParsedRelayCommand(string error)
		{
			IsValid = false;
			Type = RelayCommandType.Unknown;
			TargetSteamId = string.Empty;
			DurationSeconds = 0;
			Error = error;
		}

		internal static ParsedRelayCommand Valid(RelayCommandType type, string targetId, int duration = 0)
		{
			return new ParsedRelayCommand(type, targetId, duration);
		}

		internal static ParsedRelayCommand Invalid(string error)
		{
			return new ParsedRelayCommand(error);
		}
	}
	public static class RelayParser
	{
		public static ParsedRelayCommand Parse(string payload)
		{
			if (string.IsNullOrEmpty(payload))
			{
				return ParsedRelayCommand.Invalid("Empty payload");
			}
			int num = payload.IndexOf(':');
			if (num < 0)
			{
				return ParsedRelayCommand.Invalid("Malformed payload: " + payload);
			}
			string text = payload.Substring(0, num);
			string text2 = payload.Substring(num + 1);
			switch (text)
			{
			case "tmute":
			{
				int num2 = text2.LastIndexOf(':');
				if (num2 < 0)
				{
					return ParsedRelayCommand.Invalid("Bad tmute args: " + text2);
				}
				string text3 = text2.Substring(0, num2);
				if (string.IsNullOrEmpty(text3))
				{
					return ParsedRelayCommand.Invalid("Empty tmute target: " + text2);
				}
				if (!int.TryParse(text2.Substring(num2 + 1), out var result) || result <= 0)
				{
					return ParsedRelayCommand.Invalid("Bad duration: " + text2);
				}
				return ParsedRelayCommand.Valid(RelayCommandType.TimedMute, text3, result);
			}
			case "ban":
				if (string.IsNullOrEmpty(text2))
				{
					return ParsedRelayCommand.Invalid("Empty ban target");
				}
				return ParsedRelayCommand.Valid(RelayCommandType.Ban, text2);
			case "unmute":
				if (string.IsNullOrEmpty(text2))
				{
					return ParsedRelayCommand.Invalid("Empty unmute target");
				}
				return ParsedRelayCommand.Valid(RelayCommandType.Unmute, text2);
			default:
				return ParsedRelayCommand.Invalid("Unknown command: " + text);
			}
		}
	}
}
namespace Hush.Core.Commands
{
	public class HushAddPatternCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushaddpattern";

		public string ShortName => "hap";

		public string Description => "Add a raw regex pattern to the Hush filter. Usage: /hushaddpattern <pattern>  (e.g. (?i)f+u+c+k)";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushaddpattern <pattern>  Example: /hushaddpattern (?i)f+u+c+k");
				return;
			}
			string text = string.Join(" ", args);
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager != null)
			{
				if (filterManager.AddPattern(text))
				{
					HushPlugin.SaveFilter();
					ChatUtils.AddGlobalNotification("Hush: added pattern \"" + text + "\".");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: pattern \"" + text + "\" is invalid or already in the list.");
				}
			}
		}
	}
	public class HushAddWordCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushaddword";

		public string ShortName => "haw";

		public string Description => "Add a literal word to the Hush filter. Usage: /hushaddword <word>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushaddword <word>");
				return;
			}
			string text = string.Join(" ", args);
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager != null)
			{
				if (filterManager.Add(text))
				{
					HushPlugin.SaveFilter();
					ChatUtils.AddGlobalNotification("Hush: added word \"" + text + "\".");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: \"" + text + "\" is already in the list.");
				}
			}
		}
	}
	public class HushBanCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushban";

		public string ShortName => "hb";

		public string Description => "Ban an online player. Host executes directly; delegates relay to host. Usage: /hushban <player>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushban <player>");
				return;
			}
			string text = string.Join(" ", args);
			PlayerDetail val = PlayerUtils.FindPlayerByQuery(text);
			if (val == null)
			{
				ChatUtils.AddGlobalNotification("Hush: player not found: \"" + text + "\"");
				return;
			}
			string playerSteamID = SteamUtils.GetPlayerSteamID();
			if (PlayerUtils.GetHost()?.SteamID == playerSteamID)
			{
				BanManager banManager = HushPlugin.BanManager;
				if (banManager != null)
				{
					if (banManager.Ban(val.SteamID, val.UserName))
					{
						ChatUtils.AddGlobalNotification("Hush: banned " + val.UserName + ".");
					}
					else
					{
						ChatUtils.AddGlobalNotification("Hush: " + val.UserName + " is already banned.");
					}
				}
				return;
			}
			HushPlugin.BanManager?.BanOffline(val.SteamID, val.UserName);
			TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
			if ((Object)(object)i == (Object)null)
			{
				ChatUtils.AddGlobalNotification("Hush: not connected - cannot relay ban request.");
				return;
			}
			string s = "hush:ban:" + val.SteamID;
			Vector3 val2 = (((Object)(object)i.MainPlayer != (Object)null) ? i.MainPlayer.position : Vector3.zero);
			i.SendMessageAsync(Encoding.Unicode.GetBytes(s), Encoding.Unicode.GetBytes(i.UserName ?? string.Empty), false, val2, playerSteamID, default(RPCInfo));
			ChatUtils.AddGlobalNotification("Hush: ban request relayed for " + val.UserName + ".");
		}
	}
	public class HushBanOfflineCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushbanoffline";

		public string ShortName => "hbo";

		public string Description => "Ban a player by Steam ID without them being online (host only). Usage: /hushbanoffline <steamid> <nickname>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushbanoffline <steamid> <nickname>");
				return;
			}
			string text = args[0].Trim();
			string text2 = string.Join(" ", args, 1, args.Length - 1);
			if (!Regex.IsMatch(text, "^\\d{17}$"))
			{
				ChatUtils.AddGlobalNotification("Hush: invalid Steam ID \"" + text + "\". Must be a 17-digit number.");
				return;
			}
			BanManager banManager = HushPlugin.BanManager;
			if (banManager != null)
			{
				if (banManager.BanOffline(text, text2))
				{
					ChatUtils.AddGlobalNotification("Hush: banned " + text2 + " (" + text + ").");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: " + text + " is already banned.");
				}
			}
		}
	}
	public class HushCensorCharCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "hushcensorchar";

		public string Name => "hushcensorchar";

		public string ShortName => "hcc";

		public string Description => "Set Hush censor character: /hushcensorchar <char>.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (HushPlugin.CensorCharConfig != null)
			{
				if (args.Length == 0 || args[0].Length != 1)
				{
					ChatUtils.AddGlobalNotification("Usage: /hushcensorchar <char>. Supply exactly one character.");
					return;
				}
				HushPlugin.CensorCharConfig.Value = args[0][0].ToString();
				ChatUtils.AddGlobalNotification($"Hush censor character set to '{args[0][0]}'.");
			}
		}
	}
	public class HushDelegateAddCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushdelegateadd";

		public string ShortName => "hda";

		public string Description => "Add a player to the mute-delegate whitelist (host only). Usage: /hushdelegateadd <player>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushdelegateadd <player>");
				return;
			}
			string text = string.Join(" ", args);
			PlayerDetail val = PlayerUtils.FindPlayerByQuery(text);
			if (val == null)
			{
				ChatUtils.AddGlobalNotification("Hush: player not found: \"" + text + "\"");
				return;
			}
			PlayerMuteManager muteManager = HushPlugin.MuteManager;
			if (muteManager != null)
			{
				if (muteManager.AddDelegate(val.SteamID))
				{
					HushPlugin.SaveMutes();
					ChatUtils.AddGlobalNotification("Hush: " + val.UserNameClean + " added as mute delegate.");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: " + val.UserNameClean + " is already a delegate.");
				}
			}
		}
	}
	public class HushDelegateListCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushdelegatelist";

		public string ShortName => "hdl";

		public string Description => "List all mute delegates (host only). Usage: /hushdelegatelist";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			PlayerMuteManager muteManager = HushPlugin.MuteManager;
			if (muteManager == null)
			{
				return;
			}
			IReadOnlyCollection<string> delegates = muteManager.GetDelegates();
			if (delegates.Count == 0)
			{
				ChatUtils.AddGlobalNotification("Hush: no mute delegates configured.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder($"Hush: {delegates.Count} mute delegate(s):");
			foreach (string item in delegates)
			{
				PlayerDetail val = PlayerUtils.FindPlayerBySteamID(item);
				string text = ((val != null) ? (val.UserNameClean + " (" + item + ")") : item);
				stringBuilder.Append("\n  " + text);
			}
			ChatUtils.AddGlobalNotification(stringBuilder.ToString());
		}
	}
	public class HushDelegateRemoveCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushdelegateremove";

		public string ShortName => "hdr";

		public string Description => "Remove a player from the mute-delegate whitelist (host only). Usage: /hushdelegateremove <player>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushdelegateremove <player>");
				return;
			}
			string text = string.Join(" ", args);
			PlayerDetail val = PlayerUtils.FindPlayerByQuery(text);
			string steamId = val?.SteamID ?? text;
			string text2 = ((val != null) ? val.UserNameClean : null) ?? text;
			PlayerMuteManager muteManager = HushPlugin.MuteManager;
			if (muteManager != null)
			{
				if (muteManager.RemoveDelegate(steamId))
				{
					HushPlugin.SaveMutes();
					ChatUtils.AddGlobalNotification("Hush: " + text2 + " removed from mute delegates.");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: " + text2 + " is not a delegate.");
				}
			}
		}
	}
	public class HushFilterActionCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "hushfilteraction";

		public string Name => "hushfilteraction";

		public string ShortName => "hfa";

		public string Description => "Set Hush filter action command: /hushfilteraction <action>. Valid actions: block, censor.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (HushPlugin.FilterActionConfig != null)
			{
				FilterAction result;
				if (args.Length == 0)
				{
					ChatUtils.AddGlobalNotification("Usage: /hushfilteraction <action>. Valid actions: block, censor.");
				}
				else if (Enum.TryParse<FilterAction>(args[0], ignoreCase: true, out result))
				{
					HushPlugin.FilterActionConfig.Value = result;
					ChatUtils.AddGlobalNotification($"Hush filter action set to {result}.");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Invalid action. Valid actions: block, censor.");
				}
			}
		}
	}
	public class HushGetBansCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushgetbans";

		public string ShortName => "hgb";

		public string Description => "List all banned players (host only). Usage: /hushgetbans";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			BanManager banManager = HushPlugin.BanManager;
			if (banManager == null)
			{
				return;
			}
			List<(string, string)> bans = banManager.GetBans();
			if (bans.Count == 0)
			{
				ChatUtils.AddGlobalNotification("Hush: no players are currently banned.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder($"Hush: {bans.Count} banned player(s):");
			foreach (var (text, text2) in bans)
			{
				stringBuilder.Append("\n  " + text2 + " (" + text + ")");
			}
			ChatUtils.AddGlobalNotification(stringBuilder.ToString());
		}
	}
	public class HushGetMutesCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushgetmutes";

		public string ShortName => "hgm";

		public string Description => "List all currently muted players (host only). Usage: /hushgetmutes";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			PlayerMuteManager muteManager = HushPlugin.MuteManager;
			if (muteManager == null)
			{
				return;
			}
			List<(string, DateTime?)> mutes = muteManager.GetMutes();
			if (mutes.Count == 0)
			{
				ChatUtils.AddGlobalNotification("Hush: no players are currently muted.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder($"Hush: {mutes.Count} muted player(s):");
			foreach (var item3 in mutes)
			{
				string item = item3.Item1;
				DateTime? item2 = item3.Item2;
				PlayerDetail val = PlayerUtils.FindPlayerBySteamID(item);
				string text = ((val != null) ? val.UserNameClean : null) ?? item;
				string text2 = ((!item2.HasValue) ? "permanent" : $"until {item2.Value.ToLocalTime():HH:mm:ss}");
				stringBuilder.Append("\n  " + text + " - " + text2);
			}
			ChatUtils.AddGlobalNotification(stringBuilder.ToString());
		}
	}
	public class HushGetPatternsCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushgetpatterns";

		public string ShortName => "hgp";

		public string Description => "List all raw regex patterns currently in the Hush filter.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager != null)
			{
				string[] patterns = filterManager.GetPatterns();
				if (patterns.Length == 0)
				{
					ChatUtils.AddGlobalNotification("Hush: no patterns in the filter.");
					return;
				}
				Array.Sort(patterns, (IComparer<string>?)StringComparer.Ordinal);
				ChatUtils.AddGlobalNotification(string.Format("Hush patterns ({0}): {1}", patterns.Length, string.Join(", ", patterns)));
			}
		}
	}
	public class HushGetWordsCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushgetwords";

		public string ShortName => "hgw";

		public string Description => "List all literal words currently in the Hush filter.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager != null)
			{
				string[] words = filterManager.GetWords();
				if (words.Length == 0)
				{
					ChatUtils.AddGlobalNotification("Hush: no words in the filter.");
					return;
				}
				Array.Sort(words, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
				ChatUtils.AddGlobalNotification(string.Format("Hush words ({0}): {1}", words.Length, string.Join(", ", words)));
			}
		}
	}
	public class HushLoadFilterCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "hushloadfilter";

		public string Name => "hushloadfilter";

		public string ShortName => "hlf";

		public string Description => "Reload the Hush filter word list from disk: /hushloadfilter.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (HushPlugin.FilterManager != null)
			{
				HushPlugin.FilterManager.Load(HushPlugin.FilterConfigPath);
				ChatUtils.AddGlobalNotification($"Hush filter reloaded ({HushPlugin.FilterManager.Count} entries).");
			}
		}
	}
	public class HushLogVerboseCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "hushlogverbose";

		public string Name => "hushlogverbose";

		public string ShortName => "hlv";

		public string Description => "Toggle verbose BepInEx logging for Hush.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			HushPlugin.VerboseLogging = !HushPlugin.VerboseLogging;
			ChatUtils.AddGlobalNotification("Hush verbose logging is now " + (HushPlugin.VerboseLogging ? "enabled" : "disabled") + ".");
		}
	}
	public class HushMuteCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushmute";

		public string ShortName => "hmu";

		public string Description => "Permanently mute a player (host only). Usage: /hushmute <player>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HostGuard())
			{
				return;
			}
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushmute <player>");
				return;
			}
			string text = string.Join(" ", args);
			PlayerDetail val = PlayerUtils.FindPlayerByQuery(text);
			if (val == null)
			{
				ChatUtils.AddGlobalNotification("Hush: player not found: \"" + text + "\"");
				return;
			}
			PlayerMuteManager muteManager = HushPlugin.MuteManager;
			if (muteManager != null)
			{
				if (muteManager.Mute(val.SteamID))
				{
					HushPlugin.SaveMutes();
					ChatUtils.AddGlobalNotification("Hush: permanently muted " + val.UserNameClean + ".");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: " + val.UserNameClean + " is already permanently muted.");
				}
			}
		}

		internal static bool HostGuard()
		{
			if (PlayerUtils.GetHost()?.SteamID == SteamUtils.GetPlayerSteamID())
			{
				return true;
			}
			ChatUtils.AddGlobalNotification("Hush: only the host can mute players.");
			return false;
		}
	}
	public class HushRemovePatternCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushremovepattern";

		public string ShortName => "hrp";

		public string Description => "Remove a raw regex pattern from the Hush filter. Usage: /hushremovepattern <pattern>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushremovepattern <pattern>");
				return;
			}
			string text = string.Join(" ", args);
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager != null)
			{
				if (filterManager.RemovePattern(text))
				{
					HushPlugin.SaveFilter();
					ChatUtils.AddGlobalNotification("Hush: removed pattern \"" + text + "\".");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: pattern \"" + text + "\" was not in the list.");
				}
			}
		}
	}
	public class HushRemoveWordCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushremoveword";

		public string ShortName => "hrw";

		public string Description => "Remove a literal word from the Hush filter. Usage: /hushremoveword <word>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushremoveword <word>");
				return;
			}
			string text = string.Join(" ", args);
			ChatFilterManager filterManager = HushPlugin.FilterManager;
			if (filterManager != null)
			{
				if (filterManager.Remove(text))
				{
					HushPlugin.SaveFilter();
					ChatUtils.AddGlobalNotification("Hush: removed word \"" + text + "\".");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: \"" + text + "\" was not in the list.");
				}
			}
		}
	}
	public class HushTempMuteCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushtempmute";

		public string ShortName => "htm";

		public string Description => "Temporarily mute a player. Host executes immediately; delegates relay to host. Usage: /hushtempmute <player> <duration>  e.g. /hushtempmute bob 10m";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushtmute <player> <duration>  e.g. /hushtmute bob 10m");
				return;
			}
			string text = args[^1];
			string text2 = string.Join(" ", args, 0, args.Length - 1);
			PlayerDetail val = PlayerUtils.FindPlayerByQuery(text2);
			if (val == null)
			{
				ChatUtils.AddGlobalNotification("Hush: player not found: \"" + text2 + "\"");
				return;
			}
			TimeSpan timeSpan = default(TimeSpan);
			if (!TimeUtils.TryParseDuration(text, ref timeSpan) || timeSpan <= TimeSpan.Zero)
			{
				ChatUtils.AddGlobalNotification("Hush: invalid duration \"" + text + "\". Use e.g. 10m, 1h30m, 30s.");
				return;
			}
			PlayerMuteManager muteManager = HushPlugin.MuteManager;
			if (muteManager == null)
			{
				return;
			}
			string playerSteamID = SteamUtils.GetPlayerSteamID();
			if (PlayerUtils.GetHost()?.SteamID == playerSteamID)
			{
				muteManager.MuteFor(val.SteamID, timeSpan);
				HushPlugin.SaveMutes();
				ChatUtils.AddGlobalNotification("Hush: muted " + val.UserNameClean + " for " + FormatDuration(timeSpan) + ".");
			}
			else
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				if ((Object)(object)i == (Object)null)
				{
					ChatUtils.AddGlobalNotification("Hush: not connected - cannot relay mute request.");
					return;
				}
				string s = $"hush:tmute:{val.SteamID}:{(int)timeSpan.TotalSeconds}";
				Vector3 val2 = (((Object)(object)i.MainPlayer != (Object)null) ? i.MainPlayer.position : Vector3.zero);
				i.SendMessageAsync(Encoding.Unicode.GetBytes(s), Encoding.Unicode.GetBytes(i.UserName ?? string.Empty), false, val2, SteamUtils.GetPlayerSteamID(), default(RPCInfo));
				ChatUtils.AddGlobalNotification("Hush: mute request sent for " + val.UserNameClean + ".");
			}
		}

		private static string FormatDuration(TimeSpan ts)
		{
			if (ts.TotalSeconds < 60.0)
			{
				return $"{(int)ts.TotalSeconds}s";
			}
			if (ts.TotalMinutes < 60.0)
			{
				return $"{(int)ts.TotalMinutes}m";
			}
			if (ts.TotalHours < 24.0)
			{
				string text = $"{ts.Hours}h";
				return (ts.Minutes > 0) ? $"{text} {ts.Minutes}m" : text;
			}
			return $"{(int)ts.TotalDays}d";
		}
	}
	public class HushToggleCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "hushtoggle";

		public string Name => "hushtoggle";

		public string ShortName => "ht";

		public string Description => "Toggle Hush feature on/off. ";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (HushPlugin.EnableFeature != null)
			{
				HushPlugin.EnableFeature.Value = !HushPlugin.EnableFeature.Value;
				ChatUtils.AddGlobalNotification("Hush feature is now " + (HushPlugin.EnableFeature.Value ? "enabled" : "disabled") + ".");
			}
		}
	}
	public class HushUnbanCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushunban";

		public string ShortName => "hub";

		public string Description => "Unban a player (host only). Accepts player query or raw Steam ID. Usage: /hushunban <player|steamid>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			if (!HushMuteCommand.HostGuard())
			{
				return;
			}
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushunban <player|steamid>");
				return;
			}
			string text = string.Join(" ", args);
			BanManager banManager = HushPlugin.BanManager;
			if (banManager != null)
			{
				PlayerDetail val = PlayerUtils.FindPlayerByQuery(text);
				string steamId = val?.SteamID ?? text;
				string text2 = ((val != null) ? val.UserNameClean : null) ?? text;
				if (banManager.Unban(steamId))
				{
					ChatUtils.AddGlobalNotification("Hush: unbanned " + text2 + ".");
				}
				else
				{
					ChatUtils.AddGlobalNotification("Hush: " + text2 + " is not banned.");
				}
			}
		}
	}
	public class HushUnmuteCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushunmute";

		public string ShortName => "hum";

		public string Description => "Unmute a player. Host executes directly; delegates relay to host. Usage: /hushunmute <player|steamid>";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Usage: /hushunmute <player|steamid>");
				return;
			}
			string text = string.Join(" ", args);
			PlayerDetail val = PlayerUtils.FindPlayerByQuery(text);
			string text2 = val?.SteamID ?? text;
			string text3 = ((val != null) ? val.UserNameClean : null) ?? text;
			if (PlayerUtils.GetHost()?.SteamID == SteamUtils.GetPlayerSteamID())
			{
				PlayerMuteManager muteManager = HushPlugin.MuteManager;
				if (muteManager != null)
				{
					if (muteManager.Unmute(text2))
					{
						HushPlugin.SaveMutes();
						ChatUtils.AddGlobalNotification("Hush: unmuted " + text3 + ".");
					}
					else
					{
						ChatUtils.AddGlobalNotification("Hush: " + text3 + " is not muted.");
					}
				}
			}
			else
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				if ((Object)(object)i == (Object)null)
				{
					ChatUtils.AddGlobalNotification("Hush: not connected - cannot relay unmute request.");
					return;
				}
				string s = "hush:unmute:" + text2;
				Vector3 val2 = (((Object)(object)i.MainPlayer != (Object)null) ? i.MainPlayer.position : Vector3.zero);
				i.SendMessageAsync(Encoding.Unicode.GetBytes(s), Encoding.Unicode.GetBytes(i.UserName ?? string.Empty), false, val2, SteamUtils.GetPlayerSteamID(), default(RPCInfo));
				ChatUtils.AddGlobalNotification("Hush: unmute request sent for " + text3 + ".");
			}
		}
	}
	public class HushVersionCommand : IChatCommand, IComparable<IChatCommand>
	{
		public string Name => "hushversion";

		public string ShortName => "hv";

		public string Description => "Print the installed Hush version.";

		public string Namespace => "hush";

		public void Execute(string[] args)
		{
			ChatUtils.AddGlobalNotification("Hush v0.1.13");
		}
	}
}