diff options
author | Rory& <root@rory.gay> | 2024-10-04 19:51:44 +0200 |
---|---|---|
committer | Rory& <root@rory.gay> | 2024-10-04 19:51:44 +0200 |
commit | c8f7ef7c1d2bd705a5442c0dc591b8e5a50673a5 (patch) | |
tree | 9b951c6e2c120ec370ce8318238aadbdda880a89 /MatrixContentFilter/Commands | |
download | MatrixContentFilter-master.tar.xz |
Diffstat (limited to 'MatrixContentFilter/Commands')
7 files changed, 340 insertions, 0 deletions
diff --git a/MatrixContentFilter/Commands/CheckHistoryCommand.cs b/MatrixContentFilter/Commands/CheckHistoryCommand.cs new file mode 100644 index 0000000..1e98545 --- /dev/null +++ b/MatrixContentFilter/Commands/CheckHistoryCommand.cs @@ -0,0 +1,74 @@ +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.Utilities.Bot.Interfaces; +using MatrixContentFilter.Abstractions; +using MatrixContentFilter.Services; +using Microsoft.Extensions.Logging; + +namespace MatrixContentFilter.Commands; + +public class CheckHistoryCommand( + ConfigurationService filterConfigService, + IEnumerable<IContentFilter> filters, + AsyncMessageQueue msgQueue, + InfoCacheService infoCache +) : ICommand { + public string Name { get; } = "checkhistory"; + public string[]? Aliases { get; } = ["check"]; + public string Description { get; } = "Re-apply filters to last x messages (default: 100)"; + public bool Unlisted { get; } = false; + + public async Task Invoke(CommandContext ctx) { + var count = 100; + if (ctx.Args.Length > 0) { + if (!int.TryParse(ctx.Args[0], out count)) { + await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice").WithBody($"'{count}' is not a valid number!").Build()); + return; + } + } + + msgQueue.EnqueueMessageAsync(filterConfigService.LogRoom, + new MessageBuilder("m.notice").WithBody($"Re-applying filters to last {count} messages in ") + .WithMention(ctx.Room.RoomId, await infoCache.GetRoomNameAsync(ctx.Room.RoomId)).Build()); + + await foreach (var resp in ctx.Room.GetManyMessagesAsync(limit: count, chunkSize: Math.Min(count, 250))) { + foreach (var filter in filters) { + await filter.ProcessEventListAsync(resp.Chunk); + } + } + } + + // /// <summary>Triggered when the application host is ready to start the service.</summary> + // /// <param name="cancellationToken">Indicates that the start process has been aborted.</param> + // protected override async Task ExecuteAsync(CancellationToken cancellationToken) { + // while (!cancellationToken.IsCancellationRequested) { + // await Task.Delay(10000, cancellationToken); + // var rooms = await hs.GetJoinedRooms(); + // rooms.RemoveAll(x => x.RoomId == filterConfigService.LogRoom.RoomId); + // rooms.RemoveAll(x => x.RoomId == filterConfigService.ControlRoom.RoomId); + // + // var timelineFilter = new SyncFilter.RoomFilter.StateFilter(notTypes: ["m.room.redaction"], limit: 5000); + // var timelines = rooms.Select(async x => { + // var room = hs.GetRoom(x.RoomId); + // // var sync = await room.GetMessagesAsync(null, 1500, filter: timelineFilter.ToJson(ignoreNull: true, indent: false).UrlEncode()); + // var iter = room.GetManyMessagesAsync(null, 5000, filter: timelineFilter.ToJson(ignoreNull: true, indent: false).UrlEncode(), chunkSize: 250); + // await foreach (var sync in iter) { + // var tasks = Parallel.ForEachAsync(filters, async (filter, ct) => { + // try { + // Console.WriteLine("Processing filter {0} (sanity check, chunk[s={1}])", filter.GetType().FullName, sync.Chunk.Count); + // await filter.ProcessEventListAsync(sync.Chunk); + // } + // catch (Exception e) { + // logger.LogError(e, "Error processing sync with filter {filter}", filter.GetType().FullName); + // msgQueue.EnqueueMessageAsync(filterConfigService.LogRoom, new MessageBuilder("m.notice") + // .WithBody($"Error processing sync with filter {filter.GetType().FullName}: {e.Message}").Build()); + // } + // }); + // + // await tasks; + // } + // }).ToList(); + // await Task.WhenAll(timelines); + // } + // } +} \ No newline at end of file diff --git a/MatrixContentFilter/Commands/ConfigureCommand.cs b/MatrixContentFilter/Commands/ConfigureCommand.cs new file mode 100644 index 0000000..756fc14 --- /dev/null +++ b/MatrixContentFilter/Commands/ConfigureCommand.cs @@ -0,0 +1,38 @@ +using System.Text; +using LibMatrix.EventTypes.Spec; +using LibMatrix.Utilities.Bot.Commands; +using LibMatrix.Utilities.Bot.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace MatrixContentFilter.Commands; + +public class ConfigureCommand(IServiceProvider svcs) : ICommandGroup { + public string Name { get; } = "configure"; + public string[]? Aliases { get; } = ["config", "cfg"]; + public string Description { get; } + public bool Unlisted { get; } = true; + + public async Task Invoke(CommandContext ctx) { + var commands = svcs.GetServices<ICommand>().Where(x => x.GetType().IsAssignableTo(typeof(ICommand<>).MakeGenericType(GetType()))).ToList(); + + if (ctx.Args.Length == 0) { + await ctx.Room.SendMessageEventAsync(HelpCommand.GenerateCommandList(commands).Build()); + } + else { + var subcommand = ctx.Args[0]; + var command = commands.FirstOrDefault(x => x.Name == subcommand || x.Aliases?.Contains(subcommand) == true); + if (command == null) { + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent("m.notice", "Unknown subcommand")); + return; + } + + await command.Invoke(new CommandContext { + Room = ctx.Room, + MessageEvent = ctx.MessageEvent, + CommandName = ctx.CommandName, + Args = ctx.Args.Skip(1).ToArray(), + Homeserver = ctx.Homeserver + }); + } + } +} \ No newline at end of file diff --git a/MatrixContentFilter/Commands/ConfigureSubCommands/ControlRoomConfigureSubcommand.cs b/MatrixContentFilter/Commands/ConfigureSubCommands/ControlRoomConfigureSubcommand.cs new file mode 100644 index 0000000..5ff4f9d --- /dev/null +++ b/MatrixContentFilter/Commands/ConfigureSubCommands/ControlRoomConfigureSubcommand.cs @@ -0,0 +1,18 @@ +using LibMatrix.Helpers; +using LibMatrix.Utilities.Bot.Interfaces; + +namespace MatrixContentFilter.Commands.ConfigureSubCommands; + +public class ControlRoomConfigureSubCommand : ICommand<ConfigureCommand> { + public string Name { get; } = "controlroom"; + public string[]? Aliases { get; } + public string Description { get; } = "Configure the control room"; + public bool Unlisted { get; } + + public async Task Invoke(CommandContext ctx) { + if (ctx.Args.Length == 0) { + await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice").WithBody("meow").Build()); + } + + } +} \ No newline at end of file diff --git a/MatrixContentFilter/Commands/DumpEventCommand.cs b/MatrixContentFilter/Commands/DumpEventCommand.cs new file mode 100644 index 0000000..5131e19 --- /dev/null +++ b/MatrixContentFilter/Commands/DumpEventCommand.cs @@ -0,0 +1,31 @@ +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.Filters; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.Utilities.Bot.Interfaces; +using MatrixContentFilter.Abstractions; +using MatrixContentFilter.Services; +using MatrixContentFilter.Services.AsyncActionQueues; +using Microsoft.Extensions.Logging; + +namespace MatrixContentFilter.Commands; + +public class DumpEventCommand( + ConfigurationService filterConfigService, + AsyncMessageQueue msgQueue, + InfoCacheService infoCache, + ConfigurationService cfgService, + AbstractAsyncActionQueue actionQueue +) : ICommand { + public string Name { get; } = "dump"; + public string[]? Aliases { get; } = []; + public string Description { get; } = "Dump event by ID"; + public bool Unlisted { get; } = false; + + public async Task Invoke(CommandContext ctx) { + var evt = await ctx.Room.GetEventAsync(ctx.Args[0]); + await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice").WithBody(evt.ToJson(ignoreNull: true)).Build()); + } +} \ No newline at end of file diff --git a/MatrixContentFilter/Commands/GetConfigCommand.cs b/MatrixContentFilter/Commands/GetConfigCommand.cs new file mode 100644 index 0000000..bac00ca --- /dev/null +++ b/MatrixContentFilter/Commands/GetConfigCommand.cs @@ -0,0 +1,56 @@ +using System.Text; +using ArcaneLibs.Attributes; +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.Helpers; +using LibMatrix.Utilities.Bot.Commands; +using LibMatrix.Utilities.Bot.Interfaces; +using MatrixContentFilter.EventTypes; +using Microsoft.Extensions.DependencyInjection; + +namespace MatrixContentFilter.Commands; + +public class GetConfigCommand(IServiceProvider svcs) : ICommand { + public string Name { get; } = "getconfig"; + public string[]? Aliases { get; } = []; + public string Description { get; } = "Get the current configuration, optionally takes a room ID"; + public bool Unlisted { get; } = false; + + public async Task Invoke(CommandContext ctx) { + var room = ctx.Room; + if (ctx.Args.Length > 0) { + try { + room = ctx.Homeserver.GetRoom(ctx.Args[0]); + } + catch { + await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice").WithBody("Invalid room ID").Build()); + return; + } + } + + var defaults = await ctx.Homeserver.GetAccountDataAsync<FilterConfiguration>(FilterConfiguration.EventId); + var config = await room.GetRoomAccountDataOrNullAsync<FilterConfiguration>(FilterConfiguration.EventId); + var msb = new MessageBuilder("m.notice").WithColoredBody("#FFCC00", "Default configuration:") + .WithTable(tb => { + foreach (var prop in defaults.GetType().GetProperties()) { + var key = prop.GetFriendlyName(); + var val = prop.GetValue(defaults); + + tb = tb.WithRow(rb => { + rb.WithCell(key); + rb.WithCell(val?.ToJson() ?? "null"); + }); + } + }); + + if (config == null) { + msb = msb.WithBody("No configuration set for this room, using defaults"); + } + else { + msb = msb.WithBody("Room overrides (additive):") + .WithCodeBlock(config.ToJson(ignoreNull: true), "json"); + } + + await room.SendMessageEventAsync(msb.Build()); + } +} \ No newline at end of file diff --git a/MatrixContentFilter/Commands/NewRoomCommand.cs b/MatrixContentFilter/Commands/NewRoomCommand.cs new file mode 100644 index 0000000..b8becd4 --- /dev/null +++ b/MatrixContentFilter/Commands/NewRoomCommand.cs @@ -0,0 +1,24 @@ +using System.Text; +using ArcaneLibs.Attributes; +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.Helpers; +using LibMatrix.Utilities.Bot.Commands; +using LibMatrix.Utilities.Bot.Interfaces; +using MatrixContentFilter.EventTypes; +using Microsoft.Extensions.DependencyInjection; + +namespace MatrixContentFilter.Commands; + +public class NewRoomCommand(IServiceProvider svcs) : ICommand { + public string Name { get; } = "newroom"; + public string[]? Aliases { get; } = ["nr"]; + public string Description { get; } = "Create a new room"; + public bool Unlisted { get; } = false; + + public async Task Invoke(CommandContext ctx) { + await ctx.Homeserver.CreateRoom(new() { + Invite = [ctx.MessageEvent.Sender!] + }); + } +} \ No newline at end of file diff --git a/MatrixContentFilter/Commands/RedactCommand.cs b/MatrixContentFilter/Commands/RedactCommand.cs new file mode 100644 index 0000000..6b2f8b6 --- /dev/null +++ b/MatrixContentFilter/Commands/RedactCommand.cs @@ -0,0 +1,99 @@ +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.Filters; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.Utilities.Bot.Interfaces; +using MatrixContentFilter.Abstractions; +using MatrixContentFilter.Services; +using MatrixContentFilter.Services.AsyncActionQueues; +using Microsoft.Extensions.Logging; + +namespace MatrixContentFilter.Commands; + +public class RedactCommand( + ConfigurationService filterConfigService, + AsyncMessageQueue msgQueue, + InfoCacheService infoCache, + ConfigurationService cfgService, + AbstractAsyncActionQueue actionQueue +) : ICommand { + public string Name { get; } = "redact"; + public string[]? Aliases { get; } = []; + public string Description { get; } = "Redact last x messages from user (default: 500)"; + public bool Unlisted { get; } = false; + + public async Task Invoke(CommandContext ctx) { + var count = 500; + + if (ctx.Args.Length == 0) { + await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice") + .WithBody("Please provide a user ID to redact messages from. (Make sure that it isn't formatted! Do not autocomplete!)").Build()); + return; + } + + var mxid = ctx.Args[0]; + if (ctx.Args.Length > 1) { + if (!int.TryParse(ctx.Args[1], out count)) { + await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice").WithBody($"'{count}' is not a valid number!").Build()); + return; + } + } + + var displayName = await infoCache.GetDisplayNameAsync(ctx.Room.RoomId, ctx.MessageEvent.Sender); + var roomName = await infoCache.GetRoomNameAsync(ctx.Room.RoomId); + + msgQueue.EnqueueMessageAsync(filterConfigService.LogRoom, + new MessageBuilder("m.notice").WithBody($"Removing last {count} messages from ").WithMention(mxid) + .WithBody(" in ").WithMention(ctx.Room.RoomId, await infoCache.GetRoomNameAsync(ctx.Room.RoomId)).Build()); + var hourglassReaction = await ctx.Room.SendTimelineEventAsync("m.reaction", new RoomMessageReactionEventContent() { + RelatesTo = new() { + EventId = ctx.MessageEvent.EventId, + RelationType = "m.annotation", + Key = "\u23f3" //hour glass emoji + } + }); + + await foreach (var resp in ctx.Room.GetManyMessagesAsync(limit: count, chunkSize: Math.Min(count, 250) + ,filter: new SyncFilter.RoomFilter.StateFilter(types: [RoomMemberEventContent.EventId, RoomMessageEventContent.EventId], senders: [mxid]) + .ToJson(indent: false, ignoreNull: true).UrlEncode()) + ) { + foreach (var msg in resp.Chunk) { + if (msg.Sender != mxid) continue; + if (msg is not { Type: RoomMemberEventContent.EventId or RoomMessageEventContent.EventId }) continue; + if (msg.RawContent is not { Count: > 0 }) continue; + + await actionQueue.EqueueActionAsync(msg.EventId, async () => { + while (true) { + try { + await ctx.Room.RedactEventAsync(msg.EventId ?? throw new ArgumentException("Event ID is null?"), "Message removed by moderator."); + break; + } + catch (Exception e) { + msgQueue.EnqueueMessageAsync(cfgService.LogRoom, new MessageBuilder("m.notice") + .WithBody($"Error redacting message in {ctx.Room.RoomId}!") + .WithCollapsibleSection("Error data", msb => msb.WithCodeBlock(e.ToString(), "csharp")) + .Build()); + } + } + + msgQueue.EnqueueMessageAsync(cfgService.LogRoom, new MessageBuilder("m.notice") + .WithBody($"Message sent by ").WithMention(msg.Sender, displayName).WithBody(" in ").WithMention(ctx.Room.RoomId, roomName) + .WithBody(" was removed in request by ").WithMention(ctx.Room.RoomId, roomName).WithBody("!").WithNewline() + .WithCollapsibleSection("Message data", msb => msb.WithCodeBlock(msg.RawContent.ToJson(ignoreNull: true), "json")) + .Build()); + }); + } + + await ctx.Room.RedactEventAsync(hourglassReaction.EventId); + await ctx.Room.SendTimelineEventAsync("m.reaction", new RoomMessageReactionEventContent() { + RelatesTo = new() { + EventId = ctx.MessageEvent.EventId, + RelationType = "m.annotation", + Key = "\u2714\ufe0f" //check mark emoji + } + }); + } + } +} \ No newline at end of file |