Decompiled source of Remind v0.0.5

AndrewLin.Remind.dll

Decompiled a day ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PurrNet;
using Remind.Core;
using Remind.Core.Commands;
using Remind.Core.Util;
using Remind.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AndrewLin.Remind")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Remind: A mod for On-Together with in-game utility commands including scheduled reminders (/remindmein, /remindlocalin, /remindglobalin), and more. Use /remindhelp")]
[assembly: AssemblyFileVersion("0.0.5.0")]
[assembly: AssemblyInformationalVersion("0.0.5+f600d634ba3a8222bd27969c94736eace76b9fd1")]
[assembly: AssemblyProduct("AndrewLin.Remind")]
[assembly: AssemblyTitle("AndrewLin.Remind")]
[assembly: AssemblyVersion("0.0.5.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 Remind
{
	public static class BuildInfo
	{
		public const string Version = "0.0.5";
	}
	[BepInPlugin("com.andrewlin.ontogether.remind", "Remind", "0.0.5")]
	public class RemindPlugin : BaseUnityPlugin
	{
		public const int OriginalNotificationLimit = 99;

		public const int DefaultNotificationLimit = 300;

		public const int MaxNotificationLimit = 999;

		public const int DefaultChatSinkLocalRange = 5;

		public const int DefaultGlobalChatMessageLimit = 50;

		public const int DefaultLocalChatMessageLimit = 25;

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

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

		public const string ModName = "Remind";

		public const string ModVersion = "0.0.5";

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

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

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

		public static ChatCommandManager? CommandManager { get; private set; }

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

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

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Remind v0.0.5 is loaded!");
			InitConfig();
			new Harmony("com.andrewlin.ontogether.remind").PatchAll(typeof(TextChannelManagerPatch));
			CommandManager = new ChatCommandManager();
			ScheduledTaskManager = new ScheduledTaskManager();
		}

		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.");
			BroadcastCreation = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BroadcastCreation", true, "Broadcast the creation of reminders in chat.");
		}

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

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

		public const string PLUGIN_NAME = "AndrewLin.Remind";

		public const string PLUGIN_VERSION = "0.0.5";
	}
}
namespace Remind.Patches
{
	[HarmonyPatch(typeof(TextChannelManager))]
	public class TextChannelManagerPatch
	{
		private const int _selfDistance = 0;

		[HarmonyPatch("OnEnterPressed")]
		[HarmonyPrefix]
		public static void OnEnterPressedPrefix()
		{
			string text = MonoSingleton<UIManager>.I.MessageInput.text;
			if (!string.IsNullOrEmpty(text) && text.StartsWith('/') && RemindPlugin.EnableFeature != null && (RemindPlugin.EnableFeature.Value || text.Contains("remindtoggle")))
			{
				bool flag = RemindPlugin.CommandManager?.ProcessInput(text) ?? false;
				ConfigEntry<bool>? showCommand = RemindPlugin.ShowCommand;
				if (showCommand != null && !showCommand.Value && flag)
				{
					ChatUtils.CleanCommand();
				}
			}
		}
	}
}
namespace Remind.Core
{
	public class ChatCommandArgs
	{
		public static readonly ChatCommandArgs EMPTY = new ChatCommandArgs("", new string[0]);

		public string Name { get; }

		public string[] Args { get; }

		public ChatCommandArgs(string name, string[] args)
		{
			Name = name;
			Args = args;
		}

		public static bool TryParse(string input, out ChatCommandArgs result)
		{
			if (string.IsNullOrWhiteSpace(input) || !input.StartsWith('/'))
			{
				result = EMPTY;
				return false;
			}
			string text = input.Trim();
			string[] array = text.Substring(1, text.Length - 1).Split(' ', StringSplitOptions.RemoveEmptyEntries);
			string name = array[0].ToLower();
			string[] args = array.Skip(1).ToArray();
			result = new ChatCommandArgs(name, args);
			return true;
		}

		public override string ToString()
		{
			return "/" + Name + " " + string.Join(' ', Args);
		}
	}
	public class ChatCommandManager
	{
		private readonly ManualLogSource _log = Logger.CreateLogSource("Remind.CM");

		private readonly Dictionary<string, IChatCommand> _commands;

