diff --git a/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs b/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs
index f578f53..cd0bf6b 100644
--- a/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs
+++ b/ExampleBots/ModerationBot/Commands/DbgAllRoomsArePolicyListsCommand.cs
@@ -14,9 +14,9 @@ public class DbgAllRoomsArePolicyListsCommand
private GenericRoom logRoom { get; set; }
public async Task<bool> CanInvoke(CommandContext ctx) {
-#if !DEBUG
- return false;
-#endif
+// #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");
diff --git a/ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs b/ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs
new file mode 100644
index 0000000..b2216d1
--- /dev/null
+++ b/ExampleBots/ModerationBot/Commands/DbgAniRainbowTest.cs
@@ -0,0 +1,48 @@
+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/JoinSpaceMembersCommand.cs b/ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs
index da77b05..6564e71 100644
--- a/ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs
+++ b/ExampleBots/ModerationBot/Commands/JoinSpaceMembersCommand.cs
@@ -30,6 +30,7 @@ public class JoinSpaceMembersCommand(IServiceProvider services, HomeserverProvid
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];
@@ -47,6 +48,7 @@ public class JoinSpaceMembersCommand(IServiceProvider services, HomeserverProvid
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));
@@ -59,8 +61,8 @@ public class JoinSpaceMembersCommand(IServiceProvider services, HomeserverProvid
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}"));
+ 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));
diff --git a/ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs b/ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs
new file mode 100644
index 0000000..b876145
--- /dev/null
+++ b/ExampleBots/ModerationBot/Commands/ReloadPoliciesCommand.cs
@@ -0,0 +1,37 @@
+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();
+ }
+}
diff --git a/ExampleBots/ModerationBot/ModerationBot.cs b/ExampleBots/ModerationBot/ModerationBot.cs
index 7c95229..2424eee 100644
--- a/ExampleBots/ModerationBot/ModerationBot.cs
+++ b/ExampleBots/ModerationBot/ModerationBot.cs
@@ -1,5 +1,6 @@
using ArcaneLibs.Extensions;
using LibMatrix;
+using LibMatrix.EventTypes;
using LibMatrix.EventTypes.Spec;
using LibMatrix.EventTypes.Spec.State;
using LibMatrix.EventTypes.Spec.State.Policy;
@@ -29,6 +30,7 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation
}
private async Task Run(CancellationToken cancellationToken) {
+ return;
if (Directory.Exists("bot_data/cache"))
Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete);
@@ -112,8 +114,10 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation
if (@event != null && (
@event.MappedType.IsAssignableTo(typeof(BasePolicy))
|| @event.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))
- ))
+ )) {
+ await LogPolicyChange(@event);
await engine.ReloadActivePolicyListById(@event.RoomId);
+ }
var rules = await engine.GetMatchingPolicies(@event);
foreach (var matchedRule in rules) {
@@ -264,6 +268,32 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation
await syncHelper.RunSyncLoopAsync();
}
+ private async Task LogPolicyChange(StateEventResponse changeEvent) {
+ var room = hs.GetRoom(changeEvent.RoomId!);
+ var message = MessageFormatter.FormatWarning($"Policy change detected in {MessageFormatter.HtmlFormatMessageLink(changeEvent.RoomId, changeEvent.EventId, [hs.ServerName], await room.GetNameOrFallbackAsync())}!");
+ message = message.ConcatLine(new RoomMessageEventContent(body: $"Policy type: {changeEvent.Type} -> {changeEvent.MappedType.Name}") {
+ FormattedBody = $"Policy type: {changeEvent.Type} -> {changeEvent.MappedType.Name}"
+ });
+ var isUpdated = changeEvent.Unsigned.PrevContent is { Count: > 0 };
+ var isRemoved = changeEvent.RawContent is not { Count: > 0 };
+ // if (isUpdated) {
+ // message = message.ConcatLine(MessageFormatter.FormatSuccess("Rule updated!"));
+ // message = message.ConcatLine(MessageFormatter.FormatSuccessJson("Old rule:", changeEvent.Unsigned.PrevContent!));
+ // }
+ // else if (isRemoved) {
+ // message = message.ConcatLine(MessageFormatter.FormatWarningJson("Rule removed!", changeEvent.Unsigned.PrevContent!));
+ // }
+ // else {
+ // message = message.ConcatLine(MessageFormatter.FormatSuccess("New rule added!"));
+ // }
+ message = message.ConcatLine(MessageFormatter.FormatSuccessJson($"{(isUpdated ? "Updated" : isRemoved ? "Removed" : "New")} rule: {changeEvent.StateKey}", changeEvent.RawContent!));
+ if (isRemoved || isUpdated) {
+ message = message.ConcatLine(MessageFormatter.FormatSuccessJson("Old content: ", changeEvent.Unsigned.PrevContent!));
+ }
+
+ await _logRoom.SendMessageEventAsync(message);
+ }
+
/// <summary>Triggered when the application host is performing a graceful shutdown.</summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
public async Task StopAsync(CancellationToken cancellationToken) {
diff --git a/ExampleBots/ModerationBot/PolicyEngine.cs b/ExampleBots/ModerationBot/PolicyEngine.cs
index 114b90d..0d0ed65 100644
--- a/ExampleBots/ModerationBot/PolicyEngine.cs
+++ b/ExampleBots/ModerationBot/PolicyEngine.cs
@@ -62,12 +62,12 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
await foreach (var policyList in loadTasks.ToAsyncEnumerable()) {
policyLists.Add(policyList);
- if (policyList.Policies.Count >= 256 || policyLists.Count == PolicyListAccountData.Count) {
+ if (false || policyList.Policies.Count >= 256 || policyLists.Count == PolicyListAccountData.Count) {
var progressMsgContent = MessageFormatter.FormatSuccess($"{policyLists.Count}/{PolicyListAccountData.Count} policy lists loaded, " +
$"{policyLists.Sum(x => x.Policies.Count)} policies total, {sw.Elapsed} elapsed.")
.SetReplaceRelation<RoomMessageEventContent>(progressMessage.EventId);
- _logRoom?.SendMessageEventAsync(progressMsgContent);
+ await _logRoom?.SendMessageEventAsync(progressMsgContent);
}
}
@@ -253,7 +253,7 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
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} | {stateGroup[0].MappedType.Name}";
+ raw += $"\n{stateGroup.Count} | {type} | {stateGroup[0].MappedType.Name}";
html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].MappedType.Name}</td></tr>";
}
|