about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2024-07-07 20:25:48 +0200
committerRory& <root@rory.gay>2024-07-07 20:25:48 +0200
commit3c0f32505405eb893d9260ab29381dff1dfbe036 (patch)
treefb950d2c0346ac4a7f298cf6642ea1d9aaa1fe29
parentSome cleanup, update libs (diff)
downloadMatrixUtils-main-pre-media-refactor.tar.xz
Add space parent update tool main-pre-media-refactor
m---------LibMatrix0
-rw-r--r--MatrixUtils.Web/Pages/Rooms/Space.razor87
-rw-r--r--MatrixUtils.Web/Pages/Tools/Index.razor3
-rw-r--r--MatrixUtils.Web/Pages/Tools/Room/FixCanonicalParentSpace.razor139
4 files changed, 188 insertions, 41 deletions
diff --git a/LibMatrix b/LibMatrix
-Subproject 16e314ed714f8b3e298c0ecf2ebfe67b48e5f69
+Subproject ecddd026574fac2b22495acef70a079661f4992
diff --git a/MatrixUtils.Web/Pages/Rooms/Space.razor b/MatrixUtils.Web/Pages/Rooms/Space.razor
index 01ab1c4..bec0a72 100644
--- a/MatrixUtils.Web/Pages/Rooms/Space.razor
+++ b/MatrixUtils.Web/Pages/Rooms/Space.razor
@@ -6,15 +6,15 @@
 
 <button onclick="@JoinAllRooms">Join all rooms</button>
 @foreach (var room in Rooms) {
-    <RoomListItem Room="room" ShowOwnProfile="true"></RoomListItem>
+    @* <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.StateKey).ThenBy(x => x.Type)) {
-        <p>@stateEvent.StateKey/@stateEvent.Type:</p>
+    @foreach (var stateEvent in States.OrderBy(x => x.Type).ThenBy(x => x.StateKey)) {
+        <p>@stateEvent.Type/@stateEvent.StateKey:</p>
         <pre>@stateEvent.RawContent.ToJson()</pre>
     }
 </details>
@@ -26,13 +26,15 @@
 
     private GenericRoom? Room { get; set; }
 
-    private StateEventResponse[] States { get; set; } = Array.Empty<StateEventResponse>();
+    private List<StateEventResponse> States { get; set; } = [];
     private List<GenericRoom> Rooms { get; } = new();
     private List<string> ServersInSpace { get; } = new();
+    private AuthenticatedHomeserverGeneric Homeserver { get; set; } = null!;
 
     protected override async Task OnInitializedAsync() {
         var hs = await RMUStorage.GetCurrentSessionOrNavigate();
         if (hs is null) return;
+        Homeserver = hs;
 
         Room = hs.GetRoom(RoomId.Replace('~', '.'));
 
@@ -55,49 +57,54 @@
                     break;
                 }
             }
+            States.Add(stateEvent);
         }
-        await base.OnInitializedAsync();
+        
+        StateHasChanged();
 
-    //     var state = await Room.GetStateAsync("");
-    //     if (state is not null) {
-    // // Console.WriteLine(state.Value.ToJson());
-    //         States = state.Value.Deserialize<StateEventResponse[]>()!;
-    //
-    //         foreach (var stateEvent in States) {
-    //             if (stateEvent.Type == "m.space.child") {
-    // // if (stateEvent.Content.ToJson().Length < 5) return;
-    //                 var roomId = stateEvent.StateKey;
-    //                 var room = hs.GetRoom(roomId);
-    //                 if (room is not null) {
-    //                     Rooms.Add(room);
-    //                 }
-    //             }
-    //             else if (stateEvent.Type == "m.room.member") {
-    //                 var serverName = stateEvent.StateKey.Split(':').Last();
-    //                 if (!ServersInSpace.Contains(serverName)) {
-    //                     ServersInSpace.Add(serverName);
-    //                 }
-    //             }
-    //         }
+        //     var state = await Room.GetStateAsync("");
+        //     if (state is not null) {
+        // // Console.WriteLine(state.Value.ToJson());
+        //         States = state.Value.Deserialize<StateEventResponse[]>()!;
+        //
+        //         foreach (var stateEvent in States) {
+        //             if (stateEvent.Type == "m.space.child") {
+        // // if (stateEvent.Content.ToJson().Length < 5) return;
+        //                 var roomId = stateEvent.StateKey;
+        //                 var room = hs.GetRoom(roomId);
+        //                 if (room is not null) {
+        //                     Rooms.Add(room);
+        //                 }
+        //             }
+        //             else if (stateEvent.Type == "m.room.member") {
+        //                 var serverName = stateEvent.StateKey.Split(':').Last();
+        //                 if (!ServersInSpace.Contains(serverName)) {
+        //                     ServersInSpace.Add(serverName);
+        //                 }
+        //             }
+        //         }
 
