about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-05-22 17:20:42 +0200
committerRory& <root@rory.gay>2025-05-22 17:20:42 +0200
commit516db6f00a346d3813541b00471b9e548a9a4460 (patch)
treeaac46874756a86ad011e2e10ae271424f2f5b4d6
parentMove logging to the end of sample config (diff)
downloadMatrixAntiDmSpam-516db6f00a346d3813541b00471b9e548a9a4460.tar.xz
Split up the different features in separate classes
-rw-r--r--.gitignore2
m---------LibMatrix0
-rw-r--r--MatrixAntiDmSpam.Core/IgnoreListManager.cs (renamed from MatrixAntiDmSpam.Core/PolicyExecutor.cs)102
-rw-r--r--MatrixAntiDmSpam.Core/InviteManager.cs109
-rw-r--r--MatrixAntiDmSpam.Core/ReportManager.cs31
-rw-r--r--MatrixAntiDmSpam.sln.DotSettings.user3
-rw-r--r--MatrixAntiDmSpam/Program.cs11
7 files changed, 160 insertions, 98 deletions
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 -Subproject e16e9f3093fab575f5f9323248e7b19fa6d5456 +Subproject dffe8dbb3f477a274df43d9b56e78021d92f3d6 diff --git a/MatrixAntiDmSpam.Core/PolicyExecutor.cs b/MatrixAntiDmSpam.Core/IgnoreListManager.cs
index 9d6a843..f917f2b 100644 --- a/MatrixAntiDmSpam.Core/PolicyExecutor.cs +++ b/MatrixAntiDmSpam.Core/IgnoreListManager.cs
@@ -1,31 +1,24 @@ -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<PolicyExecutor> logger, +public class IgnoreListManager( + ILogger<IgnoreListManager> 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); @@ -34,15 +27,12 @@ public class PolicyExecutor( public Task StopAsync(CancellationToken cancellationToken) => 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 FilterInvalidIgnoreListEntries(); - foreach (var newEvent in updates.NewPolicies) { var content = newEvent.TypedContent as PolicyRuleEventContent; if (content.Entity is null || content.IsGlobRule()) continue; @@ -79,7 +69,7 @@ public class PolicyExecutor( } } - foreach (var (previousEvent, newEvent) in updates.RemovedPolicies) { + foreach (var (previousEvent, _) in updates.RemovedPolicies) { if (previousEvent.Type != UserPolicyRuleEventContent.EventId) continue; var previousContent = previousEvent.ContentAs<UserPolicyRuleEventContent>(); @@ -91,8 +81,7 @@ public class PolicyExecutor( 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); + ignoreList.IgnoredUsers.Remove(previousContent.Entity); } } } @@ -125,7 +114,7 @@ public class PolicyExecutor( } } - return metadata["was_user_added"]?.GetValue<bool>() is null or false + return metadata["was_user_added"]?.GetValue<bool>() is null or false && metadata["policies"]?.AsArray().Count == 0; }); @@ -155,84 +144,9 @@ public class PolicyExecutor( 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<Task>() // 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 (idsToRemove.Count > 0) { + logger.LogWarning("Removed {count} invalid entries from ignore list", idsToRemove.Count); + await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreList); } - - 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/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<InviteManager> 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<Func<RoomInviteContext, StateEventResponse, Task>> 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<Task>() // 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/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<ReportManager> 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 @@ <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F97_003Ffb30ee1f_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F97_003Ffb30ee1f_003FExceptionDispatchInfo_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003Fc1_003F72e4c91c_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003Fc1_003F72e4c91c_003FExceptionDispatchInfo_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHashtable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003Fd2_003F20632896_003FHashtable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F65_003Fb77a719c_003FList_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> @@ -7,4 +9,5 @@ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObjectExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8fd5e96d6574456095123be1ecfbdfa914200_003Fe2_003F3561a383_003FObjectExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceCollectionHostedServiceExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff13297a632424a6abffea4dd75a36a75d128_003Ff9_003Fb35aae11_003FServiceCollectionHostedServiceExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStackFrameIterator_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F86_003F8b4aa64e_003FStackFrameIterator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStopwatch_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003F17_003F630e8c5b_003FStopwatch_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATaskAwaiter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F10_003F04572d58_003FTaskAwaiter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> \ 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<RoomInviteHandler>(); builder.Services.AddHostedService<PolicyListFetcher>(); -builder.Services.AddHostedService<PolicyExecutor>(); +builder.Services.AddSingleton<InviteManager>(); +builder.Services.AddHostedService<InviteManager>(sp => sp.GetRequiredService<InviteManager>()); +builder.Services.AddSingleton<IgnoreListManager>(); +builder.Services.AddHostedService<IgnoreListManager>(sp => sp.GetRequiredService<IgnoreListManager>()); +builder.Services.AddSingleton<ReportManager>(); +builder.Services.AddHostedService<ReportManager>(sp => sp.GetRequiredService<ReportManager>()); builder.Services.AddSingleton<PolicyStore>(); 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