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
|