about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-03-19 11:38:27 +0100
committerRory& <root@rory.gay>2025-03-19 11:38:27 +0100
commitd766188db6d22227c6bd9c90dfacf2e40f848d9e (patch)
tree3241a38caca7a3e011730bd6874cc04ed612e879
parentAdd more stores, do osme work (diff)
downloadMatrixAntiDmSpam-d766188db6d22227c6bd9c90dfacf2e40f848d9e.tar.xz
Rejecting invites from already banned users works now
m---------LibMatrix0
-rw-r--r--MatrixAntiDmSpam/InviteHandler.cs3
-rw-r--r--MatrixAntiDmSpam/InviteStore.cs12
-rw-r--r--MatrixAntiDmSpam/PolicyExecutor.cs106
-rw-r--r--MatrixAntiDmSpam/PolicyListFetcher.cs14
-rw-r--r--MatrixAntiDmSpam/PolicyStore.cs8
-rw-r--r--MatrixAntiDmSpam/Program.cs1
-rw-r--r--MatrixAntiDmSpam/appsettings.Development.json38
8 files changed, 150 insertions, 32 deletions
diff --git a/LibMatrix b/LibMatrix
-Subproject 44bfb17b1da1bd24140da7d9dcff56fa6fce45e
+Subproject c076141883ae7e02fcef33b8aed2bcbc0a53c16
diff --git a/MatrixAntiDmSpam/InviteHandler.cs b/MatrixAntiDmSpam/InviteHandler.cs