-    // 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!");
-    // }
+        // 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();
+        // await base.OnInitializedAsync();
     }
 
     private async Task JoinAllRooms() {
-        // List<Task<RoomIdResponse>> tasks = Rooms.Select(room => room.JoinAsync(ServersInSpace.ToArray())).ToList();
-        // await Task.WhenAll(tasks);
-        foreach (var room in Rooms) {
-            await room.JoinAsync(ServersInSpace.ToArray());
-        }
+        var joinedRooms = await Homeserver.GetJoinedRooms(); 
+        List<Task<RoomIdResponse>> tasks = Rooms
+            .Where(x=>!joinedRooms.Any(y=>y.RoomId == x.RoomId))
+            .Select(room => room.JoinAsync(ServersInSpace.ToArray())).ToList();
+        await Task.WhenAll(tasks);
+        // foreach (var room in Rooms) {
+            // await room.JoinAsync(ServersInSpace.ToArray());
+        // }
     }
 
 }
diff --git a/MatrixUtils.Web/Pages/Tools/Index.razor b/MatrixUtils.Web/Pages/Tools/Index.razor
index e68bb9a..7c73b27 100644
--- a/MatrixUtils.Web/Pages/Tools/Index.razor
+++ b/MatrixUtils.Web/Pages/Tools/Index.razor
@@ -15,7 +15,8 @@
 
 <h4 class="tool-category">Room tools</h4>
 <hr/>
-<a href="/Tools/Moderation/SpaceRestrictedJoins">Change space children join access rules</a><br/>
+<a href="/Tools/Room/SpaceRestrictedJoins">Change space children join access rules</a><br/>
+<a href="/Tools/Room/FixCanonicalParentSpace">Replace canonical parent space in rooms after upgrading space</a><br/>
 
 <h4 class="tool-category">Moderation tools</h4>
 <hr/>
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
+<h3>Fix canonical parent space</h3>
+<hr/>
+<p>Note: This requires relevant privileges in space children.</p>
+<p>You <b>MUST</b> click "check" before executing, otherwise nothing will happen!</p>
+<p>Also, worth noting that canonical parent will be set if none or multiple were set previously!</p>
+<br/>
+
+<span>Old space ID: </span>
+<InputText @bind-Value="@OldSpaceId"></InputText>
+<br/>
+<span>New space ID: </span>
+<InputText @bind-Value="@NewSpaceId"></InputText>
+<br/>
+
+<br/>
+<LinkButton OnClick="Check">Check</LinkButton>
+<LinkButton OnClick="Execute">Execute</LinkButton>
+<br/>
+
+<br/>
+@foreach (var line in log.Reverse()) {
+    <pre>@line</pre>
+}
+
+@code {
+    private ObservableCollection<string> 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<GenericRoom, CurrentStatus> _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<bool?>() == 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<string> CurrentCanonicalParents { get; set; } = [];
+
+        [JsonIgnore]
+        public List<StateEventResponse> RoomState { get; set; } = [];
+
+        [JsonIgnore]
+        public RoomPowerLevelEventContent PowerLevels { get; set; } = new();
+    }
+
+}
\ No newline at end of file