diff --git a/MatrixAntiDmSpam.Core/PolicyExecutor.cs b/MatrixAntiDmSpam.Core/PolicyExecutor.cs
index 4dcaf2b..23d70a8 100644
--- a/MatrixAntiDmSpam.Core/PolicyExecutor.cs
+++ b/MatrixAntiDmSpam.Core/PolicyExecutor.cs
@@ -1,11 +1,14 @@
using System.Diagnostics;
using ArcaneLibs.Attributes;
using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec;
using LibMatrix.EventTypes.Spec.State.Policy;
using LibMatrix.Helpers;
using LibMatrix.Homeservers;
using LibMatrix.RoomTypes;
using LibMatrix.Utilities.Bot.Interfaces;
+using MatrixAntiDmSpam.Core.Classes;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -21,7 +24,11 @@ public class PolicyExecutor(
public Task StartAsync(CancellationToken cancellationToken) {
roomInviteHandler.OnInviteReceived.Add(CheckPoliciesAgainstInvite);
- policyStore.OnPolicyUpdated.Add(CheckPolicyAgainstOutstandingInvites);
+ policyStore.OnPolicyAdded.Add(CheckPolicyAgainstOutstandingInvites);
+ if (config.IgnoreBannedUsers) {
+ policyStore.OnPoliciesChanged.Add(UpdateIgnoreList);
+ }
+
return Task.CompletedTask;
}
@@ -29,6 +36,76 @@ public class PolicyExecutor(
return Task.CompletedTask;
}
+#region Feature: Manage ignore list
+
+ private async Task UpdateIgnoreList((
+ List<StateEventResponse> NewPolicies,
+ List<(StateEventResponse Old, StateEventResponse New)> UpdatedPolicies,
+ List<(StateEventResponse Old, StateEventResponse New)> RemovedPolicies) updates
+ ) {
+ var ignoreListContent = await homeserver.GetIgnoredUserListAsync();
+
+ foreach (var newEvent in updates.NewPolicies) {
+ var content = newEvent.TypedContent as PolicyRuleEventContent;
+ if (content.Entity is null || content.IsGlobRule()) continue;
+ if (content.GetNormalizedRecommendation() != "m.ban") continue;
+
+ var policyEventReference = new MadsIgnoreMetadataContent.PolicyEventReference() {
+ Type = newEvent.Type,
+ RoomId = newEvent.RoomId,
+ StateKey = newEvent.StateKey!
+ };
+
+ if (ignoreListContent.IgnoredUsers.TryGetValue(content.Entity, out var existingRule)) {
+ if (existingRule.AdditionalData?.ContainsKey(MadsIgnoreMetadataContent.EventId) ?? false) {
+ var existingMetadata = existingRule.GetAdditionalData<MadsIgnoreMetadataContent>(MadsIgnoreMetadataContent.EventId);
+ existingMetadata.Policies.Add(policyEventReference);
+ }
+ else {
+ existingRule.AdditionalData ??= new();
+ existingRule.AdditionalData.Add(MadsIgnoreMetadataContent.EventId, new MadsIgnoreMetadataContent {
+ WasUserAdded = true,
+ Policies = [policyEventReference]
+ });
+ }
+ }
+ else {
+ ignoreListContent.IgnoredUsers[content.Entity] = new() {
+ AdditionalData = new() {
+ [MadsIgnoreMetadataContent.EventId] = new MadsIgnoreMetadataContent {
+ WasUserAdded = false,
+ Policies = [policyEventReference]
+ }
+ }
+ };
+ }
+ }
+
+ foreach (var (previousEvent, newEvent) in updates.RemovedPolicies) {
+ if (previousEvent.Type != UserPolicyRuleEventContent.EventId) continue;
+ var previousContent = previousEvent.ContentAs<UserPolicyRuleEventContent>();
+
+ if (previousContent.Entity is null || previousContent.IsGlobRule()) continue;
+ if (previousContent.GetNormalizedRecommendation() != "m.ban") continue;
+
+ var ignoreList = await homeserver.GetIgnoredUserListAsync();
+ if (ignoreList.IgnoredUsers.TryGetValue(previousContent.Entity, out var existingRule)) {
+ if (existingRule.AdditionalData?.ContainsKey(MadsIgnoreMetadataContent.EventId) ?? false) {
+ var existingMetadata = existingRule.GetAdditionalData<MadsIgnoreMetadataContent>(MadsIgnoreMetadataContent.EventId);
+ existingMetadata.Policies.RemoveAll(x => x.Type == previousEvent.Type && x.RoomId == previousEvent.RoomId && x.StateKey == previousEvent.StateKey);
+ if (!existingMetadata.WasUserAdded)
+ ignoreList.IgnoredUsers.Remove(previousContent.Entity);
+ }
+ }
+ }
+
+ await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreListContent);
+ }
+
+#endregion
+
+#region Feature: Reject invites
+
private Task CheckPoliciesAgainstInvite(RoomInviteContext invite) {
logger.LogInformation("Checking policies against invite");
var sw = Stopwatch.StartNew();
@@ -44,9 +121,9 @@ public class PolicyExecutor(
return Task.CompletedTask;
}
- private async Task CheckPolicyAgainstOutstandingInvites(PolicyRuleEventContent policy) {
+ private async Task CheckPolicyAgainstOutstandingInvites(StateEventResponse newEvent) {
var tasks = roomInviteHandler.Invites
- .Select(invite => CheckPolicyAgainstInvite(invite, policy))
+ .Select(invite => CheckPolicyAgainstInvite(invite, newEvent))
.Where(x => x is not null)
.Cast<Task>() // from Task?
.ToList();
@@ -54,7 +131,8 @@ public class PolicyExecutor(
await Task.WhenAll(tasks);
}
- private Task? CheckPolicyAgainstInvite(RoomInviteContext invite, PolicyRuleEventContent policy) {
+ private Task? CheckPolicyAgainstInvite(RoomInviteContext invite, StateEventResponse policyEvent) {
+ var policy = policyEvent.TypedContent as PolicyRuleEventContent ?? throw new InvalidOperationException("Policy is null");
if (policy.Recommendation != "m.ban") return null;
var policyMatches = false;
@@ -91,4 +169,6 @@ public class PolicyExecutor(
}
});
}
+
+#endregion
}
\ No newline at end of file
|