MatrixRoomUtils

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | LICENSE

commit 383f7b633471dedf515907cb8a8752bc5885ae64
parent dc3201d641a03e051c6f0db07612eb6b0bb506c3
Author: TheArcaneBrony <myrainbowdash949@gmail.com>
Date:   Thu,  4 May 2023 20:34:16 +0200

Add room manager, profile caching

Diffstat:
M.gitignore | 2+-
A.idea/.idea.MatrixRoomUtils/.idea/vcs.xml | 7+++++++
MMatrixRoomUtils.Core/Authentication/MatrixAuth.cs | 1+
MMatrixRoomUtils.Core/Interfaces/IHomeServer.cs | 26++++++++++++++++++++++++++
MMatrixRoomUtils.Core/RemoteHomeServer.cs | 9++-------
MMatrixRoomUtils.Core/Room.cs | 3++-
MMatrixRoomUtils.Web/Pages/DataExportPage.razor | 4++--
AMatrixRoomUtils.Web/Pages/DebugTools.razor | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MMatrixRoomUtils.Web/Pages/Index.razor | 2+-
MMatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor | 40+---------------------------------------
AMatrixRoomUtils.Web/Pages/RoomManager.razor | 41+++++++++++++++++++++++++++++++++++++++++
MMatrixRoomUtils.Web/Shared/MainLayout.razor | 16+++++++++++++++-
MMatrixRoomUtils.Web/Shared/NavMenu.razor | 7++++++-
MMatrixRoomUtils.Web/Shared/RoomListItem.razor | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mdeploy.sh | 6++++++
15 files changed, 247 insertions(+), 53 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,4 @@ **/bin/ **/obj/ MatrixRoomUtils/ - +MatrixRoomUtils.Web/wwwroot/MRU.tar.xz diff --git a/.idea/.idea.MatrixRoomUtils/.idea/vcs.xml b/.idea/.idea.MatrixRoomUtils/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project> +\ No newline at end of file diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs @@ -39,6 +39,7 @@ public class MatrixAuth //return token; } + [Obsolete("Migrate to IHomeServer instance")] public static async Task<ProfileResponse> GetProfile(string homeserver, string mxid) => await (await new RemoteHomeServer(homeserver).Configure()).GetProfile(mxid); diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs @@ -1,11 +1,13 @@ using System.Net.Http.Json; using System.Text.Json; using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.Interfaces; public class IHomeServer { + private Dictionary<string, ProfileResponse?> _profileCache = new(); public string HomeServerDomain { get; set; } public string FullHomeServerDomain { get; set; } @@ -69,4 +71,28 @@ public class IHomeServer } throw new InvalidDataException($"Failed to resolve homeserver, not on {homeserver}, nor do client or server well-knowns exist!"); } + public async Task<ProfileResponse> 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()} within {_profileCache.Count} profiles..."); + await Task.Delay(Random.Shared.Next(50, 500)); + } + return _profileCache[mxid]; + } + } + + _profileCache.Add(mxid, null); + var resp = await _httpClient.GetAsync($"/_matrix/client/r0/profile/{mxid}"); + var data = await resp.Content.ReadFromJsonAsync<JsonElement>(); + if(!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data.ToString()); + var profile = data.Deserialize<ProfileResponse>(); + _profileCache[mxid] = profile; + return profile; + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs @@ -7,6 +7,8 @@ namespace MatrixRoomUtils.Core; public class RemoteHomeServer : IHomeServer { + + public RemoteHomeServer(string canonicalHomeServerDomain) { HomeServerDomain = canonicalHomeServerDomain; @@ -21,13 +23,6 @@ public class RemoteHomeServer : IHomeServer return this; } - public async Task<ProfileResponse> GetProfile(string mxid) - { - var resp = await _httpClient.GetAsync($"/_matrix/client/r0/profile/{mxid}"); - var data = await resp.Content.ReadFromJsonAsync<JsonElement>(); - if(!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data.ToString()); - return data.Deserialize<ProfileResponse>(); - } public async Task<Room> GetRoom(string roomId) { diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs @@ -33,10 +33,11 @@ public class Room var res = await GetStateAsync("m.room.name"); if (!res.HasValue) { + Console.WriteLine($"Room {RoomId} has no name!"); return null; } var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() : null; - Console.WriteLine($"Got name: {resn}"); + //Console.WriteLine($"Got name: {resn}"); return resn; } diff --git a/MatrixRoomUtils.Web/Pages/DataExportPage.razor b/MatrixRoomUtils.Web/Pages/DataExportPage.razor @@ -1,4 +1,4 @@ -@page "/export" +@page "/Export" @using MatrixRoomUtils.Web.Shared.IndexComponents @using System.Text.Json @inject NavigationManager NavigationManager @@ -6,7 +6,7 @@ <PageTitle>Export</PageTitle> -<h1>Data export</h1> +<h3>Data export</h3> <br/><br/> <h5>Signed in accounts - <a href="/Login">Add new account</a> or <a href="/ImportUsers">Import from TSV</a></h5> diff --git a/MatrixRoomUtils.Web/Pages/DebugTools.razor b/MatrixRoomUtils.Web/Pages/DebugTools.razor @@ -0,0 +1,87 @@ +@page "/Debug" +@using MatrixRoomUtils.Core.Interfaces +@using MatrixRoomUtils.Core.Extensions +@using System.Reflection +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +<h3>Debug Tools</h3> +<hr/> +@if (Rooms.Count == 0) +{ + <p>You are not in any rooms!</p> + @* <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> *@ +} +else +{ + <details> + <summary>Room List</summary> + @foreach (var room in Rooms) + { + <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')"><RoomListItem RoomId="@room"></RoomListItem></a> + } + </details> + +} + +<details open> + <summary>Send GET request to URL</summary> + <div class="input-group"> + <input type="text" class="form-control" @bind-value="get_request_url" placeholder="URL"> + <button class="btn btn-outline-secondary" type="button" @onclick="SendGetRequest">Send</button> + </div> + <br/> + <pre>@get_request_result</pre> +</details> + +<div style="margin-bottom: 4em;"></div> +<LogView></LogView> + +@code { + public List<string> Rooms { get; set; } = new(); + protected override async Task OnInitializedAsync() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + if (RuntimeCache.CurrentHomeServer == null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + 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() + { + var field = typeof(IHomeServer).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField")); + var httpClient = field.GetValue(RuntimeCache.CurrentHomeServer) as HttpClient; + try + { + var res = await httpClient.GetAsync(get_request_url); + if (res.IsSuccessStatusCode) + { + if(res.Content.Headers.ContentType.MediaType == "application/json") + get_request_result = (await res.Content.ReadFromJsonAsync<object>()).ToJson(); + else + get_request_result = await res.Content.ReadAsStringAsync(); + StateHasChanged(); + return; + } + if(res.Content.Headers.ContentType.MediaType == "application/json") + get_request_result = $"Error: {res.StatusCode}\n" + (await res.Content.ReadFromJsonAsync<object>()).ToJson(); + else + get_request_result = $"Error: {res.StatusCode}\n" + await res.Content.ReadAsStringAsync(); + + } + catch (Exception e) + { + get_request_result = $"Error: {e}"; + } + StateHasChanged(); + } + +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor @@ -5,7 +5,7 @@ <PageTitle>Index</PageTitle> -<h1>Rory&::MatrixUtils</h1> +<h3>Rory&::MatrixUtils</h3> Small collection of tools to do not-so-everyday things. <br/><br/> diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor @@ -57,7 +57,7 @@ else totalRoomCount = xxxrooms.Count; StateHasChanged(); - var xxxsemaphore = new SemaphoreSlim(256); + var xxxsemaphore = new SemaphoreSlim(1000); var xxxtasks = new List<Task<PolicyRoomInfo?>>(); foreach (var room in xxxrooms) { @@ -68,44 +68,6 @@ else Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); return; - /* - using HttpClient wc = new(); - wc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); - - - - //get room list - //temporary hack until rooms get enumerated... - string[] rooms = { "!fTjMjIzNKEsFlUIiru:neko.dev" }; - var _rooms = await wc.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/v3/joined_rooms"); - Console.WriteLine($"Got {_rooms.StatusCode}..."); - if (!_rooms.IsSuccessStatusCode) - { - Console.WriteLine($"Failed to get rooms: {await _rooms.Content.ReadAsStringAsync()}"); - return; - } - var _rooms_o = await _rooms.Content.ReadFromJsonAsync<JsonElement>(); - if (_rooms_o.TryGetProperty("joined_rooms", out JsonElement _rooms_j)) - { - rooms = _rooms_j.EnumerateArray().Select(x => x.GetString()).ToArray(); - } - - totalRoomCount = rooms.Length; - StateHasChanged(); - - var semaphore = new SemaphoreSlim(256); - var tasks = new List<Task<PolicyRoomInfo?>>(); - foreach (string room in rooms) - { - tasks.Add(GetPolicyRoomInfo(room, semaphore)); - } - var results = await Task.WhenAll(tasks); - PolicyRoomList.AddRange(results.Where(x => x != null).Select(x => x.Value)); - - - //print to console - Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); - */ } private async Task<PolicyRoomInfo?> GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) diff --git a/MatrixRoomUtils.Web/Pages/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager.razor @@ -0,0 +1,40 @@ +@page "/RoomManager" +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +<h3>Room manager</h3> +<hr/> +@if (Rooms.Count == 0) +{ + <p>You are not in any rooms!</p> + @* <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> *@ +} +else +{ + <details open> + <summary>Room List</summary> + @foreach (var room in Rooms) + { + <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')"><RoomListItem RoomId="@room" ShowOwnProfile="true"></RoomListItem></a> + } + </details> + +} + +<div style="margin-bottom: 4em;"></div> +<LogView></LogView> + +@code { + public List<string> Rooms { get; set; } = new(); + protected override async Task OnInitializedAsync() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + if (RuntimeCache.CurrentHomeServer == null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x=>x.RoomId).ToList(); + Console.WriteLine("Fetched joined rooms!"); + } +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor b/MatrixRoomUtils.Web/Shared/MainLayout.razor @@ -1,4 +1,6 @@ -@inherits LayoutComponentBase +@using MatrixRoomUtils.Core.Extensions +@using System.Net +@inherits LayoutComponentBase <div class="page"> <div class="sidebar"> @@ -9,6 +11,10 @@ <div class="top-row px-4"> <a href="https://git.rory.gay/MatrixRoomUtils.git/" target="_blank">Git</a> <a href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support" target="_blank">Matrix</a> + @if (showDownload) + { + <a href="/MRU.tar.xz" target="_blank">Download</a> + } </div> <article class="content px-4"> @@ -18,6 +24,14 @@ </div> @code { + private bool showDownload { get; set; } = false; + protected override async Task OnInitializedAsync() + { + using var hc = new HttpClient(); + var hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU.tar.xz").AbsoluteUri)); + showDownload = hr.StatusCode == HttpStatusCode.OK; + await base.OnInitializedAsync(); + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor @@ -15,11 +15,16 @@ </NavLink> </div> <div class="nav-item px-3"> - <NavLink class="nav-link" href="export"> + <NavLink class="nav-link" href="Export"> <span class="oi oi-plus" aria-hidden="true"></span> Export data </NavLink> </div> <div class="nav-item px-3"> + <NavLink class="nav-link" href="RoomManager"> + <span class="oi oi-plus" aria-hidden="true"></span> Manage Rooms + </NavLink> + </div> + <div class="nav-item px-3"> <NavLink class="nav-link" href="PolicyListEditor"> <span class="oi oi-plus" aria-hidden="true"></span> Policy list editor </NavLink> diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor @@ -1,17 +1,35 @@ +@using MatrixRoomUtils.Core.Authentication +@using System.Text.Json <div style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-content;"> + @if (ShowOwnProfile) + { + <img style="width: 32px; height: 32px; border-radius: 50%; @(hasCustomProfileAvatar ? "border-color: red; border-width: 3px; border-style: dashed;" : "")" src="@profileAvatar"/> + <span style="vertical-align: middle; margin-right: 8px; border-radius: 75px; @(hasCustomProfileName ? "background-color: red;" : "")">@profileName</span> + <span style="vertical-align: middle; padding-right: 8px; padding-left: 0px;">-></span> + } <img style="width: 32px; height: 32px; border-radius: 50%;" src="@roomIcon"/> <span style="vertical-align: middle; padding-right: 8px;">@roomName</span> </div> @code { + [Parameter] public Room Room { get; set; } + [Parameter] public string RoomId { get; set; } + [Parameter] + public bool ShowOwnProfile { get; set; } = false; + private string roomName { get; set; } = "Loading..."; private string roomIcon { get; set; } = "/icon-192.png"; + private string profileAvatar { get; set; } = "/icon-192.png"; + private string profileName { get; set; } = "Loading..."; + private bool hasCustomProfileAvatar { get; set; } = false; + private bool hasCustomProfileName { get; set; } = false; + protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); @@ -40,6 +58,36 @@ } } + if (ShowOwnProfile) + { + var profile = await RuntimeCache.CurrentHomeServer.GetProfile(RuntimeCache.CurrentHomeServer.UserId, debounce: true); + + var memberState = await Room.GetStateAsync("m.room.member", RuntimeCache.CurrentHomeServer.UserId); + if (memberState.HasValue) + { + memberState.Value.TryGetProperty("avatar_url", out var _avatar); + if (_avatar.ValueKind == JsonValueKind.String) + { + hasCustomProfileAvatar = _avatar.GetString() != profile.AvatarUrl; + profileAvatar = await RuntimeCache.CurrentHomeServer.ResolveMediaUri(_avatar.GetString()); + } + else + { + profileAvatar = "/icon-192.png"; + } + memberState.Value.TryGetProperty("displayname", out var _name); + if (_name.ValueKind == JsonValueKind.String) + { + hasCustomProfileName = _name.GetString() != profile.DisplayName; + profileName = _name.GetString(); + Console.WriteLine($"{profile.DisplayName} - {_name.GetString()}: {hasCustomProfileName}"); + } + else + { + profileName = "Unnamed user"; + } + } + } } } \ No newline at end of file diff --git a/deploy.sh b/deploy.sh @@ -1,4 +1,10 @@ #!/bin/sh +BASE_DIR=`pwd` +rm -rf **/bin/Release cd MatrixRoomUtils.Web dotnet publish -c Release rsync -raP bin/Release/net7.0/publish/wwwroot/ rory.gay:/data/nginx/html_mru/ +cd bin/Release/net7.0/publish/wwwroot +tar cf - ./ | xz -z -9 - > $BASE_DIR/MRU.tar.xz +rsync -raP $BASE_DIR/MRU.tar.xz rory.gay:/data/nginx/html_mru/MRU.tar.xz +rm -rf $BASE_DIR/MRU.tar.xz