diff options
-rw-r--r-- | ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs | 4 | ||||
-rw-r--r-- | ExampleBots/ModerationBot/ModerationBot.cs | 6 | ||||
-rw-r--r-- | ExampleBots/ModerationBot/PolicyEngine.cs | 8 | ||||
-rw-r--r-- | ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs | 2 | ||||
-rw-r--r-- | LibMatrix/Extensions/HttpClientExtensions.cs | 21 | ||||
-rw-r--r-- | LibMatrix/Helpers/SyncHelper.cs | 7 | ||||
-rw-r--r-- | LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs | 38 | ||||
-rw-r--r-- | LibMatrix/Homeservers/RemoteHomeServer.cs | 4 | ||||
-rw-r--r-- | LibMatrix/Responses/SyncResponse.cs | 6 | ||||
-rw-r--r-- | LibMatrix/RoomTypes/GenericRoom.cs | 6 | ||||
-rw-r--r-- | LibMatrix/Services/HomeserverProviderService.cs | 38 | ||||
-rw-r--r-- | LibMatrix/Services/ServiceInstaller.cs | 14 | ||||
-rw-r--r-- | LibMatrix/StateEvent.cs | 5 | ||||
-rw-r--r-- | Tests/LibMatrix.Tests/Tests/RoomTests.cs | 11 |
14 files changed, 93 insertions, 77 deletions
diff --git a/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs b/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs index 3727877..7563cac 100644 --- a/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs +++ b/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs @@ -59,8 +59,8 @@ public class DbgDumpAllStateTypesCommand string raw = "Count | State type | Mapped type", html = "<table><tr><th>Count</th><th>State type</th><th>Mapped type</th></tr>"; var groupedStates = states.GroupBy(x => x.Type).ToDictionary(x => x.Key, x => x.ToList()).OrderByDescending(x => x.Value.Count); foreach (var (type, stateGroup) in groupedStates) { - raw += $"{stateGroup.Count} | {type} | {stateGroup[0].GetType.Name}"; - html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].GetType.Name}</td></tr>"; + raw += $"{stateGroup.Count} | {type} | {StateEvent.GetStateEventType(stateGroup[0].Type).Name}"; + html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{StateEvent.GetStateEventType(stateGroup[0].Type).Name}</td></tr>"; } html += "</table>"; diff --git a/ExampleBots/ModerationBot/ModerationBot.cs b/ExampleBots/ModerationBot/ModerationBot.cs index 1be7bd5..7c95229 100644 --- a/ExampleBots/ModerationBot/ModerationBot.cs +++ b/ExampleBots/ModerationBot/ModerationBot.cs @@ -70,7 +70,7 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(async () => { while (!cancellationToken.IsCancellationRequested) { - var controlRoomMembers = _controlRoom.GetMembersAsync(); + var controlRoomMembers = _controlRoom.GetMembersEnumerableAsync(); var pls = await _controlRoom.GetPowerLevelsAsync(); await foreach (var member in controlRoomMembers) { if ((member.TypedContent as RoomMemberEventContent)? @@ -110,8 +110,8 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation "Got timeline event in {}: {}", @event.RoomId, @event.ToJson(indent: true, ignoreNull: true)); if (@event != null && ( - @event.GetType.IsAssignableTo(typeof(BasePolicy)) - || @event.GetType.IsAssignableTo(typeof(PolicyRuleEventContent)) + @event.MappedType.IsAssignableTo(typeof(BasePolicy)) + || @event.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent)) )) await engine.ReloadActivePolicyListById(@event.RoomId); diff --git a/ExampleBots/ModerationBot/PolicyEngine.cs b/ExampleBots/ModerationBot/PolicyEngine.cs index 5af99ac..114b90d 100644 --- a/ExampleBots/ModerationBot/PolicyEngine.cs +++ b/ExampleBots/ModerationBot/PolicyEngine.cs @@ -85,8 +85,8 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB var stateEvents = room.GetFullStateAsync(); await foreach (var stateEvent in stateEvents) { if (stateEvent != null && ( - stateEvent.GetType.IsAssignableTo(typeof(BasePolicy)) - || stateEvent.GetType.IsAssignableTo(typeof(PolicyRuleEventContent)) + stateEvent.MappedType.IsAssignableTo(typeof(BasePolicy)) + || stateEvent.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent)) )) { policyList.Policies.Add(stateEvent); } @@ -253,8 +253,8 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB string raw = "Count | State type | Mapped type", html = "<table><tr><th>Count</th><th>State type</th><th>Mapped type</th></tr>"; var groupedStates = states.GroupBy(x => x.Type).ToDictionary(x => x.Key, x => x.ToList()).OrderByDescending(x => x.Value.Count); foreach (var (type, stateGroup) in groupedStates) { - raw += $"{stateGroup.Count} | {type} | {stateGroup[0].GetType.Name}"; - html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].GetType.Name}</td></tr>"; + raw += $"{stateGroup.Count} | {type} | {stateGroup[0].MappedType.Name}"; + html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].MappedType.Name}</td></tr>"; } html += "</table>"; diff --git a/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs b/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs index 6d2cb3a..0bb3265 100644 --- a/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs +++ b/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs @@ -35,7 +35,7 @@ public class CreateSystemCommand(IServiceProvider services, HomeserverProviderSe Members = new(), }; - var state = ctx.Room.GetMembersAsync(); + var state = ctx.Room.GetMembersEnumerableAsync(); await foreach (var member in state) { sysData.Members.Add(member.StateKey); } diff --git a/LibMatrix/Extensions/HttpClientExtensions.cs b/LibMatrix/Extensions/HttpClientExtensions.cs index 17cc31d..71bb0e5 100644 --- a/LibMatrix/Extensions/HttpClientExtensions.cs +++ b/LibMatrix/Extensions/HttpClientExtensions.cs @@ -37,7 +37,7 @@ public class MatrixHttpClient : HttpClient { return options; } - public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null"); if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri); // if (AssertedUserId is not null) request.RequestUri = request.RequestUri.AddQuery("user_id", AssertedUserId); @@ -58,15 +58,18 @@ public class MatrixHttpClient : HttpClient { } HttpResponseMessage responseMessage; - try { + // try { responseMessage = await base.SendAsync(request, cancellationToken); - } - catch (Exception e) { - typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance) - ?.SetValue(request, 0); - await Task.Delay(2500, cancellationToken); - return await SendAsync(request, cancellationToken); - } + // } + // catch (Exception e) { + // if (requestSettings is { Retries: 0 }) throw; + // typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance) + // ?.SetValue(request, 0); + // await Task.Delay(requestSettings?.RetryDelay ?? 2500, cancellationToken); + // if(requestSettings is not null) requestSettings.Retries--; + // return await SendAsync(request, cancellationToken); + // throw; + // } if (responseMessage.IsSuccessStatusCode) return responseMessage; diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs index ba42735..0a11849 100644 --- a/LibMatrix/Helpers/SyncHelper.cs +++ b/LibMatrix/Helpers/SyncHelper.cs @@ -1,5 +1,7 @@ using System.Diagnostics; using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using ArcaneLibs.Extensions; using LibMatrix.Filters; using LibMatrix.Homeservers; @@ -28,8 +30,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg Console.WriteLine("Homeserver for SyncHelper is not properly configured!"); throw new ArgumentNullException(nameof(homeserver.ClientHttpClient), "Null passed as homeserver for SyncHelper!"); } - - + var sw = Stopwatch.StartNew(); var url = $"/_matrix/client/v3/sync?timeout={Timeout}&set_presence={SetPresence}&full_state={(FullState ? "true" : "false")}"; @@ -42,7 +43,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg if (httpResp is null) throw new NullReferenceException("Failed to send HTTP request"); logger?.LogInformation("Got sync response: {} bytes, {} elapsed", httpResp.Content.Headers.ContentLength ?? -1, sw.Elapsed); var deserializeSw = Stopwatch.StartNew(); - var resp = await httpResp.Content.ReadFromJsonAsync<SyncResponse>(cancellationToken: cancellationToken ?? CancellationToken.None); + var resp = await httpResp.Content.ReadFromJsonAsync<SyncResponse>(cancellationToken: cancellationToken ?? CancellationToken.None, jsonTypeInfo: SyncResponseSerializerContext.Default.SyncResponse); logger?.LogInformation("Deserialized sync response: {} bytes, {} elapsed, {} total", httpResp.Content.Headers.ContentLength ?? -1, deserializeSw.Elapsed, sw.Elapsed); var timeToWait = MinimumDelay.Subtract(sw.Elapsed); if (timeToWait.TotalMilliseconds > 0) diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs index c1d6461..6fcd8e8 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs @@ -16,37 +16,41 @@ using LibMatrix.Services; namespace LibMatrix.Homeservers; public class AuthenticatedHomeserverGeneric(string serverName, string accessToken) : RemoteHomeserver(serverName) { - public static async Task<T> Create<T>(string serverName, string accessToken, string? proxy = null) where T : AuthenticatedHomeserverGeneric { - var instance = Activator.CreateInstance(typeof(T), serverName, accessToken) as T - ?? throw new InvalidOperationException($"Failed to create instance of {typeof(T).Name}"); - HomeserverResolverService.WellKnownUris? urls = null; - if (proxy is null) - urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(serverName); - + public static async Task<T> Create<T>(string serverName, string accessToken, string? proxy = null) where T : AuthenticatedHomeserverGeneric => + await Create(typeof(T), serverName, accessToken, proxy) as T ?? throw new InvalidOperationException($"Failed to create instance of {typeof(T).Name}"); + public static async Task<AuthenticatedHomeserverGeneric> Create(Type type, string serverName, string accessToken, string? proxy = null) { + if(!type.IsAssignableTo(typeof(AuthenticatedHomeserverGeneric))) throw new ArgumentException("Type must be a subclass of AuthenticatedHomeserverGeneric", nameof(type)); + var instance = Activator.CreateInstance(type, serverName, accessToken) as AuthenticatedHomeserverGeneric + ?? throw new InvalidOperationException($"Failed to create instance of {type.Name}"); + instance.ClientHttpClient = new() { - BaseAddress = new Uri(proxy ?? urls?.Client - ?? throw new InvalidOperationException("Failed to resolve homeserver")), Timeout = TimeSpan.FromMinutes(15), DefaultRequestHeaders = { Authorization = new AuthenticationHeaderValue("Bearer", accessToken) } }; instance.ServerHttpClient = new() { - BaseAddress = new Uri(proxy ?? urls?.Server - ?? throw new InvalidOperationException("Failed to resolve homeserver")), Timeout = TimeSpan.FromMinutes(15), DefaultRequestHeaders = { Authorization = new AuthenticationHeaderValue("Bearer", accessToken) } }; - instance.WhoAmI = await instance.ClientHttpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"); - - if (proxy is not null) { + if (string.IsNullOrWhiteSpace(proxy)) { + HomeserverResolverService.WellKnownUris? urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(serverName); + instance.ClientHttpClient.BaseAddress = new Uri(urls?.Client ?? throw new InvalidOperationException("Failed to resolve homeserver")); + instance.ServerHttpClient.BaseAddress = new Uri(urls?.Server ?? throw new InvalidOperationException("Failed to resolve homeserver")); + } + else { + instance.ClientHttpClient.BaseAddress = new Uri(proxy); + instance.ServerHttpClient.BaseAddress = new Uri(proxy); instance.ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName); instance.ServerHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName); } + instance.WhoAmI = await instance.ClientHttpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"); + + return instance; } @@ -55,12 +59,6 @@ public class AuthenticatedHomeserverGeneric(string serverName, string accessToke public string UserLocalpart => UserId.Split(":")[0][1..]; public string ServerName => UserId.Split(":", 2)[1]; - // public virtual async Task<WhoAmIResponse> WhoAmI() { - // if (_whoAmI is not null) return _whoAmI; - // _whoAmI = await _httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"); - // return _whoAmI; - // } - public string AccessToken { get; set; } = accessToken; public GenericRoom GetRoom(string roomId) { diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs index a47b731..a461d6e 100644 --- a/LibMatrix/Homeservers/RemoteHomeServer.cs +++ b/LibMatrix/Homeservers/RemoteHomeServer.cs @@ -37,9 +37,9 @@ public class RemoteHomeserver(string baseUrl) { public MatrixHttpClient ServerHttpClient { get; set; } = null!; public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } = null!; - public async Task<UserProfileResponse> GetProfileAsync(string mxid) { + public async Task<UserProfileResponse> GetProfileAsync(string mxid, bool useCache = false) { if (mxid is null) throw new ArgumentNullException(nameof(mxid)); - if (_profileCache.TryGetValue(mxid, out var value)) { + if (useCache && _profileCache.TryGetValue(mxid, out var value)) { if (value is SemaphoreSlim s) await s.WaitAsync(); if (value is UserProfileResponse p) return p; } diff --git a/LibMatrix/Responses/SyncResponse.cs b/LibMatrix/Responses/SyncResponse.cs index 42759ff..49259d0 100644 --- a/LibMatrix/Responses/SyncResponse.cs +++ b/LibMatrix/Responses/SyncResponse.cs @@ -2,6 +2,10 @@ using System.Text.Json.Serialization; namespace LibMatrix.Responses; +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(SyncResponse))] +internal partial class SyncResponseSerializerContext : JsonSerializerContext { } + public class SyncResponse { [JsonPropertyName("next_batch")] public string NextBatch { get; set; } = null!; @@ -114,4 +118,4 @@ public class SyncResponse { public EventList? InviteState { get; set; } } } -} +} \ No newline at end of file diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs index 9e2cb67..bfb7f43 100644 --- a/LibMatrix/RoomTypes/GenericRoom.cs +++ b/LibMatrix/RoomTypes/GenericRoom.cs @@ -184,7 +184,7 @@ public class GenericRoom { else sw.Restart(); foreach (var resp in result.Chunk) { if (resp?.Type != "m.room.member") continue; - if (joinedOnly && (resp.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue; + if (joinedOnly && resp.RawContent?["membership"]?.GetValue<string>() != "join") continue; yield return resp; } @@ -209,7 +209,7 @@ public class GenericRoom { var members = new List<StateEventResponse>(); foreach (var resp in result.Chunk) { if (resp?.Type != "m.room.member") continue; - if (joinedOnly && (resp.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue; + if (joinedOnly && resp.RawContent?["membership"]?.GetValue<string>() != "join") continue; members.Add(resp); } @@ -267,7 +267,7 @@ public class GenericRoom { var memberList = new List<string>(); int memberCount = 0; await foreach (var member in members) - memberList.Add((member.TypedContent is RoomMemberEventContent memberEvent ? memberEvent.DisplayName : "") ?? ""); + memberList.Add(member.RawContent?["displayname"]?.GetValue<string>() ?? ""); memberCount = memberList.Count; memberList.RemoveAll(string.IsNullOrWhiteSpace); memberList = memberList.OrderBy(x => x).ToList(); diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs index dc4acb1..577a706 100644 --- a/LibMatrix/Services/HomeserverProviderService.cs +++ b/LibMatrix/Services/HomeserverProviderService.cs @@ -25,22 +25,34 @@ public class HomeserverProviderService(ILogger<HomeserverProviderService> logger } } - // var domain = proxy ?? (await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver)).client; - var rhs = await RemoteHomeserver.Create(homeserver, proxy); - var clientVersions = await rhs.GetClientVersionsAsync(); + ClientVersionsResponse clientVersions = new(); + try { + clientVersions = await rhs.GetClientVersionsAsync(); + } + catch (Exception e) { + logger.LogError(e, "Failed to get client versions for {homeserver}", homeserver); + } + if (proxy is not null) - logger.LogInformation($"Homeserver {homeserver} proxied via {proxy}..."); - logger.LogInformation($"{homeserver}: " + clientVersions.ToJson()); + logger.LogInformation("Homeserver {homeserver} proxied via {proxy}...", homeserver, proxy); + logger.LogInformation("{homeserver}: {clientVersions}", homeserver, clientVersions.ToJson()); - if (clientVersions.UnstableFeatures.TryGetValue("gay.rory.mxapiextensions.v0", out bool a) && a) - hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverMxApiExtended>(homeserver, accessToken, proxy); - else { - var serverVersion = await rhs.GetServerVersionAsync(); - if (serverVersion is { Server.Name: "Synapse" }) - hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverSynapse>(homeserver, accessToken, proxy); - else - hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverGeneric>(homeserver, accessToken, proxy); + try { + if (clientVersions.UnstableFeatures.TryGetValue("gay.rory.mxapiextensions.v0", out bool a) && a) + hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverMxApiExtended>(homeserver, accessToken, proxy); + else { + var serverVersion = await rhs.GetServerVersionAsync(); + if (serverVersion is { Server.Name: "Synapse" }) + hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverSynapse>(homeserver, accessToken, proxy); + else + hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverGeneric>(homeserver, accessToken, proxy); + } + } + catch (Exception e) { + logger.LogError(e, "Failed to create authenticated homeserver for {homeserver}", homeserver); + sem.Release(); + throw; } if(impersonatedMxid is not null) diff --git a/LibMatrix/Services/ServiceInstaller.cs b/LibMatrix/Services/ServiceInstaller.cs index b1c98e1..ad5bedc 100644 --- a/LibMatrix/Services/ServiceInstaller.cs +++ b/LibMatrix/Services/ServiceInstaller.cs @@ -6,20 +6,20 @@ public static class ServiceInstaller { public static IServiceCollection AddRoryLibMatrixServices(this IServiceCollection services, RoryLibMatrixConfiguration? config = null) { //Check required services - if (!services.Any(x => x.ServiceType == typeof(TieredStorageService))) - throw new Exception("[MRUCore/DI] No TieredStorageService has been registered!"); + // if (!services.Any(x => x.ServiceType == typeof(TieredStorageService))) + // throw new Exception("[MRUCore/DI] No TieredStorageService has been registered!"); //Add config services.AddSingleton(config ?? new RoryLibMatrixConfiguration()); //Add services services.AddSingleton<HomeserverResolverService>(); - if (services.First(x => x.ServiceType == typeof(TieredStorageService)).Lifetime == ServiceLifetime.Singleton) { + // if (services.First(x => x.ServiceType == typeof(TieredStorageService)).Lifetime == ServiceLifetime.Singleton) { services.AddSingleton<HomeserverProviderService>(); - } - else { - services.AddScoped<HomeserverProviderService>(); - } + // } + // else { + // services.AddScoped<HomeserverProviderService>(); + // } // services.AddScoped<MatrixHttpClient>(); return services; diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs index ad7605a..4a0adbd 100644 --- a/LibMatrix/StateEvent.cs +++ b/LibMatrix/StateEvent.cs @@ -26,8 +26,11 @@ public class StateEvent { }).ToFrozenDictionary(); public static Type GetStateEventType(string type) => KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownEventContent); + + [JsonIgnore] + public Type MappedType => GetStateEventType(Type); - private static readonly JsonSerializerOptions TypedContentSerializerOptions = new JsonSerializerOptions() { + private static readonly JsonSerializerOptions TypedContentSerializerOptions = new() { Converters = { new JsonFloatStringConverter(), new JsonDoubleStringConverter(), diff --git a/Tests/LibMatrix.Tests/Tests/RoomTests.cs b/Tests/LibMatrix.Tests/Tests/RoomTests.cs index 913e044..bec8d18 100644 --- a/Tests/LibMatrix.Tests/Tests/RoomTests.cs +++ b/Tests/LibMatrix.Tests/Tests/RoomTests.cs @@ -52,7 +52,7 @@ public class RoomTests : TestBed<TestFixture> { var hs = await HomeserverAbstraction.GetHomeserver(); var room = await RoomAbstraction.GetTestRoom(hs); Assert.NotNull(room); - var members = room.GetMembersAsync(); + var members = room.GetMembersEnumerableAsync(); Assert.NotNull(members); bool hitMembers = false; await foreach (var member in members) { @@ -248,12 +248,7 @@ public class RoomTests : TestBed<TestFixture> { } await Task.WhenAll(tasks); - var states = room.GetMembersAsync(false); - var count = 0; - await foreach (var state in states) { - count++; - } - // Assert.Equal(++expectedCount, count); - Assert.Equal(16, count); + var states = await room.GetMembersListAsync(false); + Assert.Equal(16, states.Count); } } |