about summary refs log tree commit diff
path: root/MiniUtils/Commands/RedactCommand.cs
blob: 8aa65fcba6a8d7469b43b45bf35d0f43eb6e98f5 (plain) (blame)
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using System.Collections.Frozen;
using ArcaneLibs.Extensions;
using LibMatrix;
using LibMatrix.EventTypes.Spec;
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) {
        await ignoreListManager.MoveList(false, senders);
        var count = 0;
        // var subCount = 0;
        // List<Task> tasks = [];
        foreach (var senderChunk in senders.Chunk(10)) {
            var filter = new SyncFilter.EventFilter(senders: senderChunk.ToList(), notTypes: ["m.room.redaction"]);
            
            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++;
                //         subCount++;
                //     }
                //
                //     if (subCount >= 40) {
                //         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);
                //         subCount = 0;
                //     }
                // }
                var toRedactQueryTask = resp.Chunk
                    .Where(x => senders.Contains(x.Sender!))
                    .Select(async x => (x, await IsRedactionNeeded(ctx.Room, x.EventId!, x)))
                    .ToList();
                var toRedact = (await Task.WhenAll(toRedactQueryTask)).Where(x => x.Item2).Select(x => x.x).ToList();
                foreach (var chunk in toRedact.Chunk(49)) {
                    var toSend = chunk.Select(x => new StateEvent() {
                        Type = RoomRedactionEventContent.EventId,
                        TypedContent = new RoomRedactionEventContent() {
                            Redacts = x.EventId
                        }
                    }).ToList();
                    toSend.Add(new StateEvent() {
                        Type = RoomMessageEventContent.EventId,
                        TypedContent =
                            new MessageBuilder()
                                .WithBody(
                                    $"[{Emojis.Hourglass}] {Emojis.Recycle} {count} ({Emojis.Checkmark} {count} {Emojis.Hourglass} {toSend.Count})")
                                .Build()
                    });
                    count += toSend.Count - 1;
                    await ctx.Room.BulkSendEventsAsync(toSend);
                }
            }
        }

        // 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;

        return true;
        // 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);
    }
}