diff --git a/src/Miha.Discord/ServiceCollectionExtensions.cs b/src/Miha.Discord/ServiceCollectionExtensions.cs index 21f0b85..8151271 100644 --- a/src/Miha.Discord/ServiceCollectionExtensions.cs +++ b/src/Miha.Discord/ServiceCollectionExtensions.cs @@ -5,8 +5,11 @@ using Miha.Discord.Consumers; using Miha.Discord.Consumers.GuildEvent; using Miha.Discord.Services; +using Miha.Discord.Services.Hosted; +using Miha.Discord.Services.Interfaces; using SlimMessageBus.Host; using SlimMessageBus.Host.Memory; +using GuildEventMonitorService = Miha.Discord.Services.Hosted.GuildEventMonitorService; namespace Miha.Discord; @@ -19,7 +22,14 @@ public static IServiceCollection AddDiscordOptions(this IServiceCollection servi return services; } - public static IServiceCollection AddDiscordClientServices(this IServiceCollection services) + public static IServiceCollection AddDiscordServices(this IServiceCollection services) + { + services.AddSingleton(); + + return services; + } + + public static IServiceCollection AddDiscordHostedServices(this IServiceCollection services) { services.AddHostedService(); services.AddHostedService(); diff --git a/src/Miha.Discord/Services/GuildScheduledEventService.cs b/src/Miha.Discord/Services/GuildScheduledEventService.cs new file mode 100644 index 0000000..6e8d311 --- /dev/null +++ b/src/Miha.Discord/Services/GuildScheduledEventService.cs @@ -0,0 +1,50 @@ +using Discord; +using Discord.WebSocket; +using FluentResults; +using Microsoft.Extensions.Logging; +using Miha.Discord.Services.Interfaces; +using Miha.Shared; +using NodaTime; +using NodaTime.Calendars; +using NodaTime.Extensions; + +namespace Miha.Discord.Services; + +public class GuildScheduledEventService : IGuildScheduledEventService +{ + private readonly DiscordSocketClient _discordClient; + private readonly ILogger _logger; + + public GuildScheduledEventService( + DiscordSocketClient discordClient, + ILogger logger) + { + _discordClient = discordClient; + _logger = logger; + } + + public async Task>> GetScheduledWeeklyEventsAsync(ulong guildId, LocalDate dateOfTheWeek) + { + var weekNumberInYear = WeekYearRules.Iso.GetWeekOfWeekYear(dateOfTheWeek); + + var guild = _discordClient.GetGuild(guildId); + + if (guild is null) + { + return Result.Fail>("Failed to fetch discord guild"); + } + + var events = await guild.GetEventsAsync(); + + var eventsThisWeek = events.Where(guildEvent => + { + var estDate = guildEvent.StartTime.ToZonedDateTime() + .WithZone(DateTimeZoneProviders.Tzdb[Timezones.IanaEasternTime]).Date; + var weekOfDate = WeekYearRules.Iso.GetWeekOfWeekYear(estDate); + + return weekOfDate == weekNumberInYear; + }).Cast(); + + return Result.Ok(eventsThisWeek); + } +} diff --git a/src/Miha.Discord/Services/BirthdayAnnouncementService.cs b/src/Miha.Discord/Services/Hosted/BirthdayAnnouncementService.cs similarity index 97% rename from src/Miha.Discord/Services/BirthdayAnnouncementService.cs rename to src/Miha.Discord/Services/Hosted/BirthdayAnnouncementService.cs index 3bab6ca..4360cfb 100644 --- a/src/Miha.Discord/Services/BirthdayAnnouncementService.cs +++ b/src/Miha.Discord/Services/Hosted/BirthdayAnnouncementService.cs @@ -6,7 +6,7 @@ using Miha.Logic.Services.Interfaces; using Miha.Shared.ZonedClocks.Interfaces; -namespace Miha.Discord.Services; +namespace Miha.Discord.Services.Hosted; public class BirthdayAnnouncementService : DiscordClientService { diff --git a/src/Miha.Discord/Services/GuildEventMonitorService.cs b/src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs similarity index 99% rename from src/Miha.Discord/Services/GuildEventMonitorService.cs rename to src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs index b39fbcb..2713417 100644 --- a/src/Miha.Discord/Services/GuildEventMonitorService.cs +++ b/src/Miha.Discord/Services/Hosted/GuildEventMonitorService.cs @@ -11,7 +11,7 @@ using Miha.Shared.ZonedClocks.Interfaces; using Newtonsoft.Json; -namespace Miha.Discord.Services; +namespace Miha.Discord.Services.Hosted; public partial class GuildEventMonitorService : DiscordClientService { diff --git a/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs b/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs new file mode 100644 index 0000000..ffade86 --- /dev/null +++ b/src/Miha.Discord/Services/Hosted/GuildEventScheduleService.cs @@ -0,0 +1,62 @@ +using Cronos; +using Discord.Addons.Hosting; +using Discord.Addons.Hosting.Util; +using Discord.WebSocket; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Miha.Discord.Services.Interfaces; +using Miha.Shared.ZonedClocks.Interfaces; + +namespace Miha.Discord.Services.Hosted; + +public class GuildEventScheduleService : DiscordClientService +{ + private readonly DiscordSocketClient _client; + private readonly IEasternStandardZonedClock _easternStandardZonedClock; + private readonly IGuildScheduledEventService _scheduledEventService; + private readonly DiscordOptions _discordOptions; + private readonly ILogger _logger; + private const string Schedule = "0,5,10,15,20,25,30,35,40,45,50,55 8-19 * * *"; // https://crontab.cronhub.io/ + + private readonly CronExpression _cron; + + public GuildEventScheduleService( + DiscordSocketClient client, + IEasternStandardZonedClock easternStandardZonedClock, + IGuildScheduledEventService scheduledEventService, + IOptions discordOptions, + ILogger logger) : base(client, logger) + { + _client = client; + _easternStandardZonedClock = easternStandardZonedClock; + _scheduledEventService = scheduledEventService; + _discordOptions = discordOptions.Value; + _logger = logger; + + _cron = CronExpression.Parse(Schedule, CronFormat.Standard); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await Client.WaitForReadyAsync(stoppingToken); + + var eventsThisWeek = await _scheduledEventService.GetScheduledWeeklyEventsAsync(_discordOptions.Guild.Value, _easternStandardZonedClock.GetCurrentDate()); + + _logger.LogInformation("Events this week {events}", eventsThisWeek.Value); + + while (!stoppingToken.IsCancellationRequested) + { + 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; + } + + await Task.Delay(nextUtc.Value - utcNow, stoppingToken); + } + } +} diff --git a/src/Miha.Discord/Services/InteractionHandler.cs b/src/Miha.Discord/Services/Hosted/InteractionHandler.cs similarity index 98% rename from src/Miha.Discord/Services/InteractionHandler.cs rename to src/Miha.Discord/Services/Hosted/InteractionHandler.cs index f6e8c57..880bdbc 100644 --- a/src/Miha.Discord/Services/InteractionHandler.cs +++ b/src/Miha.Discord/Services/Hosted/InteractionHandler.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Miha.Discord.Services; +namespace Miha.Discord.Services.Hosted; public class InteractionHandler : DiscordClientService { diff --git a/src/Miha.Discord/Services/SlimMessageBusService.cs b/src/Miha.Discord/Services/Hosted/SlimMessageBusService.cs similarity index 97% rename from src/Miha.Discord/Services/SlimMessageBusService.cs rename to src/Miha.Discord/Services/Hosted/SlimMessageBusService.cs index 0d8ae81..0c73bee 100644 --- a/src/Miha.Discord/Services/SlimMessageBusService.cs +++ b/src/Miha.Discord/Services/Hosted/SlimMessageBusService.cs @@ -7,7 +7,7 @@ using Miha.Shared; using SlimMessageBus; -namespace Miha.Discord.Services; +namespace Miha.Discord.Services.Hosted; public class SlimMessageBusService : DiscordClientService { diff --git a/src/Miha.Discord/Services/Interfaces/IGuildScheduledEventService.cs b/src/Miha.Discord/Services/Interfaces/IGuildScheduledEventService.cs new file mode 100644 index 0000000..c78d6b1 --- /dev/null +++ b/src/Miha.Discord/Services/Interfaces/IGuildScheduledEventService.cs @@ -0,0 +1,10 @@ +using Discord; +using FluentResults; +using NodaTime; + +namespace Miha.Discord.Services.Interfaces; + +public interface IGuildScheduledEventService +{ + Task>> GetScheduledWeeklyEventsAsync(ulong guildId, LocalDate dateOfTheWeek); +} diff --git a/src/Miha/Startup.cs b/src/Miha/Startup.cs index 426474f..bafff1a 100644 --- a/src/Miha/Startup.cs +++ b/src/Miha/Startup.cs @@ -27,7 +27,8 @@ public static void ConfigureServices(HostBuilderContext context, IServiceCollect services .AddDiscordOptions(context.Configuration) - .AddDiscordClientServices() + .AddDiscordServices() + .AddDiscordHostedServices() .AddDiscordMessageBus(); services