about summary refs log tree commit diff
path: root/LibMatrix
diff options
context:
space:
mode:
Diffstat (limited to 'LibMatrix')
-rw-r--r--LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs6
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs9
-rw-r--r--LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Filters/SynapseAdminLocalRoomQueryFilter.cs109
-rw-r--r--LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Responses/RoomListResult.cs52
-rw-r--r--LibMatrix/Homeservers/ImplementationDetails/Synapse/SynapseAdminApiClient.cs167
-rw-r--r--LibMatrix/LibMatrix.csproj6
6 files changed, 274 insertions, 75 deletions
diff --git a/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs b/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs

index e34b5cf..be8aa8d 100644 --- a/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs +++ b/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs
@@ -45,7 +45,7 @@ public class Msc4222EmulationSyncProcessor(AuthenticatedHomeserverGeneric homese tasks.AddRange(resp.Rooms.Leave.Select(ProcessLeftRooms).ToList()); } - var tasksEnum = tasks.ToAsyncEnumerable(); + var tasksEnum = tasks.ToAsyncResultEnumerable(); await foreach (var wasModified in tasksEnum) { if (wasModified) { modified = true; @@ -129,7 +129,7 @@ public class Msc4222EmulationSyncProcessor(AuthenticatedHomeserverGeneric homese } }); - var tasksEnum = tasks.ToAsyncEnumerable(); + var tasksEnum = tasks.ToAsyncResultEnumerable(); await foreach (var evt in tasksEnum) { data.StateAfter.Events.Add(evt); } @@ -198,7 +198,7 @@ public class Msc4222EmulationSyncProcessor(AuthenticatedHomeserverGeneric homese } }); - var tasksEnum = tasks.ToAsyncEnumerable(); + var tasksEnum = tasks.ToAsyncResultEnumerable(); await foreach (var evt in tasksEnum) { data.StateAfter.Events.Add(evt); } diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index 47e7039..916780e 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -147,7 +147,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { await Task.Delay(1000); } } - }).ToAsyncEnumerable(); + }).ToAsyncResultEnumerable(); await foreach (var result in tasks) if (result is not null) @@ -217,7 +217,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { if (preserveCustomRoomProfile) { var rooms = await GetJoinedRooms(); - var roomProfiles = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncEnumerable(); + var roomProfiles = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncResultEnumerable(); targetSyncCount = rooms.Count; await foreach (var (roomId, currentRoomProfile) in roomProfiles) try { @@ -290,7 +290,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { public async IAsyncEnumerable<KeyValuePair<string, RoomMemberEventContent>> GetRoomProfilesAsync() { var rooms = await GetJoinedRooms(); - var results = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncEnumerable(); + var results = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncResultEnumerable(); await foreach (var res in results) yield return res; } @@ -613,6 +613,9 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { [JsonPropertyName("gay.rory.bulk_send_events")] public BooleanCapability? BulkSendEvents { get; set; } + [JsonPropertyName("gay.rory.synapse_admin_extensions.room_list.query_events.v2")] + public BooleanCapability? SynapseRoomListQueryEventsV2 { get; set; } + [JsonExtensionData] public Dictionary<string, object>? AdditionalCapabilities { get; set; } } diff --git a/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Filters/SynapseAdminLocalRoomQueryFilter.cs b/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Filters/SynapseAdminLocalRoomQueryFilter.cs
index b8929a0..97c4bbf 100644 --- a/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Filters/SynapseAdminLocalRoomQueryFilter.cs +++ b/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Filters/SynapseAdminLocalRoomQueryFilter.cs
@@ -1,27 +1,90 @@ namespace LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters; public class SynapseAdminLocalRoomQueryFilter { - public string RoomIdContains { get; set; } = ""; - public string NameContains { get; set; } = ""; - public string CanonicalAliasContains { get; set; } = ""; - public string VersionContains { get; set; } = ""; - public string CreatorContains { get; set; } = ""; - public string EncryptionContains { get; set; } = ""; - public string JoinRulesContains { get; set; } = ""; - public string GuestAccessContains { get; set; } = ""; - public string HistoryVisibilityContains { get; set; } = ""; - - public bool Federatable { get; set; } = true; - public bool Public { get; set; } = true; - - public int JoinedMembersGreaterThan { get; set; } - public int JoinedMembersLessThan { get; set; } = int.MaxValue; - - public int JoinedLocalMembersGreaterThan { get; set; } - public int JoinedLocalMembersLessThan { get; set; } = int.MaxValue; - public int StateEventsGreaterThan { get; set; } - public int StateEventsLessThan { get; set; } = int.MaxValue; - - public bool CheckFederation { get; set; } - public bool CheckPublic { get; set; } + public StringFilter RoomId { get; set; } = new(); + public StringFilter Name { get; set; } = new(); + public StringFilter CanonicalAlias { get; set; } = new(); + public StringFilter Version { get; set; } = new(); + public StringFilter Creator { get; set; } = new(); + public StringFilter Encryption { get; set; } = new(); + public StringFilter JoinRules { get; set; } = new(); + public StringFilter GuestAccess { get; set; } = new(); + public StringFilter HistoryVisibility { get; set; } = new(); + public StringFilter RoomType { get; set; } = new(); + public StringFilter Topic { get; set; } = new(); + + public IntFilter JoinedMembers { get; set; } = new() { + GreaterThan = 0, + LessThan = int.MaxValue + }; + + public IntFilter JoinedLocalMembers { get; set; } = new() { + GreaterThan = 0, + LessThan = int.MaxValue + }; + + public IntFilter StateEvents { get; set; } = new() { + GreaterThan = 0, + LessThan = int.MaxValue + }; + + public BoolFilter Federation { get; set; } = new(); + public BoolFilter Public { get; set; } = new(); + public BoolFilter Tombstone { get; set; } = new(); +} + +public class OptionalFilter { + public bool Enabled { get; set; } +} + +public class StringFilter : OptionalFilter { + public bool CheckValueContains { get; set; } + public string? ValueContains { get; set; } + + public bool CheckValueEquals { get; set; } + public string? ValueEquals { get; set; } + + public bool Matches(string? value, StringComparison comparison = StringComparison.Ordinal) { + if (!Enabled) return true; + + if (CheckValueEquals) { + if (!string.Equals(value, ValueEquals, comparison)) return false; + } + + if (CheckValueContains && ValueContains != null) { + if (value != null && !value.Contains(ValueContains, comparison)) return false; + } + + return true; + } +} + +public class IntFilter : OptionalFilter { + public bool CheckGreaterThan { get; set; } + public int GreaterThan { get; set; } + public bool CheckLessThan { get; set; } + public int LessThan { get; set; } + + public bool Matches(int value) { + if (!Enabled) return true; + + if (CheckGreaterThan) { + if (value <= GreaterThan) return false; + } + + if (CheckLessThan) { + if (value >= LessThan) return false; + } + + return true; + } +} + +public class BoolFilter : OptionalFilter { + public bool Value { get; set; } + + public bool Matches(bool value) { + if (!Enabled) return true; + return value == Value; + } } \ No newline at end of file diff --git a/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Responses/RoomListResult.cs b/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Responses/RoomListResult.cs
index d84c89b..3bc1f47 100644 --- a/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Responses/RoomListResult.cs +++ b/LibMatrix/Homeservers/ImplementationDetails/Synapse/Models/Responses/RoomListResult.cs
@@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using LibMatrix.EventTypes.Spec.State.RoomInfo; namespace LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Responses; @@ -60,5 +61,56 @@ public class SynapseAdminRoomListResult { [JsonPropertyName("state_events")] public int StateEvents { get; set; } + + [JsonPropertyName("gay.rory.synapse_admin_extensions.tombstone")] + public StateEventResponse? TombstoneEvent { get; set; } + + [JsonPropertyName("gay.rory.synapse_admin_extensions.create")] + public StateEventResponse? CreateEvent { get; set; } + + [JsonPropertyName("gay.rory.synapse_admin_extensions.topic")] + public StateEventResponse? TopicEvent { get; set; } + + public async Task<StateEventResponse?> GetCreateEventAsync(AuthenticatedHomeserverSynapse hs) { + if (CreateEvent != null) return CreateEvent; + + try { + var events = (await hs.Admin.GetRoomStateAsync(RoomId, RoomCreateEventContent.EventId)); + CreateEvent = events.Events.SingleOrDefault(x => x.StateKey == ""); + } + catch (Exception e) { + Console.WriteLine($"Failed to fetch room create event for {RoomId}: {e}"); + } + + return null; + } + + public async Task<StateEventResponse?> GetTombstoneEventAsync(AuthenticatedHomeserverSynapse hs) { + if (TombstoneEvent != null) return TombstoneEvent; + + try { + var events = (await hs.Admin.GetRoomStateAsync(RoomId, RoomTombstoneEventContent.EventId)); + TombstoneEvent = events.Events.SingleOrDefault(x => x.StateKey == ""); + } + catch (Exception e) { + Console.WriteLine($"Failed to fetch room tombstone event for {RoomId}: {e}"); + } + + return null; + } + + public async Task<StateEventResponse?> GetTopicEventAsync(AuthenticatedHomeserverSynapse hs) { + if (TopicEvent != null) return TopicEvent; + + try { + var events = await hs.Admin.GetRoomStateAsync(RoomId, RoomTopicEventContent.EventId); + TopicEvent = events.Events.SingleOrDefault(x => x.StateKey == ""); + } + catch (Exception e) { + Console.WriteLine($"Failed to fetch room topic event for {RoomId}: {e}"); + } + + return null; + } } } \ No newline at end of file diff --git a/LibMatrix/Homeservers/ImplementationDetails/Synapse/SynapseAdminApiClient.cs b/LibMatrix/Homeservers/ImplementationDetails/Synapse/SynapseAdminApiClient.cs
index 4da0013..6d09006 100644 --- a/LibMatrix/Homeservers/ImplementationDetails/Synapse/SynapseAdminApiClient.cs +++ b/LibMatrix/Homeservers/ImplementationDetails/Synapse/SynapseAdminApiClient.cs
@@ -1,21 +1,15 @@ // #define LOG_SKIP +using System.CodeDom.Compiler; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; -using System.Net.Http.Json; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters; using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Requests; using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Responses; using LibMatrix.Responses; -using LibMatrix.Filters; -using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters; -using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Responses; -using LibMatrix.Responses; using LibMatrix.StructuredData; namespace LibMatrix.Homeservers.ImplementationDetails.Synapse; @@ -28,24 +22,41 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH #region Rooms public async IAsyncEnumerable<SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom> SearchRoomsAsync(int limit = int.MaxValue, int chunkLimit = 250, - string orderBy = "name", string dir = "f", string? searchTerm = null, SynapseAdminLocalRoomQueryFilter? localFilter = null) { + string orderBy = "name", string dir = "f", string? searchTerm = null, SynapseAdminLocalRoomQueryFilter? localFilter = null, + bool fetchTombstones = false, bool fetchTopics = false, bool fetchCreateEvents = false) { + if (localFilter != null) { + fetchTombstones |= localFilter.Tombstone.Enabled; + fetchTopics |= localFilter.Topic.Enabled; + fetchCreateEvents |= localFilter.RoomType.Enabled; + } + + var serverCaps = await authenticatedHomeserver.GetCapabilitiesAsync(); + var serverSupportsQueryEventsV2 = serverCaps.Capabilities.SynapseRoomListQueryEventsV2?.Enabled ?? false; + SynapseAdminRoomListResult? res = null; var i = 0; int? totalRooms = null; do { var url = $"/_synapse/admin/v1/rooms?limit={Math.Min(limit, chunkLimit)}&dir={dir}&order_by={orderBy}"; - if (!string.IsNullOrEmpty(searchTerm)) url += $"&search_term={searchTerm}"; + if (!string.IsNullOrEmpty(searchTerm)) url += $"&search_term={searchTerm}"; if (res?.NextBatch is not null) url += $"&from={res.NextBatch}"; + // nonstandard stuff + if (fetchTombstones) url += "&gay.rory.synapse_admin_extensions.include_tombstone=true&emma_include_tombstone=true"; + if (fetchTopics) url += "&gay.rory.synapse_admin_extensions.include_topic=true"; + if (fetchCreateEvents) url += "&gay.rory.synapse_admin_extensions.include_create_event=true"; + Console.WriteLine($"--- ADMIN Querying Room List with URL: {url} - Already have {i} items... ---"); res = await authenticatedHomeserver.ClientHttpClient.GetFromJsonAsync<SynapseAdminRoomListResult>(url); totalRooms ??= res.TotalRooms; // Console.WriteLine(res.ToJson(false)); + + List<SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom> keep = []; foreach (var room in res.Rooms) { if (localFilter is not null) { - if (!string.IsNullOrWhiteSpace(localFilter.RoomIdContains) && !room.RoomId.Contains(localFilter.RoomIdContains, StringComparison.OrdinalIgnoreCase)) { + if (!localFilter.RoomId.Matches(room.RoomId, StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule roomid."); @@ -53,7 +64,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.NameContains) && room.Name?.Contains(localFilter.NameContains, StringComparison.OrdinalIgnoreCase) != true) { + if (!localFilter.Name.Matches(room.Name ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule roomname."); @@ -61,8 +72,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.CanonicalAliasContains) && - room.CanonicalAlias?.Contains(localFilter.CanonicalAliasContains, StringComparison.OrdinalIgnoreCase) != true) { + if (!localFilter.CanonicalAlias.Matches(room.CanonicalAlias ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule alias."); @@ -70,7 +80,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.VersionContains) && !room.Version.Contains(localFilter.VersionContains, StringComparison.OrdinalIgnoreCase)) { + if (!localFilter.Version.Matches(room.Version ?? "")) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule version."); @@ -78,7 +88,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.CreatorContains) && !room.Creator.Contains(localFilter.CreatorContains, StringComparison.OrdinalIgnoreCase)) { + if (!localFilter.Creator.Matches(room.Creator ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule creator."); @@ -86,8 +96,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.EncryptionContains) && - room.Encryption?.Contains(localFilter.EncryptionContains, StringComparison.OrdinalIgnoreCase) != true) { + if (!localFilter.Encryption.Matches(room.Encryption ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule encryption."); @@ -95,8 +104,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.JoinRulesContains) && - room.JoinRules?.Contains(localFilter.JoinRulesContains, StringComparison.OrdinalIgnoreCase) != true) { + if (!localFilter.JoinRules.Matches(room.JoinRules ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule joinrules."); @@ -104,8 +112,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.GuestAccessContains) && - room.GuestAccess?.Contains(localFilter.GuestAccessContains, StringComparison.OrdinalIgnoreCase) != true) { + if (!localFilter.GuestAccess.Matches(room.GuestAccess ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule guestaccess."); @@ -113,8 +120,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (!string.IsNullOrWhiteSpace(localFilter.HistoryVisibilityContains) && - room.HistoryVisibility?.Contains(localFilter.HistoryVisibilityContains, StringComparison.OrdinalIgnoreCase) != true) { + if (!localFilter.HistoryVisibility.Matches(room.HistoryVisibility ?? "", StringComparison.OrdinalIgnoreCase)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule history visibility."); @@ -122,7 +128,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (localFilter.CheckFederation && room.Federatable != localFilter.Federatable) { + if (!localFilter.Federation.Matches(room.Federatable)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule federation."); @@ -130,7 +136,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (localFilter.CheckPublic && room.Public != localFilter.Public) { + if (!localFilter.Public.Matches(room.Public)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule public."); @@ -138,15 +144,15 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (room.StateEvents < localFilter.StateEventsGreaterThan || room.StateEvents > localFilter.StateEventsLessThan) { + if (!localFilter.StateEvents.Matches(room.StateEvents)) { totalRooms--; #if LOG_SKIP - Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule joined local members."); + Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule state events."); #endif continue; } - if (room.JoinedMembers < localFilter.JoinedMembersGreaterThan || room.JoinedMembers > localFilter.JoinedMembersLessThan) { + if (!localFilter.JoinedMembers.Matches(room.JoinedMembers)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule joined members: {localFilter.JoinedMembersGreaterThan} < {room.JoinedLocalMembers} < {localFilter.JoinedMembersLessThan}."); @@ -154,7 +160,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } - if (room.JoinedLocalMembers < localFilter.JoinedLocalMembersGreaterThan || room.JoinedLocalMembers > localFilter.JoinedLocalMembersLessThan) { + if (!localFilter.JoinedLocalMembers.Matches(room.JoinedLocalMembers)) { totalRooms--; #if LOG_SKIP Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule joined local members: {localFilter.JoinedLocalMembersGreaterThan} < {room.JoinedLocalMembers} < {localFilter.JoinedLocalMembersLessThan}."); @@ -162,23 +168,93 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH continue; } } - // if (contentSearch is not null && !string.IsNullOrEmpty(contentSearch) && - // !( - // room.Name?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true || - // room.CanonicalAlias?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true || - // room.Creator?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true - // ) - // ) { - // totalRooms--; - // continue; - // } i++; + keep.Add(room); + } + + var parallelisationLimit = new SemaphoreSlim(32, 32); + List<Task<(SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom room, StateEventResponse?[] tasks)>> tasks = []; + + async Task<(SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom room, StateEventResponse?[] tasks)> fillTask( + SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom room) { + if (serverSupportsQueryEventsV2) return (room, []); + + var fillTasks = await Task.WhenAll(((Task<StateEventResponse?>?[]) [ + fetchTombstones && room.TombstoneEvent is null + ? parallelisationLimit.RunWithLockAsync(() => room.GetTombstoneEventAsync(authenticatedHomeserver)) + : null!, + fetchTopics && room.TopicEvent is null + ? parallelisationLimit.RunWithLockAsync(() => room.GetTopicEventAsync(authenticatedHomeserver)) + : null!, + fetchCreateEvents && room.CreateEvent is null + ? parallelisationLimit.RunWithLockAsync(() => room.GetCreateEventAsync(authenticatedHomeserver)) + : null!, + ]) + .Where(t => t != null)! + ); + return ( + room, + fillTasks + ); + } + + tasks.AddRange( + serverSupportsQueryEventsV2 + ? keep.Select(x => Task.FromResult((x, (StateEventResponse?[])[]))) + : keep.Select(fillTask) + ); + + // await Task.WhenAll(tasks); + + foreach (var taskRes in tasks) { + var (room, _) = await taskRes; + if (localFilter is not null) { + if (!localFilter.Tombstone.Matches(room.TombstoneEvent != null)) { + totalRooms--; +#if LOG_SKIP + Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule tombstone."); +#endif + continue; + } + + if (!localFilter.RoomType.Matches(room.CreateEvent?.ContentAs<RoomCreateEventContent>()?.Type)) { + totalRooms--; +#if LOG_SKIP + Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule room type."); +#endif + continue; + } + + if (!localFilter.Topic.Matches(room.TopicEvent?.ContentAs<RoomTopicEventContent>()?.Topic, StringComparison.OrdinalIgnoreCase)) { + totalRooms--; +#if LOG_SKIP + Console.WriteLine($"Skipped room {room.ToJson(indent: false)} on rule topic."); +#endif + continue; + } + } + yield return room; } } while (i < Math.Min(limit, totalRooms ?? limit)); } + public async Task<bool> CheckRoomKnownAsync(string roomId) { + try { + var createEvt = await GetRoomStateAsync(roomId, RoomCreateEventContent.EventId); + if (createEvt.Events.FirstOrDefault(e => e.StateKey == "") is null) + return false; + var members = await GetRoomMembersAsync(roomId, localOnly: true); + return members.Members.Count > 0; + } + catch (Exception e) { + if (e is HttpRequestException { StatusCode: System.Net.HttpStatusCode.NotFound }) return false; + if (e is MatrixException { ErrorCode: MatrixException.ErrorCodes.M_NOT_FOUND }) return false; + throw; + } + } + #endregion #region Users @@ -194,7 +270,7 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH if (!string.IsNullOrWhiteSpace(from)) url = url.AddQuery("from", from); if (!string.IsNullOrWhiteSpace(orderBy)) url = url.AddQuery("order_by", orderBy); if (!string.IsNullOrWhiteSpace(dir)) url = url.AddQuery("dir", dir); - + Console.WriteLine($"--- ADMIN Querying User List with URL: {url} ---"); // TODO: implement URI methods in http client var res = await authenticatedHomeserver.ClientHttpClient.GetFromJsonAsync<SynapseAdminUserListResult>(url.ToString()); @@ -527,8 +603,13 @@ public class SynapseAdminApiClient(AuthenticatedHomeserverSynapse authenticatedH $"/_synapse/admin/v2/rooms/delete_status/{deleteId}"); } - public async Task<SynapseAdminRoomMemberListResult> GetRoomMembersAsync(string roomId) { - return await authenticatedHomeserver.ClientHttpClient.GetFromJsonAsync<SynapseAdminRoomMemberListResult>($"/_synapse/admin/v1/rooms/{roomId.UrlEncode()}/members"); + public async Task<SynapseAdminRoomMemberListResult> GetRoomMembersAsync(string roomId, bool localOnly = false) { + var res = await authenticatedHomeserver.ClientHttpClient.GetFromJsonAsync<SynapseAdminRoomMemberListResult>($"/_synapse/admin/v1/rooms/{roomId.UrlEncode()}/members"); + if (localOnly) { + res.Members = res.Members.Where(m => m.EndsWith($":{authenticatedHomeserver.ServerName}")).ToList(); + } + + return res; } public async Task<SynapseAdminRoomStateResult> GetRoomStateAsync(string roomId, string? type = null) { diff --git a/LibMatrix/LibMatrix.csproj b/LibMatrix/LibMatrix.csproj
index c6c1dce..61810a3 100644 --- a/LibMatrix/LibMatrix.csproj +++ b/LibMatrix/LibMatrix.csproj
@@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net9.0</TargetFramework> + <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <LangVersion>preview</LangVersion> @@ -12,8 +12,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.9" /> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107"/> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107"/> <ProjectReference Include="..\LibMatrix.EventTypes\LibMatrix.EventTypes.csproj"/> </ItemGroup>