KnownHomeserverList.razor (7139B)
1 @page "/KnownHomeserverList" 2 @using System.Text.Json 3 @using MatrixRoomUtils.Core.Extensions 4 @using System.Diagnostics 5 <h3>Known Homeserver List</h3> 6 <hr/> 7 8 @if (!IsFinished) 9 { 10 <p>Loading... Please wait...</p> 11 <progress value="@QueryProgress.ProcessedRooms" max="@QueryProgress.TotalRooms"></progress> 12 <p>@QueryProgress.ProcessedRooms / @QueryProgress.TotalRooms</p> 13 @foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList()) 14 { 15 @if (state.Blocked) 16 { 17 <p>🔒 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p> 18 } 19 else if (state.Slowmode) 20 { 21 22 <p>🐢 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p> 23 } 24 else 25 { 26 <p>@room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p> 27 } 28 <progress value="@state.Processed" max="@state.Total"></progress> 29 } 30 } 31 else 32 { 33 @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) 34 { 35 <p>@server.Server - @server.KnownUserCount</p> 36 } 37 } 38 <hr/> 39 40 @code { 41 List<HomeServerInfo> HomeServers = new(); 42 bool IsFinished { get; set; } 43 HomeServerInfoQueryProgress QueryProgress { get; set; } = new(); 44 45 protected override async Task OnInitializedAsync() 46 { 47 await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); 48 49 var sw = Stopwatch.StartNew(); 50 HomeServers = await GetHomeservers(progressCallback: async progress => 51 { 52 if (sw.ElapsedMilliseconds > 1000) 53 { 54 Console.WriteLine("Progress updated..."); 55 QueryProgress = progress; 56 StateHasChanged(); 57 Console.WriteLine("Progress rendered!"); 58 sw.Restart(); 59 await Task.Delay(100); 60 return true; 61 } 62 Console.WriteLine($"Progress updated, but not rendering because only {sw.ElapsedMilliseconds}ms elapsed since last call..."); 63 return false; 64 }); 65 66 IsFinished = true; 67 StateHasChanged(); 68 Console.WriteLine("Rerendered!"); 69 await base.OnInitializedAsync(); 70 } 71 72 73 private async Task<List<HomeServerInfo>> GetHomeservers(int memberLimit = 1000, Func<HomeServerInfoQueryProgress, Task<bool>>? progressCallback = null) 74 { 75 HomeServerInfoQueryProgress progress = new(); 76 List<HomeServerInfo> homeServers = new(); 77 var rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); 78 progress.TotalRooms = rooms.Count; 79 80 var semaphore = new SemaphoreSlim(4); 81 var semLock = new SemaphoreSlim(1); 82 var tasks = rooms.Select(async room => 83 { 84 await semaphore.WaitAsync(); 85 progress.ProcessedUsers.Add(room, new()); 86 Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})"); 87 var states = (await room.GetStateAsync("")).Value.Deserialize<List<StateEventResponse>>(); 88 states.RemoveAll(x => x.Type != "m.room.member" || x.Content.GetProperty("membership").GetString() != "join"); 89 Console.WriteLine($"Room {room.RoomId} has {states.Count} members"); 90 if (states.Count > memberLimit) 91 { 92 Console.WriteLine("Skipping!"); 93 semaphore.Release(); 94 progress.ProcessedUsers.Remove(room); 95 progress.TotalRooms--; 96 return; 97 } 98 progress.ProcessedUsers[room].Total = states.Count; 99 var updateInterval = progress.ProcessedUsers[room].Total >= 1000 ? 1000 : 100; 100 while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) 101 { 102 progress.ProcessedUsers[room].Blocked = true; 103 await Task.Delay(1000); 104 // if(progressCallback != null) 105 // await progressCallback.Invoke(progress); 106 } 107 progress.ProcessedUsers[room].Blocked = false; 108 int processedStates = 0; 109 foreach (var state in states) 110 { 111 await semLock.WaitAsync(); 112 semLock.Release(); 113 if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) 114 { 115 progress.ProcessedUsers[room].Slowmode = true; 116 await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100); 117 } 118 else 119 { 120 progress.ProcessedUsers[room].Slowmode = false; 121 } 122 if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) 123 { 124 homeServers.Add(new HomeServerInfo() { Server = state.StateKey.Split(':')[1] }); 125 } 126 var hs = homeServers.First(x => x.Server == state.StateKey.Split(':')[1]); 127 if (!hs.KnownUsers.Contains(state.StateKey.Split(':')[0])) 128 hs.KnownUsers.Add(state.StateKey.Split(':')[0]); 129 if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback != null) 130 { 131 await semLock.WaitAsync(); 132 var _ = await progressCallback.Invoke(progress); 133 semLock.Release(); 134 } 135 } 136 Console.WriteLine("Collected states!"); 137 progress.ProcessedRooms++; 138 progress.ProcessedUsers[room].IsFinished = true; 139 progressCallback?.Invoke(progress); 140 semaphore.Release(); 141 }); 142 await Task.WhenAll(tasks); 143 144 Console.WriteLine("Calculating member counts..."); 145 homeServers.ForEach(x => x.KnownUserCount = x.KnownUsers.Count); 146 Console.WriteLine(homeServers.First(x => x.Server == "rory.gay").ToJson()); 147 Console.WriteLine("Recalculated!"); 148 return homeServers; 149 } 150 151 class HomeServerInfo 152 { 153 public string Server { get; set; } 154 public int? KnownUserCount { get; set; } 155 public List<string> KnownUsers { get; set; } = new(); 156 } 157 158 class HomeServerInfoQueryProgress 159 { 160 public int ProcessedRooms { get; set; } 161 public int TotalRooms { get; set; } 162 public Dictionary<Room, State> ProcessedUsers { get; set; } = new(); 163 public List<HomeServerInfo> CurrentState { get; set; } = new(); 164 165 public class State 166 { 167 public int Processed { get; set; } 168 public int Total { get; set; } 169 public bool Blocked { get; set; } 170 public bool Slowmode { get; set; } 171 public float Progress => (float)Processed / Total; 172 public bool IsFinished { get; set; } 173 public Stopwatch Timing { get; set; } = Stopwatch.StartNew(); 174 } 175 } 176 177 }