1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-04-20 00:43:36 +03:00

Add missing xmldocs for GuildUpdateService and UtilityService

Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
Octol1ttle 2023-06-11 23:19:28 +05:00
parent 3cd2b672a1
commit df00377b06
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
2 changed files with 63 additions and 7 deletions

View file

@ -7,12 +7,16 @@ using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.API.Objects; using Remora.Discord.API.Objects;
using Remora.Discord.Extensions.Embeds; using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Extensions.Formatting; using Remora.Discord.Extensions.Formatting;
using Remora.Discord.Gateway.Responders;
using Remora.Discord.Interactivity; using Remora.Discord.Interactivity;
using Remora.Rest.Core; using Remora.Rest.Core;
using Remora.Results; using Remora.Results;
namespace Boyfriend.Services; namespace Boyfriend.Services;
/// <summary>
/// Handles executing guild updates (also called "ticks") once per second.
/// </summary>
public class GuildUpdateService : BackgroundService { public class GuildUpdateService : BackgroundService {
private readonly IDiscordRestChannelAPI _channelApi; private readonly IDiscordRestChannelAPI _channelApi;
private readonly GuildDataService _dataService; private readonly GuildDataService _dataService;
@ -35,6 +39,11 @@ public class GuildUpdateService : BackgroundService {
_utility = utility; _utility = utility;
} }
/// <summary>
/// Activates a periodic timer with a 1 second interval and adds guild update tasks on each timer tick.
/// </summary>
/// <remarks>If update tasks take longer than 1 second, the next timer tick will be skipped.</remarks>
/// <param name="ct">The cancellation token for this operation.</param>
protected override async Task ExecuteAsync(CancellationToken ct) { protected override async Task ExecuteAsync(CancellationToken ct) {
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)); using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
var tasks = new List<Task>(); var tasks = new List<Task>();
@ -47,6 +56,28 @@ public class GuildUpdateService : BackgroundService {
} }
} }
/// <summary>
/// Runs an update ("tick") for a guild with the provided <paramref name="guildId" />.
/// </summary>
/// <remarks>
/// This method does the following:
/// <list type="bullet">
/// <item>Automatically unbans users once their ban period has expired.</item>
/// <item>Sends reminders about an upcoming scheduled event.</item>
/// <item>Sends scheduled event start notifications.</item>
/// <item>Sends scheduled event completion notifications.</item>
/// </list>
/// This is done here and not in a <see cref="IResponder{TGatewayEvent}" /> for the following reasons:
/// <list type="bullet">
/// <item>
/// Downtime would affect the reliability of notifications and automatic unbans if this logic were to be in a
/// <see cref="IResponder{TGatewayEvent}" />.
/// </item>
/// <item>The Discord API doesn't provide necessary about scheduled event updates.</item>
/// </list>
/// </remarks>
/// <param name="guildId">The ID of the guild to update.</param>
/// <param name="ct">The cancellation token for this operation.</param>
private async Task TickGuildAsync(Snowflake guildId, CancellationToken ct = default) { private async Task TickGuildAsync(Snowflake guildId, CancellationToken ct = default) {
var data = await _dataService.GetData(guildId, ct); var data = await _dataService.GetData(guildId, ct);
Messages.Culture = data.Culture; Messages.Culture = data.Culture;
@ -76,7 +107,7 @@ public class GuildUpdateService : BackgroundService {
if (DateTimeOffset.UtcNow if (DateTimeOffset.UtcNow
>= scheduledEvent.ScheduledStartTime - data.Configuration.EventEarlyNotificationOffset >= scheduledEvent.ScheduledStartTime - data.Configuration.EventEarlyNotificationOffset
&& !storedEvent.EarlyNotificationSent) { && !storedEvent.EarlyNotificationSent) {
var earlyResult = await SendScheduledEventStartedMessage(scheduledEvent, data, true, ct); var earlyResult = await SendScheduledEventUpdatedMessage(scheduledEvent, data, true, ct);
if (earlyResult.IsSuccess) if (earlyResult.IsSuccess)
storedEvent.EarlyNotificationSent = true; storedEvent.EarlyNotificationSent = true;
else else
@ -95,7 +126,7 @@ public class GuildUpdateService : BackgroundService {
GuildScheduledEventStatus.Scheduled => GuildScheduledEventStatus.Scheduled =>
await SendScheduledEventCreatedMessage(scheduledEvent, data.Configuration, ct), await SendScheduledEventCreatedMessage(scheduledEvent, data.Configuration, ct),
GuildScheduledEventStatus.Active or GuildScheduledEventStatus.Completed => GuildScheduledEventStatus.Active or GuildScheduledEventStatus.Completed =>
await SendScheduledEventStartedMessage(scheduledEvent, data, false, ct), await SendScheduledEventUpdatedMessage(scheduledEvent, data, false, ct),
_ => Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.Status))) _ => Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.Status)))
}; };
@ -110,6 +141,10 @@ public class GuildUpdateService : BackgroundService {
/// when a scheduled event is created /// when a scheduled event is created
/// in a guild's <see cref="GuildConfiguration.EventNotificationChannel" /> if one is set. /// in a guild's <see cref="GuildConfiguration.EventNotificationChannel" /> if one is set.
/// </summary> /// </summary>
/// <param name="scheduledEvent">The scheduled event that has just been created.</param>
/// <param name="config">The configuration of the guild containing the scheduled event.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A notification sending result which may or may not have succeeded.</returns>
private async Task<Result> SendScheduledEventCreatedMessage( private async Task<Result> SendScheduledEventCreatedMessage(
IGuildScheduledEvent scheduledEvent, GuildConfiguration config, CancellationToken ct = default) { IGuildScheduledEvent scheduledEvent, GuildConfiguration config, CancellationToken ct = default) {
var currentUserResult = await _userApi.GetCurrentUserAsync(ct); 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 /// when a scheduled event is about to start, has started or completed
/// in a guild's <see cref="GuildConfiguration.EventNotificationChannel" /> if one is set. /// in a guild's <see cref="GuildConfiguration.EventNotificationChannel" /> if one is set.
/// </summary> /// </summary>
private async Task<Result> SendScheduledEventStartedMessage( /// <param name="scheduledEvent">The scheduled event that is about to start, has started or completed.</param>
/// <param name="data">The data for the guild containing the scheduled event.</param>
/// <param name="early">Controls whether or not a reminder for the scheduled event should be sent instead of the event started/completed notification</param>
/// <param name="ct">The cancellation token for this operation</param>
/// <returns>A reminder/notification sending result which may or may not have succeeded.</returns>
private async Task<Result> SendScheduledEventUpdatedMessage(
IGuildScheduledEvent scheduledEvent, GuildData data, bool early, CancellationToken ct = default) { IGuildScheduledEvent scheduledEvent, GuildData data, bool early, CancellationToken ct = default) {
var currentUserResult = await _userApi.GetCurrentUserAsync(ct); var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult); 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))); 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)) if (!contentResult.IsDefined(out content))
return Result.FromError(contentResult); return Result.FromError(contentResult);

