diff --git a/AccountData/BotData.cs b/AccountData/BotData.cs
index ab680c2..de52679 100644
--- a/AccountData/BotData.cs
+++ b/AccountData/BotData.cs
@@ -1,14 +1,18 @@
using System.Text.Json.Serialization;
+using LibMatrix.EventTypes;
namespace ModerationBot.AccountData;
+[MatrixEvent(EventName = EventId)]
public class BotData {
+ public const string EventId = "gay.rory.moderation_bot_data";
+
[JsonPropertyName("control_room")]
- public string ControlRoom { get; set; } = "";
+ public string? ControlRoom { get; set; } = "";
[JsonPropertyName("log_room")]
public string? LogRoom { get; set; } = "";
[JsonPropertyName("default_policy_room")]
public string? DefaultPolicyRoom { get; set; }
-}
+}
\ No newline at end of file
diff --git a/Commands/BanMediaCommand.cs b/Commands/BanMediaCommand.cs
index 535fd4f..b891932 100644
--- a/Commands/BanMediaCommand.cs
+++ b/Commands/BanMediaCommand.cs
@@ -6,22 +6,23 @@ using LibMatrix.Helpers;
using LibMatrix.Services;
using LibMatrix.Utilities.Bot.Interfaces;
using ModerationBot.AccountData;
+using ModerationBot.Services;
using ModerationBot.StateEventTypes.Policies.Implementations;
namespace ModerationBot.Commands;
-public class BanMediaCommand(IServiceProvider services, HomeserverProviderService hsProvider, HomeserverResolverService hsResolver, PolicyEngine engine) : ICommand {
+public class BanMediaCommand(HomeserverResolverService hsResolver, PolicyEngine engine, ModerationBotRoomProvider roomProvider) : 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 controlRoom = await roomProvider.GetControlRoomAsync();
+ var logRoom = await roomProvider.GetLogRoomAsync();
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(
+ await logRoom.SendMessageEventAsync(
new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text"));
}
@@ -29,10 +30,8 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic
}
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);
+ var policyRoom = await roomProvider.GetDefaultPolicyRoomAsync();
+ var logRoom = await roomProvider.GetLogRoomAsync();
//check if reply
var messageContent = ctx.MessageEvent.TypedContent as RoomMessageEventContent;
diff --git a/ModerationBot.cs b/ModerationBot.cs
index 2424eee..791d3b5 100644
--- a/ModerationBot.cs
+++ b/ModerationBot.cs
@@ -30,7 +30,6 @@ 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);
diff --git a/PolicyEngine.cs b/PolicyEngine.cs
index 0d0ed65..7556fc5 100644
--- a/PolicyEngine.cs
+++ b/PolicyEngine.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.RegularExpressions;
@@ -12,13 +13,15 @@ using LibMatrix.RoomTypes;
using LibMatrix.Services;
using Microsoft.Extensions.Logging;
using ModerationBot.AccountData;
+using ModerationBot.Services;
using ModerationBot.StateEventTypes.Policies;
using ModerationBot.StateEventTypes.Policies.Implementations;
namespace ModerationBot;
-public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationBot> logger, ModerationBotConfiguration configuration, HomeserverResolverService hsResolver) {
+public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationBot> logger, ModerationBotConfiguration configuration, HomeserverResolverService hsResolver, ModerationBotRoomProvider roomProvider) {
private Dictionary<string, PolicyList> PolicyListAccountData { get; set; } = new();
+
// ReSharper disable once MemberCanBePrivate.Global
public List<PolicyList> ActivePolicyLists { get; set; } = new();
public List<BasePolicy> ActivePolicies { get; set; } = new();
@@ -28,11 +31,10 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
public async Task ReloadActivePolicyLists() {
var sw = Stopwatch.StartNew();
-
- var botData = await hs.GetAccountDataAsync<BotData>("gay.rory.moderation_bot_data");
- _logRoom ??= hs.GetRoom(botData.LogRoom ?? botData.ControlRoom);
- _controlRoom ??= hs.GetRoom(botData.ControlRoom);
-
+
+ _logRoom = await roomProvider.GetLogRoomAsync();
+ _controlRoom = await roomProvider.GetControlRoomAsync();
+
await _controlRoom?.SendMessageEventAsync(MessageFormatter.FormatSuccess("Reloading policy lists!"))!;
await _logRoom?.SendMessageEventAsync(MessageFormatter.FormatSuccess("Reloading policy lists!"))!;
@@ -46,12 +48,12 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
if (e is not { ErrorCode: "M_NOT_FOUND" }) throw;
}
- if (!PolicyListAccountData.ContainsKey(botData.DefaultPolicyRoom!)) {
- PolicyListAccountData.Add(botData.DefaultPolicyRoom!, new PolicyList() {
- Trusted = true
- });
- await hs.SetAccountDataAsync("gay.rory.moderation_bot.policy_lists", PolicyListAccountData);
- }
+ // if (!PolicyListAccountData.ContainsKey(botData.DefaultPolicyRoom!)) {
+ // PolicyListAccountData.Add(botData.DefaultPolicyRoom!, new PolicyList() {
+ // Trusted = true
+ // });
+ // await hs.SetAccountDataAsync("gay.rory.moderation_bot.policy_lists", PolicyListAccountData);
+ // }
var loadTasks = new List<Task<PolicyList>>();
foreach (var (roomId, policyList) in PolicyListAccountData) {
@@ -62,11 +64,11 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
await foreach (var policyList in loadTasks.ToAsyncEnumerable()) {
policyLists.Add(policyList);
- if (false || policyList.Policies.Count >= 256 || policyLists.Count == PolicyListAccountData.Count) {
+ if (true || 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);
-
+
await _logRoom?.SendMessageEventAsync(progressMsgContent);
}
}
@@ -99,7 +101,6 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
return policyList;
}
-
public async Task ReloadActivePolicyListById(string roomId) {
if (!ActivePolicyLists.Any(x => x.Room.RoomId == roomId)) return;
await LoadPolicyListAsync(hs.GetRoom(roomId), ActivePolicyLists.Single(x => x.Room.RoomId == roomId));
@@ -159,7 +160,7 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
return matchingPolicies;
}
- #region Policy matching
+#region Policy matching
private async Task<List<BasePolicy>> CheckMessageContent(StateEventResponse @event) {
var matchedRules = new List<BasePolicy>();
@@ -167,11 +168,11 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
if (ActivePoliciesByType.TryGetValue(nameof(MessagePolicyContainsText), out var messageContainsPolicies))
foreach (var policy in messageContainsPolicies) {
- if ((@msgContent?.Body?.ToLowerInvariant().Contains(policy.Entity.ToLowerInvariant()) ?? false) || (@msgContent?.FormattedBody?.ToLowerInvariant().Contains(policy.Entity.ToLowerInvariant()) ?? false))
+ if ((@msgContent?.Body?.ToLowerInvariant().Contains(policy.Entity.ToLowerInvariant()) ?? false) ||
+ (@msgContent?.FormattedBody?.ToLowerInvariant().Contains(policy.Entity.ToLowerInvariant()) ?? false))
matchedRules.Add(policy);
}
-
return matchedRules;
}
@@ -243,11 +244,11 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
return matchedRules;
}
- #endregion
+#endregion
- #region Internal code
+#region Internal code
- #region Summarisation
+#region Summarisation
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>";
@@ -261,8 +262,7 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
return (raw, html);
}
- #endregion
-
- #endregion
+#endregion
-}
+#endregion
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
index d125258..d783af1 100644
--- a/Program.cs
+++ b/Program.cs
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModerationBot;
+using ModerationBot.Services;
Console.WriteLine("Hello, World!");
@@ -33,6 +34,7 @@ var host = builder.ConfigureServices((_, services) => {
services.AddBot(withCommands: true);
services.AddSingleton<PolicyEngine>();
+ services.AddSingleton<ModerationBotRoomProvider>();
services.AddHostedService<ModerationBot.ModerationBot>();
}).UseConsoleLifetime().Build();
diff --git a/Services/ModerationBotRoomProvider.cs b/Services/ModerationBotRoomProvider.cs
new file mode 100644
index 0000000..d4a200f
--- /dev/null
+++ b/Services/ModerationBotRoomProvider.cs
@@ -0,0 +1,76 @@
+using System.Diagnostics.CodeAnalysis;
+using LibMatrix.Homeservers;
+using LibMatrix.Responses;
+using LibMatrix.RoomTypes;
+using ModerationBot.AccountData;
+
+namespace ModerationBot.Services;
+
+public class ModerationBotRoomProvider(AuthenticatedHomeserverGeneric hs, ModerationBotConfiguration cfg) {
+ private BotData? _botData;
+
+ public BotData? BotData {
+ get {
+ if (BotDataExpiry >= DateTime.UtcNow) return _botData;
+ Console.WriteLine("BotData expired!");
+ return null;
+ }
+ set {
+ _botData = value;
+ Console.WriteLine("BotData updated!");
+ BotDataExpiry = DateTime.UtcNow.AddMinutes(5);
+ }
+ }
+
+ private DateTime BotDataExpiry { get; set; }
+
+ [MemberNotNull(nameof(BotData))]
+ private async Task<BotData> GetBotDataAsync() {
+ try {
+ BotData ??= await hs.GetAccountDataAsync<BotData>(BotData.EventId);
+ }
+ catch (Exception e) {
+ Console.WriteLine(e);
+ await hs.SetAccountDataAsync(BotData.EventId, new BotData());
+ return await GetBotDataAsync();
+ }
+
+ if (BotData == null)
+ throw new NullReferenceException("BotData is null!");
+
+ return BotData;
+ }
+
+ public async Task<GenericRoom> GetControlRoomAsync() {
+ var botData = await GetBotDataAsync();
+ if (botData.ControlRoom == null) {
+ var createRoomRequest = CreateRoomRequest.CreatePrivate(hs, "Rory&::ModerationBot - Control Room");
+ createRoomRequest.Invite = cfg.Admins;
+ var newRoom = await hs.CreateRoom(createRoomRequest, true, true, true);
+ BotData.ControlRoom = newRoom.RoomId;
+ await hs.SetAccountDataAsync(BotData.EventId, BotData);
+ }
+
+ return hs.GetRoom(BotData.ControlRoom!);
+ }
+
+ public async Task<GenericRoom> GetLogRoomAsync() {
+ var botData = await GetBotDataAsync();
+ if (botData.LogRoom == null) {
+ var controlRoom = await GetControlRoomAsync();
+ var createRoomRequest = CreateRoomRequest.CreatePrivate(hs, "Rory&::ModerationBot - Log Room");
+ createRoomRequest.Invite = (await controlRoom.GetMembersListAsync()).Select(x=>x.StateKey).ToList();
+ var newRoom = await hs.CreateRoom(createRoomRequest, true, true, true);
+ BotData.LogRoom = newRoom.RoomId;
+ await hs.SetAccountDataAsync(BotData.EventId, BotData);
+ }
+
+ return hs.GetRoom(BotData.LogRoom!);
+ }
+
+ public async Task<GenericRoom?> GetDefaultPolicyRoomAsync() {
+ var botData = await GetBotDataAsync();
+
+ return string.IsNullOrWhiteSpace(botData.DefaultPolicyRoom) ? null : hs.GetRoom(BotData.DefaultPolicyRoom!);
+ }
+}
\ No newline at end of file
diff --git a/StateEventTypes/Policies/Implementations/MediaPolicyFile.cs b/StateEventTypes/Policies/Implementations/MediaPolicyFile.cs
index c5b6ef2..f0fef63 100644
--- a/StateEventTypes/Policies/Implementations/MediaPolicyFile.cs
+++ b/StateEventTypes/Policies/Implementations/MediaPolicyFile.cs
@@ -9,7 +9,7 @@ namespace ModerationBot.StateEventTypes.Policies.Implementations;
[MatrixEvent(EventName = "gay.rory.moderation.rule.media")]
public class MediaPolicyFile : BasePolicy {
/// <summary>
- /// Hash of the file
+ /// Hash of the file, hashed with SHA3-256.
/// </summary>
[JsonPropertyName("file_hash")]
public string? FileHash { get; set; }
|