using System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading.Tasks; using Alpha; using Alpha.Core.Command; using Alpha.Core.Util; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Fomo; using Fomo.Core; using FomoChatLog.Commands; using FomoChatLog.Core; using FomoChatLog.Sinks; using Microsoft.CodeAnalysis; [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("FomoChatLog: A Fomo submod that logs On-Together chat messages to a local text file. Use /fomohelp and look for /fomochatlog commands")] [assembly: AssemblyFileVersion("1.2.9.0")] [assembly: AssemblyInformationalVersion("1.2.9+93bf6c254192ca3bde9494f062dab68782362cda")] [assembly: AssemblyProduct("AndrewLin.FomoChatLog")] [assembly: AssemblyTitle("AndrewLin.FomoChatLog")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/andrewlimforfun/ot-mods")] [assembly: AssemblyVersion("1.2.9.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 FomoChatLog { public static class BuildInfo { public const string Version = "1.2.9"; } [BepInPlugin("com.andrewlin.ontogether.fomochatlog", "FomoChatLog", "1.2.9")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class FomoChatLogPlugin : BaseUnityPlugin { public const string ModGUID = "com.andrewlin.ontogether.fomochatlog"; public const string ModName = "FomoChatLog"; public const string ModVersion = "1.2.9"; private static readonly string DefaultChatLogPathRaw = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "on-together/fomo/on_together_chat_log.txt"); internal static ManualLogSource Log = null; public static ConfigEntry? EnableFeature { get; private set; } private static ConfigEntry? ChatLogPathRaw { get; set; } public static ConfigEntry? MaxLogDays { get; private set; } public static ConfigEntry? MessageFormat { get; private set; } public static ConfigEntry? NotificationFormat { get; private set; } public static ChatLogManager ChatLog { get; private set; } = null; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"FomoChatLog v1.2.9 loading…"); InitConfig(); ChatLog = new ChatLogManager(Log, EnableFeature, ChatLogPathRaw, MaxLogDays); ChatLog.Initialize(); ChatSinkManager sinkManager = FomoPlugin.SinkManager; if (sinkManager != null) { sinkManager.Register((IChatSink)(object)new LogFileChatSink()); } Log.LogInfo((object)"LogFileChatSink registered with Fomo SinkManager."); ChatCommandManager commandManager = AlphaPlugin.CommandManager; if (commandManager != null) { commandManager.Register((IChatCommand)(object)new FomoChatLogGetPathCommand()); } ChatCommandManager commandManager2 = AlphaPlugin.CommandManager; if (commandManager2 != null) { commandManager2.Register((IChatCommand)(object)new FomoChatLogSetPathCommand()); } ChatCommandManager commandManager3 = AlphaPlugin.CommandManager; if (commandManager3 != null) { commandManager3.Register((IChatCommand)(object)new FomoChatLogToggleCommand()); } ChatCommandManager commandManager4 = AlphaPlugin.CommandManager; if (commandManager4 != null) { commandManager4.Register((IChatCommand)(object)new FomoChatLogMessageFormatCommand()); } ChatCommandManager commandManager5 = AlphaPlugin.CommandManager; if (commandManager5 != null) { commandManager5.Register((IChatCommand)(object)new FomoChatLogNotificationFormatCommand()); } Log.LogInfo((object)"FomoChatLog loaded."); } private void InitConfig() { EnableFeature = ((BaseUnityPlugin)this).Config.Bind("General", "EnableFeature", true, "Enable or disable chat file logging."); ChatLogPathRaw = ((BaseUnityPlugin)this).Config.Bind("General", "ChatLogPath", DefaultChatLogPathRaw, "Base path for chat log files. Supports ~ and environment variables.\nThe date (yyyy-MM-dd) is appended automatically before the file extension."); MaxLogDays = ((BaseUnityPlugin)this).Config.Bind("General", "MaxLogDays", 5, "Number of daily log files to retain. Older files are deleted on startup."); MessageFormat = ((BaseUnityPlugin)this).Config.Bind("Formatting", "MessageFormat", "[{timestamp:yyyy-MM-dd HH:mm:ss}] [{channel}] {username}: {message}", "Format string for chat messages sent to Telegram.\nPlaceholders: {timestamp[:fmt]}, {channel[:short]}, {username}, {message}, {distance}, {source}, {playerid}"); NotificationFormat = ((BaseUnityPlugin)this).Config.Bind("Formatting", "NotificationFormat", "[{timestamp:yyyy-MM-dd HH:mm:ss}] {message}", "Format string for system notifications sent to Telegram.\nPlaceholders: {timestamp[:fmt]}, {channel[:short]}, {username}, {message}, {distance}, {source}, {playerid}"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "AndrewLin.FomoChatLog"; public const string PLUGIN_NAME = "AndrewLin.FomoChatLog"; public const string PLUGIN_VERSION = "1.2.9"; } } namespace FomoChatLog.Sinks { public class LogFileChatSink : IChatSink { private readonly ManualLogSource _log = Logger.CreateLogSource("FomoChatLog.LFCS"); private const int WarnLimit = 20; private int _warnCount = 0; public async Task SendAsync(ChatEntry entry) { ConfigEntry? enableFeature = FomoChatLogPlugin.EnableFeature; if (enableFeature == null || !enableFeature.Value) { return; } try { string logPath = FomoChatLogPlugin.ChatLog.GetTodayPath(); string format = ((!entry.IsNotification) ? FomoChatLogPlugin.MessageFormat?.Value : FomoChatLogPlugin.NotificationFormat?.Value); string line = ChatEntryFormatter.Format(entry, format); await File.AppendAllTextAsync(logPath, line + Environment.NewLine); } catch (Exception ex) { _warnCount++; if (_warnCount <= 20) { _log.LogWarning((object)("Failed to write chat log: " + ex.Message)); } else if (_warnCount == 21) { _log.LogWarning((object)"Further warnings about chat log writing will be suppressed."); } } } } } namespace FomoChatLog.Core { public class ChatLogManager { private readonly ManualLogSource _log; private readonly ConfigEntry _enableFeature; private readonly ConfigEntry _pathRaw; private readonly ConfigEntry _maxLogDays; public ChatLogManager(ManualLogSource log, ConfigEntry enableFeature, ConfigEntry pathRaw, ConfigEntry maxLogDays) { _log = log; _enableFeature = enableFeature; _pathRaw = pathRaw; _maxLogDays = maxLogDays; } public void Initialize() { string todayPath = GetTodayPath(); string text = Path.GetDirectoryName(todayPath) ?? string.Empty; _log.LogInfo((object)("Creating chat log directory if it doesn't exist: " + text)); Directory.CreateDirectory(text); if (_enableFeature.Value) { PruneOldLogs(text); } } public string GetTodayPath() { string path = ExpandRaw(_pathRaw.Value); string path2 = Path.GetDirectoryName(path) ?? string.Empty; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); string extension = Path.GetExtension(path); return Path.Combine(path2, $"{fileNameWithoutExtension}_{DateTime.Now:yyyy-MM-dd}{extension}"); } public void SetBasePath(string newPath) { _pathRaw.Value = newPath; } private void PruneOldLogs(string dir) { string path = ExpandRaw(_pathRaw.Value); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); string extension = Path.GetExtension(path); string[] files = Directory.GetFiles(dir, fileNameWithoutExtension + "_????-??-??" + extension); Array.Sort(files); int num = files.Length - _maxLogDays.Value; for (int i = 0; i < num; i++) { _log.LogInfo((object)("Pruning old chat log: " + files[i])); File.Delete(files[i]); } } private static string ExpandRaw(string raw) { raw = Environment.ExpandEnvironmentVariables(raw); if (raw.StartsWith("~")) { string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); string text = raw; raw = Path.Combine(folderPath, text.Substring(1, text.Length - 1)); } return raw; } } } namespace FomoChatLog.Commands { public class FomoChatLogGetPathCommand : IChatCommand, IComparable { public const string CMD = "fomochatloggetpath"; public string Name => "fomochatloggetpath"; public string ShortName => "fclgp"; public string Description => "Get the current chat log file path."; public string Namespace => "fomo"; public void Execute(string[] args) { ChatUtils.AddGlobalNotification("Chat log path: " + FomoChatLogPlugin.ChatLog.GetTodayPath()); } } public class FomoChatLogMessageFormatCommand : IChatCommand, IComparable { public const string CMD = "fomochatlogmessageformat"; public string Name => "fomochatlogmessageformat"; public string ShortName => "fclmf"; public string Description => "Set or get the chat log message format."; public string Namespace => "fomo"; public void Execute(string[] args) { ConfigEntry? enableFeature = FomoChatLogPlugin.EnableFeature; if (enableFeature == null || !enableFeature.Value) { ChatUtils.AddGlobalNotification("Chat log feature is disabled."); } else if (FomoChatLogPlugin.MessageFormat == null) { ChatUtils.AddGlobalNotification("Fomo Chat Log Mod is not initialized yet."); } else if (args.Length == 0) { ChatUtils.AddGlobalNotification("Chat log message format: " + FomoChatLogPlugin.MessageFormat?.Value); } else if (args.Length != 0) { string text = string.Join(" ", args); if (string.IsNullOrWhiteSpace(text)) { ChatUtils.AddGlobalNotification("Please enter a valid chat log message format."); return; } FomoChatLogPlugin.MessageFormat.Value = text; ChatUtils.AddGlobalNotification("Chat log message format is now set to: " + text + "."); } } } public class FomoChatLogNotificationFormatCommand : IChatCommand, IComparable { public const string CMD = "fomochatlognotificationformat"; public string Name => "fomochatlognotificationformat"; public string ShortName => "fclnf"; public string Description => "Set or get the chat log notification format."; public string Namespace => "fomo"; public void Execute(string[] args) { ConfigEntry? enableFeature = FomoChatLogPlugin.EnableFeature; if (enableFeature == null || !enableFeature.Value) { ChatUtils.AddGlobalNotification("Chat log feature is disabled."); } else if (FomoChatLogPlugin.NotificationFormat == null) { ChatUtils.AddGlobalNotification("Fomo Chat Log Mod is not initialized yet."); } else if (args.Length == 0) { ChatUtils.AddGlobalNotification("Chat log notification format: " + FomoChatLogPlugin.NotificationFormat?.Value); } else if (args.Length != 0) { string text = string.Join(" ", args); if (string.IsNullOrWhiteSpace(text)) { ChatUtils.AddGlobalNotification("Please enter a valid chat log notification format."); return; } FomoChatLogPlugin.NotificationFormat.Value = text; ChatUtils.AddGlobalNotification("Chat log notification format is now set to: " + text + "."); } } } public class FomoChatLogSetPathCommand : IChatCommand, IComparable { public const string CMD = "fomochatlogsetpath"; public string Name => "fomochatlogsetpath"; public string ShortName => "fclsp"; public string Description => "Set the chat log file path. Usage: /fomochatlogsetpath . Example: /fomochatlogsetpath C:\\Logs\\chat.log"; public string Namespace => "fomo"; public void Execute(string[] args) { if (args.Length < 1) { ChatUtils.AddGlobalNotification("Usage: /fomochatlogsetpath . Example: /fomochatlogsetpath C:\\Logs\\chat.log"); return; } string text = args[0]; if (string.IsNullOrWhiteSpace(text)) { ChatUtils.AddGlobalNotification("Please enter a valid path."); return; } FomoChatLogPlugin.ChatLog.SetBasePath(text); try { string text2 = Path.GetDirectoryName(text) ?? string.Empty; if (!string.IsNullOrEmpty(text2) && !Directory.Exists(text2)) { Directory.CreateDirectory(text2); } } catch (Exception ex) { ChatUtils.AddGlobalNotification("Failed to create directory for the new chat log path: " + ex.Message); return; } ChatUtils.AddGlobalNotification("Chat log path set to: " + text + ". Takes effect on next restart."); } } public class FomoChatLogToggleCommand : IChatCommand, IComparable { public const string CMD = "fomochatlogtoggle"; public string Name => "fomochatlogtoggle"; public string ShortName => "fclt"; public string Description => "Toggle chat file logging on/off."; public string Namespace => "fomo"; public void Execute(string[] args) { if (FomoChatLogPlugin.EnableFeature != null) { FomoChatLogPlugin.EnableFeature.Value = !FomoChatLogPlugin.EnableFeature.Value; ChatUtils.AddGlobalNotification("Fomo chat log is now " + (FomoChatLogPlugin.EnableFeature.Value ? "enabled" : "disabled") + "."); } } } }