commit 5ad6fcf24b37fed7340ff1a4a8b7707902ef743d
parent df9031c47f8e97d8e2df3177093271a458f27267
Author: TheArcaneBrony <myrainbowdash949@gmail.com>
Date: Mon, 1 May 2023 16:51:57 +0200
Add policy room discovery ,add room state viewer
Diffstat:
5 files changed, 365 insertions(+), 14 deletions(-)
diff --git a/MatrixRoomUtils.Core/StateEventStruct.cs b/MatrixRoomUtils.Core/StateEventStruct.cs
@@ -0,0 +1,13 @@
+namespace MatrixRoomUtils;
+
+public struct StateEventStruct
+{
+ public object content { get; set; }
+ public long origin_server_ts { get; set; }
+ public string sender { get; set; }
+ public string state_key { get; set; }
+ public string type { get; set; }
+ public string event_id { get; set; }
+ public string user_id { get; set; }
+ public string replaces_state { get; set; }
+}
+\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyListRoomList.razor
@@ -4,6 +4,7 @@
@using Blazored.LocalStorage
@using System.Net.Http.Headers
@using System.Text.Json
+@using System.Xml.Schema
@using MatrixRoomUtils.Extensions
@using MatrixRoomUtils.StateEventTypes
@inject ILocalStorageService LocalStorage
@@ -15,12 +16,17 @@
@if (PolicyRoomList.Count == 0)
{
<p>No policy rooms found.</p>
+ <p>Loading progress: @checkedRoomCount/@totalRoomCount</p>
}
else
{
+ @if (checkedRoomCount != totalRoomCount)
+ {
+ <p>Loading progress: @checkedRoomCount/@totalRoomCount</p>
+ }
foreach (var s in PolicyRoomList)
{
- <a href="@(NavigationManager.Uri + "/" + s.Replace('.', '~'))">@s</a>
+ <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">@s.Name</a>
<br/>
}
<div style="margin-bottom: 4em;"></div>
@@ -34,14 +40,16 @@ else
// type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
- public List<string> PolicyRoomList { get; set; } = new();
- public List<StateEvent<PolicyRuleStateEventData>> PolicyEvents { get; set; } = new();
+ public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new();
+
+ private int checkedRoomCount { get; set; } = 0;
+ private int totalRoomCount { get; set; } = 0;
protected override async Task OnInitializedAsync()
{
if (!RuntimeStorage.WasLoaded) await RuntimeStorage.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- if(RuntimeStorage.AccessToken == null || RuntimeStorage.CurrentHomeserver == null)
+ if (RuntimeStorage.AccessToken == null || RuntimeStorage.CurrentHomeserver == null)
{
NavigationManager.NavigateTo("/Login");
return;
@@ -58,27 +66,72 @@ else
//get room list
//temporary hack until rooms get enumerated...
string[] rooms = { "!fTjMjIzNKEsFlUIiru:neko.dev" };
+ var _rooms = await wc.GetAsync($"{RuntimeStorage.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(128);
+ var tasks = new List<Task<PolicyRoomInfo?>>();
foreach (string room in rooms)
{
- Console.WriteLine($"Checking if {room} is a policy room...");
+ 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)
+ {
+ try
+ {
+ await semaphore.WaitAsync();
+ using HttpClient wc = new();
+ wc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeStorage.AccessToken);
var sk = await wc.GetAsync($"{RuntimeStorage.CurrentHomeserver}/_matrix/client/v3/rooms/{room}/state/org.matrix.mjolnir.shortcode");
if (sk.IsSuccessStatusCode)
{
- Console.WriteLine($"Got success...");
var sko = await sk.Content.ReadFromJsonAsync<JsonElement>();
if (sko.TryGetProperty("shortcode", out JsonElement shortcode))
{
Console.WriteLine($"Room {room} has a shortcode: {shortcode.GetString()}!");
- PolicyRoomList.Add(room);
- StateHasChanged();
+ return new PolicyRoomInfo() { Name = room, Shortcode = shortcode.GetString(), RoomId = room };
}
else Console.WriteLine("No record found...");
}
- else Console.WriteLine($"Got failure {sk.StatusCode}...");
+ else if (sk.StatusCode == System.Net.HttpStatusCode.NotFound)
+ {
+ }
+ else Console.WriteLine($"Got failure while checking {room}: {sk.StatusCode} ({await sk.Content.ReadAsStringAsync()})...");
+ return null;
}
-
- //print to console
- Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}");
+ finally
+ {
+ checkedRoomCount++;
+ StateHasChanged();
+ semaphore.Release();
+ }
+ }
+
+ public struct PolicyRoomInfo
+ {
+ public string RoomId { get; set; }
+ public string Shortcode { get; set; }
+ public string Name { get; set; }
}
- }
-\ No newline at end of file
+}
+\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomStateRoomList.razor
@@ -0,0 +1,103 @@
+@page "/RoomStateViewer"
+@using MatrixRoomUtils.Authentication
+@using MatrixRoomUtils.Web.Classes
+@using Blazored.LocalStorage
+@using System.Net.Http.Headers
+@using System.Text.Json
+@using System.Xml.Schema
+@using MatrixRoomUtils.Extensions
+@using MatrixRoomUtils.StateEventTypes
+@inject ILocalStorageService LocalStorage
+@inject NavigationManager NavigationManager
+<h3>Policy list editor</h3>
+
+<h5>Room list</h5>
+<hr/>
+@if (PolicyRoomList.Count == 0)
+{
+ <p>No policy rooms found.</p>
+ <p>Loading progress: @checkedRoomCount/@totalRoomCount</p>
+}
+else
+{
+ @if (checkedRoomCount != totalRoomCount)
+ {
+ <p>Loading progress: @checkedRoomCount/@totalRoomCount</p>
+ }
+ foreach (var s in PolicyRoomList)
+ {
+ <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">@s.Name</a>
+ <br/>
+ }
+ <div style="margin-bottom: 4em;"></div>
+}
+
+<LogView></LogView>
+
+@code {
+
+ public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new();
+
+ private int checkedRoomCount { get; set; } = 0;
+ private int totalRoomCount { get; set; } = 0;
+
+ protected override async Task OnInitializedAsync()
+ {
+ if (!RuntimeStorage.WasLoaded) await RuntimeStorage.LoadFromLocalStorage(LocalStorage);
+ await base.OnInitializedAsync();
+ if (RuntimeStorage.AccessToken == null || RuntimeStorage.CurrentHomeserver == null)
+ {
+ NavigationManager.NavigateTo("/Login");
+ return;
+ }
+ await EnumeratePolicyRooms();
+ Console.WriteLine("Policy list editor initialized!");
+ }
+
+ private async Task EnumeratePolicyRooms()
+ {
+ using HttpClient wc = new();
+ wc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeStorage.AccessToken);
+
+ //get room list
+ //temporary hack until rooms get enumerated...
+ string[] rooms = { "!fTjMjIzNKEsFlUIiru:neko.dev" };
+ var _rooms = await wc.GetAsync($"{RuntimeStorage.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(128);
+ // 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));
+ PolicyRoomList.AddRange(rooms.Select(x=>new PolicyRoomInfo() { Name = x, RoomId = x, Shortcode = "N/A" }));
+
+
+ //print to console
+ Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}");
+ }
+
+
+ public struct PolicyRoomInfo
+ {
+ public string RoomId { get; set; }
+ public string Shortcode { get; set; }
+ public string Name { get; set; }
+ }
+}
+\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomStateViewerPage.razor
@@ -0,0 +1,174 @@
+@page "/RoomStateViewer/{RoomId}"
+@using MatrixRoomUtils.Authentication
+@using MatrixRoomUtils.Web.Classes
+@using Blazored.LocalStorage
+@using System.Net.Http.Headers
+@using System.Text.Json
+@using System.Xml.Schema
+@using MatrixRoomUtils.Extensions
+@using MatrixRoomUtils.StateEventTypes
+@using MatrixRoomUtils.Web.Shared.IndexComponents
+@inject ILocalStorageService LocalStorage
+@inject NavigationManager NavigationManager
+<h3>Room state viewer</h3>
+<p>Room ID: @RoomId</p>
+
+<p>@status</p>
+
+<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events
+
+<table class="table table-striped table-hover" style="width: fit-content;">
+ <thead>
+ <tr>
+ <th scope="col">Type</th>
+ <th scope="col">Content</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts))
+ {
+ <tr>
+ <td>@stateEvent.type</td>
+ <td style="max-width: fit-content;">
+ <pre>@stateEvent.content</pre>
+ </td>
+ </tr>
+ }
+ </tbody>
+</table>
+
+@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != ""))
+{
+ <details>
+ <summary>@group.Key</summary>
+ <table class="table table-striped table-hover" style="width: fit-content;">
+ <thead>
+ <tr>
+ <th scope="col">Type</th>
+ <th scope="col">Content</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts))
+ {
+ <tr>
+ <td>@stateEvent.type</td>
+ <td style="max-width: fit-content;">
+ <pre>@stateEvent.content</pre>
+ </td>
+ </tr>
+ }
+ </tbody>
+ </table>
+ </details>
+}
+
+<LogView></LogView>
+
+@code {
+ //get room list
+ // - sync withroom list filter
+ // type = support.feline.msc3784
+ //support.feline.policy.lists.msc.v1
+
+ [Parameter]
+ public string? RoomId { get; set; }
+
+ public List<PreRenderedStateEvent> FilteredEvents { get; set; } = new();
+ public List<PreRenderedStateEvent> Events { get; set; } = new();
+ public string status = "";
+
+ protected override async Task OnInitializedAsync()
+ {
+ if (!RuntimeStorage.WasLoaded) await RuntimeStorage.LoadFromLocalStorage(LocalStorage);
+ await base.OnInitializedAsync();
+ if (RuntimeStorage.AccessToken == null || RuntimeStorage.CurrentHomeserver == null)
+ {
+ NavigationManager.NavigateTo("/Login");
+ return;
+ }
+ RoomId = RoomId.Replace('~', '.');
+ await LoadStatesAsync();
+ Console.WriteLine("Policy list editor initialized!");
+ }
+ private DateTime _lastUpdate = DateTime.Now;
+
+ private async Task LoadStatesAsync()
+ {
+ int StateLoaded = 0;
+ using var client = new HttpClient();
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeStorage.AccessToken);
+ var response = await client.GetAsync($"{RuntimeStorage.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state");
+ // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json");
+ //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>();
+ var _data = await response.Content.ReadAsStreamAsync();
+ var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEventStruct>(_data);
+ 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),
+ };
+ Events.Add(e);
+ if (string.IsNullOrEmpty(e.state_key))
+ {
+ FilteredEvents.Add(e);
+ }
+ StateLoaded++;
+ if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100)
+ {
+ _lastUpdate = DateTime.Now;
+ status = $"Loaded {StateLoaded} state events";
+ StateHasChanged();
+ await Task.Delay(0);
+ }
+
+ }
+
+ StateHasChanged();
+ }
+
+ 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);
+ FilteredEvents = _FilteredEvents;
+ StateHasChanged();
+ }
+
+
+ public struct PreRenderedStateEvent
+ {
+ public string content { get; set; }
+ public long origin_server_ts { get; set; }
+ public string state_key { get; set; }
+ public string type { get; set; }
+ // public string sender { get; set; }
+ // public string event_id { get; set; }
+ // public string user_id { get; set; }
+ // public string replaces_state { get; set; }
+ }
+
+ public bool ShowMembershipEvents
+ {
+ get => _showMembershipEvents;
+ set
+ {
+ _showMembershipEvents = value;
+ RebuildFilteredData();
+ }
+ }
+
+ private bool _showMembershipEvents;
+}
+\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor
@@ -24,6 +24,11 @@
<span class="oi oi-plus" aria-hidden="true"></span> Policy list editor
</NavLink>
</div>
+ <div class="nav-item px-3">
+ <NavLink class="nav-link" href="RoomStateViewer">
+ <span class="oi oi-plus" aria-hidden="true"></span> Room state viewer
+ </NavLink>
+ </div>
</nav>
</div>