index c4831ee..2a5d17b 100644 --- a/MatrixAntiDmSpam/InviteHandler.cs +++ b/MatrixAntiDmSpam/InviteHandler.cs
@@ -7,10 +7,11 @@ using LibMatrix.Utilities.Bot.Services; namespace MatrixAntiDmSpam; -public class InviteHandler(ILogger<InviteHandler> logger, AntiDmSpamConfiguration config) : InviteHandlerHostedService.IInviteHandler { +public class InviteHandler(ILogger<InviteHandler> logger, AntiDmSpamConfiguration config, InviteStore inviteStore) : InviteHandlerHostedService.IInviteHandler { public async Task HandleInviteAsync(InviteHandlerHostedService.InviteEventArgs invite) { // logger.LogInformation("Received invite to room {}", invite.RoomId); await LogInvite(invite); + await inviteStore.AddInviteAsync(invite); } private async Task LogInvite(InviteHandlerHostedService.InviteEventArgs invite) { diff --git a/MatrixAntiDmSpam/InviteStore.cs b/MatrixAntiDmSpam/InviteStore.cs
index abfa49c..d6e8d49 100644 --- a/MatrixAntiDmSpam/InviteStore.cs +++ b/MatrixAntiDmSpam/InviteStore.cs
@@ -1,14 +1,14 @@ -using LibMatrix; -using System.Collections; -using LibMatrix.EventTypes.Spec.State.Policy; -using LibMatrix.Responses; using LibMatrix.Utilities.Bot.Services; namespace MatrixAntiDmSpam; public class InviteStore { - public void AddInvite(SyncResponse.RoomsDataStructure.InvitedRoomDataStructure invite) { - + + + public async Task AddInviteAsync(InviteHandlerHostedService.InviteEventArgs invite) { + foreach (var handler in OnInviteReceived) { + await handler(invite); + } } public List<Func<InviteHandlerHostedService.InviteEventArgs, Task>> OnInviteReceived { get; set; } = []; diff --git a/MatrixAntiDmSpam/PolicyExecutor.cs b/MatrixAntiDmSpam/PolicyExecutor.cs
index 2f93f11..a6ca404 100644 --- a/MatrixAntiDmSpam/PolicyExecutor.cs +++ b/MatrixAntiDmSpam/PolicyExecutor.cs
@@ -1,28 +1,112 @@ +using ArcaneLibs.Extensions; using LibMatrix.EventTypes.Spec.State.Policy; +using LibMatrix.EventTypes.Spec.State.RoomInfo; +using LibMatrix.Helpers; using LibMatrix.Homeservers; +using LibMatrix.RoomTypes; using LibMatrix.Utilities.Bot.Services; namespace MatrixAntiDmSpam; -public class PolicyExecutor(ILogger<PolicyExecutor> logger, InviteStore inviteStore, PolicyStore policyStore, AuthenticatedHomeserverGeneric homeserver) : IHostedService { +public class PolicyExecutor( + ILogger<PolicyExecutor> logger, + AntiDmSpamConfiguration config, + InviteStore inviteStore, + PolicyStore policyStore, + AuthenticatedHomeserverGeneric homeserver) : IHostedService { + private GenericRoom? _logRoom = string.IsNullOrWhiteSpace(config.LogRoom) ? null : homeserver.GetRoom(config.LogRoom); + public async Task StartAsync(CancellationToken cancellationToken) { inviteStore.OnInviteReceived.Add(CheckPoliciesAgainstInvite); policyStore.OnPolicyUpdated.Add(CheckPolicyAgainstInvites); } - public async Task StopAsync(CancellationToken cancellationToken) { - - } - - public async Task CheckPoliciesAgainstInvite(InviteHandlerHostedService.InviteEventArgs inviteEventArgs) { - if(policyStore.RoomPolicies.Any(x=>x.Value.Entity == inviteEventArgs.RoomId)) - { + public async Task StopAsync(CancellationToken cancellationToken) { } + + public async Task CheckPoliciesAgainstInvite(InviteHandlerHostedService.InviteEventArgs invite) { + string roomName = await GetRoomNameAsync(invite); + + var roomPolicy = policyStore.RoomPolicies.Any(x => x.Value.EntityMatches(invite.RoomId)) + ? policyStore.RoomPolicies.First(x => x.Value.EntityMatches(invite.RoomId)).Value + : null; + + if (roomPolicy is not null) { + logger.LogWarning("Rejecting invite to {}, matching room policy {}", invite.RoomId, roomPolicy.ToJson(ignoreNull: true)); + + var message = new MessageBuilder() + .WithColoredBody("#FF0000", cb => cb.WithBody("Rejecting invite to ").WithMention(invite.RoomId, roomName).WithBody(", matching room policy.").WithNewline()) + .WithCollapsibleSection("Policy JSON", cb => cb.WithCodeBlock(roomPolicy.ToJson(ignoreNull: true), "json")) + .Build(); + + if (_logRoom is not null) + await _logRoom.SendMessageEventAsync(message); + await homeserver.GetRoom(invite.RoomId).LeaveAsync(); + } + + var userPolicy = policyStore.UserPolicies.Any(x => x.Value.EntityMatches(invite.MemberEvent.Sender!)) + ? policyStore.UserPolicies.First(x => x.Value.EntityMatches(invite.MemberEvent.Sender!)).Value + : null; + + if (userPolicy is not null) { + logger.LogWarning("Rejecting invite to {}, matching user policy {}", invite.RoomId, userPolicy.ToJson(ignoreNull: true)); + + var message = new MessageBuilder() + .WithColoredBody("#FF0000", cb => cb.WithBody("Rejecting invite to ").WithMention(invite.RoomId, roomName).WithBody(", matching user policy.").WithNewline()) + .WithCollapsibleSection("Policy JSON", cb => cb.WithCodeBlock(userPolicy.ToJson(ignoreNull: true), "json")) + .Build(); + + if (_logRoom is not null) + await _logRoom.SendMessageEventAsync(message); + + await homeserver.GetRoom(invite.RoomId).LeaveAsync(); } - } - + public async Task CheckPolicyAgainstInvites(PolicyRuleEventContent policy) { - + Console.WriteLine("CheckPolicyAgainstInvites called!!!!"); + } + + private async Task<string> GetRoomNameAsync(InviteHandlerHostedService.InviteEventArgs invite) { + // try to get room name from invite state + var name = invite.InviteData.InviteState?.Events? + .FirstOrDefault(evt => evt is { Type: RoomNameEventContent.EventId, StateKey: "" })? + .ContentAs<RoomNameEventContent>()?.Name; + + if (!string.IsNullOrWhiteSpace(name)) + return name; + + // try to get room alias + var alias = invite.InviteData.InviteState?.Events? + .FirstOrDefault(evt => evt is { Type: RoomCanonicalAliasEventContent.EventId, StateKey: "" })? + .ContentAs<RoomCanonicalAliasEventContent>()?.Alias; + + if (!string.IsNullOrWhiteSpace(alias)) + return alias; + + // try get room name via public previews + try { +#pragma warning disable CS0618 // Type or member is obsolete + name = await invite.Homeserver.GetRoom(invite.RoomId).GetNameOrFallbackAsync(); +#pragma warning restore CS0618 // Type or member is obsolete + if (name != invite.RoomId && !string.IsNullOrWhiteSpace(name)) + return name; + } + catch { + //ignored + } + + // fallback to room alias via public previews + try { + alias = (await invite.Homeserver.GetRoom(invite.RoomId).GetCanonicalAliasAsync())?.Alias; + if (!string.IsNullOrWhiteSpace(alias)) + return alias; + } + catch { + //ignored + } + + // fall back to room ID + return invite.RoomId; } } \ No newline at end of file diff --git a/MatrixAntiDmSpam/PolicyListFetcher.cs b/MatrixAntiDmSpam/PolicyListFetcher.cs
index a068f33..9a521b2 100644 --- a/MatrixAntiDmSpam/PolicyListFetcher.cs +++ b/MatrixAntiDmSpam/PolicyListFetcher.cs
@@ -4,7 +4,8 @@ using LibMatrix.RoomTypes; namespace MatrixAntiDmSpam; -public class PolicyListFetcher(ILogger<PolicyListFetcher> logger, AntiDmSpamConfiguration config, AuthenticatedHomeserverGeneric homeserver, PolicyStore policyStore) : IHostedService { +public class PolicyListFetcher(ILogger<PolicyListFetcher> logger, AntiDmSpamConfiguration config, AuthenticatedHomeserverGeneric homeserver, PolicyStore policyStore) + : IHostedService { public async Task StartAsync(CancellationToken cancellationToken) { logger.LogInformation("Starting policy list fetcher"); await EnsurePolicyListsJoined(); @@ -29,11 +30,14 @@ public class PolicyListFetcher(ILogger<PolicyListFetcher> logger, AntiDmSpamConf private async Task LoadPolicyLists() { foreach (var room in config.PolicyLists) { var sw = Stopwatch.StartNew(); - await LoadPolicyList(homeserver.GetRoom(room.RoomId).AsPolicyRoom()); - logger.LogInformation("Loaded policy list {} in {}", room.RoomId, sw.Elapsed); + var count = await LoadPolicyList(homeserver.GetRoom(room.RoomId).AsPolicyRoom()); + logger.LogInformation("Loaded policy list {} ({}) in {}, with {} policies", room.Name, room.RoomId, sw.Elapsed, count); } } - private async Task LoadPolicyList(PolicyRoom room) { - policyStore.AddPolicies(room.GetPoliciesAsync().ToBlockingEnumerable()); + + private async Task<int> LoadPolicyList(PolicyRoom room) { + var policies = room.GetPoliciesAsync().ToBlockingEnumerable().ToList(); + policyStore.AddPolicies(policies); + return policies.Count(); } } \ No newline at end of file diff --git a/MatrixAntiDmSpam/PolicyStore.cs b/MatrixAntiDmSpam/PolicyStore.cs
index d16f8df..22f9225 100644 --- a/MatrixAntiDmSpam/PolicyStore.cs +++ b/MatrixAntiDmSpam/PolicyStore.cs
@@ -10,9 +10,9 @@ public class PolicyStore { public Dictionary<string, RoomPolicyRuleEventContent> RoomPolicies { get; } = []; public List<Func<PolicyRuleEventContent, Task>> OnPolicyUpdated { get; } = []; - public void AddPolicies(IEnumerable<StateEventResponse> events) => events.ToList().ForEach(AddPolicy); + public async Task AddPolicies(IEnumerable<StateEventResponse> events) => events.ToList().Select(AddPolicy).ToList(); - public void AddPolicy(StateEventResponse evt) { + public async Task AddPolicy(StateEventResponse evt) { var eventKey = $"{evt.RoomId}:{evt.Type}:{evt.StateKey}"; // UserPolicies.ToList().ForEach(x => Console.WriteLine(x.Key)); switch (evt.TypedContent) { @@ -26,5 +26,9 @@ public class PolicyStore { RoomPolicies[eventKey] = roomPolicy; break; } + + foreach (var callback in OnPolicyUpdated) { + await callback((evt.TypedContent as PolicyRuleEventContent)!); + } } } \ No newline at end of file diff --git a/MatrixAntiDmSpam/Program.cs b/MatrixAntiDmSpam/Program.cs
index f813eab..e81cd58 100644 --- a/MatrixAntiDmSpam/Program.cs +++ b/MatrixAntiDmSpam/Program.cs
@@ -11,6 +11,7 @@ builder.Services.AddRoryLibMatrixServices() .WithInviteHandler<InviteHandler>(); builder.Services.AddHostedService<PolicyListFetcher>(); +builder.Services.AddHostedService<PolicyExecutor>(); builder.Services.AddSingleton<InviteStore>(); builder.Services.AddSingleton<PolicyStore>(); diff --git a/MatrixAntiDmSpam/appsettings.Development.json b/MatrixAntiDmSpam/appsettings.Development.json
index 958cc7a..fd582b4 100644 --- a/MatrixAntiDmSpam/appsettings.Development.json +++ b/MatrixAntiDmSpam/appsettings.Development.json
@@ -1,4 +1,5 @@ { + // Don't touch this unless you know what you're doing: "Logging": { "LogLevel": { "Default": "Information", @@ -6,18 +7,41 @@ } }, "LibMatrixBot": { + // Homeserver to connect to. + // Note: Homeserver resolution is applied here, but a direct base URL can be used. "Homeserver": "rory.gay", - "AccessTokenPath": "/home/Rory/matrix_access_token" + + // Absolute path to the file containing the access token + "AccessTokenPath": "/home/Rory/matrix_access_token", + "InviteHandler": { + "SyncConfiguration": { + // How long to wait until the sync request times out + // "Timeout": 300000, + + // Minimum sync interval, useful if you want to have less traffic than a normal client. + "MinimumSyncTime": "00:00:10.000", + + // What presence value to set + // Defaults to "online" if null or not set + // "Presence": "online", + + // Filter to apply to the sync request. Useful if you want custom data to be sent. + // "Filter": { }, + + // Whether to initial sync on startup - very useful in development, or just to be sure. + "InitialSyncOnStartup": true + } + } }, "AntiDmSpam": { "LogRoom": "!GrLSwdAkdrvfMrRYKR:rory.gay", "LogInviteDataAsFile": true, "PolicyLists": [ - { - "Name": "Community Moderation Effort", - "RoomId": "!fTjMjIzNKEsFlUIiru:neko.dev", - "Vias": [ "rory.gay" ] - } + { + "Name": "Community Moderation Effort", + "RoomId": "!fTjMjIzNKEsFlUIiru:neko.dev", + "Vias": [ "rory.gay" ] + } ] } -} +} \ No newline at end of file