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");
}
}
}