From 3c0f32505405eb893d9260ab29381dff1dfbe036 Mon Sep 17 00:00:00 2001 From: Rory& Date: Sun, 7 Jul 2024 20:25:48 +0200 Subject: Add space parent update tool --- .../Pages/Tools/Room/FixCanonicalParentSpace.razor | 139 +++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 MatrixUtils.Web/Pages/Tools/Room/FixCanonicalParentSpace.razor (limited to 'MatrixUtils.Web/Pages/Tools/Room') diff --git a/MatrixUtils.Web/Pages/Tools/Room/FixCanonicalParentSpace.razor b/MatrixUtils.Web/Pages/Tools/Room/FixCanonicalParentSpace.razor new file mode 100644 index 0000000..8222729 --- /dev/null +++ b/MatrixUtils.Web/Pages/Tools/Room/FixCanonicalParentSpace.razor @@ -0,0 +1,139 @@ +@page "/Tools/Room/FixCanonicalParentSpace" +@using System.Collections.ObjectModel +@using System.Text.Json.Serialization +@using ArcaneLibs.Extensions +@using LibMatrix +@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.RoomTypes +

Fix canonical parent space

+
+

Note: This requires relevant privileges in space children.

+

You MUST click "check" before executing, otherwise nothing will happen!

+

Also, worth noting that canonical parent will be set if none or multiple were set previously!

+
+ +Old space ID: + +
+New space ID: + +
+ +
+Check +Execute +
+ +
+@foreach (var line in log.Reverse()) { +
@line
+} + +@code { + private ObservableCollection log { get; set; } = new(); + + [Parameter, SupplyParameterFromQuery(Name = "OldSpaceId")] + public string OldSpaceId { get; set; } + + [Parameter, SupplyParameterFromQuery(Name = "NewSpaceId")] + public string NewSpaceId { get; set; } + + private AuthenticatedHomeserverGeneric hs { get; set; } + private Dictionary _children = new(); + private string[] NewVias = []; + + protected override async Task OnInitializedAsync() { + log.CollectionChanged += (sender, args) => StateHasChanged(); + hs = await RMUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + + log.Add($"Signed in as {hs.UserId}... Ready for input!"); + StateHasChanged(); + Console.WriteLine("Rerendered!"); + await base.OnInitializedAsync(); + } + + private async Task Check() { + _children.Clear(); + var newSpace = hs.GetRoom(NewSpaceId).AsSpace; + + await foreach (var room in newSpace.GetChildrenAsync()) { + try { + var status = new CurrentStatus(); + status.PowerLevels = await room.GetPowerLevelsAsync(); + status.HasPermission = status.PowerLevels.UserHasStatePermission(hs.UserId, SpaceParentEventContent.EventId); + + status.RoomState = await room.GetFullStateAsListAsync(); + var parentEvents = status.RoomState.Where(x => x.Type == SpaceParentEventContent.EventId); + + status.CurrentCanonicalParents = parentEvents + .Where(x => x.RawContent?["canonical"]?.GetValue() == true) + .Select(x => x.StateKey) + .ToList()!; + + // Just for end user usage: + status.Name = await room.GetNameOrFallbackAsync(maxMemberNames: 4); + + log.Add($"{room.RoomId}: {status.ToJson()}"); + _children.Add(room, status); + } + catch (MatrixException e) { + if (e is { ErrorCode: "M_FORBIDDEN" }) { + log.Add($"Warning: not in room {room.RoomId}!"); + } + else { + log.Add($"Error checking {room.RoomId}: {e.Message}"); + } + } + } + + log.Add("Calculating new Via's..."); + // Not compliant with spec recommendations, but selecting top 10 servers by member count is a fairly safe bet + var newSpaceMembers = await newSpace.GetMembersByHomeserverAsync(); + NewVias = newSpaceMembers + .OrderByDescending(x => x.Value.Count) + .Select(x=>x.Key) + .Take(10) + .ToArray(); + log.Add("New vias: " + NewVias.ToJson()); + log.Add("Done! Verify that everything looks okay, and then click Execute!"); + } + + private async Task Execute() { + foreach (var (room, status) in _children) { + if (!status.HasPermission) { + log.Add($"Skipping {room.RoomId} due to lack of permissions!"); + continue; + } + + log.Add($"Updating parent spaces for {room.RoomId}"); + foreach (var parentId in status.CurrentCanonicalParents) { + var oldParentEvent = await room.GetStateEventAsync(SpaceParentEventContent.EventId, parentId); + oldParentEvent.RawContent!["canonical"] = false; + await room.SendStateEventAsync(SpaceParentEventContent.EventId, parentId, oldParentEvent.RawContent); + } + + var newParentEvent = new SpaceParentEventContent() { + Canonical = true, + Via = NewVias + }; + + await room.SendStateEventAsync(SpaceParentEventContent.EventId, NewSpaceId, newParentEvent); + } + + log.Add("Done!"); + } + + private class CurrentStatus { + public string Name { get; set; } = ""; + public bool HasPermission { get; set; } + public List CurrentCanonicalParents { get; set; } = []; + + [JsonIgnore] + public List RoomState { get; set; } = []; + + [JsonIgnore] + public RoomPowerLevelEventContent PowerLevels { get; set; } = new(); + } + +} \ No newline at end of file -- cgit 1.4.1