View file

@ -102,11 +102,26 @@ public class UtilityService : IHostedService {
return Result<string?>.FromSuccess(null); return Result<string?>.FromSuccess(null);
} }
/// <summary>
/// Gets the string mentioning all <see cref="GuildConfiguration.NotificationReceiver" />s related to a scheduled
/// event.
/// </summary>
/// <remarks>
/// If the guild configuration enables <see cref="GuildConfiguration.NotificationReceiver.Role" />, then the
/// <see cref="GuildConfiguration.EventNotificationRole" /> will also be mentioned.
/// </remarks>
/// <param name="scheduledEvent">
/// The scheduled event whose subscribers will be mentioned if the guild configuration enables
/// <see cref="GuildConfiguration.NotificationReceiver.Interested" />.
/// </param>
/// <param name="config">The configuration of the guild containing the scheduled event</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A result containing the string which may or may not have succeeded.</returns>
public async Task<Result<string>> GetEventNotificationMentions( public async Task<Result<string>> GetEventNotificationMentions(
GuildData data, IGuildScheduledEvent scheduledEvent, CancellationToken ct = default) { IGuildScheduledEvent scheduledEvent, GuildConfiguration config, CancellationToken ct = default) {
var builder = new StringBuilder(); var builder = new StringBuilder();
var receivers = data.Configuration.EventStartedReceivers; var receivers = config.EventStartedReceivers;
var role = data.Configuration.EventNotificationRole.ToDiscordSnowflake(); var role = config.EventNotificationRole.ToDiscordSnowflake();
var usersResult = await _eventApi.GetGuildScheduledEventUsersAsync( var usersResult = await _eventApi.GetGuildScheduledEventUsersAsync(
scheduledEvent.GuildID, scheduledEvent.ID, withMember: true, ct: ct); scheduledEvent.GuildID, scheduledEvent.ID, withMember: true, ct: ct);
if (!usersResult.IsDefined(out var users)) return Result<string>.FromError(usersResult); if (!usersResult.IsDefined(out var users)) return Result<string>.FromError(usersResult);