From 7978f08235ceca22eacae11a88a7703513238cb3 Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Sat, 1 Jul 2023 20:51:15 +0200 Subject: Deduplicate some api calls --- MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs | 2 +- MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj | 1 - .../Extensions/HttpClientExtensions.cs | 13 +- MatrixRoomUtils.Core/MatrixException.cs | 70 +++++------ MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs | 21 +++- MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs | 17 +-- .../Services/HomeserverProviderService.cs | 6 +- .../Pages/RoomState/RoomStateEditorPage.razor | 2 +- .../Pages/RoomState/RoomStateRoomList.razor | 32 ----- .../Pages/RoomState/RoomStateViewerPage.razor | 3 +- MatrixRoomUtils.Web/Pages/Rooms/Index.razor | 5 +- MatrixRoomUtils.Web/Shared/NavMenu.razor | 15 +-- MatrixRoomUtils.Web/Shared/RoomList.razor | 40 +++++-- .../RoomListComponents/RoomListCategory.razor | 6 + .../RoomListComponents/RoomListPolicyRoom.razor | 12 ++ .../Shared/RoomListComponents/RoomListSpace.razor | 7 +- MatrixRoomUtils.Web/Shared/RoomListItem.razor | 131 ++++++++++++--------- 17 files changed, 214 insertions(+), 169 deletions(-) delete mode 100644 MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor create mode 100644 MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListPolicyRoom.razor diff --git a/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs b/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs index c267298..66f3c4d 100644 --- a/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs +++ b/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs @@ -39,7 +39,7 @@ public class CmdCommand : ICommand { if ((output.Count > 0 && (msg + output[0]).Length > 64000) || output.Count == 0) { await ctx.Room.SendMessageEventAsync("m.room.message", new() { FormattedBody = $"```ansi\n{msg}\n```", - Body = Markdig.Markdown.ToHtml(msg), + // Body = Markdig.Markdown.ToHtml(msg), Format = "org.matrix.custom.html" }); msg = ""; diff --git a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj index 7012647..5eba4fd 100644 --- a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj +++ b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj @@ -22,7 +22,6 @@ - diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs index 852e1d8..060867d 100644 --- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs @@ -1,3 +1,4 @@ +using System.Net.Http.Headers; using System.Reflection; using System.Text.Json; @@ -19,6 +20,7 @@ public static class HttpClientExtensions { public class MatrixHttpClient : HttpClient { public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Console.WriteLine($"Sending request to {request.RequestUri}"); try { HttpRequestOptionsKey WebAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); @@ -35,10 +37,10 @@ public class MatrixHttpClient : HttpClient { } var a = await base.SendAsync(request, cancellationToken); if (!a.IsSuccessStatusCode) { - Console.WriteLine($"Failed to send request: {a.StatusCode}"); var content = await a.Content.ReadAsStringAsync(cancellationToken); if (content.StartsWith('{')) { var ex = JsonSerializer.Deserialize(content); + Console.WriteLine($"Failed to send request: {ex}"); if (ex?.RetryAfterMs is not null) { await Task.Delay(ex.RetryAfterMs.Value, cancellationToken); typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(request, 0); @@ -50,4 +52,13 @@ public class MatrixHttpClient : HttpClient { } return a; } + // GetFromJsonAsync + public async Task GetFromJsonAsync(string requestUri, CancellationToken cancellationToken = default) { + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var response = await SendAsync(request, cancellationToken); + response.EnsureSuccessStatusCode(); + await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken); + return await JsonSerializer.DeserializeAsync(responseStream, cancellationToken: cancellationToken); + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs index 50fae20..4795d6d 100644 --- a/MatrixRoomUtils.Core/MatrixException.cs +++ b/MatrixRoomUtils.Core/MatrixException.cs @@ -17,41 +17,41 @@ public class MatrixException : Exception { public int? RetryAfterMs { get; set; } public override string Message => - ErrorCode switch { + $"{ErrorCode}: {ErrorCode switch { // common - "M_FORBIDDEN" => "You do not have permission to perform this action: " + Error, - "M_UNKNOWN_TOKEN" => "The access token specified was not recognised: " + Error + (SoftLogout == true ? " (soft logout)" : ""), - "M_MISSING_TOKEN" => "No access token was specified: " + Error, - "M_BAD_JSON" => "Request contained valid JSON, but it was malformed in some way: " + Error, - "M_NOT_JSON" => "Request did not contain valid JSON: " + Error, - "M_NOT_FOUND" => "The requested resource was not found: " + Error, - "M_LIMIT_EXCEEDED" => "Too many requests have been sent in a short period of time. Wait a while then try again: " + Error, - "M_UNRECOGNISED" => "The server did not recognise the request: " + Error, - "M_UNKOWN" => "The server encountered an unexpected error: " + Error, + "M_FORBIDDEN" => $"You do not have permission to perform this action: {Error}", + "M_UNKNOWN_TOKEN" => $"The access token specified was not recognised: {Error}{(SoftLogout == true ? " (soft logout)" : "")}", + "M_MISSING_TOKEN" => $"No access token was specified: {Error}", + "M_BAD_JSON" => $"Request contained valid JSON, but it was malformed in some way: {Error}", + "M_NOT_JSON" => $"Request did not contain valid JSON: {Error}", + "M_NOT_FOUND" => $"The requested resource was not found: {Error}", + "M_LIMIT_EXCEEDED" => $"Too many requests have been sent in a short period of time. Wait a while then try again: {Error}", + "M_UNRECOGNISED" => $"The server did not recognise the request: {Error}", + "M_UNKOWN" => $"The server encountered an unexpected error: {Error}", // endpoint specific - "M_UNAUTHORIZED" => "The request did not contain valid authentication information for the target of the request: " + Error, - "M_USER_DEACTIVATED" => "The user ID associated with the request has been deactivated: " + Error, - "M_USER_IN_USE" => "The user ID associated with the request is already in use: " + Error, - "M_INVALID_USERNAME" => "The requested user ID is not valid: " + Error, - "M_ROOM_IN_USE" => "The room alias requested is already taken: " + Error, - "M_INVALID_ROOM_STATE" => "The room associated with the request is not in a valid state to perform the request: " + Error, - "M_THREEPID_IN_USE" => "The threepid requested is already associated with a user ID on this server: " + Error, - "M_THREEPID_NOT_FOUND" => "The threepid requested is not associated with any user ID: " + Error, - "M_THREEPID_AUTH_FAILED" => "The provided threepid and/or token was invalid: " + Error, - "M_THREEPID_DENIED" => "The homeserver does not permit the third party identifier in question: " + Error, - "M_SERVER_NOT_TRUSTED" => "The homeserver does not trust the identity server: " + Error, - "M_UNSUPPORTED_ROOM_VERSION" => "The room version is not supported: " + Error, - "M_INCOMPATIBLE_ROOM_VERSION" => "The room version is incompatible: " + Error, - "M_BAD_STATE" => "The request was invalid because the state was invalid: " + Error, - "M_GUEST_ACCESS_FORBIDDEN" => "Guest access is forbidden: " + Error, - "M_CAPTCHA_NEEDED" => "Captcha needed: " + Error, - "M_CAPTCHA_INVALID" => "Captcha invalid: " + Error, - "M_MISSING_PARAM" => "Missing parameter: " + Error, - "M_INVALID_PARAM" => "Invalid parameter: " + Error, - "M_TOO_LARGE" => "The request or entity was too large: " + Error, - "M_EXCLUSIVE" => "The resource being requested is reserved by an application service, or the application service making the request has not created the resource: " + Error, - "M_RESOURCE_LIMIT_EXCEEDED" => "Exceeded resource limit: " + Error, - "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => "Cannot leave server notice room: " + Error, - _ => "Unknown error: " + new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true) - }; + "M_UNAUTHORIZED" => $"The request did not contain valid authentication information for the target of the request: {Error}", + "M_USER_DEACTIVATED" => $"The user ID associated with the request has been deactivated: {Error}", + "M_USER_IN_USE" => $"The user ID associated with the request is already in use: {Error}", + "M_INVALID_USERNAME" => $"The requested user ID is not valid: {Error}", + "M_ROOM_IN_USE" => $"The room alias requested is already taken: {Error}", + "M_INVALID_ROOM_STATE" => $"The room associated with the request is not in a valid state to perform the request: {Error}", + "M_THREEPID_IN_USE" => $"The threepid requested is already associated with a user ID on this server: {Error}", + "M_THREEPID_NOT_FOUND" => $"The threepid requested is not associated with any user ID: {Error}", + "M_THREEPID_AUTH_FAILED" => $"The provided threepid and/or token was invalid: {Error}", + "M_THREEPID_DENIED" => $"The homeserver does not permit the third party identifier in question: {Error}", + "M_SERVER_NOT_TRUSTED" => $"The homeserver does not trust the identity server: {Error}", + "M_UNSUPPORTED_ROOM_VERSION" => $"The room version is not supported: {Error}", + "M_INCOMPATIBLE_ROOM_VERSION" => $"The room version is incompatible: {Error}", + "M_BAD_STATE" => $"The request was invalid because the state was invalid: {Error}", + "M_GUEST_ACCESS_FORBIDDEN" => $"Guest access is forbidden: {Error}", + "M_CAPTCHA_NEEDED" => $"Captcha needed: {Error}", + "M_CAPTCHA_INVALID" => $"Captcha invalid: {Error}", + "M_MISSING_PARAM" => $"Missing parameter: {Error}", + "M_INVALID_PARAM" => $"Invalid parameter: {Error}", + "M_TOO_LARGE" => $"The request or entity was too large: {Error}", + "M_EXCLUSIVE" => $"The resource being requested is reserved by an application service, or the application service making the request has not created the resource: {Error}", + "M_RESOURCE_LIMIT_EXCEEDED" => $"Exceeded resource limit: {Error}", + "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}", + _ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}" + }}"; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs index f57c855..879ae6b 100644 --- a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs +++ b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs @@ -44,7 +44,17 @@ public class GenericRoom { var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; if (!string.IsNullOrEmpty(type)) url += $"/{type}"; if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; - return await _httpClient.GetFromJsonAsync(url); + try { + var resp = await _httpClient.GetFromJsonAsync(url); + return resp; + } + catch (MatrixException e) { + if (e is not { ErrorCode: "M_NOT_FOUND" }) { + throw; + } + Console.WriteLine(e); + return default; + } } public async Task GetMessagesAsync(string from = "", int limit = 10, string dir = "b", @@ -56,8 +66,13 @@ public class GenericRoom { } public async Task GetNameAsync() { - var res = await GetStateAsync("m.room.name"); - return res.Name ?? RoomId; + try { + var res = await GetStateAsync("m.room.name"); + return res?.Name ?? RoomId; + } + catch (MatrixException e) { + return $"{RoomId} ({e.ErrorCode})"; + } } public async Task JoinAsync(string[]? homeservers = null, string? reason = null) { diff --git a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs index 3be3130..1b93064 100644 --- a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs +++ b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs @@ -13,15 +13,16 @@ public class SpaceRoom : GenericRoom { _homeServer = homeServer; } - public async Task> GetRoomsAsync(bool includeRemoved = false) { + private static SemaphoreSlim _semaphore = new(1, 1); + public async IAsyncEnumerable GetRoomsAsync(bool includeRemoved = false) { + await _semaphore.WaitAsync(); var rooms = new List(); - var state = GetFullStateAsync().ToBlockingEnumerable().ToList(); - var childStates = state.Where(x => x.Type == "m.space.child"); - foreach (var stateEvent in childStates) { - if (stateEvent.TypedContent.ToJson() != "{}" || includeRemoved) - rooms.Add(await _homeServer.GetRoom(stateEvent.StateKey)); + var state = GetFullStateAsync(); + await foreach (var stateEvent in state) { + if (stateEvent.Type != "m.space.child") continue; + if (stateEvent.RawContent.ToJson() != "{}" || includeRemoved) + yield return await _homeServer.GetRoom(stateEvent.StateKey); } - - return rooms; + _semaphore.Release(); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs index 870e0d4..b2ea987 100644 --- a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs +++ b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs @@ -30,9 +30,9 @@ public class HomeserverProviderService { await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver); hs._httpClient.Dispose(); hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) }; - hs._httpClient.Timeout = TimeSpan.FromSeconds(5); + hs._httpClient.Timeout = TimeSpan.FromSeconds(120); hs._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - + hs.WhoAmI = (await hs._httpClient.GetFromJsonAsync("/_matrix/client/v3/account/whoami"))!; return hs; } @@ -43,7 +43,7 @@ public class HomeserverProviderService { await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver); hs._httpClient.Dispose(); hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) }; - hs._httpClient.Timeout = TimeSpan.FromSeconds(5); + hs._httpClient.Timeout = TimeSpan.FromSeconds(120); return hs; } diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor index b2d28f6..8b2ff0c 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor @@ -1,4 +1,4 @@ -@page "/RoomStateViewer/{RoomId}/Edit" +@page "/Rooms/{RoomId}/State/Edit" @using System.Net.Http.Headers @using System.Text.Json @using MatrixRoomUtils.Core.Responses diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor deleted file mode 100644 index 55c44d9..0000000 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor +++ /dev/null @@ -1,32 +0,0 @@ -@page "/RoomStateViewer" -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -

Room state viewer - Room list

-
-@if (Rooms.Count == 0) { -

You are not in any rooms!

- @*

Loading progress: @checkedRoomCount/@totalRoomCount

*@ -} -else { - @foreach (var room in Rooms) { - - - - } -
-} - - - -@code { - public List Rooms { get; set; } = new(); - - protected override async Task OnInitializedAsync() { - await base.OnInitializedAsync(); - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; - Rooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList(); - Console.WriteLine("Fetched joined rooms!"); - } - -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor index a0072ab..09b38f0 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor @@ -1,4 +1,4 @@ -@page "/RoomStateViewer/{RoomId}" +@page "/Rooms/{RoomId}/State/View" @using System.Net.Http.Headers @using System.Text.Json @using MatrixRoomUtils.Core.Responses @@ -73,7 +73,6 @@ await base.OnInitializedAsync(); var hs = await MRUStorage.GetCurrentSessionOrNavigate(); if (hs is null) return; - RoomId = RoomId.Replace('~', '.'); await LoadStatesAsync(); Console.WriteLine("Policy list editor initialized!"); } diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor index 20ddd0d..932748d 100644 --- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor +++ b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor @@ -1,18 +1,21 @@ @page "/Rooms" +@using MatrixRoomUtils.Core.StateEventTypes

Room list

@if (Rooms is not null) { - + } @code { private List Rooms { get; set; } + private ProfileResponse GlobalProfile { get; set; } protected override async Task OnInitializedAsync() { var hs = await MRUStorage.GetCurrentSessionOrNavigate(); if (hs is null) return; + GlobalProfile = await hs.GetProfile(hs.WhoAmI.UserId); Rooms = await hs.GetJoinedRooms(); await base.OnInitializedAsync(); diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor index 5f9ad8a..48d3196 100644 --- a/MatrixRoomUtils.Web/Shared/NavMenu.razor +++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor @@ -23,19 +23,10 @@
Main tools

+ - - @*