From 712ad189c99570f686ab779782b2a873e172428e Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Tue, 13 Jun 2023 20:25:05 +0200 Subject: Change syntax style --- MatrixRoomUtils.Core/AuthenticatedHomeServer.cs | 83 +- MatrixRoomUtils.Core/Authentication/MatrixAuth.cs | 32 +- .../Extensions/DictionaryExtensions.cs | 10 +- .../Extensions/HttpClientExtensions.cs | 37 +- .../Extensions/JsonElementExtensions.cs | 12 +- .../Extensions/ObjectExtensions.cs | 13 +- .../Extensions/StringExtensions.cs | 4 +- MatrixRoomUtils.Core/Interfaces/IHomeServer.cs | 86 +- MatrixRoomUtils.Core/MatrixException.cs | 57 ++ MatrixRoomUtils.Core/RatelimitedHttpClient.cs | 7 - MatrixRoomUtils.Core/RemoteHomeServer.cs | 35 +- .../Responses/Admin/AdminRoomListingResult.cs | 6 +- .../Responses/CreateRoomRequest.cs | 234 ++--- MatrixRoomUtils.Core/Responses/LoginResponse.cs | 19 +- MatrixRoomUtils.Core/Responses/ProfileResponse.cs | 4 +- .../Responses/StateEventResponse.cs | 24 +- MatrixRoomUtils.Core/Room.cs | 112 +-- MatrixRoomUtils.Core/RuntimeCache.cs | 119 +-- MatrixRoomUtils.Core/StateEvent.cs | 46 +- MatrixRoomUtils.Core/StateEventStruct.cs | 3 +- .../StateEventTypes/PolicyRuleStateEventData.cs | 31 +- .../MatrixRoomUtils.Web.Server.csproj | 28 +- MatrixRoomUtils.Web.Server/Pages/Error.cshtml | 49 +- MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs | 21 +- MatrixRoomUtils.Web.Server/Program.cs | 8 +- .../Properties/launchSettings.json | 68 +- MatrixRoomUtils.Web/App.razor | 7 +- MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs | 48 +- .../DefaultRoomCreationTemplate.cs | 175 ++-- .../RoomCreationTemplates/IRoomCreationTemplate.cs | 3 +- MatrixRoomUtils.Web/FileUploadTest.razor | 3 +- MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj | 12 +- MatrixRoomUtils.Web/Pages/About.razor | 17 +- MatrixRoomUtils.Web/Pages/DataExportPage.razor | 31 +- MatrixRoomUtils.Web/Pages/DebugTools.razor | 48 +- MatrixRoomUtils.Web/Pages/DevOptions.razor | 44 +- MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor | 6 +- MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor | 71 +- MatrixRoomUtils.Web/Pages/Index.razor | 9 +- .../Pages/KnownHomeserverList.razor | 84 +- MatrixRoomUtils.Web/Pages/LoginPage.razor | 30 +- MatrixRoomUtils.Web/Pages/MediaLocator.razor | 69 +- .../Pages/PolicyList/PolicyListEditorPage.razor | 113 +-- .../Pages/PolicyList/PolicyListRoomList.razor | 68 +- .../Pages/RoomManager/RoomManager.razor | 50 +- .../Pages/RoomManager/RoomManagerCreateRoom.razor | 96 +- .../Pages/RoomManager/RoomManagerSpace.razor | 69 +- .../Pages/RoomManager/RoomManagerTimeline.razor | 16 +- .../Pages/RoomState/RoomStateEditorPage.razor | 62 +- .../Pages/RoomState/RoomStateRoomList.razor | 25 +- .../Pages/RoomState/RoomStateViewerPage.razor | 59 +- MatrixRoomUtils.Web/Program.cs | 5 +- MatrixRoomUtils.Web/Shared/EditablePre.razor | 10 +- .../Shared/IndexComponents/IndexUserItem.razor | 23 +- MatrixRoomUtils.Web/Shared/InlineUserItem.razor | 22 +- MatrixRoomUtils.Web/Shared/LogView.razor | 26 +- MatrixRoomUtils.Web/Shared/MainLayout.razor | 17 +- MatrixRoomUtils.Web/Shared/MainLayout.razor.css | 24 +- MatrixRoomUtils.Web/Shared/NavMenu.razor | 12 +- MatrixRoomUtils.Web/Shared/NavMenu.razor.css | 36 +- MatrixRoomUtils.Web/Shared/PortableDevTools.razor | 23 +- MatrixRoomUtils.Web/Shared/RoomListItem.razor | 91 +- MatrixRoomUtils.Web/Shared/RoomListItem.razor.css | 6 +- .../Shared/SimpleComponents/DictionaryEditor.razor | 21 +- .../Shared/SimpleComponents/FancyTextBox.razor | 20 +- .../Shared/SimpleComponents/StringListEditor.razor | 14 +- .../Shared/SimpleComponents/ToggleSlider.razor | 6 +- .../TimelineComponents/TimelineMemberItem.razor | 24 +- MatrixRoomUtils.Web/Shared/UserListItem.razor | 20 +- MatrixRoomUtils.Web/_Imports.razor | 10 +- MatrixRoomUtils.Web/wwwroot/css/app.css | 52 +- .../wwwroot/css/open-iconic/README.md | 11 +- .../css/open-iconic/font/fonts/open-iconic.svg | 1059 ++++++++++---------- MatrixRoomUtils.Web/wwwroot/index.html | 10 +- MatrixRoomUtils.sln.DotSettings | 2 + 75 files changed, 1753 insertions(+), 2154 deletions(-) create mode 100644 MatrixRoomUtils.Core/MatrixException.cs delete mode 100644 MatrixRoomUtils.Core/RatelimitedHttpClient.cs create mode 100644 MatrixRoomUtils.sln.DotSettings diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs index 368aa20..ee6be72 100644 --- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs +++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs @@ -9,63 +9,47 @@ using MatrixRoomUtils.Core.Responses.Admin; namespace MatrixRoomUtils.Core; -public class AuthenticatedHomeServer : IHomeServer -{ - public string UserId { get; set; } - public string AccessToken { get; set; } +public class AuthenticatedHomeServer : IHomeServer { public readonly HomeserverAdminApi Admin; - public AuthenticatedHomeServer(string userId, string accessToken, string canonicalHomeServerDomain) - { + public AuthenticatedHomeServer(string userId, string accessToken, string canonicalHomeServerDomain) { UserId = userId; AccessToken = accessToken; HomeServerDomain = canonicalHomeServerDomain; Admin = new HomeserverAdminApi(this); - _httpClient = new HttpClient(); + _httpClient = new MatrixHttpClient(); } - public async Task Configure() - { + public string UserId { get; set; } + public string AccessToken { get; set; } + + public async Task Configure() { FullHomeServerDomain = await ResolveHomeserverFromWellKnown(HomeServerDomain); _httpClient.Dispose(); - _httpClient = new HttpClient { BaseAddress = new Uri(FullHomeServerDomain) }; + _httpClient = new MatrixHttpClient { BaseAddress = new Uri(FullHomeServerDomain) }; _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken); Console.WriteLine("[AHS] Finished setting up http client"); return this; } - public async Task GetRoom(string roomId) - { - return new Room(_httpClient, roomId); - } + public async Task GetRoom(string roomId) => new Room(_httpClient, roomId); - public async Task> GetJoinedRooms() - { + public async Task> GetJoinedRooms() { var rooms = new List(); var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms"); - if (!roomQuery.IsSuccessStatusCode) - { - Console.WriteLine($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}"); - throw new InvalidDataException($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}"); - } var roomsJson = await roomQuery.Content.ReadFromJsonAsync(); - foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) - { - rooms.Add(new Room(_httpClient, room.GetString())); - } + foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new Room(_httpClient, room.GetString())); Console.WriteLine($"Fetched {rooms.Count} rooms"); return rooms; } - public async Task UploadFile(string fileName, Stream fileStream, string contentType = "application/octet-stream") - { + public async Task UploadFile(string fileName, Stream fileStream, string contentType = "application/octet-stream") { var res = await _httpClient.PostAsync($"/_matrix/media/r0/upload?filename={fileName}", new StreamContent(fileStream)); - if (!res.IsSuccessStatusCode) - { + if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to upload file: {await res.Content.ReadAsStringAsync()}"); throw new InvalidDataException($"Failed to upload file: {await res.Content.ReadAsStringAsync()}"); } @@ -74,11 +58,9 @@ public class AuthenticatedHomeServer : IHomeServer return resJson.GetProperty("content_uri").GetString()!; } - public async Task CreateRoom(CreateRoomRequest creationEvent) - { + public async Task CreateRoom(CreateRoomRequest creationEvent) { var res = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/createRoom", creationEvent); - if (!res.IsSuccessStatusCode) - { + if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to create room: {await res.Content.ReadAsStringAsync()}"); throw new InvalidDataException($"Failed to create room: {await res.Content.ReadAsStringAsync()}"); } @@ -86,47 +68,34 @@ public class AuthenticatedHomeServer : IHomeServer return await GetRoom((await res.Content.ReadFromJsonAsync())!["room_id"]!.ToString()); } - public class HomeserverAdminApi - { + public class HomeserverAdminApi { private readonly AuthenticatedHomeServer _authenticatedHomeServer; - public HomeserverAdminApi(AuthenticatedHomeServer authenticatedHomeServer) - { - _authenticatedHomeServer = authenticatedHomeServer; - } + public HomeserverAdminApi(AuthenticatedHomeServer authenticatedHomeServer) => _authenticatedHomeServer = authenticatedHomeServer; - public async IAsyncEnumerable SearchRoomsAsync(int limit = int.MaxValue, string orderBy = "name", string dir = "f", string? searchTerm = null, string? contentSearch = null) - { + public async IAsyncEnumerable SearchRoomsAsync(int limit = int.MaxValue, string orderBy = "name", string dir = "f", string? searchTerm = null, string? contentSearch = null) { AdminRoomListingResult? res = null; - int i = 0; + var i = 0; int? totalRooms = null; - do - { + do { var url = $"/_synapse/admin/v1/rooms?limit={Math.Min(limit, 100)}&dir={dir}&order_by={orderBy}"; - if (!string.IsNullOrEmpty(searchTerm)) - { - url += $"&search_term={searchTerm}"; - } + if (!string.IsNullOrEmpty(searchTerm)) url += $"&search_term={searchTerm}"; + + if (res?.NextBatch != null) url += $"&from={res.NextBatch}"; - if (res?.NextBatch != null) - { - url += $"&from={res.NextBatch}"; - } Console.WriteLine($"--- ADMIN Querying Room List with URL: {url} - Already have {i} items... ---"); res = await _authenticatedHomeServer._httpClient.GetFromJsonAsync(url); totalRooms ??= res?.TotalRooms; - Console.WriteLine(res.ToJson(indent:false)); - foreach (var room in res.Rooms) - { + Console.WriteLine(res.ToJson(false)); + foreach (var room in res.Rooms) { if (contentSearch != 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; } diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs index b4b8d19..0f9eb58 100644 --- a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs +++ b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs @@ -5,35 +5,31 @@ using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.Authentication; -public class MatrixAuth -{ - public static async Task Login(string homeserver, string username, string password) - { +public class MatrixAuth { + public static async Task Login(string homeserver, string username, string password) { Console.WriteLine($"Logging in to {homeserver} as {username}..."); homeserver = (await new RemoteHomeServer(homeserver).Configure()).FullHomeServerDomain; var hc = new HttpClient(); - var payload = new - { + var payload = new { type = "m.login.password", - identifier = new - { + identifier = new { type = "m.id.user", user = username }, - password = password, + password, initial_device_display_name = "Rory&::MatrixRoomUtils" }; Console.WriteLine($"Sending login request to {homeserver}..."); var resp = await hc.PostAsJsonAsync($"{homeserver}/_matrix/client/r0/login", payload); Console.WriteLine($"Login: {resp.StatusCode}"); var data = await resp.Content.ReadFromJsonAsync(); - if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data.ToString()); - if (data.TryGetProperty("retry_after_ms", out var retryAfter)) - { + if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data); + if (data.TryGetProperty("retry_after_ms", out var retryAfter)) { Console.WriteLine($"Login: Waiting {retryAfter.GetInt32()}ms before retrying"); await Task.Delay(retryAfter.GetInt32()); return await Login(homeserver, username, password); } + Console.WriteLine($"Login: {data.ToJson()}"); return data.Deserialize(); //var token = data.GetProperty("access_token").GetString(); @@ -41,20 +37,16 @@ public class MatrixAuth } [Obsolete("Migrate to IHomeServer instance")] - public static async Task GetProfile(string homeserver, string mxid) => - await (await new RemoteHomeServer(homeserver).Configure()).GetProfile(mxid); + public static async Task GetProfile(string homeserver, string mxid) => await (await new RemoteHomeServer(homeserver).Configure()).GetProfile(mxid); - private static async Task CheckSuccessStatus(string url) - { + private static async Task CheckSuccessStatus(string url) { //cors causes failure, try to catch - try - { + try { using var hc = new HttpClient(); var resp = await hc.GetAsync(url); return resp.IsSuccessStatusCode; } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine($"Failed to check success status: {e.Message}"); return false; } diff --git a/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs b/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs index cce71dd..c51baec 100644 --- a/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs @@ -1,15 +1,13 @@ namespace MatrixRoomUtils.Core.Extensions; -public static class DictionaryExtensions -{ - public static bool ChangeKey(this IDictionary dict, - TKey oldKey, TKey newKey) - { +public static class DictionaryExtensions { + public static bool ChangeKey(this IDictionary dict, + TKey oldKey, TKey newKey) { TValue value; if (!dict.Remove(oldKey, out value)) return false; - dict[newKey] = value; // or dict.Add(newKey, value) depending on ur comfort + dict[newKey] = value; // or dict.Add(newKey, value) depending on ur comfort return true; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs index 8eb0226..47b3121 100644 --- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs @@ -1,19 +1,40 @@ +using System.Text.Json; + namespace MatrixRoomUtils.Core.Extensions; -public static class HttpClientExtensions -{ - public static async Task CheckSuccessStatus(this HttpClient hc, string url) - { +public static class HttpClientExtensions { + public static async Task CheckSuccessStatus(this HttpClient hc, string url) { //cors causes failure, try to catch - try - { + try { var resp = await hc.GetAsync(url); return resp.IsSuccessStatusCode; } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine($"Failed to check success status: {e.Message}"); return false; } } +} + +public class MatrixHttpClient : HttpClient { + public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + 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); + if (ex?.RetryAfterMs != null) { + await Task.Delay(ex.RetryAfterMs.Value, cancellationToken); + return await SendAsync(request, cancellationToken); + } + + throw ex!; + } + + throw new InvalidDataException("Encountered invalid data:\n" + content); + } + + return a; + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs index 725c832..b007136 100644 --- a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs @@ -5,18 +5,16 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.Extensions; -public static class JsonElementExtensions -{ - public static void FindExtraJsonFields([DisallowNull] this JsonElement? res, Type t) - { +public static class JsonElementExtensions { + public static void FindExtraJsonFields([DisallowNull] this JsonElement? res, Type t) { var props = t.GetProperties(); var unknownPropertyFound = false; - foreach (var field in res.Value.EnumerateObject()) - { + foreach (var field in res.Value.EnumerateObject()) { if (props.Any(x => x.GetCustomAttribute()?.Name == field.Name)) continue; Console.WriteLine($"[!!] Unknown property {field.Name} in {t.Name}!"); unknownPropertyFound = true; } - if(unknownPropertyFound) Console.WriteLine(res.Value.ToJson()); + + if (unknownPropertyFound) Console.WriteLine(res.Value.ToJson()); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs b/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs index 5aa9645..812c81c 100644 --- a/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs @@ -1,15 +1,14 @@ +using System.Text.Encodings.Web; using System.Text.Json; namespace MatrixRoomUtils.Core.Extensions; -public static class ObjectExtensions -{ - public static string ToJson(this object obj, bool indent = true, bool ignoreNull = false, bool unsafeContent = false) - { +public static class ObjectExtensions { + public static string ToJson(this object obj, bool indent = true, bool ignoreNull = false, bool unsafeContent = false) { var jso = new JsonSerializerOptions(); - if(indent) jso.WriteIndented = true; - if(ignoreNull) jso.IgnoreNullValues = true; - if(unsafeContent) jso.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping; + if (indent) jso.WriteIndented = true; + if (ignoreNull) jso.IgnoreNullValues = true; + if (unsafeContent) jso.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; return JsonSerializer.Serialize(obj, jso); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/StringExtensions.cs b/MatrixRoomUtils.Core/Extensions/StringExtensions.cs index 8fadc6d..b81d59f 100644 --- a/MatrixRoomUtils.Core/Extensions/StringExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/StringExtensions.cs @@ -1,7 +1,6 @@ namespace MatrixRoomUtils.Core.Extensions; -public static class StringExtensions -{ +public static class StringExtensions { // public static async Task GetMediaUrl(this string MxcUrl) // { // //MxcUrl: mxc://rory.gay/ocRVanZoUTCcifcVNwXgbtTg @@ -11,5 +10,4 @@ public static class StringExtensions // var mediaId = MxcUrl.Split('/')[3]; // return $"{(await new RemoteHomeServer(server).Configure()).FullHomeServerDomain}/_matrix/media/v3/download/{server}/{mediaId}"; // } - } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs index 3ae1355..9a9ba7a 100644 --- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs +++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs @@ -5,35 +5,32 @@ using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.Interfaces; -public class IHomeServer -{ - private Dictionary _profileCache = new(); +public class IHomeServer { + private readonly Dictionary _profileCache = new(); public string HomeServerDomain { get; set; } public string FullHomeServerDomain { get; set; } - private protected HttpClient _httpClient { get; set; } = new(); + private protected MatrixHttpClient _httpClient { get; set; } = new(); - public async Task ResolveHomeserverFromWellKnown(string homeserver) - { + public async Task ResolveHomeserverFromWellKnown(string homeserver) { var res = await _resolveHomeserverFromWellKnown(homeserver); - if(!res.StartsWith("http")) res = "https://" + res; - if(res.EndsWith(":443")) res = res.Substring(0, res.Length - 4); + if (!res.StartsWith("http")) res = "https://" + res; + if (res.EndsWith(":443")) res = res.Substring(0, res.Length - 4); return res; } - private async Task _resolveHomeserverFromWellKnown(string homeserver) - { - if (RuntimeCache.HomeserverResolutionCache.Count == 0) - { + + private async Task _resolveHomeserverFromWellKnown(string homeserver) { + if (RuntimeCache.HomeserverResolutionCache.Count == 0) { Console.WriteLine("No cached homeservers, resolving..."); await Task.Delay(Random.Shared.Next(1000, 5000)); - } - if (RuntimeCache.HomeserverResolutionCache.ContainsKey(homeserver)) - { - if (RuntimeCache.HomeserverResolutionCache[homeserver].ResolutionTime < DateTime.Now.AddHours(1)) - { + } + + if (RuntimeCache.HomeserverResolutionCache.ContainsKey(homeserver)) { + if (RuntimeCache.HomeserverResolutionCache[homeserver].ResolutionTime < DateTime.Now.AddHours(1)) { Console.WriteLine($"Found cached homeserver: {RuntimeCache.HomeserverResolutionCache[homeserver].Result}"); return RuntimeCache.HomeserverResolutionCache[homeserver].Result; } + Console.WriteLine($"Cached homeserver expired, removing: {RuntimeCache.HomeserverResolutionCache[homeserver].Result}"); RuntimeCache.HomeserverResolutionCache.Remove(homeserver); } @@ -42,29 +39,25 @@ public class IHomeServer string result = null; Console.WriteLine($"Resolving homeserver: {homeserver}"); if (!homeserver.StartsWith("http")) homeserver = "https://" + homeserver; - if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/client")) - { - Console.WriteLine($"Got successful response for client well-known..."); + if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/client")) { + Console.WriteLine("Got successful response for client well-known..."); var resp = await _httpClient.GetFromJsonAsync($"{homeserver}/.well-known/matrix/client"); Console.WriteLine($"Response: {resp.ToString()}"); var hs = resp.GetProperty("m.homeserver").GetProperty("base_url").GetString(); result = hs; } - else - { - Console.WriteLine($"No client well-known..."); - if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/server")) - { + else { + Console.WriteLine("No client well-known..."); + if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/server")) { var resp = await _httpClient.GetFromJsonAsync($"{homeserver}/.well-known/matrix/server"); var hs = resp.GetProperty("m.server").GetString(); result = hs; } - else - { - Console.WriteLine($"No server well-known..."); - if (await _httpClient.CheckSuccessStatus($"{homeserver}/_matrix/client/versions")) result = homeserver; - else - { + else { + Console.WriteLine("No server well-known..."); + if (await _httpClient.CheckSuccessStatus($"{homeserver}/_matrix/client/versions")) + result = homeserver; + else { Console.WriteLine("No homeserver on shortname..."); if (await _httpClient.CheckSuccessStatus($"{homeserver.Replace("//", "//matrix.")}/_matrix/client/versions")) result = homeserver.Replace("//", "//matrix."); else Console.WriteLine($"Failed to resolve homeserver, not on {homeserver}, nor do client or server well-knowns exist!"); @@ -72,30 +65,27 @@ public class IHomeServer } } - if (result != null) - { + if (result != null) { Console.WriteLine($"Resolved homeserver: {homeserver} -> {result}"); - RuntimeCache.HomeserverResolutionCache.TryAdd(homeserver, new() - { + RuntimeCache.HomeserverResolutionCache.TryAdd(homeserver, new HomeServerResolutionResult { Result = result, ResolutionTime = DateTime.Now }); return result; } + throw new InvalidDataException($"Failed to resolve homeserver, not on {homeserver}, nor do client or server well-knowns exist!"); } - public async Task GetProfile(string mxid, bool debounce = false, bool cache = true) - { - if (cache) - { - if(debounce) await Task.Delay(Random.Shared.Next(100, 500)); - if (_profileCache.ContainsKey(mxid)) - { - while (_profileCache[mxid] == null) - { + + public async Task GetProfile(string mxid, bool debounce = false, bool cache = true) { + if (cache) { + if (debounce) await Task.Delay(Random.Shared.Next(100, 500)); + if (_profileCache.ContainsKey(mxid)) { + while (_profileCache[mxid] == null) { Console.WriteLine($"Waiting for profile cache for {mxid}, currently {_profileCache[mxid]?.ToJson() ?? "null"} within {_profileCache.Count} profiles..."); await Task.Delay(Random.Shared.Next(50, 500)); } + return _profileCache[mxid]; } } @@ -103,13 +93,11 @@ public class IHomeServer _profileCache.Add(mxid, null); var resp = await _httpClient.GetAsync($"/_matrix/client/r0/profile/{mxid}"); var data = await resp.Content.ReadFromJsonAsync(); - if(!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data.ToString()); + if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data); var profile = data.Deserialize(); _profileCache[mxid] = profile; return profile; } - public string? ResolveMediaUri(string mxc) - { - return mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/"); - } + + public string? ResolveMediaUri(string mxc) => mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/"); } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs new file mode 100644 index 0000000..3df70e1 --- /dev/null +++ b/MatrixRoomUtils.Core/MatrixException.cs @@ -0,0 +1,57 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; + +namespace MatrixRoomUtils.Core; + +public class MatrixException : Exception { + [JsonPropertyName("errcode")] + public string ErrorCode { get; set; } + + [JsonPropertyName("error")] + public string Error { get; set; } + + [JsonPropertyName("soft_logout")] + public bool? SoftLogout { get; set; } + + [JsonPropertyName("retry_after_ms")] + public int? RetryAfterMs { get; set; } + + public override string Message => + 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, + // 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() + }; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RatelimitedHttpClient.cs b/MatrixRoomUtils.Core/RatelimitedHttpClient.cs deleted file mode 100644 index 61eab07..0000000 --- a/MatrixRoomUtils.Core/RatelimitedHttpClient.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MatrixRoomUtils.Core; - -public class RatelimitedHttpClient : HttpClient -{ - - -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs index 942f873..3f50d2e 100644 --- a/MatrixRoomUtils.Core/RemoteHomeServer.cs +++ b/MatrixRoomUtils.Core/RemoteHomeServer.cs @@ -1,50 +1,39 @@ using System.Net.Http.Json; using System.Text.Json; +using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core; -public class RemoteHomeServer : IHomeServer -{ - - - public RemoteHomeServer(string canonicalHomeServerDomain) - { +public class RemoteHomeServer : IHomeServer { + public RemoteHomeServer(string canonicalHomeServerDomain) { HomeServerDomain = canonicalHomeServerDomain; - _httpClient = new HttpClient(); + _httpClient = new MatrixHttpClient(); _httpClient.Timeout = TimeSpan.FromSeconds(5); } - public async Task Configure() - { + + public async Task Configure() { FullHomeServerDomain = await ResolveHomeserverFromWellKnown(HomeServerDomain); _httpClient.Dispose(); - _httpClient = new HttpClient { BaseAddress = new Uri(FullHomeServerDomain) }; + _httpClient = new MatrixHttpClient { BaseAddress = new Uri(FullHomeServerDomain) }; _httpClient.Timeout = TimeSpan.FromSeconds(5); Console.WriteLine("[RHS] Finished setting up http client"); return this; } - - public async Task GetRoom(string roomId) - { - return new Room(_httpClient, roomId); - } - public async Task> GetJoinedRooms() - { + public async Task GetRoom(string roomId) => new Room(_httpClient, roomId); + + public async Task> GetJoinedRooms() { var rooms = new List(); var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms"); - if (!roomQuery.IsSuccessStatusCode) - { + if (!roomQuery.IsSuccessStatusCode) { Console.WriteLine($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}"); throw new InvalidDataException($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}"); } var roomsJson = await roomQuery.Content.ReadFromJsonAsync(); - foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) - { - rooms.Add(new Room(_httpClient, room.GetString())); - } + foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new Room(_httpClient, room.GetString())); return rooms; } diff --git a/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs b/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs index 8ec0e4f..37bb3ba 100644 --- a/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs +++ b/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs @@ -2,8 +2,7 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.Responses.Admin; -public class AdminRoomListingResult -{ +public class AdminRoomListingResult { [JsonPropertyName("offset")] public int Offset { get; set; } @@ -19,8 +18,7 @@ public class AdminRoomListingResult [JsonPropertyName("rooms")] public List Rooms { get; set; } = new(); - public class AdminRoomListingResultRoom - { + public class AdminRoomListingResultRoom { [JsonPropertyName("room_id")] public string RoomId { get; set; } diff --git a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs index 5df99f7..da7d569 100644 --- a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs +++ b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs @@ -6,58 +6,56 @@ using MatrixRoomUtils.Core.Extensions; namespace MatrixRoomUtils.Core.Responses; -public class CreateRoomRequest -{ - [JsonPropertyName("name")] public string Name { get; set; } = null!; +public class CreateRoomRequest { + [JsonIgnore] public CreationContentBaseType _creationContentBaseType; + + public CreateRoomRequest() => _creationContentBaseType = new CreationContentBaseType(this); + + [JsonPropertyName("name")] + public string Name { get; set; } = null!; - [JsonPropertyName("room_alias_name")] public string RoomAliasName { get; set; } = null!; + [JsonPropertyName("room_alias_name")] + public string RoomAliasName { get; set; } = null!; //we dont want to use this, we want more control // [JsonPropertyName("preset")] // public string Preset { get; set; } = null!; - [JsonPropertyName("initial_state")] public List InitialState { get; set; } = null!; - [JsonPropertyName("visibility")] public string Visibility { get; set; } = null!; + [JsonPropertyName("initial_state")] + public List InitialState { get; set; } = null!; + + [JsonPropertyName("visibility")] + public string Visibility { get; set; } = null!; [JsonPropertyName("power_level_content_override")] public PowerLevelEvent PowerLevelContentOverride { get; set; } = null!; - [JsonPropertyName("creation_content")] public JsonObject CreationContent { get; set; } = new(); + [JsonPropertyName("creation_content")] + public JsonObject CreationContent { get; set; } = new(); /// - /// For use only when you can't use the CreationContent property + /// For use only when you can't use the CreationContent property /// - public StateEvent this[string event_type, string event_key = ""] - { + public StateEvent this[string event_type, string event_key = ""] { get => InitialState.First(x => x.Type == event_type && x.StateKey == event_key); - set - { + set { var stateEvent = InitialState.FirstOrDefault(x => x.Type == event_type && x.StateKey == event_key); if (stateEvent == null) - { InitialState.Add(value); - } else - { InitialState[InitialState.IndexOf(stateEvent)] = value; - } } } //extra properties [JsonIgnore] - public string HistoryVisibility - { - get - { + public string HistoryVisibility { + get { var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility"); - if (stateEvent == null) - { - InitialState.Add(new StateEvent() - { + if (stateEvent == null) { + InitialState.Add(new StateEvent { Type = "m.room.history_visibility", - Content = new JsonObject() - { + Content = new JsonObject { ["history_visibility"] = "shared" } }); @@ -66,22 +64,16 @@ public class CreateRoomRequest return stateEvent.ContentAsJsonNode["history_visibility"].GetValue(); } - set - { + set { var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility"); if (stateEvent == null) - { - InitialState.Add(new StateEvent() - { + InitialState.Add(new StateEvent { Type = "m.room.history_visibility", - Content = new JsonObject() - { + Content = new JsonObject { ["history_visibility"] = value } }); - } - else - { + else { var v = stateEvent.ContentAsJsonNode; v["history_visibility"] = value; stateEvent.ContentAsJsonNode = v; @@ -90,18 +82,13 @@ public class CreateRoomRequest } [JsonIgnore] - public string RoomIcon - { - get - { + public string RoomIcon { + get { var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar"); - if (stateEvent == null) - { - InitialState.Add(new StateEvent() - { + if (stateEvent == null) { + InitialState.Add(new StateEvent { Type = "m.room.avatar", - Content = new JsonObject() - { + Content = new JsonObject { ["url"] = "" } }); @@ -110,22 +97,16 @@ public class CreateRoomRequest return stateEvent.ContentAsJsonNode["url"].GetValue(); } - set - { + set { var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar"); if (stateEvent == null) - { - InitialState.Add(new StateEvent() - { + InitialState.Add(new StateEvent { Type = "m.room.avatar", - Content = new JsonObject() - { + Content = new JsonObject { ["url"] = value } }); - } - else - { + else { var v = stateEvent.ContentAsJsonNode; v["url"] = value; stateEvent.ContentAsJsonNode = v; @@ -177,64 +158,50 @@ public class CreateRoomRequest // } // } - public ServerACL ServerACLs - { - get - { + public ServerACL ServerACLs { + get { var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.server_acl"); - if (stateEvent == null) - { - InitialState.Add(new StateEvent() - { + if (stateEvent == null) { + InitialState.Add(new StateEvent { Type = "m.room.server_acl", - Content = new JsonObject() - { - ["allow"] = new JsonArray() - { + Content = new JsonObject { + ["allow"] = new JsonArray { "*" }, ["deny"] = new JsonArray() } }); - return new ServerACL() - { - Allow = new List() - { + return new ServerACL { + Allow = new List { "*" }, Deny = new List(), AllowIpLiterals = true }; } - return new ServerACL() - { - Allow = JsonSerializer.Deserialize>(stateEvent.ContentAsJsonNode["allow"]), - Deny = JsonSerializer.Deserialize>(stateEvent.ContentAsJsonNode["deny"]), + + return new ServerACL { + Allow = stateEvent.ContentAsJsonNode["allow"].Deserialize>(), + Deny = stateEvent.ContentAsJsonNode["deny"].Deserialize>(), AllowIpLiterals = true }; } - set - { + set { Console.WriteLine($"Setting server acl to {value.ToJson()}"); var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.server_acl"); if (stateEvent == null) - { - InitialState.Add(new StateEvent() - { + InitialState.Add(new StateEvent { Type = "m.room.server_acl", - Content = new JsonObject() - { - ["allow"] = JsonArray.Parse(JsonSerializer.Serialize(value.Allow)), - ["deny"] = JsonArray.Parse(JsonSerializer.Serialize(value.Deny)) - ["allow_ip_literals"] = value.AllowIpLiterals + Content = new JsonObject { + ["allow"] = JsonNode.Parse(JsonSerializer.Serialize(value.Allow)), + ["deny"] = JsonNode.Parse(JsonSerializer.Serialize(value.Deny)) + ["allow_ip_literals"] = value.AllowIpLiterals } }); - } - else - { + else { var v = stateEvent.ContentAsJsonNode; - v["allow"] = JsonArray.Parse(JsonSerializer.Serialize(value.Allow)); - v["deny"] = JsonArray.Parse(JsonSerializer.Serialize(value.Deny)); + v["allow"] = JsonNode.Parse(JsonSerializer.Serialize(value.Allow)); + v["deny"] = JsonNode.Parse(JsonSerializer.Serialize(value.Deny)); v["allow_ip_literals"] = value.AllowIpLiterals; stateEvent.ContentAsJsonNode = v; Console.WriteLine($"v={v.ToJson()}"); @@ -243,14 +210,7 @@ public class CreateRoomRequest } } - - [JsonIgnore] public CreationContentBaseType _creationContentBaseType; - - public CreateRoomRequest() => _creationContentBaseType = new(this); - - - public Dictionary Validate() - { + public Dictionary Validate() { Dictionary errors = new(); if (!Regex.IsMatch(RoomAliasName, @"[a-zA-Z0-9_\-]+$")) errors.Add("room_alias_name", "Room alias name must only contain letters, numbers, underscores, and hyphens."); @@ -259,49 +219,65 @@ public class CreateRoomRequest } } -public class CreationContentBaseType -{ +public class CreationContentBaseType { private readonly CreateRoomRequest createRoomRequest; - public CreationContentBaseType(CreateRoomRequest createRoomRequest) - { - this.createRoomRequest = createRoomRequest; - } + public CreationContentBaseType(CreateRoomRequest createRoomRequest) => this.createRoomRequest = createRoomRequest; [JsonPropertyName("type")] - public string Type - { + public string Type { get => (string)createRoomRequest.CreationContent["type"]; - set - { + set { if (value is "null" or "") createRoomRequest.CreationContent.Remove("type"); else createRoomRequest.CreationContent["type"] = value; } } } -public class PowerLevelEvent -{ - [JsonPropertyName("ban")] public int Ban { get; set; } // = 50; - [JsonPropertyName("events_default")] public int EventsDefault { get; set; } // = 0; - [JsonPropertyName("events")] public Dictionary Events { get; set; } // = null!; - [JsonPropertyName("invite")] public int Invite { get; set; } // = 50; - [JsonPropertyName("kick")] public int Kick { get; set; } // = 50; - [JsonPropertyName("notifications")] public NotificationsPL NotificationsPl { get; set; } // = null!; - [JsonPropertyName("redact")] public int Redact { get; set; } // = 50; - [JsonPropertyName("state_default")] public int StateDefault { get; set; } // = 50; - [JsonPropertyName("users")] public Dictionary Users { get; set; } // = null!; - [JsonPropertyName("users_default")] public int UsersDefault { get; set; } // = 0; +public class PowerLevelEvent { + [JsonPropertyName("ban")] + public int Ban { get; set; } // = 50; + + [JsonPropertyName("events_default")] + public int EventsDefault { get; set; } // = 0; + + [JsonPropertyName("events")] + public Dictionary Events { get; set; } // = null!; + + [JsonPropertyName("invite")] + public int Invite { get; set; } // = 50; + + [JsonPropertyName("kick")] + public int Kick { get; set; } // = 50; + + [JsonPropertyName("notifications")] + public NotificationsPL NotificationsPl { get; set; } // = null!; + + [JsonPropertyName("redact")] + public int Redact { get; set; } // = 50; + + [JsonPropertyName("state_default")] + public int StateDefault { get; set; } // = 50; + + [JsonPropertyName("users")] + public Dictionary Users { get; set; } // = null!; + + [JsonPropertyName("users_default")] + public int UsersDefault { get; set; } // = 0; } -public class NotificationsPL -{ - [JsonPropertyName("room")] public int Room { get; set; } = 50; +public class NotificationsPL { + [JsonPropertyName("room")] + public int Room { get; set; } = 50; } -public class ServerACL -{ - [JsonPropertyName("allow")] public List Allow { get; set; } // = null!; - [JsonPropertyName("deny")] public List Deny { get; set; } // = null!; - [JsonPropertyName("allow_ip_literals")] public bool AllowIpLiterals { get; set; } // = false; +public class ServerACL { + [JsonPropertyName("allow")] + public List Allow { get; set; } // = null!; + + [JsonPropertyName("deny")] + public List Deny { get; set; } // = null!; + + [JsonPropertyName("allow_ip_literals")] + public bool AllowIpLiterals { get; set; } // = false; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/LoginResponse.cs b/MatrixRoomUtils.Core/Responses/LoginResponse.cs index 34b42d1..3259e44 100644 --- a/MatrixRoomUtils.Core/Responses/LoginResponse.cs +++ b/MatrixRoomUtils.Core/Responses/LoginResponse.cs @@ -4,27 +4,26 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.Responses; -public class LoginResponse -{ +public class LoginResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } + [JsonPropertyName("device_id")] public string DeviceId { get; set; } + [JsonPropertyName("home_server")] public string HomeServer { get; set; } + [JsonPropertyName("user_id")] public string UserId { get; set; } - - public async Task GetProfile() - { + + public async Task GetProfile() { var hc = new HttpClient(); var resp = await hc.GetAsync($"{HomeServer}/_matrix/client/r0/profile/{UserId}"); var data = await resp.Content.ReadFromJsonAsync(); - if(!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data.ToString()); + if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data); return data.Deserialize(); } - public async Task GetCanonicalHomeserverUrl() - { - return (await new RemoteHomeServer(HomeServer).Configure()).FullHomeServerDomain; - } + + public async Task GetCanonicalHomeserverUrl() => (await new RemoteHomeServer(HomeServer).Configure()).FullHomeServerDomain; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/ProfileResponse.cs b/MatrixRoomUtils.Core/Responses/ProfileResponse.cs index 2c0b679..db72386 100644 --- a/MatrixRoomUtils.Core/Responses/ProfileResponse.cs +++ b/MatrixRoomUtils.Core/Responses/ProfileResponse.cs @@ -2,10 +2,10 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.Responses; -public class ProfileResponse -{ +public class ProfileResponse { [JsonPropertyName("avatar_url")] public string? AvatarUrl { get; set; } = ""; + [JsonPropertyName("displayname")] public string? DisplayName { get; set; } = ""; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs index 670c121..36f0a36 100644 --- a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs +++ b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs @@ -2,43 +2,47 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core; -public class StateEventResponse : StateEvent -{ +public class StateEventResponse : StateEvent { [JsonPropertyName("origin_server_ts")] public ulong OriginServerTs { get; set; } + [JsonPropertyName("room_id")] public string RoomId { get; set; } + [JsonPropertyName("sender")] public string Sender { get; set; } + [JsonPropertyName("unsigned")] public UnsignedData? Unsigned { get; set; } + [JsonPropertyName("event_id")] public string EventId { get; set; } + [JsonPropertyName("user_id")] public string UserId { get; set; } + [JsonPropertyName("replaces_state")] public string ReplacesState { get; set; } + [JsonPropertyName("prev_content")] public dynamic PrevContent { get; set; } - - - public class UnsignedData - { + + public class UnsignedData { [JsonPropertyName("age")] public ulong Age { get; set; } + [JsonPropertyName("prev_content")] public dynamic? PrevContent { get; set; } + [JsonPropertyName("redacted_because")] public dynamic? RedactedBecause { get; set; } + [JsonPropertyName("transaction_id")] public string? TransactionId { get; set; } - } } -public class StateEventResponse : StateEventResponse where T : class -{ - +public class StateEventResponse : StateEventResponse where T : class { [JsonPropertyName("content")] public T Content { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs index f228271..a867c0c 100644 --- a/MatrixRoomUtils.Core/Room.cs +++ b/MatrixRoomUtils.Core/Room.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.CodeAnalysis; using System.Net.Http.Json; -using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using System.Web; @@ -8,26 +6,23 @@ using MatrixRoomUtils.Core.Extensions; namespace MatrixRoomUtils.Core; -public class Room -{ +public class Room { private readonly HttpClient _httpClient; - public string RoomId { get; set; } - public Room(HttpClient httpClient, string roomId) - { + public Room(HttpClient httpClient, string roomId) { _httpClient = httpClient; RoomId = roomId; } - public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) - { + public string RoomId { get; set; } + + public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) { var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; if (!string.IsNullOrEmpty(type)) url += $"/{type}"; if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; var res = await _httpClient.GetAsync(url); - if (!res.IsSuccessStatusCode) - { + if (!res.IsSuccessStatusCode) { if (logOnFailure) Console.WriteLine($"{RoomId}/{stateKey}/{type} - got status: {res.StatusCode}"); return null; } @@ -36,20 +31,17 @@ public class Room return result; } - public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = false) - { + public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = false) { var res = await GetStateAsync(type, stateKey, logOnFailure); if (res == null) return default; return res.Value.Deserialize(); } - - public async Task GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") - { + + public async Task GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") { var url = $"/_matrix/client/r0/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}"; if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}"; var res = await _httpClient.GetAsync(url); - if (!res.IsSuccessStatusCode) - { + if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); throw new Exception($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); } @@ -58,11 +50,9 @@ public class Room return result ?? new MessagesResponse(); } - public async Task GetNameAsync() - { + public async Task GetNameAsync() { var res = await GetStateAsync("m.room.name"); - if (!res.HasValue) - { + if (!res.HasValue) { Console.WriteLine($"Room {RoomId} has no name!"); return RoomId; } @@ -72,22 +62,19 @@ public class Room return resn; } - public async Task JoinAsync(string[]? homeservers = null) - { - string join_url = $"/_matrix/client/r0/join/{HttpUtility.UrlEncode(RoomId)}"; + public async Task JoinAsync(string[]? homeservers = null) { + var join_url = $"/_matrix/client/r0/join/{HttpUtility.UrlEncode(RoomId)}"; Console.WriteLine($"Calling {join_url} with {homeservers?.Length ?? 0} via's..."); if (homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':')[1] }; var fullJoinUrl = $"{join_url}?server_name=" + string.Join("&server_name=", homeservers); var res = await _httpClient.PostAsync(fullJoinUrl, null); } - public async Task> GetMembersAsync(bool joinedOnly = true) - { + public async Task> GetMembersAsync(bool joinedOnly = true) { var res = await GetStateAsync(""); if (!res.HasValue) return new List(); var members = new List(); - foreach (var member in res.Value.EnumerateArray()) - { + foreach (var member in res.Value.EnumerateArray()) { if (member.GetProperty("type").GetString() != "m.room.member") continue; if (joinedOnly && member.GetProperty("content").GetProperty("membership").GetString() != "join") continue; var memberId = member.GetProperty("state_key").GetString(); @@ -97,63 +84,52 @@ public class Room return members; } - public async Task> GetAliasesAsync() - { + public async Task> GetAliasesAsync() { var res = await GetStateAsync("m.room.aliases"); if (!res.HasValue) return new List(); var aliases = new List(); - foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) - { - aliases.Add(alias.GetString() ?? ""); - } + foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) aliases.Add(alias.GetString() ?? ""); return aliases; } - public async Task GetCanonicalAliasAsync() - { + public async Task GetCanonicalAliasAsync() { var res = await GetStateAsync("m.room.canonical_alias"); if (!res.HasValue) return ""; return res.Value.GetProperty("alias").GetString() ?? ""; } - public async Task GetTopicAsync() - { + public async Task GetTopicAsync() { var res = await GetStateAsync("m.room.topic"); if (!res.HasValue) return ""; return res.Value.GetProperty("topic").GetString() ?? ""; } - public async Task GetAvatarUrlAsync() - { + public async Task GetAvatarUrlAsync() { var res = await GetStateAsync("m.room.avatar"); if (!res.HasValue) return ""; return res.Value.GetProperty("url").GetString() ?? ""; } - public async Task GetJoinRuleAsync() - { + public async Task GetJoinRuleAsync() { var res = await GetStateAsync("m.room.join_rules"); if (!res.HasValue) return new JoinRules(); return res.Value.Deserialize() ?? new JoinRules(); } - public async Task GetHistoryVisibilityAsync() - { + public async Task GetHistoryVisibilityAsync() { var res = await GetStateAsync("m.room.history_visibility"); if (!res.HasValue) return ""; return res.Value.GetProperty("history_visibility").GetString() ?? ""; } - public async Task GetGuestAccessAsync() - { + public async Task GetGuestAccessAsync() { var res = await GetStateAsync("m.room.guest_access"); if (!res.HasValue) return ""; return res.Value.GetProperty("guest_access").GetString() ?? ""; } - public async Task GetCreateEventAsync() - { + public async Task GetCreateEventAsync() { var res = await GetStateAsync("m.room.create"); if (!res.HasValue) return new CreateEvent(); @@ -163,33 +139,45 @@ public class Room } } -public class MessagesResponse -{ +public class MessagesResponse { [JsonPropertyName("start")] public string Start { get; set; } + [JsonPropertyName("end")] public string? End { get; set; } + [JsonPropertyName("chunk")] public List Chunk { get; set; } = new(); + [JsonPropertyName("state")] public List State { get; set; } = new(); } -public class CreateEvent -{ - [JsonPropertyName("creator")] public string Creator { get; set; } - [JsonPropertyName("room_version")] public string RoomVersion { get; set; } - [JsonPropertyName("type")] public string? Type { get; set; } - [JsonPropertyName("predecessor")] public object? Predecessor { get; set; } - [JsonPropertyName("m.federate")] public bool Federate { get; set; } +public class CreateEvent { + [JsonPropertyName("creator")] + public string Creator { get; set; } + + [JsonPropertyName("room_version")] + public string RoomVersion { get; set; } + + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("predecessor")] + public object? Predecessor { get; set; } + + [JsonPropertyName("m.federate")] + public bool Federate { get; set; } } -public class JoinRules -{ +public class JoinRules { private const string Public = "public"; private const string Invite = "invite"; private const string Knock = "knock"; - [JsonPropertyName("join_rule")] public string JoinRule { get; set; } - [JsonPropertyName("allow")] public List Allow { get; set; } + [JsonPropertyName("join_rule")] + public string JoinRule { get; set; } + + [JsonPropertyName("allow")] + public List Allow { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RuntimeCache.cs b/MatrixRoomUtils.Core/RuntimeCache.cs index 4f73341..a2fcf40 100644 --- a/MatrixRoomUtils.Core/RuntimeCache.cs +++ b/MatrixRoomUtils.Core/RuntimeCache.cs @@ -3,9 +3,21 @@ using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core; -public class RuntimeCache -{ +public class RuntimeCache { public static bool WasLoaded = false; + + static RuntimeCache() => + Task.Run(async () => { + while (true) { + await Task.Delay(1000); + foreach (var (key, value) in GenericResponseCache) + if (value.Cache.Any()) + SaveObject("rory.matrixroomutils.generic_cache:" + key, value); + else + RemoveObject("rory.matrixroomutils.generic_cache:" + key); + } + }); + public static string? LastUsedToken { get; set; } public static AuthenticatedHomeServer CurrentHomeServer { get; set; } public static Dictionary LoginSessions { get; set; } = new(); @@ -18,110 +30,69 @@ public class RuntimeCache public static Action Save { get; set; } = () => { Console.WriteLine("RuntimeCache.Save() was called, but no callback was set!"); }; public static Action SaveObject { get; set; } = (key, value) => { Console.WriteLine($"RuntimeCache.SaveObject({key}, {value}) was called, but no callback was set!"); }; public static Action RemoveObject { get; set; } = key => { Console.WriteLine($"RuntimeCache.RemoveObject({key}) was called, but no callback was set!"); }; - - static RuntimeCache() - { - Task.Run(async () => - { - while(true) - { - await Task.Delay(1000); - foreach (var (key, value) in GenericResponseCache) - { - if (value.Cache.Any()) - SaveObject("rory.matrixroomutils.generic_cache:" + key, value); - else - { - RemoveObject("rory.matrixroomutils.generic_cache:" + key); - } - } - } - }); - } } -public class UserInfo -{ +public class UserInfo { public ProfileResponse Profile { get; set; } = new(); public LoginResponse LoginResponse { get; set; } - public string AccessToken - { - get => LoginResponse.AccessToken; - } + public string AccessToken => LoginResponse.AccessToken; } -public class HomeServerResolutionResult -{ +public class HomeServerResolutionResult { public string Result { get; set; } public DateTime ResolutionTime { get; set; } } -public class ObjectCache where T : class -{ +public class ObjectCache where T : class { + public ObjectCache() => + //expiry timer + Task.Run(async () => { + while (Cache.Any()) { + await Task.Delay(1000); + foreach (var x in Cache.Where(x => x.Value.ExpiryTime < DateTime.Now).OrderBy(x => x.Value.ExpiryTime).Take(15).ToList()) + // Console.WriteLine($"Removing {x.Key} from cache"); + Cache.Remove(x.Key); + //RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this); + } + }); + public Dictionary> Cache { get; set; } = new(); public string Name { get; set; } = null!; - public GenericResult this[string key] - { - get - { - if (Cache.ContainsKey(key)) - { + public GenericResult this[string key] { + get { + if (Cache.ContainsKey(key)) { // Console.WriteLine($"cache.get({key}): hit"); // Console.WriteLine($"Found item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}"); if (Cache[key].ExpiryTime < DateTime.Now) - Console.WriteLine($"WARNING: item {key} in cache {Name} expired at {Cache[key].ExpiryTime}:\n{Cache[key].Result.ToJson(indent: false)}"); + Console.WriteLine($"WARNING: item {key} in cache {Name} expired at {Cache[key].ExpiryTime}:\n{Cache[key].Result.ToJson(false)}"); return Cache[key]; } Console.WriteLine($"cache.get({key}): miss"); return null; } - set - { - Cache[key] = value; - // Console.WriteLine($"set({key}) = {Cache[key].Result.ToJson(indent:false)}"); - // Console.WriteLine($"new_state: {this.ToJson(indent:false)}"); - // Console.WriteLine($"New item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}"); - // Console.Error.WriteLine("Full cache: " + Cache.ToJson()); - } - } - - public ObjectCache() - { - //expiry timer - Task.Run(async () => - { - while (Cache.Any()) - { - await Task.Delay(1000); - foreach (var x in Cache.Where(x => x.Value.ExpiryTime < DateTime.Now).OrderBy(x => x.Value.ExpiryTime).Take(15).ToList()) - { - // Console.WriteLine($"Removing {x.Key} from cache"); - Cache.Remove(x.Key); - } - //RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this); - } - }); + set => Cache[key] = value; + // Console.WriteLine($"set({key}) = {Cache[key].Result.ToJson(indent:false)}"); + // Console.WriteLine($"new_state: {this.ToJson(indent:false)}"); + // Console.WriteLine($"New item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}"); + // Console.Error.WriteLine("Full cache: " + Cache.ToJson()); } public bool ContainsKey(string key) => Cache.ContainsKey(key); } -public class GenericResult -{ - public T? Result { get; set; } - public DateTime? ExpiryTime { get; set; } = DateTime.Now; - - public GenericResult() - { +public class GenericResult { + public GenericResult() { //expiry timer } - public GenericResult(T? result, DateTime? expiryTime = null) : this() - { + public GenericResult(T? result, DateTime? expiryTime = null) : this() { Result = result; ExpiryTime = expiryTime; } + + public T? Result { get; set; } + public DateTime? ExpiryTime { get; set; } = DateTime.Now; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs index 6321fb6..a8c1fac 100644 --- a/MatrixRoomUtils.Core/StateEvent.cs +++ b/MatrixRoomUtils.Core/StateEvent.cs @@ -4,52 +4,48 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core; -public class StateEvent -{ - [JsonPropertyName("content")] public dynamic Content { get; set; } = new { }; +public class StateEvent { + [JsonPropertyName("content")] + public dynamic Content { get; set; } = new { }; + + [JsonPropertyName("state_key")] + public string StateKey { get; set; } = ""; - [JsonPropertyName("state_key")] public string StateKey { get; set; } = ""; - [JsonPropertyName("type")] public string Type { get; set; } - [JsonPropertyName("replaces_state")] public string? ReplacesState { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("replaces_state")] + public string? ReplacesState { get; set; } //extra properties [JsonIgnore] - public JsonNode ContentAsJsonNode - { + public JsonNode ContentAsJsonNode { get => JsonSerializer.SerializeToNode(Content); set => Content = value; } - public StateEvent As() where T : class - { - return (StateEvent)this; - } - - public string dtype - { - get - { - string res = GetType().Name switch - { + public string dtype { + get { + var res = GetType().Name switch { "StateEvent`1" => $"StateEvent<{Content.GetType().Name}>", _ => GetType().Name }; return res; } } + + public StateEvent As() where T : class => (StateEvent)this; } -public class StateEvent : StateEvent where T : class -{ - public StateEvent() - { +public class StateEvent : StateEvent where T : class { + public StateEvent() { //import base content if not an empty object - if (base.Content.GetType() == typeof(T)) - { + if (base.Content.GetType() == typeof(T)) { Console.WriteLine($"StateEvent<{typeof(T)}> created with base content of type {base.Content.GetType()}. Importing base content."); Content = base.Content; } } + [JsonPropertyName("content")] public new T Content { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventStruct.cs b/MatrixRoomUtils.Core/StateEventStruct.cs index bfda594..cd301ac 100644 --- a/MatrixRoomUtils.Core/StateEventStruct.cs +++ b/MatrixRoomUtils.Core/StateEventStruct.cs @@ -1,7 +1,6 @@ namespace MatrixRoomUtils.Core; -public struct StateEventStruct -{ +public struct StateEventStruct { public object content { get; set; } public long origin_server_ts { get; set; } public string sender { get; set; } diff --git a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs index a927ace..6f6d082 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs @@ -2,51 +2,50 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.StateEventTypes; -public class PolicyRuleStateEventData -{ +public class PolicyRuleStateEventData { /// - /// Entity this ban applies to, can use * and ? as globs. + /// Entity this ban applies to, can use * and ? as globs. /// [JsonPropertyName("entity")] public string Entity { get; set; } + /// - /// Reason this user is banned + /// Reason this user is banned /// [JsonPropertyName("reason")] public string? Reason { get; set; } + /// - /// Suggested action to take + /// Suggested action to take /// [JsonPropertyName("recommendation")] public string? Recommendation { get; set; } /// - /// Expiry time in milliseconds since the unix epoch, or null if the ban has no expiry. + /// Expiry time in milliseconds since the unix epoch, or null if the ban has no expiry. /// [JsonPropertyName("support.feline.policy.expiry.rev.2")] //stable prefix: expiry, msc pending public long? Expiry { get; set; } - - + //utils /// - /// Readable expiry time, provided for easy interaction + /// Readable expiry time, provided for easy interaction /// [JsonPropertyName("gay.rory.matrix_room_utils.readable_expiry_time_utc")] - public DateTime? ExpiryDateTime - { + public DateTime? ExpiryDateTime { get => Expiry == null ? null : DateTimeOffset.FromUnixTimeMilliseconds(Expiry.Value).DateTime; - set => Expiry = ((DateTimeOffset) value).ToUnixTimeMilliseconds(); + set => Expiry = ((DateTimeOffset)value).ToUnixTimeMilliseconds(); } } -public static class PolicyRecommendationTypes -{ +public static class PolicyRecommendationTypes { /// - /// Ban this user + /// Ban this user /// public static string Ban = "m.ban"; + /// - /// Mute this user + /// Mute this user /// public static string Mute = "support.feline.policy.recommendation_mute"; //stable prefix: m.mute, msc pending } \ No newline at end of file diff --git a/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj b/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj index 71c3d08..a34cd2c 100644 --- a/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj +++ b/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj @@ -1,22 +1,22 @@ - - net7.0 - enable - enable - + + net7.0 + enable + enable + - - - + + + - - - + + + - - - + + + diff --git a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml index 0125c85..04b2c2a 100644 --- a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml +++ b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml @@ -5,38 +5,37 @@ - - + + Error - - + + -
-
-

Error.

-

An error occurred while processing your request.

+
+
+

Error.

+

An error occurred while processing your request.

- @if (Model.ShowRequestId) - { -

- Request ID: @Model.RequestId -

- } - -

Development Mode

-

- Swapping to the Development environment displays detailed information about the error that occurred. -

+ @if (Model.ShowRequestId) {

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. + Request ID: @Model.RequestId

-
+ } + +

Development Mode

+

+ Swapping to the Development environment displays detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+
- + \ No newline at end of file diff --git a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs index b70d895..4b918c1 100644 --- a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs +++ b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs @@ -6,21 +6,14 @@ namespace MatrixRoomUtils.Web.Server.Pages; [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [IgnoreAntiforgeryToken] -public class ErrorModel : PageModel -{ - public string? RequestId { get; set; } +public class ErrorModel : PageModel { + private readonly ILogger _logger; - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + public ErrorModel(ILogger logger) => _logger = logger; - private readonly ILogger _logger; + public string? RequestId { get; set; } - public ErrorModel(ILogger logger) - { - _logger = logger; - } + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } -} + public void OnGet() => RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web.Server/Program.cs b/MatrixRoomUtils.Web.Server/Program.cs index a486226..b5c3869 100644 --- a/MatrixRoomUtils.Web.Server/Program.cs +++ b/MatrixRoomUtils.Web.Server/Program.cs @@ -9,11 +9,8 @@ var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) -{ app.UseWebAssemblyDebugging(); -} -else -{ +else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); @@ -26,9 +23,8 @@ app.UseStaticFiles(); app.UseRouting(); - app.MapRazorPages(); app.MapControllers(); app.MapFallbackToFile("index.html"); -app.Run(); +app.Run(); \ No newline at end of file diff --git a/MatrixRoomUtils.Web.Server/Properties/launchSettings.json b/MatrixRoomUtils.Web.Server/Properties/launchSettings.json index 3dcf07b..ad7ef88 100644 --- a/MatrixRoomUtils.Web.Server/Properties/launchSettings.json +++ b/MatrixRoomUtils.Web.Server/Properties/launchSettings.json @@ -1,40 +1,40 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:6314", - "sslPort": 44318 + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:6314", + "sslPort": 44318 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5167", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7235;http://localhost:5167", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" } }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5167", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7235;http://localhost:5167", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" } } } +} diff --git a/MatrixRoomUtils.Web/App.razor b/MatrixRoomUtils.Web/App.razor index 4e2789d..e58212b 100644 --- a/MatrixRoomUtils.Web/App.razor +++ b/MatrixRoomUtils.Web/App.razor @@ -12,10 +12,9 @@ @code { - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) - { + + protected override async Task OnInitializedAsync() { + if (!RuntimeCache.WasLoaded) { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); Console.WriteLine("Loaded from local storage"); StateHasChanged(); diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs index f70572b..4e7117d 100644 --- a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs +++ b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs @@ -3,23 +3,20 @@ using MatrixRoomUtils.Core; namespace MatrixRoomUtils.Web.Classes; -public partial class LocalStorageWrapper -{ - private static SemaphoreSlim _semaphoreSlim = new(1); +public class LocalStorageWrapper { + private static readonly SemaphoreSlim _semaphoreSlim = new(1); public static Settings Settings { get; set; } = new(); //some basic logic - public static async Task InitialiseRuntimeVariables(ILocalStorageService localStorage) - { + public static async Task InitialiseRuntimeVariables(ILocalStorageService localStorage) { //RuntimeCache stuff async void Save() => await SaveToLocalStorage(localStorage); RuntimeCache.Save = Save; RuntimeCache.SaveObject = async (key, obj) => await localStorage.SetItemAsync(key, obj); - RuntimeCache.RemoveObject = async (key) => await localStorage.RemoveItemAsync(key); - if (RuntimeCache.LastUsedToken != null) - { - Console.WriteLine($"Access token is not null, creating authenticated home server"); + RuntimeCache.RemoveObject = async key => await localStorage.RemoveItemAsync(key); + if (RuntimeCache.LastUsedToken != null) { + Console.WriteLine("Access token is not null, creating authenticated home server"); Console.WriteLine($"Homeserver cache: {RuntimeCache.HomeserverResolutionCache.Count} entries"); // Console.WriteLine(RuntimeCache.HomeserverResolutionCache.ToJson()); RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.UserId, RuntimeCache.LastUsedToken, @@ -28,26 +25,24 @@ public partial class LocalStorageWrapper } } - public static async Task LoadFromLocalStorage(ILocalStorageService localStorage) - { + public static async Task LoadFromLocalStorage(ILocalStorageService localStorage) { await _semaphoreSlim.WaitAsync(); - if (RuntimeCache.WasLoaded) - { + if (RuntimeCache.WasLoaded) { _semaphoreSlim.Release(); return; } + Console.WriteLine("Loading from local storage..."); - Settings = await localStorage.GetItemAsync("rory.matrixroomutils.settings") ?? new(); + Settings = await localStorage.GetItemAsync("rory.matrixroomutils.settings") ?? new Settings(); RuntimeCache.LastUsedToken = await localStorage.GetItemAsync("rory.matrixroomutils.last_used_token"); - RuntimeCache.LoginSessions = await localStorage.GetItemAsync>("rory.matrixroomutils.login_sessions") ?? new(); - RuntimeCache.HomeserverResolutionCache = await localStorage.GetItemAsync>("rory.matrixroomutils.homeserver_resolution_cache") ?? new(); + RuntimeCache.LoginSessions = await localStorage.GetItemAsync>("rory.matrixroomutils.login_sessions") ?? new Dictionary(); + RuntimeCache.HomeserverResolutionCache = await localStorage.GetItemAsync>("rory.matrixroomutils.homeserver_resolution_cache") ?? new Dictionary(); Console.WriteLine($"[LocalStorageWrapper] Loaded {RuntimeCache.LoginSessions.Count} login sessions, {RuntimeCache.HomeserverResolutionCache.Count} homeserver resolution cache entries"); //RuntimeCache.GenericResponseCache = await localStorage.GetItemAsync>>("rory.matrixroomutils.generic_cache") ?? new(); - foreach (var s in (await localStorage.KeysAsync()).Where(x => x.StartsWith("rory.matrixroomutils.generic_cache:")).ToList()) - { + foreach (var s in (await localStorage.KeysAsync()).Where(x => x.StartsWith("rory.matrixroomutils.generic_cache:")).ToList()) { Console.WriteLine($"Loading generic cache entry {s}"); RuntimeCache.GenericResponseCache[s.Replace("rory.matrixroomutils.generic_cache:", "")] = await localStorage.GetItemAsync>(s); } @@ -57,36 +52,31 @@ public partial class LocalStorageWrapper _semaphoreSlim.Release(); } - public static async Task SaveToLocalStorage(ILocalStorageService localStorage) - { + public static async Task SaveToLocalStorage(ILocalStorageService localStorage) { Console.WriteLine("Saving to local storage..."); await localStorage.SetItemAsync("rory.matrixroomutils.settings", Settings); if (RuntimeCache.LoginSessions != null) await localStorage.SetItemAsync("rory.matrixroomutils.login_sessions", RuntimeCache.LoginSessions); if (RuntimeCache.LastUsedToken != null) await localStorage.SetItemAsync("rory.matrixroomutils.last_used_token", RuntimeCache.LastUsedToken); } - public static async Task SaveCacheToLocalStorage(ILocalStorageService localStorage, bool awaitSave = true, bool saveGenericCache = true) - { + public static async Task SaveCacheToLocalStorage(ILocalStorageService localStorage, bool awaitSave = true, bool saveGenericCache = true) { await localStorage.SetItemAsync("rory.matrixroomutils.homeserver_resolution_cache", RuntimeCache.HomeserverResolutionCache.DistinctBy(x => x.Key) .ToDictionary(x => x.Key, x => x.Value)); //await localStorage.SetItemAsync("rory.matrixroomutils.generic_cache", RuntimeCache.GenericResponseCache); - if(saveGenericCache) - foreach (var s in RuntimeCache.GenericResponseCache.Keys) - { + if (saveGenericCache) + foreach (var s in RuntimeCache.GenericResponseCache.Keys) { var t = localStorage.SetItemAsync($"rory.matrixroomutils.generic_cache:{s}", RuntimeCache.GenericResponseCache[s]); if (awaitSave) await t; } } } -public class Settings -{ +public class Settings { public DeveloperSettings DeveloperSettings { get; set; } = new(); } -public class DeveloperSettings -{ +public class DeveloperSettings { public bool EnableLogViewers { get; set; } = false; public bool EnableConsoleLogging { get; set; } = true; public bool EnablePortableDevtools { get; set; } = false; diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs index c43bd3c..77c8281 100644 --- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs +++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs @@ -1,110 +1,89 @@ +using System.Text.Json.Nodes; using MatrixRoomUtils.Core; using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Web.Classes.RoomCreationTemplates; -public class DefaultRoomCreationTemplate : IRoomCreationTemplate -{ +public class DefaultRoomCreationTemplate : IRoomCreationTemplate { public string Name => "Default"; - public CreateRoomRequest CreateRoomRequest - { - get - { - return new() - { - Name = "My new room", - RoomAliasName = "myroom", - InitialState = new() - { - new() - { - Type = "m.room.history_visibility", - Content = new - { - history_visibility = "world_readable" - } - }, - new StateEvent - { - Type = "m.room.guest_access", - Content = new() - { - GuestAccess = "can_join" - } - }, - new() - { - Type = "m.room.join_rules", - Content = new - { - join_rule = "public" - } - }, - new() - { - Type = "m.room.server_acl", - Content = new - { - allow = new[] { "*" }, - deny = Array.Empty(), - allow_ip_literals = false - } - }, - new() - { - Type = "m.room.avatar", - Content = new - { - url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj" - } + + public CreateRoomRequest CreateRoomRequest => + new CreateRoomRequest { + Name = "My new room", + RoomAliasName = "myroom", + InitialState = new List { + new() { + Type = "m.room.history_visibility", + Content = new { + history_visibility = "world_readable" } }, - Visibility = "public", - PowerLevelContentOverride = new() - { - UsersDefault = 0, - EventsDefault = 100, - StateDefault = 50, - Invite = 0, - Redact = 50, - Kick = 50, - Ban = 50, - NotificationsPl = new() - { - Room = 50 - }, - Events = new() - { - { "im.vector.modular.widgets", 50 }, - { "io.element.voice_broadcast_info", 50 }, - { "m.reaction", 100 }, - { "m.room.avatar", 50 }, - { "m.room.canonical_alias", 50 }, - { "m.room.encryption", 100 }, - { "m.room.history_visibility", 100 }, - { "m.room.name", 50 }, - { "m.room.pinned_events", 50 }, - { "m.room.power_levels", 100 }, - { "m.room.redaction", 100 }, - { "m.room.server_acl", 100 }, - { "m.room.tombstone", 100 }, - { "m.room.topic", 50 }, - { "m.space.child", 50 }, - { "org.matrix.msc3401.call", 50 }, - { "org.matrix.msc3401.call.member", 50 } - }, - Users = new() - { - { RuntimeCache.CurrentHomeServer.UserId, 100 }, - }, + new StateEvent { + Type = "m.room.guest_access", + Content = new Pages.RoomManager.RoomManagerCreateRoom.GuestAccessContent { + GuestAccess = "can_join" + } }, - CreationContent = new() - { - { - "type", null + new() { + Type = "m.room.join_rules", + Content = new { + join_rule = "public" + } + }, + new() { + Type = "m.room.server_acl", + Content = new { + allow = new[] { "*" }, + deny = Array.Empty(), + allow_ip_literals = false } + }, + new() { + Type = "m.room.avatar", + Content = new { + url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj" + } + } + }, + Visibility = "public", + PowerLevelContentOverride = new PowerLevelEvent { + UsersDefault = 0, + EventsDefault = 100, + StateDefault = 50, + Invite = 0, + Redact = 50, + Kick = 50, + Ban = 50, + NotificationsPl = new NotificationsPL { + Room = 50 + }, + Events = new Dictionary { + { "im.vector.modular.widgets", 50 }, + { "io.element.voice_broadcast_info", 50 }, + { "m.reaction", 100 }, + { "m.room.avatar", 50 }, + { "m.room.canonical_alias", 50 }, + { "m.room.encryption", 100 }, + { "m.room.history_visibility", 100 }, + { "m.room.name", 50 }, + { "m.room.pinned_events", 50 }, + { "m.room.power_levels", 100 }, + { "m.room.redaction", 100 }, + { "m.room.server_acl", 100 }, + { "m.room.tombstone", 100 }, + { "m.room.topic", 50 }, + { "m.space.child", 50 }, + { "org.matrix.msc3401.call", 50 }, + { "org.matrix.msc3401.call.member", 50 } + }, + Users = new Dictionary { + { RuntimeCache.CurrentHomeServer.UserId, 100 } + } + }, + CreationContent = new JsonObject { + { + "type", null } - }; - } - } + } + }; } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs index 7f84dc4..bbb09b7 100644 --- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs +++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs @@ -2,8 +2,7 @@ using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Web.Classes.RoomCreationTemplates; -public interface IRoomCreationTemplate -{ +public interface IRoomCreationTemplate { public CreateRoomRequest CreateRoomRequest { get; } public string Name { get; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/FileUploadTest.razor b/MatrixRoomUtils.Web/FileUploadTest.razor index 2e25b54..478a3e4 100644 --- a/MatrixRoomUtils.Web/FileUploadTest.razor +++ b/MatrixRoomUtils.Web/FileUploadTest.razor @@ -6,8 +6,7 @@ @code { - private async void FilePicked(InputFileChangeEventArgs obj) - { + private async void FilePicked(InputFileChangeEventArgs obj) { Console.WriteLine("FilePicked"); Console.WriteLine(obj.File.Name); Console.WriteLine(obj.File.Size); diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj index cf6ce87..c76452f 100644 --- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj +++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj @@ -7,17 +7,17 @@ - - - + + + - + - + - + diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor index d47e60b..cf43c4f 100644 --- a/MatrixRoomUtils.Web/Pages/About.razor +++ b/MatrixRoomUtils.Web/Pages/About.razor @@ -13,26 +13,23 @@

You can find the source code on my git server.

You can also join the Matrix room for this project.

-@if (showBinDownload) -{ +@if (showBinDownload) {

This deployment also serves a copy of the compiled, hosting-ready binaries at /MRU-BIN.tar.xz!

} -@if (showSrcDownload) -{ +@if (showSrcDownload) {

This deployment also serves a copy of the compiled, hosting-ready binaries at /MRU-SRC.tar.xz!

} @code { - private bool showBinDownload { get; set; } = false; - private bool showSrcDownload { get; set; } = false; + private bool showBinDownload { get; set; } + private bool showSrcDownload { get; set; } - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { using var hc = new HttpClient(); - var hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri)); + var hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri)); showBinDownload = hr.StatusCode == HttpStatusCode.OK; - hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-SRC.tar.xz").AbsoluteUri)); + hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-SRC.tar.xz").AbsoluteUri)); showSrcDownload = hr.StatusCode == HttpStatusCode.OK; await base.OnInitializedAsync(); } diff --git a/MatrixRoomUtils.Web/Pages/DataExportPage.razor b/MatrixRoomUtils.Web/Pages/DataExportPage.razor index 49fb700..732cd74 100644 --- a/MatrixRoomUtils.Web/Pages/DataExportPage.razor +++ b/MatrixRoomUtils.Web/Pages/DataExportPage.razor @@ -1,5 +1,4 @@ @page "/Export" -@using MatrixRoomUtils.Web.Shared.IndexComponents @using System.Text.Json @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage @@ -11,12 +10,10 @@

Signed in accounts - Add new account or Import from TSV

-@if (_isLoaded) -{ -@foreach (var (token, user) in RuntimeCache.LoginSessions) -{ +@if (_isLoaded) { + @foreach (var (token, user) in RuntimeCache.LoginSessions) { @* *@ -
+        
 @user.LoginResponse.UserId[1..].Split(":")[0]\auth\access_token=@token
 @user.LoginResponse.UserId[1..].Split(":")[0]\auth\device_id=@user.LoginResponse.DeviceId
 @user.LoginResponse.UserId[1..].Split(":")[0]\auth\home_server=@(RuntimeCache.HomeserverResolutionCache.ContainsKey(user.LoginResponse.HomeServer) ? RuntimeCache.HomeserverResolutionCache[user.LoginResponse.HomeServer].Result : "loading...")
@@ -26,10 +23,9 @@
 @user.LoginResponse.UserId[1..].Split(":")[0]\user\online_key_backup=true
 @user.LoginResponse.UserId[1..].Split(":")[0]\user\only_share_keys_with_verified_users=false
     
+ } } -} -else -{ +else {

Loading...

@resolvedHomeservers/@totalHomeservers homeservers resolved...

} @@ -39,31 +35,26 @@ else private int resolvedHomeservers; private int totalHomeservers; - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - if (!RuntimeCache.WasLoaded) - { + if (!RuntimeCache.WasLoaded) { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); } var homeservers = RuntimeCache.LoginSessions.Values.Select(x => x.LoginResponse.HomeServer).Distinct(); totalHomeservers = homeservers.Count(); StateHasChanged(); - foreach (var hs in homeservers) - { - if (RuntimeCache.HomeserverResolutionCache.ContainsKey(hs)) - { + foreach (var hs in homeservers) { + if (RuntimeCache.HomeserverResolutionCache.ContainsKey(hs)) { resolvedHomeservers++; continue; } var resolvedHomeserver = (await new RemoteHomeServer(hs).Configure()).FullHomeServerDomain; - RuntimeCache.HomeserverResolutionCache.Add(hs, new() { Result = resolvedHomeserver, ResolutionTime = DateTime.Now }); + RuntimeCache.HomeserverResolutionCache.Add(hs, new HomeServerResolutionResult { Result = resolvedHomeserver, ResolutionTime = DateTime.Now }); await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); Console.WriteLine("Saved to local storage:"); - Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions() - { + Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions { WriteIndented = true })); resolvedHomeservers++; diff --git a/MatrixRoomUtils.Web/Pages/DebugTools.razor b/MatrixRoomUtils.Web/Pages/DebugTools.razor index c8fabaa..da5c172 100644 --- a/MatrixRoomUtils.Web/Pages/DebugTools.razor +++ b/MatrixRoomUtils.Web/Pages/DebugTools.razor @@ -1,26 +1,24 @@ @page "/Debug" -@using MatrixRoomUtils.Core.Interfaces -@using MatrixRoomUtils.Core.Extensions @using System.Reflection +@using MatrixRoomUtils.Core.Extensions +@using MatrixRoomUtils.Core.Interfaces @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager

Debug Tools


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

You are not in any rooms!

@*

Loading progress: @checkedRoomCount/@totalRoomCount

*@ } -else -{ +else {
Room List - @foreach (var room in Rooms) - { - + @foreach (var room in Rooms) { + + + }
- }
@@ -38,50 +36,44 @@ else @code { public List Rooms { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { + if (RuntimeCache.CurrentHomeServer == null) { NavigationManager.NavigateTo("/Login"); return; } - Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x=>x.RoomId).ToList(); + Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x => x.RoomId).ToList(); Console.WriteLine("Fetched joined rooms!"); } - //send req string get_request_url { get; set; } = ""; string get_request_result { get; set; } = ""; - private async Task SendGetRequest() - { + + private async Task SendGetRequest() { var field = typeof(IHomeServer).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField")); var httpClient = field.GetValue(RuntimeCache.CurrentHomeServer) as HttpClient; - try - { + try { var res = await httpClient.GetAsync(get_request_url); - if (res.IsSuccessStatusCode) - { - if(res.Content.Headers.ContentType.MediaType == "application/json") + if (res.IsSuccessStatusCode) { + if (res.Content.Headers.ContentType.MediaType == "application/json") get_request_result = (await res.Content.ReadFromJsonAsync()).ToJson(); else get_request_result = await res.Content.ReadAsStringAsync(); StateHasChanged(); return; } - if(res.Content.Headers.ContentType.MediaType == "application/json") + if (res.Content.Headers.ContentType.MediaType == "application/json") get_request_result = $"Error: {res.StatusCode}\n" + (await res.Content.ReadFromJsonAsync()).ToJson(); else get_request_result = $"Error: {res.StatusCode}\n" + await res.Content.ReadAsStringAsync(); - } - catch (Exception e) - { + catch (Exception e) { get_request_result = $"Error: {e}"; } StateHasChanged(); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/DevOptions.razor b/MatrixRoomUtils.Web/Pages/DevOptions.razor index 3ca86b4..0843d5f 100644 --- a/MatrixRoomUtils.Web/Pages/DevOptions.razor +++ b/MatrixRoomUtils.Web/Pages/DevOptions.razor @@ -19,15 +19,13 @@ View caches

Generic cache:

    - @foreach (var item in RuntimeCache.GenericResponseCache) - { + @foreach (var item in RuntimeCache.GenericResponseCache) {
  • @item.Key: @item.Value.Cache.Count entries
    - @if (item.Value.Cache.Count > 0) - { + @if (item.Value.Cache.Count > 0) {

    Earliest expiry: @(item.Value.Cache.Min(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Min(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)

    @*

    Average expiry: @(item.Value.Cache.Average(x => x.Value.ExpiryTime.Value))(@item.Value.Cache.Average(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now) from now)

    *@ -

    Last expiry: @(item.Value.Cache.Max(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Max(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)

    +

    Last expiry: @(item.Value.Cache.Max(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Max(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)

    }
  • } @@ -35,50 +33,42 @@ @code { - protected override async Task OnInitializedAsync() - { + + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - Task.Run(async () => - { - while (true) - { + Task.Run(async () => { + while (true) { await Task.Delay(1000); StateHasChanged(); } }); } - protected async Task LogStuff() - { + protected async Task LogStuff() { await Task.Delay(100); Console.WriteLine($"Settings: {LocalStorageWrapper.Settings.ToJson()}"); - + await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); } - protected async Task DropCaches() - { - foreach (var (key, value) in RuntimeCache.GenericResponseCache) - { + protected async Task DropCaches() { + foreach (var (key, value) in RuntimeCache.GenericResponseCache) { value.Cache.Clear(); } - - //RuntimeCache.GenericResponseCache.Clear(); + + //RuntimeCache.GenericResponseCache.Clear(); RuntimeCache.HomeserverResolutionCache.Clear(); await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); } - protected async Task RandomiseCacheTimers() - { - foreach (var keyValuePair in RuntimeCache.GenericResponseCache) - { + protected async Task RandomiseCacheTimers() { + foreach (var keyValuePair in RuntimeCache.GenericResponseCache) { Console.WriteLine($"Randomising cache timer for {keyValuePair.Key}"); - foreach (var cacheItem in keyValuePair.Value.Cache) - { + foreach (var cacheItem in keyValuePair.Value.Cache) { cacheItem.Value.ExpiryTime = DateTime.Now.AddSeconds(Random.Shared.Next(15, 120)); } - + await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); } } diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor index b77012b..f972236 100644 --- a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor +++ b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor @@ -2,6 +2,10 @@

    Homeserver Admininistration


    +

    Synapse tools

    +
    +Query rooms + @code { - + } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor index 109ad7d..e6f95c7 100644 --- a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor +++ b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor @@ -1,73 +1,78 @@ -@page "/RoomQuery" +@page "/HSAdmin/RoomQuery" @using MatrixRoomUtils.Core.Extensions -@using System.Runtime.InteropServices -@using System.ComponentModel @using MatrixRoomUtils.Core.Responses.Admin

    Homeserver Administration - Room Query

    -
    +
    -
    +

    -
    +

    -@foreach (var res in Results) -{ +@foreach (var res in Results) {
    - -

    @res.CanonicalAlias, created by

    + +

    + @res.CanonicalAlias + @if (!string.IsNullOrWhiteSpace(res.Creator)) { + + , created by + + } +

    @res.StateEvents state events

    @res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server

    } @code { - - [Parameter, SupplyParameterFromQuery(Name = "order_by")] + + [Parameter] + [SupplyParameterFromQuery(Name = "order_by")] public string? OrderBy { get; set; } - - [Parameter, SupplyParameterFromQuery(Name = "search_term")] + + [Parameter] + [SupplyParameterFromQuery(Name = "search_term")] public string SearchTerm { get; set; } - - [Parameter, SupplyParameterFromQuery(Name = "content_search_term")] + + [Parameter] + [SupplyParameterFromQuery(Name = "content_search_term")] public string ContentSearchTerm { get; set; } - - [Parameter, SupplyParameterFromQuery(Name = "ascending")] + + [Parameter] + [SupplyParameterFromQuery(Name = "ascending")] public bool Ascending { get; set; } public List Results { get; set; } = new(); - - protected override async Task OnParametersSetAsync() - { - if(Ascending == null) + + private string Status { get; set; } + + protected override async Task OnParametersSetAsync() { + if (Ascending == null) Ascending = true; OrderBy ??= "name"; } - - private async Task Search() - { + + private async Task Search() { Results.Clear(); var searchRooms = RuntimeCache.CurrentHomeServer.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, contentSearch: ContentSearchTerm).GetAsyncEnumerator(); - while (await searchRooms.MoveNextAsync()) - { + while (await searchRooms.MoveNextAsync()) { var room = searchRooms.Current; - Console.WriteLine("Hit: " + room.ToJson(indent: false)); + Console.WriteLine("Hit: " + room.ToJson(false)); Results.Add(room); } } - private Dictionary validOrderBy = new Dictionary() - { + private readonly Dictionary validOrderBy = new() { { "name", "Room name" }, { "canonical_alias", "Main alias address" }, { "joined_members", "Number of members (reversed)" }, @@ -80,7 +85,7 @@ { "join_rules", "Join rules" }, { "guest_access", "Guest access" }, { "history_visibility", "Visibility of history" }, - { "state_events", "Number of state events" }, + { "state_events", "Number of state events" } }; } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor index decdb0c..33cca61 100644 --- a/MatrixRoomUtils.Web/Pages/Index.razor +++ b/MatrixRoomUtils.Web/Pages/Index.razor @@ -12,18 +12,15 @@ Small collection of tools to do not-so-everyday things.
    Signed in accounts - Add new account

    - @foreach (var (token, user) in RuntimeCache.LoginSessions) - { + @foreach (var (token, user) in RuntimeCache.LoginSessions) { } @code { - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) - { + protected override async Task OnInitializedAsync() { + if (!RuntimeCache.WasLoaded) { Console.WriteLine("[INDEX] !!! LOCALSTORAGE WAS NOT LOADED !!!"); await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); } diff --git a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor index 13b717d..92a8445 100644 --- a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor +++ b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor @@ -5,33 +5,25 @@

    Known Homeserver List


    -@if (!IsFinished) -{ +@if (!IsFinished) {

    Loading... Please wait...

    @QueryProgress.ProcessedRooms / @QueryProgress.TotalRooms

    - @foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList()) - { - @if (state.Blocked) - { + @foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList()) { + @if (state.Blocked) {

    🔒 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...

    } - else if (state.Slowmode) - { - + else if (state.Slowmode) {

    🐢 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...

    } - else - { + else {

    @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...

    } } } -else -{ - @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) - { +else { + @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) {

    @server.Server - @server.KnownUserCount

    } } @@ -42,15 +34,12 @@ else bool IsFinished { get; set; } HomeServerInfoQueryProgress QueryProgress { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); var sw = Stopwatch.StartNew(); - HomeServers = await GetHomeservers(progressCallback: async progress => - { - if (sw.ElapsedMilliseconds > 1000) - { + HomeServers = await GetHomeservers(progressCallback: async progress => { + if (sw.ElapsedMilliseconds > 1000) { Console.WriteLine("Progress updated..."); QueryProgress = progress; StateHasChanged(); @@ -69,9 +58,7 @@ else await base.OnInitializedAsync(); } - - private async Task> GetHomeservers(int memberLimit = 1000, Func>? progressCallback = null) - { + private async Task> GetHomeservers(int memberLimit = 1000, Func>? progressCallback = null) { HomeServerInfoQueryProgress progress = new(); List homeServers = new(); var rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); @@ -79,16 +66,14 @@ else var semaphore = new SemaphoreSlim(4); var semLock = new SemaphoreSlim(1); - var tasks = rooms.Select(async room => - { + var tasks = rooms.Select(async room => { await semaphore.WaitAsync(); - progress.ProcessedUsers.Add(room, new()); + progress.ProcessedUsers.Add(room, new HomeServerInfoQueryProgress.State()); Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})"); var states = (await room.GetStateAsync("")).Value.Deserialize>(); states.RemoveAll(x => x.Type != "m.room.member" || x.Content.GetProperty("membership").GetString() != "join"); Console.WriteLine($"Room {room.RoomId} has {states.Count} members"); - if (states.Count > memberLimit) - { + if (states.Count > memberLimit) { Console.WriteLine("Skipping!"); semaphore.Release(); progress.ProcessedUsers.Remove(room); @@ -97,37 +82,31 @@ else } progress.ProcessedUsers[room].Total = states.Count; var updateInterval = progress.ProcessedUsers[room].Total >= 1000 ? 1000 : 100; - while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) - { + while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) { progress.ProcessedUsers[room].Blocked = true; await Task.Delay(1000); - // if(progressCallback != null) - // await progressCallback.Invoke(progress); + // if(progressCallback != null) + // await progressCallback.Invoke(progress); } progress.ProcessedUsers[room].Blocked = false; - int processedStates = 0; - foreach (var state in states) - { + var processedStates = 0; + foreach (var state in states) { await semLock.WaitAsync(); semLock.Release(); - if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) - { + if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) { progress.ProcessedUsers[room].Slowmode = true; await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100); } - else - { + else { progress.ProcessedUsers[room].Slowmode = false; } - if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) - { - homeServers.Add(new HomeServerInfo() { Server = state.StateKey.Split(':')[1] }); + if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) { + homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] }); } var hs = homeServers.First(x => x.Server == state.StateKey.Split(':')[1]); if (!hs.KnownUsers.Contains(state.StateKey.Split(':')[0])) hs.KnownUsers.Add(state.StateKey.Split(':')[0]); - if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback != null) - { + if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback != null) { await semLock.WaitAsync(); var _ = await progressCallback.Invoke(progress); semLock.Release(); @@ -148,29 +127,26 @@ else return homeServers; } - class HomeServerInfo - { + class HomeServerInfo { public string Server { get; set; } public int? KnownUserCount { get; set; } - public List KnownUsers { get; set; } = new(); + public List KnownUsers { get; } = new(); } - class HomeServerInfoQueryProgress - { + class HomeServerInfoQueryProgress { public int ProcessedRooms { get; set; } public int TotalRooms { get; set; } - public Dictionary ProcessedUsers { get; set; } = new(); + public Dictionary ProcessedUsers { get; } = new(); public List CurrentState { get; set; } = new(); - public class State - { + public class State { public int Processed { get; set; } public int Total { get; set; } public bool Blocked { get; set; } public bool Slowmode { get; set; } public float Progress => (float)Processed / Total; public bool IsFinished { get; set; } - public Stopwatch Timing { get; set; } = Stopwatch.StartNew(); + public Stopwatch Timing { get; } = Stopwatch.StartNew(); } } diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor index c46dc9a..16fdd24 100644 --- a/MatrixRoomUtils.Web/Pages/LoginPage.razor +++ b/MatrixRoomUtils.Web/Pages/LoginPage.razor @@ -1,6 +1,6 @@ @page "/Login" -@using System.Text.Json @using MatrixRoomUtils.Core.Authentication +@using System.Text.Json @using MatrixRoomUtils.Web.Shared.SimpleComponents @inject ILocalStorageService LocalStorage @inject IJSRuntime JsRuntime @@ -27,8 +27,7 @@

    Parsed records


    - @foreach (var (homeserver, username, password) in records) - { + @foreach (var (homeserver, username, password) in records) { @@ -41,19 +40,16 @@ @code { - List<(string homeserver, string username, string password)> records = new(); + readonly List<(string homeserver, string username, string password)> records = new(); (string homeserver, string username, string password) newRecordInput = ("", "", ""); - async Task Login() - { - foreach (var (homeserver, username, password) in records) - { + async Task Login() { + foreach (var (homeserver, username, password) in records) { if (RuntimeCache.LoginSessions.Any(x => x.Value.LoginResponse.UserId == $"@{username}:{homeserver}")) continue; var result = await MatrixAuth.Login(homeserver, username, password); Console.WriteLine($"Obtained access token for {result.UserId}!"); - var userinfo = new UserInfo() - { + var userinfo = new UserInfo { LoginResponse = result }; userinfo.Profile = await (await new AuthenticatedHomeServer(result.UserId, result.AccessToken, result.HomeServer).Configure()).GetProfile(result.UserId); @@ -66,18 +62,15 @@ await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); } - private async Task FileChanged(InputFileChangeEventArgs obj) - { - Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions() - { + private async Task FileChanged(InputFileChangeEventArgs obj) { + Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true })); await using var rs = obj.File.OpenReadStream(); using var sr = new StreamReader(rs); - string TsvData = await sr.ReadToEndAsync(); + var TsvData = await sr.ReadToEndAsync(); records.Clear(); - foreach (var line in TsvData.Split('\n')) - { + foreach (var line in TsvData.Split('\n')) { var parts = line.Split('\t'); if (parts.Length != 3) continue; @@ -85,8 +78,7 @@ } } - private void AddRecord() - { + private void AddRecord() { records.Add(newRecordInput); newRecordInput = ("", "", ""); } diff --git a/MatrixRoomUtils.Web/Pages/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/MediaLocator.razor index 19a0d4e..36cd8e6 100644 --- a/MatrixRoomUtils.Web/Pages/MediaLocator.razor +++ b/MatrixRoomUtils.Web/Pages/MediaLocator.razor @@ -7,8 +7,7 @@
    Checked homeserver list (@homeservers.Count entries)
      - @foreach (var hs in homeservers) - { + @foreach (var hs in homeservers) {
    • @hs
    • }
    @@ -16,26 +15,22 @@
    MXC URL: - + -@if (successResults.Count > 0) -{ +@if (successResults.Count > 0) {

    Successes

      - @foreach (var result in successResults) - { + @foreach (var result in successResults) {
    • @result
    • }
    } -@if (errorResults.Count > 0) -{ +@if (errorResults.Count > 0) {

    Errors

      - @foreach (var result in errorResults) - { + @foreach (var result in errorResults) {
    • @result
    • }
    @@ -44,15 +39,13 @@ @code { string mxcUrl { get; set; } - List successResults = new List(); - List errorResults = new List(); - List homeservers = new List(); + readonly List successResults = new(); + readonly List errorResults = new(); + readonly List homeservers = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - homeservers.AddRange(new [] - { + homeservers.AddRange(new[] { "matrix.org", "feline.support", "rory.gay", @@ -62,70 +55,58 @@ }); } - async Task executeSearch() - { + async Task executeSearch() { var sem = new SemaphoreSlim(128, 128); - homeservers.ForEach(async hs => - { + homeservers.ForEach(async hs => { await sem.WaitAsync(); var httpClient = new HttpClient { BaseAddress = new Uri(hs) }; httpClient.Timeout = TimeSpan.FromSeconds(5); var rmu = mxcUrl.Replace("mxc://", $"{hs}/_matrix/media/r0/download/"); - try - { + try { var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, rmu)); - if (res.IsSuccessStatusCode) - { + if (res.IsSuccessStatusCode) { successResults.Add($"{hs}: found - {res.Content.Headers.ContentLength} bytes"); StateHasChanged(); return; } errorResults.Add($"Error: {hs} - {res.StatusCode}\n" + await res.Content.ReadAsStringAsync()); } - catch (Exception e) - { + catch (Exception e) { errorResults.Add($"Error: {e}"); } - finally - { + finally { sem.Release(); } StateHasChanged(); }); } - - async Task addMoreHomeservers() - { + async Task addMoreHomeservers() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); var res = await Http.GetAsync("/homeservers.txt"); var content = await res.Content.ReadAsStringAsync(); homeservers.Clear(); var lines = content.Split("\n"); - + var rhs = new RemoteHomeServer("rory.gay"); var sem = new SemaphoreSlim(128, 128); - lines.ToList().ForEach(async line => - { + lines.ToList().ForEach(async line => { await sem.WaitAsync(); - try - { + try { homeservers.Add(await rhs.ResolveHomeserverFromWellKnown(line)); StateHasChanged(); - if(Random.Shared.Next(0,101) == 50) + if (Random.Shared.Next(0, 101) == 50) await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine(e); } - finally - { + finally { sem.Release(); } }); - StateHasChanged(); } + } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor index bf03ee3..b15928a 100644 --- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor @@ -1,7 +1,7 @@ @page "/PolicyListEditor/{RoomId}" -@using System.Text.Json @using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.StateEventTypes +@using System.Text.Json @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager

    Policy list editor - Editing @RoomId

    @@ -15,12 +15,10 @@ -@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.server")) -{ +@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.server")) {

    No server policies

    } -else -{ +else {

    Server policies


    @username @homeserver
    @@ -33,8 +31,7 @@ else - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.Content.Entity != null)) - { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.Content.Entity != null)) { @@ -59,23 +56,20 @@ else - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.Content.Entity == null)) - { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.Content.Entity == null)) { - + }
    Entity: @policyEvent.Content.Entity
    State: @policyEvent.StateKey
    @policyEvent.Content.Reason
    @policyEvent.StateKey@policyEvent.Content.ToJson(indent: false, ignoreNull: true)@policyEvent.Content.ToJson(false, true)
    } -@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.room")) -{ +@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.room")) {

    No room policies

    } -else -{ +else {

    Room policies


    @@ -88,8 +82,7 @@ else - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.Content.Entity != null)) - { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.Content.Entity != null)) { @@ -113,30 +106,26 @@ else - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.Content.Entity == null)) - { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.Content.Entity == null)) { - + }
    Entity: @policyEvent.Content.Entity
    State: @policyEvent.StateKey
    @policyEvent.Content.Reason
    @policyEvent.StateKey@policyEvent.Content.ToJson(indent: false, ignoreNull: true)@policyEvent.Content.ToJson(false, true)
    } -@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.user")) -{ +@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.user")) {

    No user policies

    } -else -{ +else {

    User policies


    - @if (_enableAvatars) - { + @if (_enableAvatars) { } @@ -146,12 +135,12 @@ else - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null)) - { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null)) { - @if (_enableAvatars) - { - + @if (_enableAvatars) { + } @@ -175,11 +164,10 @@ else - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity == null)) - { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity == null)) { - + } @@ -197,21 +185,19 @@ else [Parameter] public string? RoomId { get; set; } - - private bool _enableAvatars = false; - - static Dictionary avatars = new Dictionary(); - static Dictionary servers = new Dictionary(); + + private bool _enableAvatars; + + static readonly Dictionary avatars = new(); + static readonly Dictionary servers = new(); public static List> PolicyEvents { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null) - if (RuntimeCache.CurrentHomeServer == null) - { + // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null) + if (RuntimeCache.CurrentHomeServer == null) { NavigationManager.NavigateTo("/Login"); return; } @@ -220,18 +206,16 @@ else Console.WriteLine("Policy list editor initialized!"); } - private async Task LoadStatesAsync() - { - // using var client = new HttpClient(); - // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); - // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state"); - // var Content = await response.Content.ReadAsStringAsync(); - // Console.WriteLine(JsonSerializer.Deserialize(Content).ToJson()); - // var stateEvents = JsonSerializer.Deserialize>(Content); + private async Task LoadStatesAsync() { + // using var client = new HttpClient(); + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); + // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state"); + // var Content = await response.Content.ReadAsStringAsync(); + // Console.WriteLine(JsonSerializer.Deserialize(Content).ToJson()); + // var stateEvents = JsonSerializer.Deserialize>(Content); var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); var stateEventsQuery = await room.GetStateAsync(""); - if (stateEventsQuery == null) - { + if (stateEventsQuery == null) { Console.WriteLine("state events query is null!!!"); } var stateEvents = stateEventsQuery.Value.Deserialize>(); @@ -239,30 +223,25 @@ else .Select(x => JsonSerializer.Deserialize>(JsonSerializer.Serialize(x))).ToList(); StateHasChanged(); } - - private async Task GetAvatar(string userId) - { - try - { + + private async Task GetAvatar(string userId) { + try { if (avatars.ContainsKey(userId)) return; var hs = userId.Split(':')[1]; - RemoteHomeServer server = servers.ContainsKey(hs) ? servers[hs] : await new RemoteHomeServer(userId.Split(':')[1]).Configure(); + var server = servers.ContainsKey(hs) ? servers[hs] : await new RemoteHomeServer(userId.Split(':')[1]).Configure(); if (!servers.ContainsKey(hs)) servers.Add(hs, server); var profile = await server.GetProfile(userId); avatars.Add(userId, server.ResolveMediaUri(profile.AvatarUrl)); servers.Add(userId, server); StateHasChanged(); } - catch - { - // ignored + catch { + // ignored } } - - private async Task GetAllAvatars() - { - foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null)) - { + + private async Task GetAllAvatars() { + foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null)) { await GetAvatar(policyEvent.Content.Entity); } StateHasChanged(); diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor index e61598a..20eab7a 100644 --- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor +++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor @@ -1,29 +1,25 @@ @page "/PolicyListEditor" -@using System.Text.Json @using MatrixRoomUtils.Core.Extensions @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager

    Policy list editor - Room list


    -@if (PolicyRoomList.Count == 0) -{ +@if (PolicyRoomList.Count == 0) {

    No policy rooms found.

    Loading progress: @checkedRoomCount/@totalRoomCount

    } -else -{ - @if (checkedRoomCount != totalRoomCount) - { +else { + @if (checkedRoomCount != totalRoomCount) {

    Loading progress: @checkedRoomCount/@totalRoomCount

    } - foreach (var s in PolicyRoomList) - { - - -
    - Shortcode: @s.Shortcode -
    + foreach (var s in PolicyRoomList) { + + +
    + Shortcode: @s.Shortcode +
    +
    @* [@s.Shortcode] @s.Name (@s.RoomId) *@ @*
    *@ } @@ -40,15 +36,13 @@ else public List PolicyRoomList { get; set; } = new(); - private int checkedRoomCount { get; set; } = 0; - private int totalRoomCount { get; set; } = 0; + private int checkedRoomCount { get; set; } + private int totalRoomCount { get; set; } - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { + if (RuntimeCache.CurrentHomeServer == null) { NavigationManager.NavigateTo("/Login"); return; } @@ -56,59 +50,48 @@ else Console.WriteLine("Policy list editor initialized!"); } - private async Task EnumeratePolicyRooms() - { + private async Task EnumeratePolicyRooms() { var xxxrooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); totalRoomCount = xxxrooms.Count; StateHasChanged(); var xxxsemaphore = new SemaphoreSlim(1000); var xxxtasks = new List>(); - foreach (var room in xxxrooms) - { + foreach (var room in xxxrooms) { xxxtasks.Add(GetPolicyRoomInfo(room.RoomId, xxxsemaphore)); } var xxxresults = await Task.WhenAll(xxxtasks); PolicyRoomList.AddRange(xxxresults.Where(x => x != null).Select(x => x.Value)); Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); - return; } - private async Task GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) - { - try - { + private async Task GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) { + try { await semaphore.WaitAsync(); - PolicyRoomInfo roomInfo = new() - { + PolicyRoomInfo roomInfo = new() { RoomId = room }; var r = await RuntimeCache.CurrentHomeServer.GetRoom(room); var shortcodeState = await r.GetStateAsync("org.matrix.mjolnir.shortcode"); - if(!shortcodeState.HasValue) return null; - roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out JsonElement shortcode) ? shortcode.GetString() : null; + if (!shortcodeState.HasValue) return null; + roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out var shortcode) ? shortcode.GetString() : null; - if (roomInfo.Shortcode != null) - { + if (roomInfo.Shortcode != null) { roomInfo.Name = await r.GetNameAsync(); return roomInfo; } return null; } - finally - - { + finally { checkedRoomCount++; StateHasChanged(); semaphore.Release(); } } - public struct PolicyRoomInfo - - { + public struct PolicyRoomInfo { public string RoomId { get; set; } @@ -118,4 +101,5 @@ else public string? Name { get; set; } } - } \ No newline at end of file + +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor index 5daa97c..9b0bb88 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor @@ -3,12 +3,10 @@ @inject NavigationManager NavigationManager

    Room manager


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

    You are not in any rooms!

    } -else -{ +else {

    You are in @Rooms.Count rooms and @Spaces.Count spaces

    Create room @@ -16,8 +14,7 @@ else

    Space List - @foreach (var room in Spaces) - { + @foreach (var room in Spaces) { @@ -25,8 +22,7 @@ else
    Room List - @foreach (var room in Rooms) - { + @foreach (var room in Rooms) { @@ -41,15 +37,13 @@ else public List Rooms { get; set; } = new(); public List Spaces { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { Console.WriteLine("Initializing room manager"); await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); Console.WriteLine("Loaded from local storage"); await base.OnInitializedAsync(); Console.WriteLine("Initialized base"); - if (RuntimeCache.CurrentHomeServer == null) - { + if (RuntimeCache.CurrentHomeServer == null) { NavigationManager.NavigateTo("/Login"); return; } @@ -59,8 +53,7 @@ else Console.WriteLine($"Got {_rooms.Count} rooms"); var semaphore = new SemaphoreSlim(10); var tasks = new List>(); - foreach (var room in _rooms) - { + foreach (var room in _rooms) { tasks.Add(CheckIfSpace(room, semaphore)); } await Task.WhenAll(tasks); @@ -68,45 +61,36 @@ else Console.WriteLine("Fetched joined rooms!"); } - private async Task CheckIfSpace(Room room, SemaphoreSlim semaphore) - { + private async Task CheckIfSpace(Room room, SemaphoreSlim semaphore) { await semaphore.WaitAsync(); - // Console.WriteLine($"Checking if {room.RoomId} is a space"); - try - { + // Console.WriteLine($"Checking if {room.RoomId} is a space"); + try { var state = await room.GetStateAsync("m.room.create"); - if (state != null) - { + if (state != null) { //Console.WriteLine(state.Value.ToJson()); - if (state.Type != null) - { - if (state.Type == "m.space") - { + if (state.Type != null) { + if (state.Type == "m.space") { Console.WriteLine($"Room {room.RoomId} is a space!"); Spaces.Add(room); StateHasChanged(); return room; } - else - { + else { Console.WriteLine($"Encountered unknown room type {state.Type}"); } } - else - { + else { Rooms.Add(room); //this is fine, apprently... // Console.WriteLine($"Room {room.RoomId} has no Content.type in m.room.create!"); } } } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine(e); return null; } - finally - { + finally { semaphore.Release(); } return null; diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor index d561eb0..5cfda77 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor @@ -1,12 +1,13 @@ @page "/RoomManagerCreateRoom" -@using System.Text.Json @using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.Responses -@using MatrixRoomUtils.Web.Shared.SimpleComponents -@using System.Reflection -@using System.Runtime.Intrinsics.X86 +@using System.Text.Json @using System.Text.Json.Serialization +@using System.Reflection @using MatrixRoomUtils.Web.Classes.RoomCreationTemplates +@* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not *@ +@using MatrixRoomUtils.Web.Shared.SimpleComponents +

    Room Manager - Create Room

    @*
    @JsonString
    *@ @@ -20,15 +21,13 @@
    - @if (creationEvent != null) - { + @if (creationEvent != null) {
    @creationEvent.PowerLevelContentOverride.Users.Count members - @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) - { + @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) { var _event = user;
    @@ -99,8 +97,7 @@ } - @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) - { + @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) { var _user = user; @@ -131,8 +128,7 @@
    @creationEvent.InitialState.Count(x => x.Type == "m.room.member") members - @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId)) - { + @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId)) { }
    @@ -154,13 +150,11 @@ @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states
    User
    + + Entity: @string.Join("", policyEvent.Content.Entity.Take(64))
    State: @string.Join("", policyEvent.StateKey.Take(64))
    @policyEvent.Content.Reason
    @policyEvent.StateKey@policyEvent.Content.ToJson(indent: false, ignoreNull: true)@policyEvent.Content.ToJson(false, true)
    Preset: - @foreach (var createRoomRequest in Presets) - { + @foreach (var createRoomRequest in Presets) { }
    Room name: @@ -89,8 +88,7 @@ Permissions:
    :
    :
    - @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) - { + @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) {
    @(initialState.Type): - @if (!string.IsNullOrEmpty(initialState.StateKey)) - { + @if (!string.IsNullOrEmpty(initialState.StateKey)) {
    (@initialState.StateKey) } @@ -176,8 +170,7 @@
    @creationEvent.InitialState.Count initial states - @foreach (var initialState in creationEvent.InitialState) - { + @foreach (var initialState in creationEvent.InitialState) { var _state = initialState; - @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts)) - { + @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts)) {
    @@ -214,25 +207,21 @@ @code { - private string RoomPreset - { - get - { - if (Presets.ContainsValue(creationEvent)) - { + private string RoomPreset { + get { + if (Presets.ContainsValue(creationEvent)) { return Presets.First(x => x.Value == creationEvent).Key; } return "Not a preset"; } - set - { + set { creationEvent = Presets[value]; JsonChanged(); OverwriteWrappedPropertiesFromEvent(); creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); guestAccessEvent = creationEvent["m.room.guest_access"].As().Content; - + Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As().ToJson()}"); creationEvent["m.room.guest_access"].As().Content.IsGuestAccessEnabled = true; @@ -251,21 +240,18 @@ private Dictionary Presets { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); //creationEvent = Presets["Default room"] = - foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList()) - { + foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList()) { Console.WriteLine($"Found room creation template in class: {x.FullName}"); var instance = (IRoomCreationTemplate)Activator.CreateInstance(x); Presets[instance.Name] = instance.CreateRoomRequest; } Presets = Presets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - if (!Presets.ContainsKey("Default")) - { + if (!Presets.ContainsKey("Default")) { Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}"); } else RoomPreset = "Default"; @@ -273,30 +259,23 @@ await base.OnInitializedAsync(); } - private void JsonChanged() - { - Console.WriteLine(creationEvent.ToJson()); - } - + private void JsonChanged() => Console.WriteLine(creationEvent.ToJson()); //wrappers private List ServerACLAllowRules { get; set; } = new(); private List ServerACLDenyRules { get; set; } = new(); - private void OverwriteWrappedPropertiesFromEvent() - { + private void OverwriteWrappedPropertiesFromEvent() { Console.WriteLine("Overwriting wrapped properties from event"); ServerACLAllowRules = creationEvent.ServerACLs.Allow; ServerACLDenyRules = creationEvent.ServerACLs.Deny; } - private async Task OverwriteWrappedProperties() - { + private async Task OverwriteWrappedProperties() { Console.WriteLine("Overwriting wrapped properties"); Console.WriteLine($"Allow: {ServerACLAllowRules.Count}: {string.Join(", ", ServerACLAllowRules)}"); Console.WriteLine($"Deny: {ServerACLDenyRules.Count}: {string.Join(", ", ServerACLDenyRules)}"); - creationEvent.ServerACLs = new() - { + creationEvent.ServerACLs = new ServerACL { Allow = ServerACLAllowRules, Deny = ServerACLDenyRules, AllowIpLiterals = creationEvent.ServerACLs.AllowIpLiterals @@ -305,16 +284,14 @@ StateHasChanged(); } - private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) - { + private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { var res = await RuntimeCache.CurrentHomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); Console.WriteLine(res); creationEvent.RoomIcon = res; StateHasChanged(); } - private async Task CreateRoom() - { + private async Task CreateRoom() { Console.WriteLine("Create room"); Console.WriteLine(creationEvent.ToJson()); creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)"); @@ -323,22 +300,18 @@ // NavigationManager.NavigateTo($"/RoomManager/{id.RoomId.Replace('.','~')}"); } - private void InviteMember(string mxid) - { + private void InviteMember(string mxid) { if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && RuntimeCache.CurrentHomeServer.UserId != mxid) - creationEvent.InitialState.Add(new() - { + creationEvent.InitialState.Add(new StateEvent { Type = "m.room.member", StateKey = mxid, - Content = new - { + Content = new { membership = "invite", reason = "Automatically invited at room creation time." } }); } - private string GetStateFriendlyName(string key) => key switch { "m.room.history_visibility" => "History visibility", "m.room.guest_access" => "Guest access", @@ -363,19 +336,14 @@ _ => key }; - public class GuestAccessContent - { + public class GuestAccessContent { [JsonPropertyName("guest_access")] public string GuestAccess { get; set; } - public bool IsGuestAccessEnabled - { + public bool IsGuestAccessEnabled { get => GuestAccess == "can_join"; set => GuestAccess = value ? "can_join" : "forbidden"; } } - - } - diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor index 1e7e065..c5e1569 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor @@ -4,8 +4,7 @@

    Room manager - Viewing Space

    -@foreach (var room in Rooms) -{ +@foreach (var room in Rooms) { } @@ -13,8 +12,7 @@
    State list - @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) - { + @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) {

    @stateEvent.StateKey/@stateEvent.Type:

    @stateEvent.Content.ToJson()
    } @@ -24,61 +22,52 @@ [Parameter] public string RoomId { get; set; } = "invalid!!!!!!"; - + private Room? Room { get; set; } - + private StateEventResponse[] States { get; set; } = Array.Empty>(); - private List Rooms { get; set; } = new(); - private List ServersInSpace { get; set; } = new(); - - protected override async Task OnInitializedAsync() - { + private List Rooms { get; } = new(); + private List ServersInSpace { get; } = new(); + + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); Room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId.Replace('~', '.')); var state = await Room.GetStateAsync(""); - if (state != null) - { - // Console.WriteLine(state.Value.ToJson()); + if (state != null) { + // Console.WriteLine(state.Value.ToJson()); States = state.Value.Deserialize[]>()!; - - foreach (var stateEvent in States) - { - if (stateEvent.Type == "m.space.child") - { - // if (stateEvent.Content.ToJson().Length < 5) return; + + foreach (var stateEvent in States) { + if (stateEvent.Type == "m.space.child") { + // if (stateEvent.Content.ToJson().Length < 5) return; var roomId = stateEvent.StateKey; var room = await RuntimeCache.CurrentHomeServer.GetRoom(roomId); - if (room != null) - { + if (room != null) { Rooms.Add(room); } } - else if (stateEvent.Type == "m.room.member") - { + else if (stateEvent.Type == "m.room.member") { var serverName = stateEvent.StateKey.Split(':').Last(); - if (!ServersInSpace.Contains(serverName)) - { + if (!ServersInSpace.Contains(serverName)) { ServersInSpace.Add(serverName); } } } - - // if(state.Value.TryGetProperty("Type", out var Type)) - // { - // } - // else - // { - // //this is fine, apprently... - // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!"); - // } + + // if(state.Value.TryGetProperty("Type", out var Type)) + // { + // } + // else + // { + // //this is fine, apprently... + // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!"); + // } } await base.OnInitializedAsync(); } - - private async Task JoinAllRooms() - { - foreach (var room in Rooms) - { + + private async Task JoinAllRooms() { + foreach (var room in Rooms) { room.JoinAsync(ServersInSpace.ToArray()); } } diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor index a8a7fc2..2db7cab 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor @@ -1,12 +1,10 @@ @page "/RoomManager/Timeline/{RoomId}" @using MatrixRoomUtils.Web.Shared.TimelineComponents -@using MatrixRoomUtils.Core.Extensions

    RoomManagerTimeline


    Loaded @Events.Count events...

    -@foreach (var evt in Events) -{ +@foreach (var evt in Events) {
    @@ -17,18 +15,16 @@ [Parameter] public string RoomId { get; set; } = "invalid!!!!!!"; - private List Messages { get; set; } = new(); - private List Events { get; set; } = new(); + private List Messages { get; } = new(); + private List Events { get; } = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); RoomId = RoomId.Replace('~', '.'); Console.WriteLine("RoomId: " + RoomId); var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); MessagesResponse? msgs = null; - do - { + do { msgs = await room.GetMessagesAsync(limit: 250, from: msgs?.End, dir: "b"); Messages.Add(msgs); Console.WriteLine($"Got {msgs.Chunk.Count} messages"); @@ -37,13 +33,11 @@ StateHasChanged(); } while (msgs.End != null); - await base.OnInitializedAsync(); } private StateEventResponse GetProfileEventBefore(StateEventResponse Event) => Events.TakeWhile(x => x != Event).Last(e => e.Type == "m.room.member" && e.StateKey == Event.Sender); - private Type ComponentType(StateEventResponse Event) => Event.Type switch { "m.room.message" => typeof(TimelineMessageItem), "m.room.member" => typeof(TimelineMemberItem), diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor index 3037dcc..74f4f92 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor @@ -12,8 +12,7 @@
    - @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).Distinct().OrderBy(x => x)) - { + @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).Distinct().OrderBy(x => x)) { Console.WriteLine(stateEvent); } @@ -21,8 +20,7 @@
    - @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != shownStateKey).Select(x => x.Type).Distinct().OrderBy(x => x)) - { + @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != shownStateKey).Select(x => x.Type).Distinct().OrderBy(x => x)) { } @@ -45,12 +43,10 @@ public List Events { get; set; } = new(); public string status = ""; - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer != null) - { + if (RuntimeCache.CurrentHomeServer != null) { NavigationManager.NavigateTo("/Login"); return; } @@ -61,34 +57,29 @@ private DateTime _lastUpdate = DateTime.Now; - private async Task LoadStatesAsync() - { - int StateLoaded = 0; + private async Task LoadStatesAsync() { + var StateLoaded = 0; using var client = new HttpClient(); - //TODO: can this be improved? + //TODO: can this be improved? client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); //var _events = await response.Content.ReadFromJsonAsync>(); var _data = await response.Content.ReadAsStreamAsync(); var __events = JsonSerializer.DeserializeAsyncEnumerable(_data); - await foreach (var _ev in __events) - { - var e = new StateEventResponse() - { + await foreach (var _ev in __events) { + var e = new StateEventResponse { Type = _ev.Type, StateKey = _ev.StateKey, OriginServerTs = _ev.OriginServerTs, Content = _ev.Content }; Events.Add(e); - if (string.IsNullOrEmpty(e.StateKey)) - { + if (string.IsNullOrEmpty(e.StateKey)) { FilteredEvents.Add(e); } StateLoaded++; - if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) - { + if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) { _lastUpdate = DateTime.Now; status = $"Loaded {StateLoaded} state events"; StateHasChanged(); @@ -99,8 +90,7 @@ StateHasChanged(); } - private async Task RebuildFilteredData() - { + private async Task RebuildFilteredData() { status = "Rebuilding filtered data..."; StateHasChanged(); await Task.Delay(1); @@ -112,16 +102,14 @@ StateHasChanged(); await Task.Delay(1); FilteredEvents = _FilteredEvents; - - if(_shownType != null) + + if (_shownType != null) shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().Content.ToJson(indent: true, ignoreNull: true); - + StateHasChanged(); } - - public struct PreRenderedStateEvent - { + public struct PreRenderedStateEvent { public string content { get; set; } public long origin_server_ts { get; set; } public string state_key { get; set; } @@ -132,11 +120,9 @@ // public string ReplacesState { get; set; } } - public bool ShowMembershipEvents - { + public bool ShowMembershipEvents { get => _showMembershipEvents; - set - { + set { _showMembershipEvents = value; RebuildFilteredData(); } @@ -146,21 +132,17 @@ private string _shownStateKey; private string _shownType; - private string shownStateKey - { + private string shownStateKey { get => _shownStateKey; - set - { + set { _shownStateKey = value; RebuildFilteredData(); } } - private string shownType - { + private string shownType { get => _shownType; - set - { + set { _shownType = value; RebuildFilteredData(); } diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor index c654b13..82b5d75 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor @@ -3,16 +3,15 @@ @inject NavigationManager NavigationManager

    Room state viewer - Room list


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

    You are not in any rooms!

    @*

    Loading progress: @checkedRoomCount/@totalRoomCount

    *@ } -else -{ - @foreach (var room in Rooms) - { - +else { + @foreach (var room in Rooms) { + + + }
    } @@ -21,16 +20,16 @@ else @code { public List Rooms { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { + if (RuntimeCache.CurrentHomeServer == null) { NavigationManager.NavigateTo("/Login"); return; } - Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x=>x.RoomId).ToList(); + Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x => x.RoomId).ToList(); Console.WriteLine("Fetched joined rooms!"); } -} \ No newline at end of file + +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor index c7f9f3c..bfd4d10 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor @@ -1,7 +1,7 @@ @page "/RoomStateViewer/{RoomId}" +@using MatrixRoomUtils.Core.Extensions @using System.Net.Http.Headers @using System.Text.Json -@using MatrixRoomUtils.Core.Extensions @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager

    Room state viewer - Viewing @RoomId

    @@ -19,8 +19,7 @@
    @stateEvent.type @@ -31,8 +30,7 @@
    -@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != "")) -{ +@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != "")) {
    @group.Key @@ -43,8 +41,7 @@ - @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts)) - { + @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts)) {
    @stateEvent.type @@ -72,12 +69,10 @@ public List Events { get; set; } = new(); public string status = ""; - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { + if (RuntimeCache.CurrentHomeServer == null) { NavigationManager.NavigateTo("/Login"); return; } @@ -85,56 +80,50 @@ await LoadStatesAsync(); Console.WriteLine("Policy list editor initialized!"); } + private DateTime _lastUpdate = DateTime.Now; - private async Task LoadStatesAsync() - { - int StateLoaded = 0; - //TODO: can we improve this? + private async Task LoadStatesAsync() { + var StateLoaded = 0; + //TODO: can we improve this? using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); - // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); + var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); + // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); //var _events = await response.Content.ReadFromJsonAsync>(); var _data = await response.Content.ReadAsStreamAsync(); var __events = JsonSerializer.DeserializeAsyncEnumerable(_data); - await foreach (var _ev in __events) - { - var e = new PreRenderedStateEvent() - { + await foreach (var _ev in __events) { + var e = new PreRenderedStateEvent { type = _ev.type, state_key = _ev.state_key, origin_server_ts = _ev.origin_server_ts, - content = _ev.content.ToJson(indent: true, ignoreNull: true), + content = _ev.content.ToJson(true, true) }; Events.Add(e); - if (string.IsNullOrEmpty(e.state_key)) - { + if (string.IsNullOrEmpty(e.state_key)) { FilteredEvents.Add(e); } StateLoaded++; - if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) - { + if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) { _lastUpdate = DateTime.Now; status = $"Loaded {StateLoaded} state events"; StateHasChanged(); await Task.Delay(0); } - } StateHasChanged(); } - private async Task RebuildFilteredData() - { + private async Task RebuildFilteredData() { status = "Rebuilding filtered data..."; StateHasChanged(); await Task.Delay(1); var _FilteredEvents = Events; if (!ShowMembershipEvents) _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList(); - + status = "Done, rerendering!"; StateHasChanged(); await Task.Delay(1); @@ -142,9 +131,7 @@ StateHasChanged(); } - - public struct PreRenderedStateEvent - { + public struct PreRenderedStateEvent { public string content { get; set; } public long origin_server_ts { get; set; } public string state_key { get; set; } @@ -155,11 +142,9 @@ // public string ReplacesState { get; set; } } - public bool ShowMembershipEvents - { + public bool ShowMembershipEvents { get => _showMembershipEvents; - set - { + set { _showMembershipEvents = value; RebuildFilteredData(); } diff --git a/MatrixRoomUtils.Web/Program.cs b/MatrixRoomUtils.Web/Program.cs index 15c18f7..164f746 100644 --- a/MatrixRoomUtils.Web/Program.cs +++ b/MatrixRoomUtils.Web/Program.cs @@ -1,17 +1,16 @@ using System.Text.Json; using System.Text.Json.Serialization; using Blazored.LocalStorage; +using MatrixRoomUtils.Web; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using MatrixRoomUtils.Web; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); builder.RootComponents.Add("head::after"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); -builder.Services.AddBlazoredLocalStorage(config => -{ +builder.Services.AddBlazoredLocalStorage(config => { config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; config.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; config.JsonSerializerOptions.IgnoreReadOnlyProperties = true; diff --git a/MatrixRoomUtils.Web/Shared/EditablePre.razor b/MatrixRoomUtils.Web/Shared/EditablePre.razor index 01bea0d..e759015 100644 --- a/MatrixRoomUtils.Web/Shared/EditablePre.razor +++ b/MatrixRoomUtils.Web/Shared/EditablePre.razor @@ -1,8 +1,9 @@ @inherits InputBase
    @CurrentValue
    + @code { - protected override bool TryParseValueFromString(string? value, out string result, out string? validationErrorMessage) - { + + protected override bool TryParseValueFromString(string? value, out string result, out string? validationErrorMessage) { result = value; validationErrorMessage = null; return true; @@ -10,9 +11,6 @@ public object Id { get; set; } - private async Task Callback() - { - Console.WriteLine("beep"); - } + private async Task Callback() => Console.WriteLine("beep"); } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor index 03a7145..1fc70f2 100644 --- a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor +++ b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor @@ -1,10 +1,4 @@ -@using MatrixRoomUtils.Web.Classes -@using System.Text.Json -@using Blazored.LocalStorage -@using MatrixRoomUtils.Core @using MatrixRoomUtils.Core.Extensions -@using Index = MatrixRoomUtils.Web.Pages.Index -@using System.ComponentModel.DataAnnotations @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager @@ -27,30 +21,33 @@ private string? _avatarUrl { get; set; } private int _roomCount { get; set; } = 0; - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { var hs = await new AuthenticatedHomeServer(User.LoginResponse.UserId, User.AccessToken, User.LoginResponse.HomeServer).Configure(); if (User.Profile.AvatarUrl != null && User.Profile.AvatarUrl != "") _avatarUrl = hs.ResolveMediaUri(User.Profile.AvatarUrl); else _avatarUrl = "https://api.dicebear.com/6.x/identicon/svg?seed=" + User.LoginResponse.UserId; - _roomCount = (await hs.GetJoinedRooms()).Count; + try { + _roomCount = (await hs.GetJoinedRooms()).Count; + } + catch { + _roomCount = -1; + } await base.OnInitializedAsync(); } - private async Task RemoveUser() - { + private async Task RemoveUser() { Console.WriteLine(User.ToJson()); RuntimeCache.LoginSessions.Remove(User.AccessToken); StateHasChanged(); } - private async Task SetCurrent() - { + private async Task SetCurrent() { RuntimeCache.LastUsedToken = User.AccessToken; //RuntimeCache.CurrentHomeserver = await MatrixAuth.ResolveHomeserverFromWellKnown(LocalStorageWrapper.LoginSessions[Token].LoginResponse.HomeServer); await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); await LocalStorageWrapper.InitialiseRuntimeVariables(LocalStorage); StateHasChanged(); } + } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor index 56131c8..9833c62 100644 --- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor +++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor @@ -4,8 +4,7 @@ @ProfileName
    - @if (ChildContent != null) - { + @if (ChildContent != null) { @ChildContent }
    @@ -25,15 +24,13 @@ [Parameter] public string? ProfileAvatar { get; set; } = null; - + [Parameter] public string? ProfileName { get; set; } = null; - private static SemaphoreSlim _semaphoreSlim = new(128); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); @@ -41,19 +38,14 @@ var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.UserId, RuntimeCache.CurrentHomeServer.AccessToken, RuntimeCache.CurrentHomeServer.HomeServerDomain).Configure(); - if (User == null) - { - if (UserId == null) - { + if (User == null) { + if (UserId == null) { throw new ArgumentNullException(nameof(UserId)); } User = await hs.GetProfile(UserId); } - else - { - // UserId = User.; - } - + + // UserId = User.; ProfileAvatar ??= RuntimeCache.CurrentHomeServer.ResolveMediaUri(User.AvatarUrl); ProfileName ??= User.DisplayName; diff --git a/MatrixRoomUtils.Web/Shared/LogView.razor b/MatrixRoomUtils.Web/Shared/LogView.razor index 80fd355..2f83cb2 100644 --- a/MatrixRoomUtils.Web/Shared/LogView.razor +++ b/MatrixRoomUtils.Web/Shared/LogView.razor @@ -1,6 +1,5 @@ @using System.Text -@if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) -{ +@if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) { Logs
    @@ -10,11 +9,10 @@
     
     @code {
         StringBuilder _stringBuilder = new();
    -    protected override async Task OnInitializedAsync()
    -    {
    +
    +    protected override async Task OnInitializedAsync() {
             await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
    -        if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging)
    -        {
    +        if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) {
                 Console.WriteLine("Console logging disabled!");
                 var _sw = new StringWriter();
                 Console.SetOut(_sw);
    @@ -22,19 +20,16 @@
                 return;
             }
             if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) return;
    -        //intecept stdout with textwriter to get logs
    +    //intecept stdout with textwriter to get logs
             var sw = new StringWriter(_stringBuilder);
             Console.SetOut(sw);
             Console.SetError(sw);
    -        //keep updated
    -        int length = 0;
    -        Task.Run(async () =>
    -        {
    -            while (true)
    -            {
    +    //keep updated
    +        var length = 0;
    +        Task.Run(async () => {
    +            while (true) {
                     await Task.Delay(100);
    -                if (_stringBuilder.Length != length)
    -                {
    +                if (_stringBuilder.Length != length) {
                         StateHasChanged();
                         length = _stringBuilder.Length;
                     }
    @@ -43,4 +38,5 @@
             });
             await base.OnInitializedAsync();
         }
    +
     }
    \ No newline at end of file
    diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor b/MatrixRoomUtils.Web/Shared/MainLayout.razor
    index cdb1205..317f9e6 100644
    --- a/MatrixRoomUtils.Web/Shared/MainLayout.razor
    +++ b/MatrixRoomUtils.Web/Shared/MainLayout.razor
    @@ -11,8 +11,7 @@
                 
                 Git
                 Matrix
    -            @if (showDownload)
    -            {
    +            @if (showDownload) {
                     Download
                 }
             
    @@ -24,24 +23,22 @@
     
     
     @code {
    -    private bool showDownload { get; set; } = false;
    +    private bool showDownload { get; set; }
     
    -    protected override async Task OnInitializedAsync()
    -    {
    +    protected override async Task OnInitializedAsync() {
             using var hc = new HttpClient();
    -        var hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri));
    +        var hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri));
             showDownload = hr.StatusCode == HttpStatusCode.OK;
     
             await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
    -        if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging)
    -        {
    +        if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) {
                 Console.WriteLine("Console logging disabled!");
                 var sw = new StringWriter();
                 Console.SetOut(sw);
                 Console.SetError(sw);
             }
    -        
    -        await base.OnInitializedAsync();
     
    +        await base.OnInitializedAsync();
         }
    +
     }
    \ No newline at end of file
    diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor.css b/MatrixRoomUtils.Web/Shared/MainLayout.razor.css
    index c865427..01a5066 100644
    --- a/MatrixRoomUtils.Web/Shared/MainLayout.razor.css
    +++ b/MatrixRoomUtils.Web/Shared/MainLayout.razor.css
    @@ -21,20 +21,20 @@ main {
         align-items: center;
     }
     
    -    .top-row ::deep a, .top-row ::deep .btn-link {
    -        white-space: nowrap;
    -        margin-left: 1.5rem;
    -        text-decoration: none;
    -    }
    +.top-row ::deep a, .top-row ::deep .btn-link {
    +    white-space: nowrap;
    +    margin-left: 1.5rem;
    +    text-decoration: none;
    +}
     
    -    .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
    -        text-decoration: underline;
    -    }
    +.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
    +    text-decoration: underline;
    +}
     
    -    .top-row ::deep a:first-child {
    -        overflow: hidden;
    -        text-overflow: ellipsis;
    -    }
    +.top-row ::deep a:first-child {
    +    overflow: hidden;
    +    text-overflow: ellipsis;
    +}
     
     @media (max-width: 640.98px) {
         .top-row:not(.auth) {
    diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor
    index 44dd9df..9ddbd84 100644
    --- a/MatrixRoomUtils.Web/Shared/NavMenu.razor
    +++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor
    @@ -61,6 +61,13 @@
             @*          Media locator *@
             @*      *@
             @*  *@
    +
    +        
    +