		public ChatCommandManager()
		{
			IEnumerable<Type> enumerable = from t in Assembly.GetExecutingAssembly().GetTypes()
				where typeof(IChatCommand).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract
				select t;
			_commands = new Dictionary<string, IChatCommand>();
			foreach (Type item in enumerable)
			{
				IChatCommand command = Activator.CreateInstance(item) as IChatCommand;
				Register(command);
			}
			InitializeHelpCommand(_commands);
		}

		public void Register(IChatCommand? command)
		{
			if (command == null || string.IsNullOrWhiteSpace(command.Name) || string.IsNullOrWhiteSpace(command.ShortName))
			{
				_log.LogWarning((object)("Attempted to register invalid command: " + (command?.GetType().Name ?? "null")));
				return;
			}
			_commands[command.Name] = command;
			_commands[command.ShortName] = command;
			_log.LogInfo((object)("Registered command: /" + command.Name + " (/" + command.ShortName + ")"));
		}

		private static void InitializeHelpCommand(Dictionary<string, IChatCommand> commands)
		{
			if (commands.TryGetValue("remindhelp", out IChatCommand value))
			{
				(value as RemindHelpCommand)?.Initialize(commands);
			}
		}

		public bool ProcessInput(string input)
		{
			if (!ChatCommandArgs.TryParse(input, out ChatCommandArgs result))
			{
				return false;
			}
			if (_commands.TryGetValue(result.Name, out IChatCommand value))
			{
				_log.LogInfo((object)$"Executing command: {result}");
				try
				{
					value.Execute(result.Args);
				}
				catch (Exception ex)
				{
					ChatUtils.AddGlobalNotification($"Error executing '{result}': {ex.Message}");
				}
				return true;
			}
			return false;
		}
	}
	public interface IChatCommand : IComparable<IChatCommand>
	{
		string Name { get; }

		string ShortName => "";

		string Description { get; }

		void Execute(string[] args);

		new string ToString()
		{
			return Name;
		}

		int IComparable<IChatCommand>.CompareTo(IChatCommand? other)
		{
			if (other == null)
			{
				return 1;
			}
			return string.Compare(Name, other.Name, StringComparison.Ordinal);
		}
	}
	public class ScheduledTask
	{
		private readonly Action _action;

		public DateTime FireAt { get; }

		public bool IsCancelled { get; private set; }

		internal ScheduledTask(DateTime fireAt, Action action)
		{
			FireAt = fireAt;
			_action = action;
		}

		public void Cancel()
		{
			IsCancelled = true;
		}

		internal void Execute()
		{
			_action();
		}
	}
	public class ScheduledTaskManager
	{
		private readonly List<ScheduledTask> _tasks = new List<ScheduledTask>();

		private readonly ManualLogSource _log = Logger.CreateLogSource("Remind.STM");

		public int PendingCount => _tasks.Count;

		public ScheduledTask ScheduleIn(TimeSpan delay, Action action)
		{
			return ScheduleAt(DateTime.UtcNow + delay, action);
		}

		public bool TryScheduleIn(string delay, Action action, out ScheduledTask? task, out string error)
		{
			if (TryParseDelay(delay, out var result) && result > TimeSpan.Zero)
			{
				task = ScheduleIn(result, action);
				error = "";
				return true;
			}
			task = null;
			error = "Invalid duration: '" + delay + "'. Use ISO 8601 (1h30m, 15s, PT1H30M) or hh:mm:ss.";
			return false;
		}

		private static bool TryParseDelay(string input, out TimeSpan result)
		{
			string text = input.ToUpperInvariant();
			string s = (text.StartsWith("P") ? text : ("PT" + text));
			try
			{
				result = XmlConvert.ToTimeSpan(s);
				return true;
			}
			catch
			{
			}
			return TimeSpan.TryParse(input, out result);
		}

		private ScheduledTask ScheduleAt(DateTime fireAt, Action action)
		{
			ScheduledTask scheduledTask = new ScheduledTask(fireAt, action);
			_tasks.Add(scheduledTask);
			return scheduledTask;
		}

		public bool TryScheduleAt(string fireAt, Action action, out ScheduledTask? task, out string error)
		{
			if (!DateTime.TryParse(fireAt, out var result))
			{
				task = null;
				error = "Invalid time: '" + fireAt + "'. Use HH:mm, HH:mm:ss, or yyyy-MM-dd HH:mm.";
				return false;
			}
			DateTime dateTime = ((result.Kind == DateTimeKind.Utc) ? result : result.ToUniversalTime());
			if (dateTime <= DateTime.UtcNow)
			{
				task = null;
				error = "Time '" + fireAt + "' is in the past.";
				return false;
			}
			task = ScheduleAt(dateTime, action);
			error = "";
			return true;
		}

