From 863faaa050c4ba02190c0d53e41d931df890e593 Mon Sep 17 00:00:00 2001 From: Rory& Date: Fri, 21 Feb 2025 18:13:02 +0100 Subject: HomeserverEmulator changes --- MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor | 5 + .../Pages/HSAdmin/HSE/ManageExternalProfiles.razor | 43 +++++ MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor | 201 --------------------- MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor.css | 0 .../Pages/HSAdmin/Synapse/RoomQuery.razor | 201 +++++++++++++++++++++ .../Pages/HSAdmin/Synapse/RoomQuery.razor.css | 0 .../Pages/Tools/Info/PolicyListActivity.razor | 177 ++++++++---------- 7 files changed, 325 insertions(+), 302 deletions(-) create mode 100644 MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor delete mode 100644 MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor delete mode 100644 MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor.css create mode 100644 MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor create mode 100644 MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css (limited to 'MatrixUtils.Web/Pages') diff --git a/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor index 409d582..09cc2cd 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor @@ -12,6 +12,11 @@ else {
Query rooms } + else if (Homeserver is AuthenticatedHomeserverHSE) { +

Rory&::LibMatrix.HomeserverEmulator tools

+
+ Manage external profiles + } else {

Homeserver type @Homeserver.GetType().Name does not have any administration tools in RMU.

Server info:

diff --git a/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor b/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor new file mode 100644 index 0000000..6d492a7 --- /dev/null +++ b/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor @@ -0,0 +1,43 @@ +@page "/HSAdmin/HSE/ManageExternalProfiles" +@using ArcaneLibs.Extensions +@using LibMatrix.Responses +

Manage external profiles

+ +Add local sessions + +@foreach(var p in ExternalProfiles) +{ +

@p.Key

+
@p.Value.ToJson(indent: true)
+} + +@code { + public AuthenticatedHomeserverGeneric? Homeserver { get; set; } + private Dictionary ExternalProfiles = new(); + + protected override async Task OnInitializedAsync() + { + Homeserver = await RmuStorage.GetCurrentSessionOrNavigate(); + if (Homeserver is null) return; + await LoadProfiles(); + await base.OnInitializedAsync(); + } + + private async Task LoadProfiles() { + if(Homeserver is AuthenticatedHomeserverHSE hse) + { + ExternalProfiles = await hse.GetExternalProfilesAsync(); + } + StateHasChanged(); + } + + private async Task AddAllLocalProfiles() { + if(Homeserver is AuthenticatedHomeserverHSE hse) { + var sessions = await RmuStorage.GetAllTokens(); + foreach(var session in sessions) { + await hse.SetExternalProfile(session.UserId, session); + } + await LoadProfiles(); + } + } +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor b/MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor deleted file mode 100644 index 1e63e16..0000000 --- a/MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor +++ /dev/null @@ -1,201 +0,0 @@ -@page "/HSAdmin/RoomQuery" -@using LibMatrix.Responses.Admin -@using LibMatrix.Filters -@using ArcaneLibs.Extensions - -

Homeserver Administration - Room Query

- - -
- -
- -
-
- - Local filtering (slow) - - -
- String contains - Room ID: - Room name: - Canonical alias: - Creator: - Room version: - Encryption algorithm: - Join rules: - Guest access: - History visibility: - - Optional checks - - Is federated: - @if (Filter.CheckFederation) { - - } - - - Is public: - @if (Filter.CheckPublic) { - - } - - - Ranges - - state events - - - members - - - local members - -
-
- -
- -@if (Results.Count > 0) { -

Found @Results.Count rooms

-
- TSV data (copy/paste) -
-            
-                @foreach (var res in Results) {
-                    
-                        
-                        
-                        
-                        
-                    
-                }
-            
@res.RoomId@("\t")@res.CanonicalAlias@("\t")@res.Creator@("\t")@res.Name
-
-
-} - -@foreach (var res in Results) { -
- @* *@ -

- @if (!string.IsNullOrWhiteSpace(res.CanonicalAlias)) { - @res.CanonicalAlias - @res.RoomId (@res.Name) -
- } - else { - @res.RoomId (@res.Name) -
- } - @if (!string.IsNullOrWhiteSpace(res.Creator)) { - @* Created by *@ - Created by @res.Creator -
- } -

- @res.StateEvents state events
- @res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server -
- Full result data -
@res.ToJson(ignoreNull: true)
-
-
-} - - - -@code { - - [Parameter] - [SupplyParameterFromQuery(Name = "order_by")] - public string? OrderBy { get; set; } - - [Parameter] - [SupplyParameterFromQuery(Name = "name_search")] - public string SearchTerm { get; set; } - - [Parameter] - [SupplyParameterFromQuery(Name = "ascending")] - public bool Ascending { get; set; } - - public List Results { get; set; } = new(); - - private string Status { get; set; } - - public LocalRoomQueryFilter Filter { get; set; } = new(); - - protected override Task OnParametersSetAsync() { - if (Ascending == null) - Ascending = true; - OrderBy ??= "name"; - return Task.CompletedTask; - } - - private async Task Search() { - Results.Clear(); - var hs = await RmuStorage.GetCurrentSessionOrNavigate(); - if (hs is AuthenticatedHomeserverSynapse synapse) { - var searchRooms = synapse.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, localFilter: Filter).GetAsyncEnumerator(); - while (await searchRooms.MoveNextAsync()) { - var room = searchRooms.Current; - Console.WriteLine("Hit: " + room.ToJson(false)); - Results.Add(room); - if (Results.Count % 10 == 0) - StateHasChanged(); - } - } - - StateHasChanged(); - } - - private readonly Dictionary validOrderBy = new() { - { "name", "Room name" }, - { "canonical_alias", "Main alias address" }, - { "joined_members", "Number of members (reversed)" }, - { "joined_local_members", "Number of local members (reversed)" }, - { "version", "Room version" }, - { "creator", "Creator of the room" }, - { "encryption", "End-to-end encryption algorithm" }, - { "federatable", "Is room federated" }, - { "public", "Visibility in room list" }, - { "join_rules", "Join rules" }, - { "guest_access", "Guest access" }, - { "history_visibility", "Visibility of history" }, - { "state_events", "Number of state events" } - }; - -} diff --git a/MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor.css b/MatrixUtils.Web/Pages/HSAdmin/RoomQuery.razor.css deleted file mode 100644 index e69de29..0000000 diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor new file mode 100644 index 0000000..07af1dc --- /dev/null +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor @@ -0,0 +1,201 @@ +@page "/HSAdmin/Synapse/RoomQuery" +@using LibMatrix.Responses.Admin +@using LibMatrix.Filters +@using ArcaneLibs.Extensions + +

Homeserver Administration - Room Query

+ + +
+ +
+ +
+
+ + Local filtering (slow) + + +
+ String contains + Room ID: + Room name: + Canonical alias: + Creator: + Room version: + Encryption algorithm: + Join rules: + Guest access: + History visibility: + + Optional checks + + Is federated: + @if (Filter.CheckFederation) { + + } + + + Is public: + @if (Filter.CheckPublic) { + + } + + + Ranges + + state events + + + members + + + local members + +
+
+ +
+ +@if (Results.Count > 0) { +

Found @Results.Count rooms

+
+ TSV data (copy/paste) +
+            
+                @foreach (var res in Results) {
+                    
+                        
+                        
+                        
+                        
+                    
+                }
+            
@res.RoomId@("\t")@res.CanonicalAlias@("\t")@res.Creator@("\t")@res.Name
+
+
+} + +@foreach (var res in Results) { +
+ @* *@ +

+ @if (!string.IsNullOrWhiteSpace(res.CanonicalAlias)) { + @res.CanonicalAlias - @res.RoomId (@res.Name) +
+ } + else { + @res.RoomId (@res.Name) +
+ } + @if (!string.IsNullOrWhiteSpace(res.Creator)) { + @* Created by *@ + Created by @res.Creator +
+ } +

+ @res.StateEvents state events
+ @res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server +
+ Full result data +
@res.ToJson(ignoreNull: true)
+
+
+} + + + +@code { + + [Parameter] + [SupplyParameterFromQuery(Name = "order_by")] + public string? OrderBy { get; set; } + + [Parameter] + [SupplyParameterFromQuery(Name = "name_search")] + public string SearchTerm { get; set; } + + [Parameter] + [SupplyParameterFromQuery(Name = "ascending")] + public bool Ascending { get; set; } + + public List Results { get; set; } = new(); + + private string Status { get; set; } + + public LocalRoomQueryFilter Filter { get; set; } = new(); + + protected override Task OnParametersSetAsync() { + if (Ascending == null) + Ascending = true; + OrderBy ??= "name"; + return Task.CompletedTask; + } + + private async Task Search() { + Results.Clear(); + var hs = await RmuStorage.GetCurrentSessionOrNavigate(); + if (hs is AuthenticatedHomeserverSynapse synapse) { + var searchRooms = synapse.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, localFilter: Filter).GetAsyncEnumerator(); + while (await searchRooms.MoveNextAsync()) { + var room = searchRooms.Current; + Console.WriteLine("Hit: " + room.ToJson(false)); + Results.Add(room); + if (Results.Count % 10 == 0) + StateHasChanged(); + } + } + + StateHasChanged(); + } + + private readonly Dictionary validOrderBy = new() { + { "name", "Room name" }, + { "canonical_alias", "Main alias address" }, + { "joined_members", "Number of members (reversed)" }, + { "joined_local_members", "Number of local members (reversed)" }, + { "version", "Room version" }, + { "creator", "Creator of the room" }, + { "encryption", "End-to-end encryption algorithm" }, + { "federatable", "Is room federated" }, + { "public", "Visibility in room list" }, + { "join_rules", "Join rules" }, + { "guest_access", "Guest access" }, + { "history_visibility", "Visibility of history" }, + { "state_events", "Number of state events" } + }; + +} diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor b/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor index 8f9f043..4506c39 100644 --- a/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor +++ b/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor @@ -1,22 +1,22 @@ @page "/Tools/Info/PolicyListActivity" @using LibMatrix.EventTypes.Spec.State.Policy @using System.Diagnostics +@using System.Reflection +@using ArcaneLibs.Extensions +@using LibMatrix +@using LibMatrix.EventTypes @using LibMatrix.RoomTypes @using LibMatrix.EventTypes.Common - - +@using LibMatrix.Filters @* *@ -@if (RoomData.Count == 0) -{ +@if (RoomData.Count == 0) {

Loading...

} else - foreach (var room in RoomData) - { + foreach (var room in RoomData) {

@room.Key

- @foreach (var year in room.Value.OrderBy(x => x.Key)) - { + @foreach (var year in room.Value.OrderBy(x => x.Key)) { @year.Key } @@ -29,66 +29,26 @@ else public Dictionary TestData { get; set; } = new(); - public ActivityGraph.RGB MaxValue { get; set; } = new() - { + public ActivityGraph.RGB MaxValue { get; set; } = new() { R = 255, G = 255, B = 255 }; public Dictionary>> RoomData { get; set; } = new(); - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() { var sw = Stopwatch.StartNew(); await base.OnInitializedAsync(); Homeserver = (await RmuStorage.GetCurrentSessionOrNavigate())!; if (Homeserver is null) return; - // - //random test data - for (DateOnly i = new DateOnly(2020, 1, 1); i < new DateOnly(2020, 12, 30); i = i.AddDays(2)) - { - // TestData[i] = new() - // { - // R = (int)(Random.Shared.NextSingle() * 255), - // G = (int)(Random.Shared.NextSingle() * 255), - // B = (int)(Random.Shared.NextSingle() * 255) - // }; - // rgb based on number of week - TestData[i] = new() - { - R = i.DayOfYear % 255, - G = i.DayOfYear + 96 % 255, - B = i.DayOfYear + 192 % 255 - }; - } - - StateHasChanged(); - // return; var rooms = await Homeserver.GetJoinedRooms(); - // foreach (var room in rooms) - // { - // var type = await room.GetRoomType(); - // if (type == "support.feline.policy.lists.msc.v1") - // { - // Console.WriteLine($"{room.RoomId} is policy list by type"); - // FilteredRooms.Add(room); - // } - // else if(await room.GetStateOrNullAsync(MjolnirShortcodeEventContent.EventId) is not null) - // { - // Console.WriteLine($"{room.RoomId} is policy list by shortcode"); - // FilteredRooms.Add(room); - // } - // } - var roomFilterTasks = rooms.Select(async room => - { + var roomFilterTasks = rooms.Select(async room => { var type = await room.GetRoomType(); - if (type == "support.feline.policy.lists.msc.v1") - { + if (type == "support.feline.policy.lists.msc.v1") { Console.WriteLine($"{room.RoomId} is policy list by type"); return room; } - else if (await room.GetStateOrNullAsync(MjolnirShortcodeEventContent.EventId) is not null) - { + else if (await room.GetStateOrNullAsync(MjolnirShortcodeEventContent.EventId) is not null) { Console.WriteLine($"{room.RoomId} is policy list by shortcode"); return room; } @@ -106,60 +66,75 @@ else Console.WriteLine($"Filtered {FilteredRooms.Count} rooms in {sw.ElapsedMilliseconds}ms"); } - public async Task FetchRoomHistory(GenericRoom room) - { + public async Task FetchRoomHistory(GenericRoom room) { var roomName = await room.GetNameOrFallbackAsync(); - if (string.IsNullOrWhiteSpace(roomName)) roomName = room.RoomId; - if (!RoomData.ContainsKey(roomName)) - { - RoomData[roomName] = new(); - } + if (string.IsNullOrWhiteSpace(roomName)) roomName = room.RoomId; + if (!RoomData.ContainsKey(roomName)) { + RoomData[roomName] = new(); + } + + //use timeline + var types = StateEventResponse.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))); + var filter = new SyncFilter.EventFilter(types: types.SelectMany(x => x.GetCustomAttributes().Select(y => y.EventName)).ToList()); + var timeline = room.GetManyMessagesAsync(limit: int.MaxValue, chunkSize: 2500, filter: filter.ToJson(indent: false, ignoreNull: true)); + await foreach (var response in timeline) { + Console.WriteLine($"Got {response.State.Count} state, {response.Chunk.Count} timeline"); + if (response.State.Count != 0) throw new Exception("Why the hell did we receive state events?"); + foreach (var message in response.Chunk) { + if (!message.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue; + //OriginServerTs to datetime + var dt = DateTimeOffset.FromUnixTimeMilliseconds((long)message.OriginServerTs!.Value).DateTime; + var date = new DateOnly(dt.Year, dt.Month, dt.Day); + if (!RoomData[roomName].ContainsKey(date.Year)) { + RoomData[roomName][date.Year] = new(); + } - //use timeline - var timeline = room.GetManyMessagesAsync(limit: int.MaxValue, chunkSize: 2000); - await foreach (var response in timeline) - { - Console.WriteLine($"Got {response.State.Count} state, {response.Chunk.Count} timeline"); - if (response.State.Count != 0) throw new Exception("Why the hell did we receive state events?"); - foreach (var message in response.Chunk) - { - if (!message.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue; - //OriginServerTs to datetime - var dt = DateTimeOffset.FromUnixTimeMilliseconds((long)message.OriginServerTs!.Value).DateTime; - var date = new DateOnly(dt.Year, dt.Month, dt.Day); - if (!RoomData[roomName].ContainsKey(date.Year)) - { - RoomData[roomName][date.Year] = new(); - } - - if (!RoomData[roomName][date.Year].ContainsKey(date)) - { - // Console.WriteLine($"Adding {date} to {roomName}"); - RoomData[roomName][date.Year][date] = new(); - } - - var rgb = RoomData[roomName][date.Year][date]; - if (message.RawContent?.Count == 0) rgb.R++; - else if (message.Unsigned?.ContainsKey("replaces_state") ?? false) rgb.G++; - else rgb.B++; - RoomData[roomName][date.Year][date] = rgb; + if (!RoomData[roomName][date.Year].ContainsKey(date)) { + // Console.WriteLine($"Adding {date} to {roomName}"); + RoomData[roomName][date.Year][date] = new(); } - var max = RoomData.SelectMany(x => x.Value.Values).Aggregate(new ActivityGraph.RGB(), (current, next) => new() - { - R = Math.Max(current.R, next.Average(x => x.Value.R)), - G = Math.Max(current.G, next.Average(x => x.Value.G)), - B = Math.Max(current.B, next.Average(x => x.Value.B)) - }); - MaxValue = new ActivityGraph.RGB( - r: Math.Max(max.R, Math.Max(max.G, max.B)), - g: Math.Max(max.R, Math.Max(max.G, max.B)), - b: Math.Max(max.R, Math.Max(max.G, max.B))); - Console.WriteLine($"Max value is {MaxValue.R} {MaxValue.G} {MaxValue.B}"); - StateHasChanged(); - await Task.Delay(100); + var rgb = RoomData[roomName][date.Year][date]; + if (message.RawContent is { Count: 0 } or null) rgb.R++; + else if (!message.Unsigned?.ContainsKey("replaces_state") ?? true) rgb.G++; + else rgb.B++; + RoomData[roomName][date.Year][date] = rgb; } + + + } + var max = RoomData.SelectMany(x => x.Value.Values).Aggregate(new ActivityGraph.RGB(), (current, next) => new() { + R = Math.Max(current.R, next.Average(x => x.Value.R)), + G = Math.Max(current.G, next.Average(x => x.Value.G)), + B = Math.Max(current.B, next.Average(x => x.Value.B)) + }); + MaxValue = new ActivityGraph.RGB( + r: Math.Max(max.R, Math.Max(max.G, max.B)), + g: Math.Max(max.R, Math.Max(max.G, max.B)), + b: Math.Max(max.R, Math.Max(max.G, max.B))); + Console.WriteLine($"Max value is {MaxValue.R} {MaxValue.G} {MaxValue.B}"); + StateHasChanged(); + await Task.Yield(); } + private readonly struct StateEventEntry { + public required DateTime Timestamp { get; init; } + public required StateEventTransition State { get; init; } + public required StateEventResponse Event { get; init; } + public required StateEventResponse? Previous { get; init; } + + public void Deconstruct(out StateEventTransition transition, out StateEventResponse evt, out StateEventResponse? prev) { + transition = State; + evt = Event; + prev = Previous; + } + } + + private enum StateEventTransition : byte { + None, + Add, + Update, + Remove + } } \ No newline at end of file -- cgit 1.5.1