about summary refs log tree commit diff
diff options
context:
space:
mode:
m---------ArcaneLibs0
m---------LibMatrix0
-rw-r--r--MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs2
-rw-r--r--MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs2
-rw-r--r--MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor2
-rw-r--r--MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor9
-rw-r--r--MatrixRoomUtils.Web/Pages/Index.razor52
-rw-r--r--MatrixRoomUtils.Web/Pages/Rooms/Create.razor18
-rw-r--r--MatrixRoomUtils.Web/Pages/Rooms/Index.razor2
-rw-r--r--MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor4
-rw-r--r--MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor10
-rw-r--r--MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor77
-rw-r--r--MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor4
-rw-r--r--MatrixRoomUtils.Web/Pages/User/DMManager.razor14
-rw-r--r--MatrixRoomUtils.Web/Pages/User/Profile.razor (renamed from MatrixRoomUtils.Web/Pages/User/Manage.razor)44
-rw-r--r--MatrixRoomUtils.Web/Shared/InlineUserItem.razor10
-rw-r--r--MatrixRoomUtils.Web/Shared/NavMenu.razor2
-rw-r--r--MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor2
-rw-r--r--MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor2
-rw-r--r--[-rwxr-xr-x]MatrixRoomUtils.sln0
-rw-r--r--MatrixRoomUtils.sln.DotSettings.user12
m---------MxApiExtensions0
22 files changed, 125 insertions, 143 deletions
diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject 7c8f845f2376890aeb9d564f078ee93c3157d57
+Subproject da742ff53b78e3b5f41402e723e21e62a2f4045
diff --git a/LibMatrix b/LibMatrix
-Subproject 478fc1b0ef855530e1e93c5212d154280f9d7dd
+Subproject b75135d8cdb702423d693558ffaec3f025264b9
diff --git a/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs b/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs
index c7567ce..aaa1bee 100644
--- a/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs
+++ b/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs
@@ -51,7 +51,7 @@ public partial class RoomListEntry : UserControl {
                 var storageKey = $"media/{mxcUrl.Replace("mxc://", "").Replace("/", ".")}";
                 try {
                     if (!await storage.ObjectExistsAsync(storageKey))
-                        await storage.SaveStreamAsync(storageKey, await hs._httpClient.GetStreamAsync(resolvedUrl));
+                        await storage.SaveStreamAsync(storageKey, await hs.ClientHttpClient.GetStreamAsync(resolvedUrl));
 
                     RoomIcon.Source = new Bitmap(await storage.LoadStreamAsync(storageKey) ?? throw new NullReferenceException());
                 }
diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
index bea1ced..44cd988 100644
--- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
+++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
@@ -78,7 +78,7 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate {
                     { "org.matrix.msc3401.call.member", 50 }
                 },
                 Users = new() {
-                    // { RuntimeCache.CurrentHomeServer.UserId, 100 }
+                    // { RuntimeCache.CurrentHomeserver.UserId, 100 }
                     //TODO: re-implement this
                 }
             },