		public void Tick()
		{
			if (_tasks.Count == 0)
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			for (int num = _tasks.Count - 1; num >= 0; num--)
			{
				ScheduledTask scheduledTask = _tasks[num];
				if (scheduledTask.IsCancelled || scheduledTask.FireAt <= utcNow)
				{
					_tasks.RemoveAt(num);
					if (!scheduledTask.IsCancelled)
					{
						try
						{
							scheduledTask.Execute();
						}
						catch (Exception arg)
						{
							_log.LogError((object)$"[ScheduledTaskManager] Task threw: {arg}");
						}
					}
				}
			}
		}
	}
}
namespace Remind.Core.Util
{
	public static class ChatUtils
	{
		private static readonly Regex _commonTMPTagRegex = new Regex("</?(?:color|b|i|u|s|sup|sub|size|alpha|mark|uppercase|lowercase|smallcaps|font|voffset|nobr|noparse|sprite|link|align|rotate|#[0-9a-fA-F]{3,8})[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		public static void AddGlobalNotification(string text)
		{
			TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
			if (!((Object)(object)i == (Object)null))
			{
				i.AddNotification(text);
			}
		}

		public static void SendMessageAsync(string userName, string text, bool Islocal = false)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
			Transform mainPlayer = i.MainPlayer;
			byte[] bytes = Encoding.Unicode.GetBytes(text.Substring(0, Math.Min(text.Length, 250)));
			byte[] bytes2 = Encoding.Unicode.GetBytes(userName);
			string steamPlayerIdString = PlayerUtils.GetSteamPlayerIdString();
			i.SendMessageAsync(bytes, bytes2, Islocal, mainPlayer.position, steamPlayerIdString, default(RPCInfo));
		}

		public static void CleanCommand()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			LockState lockState = (LockState)(NetworkSingleton<MusicManager>.I.IsActive ? 5 : 0);
			MonoSingleton<TaskManager>.I.SetLockState(lockState);
			EventSystem.current.SetSelectedGameObject((GameObject)null);
			string text = MonoSingleton<UIManager>.I.MessageInput.text;
			if (!text.StartsWith("/help") || text == "/help remind")
			{
				MonoSingleton<UIManager>.I.MessageInput.text = "";
			}
		}

		public static string CleanTMPTags(string input)
		{
			return _commonTMPTagRegex.Replace(input, string.Empty);
		}

		public static void UISendMessage(string text)
		{
			TMP_InputField messageInput = MonoSingleton<UIManager>.I.MessageInput;
			messageInput.text = text;
			((UnityEvent<string>)(object)messageInput.onSubmit).Invoke(messageInput.text);
		}

		public static void DispatchIncomingText(string text)
		{
			string text2 = text;
			if (string.IsNullOrWhiteSpace(text2))
			{
				return;
			}
			text2 = text2.Trim();
			if (text2.StartsWith("/"))
			{
				RemindPlugin.RunOnMainThread(delegate
				{
					if (text2.StartsWith("/remind"))
					{
						ChatCommandManager? commandManager = RemindPlugin.CommandManager;
						if (commandManager != null && commandManager.ProcessInput(text2))
						{
							return;
						}
					}
					UISendMessage(text2);
				});
			}
			else
			{
				SendMessageAsync(PlayerUtils.GetUserName(), text2);
			}
		}
	}
	public static class PlayerUtils
	{
		private static ManualLogSource _log = Logger.CreateLogSource("Remind.PU");

		private static string _steamPlayerIdString = string.Empty;

		public static string GetUserName()
		{
			return NetworkSingleton<TextChannelManager>.I.UserName;
		}

		public static string GetUserNameNoFormat()
		{
			return ChatUtils.CleanTMPTags(GetUserName());
		}

		public static string GetSteamPlayerIdString()
		{
			if (!string.IsNullOrEmpty(_steamPlayerIdString))
			{
				return _steamPlayerIdString;
			}
			_steamPlayerIdString = Traverse.Create((object)NetworkSingleton<TextChannelManager>.I).Field<string>("_playerId").Value ?? string.Empty;
			return _steamPlayerIdString;
		}

		public static PlayerID? GetPlayerId()
		{
			try
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				return (i != null) ? ((NetworkIdentity)i).localPlayer : null;
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"Error getting player ID: {arg}");
				return null;
			}
		}

		public static PlayerID? GetPlayerIdForced()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				return (i != null) ? new PlayerID?(((NetworkIdentity)i).localPlayerForced) : null;
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"Error getting player ID: {arg}");
				return null;
			}
		}
	}
}
namespace Remind.Core.Commands
{
	public class RemindBroadcastCreationCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindbroadcastcreation";

