1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
using System.Collections.Frozen;
using ArcaneLibs.Extensions;
using LibMatrix;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Filters;
using LibMatrix.Helpers;
using LibMatrix.RoomTypes;
using LibMatrix.Utilities.Bot.Interfaces;
using MiniUtils.Classes;
using MiniUtils.Services;
namespace MiniUtils.Commands;
public class RedactCommand(IgnoreListManager ignoreListManager) : ICommand {
public string Name => "redact";
public string[]? Aliases => [];
public string Description => "Redact all user's events";
public bool Unlisted => false;
public async Task Invoke(CommandContext ctx) {
if (ctx.Args is ["banned"])
await RedactUsers(ctx, await ctx.Room.GetMemberIdsListAsync("ban"));
else if (ctx.Args is [.. var senders]) {
var sendersSet = senders.ToFrozenSet();
await RedactUsers(ctx, sendersSet);
}
}
private async Task RedactUsers(CommandContext ctx, FrozenSet<string> senders) {
var filter = new SyncFilter.EventFilter(senders: senders.ToList(), notTypes: ["m.room.redaction"]);
await ignoreListManager.MoveList(false, senders);
var count = 0;
List<Task> tasks = [];
await foreach (var resp in ctx.Room.GetManyMessagesAsync(filter: filter.ToJson(false, ignoreNull: true), chunkSize: 1000)) {
foreach (var chunk in resp.Chunk.Chunk(49)) {
foreach (var evt in chunk) {
if (!senders.Contains(evt.Sender!)) continue;
if(!await IsRedactionNeeded(ctx.Room, evt.EventId!, evt)) continue;
tasks.Add(RedactEvent(ctx.Room, evt.EventId!));
count++;
}
if (tasks.Count > 0) {
await ctx.Room.SendMessageEventAsync(new MessageBuilder()
.WithBody(
$"[{Emojis.Hourglass}] {Emojis.Recycle} {count} ({Emojis.Checkmark} {tasks.Count(t => t.IsCompletedSuccessfully)} {Emojis.Prohibited} {tasks.Count(t => t.IsFaulted)} {Emojis.Hourglass} {tasks.Count(t => t.Status == TaskStatus.Running)})")
.Build());
// await Task.WhenAll(tasks);
}
}
}
await Task.WhenAll(tasks);
await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"{Emojis.Recycle} {count}").Build());
// await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.Recycle} {count}");
}
private async Task<bool> IsRedactionNeeded(GenericRoom roomId, string eventId, StateEventResponse? evt = null) {
evt ??= await roomId.GetEventAsync(eventId);
// Ignore room member state events
if (evt is { StateKey: not null, Type: not RoomMemberEventContent.EventId }) return false;
// Ignore redaction events
if (evt is { Type: RoomRedactionEventContent.EventId }) return false;
// Ignore empty events
if (evt is { RawContent: null or { Count: 0 } }) return false;
// Ignore redacted events
if (evt.Unsigned?.ContainsKey("redacted_because") == true) return false;
throw new NotImplementedException("Redaction check not implemented");
}
private async Task RedactEvent(GenericRoom room, string eventId) {
bool success;
do {
try {
await room.RedactEventAsync(eventId);
success = true;
}
catch (Exception e) {
success = false;
Console.WriteLine($"Failed to redact event {eventId}: {e}");
}
} while (!success);
}
}
|