about summary refs log tree commit diff
path: root/MatrixAntiDmSpam.Core/PolicyStore.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MatrixAntiDmSpam.Core/PolicyStore.cs')
-rw-r--r--MatrixAntiDmSpam.Core/PolicyStore.cs104
1 files changed, 92 insertions, 12 deletions
diff --git a/MatrixAntiDmSpam.Core/PolicyStore.cs b/MatrixAntiDmSpam.Core/PolicyStore.cs

index 9c439fa..0dfd490 100644 --- a/MatrixAntiDmSpam.Core/PolicyStore.cs +++ b/MatrixAntiDmSpam.Core/PolicyStore.cs
@@ -1,24 +1,104 @@ +using System.Diagnostics; using LibMatrix; using LibMatrix.EventTypes.Spec.State.Policy; +using Microsoft.Extensions.Logging; namespace MatrixAntiDmSpam.Core; -public class PolicyStore { - public Dictionary<string, PolicyRuleEventContent> AllPolicies { get; } = []; - public List<Func<PolicyRuleEventContent, Task>> OnPolicyUpdated { get; } = []; +public class PolicyStore(ILogger<PolicyStore> logger) { + public Dictionary<string, StateEventResponse> AllPolicies { get; } = []; - public Task AddPoliciesAsync(IEnumerable<StateEventResponse> events) => Task.WhenAll(events.Select(AddPolicyAsync).ToList()); +#region Single policy events - public async Task AddPolicyAsync(StateEventResponse evt) { - var eventKey = $"{evt.RoomId}:{evt.Type}:{evt.StateKey}"; - if (evt.TypedContent is PolicyRuleEventContent policy) { - if (policy.Recommendation == "m.ban") - AllPolicies[eventKey] = policy; - else AllPolicies.Remove(eventKey); + /// <summary> + /// Fired when any policy event is received + /// </summary> + public List<Func<StateEventResponse, Task>> OnPolicyReceived { get; } = []; - foreach (var callback in OnPolicyUpdated) { - await callback(policy); + /// <summary> + /// Fired when a new policy is added + /// </summary> + public List<Func<StateEventResponse, Task>> OnPolicyAdded { get; } = []; + + /// <summary> + /// Fired when a policy is updated, without being removed. + /// </summary> + public List<Func<StateEventResponse, StateEventResponse, Task>> OnPolicyUpdated { get; } = []; + + /// <summary> + /// Fired when a policy is removed. + /// </summary> + public List<Func<StateEventResponse, StateEventResponse, Task>> OnPolicyRemoved { get; } = []; + +#endregion + +#region Bulk policy events + + /// <summary> + /// Fired when any policy event is received + /// </summary> + public List<Func<(List<StateEventResponse> NewPolicies, + List<(StateEventResponse Old, StateEventResponse New)> UpdatedPolicies, + List<(StateEventResponse Old, StateEventResponse New)> RemovedPolicies), Task>> OnPoliciesChanged { get; } = []; + +#endregion + + public async Task AddPoliciesAsync(IEnumerable<StateEventResponse> events) { + var policyEvents = events + .Where(evt => evt.TypedContent is PolicyRuleEventContent) + .ToList(); + List<StateEventResponse> newPolicies = new(); + List<(StateEventResponse Old, StateEventResponse New)> updatedPolicies = new(); + List<(StateEventResponse Old, StateEventResponse New)> removedPolicies = new(); + + var sw = Stopwatch.StartNew(); + try { + foreach (var evt in policyEvents) { + var eventKey = $"{evt.RoomId}:{evt.Type}:{evt.StateKey}"; + if (evt.TypedContent is not PolicyRuleEventContent policy) continue; + if (policy.GetNormalizedRecommendation() is "m.ban") { + if (AllPolicies.TryGetValue(eventKey, out var oldContent)) + updatedPolicies.Add((oldContent, evt)); + else + newPolicies.Add(evt); + AllPolicies[eventKey] = evt; + } + else if (AllPolicies.Remove(eventKey, out var oldContent)) + removedPolicies.Add((oldContent, evt)); } } + catch (Exception e) { + Console.WriteLine(e); + } + + logger.LogInformation("Processed {Count} policies in {Elapsed}", policyEvents.Count, sw.Elapsed); + + + // execute all the callbacks in parallel, as much as possible... + await Task.WhenAll( + Task.WhenAll( + policyEvents.Select(evt => + Task.WhenAll(OnPolicyReceived.Select(callback => callback(evt)).ToList()) + ) + ), + Task.WhenAll( + newPolicies.Select(evt => + Task.WhenAll(OnPolicyAdded.Select(callback => callback(evt)).ToList()) + ) + ), + Task.WhenAll( + updatedPolicies.Select(evt => + Task.WhenAll(OnPolicyUpdated.Select(callback => callback(evt.Old, evt.New)).ToList()) + ) + ), + Task.WhenAll( + removedPolicies.Select(evt => + Task.WhenAll(OnPolicyRemoved.Select(callback => callback(evt.Old, evt.New)).ToList()) + ) + ), + Task.WhenAll(OnPoliciesChanged.Select(callback => callback((newPolicies, updatedPolicies, removedPolicies))).ToList()) + ); } + + private async Task AddPolicyAsync(StateEventResponse evt) { } } \ No newline at end of file