diff options
Diffstat (limited to 'MatrixRoomUtils.Web/Pages/Tools')
-rw-r--r-- | MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor | 171 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor | 111 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor | 113 |
3 files changed, 395 insertions, 0 deletions
diff --git a/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor new file mode 100644 index 0000000..939838e --- /dev/null +++ b/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor @@ -0,0 +1,171 @@ +@page "/KnownHomeserverList" +@using System.Diagnostics +@using ArcaneLibs.Extensions +@using LibMatrix.Homeservers +@using LibMatrix.RoomTypes +<h3>Known Homeserver List</h3> +<hr/> + +@if (!IsFinished) { + <p>Loading... Please wait...</p> + <progress value="@QueryProgress.ProcessedRooms" max="@QueryProgress.TotalRooms"></progress> + <p>@QueryProgress.ProcessedRooms / @QueryProgress.TotalRooms</p> + @foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList()) { + @if (state.Blocked) { + <p>🔒 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p> + } + else if (state.Slowmode) { + <p>🐢 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p> + } + else { + <p>@room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p> + } + <progress value="@state.Processed" max="@state.Total"></progress> + } +} +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; } + HomeServerInfoQueryProgress QueryProgress { get; set; } = new(); + AuthenticatedHomeserverGeneric hs { get; set; } + protected override async Task OnInitializedAsync() { + hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + var sw = Stopwatch.StartNew(); + HomeServers = await GetHomeservers(progressCallback: async progress => { + if (sw.ElapsedMilliseconds > 1000) { + Console.WriteLine("Progress updated..."); + QueryProgress = progress; + StateHasChanged(); + Console.WriteLine("Progress rendered!"); + sw.Restart(); + await Task.Delay(100); + return true; + } + Console.WriteLine($"Progress updated, but not rendering because only {sw.ElapsedMilliseconds}ms elapsed since last call..."); + return false; + }); + + IsFinished = true; + StateHasChanged(); + Console.WriteLine("Rerendered!"); + await base.OnInitializedAsync(); + } + + private async Task<List<HomeServerInfo>> GetHomeservers(int memberLimit = 1000, Func<HomeServerInfoQueryProgress, Task<bool>>? progressCallback = null) { + HomeServerInfoQueryProgress progress = new(); + List<HomeServerInfo> homeServers = new(); + + var rooms = await hs.GetJoinedRooms(); + progress.TotalRooms = rooms.Count; + + var semaphore = new SemaphoreSlim(4); + var semLock = new SemaphoreSlim(1); + var tasks = rooms.Select(async room => { + await semaphore.WaitAsync(); + progress.ProcessedUsers.Add(room, new HomeServerInfoQueryProgress.State()); + Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})"); + var states = room.GetFullStateAsync(); + await foreach (var state in states) { + if (state.Type is not "m.room.member") continue; + progress.ProcessedUsers[room].Total++; + + if (homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) continue; + homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] }); + Console.WriteLine($"Added new homeserver {state.StateKey.Split(':')[1]}"); + } + semaphore.Release(); + progress.ProcessedUsers[room].IsFinished = true; + progress.ProcessedRooms++; + if (progressCallback is not null) + await progressCallback.Invoke(progress); + + + + // states.RemoveAll(x => x.Type != "m.room.member" || (x.TypedContent as RoomMemberEventContent).Membership != "join"); + // Console.WriteLine($"Room {room.RoomId} has {states.Count} members"); + // if (states.Count > memberLimit) { + // Console.WriteLine("Skipping!"); + // semaphore.Release(); + // progress.ProcessedUsers.Remove(room); + // progress.TotalRooms--; + // return; + // } + // 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) { + // progress.ProcessedUsers[room].Blocked = true; + // await Task.Delay(1000); + // // if(progressCallback is not null) + // // await progressCallback.Invoke(progress); + // } + // progress.ProcessedUsers[room].Blocked = false; + // 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) { + // progress.ProcessedUsers[room].Slowmode = true; + // await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100); + // } + // 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] }); + // } + // 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 is not null) { + // await semLock.WaitAsync(); + // var _ = await progressCallback.Invoke(progress); + // semLock.Release(); + // } + // } + // Console.WriteLine("Collected states!"); + // progress.ProcessedRooms++; + // progress.ProcessedUsers[room].IsFinished = true; + // progressCallback?.Invoke(progress); + // semaphore.Release(); + }); + 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; } = new(); + } + + class HomeServerInfoQueryProgress { + public int ProcessedRooms { get; set; } + public int TotalRooms { get; set; } + public Dictionary<GenericRoom, State> ProcessedUsers { get; } = new(); + public List<HomeServerInfo> CurrentState { get; set; } = new(); + + 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; } = Stopwatch.StartNew(); + } + } + +} diff --git a/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor new file mode 100644 index 0000000..a376efa --- /dev/null +++ b/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor @@ -0,0 +1,111 @@ +@page "/MediaLocator" +@using LibMatrix.Homeservers +@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; } + readonly List<string> successResults = new(); + readonly List<string> errorResults = new(); + readonly List<string> homeservers = new(); + + protected override async Task OnInitializedAsync() { + await base.OnInitializedAsync(); + homeservers.AddRange(new[] { + "matrix.org", + "feline.support", + "rory.gay", + "the-apothecary.club", + "envs.net", + "projectsegfau.lt" + }); + } + + Task executeSearch() { + var sem = new SemaphoreSlim(128, 128); + 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/v3/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(); + }); + return Task.CompletedTask; + } + + async Task addMoreHomeservers() { + 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 hsResolver.ResolveHomeserverFromWellKnown(line)); + StateHasChanged(); + } + catch (Exception e) { + Console.WriteLine(e); + } + finally { + sem.Release(); + } + }); + + StateHasChanged(); + } + +} diff --git a/MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor b/MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor new file mode 100644 index 0000000..5b4815d --- /dev/null +++ b/MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor @@ -0,0 +1,113 @@ +@page "/SpaceDebug" +@using LibMatrix.Filters +@using LibMatrix.Helpers +<h3>SpaceDebug</h3> +<hr/> + +<p>@Status</p> + +<b>Has parent:</b> +<br/> + +@foreach (var (roomId, parents) in SpaceParents) { + <p>@roomId's parents</p> + <ul> + @foreach (var parent in parents) { + <li>@parent</li> + } + </ul> +} + +<b>Space children:</b> + +@foreach (var (roomId, children) in SpaceChildren) { + <p>@roomId's children</p> + <ul> + @foreach (var child in children) { + <li>@child</li> + } + </ul> +} + +@code { + private string _status = "Loading..."; + + public string Status { + get => _status; + set { + _status = value; + StateHasChanged(); + } + } + + public Dictionary<string, List<string>> SpaceChildren { get; set; } = new(); + public Dictionary<string, List<string>> SpaceParents { get; set; } = new(); + + protected override async Task OnInitializedAsync() { + Status = "Getting homeserver..."; + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + + var syncHelper = new SyncHelper(hs) { + Filter = new SyncFilter() { + Presence = new(0), + Room = new() { + AccountData = new(limit: 0), + Ephemeral = new(limit: 0), + State = new(limit: 1000, types: new() { "m.space.child", "m.space.parent" }), + Timeline = new(limit: 0) + }, + AccountData = new(limit: 0) + } + }; + + Status = "Syncing..."; + + var syncs = syncHelper.EnumerateSyncAsync(); + await foreach (var sync in syncs) { + if (sync is null) { + Status = "Sync failed"; + continue; + } + + if (sync.Rooms is null) { + Status = "No rooms in sync..."; + break; + } + + if (sync.Rooms.Join is null) { + Status = "No joined rooms in sync..."; + break; + } + + if (sync.Rooms.Join.Count == 0) { + Status = "Joined rooms list was empty..."; + break; + } + + // nextBatch = sync.NextBatch; + foreach (var (roomId, data) in sync.Rooms!.Join!) { + data.State?.Events?.ForEach(e => { + if (e.Type == "m.space.child") { + if (!SpaceChildren.ContainsKey(roomId)) SpaceChildren[roomId] = new(); + if (e.RawContent is null) e.StateKey += " (null)"; + else if (e.RawContent.Count == 0) e.StateKey += " (empty)"; + SpaceChildren[roomId].Add(e.StateKey); + } + if (e.Type == "m.space.parent") { + if (!SpaceParents.ContainsKey(roomId)) SpaceParents[roomId] = new(); + if (e.RawContent is null) e.StateKey += " (null)"; + else if (e.RawContent.Count == 0) e.StateKey += " (empty)"; + SpaceParents[roomId].Add(e.StateKey); + } + }); + } + Status = $"Synced {sync.Rooms.Join.Count} rooms, found {SpaceChildren.Count} spaces, {SpaceParents.Count} parents"; + } + Status = $"Synced: found {SpaceChildren.Count}->{SpaceChildren.Sum(x => x.Value.Count)} spaces, {SpaceParents.Count}->{SpaceParents.Sum(x => x.Value.Count)} parents!"; + + await base.OnInitializedAsync(); + } + + +} |