diff --git a/InstanceConfig.cs b/InstanceConfig.cs index 8e59fef..b6db68b 100644 --- a/InstanceConfig.cs +++ b/InstanceConfig.cs @@ -19,7 +19,6 @@ class InstanceConfig { /// List of assemblies to load, by file. Paths are always relative to the bot directory. /// internal IReadOnlyList Assemblies { get; } - internal string InstanceLogTarget { get; } public string? SqlHost { get; } public string? SqlDatabase { get; } @@ -47,7 +46,6 @@ class InstanceConfig { } BotToken = ReadConfKey(conf, nameof(BotToken), true); - InstanceLogTarget = ReadConfKey(conf, nameof(InstanceLogTarget), true); try { Assemblies = Common.Utilities.LoadStringOrStringArray(conf[nameof(Assemblies)]).AsReadOnly(); diff --git a/ModuleLoader.cs b/ModuleLoader.cs index e3c79d5..cacad90 100644 --- a/ModuleLoader.cs +++ b/ModuleLoader.cs @@ -51,7 +51,7 @@ static class ModuleLoader { newreport.Append($" {t.Name}"); newmods.Add((RegexbotModule)mod); } - rb._svcLogging.DoLog(false, nameof(ModuleLoader), newreport.ToString()); + rb._svcLogging.DoLog(nameof(ModuleLoader), newreport.ToString()); return newmods; } } diff --git a/Program.cs b/Program.cs index dbcf907..7fee3e8 100644 --- a/Program.cs +++ b/Program.cs @@ -34,15 +34,13 @@ class Program { AlwaysDownloadUsers = true }); + // Initialize services, load modules _main = new RegexbotClient(cfg, client); // Set up application close handler Console.CancelKeyPress += Console_CancelKeyPress; - // TODO Set up unhandled exception handler - // send error notification to instance log channel, if possible - - // And off we go. + // Proceed to connect await _main.DiscordClient.LoginAsync(TokenType.Bot, cfg.BotToken); await _main.DiscordClient.StartAsync(); await Task.Delay(-1); @@ -51,7 +49,7 @@ class Program { private static void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e) { e.Cancel = true; - _main._svcLogging.DoLog(true, nameof(RegexBot), "Shutting down. Reason: Interrupt signal."); + _main._svcLogging.DoLog(nameof(RegexBot), "Shutting down."); var finishingTasks = Task.Run(async () => { // TODO periodic task service: stop processing, wait for all tasks to finish @@ -60,7 +58,7 @@ class Program { }); if (!finishingTasks.Wait(5000)) - _main._svcLogging.DoLog(false, nameof(RegexBot), "Could not disconnect properly. Exiting..."); + _main._svcLogging.DoLog(nameof(RegexBot), "Warning: Normal shutdown is taking too long. Exiting now."); Environment.Exit(0); } } diff --git a/RegexbotClient.cs b/RegexbotClient.cs index 9128873..3c2c754 100644 --- a/RegexbotClient.cs +++ b/RegexbotClient.cs @@ -30,8 +30,8 @@ public partial class RegexbotClient { _svcCommonFunctions = new Services.CommonFunctions.CommonFunctionsService(this); _svcEntityCache = new Services.EntityCache.EntityCacheService(this); - var ver = Assembly.GetExecutingAssembly().GetName().Version!; - _svcLogging.DoLog(true, nameof(RegexBot), $"{nameof(RegexBot)} v{ver:3} - https://github.com/NoiTheCat/RegexBot"); + var ver = Assembly.GetExecutingAssembly().GetName().Version!.ToString(3); + _svcLogging.DoLog(nameof(RegexBot), $"{nameof(RegexBot)} v{ver} - https://github.com/NoiTheCat/RegexBot"); // Load externally defined functionality Modules = ModuleLoader.Load(Config, this); diff --git a/RegexbotModule.cs b/RegexbotModule.cs index 726f6ff..033817b 100644 --- a/RegexbotModule.cs +++ b/RegexbotModule.cs @@ -73,16 +73,12 @@ public abstract class RegexbotModule { /// The log message to send. Multi-line messages are acceptable. protected void Log(SocketGuild guild, string? message) { var gname = guild.Name ?? $"Guild ID {guild.Id}"; - Bot._svcLogging.DoLog(false, $"{gname}] [{Name}", message); + Bot._svcLogging.DoLog($"{gname}] [{Name}", message); } /// /// Emits a log message to the bot console and, optionally, the logging webhook. /// /// The log message to send. Multi-line messages are acceptable. - /// - /// Specifies if the log message should be sent to the reporting channel. - /// Only messages of very high importance should use this option. - /// - protected void Log(string message, bool report = false) => Bot._svcLogging.DoLog(report, Name, message); + protected void Log(string message) => Bot._svcLogging.DoLog(Name, message); } diff --git a/Services/EntityCache/MessageCachingSubservice.cs b/Services/EntityCache/MessageCachingSubservice.cs index 7ca6998..f11b478 100644 --- a/Services/EntityCache/MessageCachingSubservice.cs +++ b/Services/EntityCache/MessageCachingSubservice.cs @@ -7,9 +7,9 @@ class MessageCachingSubservice { // Hooked public event EcMessageUpdateHandler? OnCachePreUpdate; - private readonly Action _log; + private readonly Action _log; - internal MessageCachingSubservice(RegexbotClient bot, Action logMethod) { + internal MessageCachingSubservice(RegexbotClient bot, Action logMethod) { _log = logMethod; bot.DiscordClient.MessageReceived += DiscordClient_MessageReceived; bot.DiscordClient.MessageUpdated += DiscordClient_MessageUpdated; @@ -63,8 +63,8 @@ class MessageCachingSubservice { try { await (Task)handler.DynamicInvoke(oldMsg, newMsg)!; } catch (Exception ex) { - _log($"Unhandled exception in {nameof(RegexbotClient.EcOnMessageUpdate)} handler '{handler.Method.Name}':", false); - _log(ex.ToString(), false); + _log($"Unhandled exception in {nameof(RegexbotClient.EcOnMessageUpdate)} handler '{handler.Method.Name}':\n" + + ex.ToString()); } } } diff --git a/Services/Logging/LoggingService.cs b/Services/Logging/LoggingService.cs index 2e02e97..2e51d6a 100644 --- a/Services/Logging/LoggingService.cs +++ b/Services/Logging/LoggingService.cs @@ -1,5 +1,4 @@ using Discord; -using Discord.Webhook; using System.Reflection; using System.Text; @@ -9,11 +8,9 @@ namespace RegexBot.Services.Logging; /// class LoggingService : Service { // NOTE: Service.Log's functionality is implemented here. DO NOT use within this class. - private readonly DiscordWebhookClient _instLogWebhook; private readonly string? _logBasePath; internal LoggingService(RegexbotClient bot) : base(bot) { - _instLogWebhook = new DiscordWebhookClient(bot.Config.InstanceLogTarget); _logBasePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location) + Path.DirectorySeparatorChar + "logs"; try { @@ -21,7 +18,7 @@ class LoggingService : Service { Directory.GetFiles(_logBasePath); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) { _logBasePath = null; - Output(Name, "Cannot create or access logging directory. File logging will be disabled."); + DoLog(Name, "Cannot create or access logging directory. File logging will be disabled."); } bot.DiscordClient.Log += DiscordClient_Log; @@ -33,9 +30,8 @@ class LoggingService : Service { /// private Task DiscordClient_Log(LogMessage arg) { var msg = $"[{Enum.GetName(typeof(LogSeverity), arg.Severity)}] {arg.Message}"; - if (arg.Exception != null) msg += "\n```\n" + arg.Exception.ToString() + "\n```"; + if (arg.Exception != null) msg += arg.Exception.ToString(); - var important = arg.Severity != LogSeverity.Info; switch (arg.Message) { // Prevent webhook logs for these 'important' Discord.Net messages case "Connecting": case "Connected": @@ -44,15 +40,16 @@ class LoggingService : Service { case "Disconnected": case "Resumed previous session": case "Failed to resume previous session": - important = false; break; } - DoLog(important, "Discord.Net", msg); + DoLog("Discord.Net", msg); return Task.CompletedTask; } - private void Output(string source, string message) { + // Hooked + internal void DoLog(string source, string? message) { + message ??= "(null)"; var now = DateTimeOffset.UtcNow; var output = new StringBuilder(); var prefix = $"[{now:u}] [{source}] "; @@ -66,24 +63,4 @@ class LoggingService : Service { File.AppendAllText(filename, outstr, Encoding.UTF8); } } - - // Hooked - internal void DoLog(bool report, string source, string? message) { - message ??= "(null)"; - Output(source, message); - if (report) Task.Run(() => ReportInstanceWebhook(source, message)); - } - - private async Task ReportInstanceWebhook(string source, string message) { - try { - EmbedBuilder e = new() { - Footer = new EmbedFooterBuilder() { Text = source }, - Timestamp = DateTimeOffset.UtcNow, - Description = message - }; - await _instLogWebhook.SendMessageAsync(embeds: new[] { e.Build() }); - } catch (Exception ex) { - DoLog(false, Name, "Failed to send message to reporting channel: " + ex.Message); - } - } } diff --git a/Services/ModuleState/ModuleStateService.cs b/Services/ModuleState/ModuleStateService.cs index ccaa13b..5d5d061 100644 --- a/Services/ModuleState/ModuleStateService.cs +++ b/Services/ModuleState/ModuleStateService.cs @@ -75,8 +75,6 @@ class ModuleStateService : Service { return false; } - // TODO Guild-specific service options? If implemented, this is where to load them. - // Load moderator list var mods = new EntityList(guildConf["Moderators"]!, true); @@ -94,7 +92,7 @@ class ModuleStateService : Service { Log("Unhandled exception while initializing guild state for module:\n" + $"Module: {module.Name} | " + $"Guild: {guildId} ({BotClient.DiscordClient.GetGuild(guildId)?.Name ?? "unknown name"})\n" + - $"```\n{ex}\n```", true); + $"```\n{ex}\n```"); return false; } } diff --git a/Services/Service.cs b/Services/Service.cs index f5ee3b2..cdf0b2d 100644 --- a/Services/Service.cs +++ b/Services/Service.cs @@ -17,6 +17,5 @@ internal abstract class Service { /// Emits a log message. /// /// The log message to send. Multi-line messages are acceptable. - /// Specify if the log message should be sent to a reporting channel. - protected void Log(string message, bool report = false) => BotClient._svcLogging.DoLog(report, Name, message); + protected void Log(string message) => BotClient._svcLogging.DoLog(Name, message); }