From 29c2332ad93a56bbdcfab21fa73f142ce2a1a9db Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Wed, 16 Nov 2022 23:27:10 +0500 Subject: [PATCH] Log exceptions thrown during "fire-and-forget" async method calls --- Boyfriend/Boyfriend.cs | 11 ++++++--- Boyfriend/CommandProcessor.cs | 15 +++++++----- Boyfriend/Utils.cs | 46 +++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/Boyfriend/Boyfriend.cs b/Boyfriend/Boyfriend.cs index 2e36a76..f33caec 100644 --- a/Boyfriend/Boyfriend.cs +++ b/Boyfriend/Boyfriend.cs @@ -11,7 +11,9 @@ public static class Boyfriend { private static readonly DiscordSocketConfig Config = new() { MessageCacheSize = 250, - GatewayIntents = (GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent | GatewayIntents.GuildMembers) & ~GatewayIntents.GuildInvites, + GatewayIntents + = (GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent | GatewayIntents.GuildMembers) & + ~GatewayIntents.GuildInvites, AlwaysDownloadUsers = true, AlwaysResolveStickers = false, AlwaysDownloadDefaultStickers = false, @@ -59,7 +61,7 @@ public static class Boyfriend { private static async Task Init() { var token = (await File.ReadAllTextAsync("token.txt")).Trim(); - Client.Log += Log; + Client.Log += x => Log(x); await Client.LoginAsync(TokenType.Bot, token); await Client.StartAsync(); @@ -75,7 +77,7 @@ public static class Boyfriend { // ReSharper disable once FunctionNeverReturns } - private static Task Log(LogMessage msg) { + public static Task Log(LogMessage msg) { switch (msg.Severity) { case LogSeverity.Critical: Console.ForegroundColor = ConsoleColor.DarkRed; @@ -103,7 +105,8 @@ public static class Boyfriend { } public static async Task WriteGuildConfigAsync(ulong id) { - await File.WriteAllTextAsync($"config_{id}.json", JsonConvert.SerializeObject(GuildConfigDictionary[id], Formatting.Indented)); + await File.WriteAllTextAsync($"config_{id}.json", + JsonConvert.SerializeObject(GuildConfigDictionary[id], Formatting.Indented)); if (RemovedRolesDictionary.TryGetValue(id, out var removedRoles)) await File.WriteAllTextAsync($"removedroles_{id}.json", diff --git a/Boyfriend/CommandProcessor.cs b/Boyfriend/CommandProcessor.cs index f34242d..ccba1e8 100644 --- a/Boyfriend/CommandProcessor.cs +++ b/Boyfriend/CommandProcessor.cs @@ -47,13 +47,15 @@ public sealed class CommandProcessor { var list = Context.Message.Content.Split("\n"); var cleanList = Context.Message.CleanContent.Split("\n"); - for (var i = 0; i < list.Length; i++) { - RunCommandOnLine(list[i], cleanList[i], config["Prefix"]); + for (var i = 0; i < list.Length; i++) + _tasks.Add(RunCommandOnLine(list[i], cleanList[i], config["Prefix"])); - if (_stackedReplyMessage.Length > 0) _ = Context.Channel.TriggerTypingAsync(); + try { Task.WaitAll(_tasks.ToArray()); } catch (AggregateException e) { + foreach (var ex in e.InnerExceptions) + await Boyfriend.Log(new LogMessage(LogSeverity.Error, nameof(CommandProcessor), + "Exception while executing commands", ex)); } - await Task.WhenAll(_tasks); _tasks.Clear(); if (ConfigWriteScheduled) await Boyfriend.WriteGuildConfigAsync(guild.Id); @@ -61,7 +63,7 @@ public sealed class CommandProcessor { SendFeedbacks(); } - private void RunCommandOnLine(string line, string cleanLine, string prefix) { + private async Task RunCommandOnLine(string line, string cleanLine, string prefix) { var prefixed = line.StartsWith(prefix); if (!prefixed && !line.StartsWith(Mention)) return; foreach (var command in Commands) { @@ -70,7 +72,8 @@ public sealed class CommandProcessor { var args = lineNoMention.Trim().Split().Skip(1).ToArray(); var cleanArgs = cleanLine.Split().Skip(lineNoMention.StartsWith(" ") ? 2 : 1).ToArray(); - _tasks.Add(command.RunAsync(this, args, cleanArgs)); + await command.RunAsync(this, args, cleanArgs); + if (_stackedReplyMessage.Length > 0) _ = Context.Channel.TriggerTypingAsync(); return; } } diff --git a/Boyfriend/Utils.cs b/Boyfriend/Utils.cs index d9c2bd6..1c2316d 100644 --- a/Boyfriend/Utils.cs +++ b/Boyfriend/Utils.cs @@ -74,10 +74,15 @@ public static class Utils { } public static async Task SilentSendAsync(SocketTextChannel? channel, string text, bool allowRoles = false) { - if (channel is null || text.Length is 0 or > 2000) - throw new Exception($"Message length is out of range: {text.Length}"); + try { + if (channel is null || text.Length is 0 or > 2000) + throw new Exception($"Message length is out of range: {text.Length}"); - await channel.SendMessageAsync(text, false, null, null, allowRoles ? AllowRoles : AllowedMentions.None); + await channel.SendMessageAsync(text, false, null, null, allowRoles ? AllowRoles : AllowedMentions.None); + } catch (Exception e) { + await Boyfriend.Log(new LogMessage(LogSeverity.Error, nameof(Utils), + "Exception while silently sending message", e)); + } } public static RequestOptions GetRequestOptions(string reason) { @@ -154,23 +159,28 @@ public static class Utils { public static async Task SendEarlyEventStartNotificationAsync(SocketTextChannel? channel, SocketGuildEvent scheduledEvent, int minuteOffset) { - await Task.Delay(scheduledEvent.StartTime.Subtract(DateTimeOffset.Now) - .Subtract(TimeSpan.FromMinutes(minuteOffset))); - var guild = scheduledEvent.Guild; - if (guild.GetEvent(scheduledEvent.Id) is null) return; - var eventConfig = Boyfriend.GetGuildConfig(guild.Id); + try { + await Task.Delay(scheduledEvent.StartTime.Subtract(DateTimeOffset.Now) + .Subtract(TimeSpan.FromMinutes(minuteOffset))); + var guild = scheduledEvent.Guild; + if (guild.GetEvent(scheduledEvent.Id) is null) return; + var eventConfig = Boyfriend.GetGuildConfig(guild.Id); - var receivers = eventConfig["EventStartedReceivers"]; - var role = guild.GetRole(ulong.Parse(eventConfig["EventNotificationRole"])); - var mentions = Boyfriend.StringBuilder; + var receivers = eventConfig["EventStartedReceivers"]; + var role = guild.GetRole(ulong.Parse(eventConfig["EventNotificationRole"])); + var mentions = Boyfriend.StringBuilder; - if (receivers.Contains("role") && role is not null) mentions.Append($"{role.Mention} "); - if (receivers.Contains("users") || receivers.Contains("interested")) - mentions = (await scheduledEvent.GetUsersAsync(15)).Aggregate(mentions, - (current, user) => current.Append($"{user.Mention} ")); - await channel?.SendMessageAsync(string.Format(Messages.EventEarlyNotification, mentions, - Wrap(scheduledEvent.Name), scheduledEvent.StartTime.ToUnixTimeSeconds().ToString()))!; - mentions.Clear(); + if (receivers.Contains("role") && role is not null) mentions.Append($"{role.Mention} "); + if (receivers.Contains("users") || receivers.Contains("interested")) + mentions = (await scheduledEvent.GetUsersAsync(15)).Aggregate(mentions, + (current, user) => current.Append($"{user.Mention} ")); + await channel?.SendMessageAsync(string.Format(Messages.EventEarlyNotification, mentions, + Wrap(scheduledEvent.Name), scheduledEvent.StartTime.ToUnixTimeSeconds().ToString()))!; + mentions.Clear(); + } catch (Exception e) { + await Boyfriend.Log(new LogMessage(LogSeverity.Error, nameof(Utils), + "Exception while sending early event start notification", e)); + } } public static SocketTextChannel? GetEventNotificationChannel(SocketGuild guild) {