about summary refs log tree commit diff
path: root/MiniUtils/Commands
diff options
context:
space:
mode:
Diffstat (limited to 'MiniUtils/Commands')
-rw-r--r--MiniUtils/Commands/ExperimentalFeaturesCommand.cs68
-rw-r--r--MiniUtils/Commands/IgnoreCommand.cs62
-rw-r--r--MiniUtils/Commands/MscCommand.cs24
-rw-r--r--MiniUtils/Commands/RedactCommand.cs80
-rw-r--r--MiniUtils/Commands/SpamCommand.cs44
5 files changed, 278 insertions, 0 deletions
diff --git a/MiniUtils/Commands/ExperimentalFeaturesCommand.cs b/MiniUtils/Commands/ExperimentalFeaturesCommand.cs
new file mode 100644

index 0000000..de5d035 --- /dev/null +++ b/MiniUtils/Commands/ExperimentalFeaturesCommand.cs
@@ -0,0 +1,68 @@ +using System.Globalization; +using System.Net.Http.Json; +using System.Text; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.Extensions; +using LibMatrix.Helpers; +using LibMatrix.Utilities.Bot.Interfaces; +using MiniUtils.Utilities; + +namespace SynapseDataMiner.Commands; + +public class ExperimentalFeaturesCommand(MscInfoProvider mscInfoProvider) : ICommand { + public string Name { get; } = "experimental"; + public string[]? Aliases { get; } = []; + public string Description { get; } = "List experimental synapse features"; + public bool Unlisted { get; } = false; + + private static readonly Regex ExperimentalGetMultilineRegex = new("""experimental\.get\(\s*"(?<key>.+?)"(,\s*(?<defaultValue>.+?))?\s*\)""", + RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); + + private static readonly Regex MscNumberRegex = new(@"msc(?<id>\d+)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public async Task Invoke(CommandContext ctx) { + var hc = new MatrixHttpClient(); + var resp = await hc.GetAsync("https://raw.githubusercontent.com/element-hq/synapse/develop/synapse/config/experimental.py"); + var data = await resp.Content.ReadAsStringAsync(); + + var msb = new MessageBuilder("m.notice"); + List<SynapseFeature> features = []; + + foreach (Match match in ExperimentalGetMultilineRegex.Matches(data)) { + var mscMatch = MscNumberRegex.Match(match.Groups["key"].Value); + features.Add(new() { + ConfigKey = match.Groups["key"].Value, + DefaultValue = match.Groups["defaultValue"]?.Value, + MscInfo = mscMatch.Success ? await mscInfoProvider.GetMscInfo(int.Parse(mscMatch.Groups["id"].Value)) : null + }); + } + + msb.WithTable(tb => { + tb.WithTitle("Available features", 2); + tb.WithRow(rb => { + rb.WithCell("Feature flag"); + rb.WithCell("Description"); + }); + + foreach (var feature in features.OrderBy(x => x.ConfigKey)) { + tb.WithRow(rb => { + rb.WithCell($"{feature.ConfigKey}<br/>Default: {feature.DefaultValue}"); + rb.WithCell(feature.MscInfo is not null ? feature.MscInfo.ToHtml() : "No MSC info found"); + }); + } + }); + + await ctx.Room.SendMessageEventAsync(msb.Build()); + Console.WriteLine(msb.Build().FormattedBody); + } + + private class SynapseFeature { + public string ConfigKey { get; set; } + public string? DefaultValue { get; set; } + public MscInfoProvider.MscInfo? MscInfo { get; set; } + } + + +} \ No newline at end of file diff --git a/MiniUtils/Commands/IgnoreCommand.cs b/MiniUtils/Commands/IgnoreCommand.cs new file mode 100644
index 0000000..4b3fe86 --- /dev/null +++ b/MiniUtils/Commands/IgnoreCommand.cs
@@ -0,0 +1,62 @@ +using LibMatrix.EventTypes.Spec; +using LibMatrix.Helpers; +using LibMatrix.Utilities.Bot.Interfaces; +using MiniUtils.Classes; +using MiniUtils.Services; + +namespace MiniUtils.Commands; + +public class IgnoreCommand(IgnoreListManager ignoreListManager) : ICommand { + public string Name => "ignore"; + + public string[]? Aliases => ["ignorelist"]; + + public string Description => "Manage ignore list"; + + public bool Unlisted => false; + + public async Task Invoke(CommandContext ctx) { + var ignoreList = await ctx.Homeserver.GetAccountDataOrNullAsync<IgnoredUserListEventContentWithDisabled>(IgnoredUserListEventContent.EventId) ?? new(); + if (ctx.Args.Length == 0) + await Summarize(ctx, ignoreList); + else if (ctx.Args is ["disable", "all"] or ["disableall"] or ["disall"]) { + var count = await ignoreListManager.DisableAll(); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.Recycle} {count}"); + } + else if (ctx.Args is ["enable", "all"] or ["enableall"] or ["enall"]) { + var count = await ignoreListManager.EnableAll(); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.Bullseye} {count}"); + } + else if (ctx.Args is ["disable", "joined"] or ["disablejoined"]) { + var count = await ignoreListManager.MoveList(false, (await ctx.Room.GetMembersListAsync("join")).Select(x => x.StateKey!)); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}"); + } + else if (ctx.Args is ["enable", "joined"] or ["enablejoined"]) { + var count = await ignoreListManager.MoveList(true, (await ctx.Room.GetMembersListAsync("join")).Select(x => x.StateKey!)); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}"); + } + else if (ctx.Args is ["disable", "local"] or ["disablelocal"] or ["disable", "room"] or ["disableroom"]) { + var count = await ignoreListManager.MoveList(false, (await ctx.Room.GetMembersListAsync()).Select(x => x.StateKey!)); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}"); + } + else if (ctx.Args is ["enable", "local"] or ["enablelocal"] or ["enable", "room"] or ["enableroom"]) { + var count = await ignoreListManager.MoveList(true, (await ctx.Room.GetMembersListAsync()).Select(x => x.StateKey!)); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}"); + } + else if (ctx.Args is ["disable", .. var itemsToDisable]) { + var count = await ignoreListManager.MoveList(false, itemsToDisable); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}"); + } + else if (ctx.Args is ["enable", .. var itemsToEnable]) { + var count = await ignoreListManager.MoveList(true, itemsToEnable); + await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}"); + } + } + + private async Task Summarize(CommandContext ctx, IgnoredUserListEventContentWithDisabled ignoreList) { + var msb = new MessageBuilder() + .WithBody($"Ignored users: {ignoreList.IgnoredUsers.Count}").WithNewline() + .WithBody($"Disabled ignores: {ignoreList.DisabledIgnoredUsers.Count}").WithNewline(); + await ctx.Room.SendMessageEventAsync(msb.Build()); + } + } \ No newline at end of file diff --git a/MiniUtils/Commands/MscCommand.cs b/MiniUtils/Commands/MscCommand.cs new file mode 100644
index 0000000..62f1bd7 --- /dev/null +++ b/MiniUtils/Commands/MscCommand.cs
@@ -0,0 +1,24 @@ +using LibMatrix.Extensions; +using LibMatrix.Helpers; +using LibMatrix.Utilities.Bot.Interfaces; +using MiniUtils.Utilities; + +namespace MiniUtils.Commands; + +public class MscCommand(MscInfoProvider mscInfoProvider) : ICommand { + public string Name { get; } = "msc"; + public string[]? Aliases { get; } = []; + public string Description { get; } = "Get MSC info"; + public bool Unlisted { get; } = false; + + public async Task Invoke(CommandContext ctx) { + var msb = new MessageBuilder("m.notice"); + var id = int.Parse(ctx.Args[0]); + var mscInfo = await mscInfoProvider.GetMscInfo(id); + + msb.WithBody(mscInfo?.ToHtml() ?? "No info found!"); + + await ctx.Reply(msb.Build()); + Console.WriteLine(msb.Build().FormattedBody); + } +} \ No newline at end of file diff --git a/MiniUtils/Commands/RedactCommand.cs b/MiniUtils/Commands/RedactCommand.cs new file mode 100644
index 0000000..cba06c9 --- /dev/null +++ b/MiniUtils/Commands/RedactCommand.cs
@@ -0,0 +1,80 @@ +using System.Collections.Frozen; +using ArcaneLibs.Extensions; +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; + private const string ThumbsUp = "\ud83d\udc4d\ufe0e"; + private const string Recycle = "\u267b\ufe0e"; + private const string Bullseye = "\u25ce\ufe0e"; + private const string RightArrowWithTail = "\u21a3\ufe0e"; + + 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 (evt is { StateKey: not null, Type: not RoomMemberEventContent.EventId }) continue; + if (evt is { RawContent: null or { Count: 0 } }) 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 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); + } +} \ No newline at end of file diff --git a/MiniUtils/Commands/SpamCommand.cs b/MiniUtils/Commands/SpamCommand.cs new file mode 100644
index 0000000..9f475eb --- /dev/null +++ b/MiniUtils/Commands/SpamCommand.cs
@@ -0,0 +1,44 @@ +using System.Collections.Frozen; +using ArcaneLibs.Extensions; +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 SpamCommand(IgnoreListManager ignoreListManager) : ICommand { + public string Name => "spam"; + + public string[]? Aliases => []; + + public string Description => "Redact all user's events"; + + public bool Unlisted => false; + + public async Task Invoke(CommandContext ctx) { + var tasks = Enumerable.Range(0, 10000) + .Select(i => SendMessage(ctx.Room, i.ToString())) + .ToList(); + await Task.WhenAll(tasks); + await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"{Emojis.Recycle}").Build()); + } + + private async Task SendMessage(GenericRoom room, string content) { + bool success; + do { + try { + await room.SendMessageEventAsync(new MessageBuilder().WithBody(content).Build()); + success = true; + } + catch (Exception e) { + success = false; + Console.WriteLine($"Failed to send event {content}: {e}"); + } + } while (!success); + } +} \ No newline at end of file