mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-05-14 01:36:08 +03:00
Split GuildUpdateService into separate services (#80)
GuildUpdateService is a service that contains way too many responsibilities with everything strictly coupled to each other. The code is buggy, hard to refactor and swallows errors. This prompted me to make this PR, which splits it into three independant services: - SongUpdateService (responsible for changing songs presence); - MemberUpdateService (responsible for updating member datas: unbanning users, adding the default role, sending reminders, filtering nicknames); - ScheduledEventUpdateService (responsible for updating scheduled events: sending notifications, automatically starting events). All of these services and their methods use Results to push errors all the way up in the stack, making sure no error is missed. To make logging and debugging easier, an extension method for `ILogger` was created - `LogResult`. The method checks if the result was successful or if its failure was caused by a user or environment error before logging anything - providing cleaner code and logs. `ExceptionError`s will also have their exception stacktrace and type logged (except in Remora code). This PR also fixes an issue that prevented banned users from being unbanned when their punishment was over. --------- Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
parent
e9f7825e4a
commit
f260681b39
10 changed files with 720 additions and 653 deletions
|
@ -1,6 +1,7 @@
|
|||
using System.Net;
|
||||
using System.Text;
|
||||
using DiffPlex.DiffBuilder.Model;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Remora.Discord.API;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Objects;
|
||||
|
@ -258,4 +259,78 @@ public static class Extensions
|
|||
|
||||
return (Result)await feedback.SendContextualEmbedAsync(embed, ct: ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <paramref name="result" /> has failed due to an error that has resulted from neither invalid user
|
||||
/// input nor the execution environment and logs the error using the provided <paramref name="logger" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This has special behavior for <see cref="ExceptionError" /> - its exception will be passed to the
|
||||
/// <paramref name="logger" />
|
||||
/// </remarks>
|
||||
/// <param name="logger">The logger to use.</param>
|
||||
/// <param name="result">The Result whose error check.</param>
|
||||
/// <param name="message">The message to use if this result has failed.</param>
|
||||
public static void LogResult(this ILogger logger, IResult result, string? message = "")
|
||||
{
|
||||
if (result.IsSuccess || result.Error.IsUserOrEnvironmentError())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.Error is ExceptionError exe)
|
||||
{
|
||||
logger.LogError(exe.Exception, "{ErrorMessage}", message);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogWarning("{UserMessage}\n{ResultErrorMessage}", message, result.Error.Message);
|
||||
}
|
||||
|
||||
public static void AddIfFailed(this List<Result> list, Result result)
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
list.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an appropriate result for a list of failed results. The list must only contain failed results.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of failed results.</param>
|
||||
/// <returns>
|
||||
/// A successful result if the list is empty, the only Result in the list, or <see cref="AggregateError" />
|
||||
/// containing all results from the list.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public static Result AggregateErrors(this List<Result> list)
|
||||
{
|
||||
return list.Count switch
|
||||
{
|
||||
0 => Result.FromSuccess(),
|
||||
1 => list[0],
|
||||
_ => new AggregateError(list.Cast<IResult>().ToArray())
|
||||
};
|
||||
}
|
||||
|
||||
public static Result TryGetExternalEventData(this IGuildScheduledEvent scheduledEvent, out DateTimeOffset endTime,
|
||||
out string? location)
|
||||
{
|
||||
endTime = default;
|
||||
location = default;
|
||||
if (!scheduledEvent.EntityMetadata.AsOptional().IsDefined(out var metadata))
|
||||
{
|
||||
return new ArgumentNullError(nameof(scheduledEvent.EntityMetadata));
|
||||
}
|
||||
|
||||
if (!metadata.Location.IsDefined(out location))
|
||||
{
|
||||
return new ArgumentNullError(nameof(metadata.Location));
|
||||
}
|
||||
|
||||
return scheduledEvent.ScheduledEndTime.AsOptional().IsDefined(out endTime)
|
||||
? Result.FromSuccess()
|
||||
: new ArgumentNullError(nameof(scheduledEvent.ScheduledEndTime));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue