122 lines
3.9 KiB
C#
122 lines
3.9 KiB
C#
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();
|
|
await using 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();
|
|
await using var guildRepository =
|
|
scope.ServiceProvider.GetRequiredService<GuildRepository>();
|
|
await using 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();
|
|
}
|
|
}
|