diff --git a/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor b/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
index f7e6aec..4b2dc4f 100644
--- a/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
+++ b/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
@@ -51,7 +51,7 @@ else {
     string get_request_result { get; set; } = "";
 
     private async Task SendGetRequest() {
-        var field = typeof(RemoteHomeServer).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField"));
+        var field = typeof(RemoteHomeserver).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField"));
         var hs = await MRUStorage.GetCurrentSessionOrNavigate();
         if (hs == null) return;
         var httpClient = field.GetValue(hs) as MatrixHttpClient;
diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
index f972236..59ce70f 100644
--- a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
+++ b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
@@ -1,4 +1,5 @@
 @page "/HSAdmin"
+@using LibMatrix.Homeservers
 <h3>Homeserver Admininistration</h3>
 <hr/>
 
@@ -7,5 +8,13 @@
 <a href="/HSAdmin/RoomQuery">Query rooms</a>
 
 @code {
+    public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
+
+    protected override async Task OnInitializedAsync() {
+        Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (Homeserver is null) return;
+        await base.OnInitializedAsync();
+    }
+
 
 }
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
index 00f3253..74dd651 100644
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Index.razor
@@ -14,28 +14,31 @@ Small collection of tools to do not-so-everyday things.
 <hr/>
 <form>
     <table>
-        @foreach (var (auth, user) in _users.OrderByDescending(x => x.Value.RoomCount)) {
-            var _auth = auth;
+        @foreach (var __auth in _auth.OrderByDescending(x => x.UserInfo.RoomCount)) {
+            var _auth = __auth.UserAuth;
             <tr class="user-entry">
                 <td>
-                    <img class="avatar" src="@user.AvatarUrl"/>
+                    <img class="avatar" src="@__auth.UserInfo.AvatarUrl"/>
                 </td>
                 <td class="user-info">
                     @* <div class="user-info"> *@
                     <p>
                         <input type="radio" name="csa" checked="@(_currentSession.AccessToken == _auth.AccessToken)" @onclick="@(() => SwitchSession(_auth))" style="text-decoration-line: unset;"/>
-                        <b>@user.DisplayName</b> on <b>@_auth.Homeserver</b>
-                        @if (_auth.Proxy != null) {
-                            <span class="badge badge-info"> (proxied via @_auth.Proxy)</span>
-                        }
-                    </p>
-                    <p>Member of @user.RoomCount rooms</p>
+                        <b>@__auth.UserInfo.DisplayName</b> on <b>@_auth.Homeserver</b><br/>
 
-                    <p>Not proxied</p>
+                    </p>
+                    <span style="display: inline-block; width: 128px;">@__auth.UserInfo.RoomCount rooms</span>
+                    <span style="color: #888888">@__auth.ServerVersion.Server.Name @__auth.ServerVersion.Server.Version</span>
+                    @if (_auth.Proxy != null) {
+                        <span class="badge badge-info"> (proxied via @_auth.Proxy)</span>
+                    }
+                    else {
+                        <p>Not proxied</p>
+                    }
                 </td>
                 <td>
                     <p>
-                        <LinkButton OnClick="@(()=>ManageUser(_auth))">Manage</LinkButton>
+                        <LinkButton OnClick="@(() => ManageUser(_auth))">Manage</LinkButton>
                         <LinkButton OnClick="@(() => RemoveUser(_auth))">Remove</LinkButton>
                         <LinkButton OnClick="@(() => RemoveUser(_auth, true))">Log out</LinkButton>
                     </p>
@@ -48,11 +51,19 @@ Small collection of tools to do not-so-everyday things.
 
 @code
 {
-    private Dictionary<UserAuth, UserInfo> _users = new();
+    private class AuthInfo {
+        public UserAuth UserAuth { get; set; }
+        public UserInfo UserInfo { get; set; }
+        public ServerVersionResponse ServerVersion { get; set; }
+    }
+
+    // private Dictionary<UserAuth, UserInfo> _users = new();
+    private List<AuthInfo> _auth = new();
 
     protected override async Task OnInitializedAsync() {
         _currentSession = await MRUStorage.GetCurrentToken();
-        _users.Clear();
+    // _users.Clear();
+        _auth.Clear();
         var tokens = await MRUStorage.GetAllTokens();
         var profileTasks = tokens.Select(async token => {
             UserInfo userInfo = new();
@@ -73,7 +84,12 @@ Small collection of tools to do not-so-everyday things.
             Console.WriteLine(profile.ToJson());
             userInfo.AvatarUrl = string.IsNullOrWhiteSpace(profile.AvatarUrl) ? "https://api.dicebear.com/6.x/identicon/svg?seed=" + hs.WhoAmI.UserId : hs.ResolveMediaUri(profile.AvatarUrl);
             userInfo.RoomCount = (await roomCountTask).Count;
-            _users.Add(token, userInfo);
+    // _users.Add(token, userInfo);
+            _auth.Add(new() {
+                UserInfo = userInfo,
+                UserAuth = token,
+                ServerVersion = await hs.GetServerVersionAsync()
+            });
     // StateHasChanged();
         });
         await Task.WhenAll(profileTasks);
@@ -93,8 +109,8 @@ Small collection of tools to do not-so-everyday things.
             }
         }
         catch (Exception e) {
-            if(e is MatrixException {ErrorCode: "M_UNKNOWN_TOKEN" }) {
-                //todo: handle this
+            if (e is MatrixException {ErrorCode: "M_UNKNOWN_TOKEN" }) {
+    //todo: handle this
                 return;
             }
             Console.WriteLine(e);
@@ -115,6 +131,6 @@ Small collection of tools to do not-so-everyday things.
 
     private async Task ManageUser(UserAuth auth) {
         await SwitchSession(auth);
-        NavigationManager.NavigateTo("/User/Manage");
+        NavigationManager.NavigateTo("/User/Profile");
     }
-}
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor
index 04dcdcc..5823757 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor
@@ -42,7 +42,7 @@
                 }
                 else {
                     <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox>
-                    <p>(#<FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox>:@HomeServer.WhoAmI.UserId.Split(':').Last())</p>
+                    <p>(#<FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox>:@Homeserver.WhoAmI.UserId.Split(':').Last())</p>
                 }
             </td>
         </tr>
@@ -88,7 +88,7 @@
         <tr>
             <td>Room icon:</td>
             <td>
-                <img src="@hsResolver.ResolveMediaUri(HomeServer.ServerName, roomAvatarEvent.Url)" style="width: 128px; height: 128px; border-radius: 50%;"/>
+                <img src="@hsResolver.ResolveMediaUri(Homeserver.ServerName, roomAvatarEvent.Url)" style="width: 128px; height: 128px; border-radius: 50%;"/>
                 <div style="display: inline-block; vertical-align: middle;">
                     <FancyTextBox @bind-Value="@roomAvatarEvent.Url"></FancyTextBox><br/>
                     <InputFile OnChange="RoomIconFilePicked"></InputFile>
@@ -156,7 +156,7 @@
                 <details>
                     <summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary>
                     @* <button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button> *@
-                    @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != HomeServer.UserId)) {
+                    @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != Homeserver.UserId)) {
                         <UserListItem UserId="@member.StateKey"></UserListItem>
                     }
                 </details>
@@ -249,7 +249,7 @@
     private CreateRoomRequest? creationEvent { get; set; }
 
     private Dictionary<string, CreateRoomRequest>? Presets { get; set; } = new();
-    private AuthenticatedHomeserverGeneric? HomeServer { get; set; }
+    private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
 
     private MatrixException? _matrixException { get; set; }
 
@@ -259,8 +259,8 @@
     private RoomAvatarEventContent? roomAvatarEvent => creationEvent?["m.room.avatar"].TypedContent as RoomAvatarEventContent;
 
     protected override async Task OnInitializedAsync() {
-        HomeServer = await MRUStorage.GetCurrentSessionOrNavigate();
-        if (HomeServer is null) return;
+        Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (Homeserver is null) return;
 
         foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList()) {
             Console.WriteLine($"Found room creation template in class: {x.FullName}");
@@ -280,7 +280,7 @@
     private void JsonChanged() => Console.WriteLine(creationEvent.ToJson());
 
     private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) {
-        var res = await HomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType);
+        var res = await Homeserver.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType);
         Console.WriteLine(res);
         (creationEvent["m.room.avatar"].TypedContent as RoomAvatarEventContent).Url = res;
         StateHasChanged();
@@ -291,7 +291,7 @@
         Console.WriteLine(creationEvent.ToJson());
         creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)");
         try {
-            var id = await HomeServer.CreateRoom(creationEvent);
+            var id = await Homeserver.CreateRoom(creationEvent);
         }
         catch (MatrixException e) {
             _matrixException = e;
@@ -299,7 +299,7 @@
     }
 
     private void InviteMember(string mxid) {
-        if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && HomeServer.UserId != mxid)
+        if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && Homeserver.UserId != mxid)
             creationEvent.InitialState.Add(new StateEvent {
                 Type = "m.room.member",
                 StateKey = mxid,
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
index 516e9ca..fd32cb3 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
@@ -208,7 +208,7 @@
                         joinedRoom.Value.State.Events.RemoveAll(x => x.Type == "m.room.member" && x.StateKey != Homeserver.WhoAmI?.UserId);
                         // We can't trust servers to give us what we ask for, and this ruins performance
                         // Thanks, Conduit.
-                        joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.Types?.Contains(x.Type) ?? false);
+                        joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.Types?.Contains(x.Type) == false);
                         if(filter.Room?.State?.NotSenders?.Any() ?? false)
                             joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.NotSenders?.Contains(x.Sender) ?? false);
                         
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
index 3cc6a15..adedbd3 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
@@ -192,7 +192,7 @@ else {
     private bool _enableAvatars;
 
     static readonly Dictionary<string, string?> avatars = new();
-    static readonly Dictionary<string, RemoteHomeServer> servers = new();
+    static readonly Dictionary<string, RemoteHomeserver> servers = new();
 
     public static List<StateEventResponse> PolicyEvents { get; set; } = new();
 
@@ -229,7 +229,7 @@ else {
         try {
             if (avatars.ContainsKey(userId)) return;
             var hs = userId.Split(':')[1];
-            var server = servers.ContainsKey(hs) ? servers[hs] : new RemoteHomeServer(userId.Split(':')[1]);
+            var server = servers.ContainsKey(hs) ? servers[hs] : new RemoteHomeserver(userId.Split(':')[1]);
             if (!servers.ContainsKey(hs)) servers.Add(hs, server);
             var profile = await server.GetProfileAsync(userId);
             avatars.Add(userId, await hsResolver.ResolveMediaUri(server.BaseUrl, profile.AvatarUrl));
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor b/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor
index e22be4a..1f4a923 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor
@@ -11,7 +11,7 @@
 @foreach (var evt in Events) {
     <div type="@evt.Type" key="@evt.StateKey" itemid="@evt.EventId">
         <DynamicComponent Type="@ComponentType(evt)"
-                          Parameters="@(new Dictionary<string, object> { { "Event", evt }, { "Events", Events }, { "HomeServer", HomeServer!} })">
+                          Parameters="@(new Dictionary<string, object> { { "Event", evt }, { "Events", Events }, { "Homeserver", Homeserver!} })">
         </DynamicComponent>
     </div>
 }
@@ -24,13 +24,13 @@
     private List<MessagesResponse> Messages { get; } = new();
     private List<StateEventResponse> Events { get; } = new();
 
-    private AuthenticatedHomeserverGeneric? HomeServer { get; set; }
+    private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
 
     protected override async Task OnInitializedAsync() {
         Console.WriteLine("RoomId: " + RoomId);
-        HomeServer = await MRUStorage.GetCurrentSessionOrNavigate();
-        if (HomeServer is null) return;
-        var room = HomeServer.GetRoom(RoomId);
+        Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (Homeserver is null) return;
+        var room = Homeserver.GetRoom(RoomId);
         MessagesResponse? msgs = null;
         do {
             msgs = await room.GetMessagesAsync(limit: 1000, from: msgs?.End, dir: "b");
diff --git a/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor
index 939838e..0ab0bd2 100644
--- a/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor
+++ b/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor
@@ -24,22 +24,22 @@
     }
 }
 else {
-    @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) {
+    @foreach (var server in Homeservers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) {
         <p>@server.Server - @server.KnownUserCount</p>
     }
 }
 <hr/>
 
 @code {
-    List<HomeServerInfo> HomeServers = new();
+    List<HomeserverInfo> Homeservers = new();
     bool IsFinished { get; set; }
-    HomeServerInfoQueryProgress QueryProgress { get; set; } = new();
+    HomeserverInfoQueryProgress QueryProgress { get; set; } = new();
     AuthenticatedHomeserverGeneric hs { get; set; }
     protected override async Task OnInitializedAsync() {
         hs = await MRUStorage.GetCurrentSessionOrNavigate();
         if (hs is null) return;
         var sw = Stopwatch.StartNew();
-        HomeServers = await GetHomeservers(progressCallback: async progress => {
+        Homeservers = await GetHomeservers(progressCallback: async progress => {
             if (sw.ElapsedMilliseconds > 1000) {
                 Console.WriteLine("Progress updated...");
                 QueryProgress = progress;
@@ -59,26 +59,25 @@ else {
         await base.OnInitializedAsync();
     }
 
-    private async Task<List<HomeServerInfo>> GetHomeservers(int memberLimit = 1000, Func<HomeServerInfoQueryProgress, Task<bool>>? progressCallback = null) {
-        HomeServerInfoQueryProgress progress = new();
-        List<HomeServerInfo> homeServers = new();
+    private async Task<List<HomeserverInfo>> GetHomeservers(int memberLimit = 1000, Func<HomeserverInfoQueryProgress, Task<bool>>? progressCallback = null) {
+        HomeserverInfoQueryProgress progress = new();
+        List<HomeserverInfo> homeServers = new();
 
         var rooms = await hs.GetJoinedRooms();
         progress.TotalRooms = rooms.Count;
 
         var semaphore = new SemaphoreSlim(4);
-        var semLock = new SemaphoreSlim(1);
         var tasks = rooms.Select(async room => {
             await semaphore.WaitAsync();
-            progress.ProcessedUsers.Add(room, new HomeServerInfoQueryProgress.State());
+            progress.ProcessedUsers.Add(room, new HomeserverInfoQueryProgress.State());
             Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})");
-            var states = room.GetFullStateAsync();
+            var states = room.GetMembersAsync();
             await foreach (var state in states) {
                 if (state.Type is not "m.room.member") continue;
                 progress.ProcessedUsers[room].Total++;
 
                 if (homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) continue;
-                homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] });
+                homeServers.Add(new HomeserverInfo { Server = state.StateKey.Split(':')[1] });
                 Console.WriteLine($"Added new homeserver {state.StateKey.Split(':')[1]}");
             }
             semaphore.Release();
@@ -86,56 +85,8 @@ else {
             progress.ProcessedRooms++;
             if (progressCallback is not null)
                 await progressCallback.Invoke(progress);
-
-
-
-    //         states.RemoveAll(x => x.Type != "m.room.member" || (x.TypedContent as RoomMemberEventContent).Membership != "join");
-    //         Console.WriteLine($"Room {room.RoomId} has {states.Count} members");
-    //         if (states.Count > memberLimit) {
-    //             Console.WriteLine("Skipping!");
-    //             semaphore.Release();
-    //             progress.ProcessedUsers.Remove(room);
-    //             progress.TotalRooms--;
-    //             return;
-    //         }
-    //         progress.ProcessedUsers[room].Total = states.Count;
-    //         var updateInterval = progress.ProcessedUsers[room].Total >= 1000 ? 1000 : 100;
-    //         while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) {
-    //             progress.ProcessedUsers[room].Blocked = true;
-    //             await Task.Delay(1000);
-    // // if(progressCallback is not null)
-    // //     await progressCallback.Invoke(progress);
-    //         }
-    //         progress.ProcessedUsers[room].Blocked = false;
-    //         var processedStates = 0;
-    //         foreach (var state in states) {
-    //             await semLock.WaitAsync();
-    //             semLock.Release();
-    //             if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) {
-    //                 progress.ProcessedUsers[room].Slowmode = true;
-    //                 await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100);
-    //             }
-    //             else {
-    //                 progress.ProcessedUsers[room].Slowmode = false;
-    //             }
-    //             if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) {
-    //                 homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] });
-    //             }
-    //             var hs = homeServers.First(x => x.Server == state.StateKey.Split(':')[1]);
-    //             if (!hs.KnownUsers.Contains(state.StateKey.Split(':')[0]))
-    //                 hs.KnownUsers.Add(state.StateKey.Split(':')[0]);
-    //             if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback is not null) {
-    //                 await semLock.WaitAsync();
-    //                 var _ = await progressCallback.Invoke(progress);
-    //                 semLock.Release();
-    //             }
-    //         }
-            // Console.WriteLine("Collected states!");
-            // progress.ProcessedRooms++;
-            // progress.ProcessedUsers[room].IsFinished = true;
-            // progressCallback?.Invoke(progress);
-            // semaphore.Release();
         });
+        // var results = tasks.ToAsyncEnumerable();
         await Task.WhenAll(tasks);
 
         Console.WriteLine("Calculating member counts...");
@@ -145,17 +96,17 @@ else {
         return homeServers;
     }
 
-    class HomeServerInfo {
+    class HomeserverInfo {
         public string Server { get; set; }
         public int? KnownUserCount { get; set; }
         public List<string> KnownUsers { get; } = new();
     }
 
-    class HomeServerInfoQueryProgress {
+    class HomeserverInfoQueryProgress {
         public int ProcessedRooms { get; set; }
         public int TotalRooms { get; set; }
         public Dictionary<GenericRoom, State> ProcessedUsers { get; } = new();
-        public List<HomeServerInfo> CurrentState { get; set; } = new();
+        public List<HomeserverInfo> CurrentState { get; set; } = new();
 
         public class State {
             public int Processed { get; set; }
diff --git a/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor
index a376efa..59ec79e 100644
--- a/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor
+++ b/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor
@@ -89,12 +89,12 @@
         homeservers.Clear();
         var lines = content.Split("\n");
 
-        var rhs = new RemoteHomeServer("rory.gay");
+        var rhs = new RemoteHomeserver("rory.gay");
         var sem = new SemaphoreSlim(128, 128);
         lines.ToList().ForEach(async line => {
             await sem.WaitAsync();
             try {
-                homeservers.Add(await hsResolver.ResolveHomeserverFromWellKnown(line));
+                homeservers.Add((await hsResolver.ResolveHomeserverFromWellKnown(line)).client);
                 StateHasChanged();
             }
             catch (Exception e) {
diff --git a/MatrixRoomUtils.Web/Pages/User/DMManager.razor b/MatrixRoomUtils.Web/Pages/User/DMManager.razor
index 04ff6e5..92e1bc2 100644
--- a/MatrixRoomUtils.Web/Pages/User/DMManager.razor
+++ b/MatrixRoomUtils.Web/Pages/User/DMManager.razor
@@ -16,7 +16,7 @@
 
 @code {
     private string? _status;
-    private AuthenticatedHomeserverGeneric? HomeServer { get; set; }
+    private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
     private Dictionary<UserProfileResponse, List<RoomInfo>> DMRooms { get; set; } = new();
 
     public string? Status {
@@ -28,19 +28,19 @@
     }
 
     protected override async Task OnInitializedAsync() {
-        HomeServer = await MRUStorage.GetCurrentSessionOrNavigate();
-        if (HomeServer is null) return;
+        Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (Homeserver is null) return;
         Status = "Loading global profile...";
-        if (HomeServer.WhoAmI?.UserId is null) return;
+        if (Homeserver.WhoAmI?.UserId is null) return;
 
         Status = "Loading DM list from account data...";
-        var dms = await HomeServer.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
+        var dms = await Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
         DMRooms.Clear();
         foreach (var (userId, rooms) in dms) {
             var roomList = new List<RoomInfo>();
-            DMRooms.Add(await HomeServer.GetProfileAsync(userId), roomList);
+            DMRooms.Add(await Homeserver.GetProfileAsync(userId), roomList);
             foreach (var room in rooms) {
-                roomList.Add(new RoomInfo() { Room = HomeServer.GetRoom(room) });
+                roomList.Add(new RoomInfo() { Room = Homeserver.GetRoom(room) });
             }
             StateHasChanged();
         }
diff --git a/MatrixRoomUtils.Web/Pages/User/Manage.razor b/MatrixRoomUtils.Web/Pages/User/Profile.razor
index eac47e8..ae3fb76 100644
--- a/MatrixRoomUtils.Web/Pages/User/Manage.razor
+++ b/MatrixRoomUtils.Web/Pages/User/Profile.razor
@@ -3,17 +3,17 @@
 @using LibMatrix.EventTypes.Spec.State
 @using ArcaneLibs.Extensions
 @using LibMatrix.Responses
-<h3>Manage Profile - @HomeServer?.WhoAmI?.UserId</h3>
+<h3>Manage Profile - @Homeserver?.WhoAmI?.UserId</h3>
 <hr/>
 
-@if (Profile is not null) {
+@if (NewProfile is not null) {
     <h4>Profile</h4>
     <hr/>
 
-    <img src="@HomeServer.ResolveMediaUri(Profile.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/>
+    <img src="@Homeserver.ResolveMediaUri(NewProfile.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/>
     <div style="display: inline-block; vertical-align: middle;">
-        <span>Display name: </span><FancyTextBox @bind-Value="@Profile.DisplayName"></FancyTextBox><br/>
-        <span>Avatar URL: </span><FancyTextBox @bind-Value="@Profile.AvatarUrl"></FancyTextBox>
+        <span>Display name: </span><FancyTextBox @bind-Value="@NewProfile.DisplayName"></FancyTextBox><br/>
+        <span>Avatar URL: </span><FancyTextBox @bind-Value="@NewProfile.AvatarUrl"></FancyTextBox>
         <InputFile OnChange="@AvatarChanged"></InputFile><br/>
         <LinkButton OnClick="@(() => UpdateProfile())">Update profile</LinkButton>
         <LinkButton OnClick="@(() => UpdateProfile(true))">Update profile (restore room overrides)</LinkButton>
@@ -28,7 +28,7 @@
         @foreach (var (roomId, roomProfile) in RoomProfiles.OrderBy(x=>RoomNames.TryGetValue(x.Key, out var _name) ? _name : x.Key)) {
             <details class="details-compact">
                 <summary style="@(roomProfile.DisplayName == OldProfile.DisplayName && roomProfile.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")">@(RoomNames.TryGetValue(roomId, out var name) ? name : roomId)</summary>
-                <img src="@HomeServer.ResolveMediaUri(roomProfile.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/>
+                <img src="@Homeserver.ResolveMediaUri(roomProfile.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/>
                 <div style="display: inline-block; vertical-align: middle;">
                     <span>Display name: </span><FancyTextBox BackgroundColor="@(roomProfile.DisplayName == OldProfile.DisplayName ? "" : "#ffff0033")" @bind-Value="@roomProfile.DisplayName"></FancyTextBox><br/>
                     <span>Avatar URL: </span><FancyTextBox BackgroundColor="@(roomProfile.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")" @bind-Value="@roomProfile.AvatarUrl"></FancyTextBox>
@@ -48,8 +48,8 @@
 @code {
     private string? _status = null;
 
-    private AuthenticatedHomeserverGeneric? HomeServer { get; set; }
-    private UserProfileResponse? Profile { get; set; }
+    private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
+    private UserProfileResponse? NewProfile { get; set; }
     private UserProfileResponse? OldProfile { get; set; }
 
     private string? Status {
@@ -61,14 +61,14 @@
     private Dictionary<string, string> RoomNames { get; set; } = new();
 
     protected override async Task OnInitializedAsync() {
-        HomeServer = await MRUStorage.GetCurrentSessionOrNavigate();
-        if (HomeServer is null) return;
+        Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (Homeserver is null) return;
         Status = "Loading global profile...";
-        if (HomeServer.WhoAmI?.UserId is null) return;
-        Profile = (await HomeServer.GetProfileAsync(HomeServer.WhoAmI.UserId)).DeepClone();
-        OldProfile = (await HomeServer.GetProfileAsync(HomeServer.WhoAmI.UserId)).DeepClone();
+        if (Homeserver.WhoAmI?.UserId is null) return;
+        NewProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)).DeepClone();
+        OldProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)).DeepClone();
         Status = "Loading room profiles...";
-        var roomProfiles = HomeServer.GetRoomProfilesAsync();
+        var roomProfiles = Homeserver.GetRoomProfilesAsync();
         await foreach (var (roomId, roomProfile) in roomProfiles) {
             // Status = $"Got profile for {roomId}...";
             RoomProfiles[roomId] = roomProfile.DeepClone();
@@ -76,8 +76,8 @@
         StateHasChanged();
         Status = "Room profiles loaded, loading room names...";
 
-        var roomNameTasks = RoomProfiles.Keys.Select(x => HomeServer.GetRoom(x)).Select(async x => {
-            var name = await x.GetNameAsync();
+        var roomNameTasks = RoomProfiles.Keys.Select(x => Homeserver.GetRoom(x)).Select(async x => {
+            var name = await x.GetNameOrFallbackAsync();
             return new KeyValuePair<string, string?>(x.RoomId, name);
         }).ToAsyncEnumerable();
         await foreach (var (roomId, roomName) in roomNameTasks) {
@@ -92,22 +92,22 @@
     }
 
     private async Task AvatarChanged(InputFileChangeEventArgs arg) {
-        var res = await HomeServer.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
+        var res = await Homeserver.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
         Console.WriteLine(res);
-        Profile.AvatarUrl = res;
+        NewProfile.AvatarUrl = res;
         StateHasChanged();
     }
 
     private async Task UpdateProfile(bool restoreRoomProfiles = false) {
         Status = "Busy processing global profile update, please do not leave this page...";
         StateHasChanged();
-        await HomeServer.UpdateProfileAsync(Profile, restoreRoomProfiles);
+        await Homeserver.UpdateProfileAsync(NewProfile, restoreRoomProfiles);
         Status = null;
         StateHasChanged();
         await OnInitializedAsync();
     }
     private async Task RoomAvatarChanged(InputFileChangeEventArgs arg, string roomId) {
-        var res = await HomeServer.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
+        var res = await Homeserver.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
         Console.WriteLine(res);
         RoomProfiles[roomId].AvatarUrl = res;
         StateHasChanged();
@@ -116,8 +116,8 @@
     private async Task UpdateRoomProfile(string roomId) {
         Status = "Busy processing room profile update, please do not leave this page...";
         StateHasChanged();
-        var room = HomeServer.GetRoom(roomId);
-        await room.SendStateEventAsync("m.room.member", HomeServer.WhoAmI.UserId, RoomProfiles[roomId]);
+        var room = Homeserver.GetRoom(roomId);
+        await room.SendStateEventAsync("m.room.member", Homeserver.WhoAmI.UserId, RoomProfiles[roomId]);
         Status = null;
         StateHasChanged();
     }
diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
index 7bc88e5..dc58210 100644
--- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
@@ -36,14 +36,14 @@
     public string? ProfileName { get; set; } = null;
 
     [Parameter]
-    public AuthenticatedHomeserverGeneric? HomeServer { get; set; }
+    public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
 
     private static SemaphoreSlim _semaphoreSlim = new(128);
 
     protected override async Task OnInitializedAsync() {
         await base.OnInitializedAsync();
-        HomeServer ??= await MRUStorage.GetCurrentSessionOrNavigate();
-        if(HomeServer is null) return;
+        Homeserver ??= await MRUStorage.GetCurrentSessionOrNavigate();
+        if(Homeserver is null) return;
 
         await _semaphoreSlim.WaitAsync();
 
@@ -58,11 +58,11 @@
         }
 
         if (User is null && UserId is not null) {
-            User ??= await HomeServer.GetProfileAsync(UserId);
+            User ??= await Homeserver.GetProfileAsync(UserId);
         }
 
 
-        ProfileAvatar ??= HomeServer.ResolveMediaUri(User.AvatarUrl);
+        ProfileAvatar ??= Homeserver.ResolveMediaUri(User.AvatarUrl);
         ProfileName ??= User.DisplayName;
 
         _semaphoreSlim.Release();
diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor
index daa4a52..f232940 100644
--- a/MatrixRoomUtils.Web/Shared/NavMenu.razor
+++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor
@@ -55,7 +55,7 @@
 
         <div class="nav-item px-3">
             <NavLink class="nav-link" href="HSAdmin">
-                <span class="oi oi-plus" aria-hidden="true"></span> Synapse administration
+                <span class="oi oi-plus" aria-hidden="true"></span> Homeserver admin
             </NavLink>
         </div>
 
diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
index e2c1285..cbe542a 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
@@ -9,7 +9,7 @@
         <div class="room-list-item">
             <RoomListItem RoomInfo="@room" ShowOwnProfile="@(roomType == "Room")"></RoomListItem>
             @* @if (RoomVersionDangerLevel(room) != 0 && *@
-            @*      (room.StateEvents.FirstOrDefault(x=>x.Type == "m.room.power_levels")?.TypedContent is RoomPowerLevelEventContent powerLevels && powerLevels.UserHasPermission(HomeServer.UserId, "m.room.tombstone"))) { *@
+            @*      (room.StateEvents.FirstOrDefault(x=>x.Type == "m.room.power_levels")?.TypedContent is RoomPowerLevelEventContent powerLevels && powerLevels.UserHasPermission(Homeserver.UserId, "m.room.tombstone"))) { *@
             @*     <MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton Color="@(RoomVersionDangerLevel(room) == 2 ? "#ff0000" : "#ff8800")" href="@($"/Rooms/Create?Import={room.Room.RoomId}")">Upgrade room</MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton> *@
             @* } *@
             <LinkButton href="@($"/Rooms/{room.Room.RoomId}/Timeline")">View timeline</LinkButton>
diff --git a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
index 075e402..ed4dceb 100644
--- a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
+++ b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
@@ -15,7 +15,7 @@
             <i>@Event.StateKey changed their display name to @(roomMemberData.DisplayName ?? Event.Sender)</i>
             break;
         case "join":
-            <i><InlineUserItem User="@(new UserProfileResponse())" HomeServer="@Homeserver" UserId="@Event.StateKey"></InlineUserItem> joined</i>
+            <i><InlineUserItem User="@(new UserProfileResponse())" Homeserver="@Homeserver" UserId="@Event.StateKey"></InlineUserItem> joined</i>
             break;
         case "leave":
             <i>@Event.StateKey left</i>
diff --git a/MatrixRoomUtils.sln b/MatrixRoomUtils.sln
index 3ea06bf..3ea06bf 100755..100644
--- a/MatrixRoomUtils.sln
+++ b/MatrixRoomUtils.sln
diff --git a/MatrixRoomUtils.sln.DotSettings.user b/MatrixRoomUtils.sln.DotSettings.user
index 1df0f07..b15cbb5 100644
--- a/MatrixRoomUtils.sln.DotSettings.user
+++ b/MatrixRoomUtils.sln.DotSettings.user
@@ -44,9 +44,15 @@
 	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=c33adfe1_002D4af3_002D4c1e_002D9689_002De5e34a9f9113/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &amp;lt;LibMatrix&amp;gt; #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;
   &lt;Project Location="/home/root@Rory/git/Matrix/MatrixRoomUtils" Presentation="&amp;lt;LibMatrix&amp;gt;" /&gt;
 &lt;/SessionState&gt;</s:String>
-	<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=2cb12623_002D4918_002D4176_002D9b4a_002D88d846ccd3ed_0023LibMatrix_002EExampleBot/@EntryIndexedValue">True</s:Boolean>
-	<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=48dbb05f_002Db007_002D4b24_002D89b3_002D3cc177c79007_0023MediaModeratorPoC/@EntryIndexedValue">True</s:Boolean>
-	<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=95052ee6_002D7513_002D46fb_002D91bd_002Dee82026b42f1_0023PluralContactBotPoC/@EntryIndexedValue">True</s:Boolean>
+	
+	
+	
+	
+	
+	
+	
+	
+	
 	
 	
 	
diff --git a/MxApiExtensions b/MxApiExtensions
-Subproject 72b8f014866be5d0631a5b432b9d55569554e52
+Subproject 19438c9bebfe0cdddc7c48d882970d615436d3f