about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--AccountData/BotData.cs8
-rw-r--r--Commands/BanMediaCommand.cs15
-rw-r--r--ModerationBot.cs1
-rw-r--r--PolicyEngine.cs50
-rw-r--r--Program.cs2
-rw-r--r--Services/ModerationBotRoomProvider.cs76
-rw-r--r--StateEventTypes/Policies/Implementations/MediaPolicyFile.cs2
7 files changed, 117 insertions, 37 deletions
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; }