feat: store timeouts in database and log them ending
we have to do this because discord doesn't notify us when a timeout ends naturally, only when a moderator removes it early.
This commit is contained in:
parent
f0fcfd7bd3
commit
e6d68338db
7 changed files with 249 additions and 1 deletions
119
Catalogger.Backend/Services/TimeoutService.cs
Normal file
119
Catalogger.Backend/Services/TimeoutService.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Catalogger.Backend.Bot;
|
||||
using Catalogger.Backend.Cache.InMemoryCache;
|
||||
using Catalogger.Backend.Database.Models;
|
||||
using Catalogger.Backend.Database.Repositories;
|
||||
using Catalogger.Backend.Extensions;
|
||||
using Remora.Discord.API;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Rest.Core;
|
||||
|
||||
namespace Catalogger.Backend.Services;
|
||||
|
||||
public class TimeoutService(
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger logger,
|
||||
WebhookExecutorService webhookExecutor,
|
||||
UserCache userCache
|
||||
)
|
||||
{
|
||||
private readonly ILogger _logger = logger.ForContext<TimeoutService>();
|
||||
private readonly ConcurrentDictionary<int, Timer> _timers = new();
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_logger.Information("Populating timeout service with existing database timeouts");
|
||||
|
||||
await using var scope = serviceProvider.CreateAsyncScope();
|
||||
var timeoutRepository = scope.ServiceProvider.GetRequiredService<TimeoutRepository>();
|
||||
|
||||
var timeouts = await timeoutRepository.GetAllAsync();
|
||||
foreach (var timeout in timeouts)
|
||||
AddTimer(timeout);
|
||||
}
|
||||
|
||||
public void AddTimer(DiscordTimeout timeout)
|
||||
{
|
||||
_logger.Debug("Adding timeout {TimeoutId} to queue", timeout.Id);
|
||||
|
||||
RemoveTimer(timeout.Id);
|
||||
_timers[timeout.Id] = new Timer(
|
||||
_ =>
|
||||
{
|
||||
var __ = SendTimeoutLogAsync(timeout.Id);
|
||||
},
|
||||
null,
|
||||
timeout.Until.ToDateTimeOffset() - DateTimeOffset.UtcNow,
|
||||
Timeout.InfiniteTimeSpan
|
||||
);
|
||||
}
|
||||
|
||||
private async Task SendTimeoutLogAsync(int timeoutId)
|
||||
{
|
||||
_logger.Information("Sending timeout log for {TimeoutId}", timeoutId);
|
||||
|
||||
await using var scope = serviceProvider.CreateAsyncScope();
|
||||
var guildRepository = scope.ServiceProvider.GetRequiredService<GuildRepository>();
|
||||
var timeoutRepository = scope.ServiceProvider.GetRequiredService<TimeoutRepository>();
|
||||
|
||||
var timeout = await timeoutRepository.RemoveAsync(timeoutId);
|
||||
if (timeout == null)
|
||||
{
|
||||
_logger.Warning("Timeout {TimeoutId} not found, can't log anything", timeoutId);
|
||||
return;
|
||||
}
|
||||
|
||||
var userId = DiscordSnowflake.New(timeout.UserId);
|
||||
var moderatorId =
|
||||
timeout.ModeratorId != null
|
||||
? DiscordSnowflake.New(timeout.ModeratorId.Value)
|
||||
: (Snowflake?)null;
|
||||
|
||||
var user = await userCache.GetUserAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.Warning("Could not get user {UserId} from cache, can't log timeout", userId);
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithAuthor(user.Tag(), null, user.AvatarUrl())
|
||||
.WithTitle("Member timeout ended")
|
||||
.WithDescription($"<@{user.ID}>")
|
||||
.WithColour(DiscordUtils.Green)
|
||||
.WithFooter($"User ID: {user.ID}")
|
||||
.WithCurrentTimestamp();
|
||||
|
||||
if (moderatorId != null)
|
||||
{
|
||||
var moderator = await userCache.TryFormatUserAsync(moderatorId.Value);
|
||||
embed.AddField("Originally timed out by", moderator);
|
||||
}
|
||||
else
|
||||
{
|
||||
embed.AddField("Originally timed out by", "*(unknown)*");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var guildConfig = await guildRepository.GetAsync(DiscordSnowflake.New(timeout.GuildId));
|
||||
webhookExecutor.QueueLog(
|
||||
guildConfig,
|
||||
LogChannelType.GuildMemberTimeout,
|
||||
embed.Build().GetOrThrow()
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Could not log timeout {TimeoutId} expiring", timeout.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTimer(int timeoutId)
|
||||
{
|
||||
if (!_timers.TryRemove(timeoutId, out var timer))
|
||||
return;
|
||||
_logger.Debug("Removing timeout {TimeoutId} from queue", timeoutId);
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue