using LibMatrix.Filters; using LibMatrix.Helpers; using LibMatrix.Homeservers; using LibMatrix.RoomTypes; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace MatrixAntiDmSpam.Core; public class PolicyListFetcher(ILogger logger, AntiDmSpamConfiguration config, AuthenticatedHomeserverGeneric homeserver, PolicyStore policyStore) : IHostedService { private CancellationTokenSource _cts = new(); public async Task StartAsync(CancellationToken cancellationToken) { // _ = Enumerable.Range(0, 10_000_000).Select(x => { // policyStore.AllPolicies.Add(Guid.NewGuid().ToString(), new UserPolicyRuleEventContent() { // Entity = Guid.NewGuid().ToString() + x, // Reason = "meow " + x, // Recommendation = "m.ban" // }); // return 0; // }).ToList(); logger.LogInformation("Starting policy list fetcher"); await EnsurePolicyListsJoined(); _ = SyncPolicyLists().ContinueWith(x => { if (x.IsFaulted) { logger.LogError(x.Exception, "Error in policy list fetcher"); } }, TaskContinuationOptions.OnlyOnFaulted); } public async Task StopAsync(CancellationToken cancellationToken) { logger.LogInformation("Stopping policy list fetcher"); await _cts.CancelAsync(); } private async Task EnsurePolicyListsJoined() { var joinedRooms = await homeserver.GetJoinedRooms(); var expectedPolicyRooms = config.PolicyLists; var missingRooms = expectedPolicyRooms.Where(room => !joinedRooms.Any(r => r.RoomId == room.RoomId)).ToList(); await Task.WhenAll(missingRooms.Select(async room => { logger.LogInformation("Joining policy list room {}", room.RoomId); await homeserver.GetRoom(room.RoomId).JoinAsync(room.Vias); }).ToList()); } private async Task SyncPolicyLists() { var syncHelper = new SyncHelper(homeserver, logger) { Timeout = 30_000, MinimumDelay = config.MinimumSyncTime, FilterId = (await homeserver.UploadFilterAsync(new SyncFilter { AccountData = SyncFilter.EventFilter.Empty, Presence = SyncFilter.EventFilter.Empty, Room = new SyncFilter.RoomFilter { AccountData = SyncFilter.RoomFilter.StateFilter.Empty, Ephemeral = SyncFilter.RoomFilter.StateFilter.Empty, State = new SyncFilter.RoomFilter.StateFilter(types: PolicyRoom.SpecPolicyEventTypes.ToList()), Timeline = new SyncFilter.RoomFilter.StateFilter(types: PolicyRoom.SpecPolicyEventTypes.ToList()), Rooms = config.PolicyLists.Select(x => x.RoomId).ToList(), IncludeLeave = false } })).FilterId, UseMsc4222StateAfter = true }; await foreach (var syncResponse in syncHelper.EnumerateSyncAsync(_cts.Token)) { if (_cts.IsCancellationRequested) return; if (syncResponse is { Rooms.Join.Count: > 0 }) { var newPolicies = syncResponse.Rooms.Join .Where(x => config.PolicyLists.Any(y => y.RoomId == x.Key)) .SelectMany(x => x.Value.StateAfter?.Events ?? []) .Where(x => PolicyRoom.SpecPolicyEventTypes.Contains(x.Type)) .ToList(); logger.LogWarning("Received non-empty sync response with {}/{}/{} rooms, resulting in {} policies", syncResponse.Rooms?.Join?.Count, syncResponse.Rooms?.Invite?.Count, syncResponse.Rooms?.Leave?.Count, newPolicies.Count); await policyStore.AddPoliciesAsync(newPolicies); } } } }