diff --git a/MiniUtils/Commands/DeleteRoomCommand.cs b/MiniUtils/Commands/DeleteRoomCommand.cs
index 41dbfb3..8d02dfb 100644
--- a/MiniUtils/Commands/DeleteRoomCommand.cs
+++ b/MiniUtils/Commands/DeleteRoomCommand.cs
@@ -1,9 +1,4 @@
-using ArcaneLibs.Extensions;
-using LibMatrix.EventTypes.Common;
-using LibMatrix.EventTypes.Spec.State.RoomInfo;
-using LibMatrix.Helpers;
-using LibMatrix.Responses;
-using LibMatrix.RoomTypes;
+using LibMatrix.Homeservers;
using LibMatrix.Utilities.Bot.Interfaces;
namespace MiniUtils.Commands;
@@ -18,30 +13,9 @@ public class DeleteRoomCommand() : ICommand {
public bool Unlisted => false;
public async Task Invoke(CommandContext ctx) {
- var creationContent = new CreateRoomRequest() {
- Name = ctx.Args[0],
- RoomAliasName = ctx.Args[0],
- Visibility = "private",
- CreationContent = new() {
- { "type", PolicyRoom.TypeName },
- { "room_version", 11 }
- },
- PowerLevelContentOverride = new RoomPowerLevelEventContent() {
- EventsDefault = 50,
- Invite = 50
- },
- InitialState = [
- new() {
- Type = MjolnirShortcodeEventContent.EventId,
- StateKey = "",
- TypedContent = new MjolnirShortcodeEventContent() {
- Shortcode = ctx.Args[0]
- }
- }
- ]
- };
-
- var result = await ctx.Homeserver.CreateRoom(creationContent);
- await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithMention($"#{ctx.Args[0]}:{ctx.Homeserver.ServerName}").Build());
+ if (ctx.Homeserver is not AuthenticatedHomeserverSynapse synapse) return;
+ var res = await synapse.Admin.DeleteRoom(ctx.Args[0], new() {
+ Purge = true
+ }, waitForCompletion: false);
}
}
\ No newline at end of file
diff --git a/MiniUtils/Commands/DetectStateSplitCommand.cs b/MiniUtils/Commands/DetectStateSplitCommand.cs
new file mode 100644
index 0000000..6180f20
--- /dev/null
+++ b/MiniUtils/Commands/DetectStateSplitCommand.cs
@@ -0,0 +1,72 @@
+using System.Collections.Frozen;
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.Helpers;
+using LibMatrix.Services;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace MiniUtils.Commands;
+
+public class DetectStateSplitCommand(MiniUtilsConfiguration config, HomeserverProviderService hsProvider) : ICommand {
+ public string Name => "detect state split";
+
+ public string[]? Aliases => ["dss"];
+
+ public string Description => "Detect room splits";
+
+ public bool Unlisted => false;
+
+ public async Task Invoke(CommandContext ctx) {
+ var profile = config.ExternalProfiles[ctx.Args[0]];
+ var rhs = await hsProvider.GetAuthenticatedWithToken(profile.Homeserver, profile.AccessToken, enableServer: false, useGeneric: true);
+ var localStateTask = ctx.Room.GetFullStateAsListAsync();
+ var remoteStateTask = rhs.GetRoom(ctx.Room.RoomId).GetFullStateAsListAsync();
+
+ var localState = await localStateTask;
+ var remoteState = await remoteStateTask;
+
+ var keySet = localState.Concat(remoteState)
+ .Select(x => (x.Type, x.StateKey))
+ .ToFrozenSet();
+
+ var differences = 0;
+ foreach (var keyPair in keySet) {
+ var local = localState.FirstOrDefault(x => x.Type == keyPair.Type && x.StateKey == keyPair.StateKey);
+ var remote = remoteState.FirstOrDefault(x => x.Type == keyPair.Type && x.StateKey == keyPair.StateKey);
+
+ if (local == null) {
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ .WithCollapsibleSection($"Missing {keyPair.Type} {keyPair.StateKey} locally", b =>
+ b.WithCodeBlock(remote.ToJson(ignoreNull: true), "json")
+ ).Build());
+ differences++;
+ continue;
+ }
+
+ if (remote == null) {
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ .WithCollapsibleSection($"Missing {keyPair.Type} {keyPair.StateKey} remotely", b =>
+ b.WithCodeBlock(local.ToJson(ignoreNull: true), "json")
+ ).Build());
+ differences++;
+ continue;
+ }
+
+ if (!StateEvent.Equals(local, remote)) {
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ .WithCollapsibleSection($"Different {keyPair.Type} {keyPair.StateKey}", b =>
+ b.WithCodeBlock(local.ToJson(ignoreNull: true), "json")
+ .WithCodeBlock(remote.ToJson(ignoreNull: true), "json")
+ ).Build());
+ differences++;
+ }
+ }
+
+ if (differences > 0)
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"No differences found").Build());
+ else
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"Found {differences} differences!").Build());
+
+ // await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithCodeBlock(rhs.WhoAmI.ToJson()).Build());
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/DumpTimelineCommand.cs b/MiniUtils/Commands/DumpTimelineCommand.cs
new file mode 100644
index 0000000..4ee53c9
--- /dev/null
+++ b/MiniUtils/Commands/DumpTimelineCommand.cs
@@ -0,0 +1,38 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using LibMatrix;
+using LibMatrix.Services;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace MiniUtils.Commands;
+
+public class DumpTimelineCommand(MiniUtilsConfiguration config, HomeserverProviderService hsProvider) : ICommand {
+ public string Name => "dump timeline";
+
+ public string[]? Aliases => ["dt"];
+
+ public string Description => "Dump timeline";
+
+ public bool Unlisted => false;
+
+ public async Task Invoke(CommandContext ctx) {
+ MessagesResponse res;
+ if (ctx.Args.Length < 1) {
+ res = await ctx.Room.GetMessagesAsync(limit: 250);
+ }
+ else {
+ var profile = config.ExternalProfiles[ctx.Args[0]];
+ var rhs = await hsProvider.GetAuthenticatedWithToken(profile.Homeserver, profile.AccessToken, enableServer: false, useGeneric: true);
+ res = await rhs.GetRoom(ctx.Room.RoomId).GetMessagesAsync(limit: 250);
+ }
+
+ var ms = new MemoryStream();
+ await JsonSerializer.SerializeAsync(ms, res, new JsonSerializerOptions() {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ WriteIndented = true
+ });
+ ms.Seek(0, SeekOrigin.Begin);
+
+ await ctx.Room.SendFileAsync("timeline.json", ms, contentType: "application/json");
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/ExperimentalFeaturesCommand.cs b/MiniUtils/Commands/ExperimentalFeaturesCommand.cs
index de5d035..8ae40d3 100644
--- a/MiniUtils/Commands/ExperimentalFeaturesCommand.cs
+++ b/MiniUtils/Commands/ExperimentalFeaturesCommand.cs
@@ -1,9 +1,4 @@
-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;
diff --git a/MiniUtils/Commands/IgnoreCommand.cs b/MiniUtils/Commands/IgnoreCommand.cs
index 4b3fe86..4206b72 100644
--- a/MiniUtils/Commands/IgnoreCommand.cs
+++ b/MiniUtils/Commands/IgnoreCommand.cs
@@ -51,6 +51,10 @@ public class IgnoreCommand(IgnoreListManager ignoreListManager) : ICommand {
var count = await ignoreListManager.MoveList(true, itemsToEnable);
await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
}
+ else if (ctx.Args is ["add", .. var itemsToAdd]) {
+ var count = await ignoreListManager.AddList(itemsToAdd);
+ await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
+ }
}
private async Task Summarize(CommandContext ctx, IgnoredUserListEventContentWithDisabled ignoreList) {
diff --git a/MiniUtils/Commands/JoinCommand.cs b/MiniUtils/Commands/JoinCommand.cs
new file mode 100644
index 0000000..1ed51b6
--- /dev/null
+++ b/MiniUtils/Commands/JoinCommand.cs
@@ -0,0 +1,26 @@
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Services;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace MiniUtils.Commands;
+
+public class JoinCommand(MiniUtilsConfiguration config, HomeserverProviderService hsProvider) : ICommand {
+ public string Name => "join";
+
+ public string[]? Aliases => [];
+
+ public string Description => "Redact all user's events";
+
+ public bool Unlisted => false;
+ public async Task Invoke(CommandContext ctx) {
+ var profile = config.ExternalProfiles[ctx.Args[0]];
+ var rhs = await hsProvider.GetAuthenticatedWithToken(profile.Homeserver, profile.AccessToken, enableServer: false, useGeneric: true);
+
+ var joinRules = await ctx.Room.GetJoinRuleAsync();
+ if (joinRules?.JoinRule is not RoomJoinRulesEventContent.JoinRules.Public) {
+ await ctx.Room.InviteUserAsync(rhs.UserId);
+ }
+
+ _ = rhs.GetRoom(ctx.Room.RoomId).JoinAsync([ctx.Homeserver.ServerName]);
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/MakePolicyListCommand.cs b/MiniUtils/Commands/MakePolicyListCommand.cs
index 0498712..40b0695 100644
--- a/MiniUtils/Commands/MakePolicyListCommand.cs
+++ b/MiniUtils/Commands/MakePolicyListCommand.cs
@@ -1,4 +1,3 @@
-using ArcaneLibs.Extensions;
using LibMatrix.EventTypes.Common;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Helpers;
diff --git a/MiniUtils/Commands/MscCommand.cs b/MiniUtils/Commands/MscCommand.cs
index 62f1bd7..89e9aec 100644
--- a/MiniUtils/Commands/MscCommand.cs
+++ b/MiniUtils/Commands/MscCommand.cs
@@ -1,4 +1,3 @@
-using LibMatrix.Extensions;
using LibMatrix.Helpers;
using LibMatrix.Utilities.Bot.Interfaces;
using MiniUtils.Utilities;
diff --git a/MiniUtils/Commands/OpsCommand.cs b/MiniUtils/Commands/OpsCommand.cs
new file mode 100644
index 0000000..eef6c6f
--- /dev/null
+++ b/MiniUtils/Commands/OpsCommand.cs
@@ -0,0 +1,25 @@
+using LibMatrix.Helpers;
+using LibMatrix.Utilities.Bot.Interfaces;
+using MiniUtils.Services;
+
+namespace MiniUtils.Commands;
+
+public class OpsCommand(IgnoreListManager ignoreListManager) : ICommand {
+ public string Name => "ops";
+
+ public string[]? Aliases => ["admins", "mods"];
+
+ public string Description => "Ping all the mods";
+
+ public bool Unlisted => true;
+
+ public async Task Invoke(CommandContext ctx) {
+ var pls = await ctx.Room.GetPowerLevelsAsync();
+ var msb = new MessageBuilder();
+ foreach (var pl in pls.Users.Where(x => x.Value >= pls.Kick)) {
+ msb = msb.WithMention(pl.Key).WithBody(" ");
+ }
+
+ await ctx.Room.SendMessageEventAsync(msb.Build());
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/RedactCommand.cs b/MiniUtils/Commands/RedactCommand.cs
index cba06c9..e84191e 100644
--- a/MiniUtils/Commands/RedactCommand.cs
+++ b/MiniUtils/Commands/RedactCommand.cs
@@ -1,6 +1,6 @@
using System.Collections.Frozen;
using ArcaneLibs.Extensions;
-using LibMatrix.EventTypes.Spec;
+using LibMatrix;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Filters;
using LibMatrix.Helpers;
@@ -19,10 +19,6 @@ public class RedactCommand(IgnoreListManager ignoreListManager) : ICommand {
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"])
@@ -42,8 +38,7 @@ public class RedactCommand(IgnoreListManager ignoreListManager) : ICommand {
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;
+ if(!await IsRedactionNeeded(ctx.Room, evt.EventId!, evt)) continue;
tasks.Add(RedactEvent(ctx.Room, evt.EventId!));
count++;
}
@@ -64,6 +59,26 @@ public class RedactCommand(IgnoreListManager ignoreListManager) : ICommand {
// 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 {
diff --git a/MiniUtils/Commands/SpamCommand.cs b/MiniUtils/Commands/SpamCommand.cs
index 9f475eb..742ae3b 100644
--- a/MiniUtils/Commands/SpamCommand.cs
+++ b/MiniUtils/Commands/SpamCommand.cs
@@ -1,8 +1,3 @@
-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;
@@ -18,7 +13,7 @@ public class SpamCommand(IgnoreListManager ignoreListManager) : ICommand {
public string Description => "Redact all user's events";
- public bool Unlisted => false;
+ public bool Unlisted => true;
public async Task Invoke(CommandContext ctx) {
var tasks = Enumerable.Range(0, 10000)
|