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:
sam 2024-11-05 22:22:12 +01:00
parent f0fcfd7bd3
commit e6d68338db
Signed by: sam
GPG key ID: 5F3C3C1B3166639D
7 changed files with 249 additions and 1 deletions

View file

@ -0,0 +1 @@
drop table timeouts;

View file

@ -0,0 +1,9 @@
create table timeouts (
id integer generated by default as identity primary key,
user_id bigint not null,
guild_id bigint not null,
moderator_id bigint,
until timestamptz not null
);
create unique index ix_timeouts_user_guild on timeouts (user_id, guild_id);

View file

@ -0,0 +1,12 @@
using NodaTime;
namespace Catalogger.Backend.Database.Models;
public class DiscordTimeout
{
public int Id { get; init; }
public ulong UserId { get; init; }
public ulong GuildId { get; init; }
public ulong? ModeratorId { get; init; }
public Instant Until { get; init; }
}

View file

@ -0,0 +1,72 @@
using Catalogger.Backend.Database.Models;
using Dapper;
using NodaTime;
using Remora.Rest.Core;
namespace Catalogger.Backend.Database.Repositories;
public class TimeoutRepository(DatabaseConnection conn) : IDisposable, IAsyncDisposable
{
public async Task<DiscordTimeout?> GetAsync(int id) =>
await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
"select * from timeouts where id = @id",
new { id }
);
public async Task<DiscordTimeout?> GetAsync(Snowflake guildId, Snowflake userId) =>
await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
"select * from timeouts where guild_id = @GuildId and user_id = @UserId",
new { GuildId = guildId.Value, UserId = userId.Value }
);
public async Task<List<DiscordTimeout>> GetAllAsync() =>
(await conn.QueryAsync<DiscordTimeout>("select * from timeouts order by id")).ToList();
public async Task<DiscordTimeout> SetAsync(
Snowflake guildId,
Snowflake userId,
Instant until,
Snowflake? moderatorId
) =>
await conn.QueryFirstAsync<DiscordTimeout>(
"""
insert into timeouts (user_id, guild_id, moderator_id, until)
values (@UserId, @GuildId, @ModeratorId, @Until)
on conflict (user_id, guild_id) do update
set moderator_id = @ModeratorId,
until = @Until
returning *
""",
new
{
UserId = userId.Value,
GuildId = guildId.Value,
ModeratorId = moderatorId?.Value,
Until = until,
}
);
public async Task<DiscordTimeout?> RemoveAsync(int id) =>
await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
"delete from timeouts where id = @id returning *",
new { id }
);
public async Task<DiscordTimeout?> RemoveAsync(Snowflake guildId, Snowflake userId) =>
await conn.QueryFirstOrDefaultAsync<DiscordTimeout>(
"delete from timeouts where guild_id = @GuildId and user_id = @UserId returning *",
new { GuildId = guildId.Value, UserId = userId.Value }
);
public void Dispose()
{
conn.Dispose();
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await conn.DisposeAsync();
GC.SuppressFinalize(this);
}
}