		public string Name => "remindbroadcastcreation";

		public string ShortName => "rbc";

		public string Description => "Toggle Remind broadcast creation on/off. ";

		public void Execute(string[] args)
		{
			if (RemindPlugin.BroadcastCreation != null)
			{
				RemindPlugin.BroadcastCreation.Value = !RemindPlugin.BroadcastCreation.Value;
				ChatUtils.AddGlobalNotification("Remind broadcast creation is now " + (RemindPlugin.BroadcastCreation.Value ? "enabled" : "disabled") + ".");
			}
		}
	}
	public class RemindGlobalAtCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindglobalat";

		public string Name => "remindglobalat";

		public string ShortName => "rga";

		public string Description => "Send a global chat message at a specific time. Usage: /remindglobalat [HH:mm] [message]";

		public void Execute(string[] args)
		{
			if (RemindPlugin.ScheduledTaskManager == null)
			{
				ChatUtils.AddGlobalNotification("ScheduledTaskManager is not initialized.");
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /remindglobalat [HH:mm] [message]");
				return;
			}
			string message = string.Join(" ", args, 1, args.Length - 1);
			if (string.IsNullOrWhiteSpace(message))
			{
				ChatUtils.AddGlobalNotification("Please enter a valid message.");
				return;
			}
			string text = args[0];
			string username = PlayerUtils.GetUserNameNoFormat();
			if (!DateTime.TryParse(text, out var result))
			{
				ChatUtils.AddGlobalNotification("Invalid time format. Please use HH:mm.");
				return;
			}
			bool isLocal = false;
			DateTime dateTime = ((result.Kind == DateTimeKind.Utc) ? result : result.ToUniversalTime());
			if (!RemindPlugin.ScheduledTaskManager.TryScheduleAt(text, delegate
			{
				ChatUtils.SendMessageAsync("Remind" + username, message, isLocal);
			}, out ScheduledTask _, out string error))
			{
				ChatUtils.AddGlobalNotification(error);
				return;
			}
			ConfigEntry<bool>? broadcastCreation = RemindPlugin.BroadcastCreation;
			if (broadcastCreation != null && broadcastCreation.Value)
			{
				ChatUtils.SendMessageAsync("Remind", $"@{username} at {dateTime:HH:mm}UTC", isLocal);
				ChatUtils.SendMessageAsync("Remind", "with '" + message + "'", isLocal);
			}
		}
	}
	public class RemindGlobalInCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindglobalin";

		public string Name => "remindglobalin";

		public string ShortName => "rgi";

		public string Description => "Send a global chat message after a delay. Usage: /remindglobalin [hh:mm:ss] [message]";

		public void Execute(string[] args)
		{
			if (RemindPlugin.ScheduledTaskManager == null)
			{
				ChatUtils.AddGlobalNotification("ScheduledTaskManager is not initialized.");
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /remindglobalin [hh:mm:ss] [message]");
				return;
			}
			string message = string.Join(" ", args, 1, args.Length - 1);
			if (string.IsNullOrWhiteSpace(message))
			{
				ChatUtils.AddGlobalNotification("Please enter a valid message.");
				return;
			}
			string text = args[0];
			string username = PlayerUtils.GetUserNameNoFormat();
			bool islocal = false;
			if (!RemindPlugin.ScheduledTaskManager.TryScheduleIn(text, delegate
			{
				ChatUtils.SendMessageAsync("Remind" + username, message);
			}, out ScheduledTask _, out string error))
			{
				ChatUtils.AddGlobalNotification(error);
				return;
			}
			ConfigEntry<bool>? broadcastCreation = RemindPlugin.BroadcastCreation;
			if (broadcastCreation != null && broadcastCreation.Value)
			{
				ChatUtils.SendMessageAsync("Remind" + username, "scheduled in " + text + " -> " + message, islocal);
			}
		}
	}
	public class RemindHelpCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindhelp";

		private Dictionary<string, IChatCommand>? commandsMap;

		public string Name => "remindhelp";

		public string ShortName => "rh";

		public string Description => "Lists all available commands.";

		public void Initialize(Dictionary<string, IChatCommand> commandsMap)
		{
			this.commandsMap = commandsMap;
		}

		public void Execute(string[] args)
		{
			if (commandsMap == null)
			{
				ChatUtils.AddGlobalNotification("No commands available!");
				return;
			}
			SortedSet<IChatCommand> sortedSet = new SortedSet<IChatCommand>(commandsMap.Values);
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("Remind Version (0.0.5).");
				ChatUtils.AddGlobalNotification("For detailed usage of a specific command, type /remindhelp (/fh) [command].");
				ChatUtils.AddGlobalNotification("Available commands:");
				foreach (IChatCommand item in sortedSet)
				{
					ChatUtils.AddGlobalNotification("/" + item.Name + " (/" + item.ShortName + ")");
				}
			}
			if (args.Length != 1)
			{
				return;
			}
			string text = args[0].ToLower();
			if (text == "verbose")
			{
				ChatUtils.AddGlobalNotification("Remind Version (0.0.5).");
				ChatUtils.AddGlobalNotification("For detailed usage of a specific command, type /remindhelp (/fh) [command].");
				ChatUtils.AddGlobalNotification("Available commands:");
				{
					foreach (IChatCommand item2 in sortedSet)
					{
						ChatUtils.AddGlobalNotification("/" + item2.Name + " (/" + item2.ShortName + "): " + item2.Description);
					}
					return;
				}
			}
			if (commandsMap.TryGetValue(text, out IChatCommand value))
			{
				ChatUtils.AddGlobalNotification("/" + value.Name + " (/" + value.ShortName + "): " + value.Description);
			}
			else
			{
				ChatUtils.AddGlobalNotification("Command not found: " + text);
			}
		}
	}
	public class RemindLocalAtCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindlocalat";

		public string Name => "remindlocalat";

		public string ShortName => "rla";

		public string Description => "Send a local chat message at a specific time. Usage: /remindlocalat [HH:mm] [message]";

		public void Execute(string[] args)
		{
			if (RemindPlugin.ScheduledTaskManager == null)
			{
				ChatUtils.AddGlobalNotification("ScheduledTaskManager is not initialized.");
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /remindlocalat [HH:mm] [message]");
				return;
			}
			string message = string.Join(" ", args, 1, args.Length - 1);
			if (string.IsNullOrWhiteSpace(message))
			{
				ChatUtils.AddGlobalNotification("Please enter a valid message.");
				return;
			}
			string text = args[0];
			string username = PlayerUtils.GetUserNameNoFormat();
			if (!DateTime.TryParse(text, out var result))
			{
				ChatUtils.AddGlobalNotification("Invalid time format. Please use HH:mm.");
				return;
			}
			ChatUtils.AddGlobalNotification("[remindlocalat] Reminder at " + text + ": \"" + message + "\"");
			bool isLocal = true;
			DateTime dateTime = ((result.Kind == DateTimeKind.Utc) ? result : result.ToUniversalTime());
			if (!RemindPlugin.ScheduledTaskManager.TryScheduleAt(text, delegate
			{
				ChatUtils.SendMessageAsync("Remind" + username, message, isLocal);
			}, out ScheduledTask _, out string error))
			{
				ChatUtils.AddGlobalNotification(error);
				return;
			}
			ConfigEntry<bool>? broadcastCreation = RemindPlugin.BroadcastCreation;
			if (broadcastCreation != null && broadcastCreation.Value)
			{
				ChatUtils.SendMessageAsync("Remind" + username, $"scheduled at {dateTime:HH:mm}UTC -> {message}", isLocal);
			}
		}
	}
	public class RemindLocalInCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindlocalin";

		public string Name => "remindlocalin";

		public string ShortName => "rli";

		public string Description => "Send a local chat message after a delay. Usage: /remindlocalin [hh:mm:ss] [message]";

		public void Execute(string[] args)
		{
			if (RemindPlugin.ScheduledTaskManager == null)
			{
				ChatUtils.AddGlobalNotification("ScheduledTaskManager is not initialized.");
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /remindlocalin [hh:mm:ss] [message]");
				return;
			}
			string message = string.Join(" ", args, 1, args.Length - 1);
			if (string.IsNullOrWhiteSpace(message))
			{
				ChatUtils.AddGlobalNotification("Please enter a valid message.");
				return;
			}
			string text = args[0];
			string username = PlayerUtils.GetUserNameNoFormat();
			bool islocal = true;
			if (!RemindPlugin.ScheduledTaskManager.TryScheduleIn(text, delegate
			{
				ChatUtils.SendMessageAsync("Remind" + username, message);
			}, out ScheduledTask _, out string error))
			{
				ChatUtils.AddGlobalNotification(error);
				return;
			}
			ConfigEntry<bool>? broadcastCreation = RemindPlugin.BroadcastCreation;
			if (broadcastCreation != null && broadcastCreation.Value)
			{
				ChatUtils.SendMessageAsync("Remind" + username, "scheduled in " + text + " -> " + message, islocal);
			}
		}
	}
	public class RemindMeAtCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindmeat";

		public string Name => "remindmeat";

		public string ShortName => "rma";

		public string Description => "Show a private notification at a specific time. Usage: /remindmeat [HH:mm] [message]";

		public void Execute(string[] args)
		{
			if (RemindPlugin.ScheduledTaskManager == null)
			{
				ChatUtils.AddGlobalNotification("ScheduledTaskManager is not initialized.");
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /remindmeat [HH:mm] [message]");
				return;
			}
			string message = string.Join(" ", args, 1, args.Length - 1);
			if (string.IsNullOrWhiteSpace(message))
			{
				ChatUtils.AddGlobalNotification("Please enter a valid message.");
				return;
			}
			if (!RemindPlugin.ScheduledTaskManager.TryScheduleAt(args[0], delegate
			{
				ChatUtils.AddGlobalNotification(message);
			}, out ScheduledTask _, out string error))
			{
				ChatUtils.AddGlobalNotification(error);
				return;
			}
			ChatUtils.AddGlobalNotification("[remindmeat] Reminder at " + args[0] + ": \"" + message + "\"");
		}
	}
	public class RemindMeInCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindmein";

		public string Name => "remindmein";

		public string ShortName => "rmi";

		public string Description => "Show a private notification after a delay. Usage: /remindmein [hh:mm:ss] [message]";

		public void Execute(string[] args)
		{
			if (RemindPlugin.ScheduledTaskManager == null)
			{
				ChatUtils.AddGlobalNotification("ScheduledTaskManager is not initialized.");
				return;
			}
			if (args.Length < 2)
			{
				ChatUtils.AddGlobalNotification("Usage: /remindmein [hh:mm:ss] [message]");
				return;
			}
			string message = string.Join(" ", args, 1, args.Length - 1);
			if (string.IsNullOrWhiteSpace(message))
			{
				ChatUtils.AddGlobalNotification("Please enter a valid message.");
				return;
			}
			string text = args[0];
			if (!RemindPlugin.ScheduledTaskManager.TryScheduleIn(text, delegate
			{
				ChatUtils.AddGlobalNotification(message);
			}, out ScheduledTask _, out string error))
			{
				ChatUtils.AddGlobalNotification(error);
				return;
			}
			ChatUtils.AddGlobalNotification("[remindmein] Reminder in " + text + ": \"" + message + "\"");
		}
	}
	public class ShowRemindCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindshowcommand";

		public string Name => "remindshowcommand";

		public string ShortName => "rsc";

		public string Description
		{
			get
			{
				ConfigEntry<bool>? showCommand = RemindPlugin.ShowCommand;
				return "Toggle Remind show/hide Remind command in chat.If enabled, user command such as '/roll' will be shown in chat. Current: " + ((showCommand != null && showCommand.Value) ? "shown" : "hidden");
			}
		}

		public void Execute(string[] args)
		{
			if (RemindPlugin.ShowCommand != null)
			{
				RemindPlugin.ShowCommand.Value = !RemindPlugin.ShowCommand.Value;
				ChatUtils.AddGlobalNotification("Remind user command is now " + (RemindPlugin.ShowCommand.Value ? "shown" : "hidden") + ".");
			}
		}
	}
	public class RemindToggleCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "remindtoggle";

		public string Name => "remindtoggle";

		public string ShortName => "rt";

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

		public void Execute(string[] args)
		{
			if (RemindPlugin.EnableFeature != null)
			{
				RemindPlugin.EnableFeature.Value = !RemindPlugin.EnableFeature.Value;
				ChatUtils.AddGlobalNotification("Remind feature is now " + (RemindPlugin.EnableFeature.Value ? "enabled" : "disabled") + ".");
			}
		}
	}
}