@page "/KnownHomeserverList"
@using System.Text.Json
@using System.Diagnostics
@using MatrixRoomUtils.Core.Responses
Known Homeserver List
@if (!IsFinished) {
Loading... Please wait...
@QueryProgress.ProcessedRooms / @QueryProgress.TotalRooms
@foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList()) {
@if (state.Blocked) {
🔒 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...
}
else if (state.Slowmode) {
🐢 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...
}
else {
@room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...
}
}
}
else {
@foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) {
@server.Server - @server.KnownUserCount
}
}
@code {
List HomeServers = new();
bool IsFinished { get; set; }
HomeServerInfoQueryProgress QueryProgress { get; set; } = new();
protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
var sw = Stopwatch.StartNew();
HomeServers = await GetHomeservers(progressCallback: async progress => {
if (sw.ElapsedMilliseconds > 1000) {
Console.WriteLine("Progress updated...");
QueryProgress = progress;
StateHasChanged();
Console.WriteLine("Progress rendered!");
sw.Restart();
await Task.Delay(100);
return true;
}
Console.WriteLine($"Progress updated, but not rendering because only {sw.ElapsedMilliseconds}ms elapsed since last call...");
return false;
});
IsFinished = true;
StateHasChanged();
Console.WriteLine("Rerendered!");
await base.OnInitializedAsync();
}
private async Task> GetHomeservers(int memberLimit = 1000, Func>? progressCallback = null) {
HomeServerInfoQueryProgress progress = new();
List homeServers = new();
var rooms = await RuntimeCache.CurrentHomeServer.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());
Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})");
var states = (await room.GetStateAsync("")).Value.Deserialize>();
states.RemoveAll(x => x.Type != "m.room.member" || x.Content.GetProperty("membership").GetString() != "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 != 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 != 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();
});
await Task.WhenAll(tasks);
Console.WriteLine("Calculating member counts...");
homeServers.ForEach(x => x.KnownUserCount = x.KnownUsers.Count);
Console.WriteLine(homeServers.First(x => x.Server == "rory.gay").ToJson());
Console.WriteLine("Recalculated!");
return homeServers;
}
class HomeServerInfo {
public string Server { get; set; }
public int? KnownUserCount { get; set; }
public List KnownUsers { get; } = new();
}
class HomeServerInfoQueryProgress {
public int ProcessedRooms { get; set; }
public int TotalRooms { get; set; }
public Dictionary ProcessedUsers { get; } = new();
public List CurrentState { get; set; } = new();
public class State {
public int Processed { get; set; }
public int Total { get; set; }
public bool Blocked { get; set; }
public bool Slowmode { get; set; }
public float Progress => (float)Processed / Total;
public bool IsFinished { get; set; }
public Stopwatch Timing { get; } = Stopwatch.StartNew();
}
}
}