diff --git a/MatrixContentFilter/Handlers/Filters/Generic/BasicMessageTypeFilter.cs b/MatrixContentFilter/Handlers/Filters/Generic/BasicMessageTypeFilter.cs
new file mode 100644
index 0000000..2a9c674
--- /dev/null
+++ b/MatrixContentFilter/Handlers/Filters/Generic/BasicMessageTypeFilter.cs
@@ -0,0 +1,88 @@
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec;
+using LibMatrix.Helpers;
+using LibMatrix.Homeservers;
+using LibMatrix.Responses;
+using LibMatrix.RoomTypes;
+using MatrixContentFilter.Abstractions;
+using MatrixContentFilter.EventTypes;
+using MatrixContentFilter.Services;
+using MatrixContentFilter.Services.AsyncActionQueues;
+
+namespace MatrixContentFilter.Handlers.Filters.Generic;
+
+public abstract class BasicMessageTypeFilter(
+ ConfigurationService cfgService,
+ AuthenticatedHomeserverGeneric hs,
+ AsyncMessageQueue msgQueue,
+ InfoCacheService infoCache,
+ AbstractAsyncActionQueue actionQueue,
+ MatrixContentFilterMetrics metrics)
+ : IContentFilter {
+ private protected string MessageType = null!;
+ private protected string MessageTypeName = null!;
+ private protected string MessageTypeNamePlural = null!;
+
+ public override Task ProcessSyncAsync(SyncResponse syncResponse) {
+ if (syncResponse.Rooms?.Join is null) return Task.CompletedTask;
+ var tasks = syncResponse.Rooms.Join.Select(ProcessRoomAsync);
+ return Task.WhenAll(tasks);
+ }
+
+ private Task ProcessRoomAsync(KeyValuePair<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure> syncRoom) {
+ var (roomId, roomData) = syncRoom;
+ if (roomId == cfgService.LogRoom.RoomId || roomId == cfgService.ControlRoom.RoomId) return Task.CompletedTask;
+ if (roomData.Timeline?.Events is null) return Task.CompletedTask;
+ var config = cfgService.RoomConfigurationOverrides.GetValueOrDefault(roomId)?.ImageFilter;
+
+ var room = hs.GetRoom(roomId);
+
+ var tasks = roomData.Timeline.Events.Select(msg => ProcessEventAsync(room, msg, config));
+ return Task.WhenAll(tasks);
+ }
+
+ public override Task ProcessEventListAsync(List<StateEventResponse> events) {
+ var tasks = events.GroupBy(x => x.RoomId).Select(async x => {
+ var room = hs.GetRoom(x.Key);
+ var config = cfgService.RoomConfigurationOverrides.GetValueOrDefault(x.Key)?.ImageFilter;
+ var tasks = x.Select(msg => ProcessEventAsync(room, msg, config)).ToList();
+ await Task.WhenAll(tasks);
+ });
+
+ return Task.WhenAll(tasks);
+ }
+
+ private async Task ProcessEventAsync(GenericRoom room, StateEventResponse msg, FilterConfiguration.BasicFilterConfiguration roomConfiguration) {
+ if (msg.Type != "m.room.message") return;
+ var content = msg.TypedContent as RoomMessageEventContent;
+ if (content?.MessageType != MessageType) return;
+ metrics.Increment(MessageTypeName + "_filter_processed_event_count");
+
+ await actionQueue.EqueueActionAsync(msg.EventId, async () => {
+ while (true) {
+ try {
+ Console.WriteLine("Redacting {0} message: {1}", MessageTypeName, msg.EventId);
+ await room.RedactEventAsync(msg.EventId ?? throw new ArgumentException("Event ID is null?"), $"Not allowed to send {MessageTypeNamePlural} in this room!");
+ break;
+ }
+ catch (Exception e) {
+ msgQueue.EnqueueMessageAsync(cfgService.LogRoom, new MessageBuilder("m.notice")
+ .WithBody($"Error redacting {MessageTypeName} message in {room.RoomId}!")
+ .WithCollapsibleSection("Error data", msb => msb.WithCodeBlock(e.ToString(), "csharp"))
+ .Build());
+ }
+ }
+
+ var displayName = await infoCache.GetDisplayNameAsync(room.RoomId, msg.Sender);
+ var roomName = await infoCache.GetRoomNameAsync(room.RoomId);
+
+ msgQueue.EnqueueMessageAsync(cfgService.LogRoom, new MessageBuilder("m.notice")
+ .WithBody($"{MessageTypeName[0].ToString().ToUpper() + MessageTypeName[1..]} sent by ").WithMention(msg.Sender, displayName).WithBody(" in ")
+ .WithMention(room.RoomId, roomName).WithBody(" was removed!").WithNewline()
+ .WithCollapsibleSection("Message data", msb => msb.WithCodeBlock(content.ToJson(ignoreNull: true), "json"))
+ .Build());
+ });
+ ActionCount++;
+ }
+}
\ No newline at end of file
diff --git a/MatrixContentFilter/Handlers/Filters/Generic/GenericEventTypeFilter.cs b/MatrixContentFilter/Handlers/Filters/Generic/GenericEventTypeFilter.cs
new file mode 100644
index 0000000..64e4cff
--- /dev/null
+++ b/MatrixContentFilter/Handlers/Filters/Generic/GenericEventTypeFilter.cs
@@ -0,0 +1,86 @@
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec;
+using LibMatrix.Helpers;
+using LibMatrix.Homeservers;
+using LibMatrix.Responses;
+using LibMatrix.RoomTypes;
+using MatrixContentFilter.Abstractions;
+using MatrixContentFilter.EventTypes;
+using MatrixContentFilter.Services;
+using MatrixContentFilter.Services.AsyncActionQueues;
+
+namespace MatrixContentFilter.Handlers.Filters.Generic;
+
+public abstract class GenericEventTypeFilter(
+ ConfigurationService cfgService,
+ AuthenticatedHomeserverGeneric hs,
+ AsyncMessageQueue msgQueue,
+ InfoCacheService infoCache,
+ AbstractAsyncActionQueue actionQueue,
+ MatrixContentFilterMetrics metrics)
+ : IContentFilter {
+ private protected abstract string? GetEventType();
+
+ public override Task ProcessSyncAsync(SyncResponse syncResponse) {
+ if (syncResponse.Rooms?.Join is null) return Task.CompletedTask;
+ var tasks = syncResponse.Rooms.Join.Select(ProcessRoomAsync);
+ return Task.WhenAll(tasks);
+ }
+
+ private Task ProcessRoomAsync(KeyValuePair<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure> syncRoom) {
+ var (roomId, roomData) = syncRoom;
+ if (roomId == cfgService.LogRoom.RoomId || roomId == cfgService.ControlRoom.RoomId) return Task.CompletedTask;
+ if (roomData.Timeline?.Events is null) return Task.CompletedTask;
+ var config = cfgService.RoomConfigurationOverrides.GetValueOrDefault(roomId)?.ImageFilter;
+
+ var room = hs.GetRoom(roomId);
+
+ var tasks = roomData.Timeline.Events.Select(msg => ProcessEventAsync(room, msg, config));
+ return Task.WhenAll(tasks);
+ }
+
+ public override Task ProcessEventListAsync(List<StateEventResponse> events) {
+ var tasks = events.GroupBy(x => x.RoomId).Select(async x => {
+ var room = hs.GetRoom(x.Key);
+ var config = cfgService.RoomConfigurationOverrides.GetValueOrDefault(x.Key)?.ImageFilter;
+ var tasks = x.Select(msg => ProcessEventAsync(room, msg, config)).ToList();
+ await Task.WhenAll(tasks);
+ });
+
+ return Task.WhenAll(tasks);
+ }
+
+ private async Task ProcessEventAsync(GenericRoom room, StateEventResponse msg, FilterConfiguration.BasicFilterConfiguration roomConfiguration) {
+ if (msg.Type != "m.room.message") return;
+ var content = msg.TypedContent as RoomMessageEventContent;
+ if (content?.MessageType != GetEventType()) return;
+ metrics.Increment(GetMetricsPrefix() + "_filter_processed_event_count");
+
+ await actionQueue.EqueueActionAsync(msg.EventId, async () => {
+ while (true) {
+ try {
+ Console.WriteLine("Redacting {0} message: {1}", MessageTypeName, msg.EventId);
+ await room.RedactEventAsync(msg.EventId ?? throw new ArgumentException("Event ID is null?"), $"Not allowed to send {MessageTypeNamePlural} in this room!");
+ break;
+ }
+ catch (Exception e) {
+ msgQueue.EnqueueMessageAsync(cfgService.LogRoom, new MessageBuilder("m.notice")
+ .WithBody($"Error redacting {MessageTypeName} message in {room.RoomId}!")
+ .WithCollapsibleSection("Error data", msb => msb.WithCodeBlock(e.ToString(), "csharp"))
+ .Build());
+ }
+ }
+
+ var displayName = await infoCache.GetDisplayNameAsync(room.RoomId, msg.Sender);
+ var roomName = await infoCache.GetRoomNameAsync(room.RoomId);
+
+ msgQueue.EnqueueMessageAsync(cfgService.LogRoom, new MessageBuilder("m.notice")
+ .WithBody($"{MessageTypeName[0].ToString().ToUpper() + MessageTypeName[1..]} sent by ").WithMention(msg.Sender, displayName).WithBody(" in ")
+ .WithMention(room.RoomId, roomName).WithBody(" was removed!").WithNewline()
+ .WithCollapsibleSection("Message data", msb => msb.WithCodeBlock(content.ToJson(ignoreNull: true), "json"))
+ .Build());
+ });
+ ActionCount++;
+ }
+}
\ No newline at end of file
|