MatrixRoomUtils

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

commit 3ef3e5caa65458e595e2303358322626c7da1dda
parent 60d713b82fcc6b17858dd4975601ee67177a9313
Author: TheArcaneBrony <myrainbowdash949@gmail.com>
Date:   Tue, 23 May 2023 08:51:02 +0200

Add numerous new things

Diffstat:
MMatrixRoomUtils.Core/AuthenticatedHomeServer.cs | 27++++++++++++++++++++++-----
MMatrixRoomUtils.Core/Interfaces/IHomeServer.cs | 17+++++++++++++++++
MMatrixRoomUtils.Core/RemoteHomeServer.cs | 3++-
MMatrixRoomUtils.Core/Room.cs | 70++++++++++++++++++++++++++++++++++++++++++++++++----------------------
MMatrixRoomUtils.Core/RuntimeCache.cs | 45++++++++++++++++++++++++++++++---------------
MMatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs | 16++++++++++++++--
MMatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj | 4++++
MMatrixRoomUtils.Web/Pages/About.razor | 1-
MMatrixRoomUtils.Web/Pages/DevOptions.razor | 57++++++++++++++++++++++++++++++++++++++++++++++++++++++---
AMatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor | 8++++++++
AMatrixRoomUtils.Web/Pages/KnownHomeserverList.razor | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MMatrixRoomUtils.Web/Pages/MediaLocator.razor | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
MMatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor | 3++-
MMatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor | 13+++++++------
DMatrixRoomUtils.Web/Pages/RoomManager.razor | 41-----------------------------------------
AMatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MMatrixRoomUtils.Web/Properties/launchSettings.json | 4++--
MMatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor | 24+++++++++++++++++-------
MMatrixRoomUtils.Web/Shared/LogView.razor | 26++++++++++++++++++++------
MMatrixRoomUtils.Web/Shared/MainLayout.razor | 14++++++++++++--
MMatrixRoomUtils.Web/Shared/NavMenu.razor | 9+++++++--
AMatrixRoomUtils.Web/Shared/PortableDevTools.razor | 32++++++++++++++++++++++++++++++++
MMatrixRoomUtils.Web/Shared/RoomListItem.razor | 33++++++++++++++++++++++++---------
Mdeploy.sh | 2+-
25 files changed, 685 insertions(+), 138 deletions(-)

diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs @@ -9,12 +9,14 @@ public class AuthenticatedHomeServer : IHomeServer { public string UserId { get; set; } public string AccessToken { get; set; } + public readonly HomeserverAdminApi Admin; public AuthenticatedHomeServer(string userId, string accessToken, string canonicalHomeServerDomain) { UserId = userId; AccessToken = accessToken; HomeServerDomain = canonicalHomeServerDomain; + Admin = new HomeserverAdminApi(this); _httpClient = new HttpClient(); } @@ -56,9 +58,25 @@ public class AuthenticatedHomeServer : IHomeServer return rooms; } - - public async Task<string> ResolveMediaUri(string mxc) + + + + + + public class HomeserverAdminApi { - return mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/"); + private readonly AuthenticatedHomeServer _authenticatedHomeServer; + + public HomeserverAdminApi(AuthenticatedHomeServer authenticatedHomeServer) + { + _authenticatedHomeServer = authenticatedHomeServer; + } + + + + + + + } -} -\ No newline at end of file +} diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs @@ -15,6 +15,18 @@ public class IHomeServer public async Task<string> 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); + return res; + } + private async Task<string> _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)) @@ -22,6 +34,7 @@ public class IHomeServer 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); } //throw new NotImplementedException(); @@ -95,4 +108,8 @@ public class IHomeServer _profileCache[mxid] = profile; return profile; } + public async Task<string> ResolveMediaUri(string mxc) + { + return mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/"); + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs @@ -1,7 +1,6 @@ using System.Net.Http.Json; using System.Text.Json; using MatrixRoomUtils.Core.Interfaces; -using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core; @@ -13,12 +12,14 @@ public class RemoteHomeServer : IHomeServer { HomeServerDomain = canonicalHomeServerDomain; _httpClient = new HttpClient(); + _httpClient.Timeout = TimeSpan.FromSeconds(5); } public async Task<RemoteHomeServer> Configure() { FullHomeServerDomain = await ResolveHomeserverFromWellKnown(HomeServerDomain); _httpClient.Dispose(); _httpClient = new HttpClient { BaseAddress = new Uri(FullHomeServerDomain) }; + _httpClient.Timeout = TimeSpan.FromSeconds(5); Console.WriteLine("[RHS] Finished setting up http client"); return this; diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs @@ -1,10 +1,13 @@ using System.Net.Http.Json; using System.Text.Json; +using System.Web; namespace MatrixRoomUtils.Core; public class Room { + private static SemaphoreSlim _semaphore = new SemaphoreSlim(16, 16); + private readonly HttpClient _httpClient; public string RoomId { get; set; } @@ -13,31 +16,34 @@ public class Room _httpClient = httpClient; RoomId = roomId; } - - public async Task<JsonElement?> GetStateAsync(string type, string state_key="", bool logOnFailure = false) + + public async Task<JsonElement?> GetStateAsync(string type, string state_key = "", bool logOnFailure = false) { + await _semaphore.WaitAsync(); var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; if (!string.IsNullOrEmpty(state_key)) url += $"/{type}/{state_key}"; else if (!string.IsNullOrEmpty(type)) url += $"/{type}"; - var cache_key = "room_states_"+type; + var cache_key = "room_states:" + type; if (!RuntimeCache.GenericResponseCache.ContainsKey(cache_key)) { Console.WriteLine($"[!!] No cache for {cache_key}, creating..."); - RuntimeCache.GenericResponseCache.Add(cache_key, new ObjectCache<object?>() - { - DefaultExpiry = type switch - { - "m.room.name" => TimeSpan.FromMinutes(15), - _ => TimeSpan.FromMinutes(5) - } - }); + RuntimeCache.GenericResponseCache.Add(cache_key, new ObjectCache<object?>()); } - if (RuntimeCache.GenericResponseCache[cache_key][url] != null) + RuntimeCache.GenericResponseCache[cache_key].DefaultExpiry = type switch + { + "m.room.name" => TimeSpan.FromMinutes(30), + "org.matrix.mjolnir.shortcode" => TimeSpan.FromHours(4), + "" => TimeSpan.FromSeconds(0), + _ => TimeSpan.FromMinutes(15) + }; + + if (RuntimeCache.GenericResponseCache[cache_key].Cache.ContainsKey(url) && RuntimeCache.GenericResponseCache[cache_key][url] != null) { - if(RuntimeCache.GenericResponseCache[cache_key][url].ExpiryTime > DateTime.Now) + if (RuntimeCache.GenericResponseCache[cache_key][url].ExpiryTime > DateTime.Now) { // Console.WriteLine($"[:3] Found cached state: {RuntimeCache.GenericResponseCache[cache_key][url].Result}"); + _semaphore.Release(); return (JsonElement?)RuntimeCache.GenericResponseCache[cache_key][url].Result; } else @@ -45,35 +51,55 @@ public class Room Console.WriteLine($"[!!] Cached state expired at {RuntimeCache.GenericResponseCache[cache_key][url].ExpiryTime}: {RuntimeCache.GenericResponseCache[cache_key][url].Result}"); } } - else - { - Console.WriteLine($"[!!] No cached state for {url}"); - } + // else + // { + // Console.WriteLine($"[!!] No cached state for {url}"); + // } var res = await _httpClient.GetAsync(url); if (!res.IsSuccessStatusCode) { - if(logOnFailure) Console.WriteLine($"{RoomId}/{state_key}/{type} - got status: {res.StatusCode}"); + if (logOnFailure) Console.WriteLine($"{RoomId}/{state_key}/{type} - got status: {res.StatusCode}"); + _semaphore.Release(); return null; } + var result = await res.Content.ReadFromJsonAsync<JsonElement>(); + + if (!RuntimeCache.GenericResponseCache.ContainsKey(cache_key) && type != "") + { + Console.WriteLine($"[!!] No cache for {cache_key}, creating..."); + RuntimeCache.GenericResponseCache.Add(cache_key, new ObjectCache<object?>()); + } + RuntimeCache.GenericResponseCache[cache_key][url] = new GenericResult<object>() { Result = result }; + _semaphore.Release(); return result; } - public async Task<string?> GetNameAsync() + + public async Task<string> GetNameAsync() { var res = await GetStateAsync("m.room.name"); if (!res.HasValue) { Console.WriteLine($"Room {RoomId} has no name!"); - return null; + return RoomId; } - var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() : null; + + var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() ?? RoomId : RoomId; //Console.WriteLine($"Got name: {resn}"); return resn; } - + + public async Task JoinAsync(string[]? homeservers = null) + { + string join_url = $"/_matrix/client/r0/join/{HttpUtility.UrlEncode(RoomId)}"; + Console.WriteLine($"Calling {join_url} with {(homeservers == null ? 0 : homeservers.Length)} via's..."); + if(homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':')[1] }; + var full_join_url = $"{join_url}?server_name=" + string.Join("&server_name=", homeservers); + var res = await _httpClient.PostAsync(full_join_url, null); + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RuntimeCache.cs b/MatrixRoomUtils.Core/RuntimeCache.cs @@ -1,6 +1,3 @@ -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.JavaScript; -using System.Xml.Schema; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Responses; @@ -40,17 +37,6 @@ public class ObjectCache<T> where T : class { get { - if (Random.Shared.Next(100) == 1) - { - // Console.WriteLine("Cleaning cache..."); - // foreach (var x in Cache.Where(x => x.Value.ExpiryTime < DateTime.Now).OrderBy(x => x.Value.ExpiryTime).Take(3).ToList()) - // { - // Console.WriteLine($"Removing {x.Key} from cache"); - // Cache.Remove(x.Key); - // } - } - - if (Cache.ContainsKey(key)) { // Console.WriteLine($"Found item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}"); @@ -67,19 +53,48 @@ public class ObjectCache<T> where T : class Console.WriteLine($"Failed to remove {key} from cache: {e.Message}"); } } + Console.WriteLine($"No item in cache: {key}"); return null; } set { Cache[key] = value; if(Cache[key].ExpiryTime == null) Cache[key].ExpiryTime = DateTime.Now.Add(DefaultExpiry); - Console.WriteLine($"New item in cache: {key} - {Cache[key].Result.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 (true) + { + 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); + } + } + }); + } } public class GenericResult<T> { public T? Result { get; set; } public DateTime? ExpiryTime { get; set; } + + public GenericResult() + { + //expiry timer + + } + public GenericResult(T? result, DateTime? expiryTime = null) : this() + { + Result = result; + ExpiryTime = expiryTime; + } } diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs @@ -1,6 +1,5 @@ using Blazored.LocalStorage; using MatrixRoomUtils.Core; -using MatrixRoomUtils.Core.Extensions; namespace MatrixRoomUtils.Web.Classes; @@ -27,7 +26,7 @@ public partial class LocalStorageWrapper { Console.WriteLine($"Access token is not null, creating authenticated home server"); Console.WriteLine($"Homeserver cache: {RuntimeCache.HomeserverResolutionCache.Count} entries"); - Console.WriteLine(RuntimeCache.HomeserverResolutionCache.ToJson()); + // Console.WriteLine(RuntimeCache.HomeserverResolutionCache.ToJson()); RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.UserId, RuntimeCache.LastUsedToken, RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.HomeServer).Configure(); Console.WriteLine("Created authenticated home server"); } @@ -47,6 +46,17 @@ public partial class LocalStorageWrapper .ToDictionary(x => x.Key, x => x.Value)); await localStorage.SetItemAsync("rory.matrixroomutils.generic_cache", RuntimeCache.GenericResponseCache); } + public static async Task SaveFieldToLocalStorage(ILocalStorageService localStorage, string key) + { + if (key == "rory.matrixroomutils.settings") await localStorage.SetItemAsync(key, Settings); + // if (key == "rory.matrixroomutils.token") await localStorage.SetItemAsStringAsync(key, RuntimeCache.AccessToken); + // if (key == "rory.matrixroomutils.current_homeserver") await localStorage.SetItemAsync(key, RuntimeCache.CurrentHomeserver); + if (key == "rory.matrixroomutils.user_cache") await localStorage.SetItemAsync(key, RuntimeCache.LoginSessions); + if (key == "rory.matrixroomutils.last_used_token") await localStorage.SetItemAsync(key, RuntimeCache.LastUsedToken); + if (key == "rory.matrixroomutils.homeserver_resolution_cache") await localStorage.SetItemAsync(key, RuntimeCache.HomeserverResolutionCache); + if (key == "rory.matrixroomutils.generic_cache") await localStorage.SetItemAsync(key, RuntimeCache.GenericResponseCache); + + } } @@ -59,4 +69,6 @@ public class Settings public class DeveloperSettings { public bool EnableLogViewers { get; set; } = false; + public bool EnableConsoleLogging { get; set; } = true; + public bool EnablePortableDevtools { get; set; } = false; } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj @@ -20,4 +20,8 @@ <_ContentIncludedByDefault Remove="wwwroot\sample-data\weather.json" /> </ItemGroup> + <ItemGroup> + <None Include="wwwroot\homeservers.txt" /> + </ItemGroup> + </Project> diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor @@ -1,5 +1,4 @@ @page "/About" -@using MatrixRoomUtils.Web.Shared.IndexComponents @using System.Net @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage diff --git a/MatrixRoomUtils.Web/Pages/DevOptions.razor b/MatrixRoomUtils.Web/Pages/DevOptions.razor @@ -1,6 +1,4 @@ @page "/DevOptions" -@using MatrixRoomUtils.Web.Shared.IndexComponents -@using System.Net @using MatrixRoomUtils.Core.Extensions @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage @@ -10,8 +8,32 @@ <h3>Rory&::MatrixUtils - Developer options</h3> <hr/> -<InputCheckbox @bind-Value="@LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers" @oninput="@LogStuff"></InputCheckbox><label> Enable log views</label> +<InputCheckbox @bind-Value="@LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers" @oninput="@LogStuff"></InputCheckbox><label> Enable log views</label><br/> +<InputCheckbox @bind-Value="@LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging" @oninput="@LogStuff"></InputCheckbox><label> Enable console logging</label><br/> +<InputCheckbox @bind-Value="@LocalStorageWrapper.Settings.DeveloperSettings.EnablePortableDevtools" @oninput="@LogStuff"></InputCheckbox><label> Enable portable devtools</label><br/> +<button @onclick="@DropCaches">Drop caches</button> +<button @onclick="@RandomiseCacheTimers">Randomise cache timers</button> +<br/> +<details open> + <summary>View caches</summary> + <p>Generic cache:</p> + <ul> + @foreach (var item in RuntimeCache.GenericResponseCache) + { + <li> + @item.Key: @item.Value.Cache.Count entries<br/> + Default expiry: @item.Value.DefaultExpiry<br/> + @if (item.Value.Cache.Count > 0) + { + <p>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)</p> + @* <p>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)</p> *@ + <p>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)</p> + } + </li> + } + </ul> +</details> @code { protected override async Task OnInitializedAsync() @@ -19,6 +41,14 @@ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); + Task.Run(async () => + { + while (true) + { + await Task.Delay(100); + StateHasChanged(); + } + }); } protected async Task LogStuff() @@ -29,4 +59,25 @@ await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); } + protected async Task DropCaches() + { + RuntimeCache.GenericResponseCache.Clear(); + RuntimeCache.HomeserverResolutionCache.Clear(); + await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); + } + + 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) + { + cacheItem.Value.ExpiryTime = DateTime.Now.AddSeconds(Random.Shared.Next(15, 120)); + } + + await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); + } + } + } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor @@ -0,0 +1,7 @@ +@page "/HSAdmin" +<h3>Homeserver Admininistration</h3> +<hr/> + +@code { + +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor @@ -0,0 +1,79 @@ +@page "/KnownHomeserverList" +@using System.Text.Json +@using MatrixRoomUtils.Core.Extensions +<h3>Known Homeserver List</h3> +<hr/> + +@if (!IsFinished) +{ + <p>Loading... Please wait...</p> +} +else +{ + @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) + { + <p>@server.Server - @server.KnownUserCount</p> + } +} +<hr/> + +@code { + List<HomeServerInfo> HomeServers = new(); + bool IsFinished { get; set; } + + protected override async Task OnInitializedAsync() + { + await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + + HomeServers = await GetHomeservers(); + + IsFinished = true; + StateHasChanged(); + Console.WriteLine("Rerendered!"); + await base.OnInitializedAsync(); + } + + + private async Task<List<HomeServerInfo>> GetHomeservers() + { + List<HomeServerInfo> homeServers = new(); + var rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); + // Dictionary<string, StateEvent> roomMembers = new(); + //start a task for each room + var tasks = rooms.Select(async room => + { + Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})"); + StateHasChanged(); + + var states = (await room.GetStateAsync("")).Value.Deserialize<List<StateEvent>>(); + states.RemoveAll(x => x.type != "m.room.member"); + Console.WriteLine($"Room {room.RoomId} has {states.Count} members"); + foreach (var state in states) + { + if (!homeServers.Any(x => x.Server == state.state_key.Split(':')[1])) + { + homeServers.Add(new HomeServerInfo() { Server = state.state_key.Split(':')[1] }); + } + var hs = homeServers.First(x => x.Server == state.state_key.Split(':')[1]); + if(!hs.KnownUsers.Contains(state.state_key.Split(':')[0])) + hs.KnownUsers.Add(state.state_key.Split(':')[0]); + } + Console.WriteLine("Collected states!"); + }); + await Task.WhenAll(tasks); + + Console.WriteLine("Calculating member counts..."); + homeServers.ForEach(x => x.KnownUserCount = x.KnownUsers.Count); + Console.WriteLine(homeServers.First(x=>x.Server=="rory.gay").ToJson()); + Console.WriteLine("Recalculated!"); + return homeServers; + } + + class HomeServerInfo + { + public string Server { get; set; } + public int? KnownUserCount { get; set; } + public List<string> KnownUsers { get; set; } = new(); + } + +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/MediaLocator.razor @@ -1,11 +1,46 @@ @page "/MediaLocator" -<h3>MediaLocator</h3> +@inject HttpClient Http +<h3>Media locator</h3> <hr/> +<b>This is going to expose your IP address to all these homeservers!</b> +<details> + <summary>Checked homeserver list (@homeservers.Count entries)</summary> + <ul> + @foreach (var hs in homeservers) + { + <li>@hs</li> + } + </ul> +</details> +<button @onclick="addMoreHomeservers">Add more homeservers</button> +<br/> <span>MXC URL: </span> <input type="text" @bind="mxcUrl" /> <button @onclick="executeSearch">Search</button> +@if (successResults.Count > 0) +{ + <h4>Successes</h4> + <ul> + @foreach (var result in successResults) + { + <li>@result</li> + } + </ul> +} + +@if (errorResults.Count > 0) +{ + <h4>Errors</h4> + <ul> + @foreach (var result in errorResults) + { + <li>@result</li> + } + </ul> +} + @code { string mxcUrl { get; set; } @@ -15,22 +50,82 @@ protected override async Task OnInitializedAsync() { - base.OnInitializedAsync(); - + await base.OnInitializedAsync(); + homeservers.AddRange(new [] + { + "matrix.org", + "feline.support", + "rory.gay", + "the-apothecary.club", + "envs.net", + "projectsegfau.lt" + }); } async Task executeSearch() { - var client = new HttpClient(); - var response = await client.GetAsync($"https://matrix.org/_matrix/media/r0/identicon/{mxcUrl}"); - if (response.IsSuccessStatusCode) + var sem = new SemaphoreSlim(128, 128); + homeservers.ForEach(async hs => { - successResults.Add(mxcUrl); - } - else - { - errorResults.Add(mxcUrl); - } + 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 + { + var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, rmu)); + 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) + { + errorResults.Add($"Error: {e}"); + } + finally + { + sem.Release(); + } + StateHasChanged(); + }); } + + 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 => + { + await sem.WaitAsync(); + try + { + homeservers.Add(await rhs.ResolveHomeserverFromWellKnown(line)); + StateHasChanged(); + if(Random.Shared.Next(0,101) == 50) + await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); + } + catch (Exception e) + { + Console.WriteLine(e); + } + 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 @@ -41,7 +41,8 @@ else @policyEvent.content.ExpiryDateTime </td> <td> - <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> + <button class="btn" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Edit</button> + @* <button class="btn btn-danger" $1$ @onclick="async () => await RemovePolicyAsync(policyEvent)" #1#>Remove</button> *@ </td> </tr> } diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor @@ -19,8 +19,13 @@ else } foreach (var s in PolicyRoomList) { - <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">[@s.Shortcode] @s.Name (@s.RoomId)</a> - <br/> + + <a style="color: unset; text-decoration: unset;" href="/PolicyListEditor/@s.RoomId.Replace('.','~')"><RoomListItem RoomId="@s.RoomId"> + <br/> + <span>Shortcode: @s.Shortcode</span> + </RoomListItem></a> + @* <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">[@s.Shortcode] @s.Name (@s.RoomId)</a> *@ + @* <br/> *@ } } @@ -74,15 +79,11 @@ else { try { - //TODO: refactor!!!!! await semaphore.WaitAsync(); 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; diff --git a/MatrixRoomUtils.Web/Pages/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager.razor @@ -1,40 +0,0 @@ -@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/Pages/RoomManager/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor @@ -0,0 +1,96 @@ +@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 +{ + <p>You are in @Rooms.Count rooms and @Spaces.Count spaces</p> + <details open> + <summary>Space List</summary> + @foreach (var room in Spaces) + { + <a style="color: unset; text-decoration: unset;" href="/RoomManager/Space/@room.RoomId.Replace('.', '~')"><RoomListItem Room="@room" ShowOwnProfile="true"></RoomListItem></a> + } + </details> + <details open> + <summary>Room List</summary> + @foreach (var room in Rooms) + { + <a style="color: unset; text-decoration: unset;" href="/RoomManager/Room/@room.RoomId.Replace('.', '~')"><RoomListItem Room="@room" ShowOwnProfile="true"></RoomListItem></a> + } + </details> + +} + +<div style="margin-bottom: 4em;"></div> +<LogView></LogView> + +@code { + public List<Room> Rooms { get; set; } = new(); + public List<Room> Spaces { 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(); + StateHasChanged(); + var semaphore = new SemaphoreSlim(1000); + var tasks = new List<Task<Room?>>(); + foreach (var room in Rooms) + { + tasks.Add(CheckIfSpace(room, semaphore)); + } + await Task.WhenAll(tasks); + + Console.WriteLine("Fetched joined rooms!"); + } + + private async Task<Room?> CheckIfSpace(Room room, SemaphoreSlim semaphore) + { + await semaphore.WaitAsync(); + try + { + var state = await room.GetStateAsync("m.room.create"); + if (state != null) + { + //Console.WriteLine(state.Value.ToJson()); + if(state.Value.TryGetProperty("type", out var type)) + { + if(type.ToString() == "m.space") + { + Spaces.Add(room); + Rooms.Remove(room); + StateHasChanged(); + return room; + } + } + else + { + //this is fine, apprently... + //Console.WriteLine($"Room {room.RoomId} has no content.type in m.room.create!"); + } + } + } + catch (Exception e) + { + Console.WriteLine(e); + return null; + } + finally + { + semaphore.Release(); + } + return null; + } +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor @@ -0,0 +1,77 @@ +@page "/RoomManager/Space/{RoomId}" +@using MatrixRoomUtils.Core.Extensions +@using System.Text.Json +<h3>Room manager - Viewing Space</h3> + +<button onclick="@JoinAllRooms">Join all rooms</button> +@foreach (var room in Rooms) +{ + <RoomListItem Room="room" ShowOwnProfile="true"></RoomListItem> +} + + +<br/> +<details style="background: #0002;"> + <summary style="background: #fff1;">State list</summary> + @foreach (var stateEvent in States.OrderBy(x => x.state_key).ThenBy(x => x.type)) + { + <p>@stateEvent.state_key/@stateEvent.type:</p> + <pre>@stateEvent.content.ToJson()</pre> + } +</details> + +@code { + + [Parameter] + public string RoomId { get; set; } = "invalid!!!!!!"; + + private Room? Room { get; set; } + + private StateEvent<object>[] States { get; set; } = Array.Empty<StateEvent<object>>(); + private List<Room> Rooms { get; set; } = 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()); + States = state.Value.Deserialize<StateEvent<object>[]>()!; + + foreach (var stateEvent in States) + { + if (stateEvent.type == "m.space.child") + { + // if (stateEvent.content.ToJson().Length < 5) return; + var roomId = stateEvent.state_key; + var room = await RuntimeCache.CurrentHomeServer.GetRoom(roomId); + if (room != null) + { + Rooms.Add(room); + } + } + } + + // 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) + { + room.JoinAsync(); + } + } + +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Properties/launchSettings.json b/MatrixRoomUtils.Web/Properties/launchSettings.json @@ -11,7 +11,7 @@ "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "http://localhost:5117", "environmentVariables": { @@ -21,7 +21,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "https://localhost:7014;http://localhost:5117", "environmentVariables": { diff --git a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor @@ -10,22 +10,30 @@ <div style="margin-bottom: 1em;"> <img style="border-radius: 50%; height: 3em; width: 3em;" src="@_avatarUrl"/> - <span style="margin-left: 1em;"><input type="radio" name="csa" checked="@(RuntimeCache.LastUsedToken == User.AccessToken)" onclick="@SetCurrent" style="text-decoration-line: unset;"/> <b>@User.Profile.DisplayName</b> on <b>@User.LoginResponse.HomeServer</b></span> - <a href="#" onclick="@RemoveUser">Remove</a> + <p style="margin-left: 1em; margin-top: -0.5em; display: inline-block;"> + <input type="radio" name="csa" checked="@(RuntimeCache.LastUsedToken == User.AccessToken)" onclick="@SetCurrent" style="text-decoration-line: unset;"/> + <b>@User.Profile.DisplayName</b> on <b>@User.LoginResponse.HomeServer</b> + <a href="#" onclick="@RemoveUser">Remove</a> + </p> + <p style="margin-top: -1.5em; margin-left: 4em;">Member of @_roomCount rooms</p> + </div> @code { [Parameter] public UserInfo User { get; set; } = null!; - + private string _avatarUrl { get; set; } + private int _roomCount { get; set; } = 0; 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 = await (await new AuthenticatedHomeServer(User.LoginResponse.UserId, User.AccessToken, User.LoginResponse.HomeServer).Configure()).ResolveMediaUri(User.Profile.AvatarUrl); + _avatarUrl = await hs.ResolveMediaUri(User.Profile.AvatarUrl); else _avatarUrl = "https://api.dicebear.com/6.x/identicon/svg?seed=" + User.LoginResponse.UserId; + _roomCount = (await hs.GetJoinedRooms()).Count; await base.OnInitializedAsync(); } @@ -34,15 +42,17 @@ Console.WriteLine(User.ToJson()); RuntimeCache.LoginSessions.Remove(User.AccessToken); await LocalStorageWrapper.ReloadLocalStorage(LocalStorage); - + StateHasChanged(); } + private async Task SetCurrent() { RuntimeCache.LastUsedToken = User.AccessToken; - //RuntimeCache.CurrentHomeserver = await MatrixAuth.ResolveHomeserverFromWellKnown(LocalStorageWrapper.LoginSessions[Token].LoginResponse.HomeServer); + //RuntimeCache.CurrentHomeserver = await MatrixAuth.ResolveHomeserverFromWellKnown(LocalStorageWrapper.LoginSessions[Token].LoginResponse.HomeServer); await LocalStorageWrapper.ReloadLocalStorage(LocalStorage); - + StateHasChanged(); } + } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/LogView.razor b/MatrixRoomUtils.Web/Shared/LogView.razor @@ -1,13 +1,27 @@ @using System.Text -<u>Logs</u><br/> -<pre> - @_stringBuilder -</pre> +@if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) +{ + <u>Logs</u> + <br/> + <pre> + @_stringBuilder + </pre> +} @code { StringBuilder _stringBuilder = new(); - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { + await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) + { + Console.WriteLine("Console logging disabled!"); + var _sw = new StringWriter(); + Console.SetOut(_sw); + Console.SetError(_sw); + return; + } + if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) return; //intecept stdout with textwriter to get logs var sw = new StringWriter(_stringBuilder); Console.SetOut(sw); @@ -27,6 +41,6 @@ } // ReSharper disable once FunctionNeverReturns - This is intentional behavior }); - base.OnInitialized(); + await base.OnInitializedAsync(); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor b/MatrixRoomUtils.Web/Shared/MainLayout.razor @@ -1,5 +1,4 @@ -@using MatrixRoomUtils.Core.Extensions -@using System.Net +@using System.Net @inherits LayoutComponentBase <div class="page"> @@ -9,6 +8,7 @@ <main> <div class="top-row px-4"> + <PortableDevTools></PortableDevTools> <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) @@ -31,6 +31,16 @@ using var hc = new HttpClient(); var hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri)); showDownload = hr.StatusCode == HttpStatusCode.OK; + + await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) + { + Console.WriteLine("Console logging disabled!"); + var sw = new StringWriter(); + Console.SetOut(sw); + Console.SetError(sw); + } + await base.OnInitializedAsync(); } diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor @@ -52,10 +52,15 @@ <hr style="margin-bottom: 0em;"/> </div> <div class="nav-item px-3"> - <NavLink class="nav-link" href="MediaLocator"> - <span class="oi oi-plus" aria-hidden="true"></span> Media locator + <NavLink class="nav-link" href="KnownHomeserverList"> + <span class="oi oi-plus" aria-hidden="true"></span> Known homeserver list </NavLink> </div> + @* <div class="nav-item px-3"> *@ + @* <NavLink class="nav-link" href="MediaLocator"> *@ + @* <span class="oi oi-plus" aria-hidden="true"></span> Media locator *@ + @* </NavLink> *@ + @* </div> *@ <div class="nav-item px-3"> <h5 style="margin-left: 1em;">MRU</h5> <hr style="margin-bottom: 0em;"/> diff --git a/MatrixRoomUtils.Web/Shared/PortableDevTools.razor b/MatrixRoomUtils.Web/Shared/PortableDevTools.razor @@ -0,0 +1,31 @@ + +@if (Enabled) +{ + <a href="/DevOptions">Portable devtools (enabled)</a> + <div id="PortableDevTools" style="position: fixed; bottom: 0; right: 0; min-width: 200px; min-height: 100px; background: #0002;" draggable> + <p>Cache size: @RuntimeCache.GenericResponseCache.Sum(x=>x.Value.Cache.Count)</p> + </div> +} +else { + <a href="/DevOptions">Portable devtools (disabled)</a> +} + +@code { + private bool Enabled { get; set; } = LocalStorageWrapper.Settings.DeveloperSettings.EnablePortableDevtools; + + protected override async Task OnInitializedAsync() + { + // if(!RuntimeCache.WasLoaded) + // await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + // StateHasChanged(); + Task.Run(async () => + { + while (true) + { + await Task.Delay(100); + Enabled = LocalStorageWrapper.Settings.DeveloperSettings.EnablePortableDevtools; + StateHasChanged(); + } + }); + } +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor @@ -3,17 +3,27 @@ <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"/> + <img style="@(ChildContent != null ? "vertical-align: baseline;":"") 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> + <img style="@(ChildContent != null ? "vertical-align: baseline;":"") width: 32px; height: 32px; border-radius: 50%;" src="@roomIcon"/> + <div style="display: inline-block;"> + <span style="vertical-align: middle; padding-right: 8px;">@roomName</span> + @if (ChildContent != null) + { + @ChildContent + } + </div> + </div> @code { [Parameter] + public RenderFragment? ChildContent { get; set; } + + [Parameter] public Room Room { get; set; } [Parameter] @@ -33,8 +43,9 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - - if(!RuntimeCache.WasLoaded) { + + if (!RuntimeCache.WasLoaded) + { Console.WriteLine("Loading from local storage"); await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); } @@ -47,6 +58,10 @@ } Room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); } + else + { + RoomId = Room.RoomId; + } roomName = await Room.GetNameAsync(); if (roomName == null) @@ -66,8 +81,8 @@ if (ShowOwnProfile) { - var profile = await RuntimeCache.CurrentHomeServer.GetProfile(RuntimeCache.CurrentHomeServer.UserId, debounce: true); - + 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) { @@ -86,7 +101,7 @@ { hasCustomProfileName = _name.GetString() != profile.DisplayName; profileName = _name.GetString(); - // Console.WriteLine($"{profile.DisplayName} - {_name.GetString()}: {hasCustomProfileName}"); + // Console.WriteLine($"{profile.DisplayName} - {_name.GetString()}: {hasCustomProfileName}"); } else { @@ -94,7 +109,7 @@ } } } - if(Random.Shared.Next(100) == 1) + if (Random.Shared.Next(100) == 1) await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); } diff --git a/deploy.sh b/deploy.sh @@ -6,7 +6,7 @@ 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-BIN.tar.xz -rsync -raP $BASE_DIR/MRU-BIN.tar.xz rory.gay:/data/nginx/html_mru/MRU-BIN.tar.xz +#rsync -raP $BASE_DIR/MRU-BIN.tar.xz rory.gay:/data/nginx/html_mru/MRU-BIN.tar.xz rm -rf $BASE_DIR/MRU-BIN.tar.xz cd $BASE_DIR git clone .git -b `git branch --show-current` src