diff options
m--------- | LibMatrix | 0 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Space.razor | 87 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Tools/Index.razor | 3 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Tools/Room/FixCanonicalParentSpace.razor | 139 |
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 |