From 7b5e99e42851f82a18f2da5f7bb7610f209c35c8 Mon Sep 17 00:00:00 2001 From: Twinki Date: Fri, 4 Oct 2024 07:56:51 -0400 Subject: [PATCH] Catch exceptions on a loop-level in Schedule service & Disable birthday scanner --- .../Hosted/GuildEventMonitorService.cs | 6 +- .../Hosted/GuildEventScheduleService.cs | 99 ++++++++++--------- src/Miha/Startup.cs | 3 +- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs b/src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs index 7e719f1..84534be 100644 --- a/src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs +++ b/src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs @@ -31,6 +31,7 @@ public partial class GuildEventMonitorService( .SetSize(1) .SetAbsoluteExpiration(TimeSpan.FromMinutes(25)) .SetSlidingExpiration(TimeSpan.FromMinutes(15)); + private readonly CronExpression _cron = CronExpression.Parse(Schedule, CronFormat.Standard); protected override async Task ExecuteAsync(CancellationToken stoppingToken) @@ -63,7 +64,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { - _logger.LogError(e, "Exception encountered in GuildEventMonitorService"); + LogExceptionInBackgroundServiceLoop(e); await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } @@ -197,4 +198,7 @@ private async Task CheckScheduledEventsAsync() [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "Exception occurred in GuildEventMonitorService during guildEvent {guildEventId}")] public partial void LogError(Exception e, ulong guildEventId); + + [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "Exception occurred in background service loop")] + public partial void LogExceptionInBackgroundServiceLoop(Exception e); } diff --git a/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs b/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs index 61e21c2..5a668b7 100644 --- a/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs +++ b/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs @@ -3,7 +3,6 @@ using Discord; using Discord.Addons.Hosting; using Discord.Addons.Hosting.Util; -using Discord.Net; using Discord.WebSocket; using Humanizer; using Microsoft.Extensions.Logging; @@ -27,49 +26,48 @@ public partial class GuildEventScheduleService( private readonly DiscordSocketClient _client = client; private readonly DiscordOptions _discordOptions = discordOptions.Value; private readonly ILogger _logger = logger; - + private const string Schedule = "0,5,10,15,20,25,30,35,40,45,50,55 * * * *"; // https://crontab.cronhub.io/ - + private readonly CronExpression _cron = CronExpression.Parse(Schedule, CronFormat.Standard); protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Waiting for client to be ready..."); - + await Client.WaitForReadyAsync(stoppingToken); - + while (!stoppingToken.IsCancellationRequested) { try { await PostWeeklyScheduleAsync(); + + var utcNow = easternStandardZonedClock.GetCurrentInstant().ToDateTimeUtc(); + var nextUtc = _cron.GetNextOccurrence(DateTimeOffset.UtcNow, easternStandardZonedClock.GetTimeZoneInfo()); + + if (nextUtc is null) + { + _logger.LogWarning("Next utc occurence is null"); + await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); + continue; + } + + var next = nextUtc.Value - utcNow; + + _logger.LogDebug("Waiting {Time} until next operation", next.Humanize(3)); + + await Task.Delay(nextUtc.Value - utcNow, stoppingToken); + } - catch (HttpException e) + catch (Exception e) { - _logger.LogWarning(e, "Discord dotnet http exception caught, likely caused by rate-limits, waiting a few minutes before continuing"); - - await Task.Delay(TimeSpan.FromMinutes(3), stoppingToken); - - continue; - } - - var utcNow = easternStandardZonedClock.GetCurrentInstant().ToDateTimeUtc(); - var nextUtc = _cron.GetNextOccurrence(DateTimeOffset.UtcNow, easternStandardZonedClock.GetTimeZoneInfo()); + LogExceptionInBackgroundServiceLoop(e); - if (nextUtc is null) - { - _logger.LogWarning("Next utc occurence is null"); - await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); - continue; + await Task.Delay(TimeSpan.FromMinutes(3), stoppingToken); } - - var next = nextUtc.Value - utcNow; - - _logger.LogDebug("Waiting {Time} until next operation", next.Humanize(3)); - - await Task.Delay(nextUtc.Value - utcNow, stoppingToken); } - + _logger.LogInformation("Hosted service ended"); } @@ -98,16 +96,16 @@ private async Task PostWeeklyScheduleAsync() var eventsThisWeekResult = await scheduledEventService.GetScheduledWeeklyEventsAsync(guild.Id, easternStandardZonedClock.GetCurrentDate()); var eventsThisWeek = eventsThisWeekResult.Value; - + if (eventsThisWeekResult.IsFailed || eventsThisWeek is null) { _logger.LogWarning("Fetching this weeks events failed, or is null {Errors}", eventsThisWeekResult.Errors); return; } - + var weeklyScheduleChannelResult = await guildService.GetWeeklyScheduleChannel(guild.Id); var weeklyScheduleChannel = weeklyScheduleChannelResult.Value; - + if (weeklyScheduleChannelResult.IsFailed || weeklyScheduleChannel is null) { _logger.LogWarning("Fetching the guilds weekly schedule channel failed, or is null {Errors}", weeklyScheduleChannelResult.Errors); @@ -115,24 +113,24 @@ private async Task PostWeeklyScheduleAsync() } var daysThisWeek = easternStandardZonedClock.GetCurrentWeekAsDates(); - + var eventsByDay = new Dictionary>(); var eventsThisWeekList = eventsThisWeek.ToList(); foreach (var dayOfWeek in daysThisWeek.OrderBy(d => d)) { eventsByDay.Add(dayOfWeek, new List()); - + foreach (var guildScheduledEvent in eventsThisWeekList.Where(e => easternStandardZonedClock.ToZonedDateTime(e.StartTime).Date.ToDateOnly() == dayOfWeek)) { eventsByDay[dayOfWeek].Add(guildScheduledEvent); } } - + _logger.LogInformation("Updating weekly schedule"); - + var postedHeader = false; var postedFooter = false; - + var messages = (await weeklyScheduleChannel .GetMessagesAsync(50) .FlattenAsync()) @@ -150,15 +148,15 @@ private async Task PostWeeklyScheduleAsync() { continue; } - + var messagesToDelete = messages .Where(m => m.Author.Id == _client.CurrentUser.Id) .ToList(); - + if (messagesToDelete.Any()) { var deletedMessages = 0; - + _logger.LogInformation("Wiping posted messages"); foreach (var message in messagesToDelete) @@ -168,23 +166,23 @@ private async Task PostWeeklyScheduleAsync() } _logger.LogInformation("Deleted {DeletedMessages} messages", deletedMessages); - + // Update the messages list messages = (await weeklyScheduleChannel .GetMessagesAsync(50) .FlattenAsync()) .ToList(); } - + break; } - + // TODO - Future me // If the ordering becomes a problem, a potential solution could be to use an index // to update the message at [1] (Tuesday), [6] (Sunday), [0] Monday for example // this would ensure the order of messages align with the days of the week // and to delete all messages from the bot if there's any more than 7 messages total - + // Update (or post) a message with an embed of events for that day, for each day of the week foreach (var (day, events) in eventsByDay) { @@ -204,7 +202,7 @@ private async Task PostWeeklyScheduleAsync() .AtStartOfDayInZone(easternStandardZonedClock.GetTzdbTimeZone()) .ToInstant() .ToDiscordTimestamp(TimestampTagStyles.ShortDate); - + // The day has passed if (easternStandardZonedClock.GetCurrentDate().ToDateOnly() > day) { @@ -212,7 +210,7 @@ private async Task PostWeeklyScheduleAsync() } else { description.AppendLine($"### {day.ToString("dddd")} - {timeStamp}"); - + if (!events.Any()) { description.AppendLine("*No events scheduled*"); @@ -248,7 +246,7 @@ private async Task PostWeeklyScheduleAsync() } } } - + if (!postedFooter && day == eventsByDay.Last().Key) { embed @@ -257,7 +255,7 @@ private async Task PostWeeklyScheduleAsync() postedFooter = true; } - + embed .WithColor(new Color(255, 43, 241)) .WithDescription(description.ToString()); @@ -266,7 +264,7 @@ private async Task PostWeeklyScheduleAsync() .FirstOrDefault(m => m.Author.Id == _client.CurrentUser.Id && m.Embeds.Any(e => e.Description.Contains(day.ToString("dddd")))); - + if (lastPostedMessage is null) { _logger.LogInformation("Posting new message"); @@ -280,10 +278,13 @@ await weeklyScheduleChannel.ModifyMessageAsync(lastPostedMessage.Id, props => }); } } - + _logger.LogInformation("Finished updating weekly schedule"); } - + [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "Exception occurred")] public partial void LogError(Exception e); + + [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "Exception occurred in background service loop")] + public partial void LogExceptionInBackgroundServiceLoop(Exception e); } diff --git a/src/Miha/Startup.cs b/src/Miha/Startup.cs index fe83fb8..3d873ad 100644 --- a/src/Miha/Startup.cs +++ b/src/Miha/Startup.cs @@ -105,7 +105,8 @@ private static IServiceCollection AddHealthServices(this IServiceCollection serv private static IServiceCollection AddBackgroundServices(this IServiceCollection services) { - services.AddHostedService(); + // TODO - Disabled until Birthdays are actually implemented + //services.AddHostedService(); return services; }