diff --git a/Services/GuildUpdateService.cs b/Services/GuildUpdateService.cs
index 8bb438c..b977591 100644
--- a/Services/GuildUpdateService.cs
+++ b/Services/GuildUpdateService.cs
@@ -7,12 +7,16 @@ using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.API.Objects;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Extensions.Formatting;
+using Remora.Discord.Gateway.Responders;
using Remora.Discord.Interactivity;
using Remora.Rest.Core;
using Remora.Results;
namespace Boyfriend.Services;
+///
+/// Handles executing guild updates (also called "ticks") once per second.
+///
public class GuildUpdateService : BackgroundService {
private readonly IDiscordRestChannelAPI _channelApi;
private readonly GuildDataService _dataService;
@@ -35,6 +39,11 @@ public class GuildUpdateService : BackgroundService {
_utility = utility;
}
+ ///
+ /// Activates a periodic timer with a 1 second interval and adds guild update tasks on each timer tick.
+ ///
+ /// If update tasks take longer than 1 second, the next timer tick will be skipped.
+ /// The cancellation token for this operation.
protected override async Task ExecuteAsync(CancellationToken ct) {
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
var tasks = new List();
@@ -47,6 +56,28 @@ public class GuildUpdateService : BackgroundService {
}
}
+ ///
+ /// Runs an update ("tick") for a guild with the provided .
+ ///
+ ///
+ /// This method does the following:
+ ///
+ /// - Automatically unbans users once their ban period has expired.
+ /// - Sends reminders about an upcoming scheduled event.
+ /// - Sends scheduled event start notifications.
+ /// - Sends scheduled event completion notifications.
+ ///
+ /// This is done here and not in a for the following reasons:
+ ///
+ /// -
+ /// Downtime would affect the reliability of notifications and automatic unbans if this logic were to be in a
+ /// .
+ ///
+ /// - The Discord API doesn't provide necessary about scheduled event updates.
+ ///
+ ///
+ /// The ID of the guild to update.
+ /// The cancellation token for this operation.
private async Task TickGuildAsync(Snowflake guildId, CancellationToken ct = default) {
var data = await _dataService.GetData(guildId, ct);
Messages.Culture = data.Culture;
@@ -76,7 +107,7 @@ public class GuildUpdateService : BackgroundService {
if (DateTimeOffset.UtcNow
>= scheduledEvent.ScheduledStartTime - data.Configuration.EventEarlyNotificationOffset
&& !storedEvent.EarlyNotificationSent) {
- var earlyResult = await SendScheduledEventStartedMessage(scheduledEvent, data, true, ct);
+ var earlyResult = await SendScheduledEventUpdatedMessage(scheduledEvent, data, true, ct);
if (earlyResult.IsSuccess)
storedEvent.EarlyNotificationSent = true;
else
@@ -95,7 +126,7 @@ public class GuildUpdateService : BackgroundService {
GuildScheduledEventStatus.Scheduled =>
await SendScheduledEventCreatedMessage(scheduledEvent, data.Configuration, ct),
GuildScheduledEventStatus.Active or GuildScheduledEventStatus.Completed =>
- await SendScheduledEventStartedMessage(scheduledEvent, data, false, ct),
+ await SendScheduledEventUpdatedMessage(scheduledEvent, data, false, ct),
_ => Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.Status)))
};
@@ -110,6 +141,10 @@ public class GuildUpdateService : BackgroundService {
/// when a scheduled event is created
/// in a guild's if one is set.
///
+ /// The scheduled event that has just been created.
+ /// The configuration of the guild containing the scheduled event.
+ /// The cancellation token for this operation.
+ /// A notification sending result which may or may not have succeeded.
private async Task SendScheduledEventCreatedMessage(
IGuildScheduledEvent scheduledEvent, GuildConfiguration config, CancellationToken ct = default) {
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
@@ -189,7 +224,12 @@ public class GuildUpdateService : BackgroundService {
/// when a scheduled event is about to start, has started or completed
/// in a guild's if one is set.
///
- private async Task SendScheduledEventStartedMessage(
+ /// The scheduled event that is about to start, has started or completed.
+ /// The data for the guild containing the scheduled event.
+ /// Controls whether or not a reminder for the scheduled event should be sent instead of the event started/completed notification
+ /// The cancellation token for this operation
+ /// A reminder/notification sending result which may or may not have succeeded.
+ private async Task SendScheduledEventUpdatedMessage(
IGuildScheduledEvent scheduledEvent, GuildData data, bool early, CancellationToken ct = default) {
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
@@ -233,7 +273,8 @@ public class GuildUpdateService : BackgroundService {
return Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.EntityType)));
}
- var contentResult = await _utility.GetEventNotificationMentions(data, scheduledEvent, ct);
+ var contentResult = await _utility.GetEventNotificationMentions(
+ scheduledEvent, data.Configuration, ct);
if (!contentResult.IsDefined(out content))
return Result.FromError(contentResult);
diff --git a/Services/UtilityService.cs b/Services/UtilityService.cs
index 4c6da4d..b4ff6fb 100644
--- a/Services/UtilityService.cs
+++ b/Services/UtilityService.cs
@@ -102,11 +102,26 @@ public class UtilityService : IHostedService {
return Result.FromSuccess(null);
}
+ ///
+ /// Gets the string mentioning all s related to a scheduled
+ /// event.
+ ///
+ ///
+ /// If the guild configuration enables , then the
+ /// will also be mentioned.
+ ///
+ ///
+ /// The scheduled event whose subscribers will be mentioned if the guild configuration enables
+ /// .
+ ///
+ /// The configuration of the guild containing the scheduled event
+ /// The cancellation token for this operation.
+ /// A result containing the string which may or may not have succeeded.
public async Task> GetEventNotificationMentions(
- GuildData data, IGuildScheduledEvent scheduledEvent, CancellationToken ct = default) {
+ IGuildScheduledEvent scheduledEvent, GuildConfiguration config, CancellationToken ct = default) {
var builder = new StringBuilder();
- var receivers = data.Configuration.EventStartedReceivers;
- var role = data.Configuration.EventNotificationRole.ToDiscordSnowflake();
+ var receivers = config.EventStartedReceivers;
+ var role = config.EventNotificationRole.ToDiscordSnowflake();
var usersResult = await _eventApi.GetGuildScheduledEventUsersAsync(
scheduledEvent.GuildID, scheduledEvent.ID, withMember: true, ct: ct);
if (!usersResult.IsDefined(out var users)) return Result.FromError(usersResult);