about summary refs log tree commit diff
path: root/ExampleBots/ModerationBot/Commands
diff options
context:
space:
mode:
Diffstat (limited to 'ExampleBots/ModerationBot/Commands')
-rw-r--r--ExampleBots/ModerationBot/Commands/BanMediaCommand.cs111
-rw-r--r--ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs58
-rw-r--r--ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs48
-rw-r--r--ExampleBots/ModerationBot/Commands/DbgDumpActivePoliciesCommand.cs38
-rw-r--r--ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs69
-rw-r--r--ExampleBots/ModerationBot/Commands/JoinRoomCommand.cs49
-rw-r--r--ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs73
-rw-r--r--ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs37
8 files changed, 0 insertions, 483 deletions
diff --git a/ExampleBots/ModerationBot/Commands/BanMediaCommand.cs b/ExampleBots/ModerationBot/Commands/BanMediaCommand.cs
deleted file mode 100644
index 535fd4f..0000000
--- a/ExampleBots/ModerationBot/Commands/BanMediaCommand.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using System.Security.Cryptography;
-using ArcaneLibs.Extensions;
-using LibMatrix;
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.Helpers;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-using ModerationBot.StateEventTypes.Policies.Implementations;
-
-namespace ModerationBot.Commands;
-
-public class BanMediaCommand(IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "banmedia";
-    public string Description { get; } = "Create a policy banning a piece of media, must be used in reply to a message";
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var policyRoom = ctx.Homeserver.GetRoom(botData.DefaultPolicyRoom ?? botData.ControlRoom);
-        var logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom);
-
-        //check if reply
-        var messageContent = ctx.MessageEvent.TypedContent as RoomMessageEventContent;
-        if (messageContent?.RelatesTo is { InReplyTo: not null }) {
-            try {
-                await logRoom.SendMessageEventAsync(
-                    new RoomMessageEventContent(
-                        body: $"User {MessageFormatter.HtmlFormatMention(ctx.MessageEvent.Sender)} is trying to ban media {messageContent!.RelatesTo!.InReplyTo!.EventId}",
-                        messageType: "m.text"));
-
-                //get replied message
-                var repliedMessage = await ctx.Room.GetEventAsync<StateEventResponse>(messageContent.RelatesTo!.InReplyTo!.EventId);
-
-                //check if recommendation is in list
-                if (ctx.Args.Length < 2) {
-                    await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatError("You must specify a recommendation type and reason!"));
-                    return;
-                }
-
-                var recommendation = ctx.Args[0];
-
-                if (recommendation is not ("ban" or "kick" or "mute" or "redact" or "spoiler" or "warn" or "warn_admins")) {
-                    await ctx.Room.SendMessageEventAsync(
-                        MessageFormatter.FormatError(
-                            $"Invalid recommendation type {recommendation}, must be `warn_admins`, `warn`, `spoiler`, `redact`, `mute`, `kick` or `ban`!"));
-                    return;
-                }
-
-                //hash file
-                var mxcUri = (repliedMessage.TypedContent as RoomMessageEventContent).Url!;
-                var resolvedUri = await hsResolver.ResolveMediaUri(mxcUri.Split('/')[2], mxcUri);
-                var hashAlgo = SHA3_256.Create();
-                var uriHash = hashAlgo.ComputeHash(mxcUri.AsBytes().ToArray());
-                byte[]? fileHash = null;
-
-                try {
-                    fileHash = await hashAlgo.ComputeHashAsync(await ctx.Homeserver.ClientHttpClient.GetStreamAsync(resolvedUri));
-                }
-                catch (Exception ex) {
-                    await logRoom.SendMessageEventAsync(
-                        MessageFormatter.FormatException($"Error calculating file hash for {mxcUri} via {mxcUri.Split('/')[2]}, retrying via {ctx.Homeserver.BaseUrl}...",
-                            ex));
-                    try {
-                        resolvedUri = await hsResolver.ResolveMediaUri(ctx.Homeserver.BaseUrl, mxcUri);
-                        fileHash = await hashAlgo.ComputeHashAsync(await ctx.Homeserver.ClientHttpClient.GetStreamAsync(resolvedUri));
-                    }
-                    catch (Exception ex2) {
-                        await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatException("Error calculating file hash", ex2));
-                        await logRoom.SendMessageEventAsync(
-                            MessageFormatter.FormatException($"Error calculating file hash via {ctx.Homeserver.BaseUrl}!", ex2));
-                    }
-                }
-
-                MediaPolicyFile policy;
-                await policyRoom.SendStateEventAsync("gay.rory.moderation.rule.media", Guid.NewGuid().ToString(), policy = new MediaPolicyFile {
-                    Entity = Convert.ToBase64String(uriHash),
-                    FileHash = Convert.ToBase64String(fileHash),
-                    Reason = string.Join(' ', ctx.Args[1..]),
-                    Recommendation = recommendation,
-                });
-
-                await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatSuccessJson("Media policy created", policy));
-                await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccessJson("Media policy created", policy));
-            }
-            catch (Exception e) {
-                await logRoom.SendMessageEventAsync(MessageFormatter.FormatException("Error creating policy", e));
-                await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatException("Error creating policy", e));
-                await using var stream = new MemoryStream(e.ToString().AsBytes().ToArray());
-                await logRoom.SendFileAsync("error.log.cs", stream);
-            }
-        }
-        else {
-            await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatError("This command must be used in reply to a message!"));
-        }
-    }
-}
diff --git a/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs b/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs
deleted file mode 100644
index cd0bf6b..0000000
--- a/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.Helpers;
-using LibMatrix.RoomTypes;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class DbgAllRoomsArePolicyListsCommand
-    (IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "dbg-allroomsarepolicy";
-    public string Description { get; } = "[Debug] mark all rooms as trusted policy rooms";
-    private GenericRoom logRoom { get; set; }
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-// #if !DEBUG
-//         return false;
-// #endif
-
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom);
-
-        var joinedRooms = await ctx.Homeserver.GetJoinedRooms();
-
-        await ctx.Homeserver.SetAccountDataAsync("gay.rory.moderation_bot.policy_lists", joinedRooms.ToDictionary(x => x.RoomId, x => new PolicyList() {
-            Trusted = true
-        }));
-
-        await engine.ReloadActivePolicyLists();
-    }
-
-    private async Task<bool> JoinRoom(GenericRoom memberRoom, string reason, List<string> servers) {
-        try {
-            await memberRoom.JoinAsync(servers.ToArray(), reason);
-            await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccess($"Joined room {memberRoom.RoomId}"));
-        }
-        catch (Exception e) {
-            await logRoom.SendMessageEventAsync(MessageFormatter.FormatException($"Failed to join {memberRoom.RoomId}", e));
-        }
-
-        return true;
-    }
-}
diff --git a/ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs b/ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs
deleted file mode 100644
index b2216d1..0000000
--- a/ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Diagnostics;
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.Helpers;
-using LibMatrix.RoomTypes;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class DbgAniRainbowTest(IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "dbg-ani-rainbow";
-    public string Description { get; } = "[Debug] animated rainbow :)";
-    private GenericRoom logRoom { get; set; }
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-        return ctx.Room.RoomId == "!DoHEdFablOLjddKWIp:rory.gay";
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-        //255 long string
-        // var rainbow = "🟥🟧🟨🟩🟦🟪";
-        var rainbow = "M";
-        var chars = rainbow;
-        for (var i = 0; i < 76; i++) {
-            chars += rainbow[i%rainbow.Length];
-        }
-
-        var msg = new MessageBuilder(msgType: "m.notice").WithRainbowString(chars).Build();
-        var msgEvent = await ctx.Room.SendMessageEventAsync(msg);
-        
-        Task.Run(async () => {
-
-            int i = 0;
-            while (true) {
-                msg = new MessageBuilder(msgType: "m.notice").WithRainbowString(chars, offset: i+=5).Build();
-                    // .SetReplaceRelation<RoomMessageEventContent>(msgEvent.EventId);
-                // msg.Body = "";
-                // msg.FormattedBody = "";
-                var sw = Stopwatch.StartNew();
-                await ctx.Room.SendMessageEventAsync(msg);
-                await Task.Delay(sw.Elapsed);
-            }
-            
-        });
-
-    }
-}
\ No newline at end of file
diff --git a/ExampleBots/ModerationBot/Commands/DbgDumpActivePoliciesCommand.cs b/ExampleBots/ModerationBot/Commands/DbgDumpActivePoliciesCommand.cs
deleted file mode 100644
index 285d792..0000000
--- a/ExampleBots/ModerationBot/Commands/DbgDumpActivePoliciesCommand.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using ArcaneLibs.Extensions;
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.RoomTypes;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class DbgDumpActivePoliciesCommand
-    (IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "dbg-dumppolicies";
-    public string Description { get; } = "[Debug] Dump all active policies";
-    private GenericRoom logRoom { get; set; }
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-#if !DEBUG
-        return false;
-#endif
-
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-        await ctx.Room.SendFileAsync("all.json", new MemoryStream(engine.ActivePolicies.ToJson().AsBytes().ToArray()), contentType: "application/json");
-        await ctx.Room.SendFileAsync("by-type.json", new MemoryStream(engine.ActivePoliciesByType.ToJson().AsBytes().ToArray()), contentType: "application/json");
-    }
-}
diff --git a/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs b/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs
deleted file mode 100644
index 7563cac..0000000
--- a/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using ArcaneLibs.Extensions;
-using LibMatrix;
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.RoomTypes;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class DbgDumpAllStateTypesCommand
-    (IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "dbg-dumpstatetypes";
-    public string Description { get; } = "[Debug] Dump all state types we can find";
-    private GenericRoom logRoom { get; set; }
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-#if !DEBUG
-        return false;
-#endif
-
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom);
-
-
-        var joinedRooms = await ctx.Homeserver.GetJoinedRooms();
-
-        var tasks = joinedRooms.Select(GetStateTypes).ToAsyncEnumerable();
-        await foreach (var (room, (raw, html)) in tasks) {
-            await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent("m.text") {
-                Body = $"States for {room.RoomId}:\n{raw}",
-                FormattedBody = $"States for {room.RoomId}:\n{html}",
-                Format = "org.matrix.custom.html"
-            });
-        }
-    }
-
-    private async Task<(GenericRoom room, (string raw, string html))> GetStateTypes(GenericRoom memberRoom) {
-        var states = await memberRoom.GetFullStateAsListAsync();
-
-        return (memberRoom, SummariseStateTypeCounts(states));
-    }
-
-    private static (string Raw, string Html) SummariseStateTypeCounts(IList<StateEventResponse> states) {
-        string raw = "Count | State type | Mapped type", html = "<table><tr><th>Count</th><th>State type</th><th>Mapped type</th></tr>";
-        var groupedStates = states.GroupBy(x => x.Type).ToDictionary(x => x.Key, x => x.ToList()).OrderByDescending(x => x.Value.Count);
-        foreach (var (type, stateGroup) in groupedStates) {
-            raw += $"{stateGroup.Count} | {type} | {StateEvent.GetStateEventType(stateGroup[0].Type).Name}";
-            html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{StateEvent.GetStateEventType(stateGroup[0].Type).Name}</td></tr>";
-        }
-
-        html += "</table>";
-        return (raw, html);
-    }
-}
diff --git a/ExampleBots/ModerationBot/Commands/JoinRoomCommand.cs b/ExampleBots/ModerationBot/Commands/JoinRoomCommand.cs
deleted file mode 100644
index eb22a70..0000000
--- a/ExampleBots/ModerationBot/Commands/JoinRoomCommand.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.Helpers;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class JoinRoomCommand(IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "join";
-    public string Description { get; } = "Join arbitrary rooms";
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var policyRoom = ctx.Homeserver.GetRoom(botData.DefaultPolicyRoom ?? botData.ControlRoom);
-        var logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom);
-
-        await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccess($"Joining room {ctx.Args[0]} with reason: {string.Join(' ', ctx.Args[1..])}"));
-        var roomId = ctx.Args[0];
-        var servers = new List<string>() { ctx.Homeserver.ServerName };
-        if (roomId.StartsWith('[')) {
-
-        }
-
-        if (roomId.StartsWith('#')) {
-            var res = await ctx.Homeserver.ResolveRoomAliasAsync(roomId);
-            roomId = res.RoomId;
-            servers.AddRange(servers);
-        }
-
-        await ctx.Homeserver.JoinRoomAsync(roomId, servers, string.Join(' ', ctx.Args[1..]));
-        await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccess($"Resolved room {ctx.Args[0]} to {roomId} with servers: {string.Join(", ", servers)}"));
-    }
-}
diff --git a/ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs b/ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs
deleted file mode 100644
index 6564e71..0000000
--- a/ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using ArcaneLibs.Extensions;
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.Helpers;
-using LibMatrix.RoomTypes;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class JoinSpaceMembersCommand(IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "joinspacemembers";
-    public string Description { get; } = "Join all rooms in space";
-    private GenericRoom logRoom { get; set; }
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom);
-        var currentRooms = (await ctx.Homeserver.GetJoinedRooms()).Select(x=>x.RoomId).ToList();
-
-        await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccess($"Joining space children of {ctx.Args[0]} with reason: {string.Join(' ', ctx.Args[1..])}"));
-        var roomId = ctx.Args[0];
-        var servers = new List<string>() { ctx.Homeserver.ServerName };
-        if (roomId.StartsWith('[')) {
-
-        }
-
-        if (roomId.StartsWith('#')) {
-            var res = await ctx.Homeserver.ResolveRoomAliasAsync(roomId);
-            roomId = res.RoomId;
-            servers.AddRange(servers);
-        }
-
-        var room = ctx.Homeserver.GetRoom(roomId);
-        var tasks = new List<Task<bool>>();
-        await foreach (var memberRoom in room.AsSpace.GetChildrenAsync()) {
-            if (currentRooms.Contains(memberRoom.RoomId)) continue;
-            servers.Add(room.RoomId.Split(':', 2)[1]);
-            servers = servers.Distinct().ToList();
-            tasks.Add(JoinRoom(memberRoom, string.Join(' ', ctx.Args[1..]), servers));
-        }
-
-        await foreach (var b in tasks.ToAsyncEnumerable()) {
-            await Task.Delay(50);
-        }
-    }
-
-    private async Task<bool> JoinRoom(GenericRoom memberRoom, string reason, List<string> servers) {
-        try {
-            var resp = await memberRoom.JoinAsync(servers.ToArray(), reason, checkIfAlreadyMember: false);
-            await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccess($"Joined room {memberRoom.RoomId} (resp={resp.RoomId})"));
-        }
-        catch (Exception e) {
-            await logRoom.SendMessageEventAsync(MessageFormatter.FormatException($"Failed to join {memberRoom.RoomId}", e));
-        }
-
-        return true;
-    }
-}
diff --git a/ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs b/ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs
deleted file mode 100644
index b876145..0000000
--- a/ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using LibMatrix.EventTypes.Spec;
-using LibMatrix.Helpers;
-using LibMatrix.Services;
-using LibMatrix.Utilities.Bot.Interfaces;
-using ModerationBot.AccountData;
-
-namespace ModerationBot.Commands;
-
-public class ReloadPoliciesCommand(IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
-    public string Name { get; } = "reloadpolicies";
-    public string Description { get; } = "Reload policies";
-
-    public async Task<bool> CanInvoke(CommandContext ctx) {
-        if (ctx.MessageEvent.Sender == "@cadence:cadence.moe") return true;
-        //check if user is admin in control room
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom);
-        var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasStatePermission(ctx.MessageEvent.Sender, "m.room.ban");
-        if (!isAdmin) {
-            // await ctx.Reply("You do not have permission to use this command!");
-            await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync(
-                new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
-        }
-
-        return isAdmin;
-    }
-
-    public async Task Invoke(CommandContext ctx) {
-
-        var botData = await ctx.Homeserver.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
-        var policyRoom = ctx.Homeserver.GetRoom(botData.DefaultPolicyRoom ?? botData.ControlRoom);
-        var logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom);
-        
-        await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccess($"Reloading policy lists due to manual invocation!!!!"));
-        await engine.ReloadActivePolicyLists();
-    }
-}