2024-09-21 00:13:18 +12:00
|
|
|
|
#nullable disable
|
|
|
|
|
namespace EllieBot.Modules.Administration.Services;
|
|
|
|
|
|
2025-03-22 12:10:17 +13:00
|
|
|
|
public class PruneService(ILogCommandService logService) : IEService
|
2024-09-21 00:13:18 +12:00
|
|
|
|
{
|
|
|
|
|
private readonly ConcurrentDictionary<ulong, CancellationTokenSource> _pruningGuilds = new();
|
|
|
|
|
private readonly TimeSpan _twoWeeks = TimeSpan.FromDays(14);
|
|
|
|
|
|
|
|
|
|
public async Task<PruneResult> PruneWhere(
|
2025-03-22 12:10:17 +13:00
|
|
|
|
ulong runningUserId,
|
2025-01-21 19:51:12 +13:00
|
|
|
|
IMessageChannel channel,
|
2024-09-21 00:13:18 +12:00
|
|
|
|
int amount,
|
|
|
|
|
Func<IMessage, bool> predicate,
|
|
|
|
|
IProgress<(int deleted, int total)> progress,
|
|
|
|
|
ulong? after = null
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(channel, nameof(channel));
|
|
|
|
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
|
|
|
|
|
|
|
|
|
|
var originalAmount = amount;
|
|
|
|
|
|
2025-01-21 19:51:12 +13:00
|
|
|
|
var gid = (channel as ITextChannel)?.GuildId ?? channel.Id;
|
2024-09-21 00:13:18 +12:00
|
|
|
|
using var cancelSource = new CancellationTokenSource();
|
2025-01-21 19:51:12 +13:00
|
|
|
|
if (!_pruningGuilds.TryAdd(gid, cancelSource))
|
2024-09-21 00:13:18 +12:00
|
|
|
|
return PruneResult.AlreadyRunning;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
|
IMessage[] msgs;
|
|
|
|
|
IMessage lastMessage = null;
|
|
|
|
|
|
|
|
|
|
while (amount > 0 && !cancelSource.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
var dled = lastMessage is null
|
|
|
|
|
? await channel.GetMessagesAsync(50).FlattenAsync()
|
|
|
|
|
: await channel.GetMessagesAsync(lastMessage, Direction.Before, 50).FlattenAsync();
|
|
|
|
|
|
|
|
|
|
msgs = dled
|
|
|
|
|
.Where(predicate)
|
|
|
|
|
.Where(x => after is not ulong a || x.Id > a)
|
|
|
|
|
.Take(amount)
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
if (!msgs.Any())
|
|
|
|
|
return PruneResult.Success;
|
|
|
|
|
|
|
|
|
|
lastMessage = msgs[^1];
|
|
|
|
|
|
|
|
|
|
var bulkDeletable = new List<IMessage>();
|
|
|
|
|
var singleDeletable = new List<IMessage>();
|
|
|
|
|
foreach (var x in msgs)
|
|
|
|
|
{
|
2025-03-22 12:10:17 +13:00
|
|
|
|
logService.AddDeleteIgnore(x.Id);
|
2024-09-21 00:13:18 +12:00
|
|
|
|
|
|
|
|
|
if (now - x.CreatedAt < _twoWeeks)
|
|
|
|
|
bulkDeletable.Add(x);
|
|
|
|
|
else
|
|
|
|
|
singleDeletable.Add(x);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-21 19:51:12 +13:00
|
|
|
|
if (channel is ITextChannel tc2 && bulkDeletable.Count > 0)
|
2024-09-21 00:13:18 +12:00
|
|
|
|
{
|
2025-01-21 19:51:12 +13:00
|
|
|
|
await tc2.DeleteMessagesAsync(bulkDeletable);
|
2024-09-21 00:13:18 +12:00
|
|
|
|
amount -= msgs.Length;
|
|
|
|
|
progress.Report((originalAmount - amount, originalAmount));
|
|
|
|
|
await Task.Delay(2000, cancelSource.Token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var group in singleDeletable.Chunk(5))
|
|
|
|
|
{
|
|
|
|
|
await group.Select(x => x.DeleteAsync()).WhenAll();
|
|
|
|
|
amount -= 5;
|
|
|
|
|
progress.Report((originalAmount - amount, originalAmount));
|
|
|
|
|
await Task.Delay(5000, cancelSource.Token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
//ignore
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
2025-01-21 19:51:12 +13:00
|
|
|
|
_pruningGuilds.TryRemove(gid, out _);
|
2024-09-21 00:13:18 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PruneResult.Success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> CancelAsync(ulong guildId)
|
|
|
|
|
{
|
|
|
|
|
if (!_pruningGuilds.TryRemove(guildId, out var source))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
await source.CancelAsync();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|