From 516db6f00a346d3813541b00471b9e548a9a4460 Mon Sep 17 00:00:00 2001 From: Rory& Date: Thu, 22 May 2025 17:20:42 +0200 Subject: Split up the different features in separate classes --- .gitignore | 2 +- LibMatrix | 2 +- MatrixAntiDmSpam.Core/IgnoreListManager.cs | 152 ++++++++++++++++++ MatrixAntiDmSpam.Core/InviteManager.cs | 109 +++++++++++++ MatrixAntiDmSpam.Core/PolicyExecutor.cs | 238 ----------------------------- MatrixAntiDmSpam.Core/ReportManager.cs | 31 ++++ MatrixAntiDmSpam.sln.DotSettings.user | 3 + MatrixAntiDmSpam/Program.cs | 11 +- 8 files changed, 305 insertions(+), 243 deletions(-) create mode 100644 MatrixAntiDmSpam.Core/IgnoreListManager.cs create mode 100644 MatrixAntiDmSpam.Core/InviteManager.cs delete mode 100644 MatrixAntiDmSpam.Core/PolicyExecutor.cs create mode 100644 MatrixAntiDmSpam.Core/ReportManager.cs diff --git a/.gitignore b/.gitignore index f798f61..6362c07 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ obj/ /packages/ riderModule.iml /_ReSharper.Caches/ -appsettings.Local.json +appsettings.Local*.json diff --git a/LibMatrix b/LibMatrix index e16e9f3..dffe8db 160000 --- a/LibMatrix +++ b/LibMatrix @@ -1 +1 @@ -Subproject commit e16e9f3093fab575f5f9323248e7b19fa6d54566 +Subproject commit dffe8dbb3f477a274df43d9b56e78021d92f3d6d diff --git a/MatrixAntiDmSpam.Core/IgnoreListManager.cs b/MatrixAntiDmSpam.Core/IgnoreListManager.cs new file mode 100644 index 0000000..f917f2b --- /dev/null +++ b/MatrixAntiDmSpam.Core/IgnoreListManager.cs @@ -0,0 +1,152 @@ +using System.Text.Json.Nodes; +using ArcaneLibs.Extensions; +using LibMatrix; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State.Policy; +using LibMatrix.Homeservers; +using LibMatrix.RoomTypes; +using MatrixAntiDmSpam.Core.Classes; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace MatrixAntiDmSpam.Core; + +public class IgnoreListManager( + ILogger logger, + AntiDmSpamConfiguration config, + PolicyStore policyStore, + AuthenticatedHomeserverGeneric homeserver) : IHostedService { + private readonly GenericRoom? _logRoom = string.IsNullOrWhiteSpace(config.LogRoom) ? null : homeserver.GetRoom(config.LogRoom); + + public async Task StartAsync(CancellationToken cancellationToken) { + if (config.IgnoreBannedUsers) { + await CleanupInvalidIgnoreListEntries(); + policyStore.OnPoliciesChanged.Add(UpdateIgnoreList); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + private async Task UpdateIgnoreList(( + List NewPolicies, + List<(StateEventResponse Old, StateEventResponse New)> UpdatedPolicies, + List<(StateEventResponse Old, StateEventResponse New)> RemovedPolicies) updates + ) { + var ignoreListContent = await FilterInvalidIgnoreListEntries(); + 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 ?? throw new InvalidOperationException("RoomId is null"), + StateKey = newEvent.StateKey! + }; + + if (ignoreListContent.IgnoredUsers.TryGetValue(content.Entity, out var existingRule)) { + if (existingRule.AdditionalData?.ContainsKey(MadsIgnoreMetadataContent.EventId) ?? false) { + var existingMetadata = existingRule.GetAdditionalData(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, _) in updates.RemovedPolicies) { + if (previousEvent.Type != UserPolicyRuleEventContent.EventId) continue; + var previousContent = previousEvent.ContentAs(); + + 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.EventId); + existingMetadata.Policies.RemoveAll(x => x.Type == previousEvent.Type && x.RoomId == previousEvent.RoomId && x.StateKey == previousEvent.StateKey); + ignoreList.IgnoredUsers.Remove(previousContent.Entity); + } + } + } + + await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreListContent); + } + + private async Task FilterInvalidIgnoreListEntries() { + var ignoreList = await homeserver.GetAccountDataOrNullAsync(IgnoredUserListEventContent.EventId) + ?? throw new InvalidOperationException("Ignore list is null"); + + ignoreList.IgnoredUsers.RemoveAll((id, ignoredUserData) => { + if (ignoredUserData.AdditionalData is null) return false; + if (!ignoredUserData.AdditionalData.ContainsKey(MadsIgnoreMetadataContent.EventId)) return false; + var metadata = ignoredUserData.GetAdditionalData(MadsIgnoreMetadataContent.EventId)!; + + if (metadata.ContainsKey("policies")) { + var policies = metadata["policies"]!.AsArray(); + + static bool IsPolicyEntryValid(JsonNode? p) => + p?["room_id"]?.GetValue() != null + && p["type"]?.GetValue() != null + && p["state_key"]?.GetValue() != null; + + if (policies.Any(x => !IsPolicyEntryValid(x))) { + logger.LogWarning("Found invalid policy reference in ignore list, removing! {policy}", + policies.Where(x => !IsPolicyEntryValid(x)).Select(x => x.ToJson(ignoreNull: true))); + metadata["policies"] = new JsonArray(policies.Where(IsPolicyEntryValid).ToArray()); + ignoredUserData.AdditionalData[MadsIgnoreMetadataContent.EventId] = metadata; + } + } + + return metadata["was_user_added"]?.GetValue() is null or false + && metadata["policies"]?.AsArray().Count == 0; + }); + + return ignoreList; + } + + private async Task CleanupInvalidIgnoreListEntries() { + var ignoreList = await FilterInvalidIgnoreListEntries(); + ignoreList.IgnoredUsers.RemoveAll((id, _) => !(id.StartsWith('@') && id.Contains(':'))); + List idsToRemove = []; + foreach (var (id, ignoredUserData) in ignoreList.IgnoredUsers) { + if (ignoredUserData.AdditionalData is null) continue; + if (!ignoredUserData.AdditionalData.ContainsKey(MadsIgnoreMetadataContent.EventId)) continue; + try { + var metadata = ignoredUserData.GetAdditionalData(MadsIgnoreMetadataContent.EventId)!; + + if (metadata.Policies.Count == 0 && !metadata.WasUserAdded) { + idsToRemove.Add(id); + } + } + catch (Exception e) { + logger.LogError(e, "Failed to parse ignore list entry for {}", id); + } + } + + foreach (var id in idsToRemove) { + ignoreList.IgnoredUsers.Remove(id); + } + + if (idsToRemove.Count > 0) { + logger.LogWarning("Removed {count} invalid entries from ignore list", idsToRemove.Count); + await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreList); + } + } +} \ No newline at end of file diff --git a/MatrixAntiDmSpam.Core/InviteManager.cs b/MatrixAntiDmSpam.Core/InviteManager.cs new file mode 100644 index 0000000..328b90c --- /dev/null +++ b/MatrixAntiDmSpam.Core/InviteManager.cs @@ -0,0 +1,109 @@ +using System.Diagnostics; +using ArcaneLibs.Attributes; +using ArcaneLibs.Extensions; +using LibMatrix; +using LibMatrix.EventTypes.Spec.State.Policy; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.RoomTypes; +using LibMatrix.Utilities.Bot.Interfaces; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace MatrixAntiDmSpam.Core; + +public class InviteManager( + ILogger logger, + AntiDmSpamConfiguration config, + RoomInviteHandler roomInviteHandler, + PolicyStore policyStore, + AuthenticatedHomeserverGeneric homeserver) : IHostedService { + private readonly GenericRoom? _logRoom = string.IsNullOrWhiteSpace(config.LogRoom) ? null : homeserver.GetRoom(config.LogRoom); + public List> OnInviteRejected { get; } = []; + + public async Task StartAsync(CancellationToken cancellationToken) { + roomInviteHandler.OnInviteReceived.Add(CheckPoliciesAgainstInvite); + policyStore.OnPolicyAdded.Add(CheckPolicyAgainstOutstandingInvites); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + private Task CheckPoliciesAgainstInvite(RoomInviteContext invite) { + logger.LogInformation("Checking policies against invite"); + var sw = Stopwatch.StartNew(); + + // Technically not required, but helps with scaling against millions of policies + Parallel.ForEach(policyStore.AllPolicies.Values, (policy, loopState, idx) => { + if (CheckPolicyAgainstInvite(invite, policy) is not null) { + logger.LogInformation("Found matching policy after {} iterations ({})", idx, sw.Elapsed); + loopState.Break(); + } + }); + + return Task.CompletedTask; + } + + private async Task CheckPolicyAgainstOutstandingInvites(StateEventResponse newEvent) { + var tasks = roomInviteHandler.Invites + .Select(invite => CheckPolicyAgainstInvite(invite, newEvent)) + .Where(x => x is not null) + .Cast() // from Task? + .ToList(); + + await Task.WhenAll(tasks); + } + + 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; + switch (policy) { + case UserPolicyRuleEventContent userPolicy: + policyMatches = userPolicy.EntityMatches(invite.MemberEvent.Sender!); + break; + case ServerPolicyRuleEventContent serverPolicy: + policyMatches = serverPolicy.EntityMatches(invite.MemberEvent.Sender!); + break; + case RoomPolicyRuleEventContent roomPolicy: + policyMatches = roomPolicy.EntityMatches(invite.RoomId); + break; + default: + if (_logRoom is not null) + _ = _logRoom.SendMessageEventAsync(new MessageBuilder().WithColoredBody("#FF0000", "Unknown policy type " + policy.GetType().FullName).Build()); + break; + } + + if (!policyMatches) { + logger.LogTrace("[{}] Policy {} does not match invite to {} by {}: {}", homeserver.WhoAmI.UserId, policy.GetType().GetFriendlyName(), invite.RoomId, + invite.MemberEvent.Sender, policy.ToJson(ignoreNull: true, indent: false)); + return null; + } + + + return LogAndRejectInvite(invite, policyEvent); + } + + private async Task LogAndRejectInvite(RoomInviteContext invite, StateEventResponse policyEvent) { + var policy = policyEvent.TypedContent as PolicyRuleEventContent ?? throw new InvalidOperationException("Policy is null"); + var policyRoom = config.PolicyLists.First(x => x.RoomId == policyEvent.RoomId); + logger.LogWarning("[{}] Rejecting invite to {}, matching {} in {}: {}", homeserver.WhoAmI.UserId, invite.RoomId, policy.GetType().GetFriendlyName(), + policyRoom.Name, policy.ToJson(ignoreNull: true)); + + if (_logRoom is not null) { + var roomName = await invite.TryGetRoomNameAsync(); + + await roomInviteHandler.RejectInvite(invite, new MessageBuilder() + .WithColoredBody("#FF0000", + cb => cb.WithBody("Rejecting invite to ").WithMention(invite.RoomId, roomName) + .WithBody($", matching {policy.GetType().GetFriendlyName().ToLowerInvariant()} in {policyRoom.Name}.") + .WithNewline()) + .WithCollapsibleSection("Policy JSON", cb => cb.WithCodeBlock(policy.ToJson(ignoreNull: true), "json")) + ); + } + + foreach (var callback in OnInviteRejected) { + await callback(invite, policyEvent); + } + } +} \ No newline at end of file diff --git a/MatrixAntiDmSpam.Core/PolicyExecutor.cs b/MatrixAntiDmSpam.Core/PolicyExecutor.cs deleted file mode 100644 index 9d6a843..0000000 --- a/MatrixAntiDmSpam.Core/PolicyExecutor.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System.Diagnostics; -using System.Text.Json.Nodes; -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; - -namespace MatrixAntiDmSpam.Core; - -public class PolicyExecutor( - ILogger logger, - AntiDmSpamConfiguration config, - RoomInviteHandler roomInviteHandler, - PolicyStore policyStore, - AuthenticatedHomeserverGeneric homeserver) : IHostedService { - private readonly GenericRoom? _logRoom = string.IsNullOrWhiteSpace(config.LogRoom) ? null : homeserver.GetRoom(config.LogRoom); - - public async Task StartAsync(CancellationToken cancellationToken) { - roomInviteHandler.OnInviteReceived.Add(CheckPoliciesAgainstInvite); - policyStore.OnPolicyAdded.Add(CheckPolicyAgainstOutstandingInvites); - if (config.IgnoreBannedUsers) { - await CleanupInvalidIgnoreListEntries(); - policyStore.OnPoliciesChanged.Add(UpdateIgnoreList); - } - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - -#region Feature: Manage ignore list - - private async Task UpdateIgnoreList(( - List NewPolicies, - List<(StateEventResponse Old, StateEventResponse New)> UpdatedPolicies, - List<(StateEventResponse Old, StateEventResponse New)> RemovedPolicies) updates - ) { - var ignoreListContent = await FilterInvalidIgnoreListEntries(); - - 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 ?? throw new InvalidOperationException("RoomId is null"), - StateKey = newEvent.StateKey! - }; - - if (ignoreListContent.IgnoredUsers.TryGetValue(content.Entity, out var existingRule)) { - if (existingRule.AdditionalData?.ContainsKey(MadsIgnoreMetadataContent.EventId) ?? false) { - var existingMetadata = existingRule.GetAdditionalData(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(); - - 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.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); - } - - private async Task FilterInvalidIgnoreListEntries() { - var ignoreList = await homeserver.GetAccountDataOrNullAsync(IgnoredUserListEventContent.EventId) - ?? throw new InvalidOperationException("Ignore list is null"); - - ignoreList.IgnoredUsers.RemoveAll((id, ignoredUserData) => { - if (ignoredUserData.AdditionalData is null) return false; - if (!ignoredUserData.AdditionalData.ContainsKey(MadsIgnoreMetadataContent.EventId)) return false; - var metadata = ignoredUserData.GetAdditionalData(MadsIgnoreMetadataContent.EventId)!; - - if (metadata.ContainsKey("policies")) { - var policies = metadata["policies"]!.AsArray(); - - static bool IsPolicyEntryValid(JsonNode? p) => - p?["room_id"]?.GetValue() != null - && p["type"]?.GetValue() != null - && p["state_key"]?.GetValue() != null; - - if (policies.Any(x => !IsPolicyEntryValid(x))) { - logger.LogWarning("Found invalid policy reference in ignore list, removing! {policy}", - policies.Where(x => !IsPolicyEntryValid(x)).Select(x => x.ToJson(ignoreNull: true))); - metadata["policies"] = new JsonArray(policies.Where(IsPolicyEntryValid).ToArray()); - ignoredUserData.AdditionalData[MadsIgnoreMetadataContent.EventId] = metadata; - } - } - - return metadata["was_user_added"]?.GetValue() is null or false - && metadata["policies"]?.AsArray().Count == 0; - }); - - return ignoreList; - } - - private async Task CleanupInvalidIgnoreListEntries() { - var ignoreList = await FilterInvalidIgnoreListEntries(); - ignoreList.IgnoredUsers.RemoveAll((id, _) => !(id.StartsWith('@') && id.Contains(':'))); - List idsToRemove = []; - foreach (var (id, ignoredUserData) in ignoreList.IgnoredUsers) { - if (ignoredUserData.AdditionalData is null) continue; - if (!ignoredUserData.AdditionalData.ContainsKey(MadsIgnoreMetadataContent.EventId)) continue; - try { - var metadata = ignoredUserData.GetAdditionalData(MadsIgnoreMetadataContent.EventId)!; - - if (metadata.Policies.Count == 0 && !metadata.WasUserAdded) { - idsToRemove.Add(id); - } - } - catch (Exception e) { - logger.LogError(e, "Failed to parse ignore list entry for {}", id); - } - } - - foreach (var id in idsToRemove) { - ignoreList.IgnoredUsers.Remove(id); - } - - await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreList); - } - -#endregion - -#region Feature: Report blocked invites - - - -#endregion - -#region Feature: Reject invites - - private Task CheckPoliciesAgainstInvite(RoomInviteContext invite) { - logger.LogInformation("Checking policies against invite"); - var sw = Stopwatch.StartNew(); - - // Technically not required, but helps with scaling against millions of policies - Parallel.ForEach(policyStore.AllPolicies.Values, (policy, loopState, idx) => { - if (CheckPolicyAgainstInvite(invite, policy) is not null) { - logger.LogInformation("Found matching policy after {} iterations ({})", idx, sw.Elapsed); - loopState.Break(); - } - }); - - return Task.CompletedTask; - } - - private async Task CheckPolicyAgainstOutstandingInvites(StateEventResponse newEvent) { - var tasks = roomInviteHandler.Invites - .Select(invite => CheckPolicyAgainstInvite(invite, newEvent)) - .Where(x => x is not null) - .Cast() // from Task? - .ToList(); - - await Task.WhenAll(tasks); - } - - 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; - switch (policy) { - case UserPolicyRuleEventContent userPolicy: - policyMatches = userPolicy.EntityMatches(invite.MemberEvent.Sender!); - break; - case ServerPolicyRuleEventContent serverPolicy: - policyMatches = serverPolicy.EntityMatches(invite.MemberEvent.Sender!); - break; - case RoomPolicyRuleEventContent roomPolicy: - policyMatches = roomPolicy.EntityMatches(invite.RoomId); - break; - default: - if (_logRoom is not null) - _ = _logRoom.SendMessageEventAsync(new MessageBuilder().WithColoredBody("#FF0000", "Unknown policy type " + policy.GetType().FullName).Build()); - break; - } - - if (!policyMatches) return null; - var policyRoom = config.PolicyLists.First(x=>x.RoomId == policyEvent.RoomId); - logger.LogWarning("[{}] Rejecting invite to {}, matching {} in {}: {}", homeserver.WhoAmI.UserId, invite.RoomId, policy.GetType().GetFriendlyName(), - policyRoom.Name, policy.ToJson(ignoreNull: true)); - - return Task.Run(async () => { - if (_logRoom is not null) { - var roomName = await invite.TryGetRoomNameAsync(); - - await roomInviteHandler.RejectInvite(invite, new MessageBuilder() - .WithColoredBody("#FF0000", - cb => cb.WithBody("Rejecting invite to ").WithMention(invite.RoomId, roomName) - .WithBody($", matching {policy.GetType().GetFriendlyName().ToLowerInvariant()} in {policyRoom.Name}.") - .WithNewline()) - .WithCollapsibleSection("Policy JSON", cb => cb.WithCodeBlock(policy.ToJson(ignoreNull: true), "json")) - ); - } - }); - } - -#endregion -} \ No newline at end of file diff --git a/MatrixAntiDmSpam.Core/ReportManager.cs b/MatrixAntiDmSpam.Core/ReportManager.cs new file mode 100644 index 0000000..46f0666 --- /dev/null +++ b/MatrixAntiDmSpam.Core/ReportManager.cs @@ -0,0 +1,31 @@ +using LibMatrix; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.RoomTypes; +using LibMatrix.Utilities.Bot.Interfaces; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace MatrixAntiDmSpam.Core; + +public class ReportManager( + ILogger logger, + AntiDmSpamConfiguration config, + InviteManager inviteManager, + AuthenticatedHomeserverGeneric homeserver) : IHostedService { + private readonly GenericRoom? _logRoom = string.IsNullOrWhiteSpace(config.LogRoom) ? null : homeserver.GetRoom(config.LogRoom); + + public async Task StartAsync(CancellationToken cancellationToken) { + if (config.ReportBlockedInvites) { + inviteManager.OnInviteRejected.Add(ReportRejectedInvite); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + private async Task ReportRejectedInvite(RoomInviteContext invite, StateEventResponse policyEvent) { + logger.LogInformation("ReportRejectedInvite not implemented"); + var msgTask = _logRoom?.SendMessageEventAsync(new MessageBuilder().WithBody("meow").Build()); + if (msgTask is not null) await msgTask; + } +} \ No newline at end of file diff --git a/MatrixAntiDmSpam.sln.DotSettings.user b/MatrixAntiDmSpam.sln.DotSettings.user index 4cc1d7d..1a4000d 100644 --- a/MatrixAntiDmSpam.sln.DotSettings.user +++ b/MatrixAntiDmSpam.sln.DotSettings.user @@ -1,5 +1,7 @@  ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -7,4 +9,5 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded \ No newline at end of file diff --git a/MatrixAntiDmSpam/Program.cs b/MatrixAntiDmSpam/Program.cs index 5da7184..0be6825 100644 --- a/MatrixAntiDmSpam/Program.cs +++ b/MatrixAntiDmSpam/Program.cs @@ -1,3 +1,4 @@ +using LibMatrix.Extensions; using LibMatrix.Services; using LibMatrix.Utilities.Bot; using MatrixAntiDmSpam.Core; @@ -10,14 +11,18 @@ builder.Services.AddRoryLibMatrixServices() .WithInviteHandler(); builder.Services.AddHostedService(); -builder.Services.AddHostedService(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(sp => sp.GetRequiredService()); builder.Services.AddSingleton(); builder.Configuration["LibMatrixBot:InviteListener:SyncConfiguration:MinimumSyncTime"] ??= builder.Configuration["AntiDmSpam:MinimumSyncTime"]; - -// MatrixHttpClient.LogRequests = false; +MatrixHttpClient.LogRequests = false; var host = builder.Build(); host.Run(); \ No newline at end of file -- cgit 1.5.1