add: Added .cleanupguilddata command which will delete all guildconfigs, xp stats and other data related to any guild the bot is no longer in. This is a highly destructive and irreversible command.

dev: Added cascade deletes to any tables which have guildconfigs FK, as well as to some other missing places
This commit is contained in:
Kwoth
2024-05-18 16:09:54 +00:00
parent 03fb1a5ca2
commit 0c167a9382
28 changed files with 13286 additions and 273 deletions

View File

@@ -0,0 +1,31 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public partial class Administration
{
[Group]
public class CleanupCommands : CleanupModuleBase
{
private readonly ICleanupService _svc;
public CleanupCommands(ICleanupService svc)
=> _svc = svc;
[Cmd]
[OwnerOnly]
[RequireContext(ContextType.DM)]
public async Task CleanupGuildData()
{
var result = await _svc.DeleteMissingGuildDataAsync();
if (result is null)
{
await ctx.ErrorAsync();
return;
}
await Response()
.Confirm($"{result.GuildCount} guilds' data remain in the database.")
.SendAsync();
}
}
}

View File

@@ -0,0 +1,106 @@
using LinqToDB;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
{
private readonly IPubSub _pubSub;
private TypedKey<KeepReport> _keepReportKey = new("cleanup:report");
private TypedKey<bool> _keepTriggerKey = new("cleanup:trigger");
private readonly DiscordSocketClient _client;
private ConcurrentDictionary<int, ulong[]> guildIds;
private readonly IBotCredsProvider _creds;
private readonly DbService _db;
public CleanupService(
IPubSub pubSub,
DiscordSocketClient client,
IBotCredsProvider creds,
DbService db)
{
_pubSub = pubSub;
_client = client;
_creds = creds;
_db = db;
}
public async Task<KeepResult?> DeleteMissingGuildDataAsync()
{
guildIds = new();
var totalShards = _creds.GetCreds().TotalShards;
await _pubSub.Pub(_keepTriggerKey, true);
var counter = 0;
while (guildIds.Keys.Count < totalShards)
{
await Task.Delay(1000);
counter++;
if (counter >= 5)
break;
}
if (guildIds.Keys.Count < totalShards)
return default;
var allIds = guildIds.SelectMany(x => x.Value)
.ToArray();
await using var ctx = _db.GetDbContext();
await using var linqCtx = ctx.CreateLinqToDBContext();
await using var tempTable = linqCtx.CreateTempTable<CleanupId>();
foreach (var chunk in allIds.Chunk(20000))
{
await tempTable.BulkCopyAsync(chunk.Select(x => new CleanupId()
{
GuildId = x
}));
}
await ctx.GetTable<GuildConfig>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
await ctx.GetTable<UserXpStats>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
return new()
{
GuildCount = guildIds.Keys.Count,
};
}
private ValueTask OnKeepReport(KeepReport report)
{
guildIds[report.ShardId] = report.GuildIds;
return default;
}
public async Task OnReadyAsync()
{
await _pubSub.Sub(_keepTriggerKey, OnKeepTrigger);
if (_client.ShardId == 0)
await _pubSub.Sub(_keepReportKey, OnKeepReport);
}
private ValueTask OnKeepTrigger(bool arg)
{
_pubSub.Pub(_keepReportKey,
new KeepReport()
{
ShardId = _client.ShardId,
GuildIds = _client.GetGuildIds(),
});
return default;
}
}

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class CleanupId
{
[Key]
public ulong GuildId { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public interface ICleanupService
{
Task<KeepResult?> DeleteMissingGuildDataAsync();
}

View File

@@ -0,0 +1,7 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class KeepReport
{
public required int ShardId { get; init; }
public required ulong[] GuildIds { get; init; }
}

View File

@@ -0,0 +1,6 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class KeepResult
{
public required int GuildCount { get; init; }
}