diff --git a/.gitignore b/.gitignore
index 1821c7e..51eecb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ test-proxy.tsv
homeservers.txt
LoginPayload.txt
LoginPayload.txt.old
+BugMine.DevTools.CLI/auth.json
diff --git a/BugMine.DevTools.CLI/Worker.cs b/BugMine.DevTools.CLI/Worker.cs
index 8cd931e..900d742 100644
--- a/BugMine.DevTools.CLI/Worker.cs
+++ b/BugMine.DevTools.CLI/Worker.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using System.Text.Json;
using ArcaneLibs.Extensions;
using BugMine.Web.Classes;
@@ -30,74 +31,90 @@ public class Worker(ILogger<Worker> logger, HomeserverProviderService hsProvider
while (!stoppingToken.IsCancellationRequested) {
Console.WriteLine("""
1) List all projects
- 2) Create 500 projects
+ 2) Mass create projects
3) Destroy all projects
4) Get room count
+ 5) Summarize all projects
+ 6) Mass create regular rooms
+
+ L) Logout
+ Q) Quit
""");
var input = Console.ReadKey();
Console.WriteLine();
switch (input.Key) {
case ConsoleKey.D1: {
- if (Client is null) {
- _logger.LogError("No client available.");
- break;
- }
-
+ var sw = Stopwatch.StartNew();
var projects = Client.GetProjects();
await foreach (var project in projects) {
Console.WriteLine(project.ToJson(indent: false));
}
+ Console.WriteLine($"Queried in {sw.Elapsed}");
break;
}
case ConsoleKey.D2: {
- if (Client is null) {
- _logger.LogError("No client available.");
- break;
- }
-
await MassCreateProjects();
break;
}
case ConsoleKey.D3: {
- if (Client is null) {
- _logger.LogError("No client available.");
- break;
- }
-
await DestroyAllProjects();
break;
}
case ConsoleKey.D4: {
- if (Client is null) {
- _logger.LogError("No client available.");
- break;
+ var sw = Stopwatch.StartNew();
+ var rooms = await Client.Homeserver.GetJoinedRooms();
+ Console.WriteLine(rooms.Count() + " rooms.");
+ var projectCount = 0;
+ await foreach (var room in Client.Homeserver.GetJoinedRoomsByType("gay.rory.bugmine.project").WithCancellation(stoppingToken)) {
+ projectCount++;
}
- var rooms = await Client.Homeserver.GetJoinedRooms();
- Console.WriteLine(rooms.Count());
+ Console.WriteLine($"{projectCount} projects ({rooms.Count} rooms) queried in {sw.Elapsed}");
break;
}
+ case ConsoleKey.D5: {
+ await SummarizeAllProjects();
+ break;
+ }
+ case ConsoleKey.D6: {
+ var sw = Stopwatch.StartNew();
+ await Task.WhenAll(Enumerable.Range(0,1000).Select(async _ => {
+ await Client.Homeserver.CreateRoom(new() {
+ Name = Guid.NewGuid().ToString()[..8]
+ });
+ }));
+ Console.WriteLine($"Created 1000 rooms in {sw.Elapsed}");
+ break;
+ }
+ case ConsoleKey.L: {
+ File.Delete("auth.json");
+ await ExecuteAsync(stoppingToken);
+ return;
+ }
+ case ConsoleKey.Q: {
+ Environment.Exit(0);
+ return;
+ }
}
}
}
private async Task DestroyAllProjects() {
- var ss = new SemaphoreSlim(4, 4);
- await foreach (var proj in Client.Homeserver.GetJoinedRoomsByType(null!)) {
- if (proj.RoomId == "!UktPWOzit8gmms5FQ6:conduit.matrixunittests.rory.gay") continue;
+ var ss = new SemaphoreSlim(48, 48);
+ await foreach (var proj in Client.Homeserver.GetJoinedRoomsByType("gay.rory.bugmine.project")) {
Task.Run(async () => {
// await ss.WaitAsync();
- await proj.SendStateEventAsync(RoomNameEventContent.EventId, new RoomNameEventContent() {
- Name = "Disbanded BugMine project."
- });
- await proj.SendStateEventAsync(RoomJoinRulesEventContent.EventId, new RoomJoinRulesEventContent() {
- JoinRule = RoomJoinRulesEventContent.JoinRules.Private
- });
- await proj.SendStateEventAsync(RoomCanonicalAliasEventContent.EventId, new RoomCanonicalAliasEventContent() {
- Alias = null
- });
+ // await proj.SendStateEventAsync(RoomNameEventContent.EventId, new RoomNameEventContent() {
+ // Name = "Disbanded BugMine project."
+ // });
+ // await proj.SendStateEventAsync(RoomJoinRulesEventContent.EventId, new RoomJoinRulesEventContent() {
+ // JoinRule = RoomJoinRulesEventContent.JoinRules.Private
+ // });
+ // await proj.SendStateEventAsync(RoomCanonicalAliasEventContent.EventId, new RoomCanonicalAliasEventContent() {
+ // Alias = null
+ // });
await proj.LeaveAsync("Disbanded room.");
// ss.Release();
});
@@ -106,18 +123,59 @@ public class Worker(ILogger<Worker> logger, HomeserverProviderService hsProvider
private async Task MassCreateProjects() {
// var rooms = await Client.Homeserver.GetJoinedRooms();
- // List<string> roomNames = (await Task.WhenAll(rooms.Select(x => x.GetNameAsync()))).Where(x => x != null).ToList();
+ // List<string> roomNames = (await Task.WhenAll(rooms.Select(x => x.GetNameAsync()))).Where(x => x !=
+ var tasks = new List<Task>();
for (int i = 0; i < 500; i++) {
- Task.Run(async () => {
+ tasks.Add(Task.Run(async () => {
// var randomName = roomNames[Random.Shared.Next(roomNames.Count)];
var proj = await Client.CreateProject(new() {
Name = /*randomName + */Guid.NewGuid().ToString()[..8]
});
// await proj.CreateIssue(new() {
- // Name = "meow"
+ // Name = "meow"
// });
- });
+ await CreateRandomIssues(proj, Random.Shared.Next(20));
+ }));
+ // await Task.Delay(250);
+ }
+
+ await Task.WhenAll(tasks);
+ }
+
+ private async Task<string> SummarizeProject(BugMineProject project) {
+ string result = $"Project: {project.Info.Name}, slug: {project.ProjectSlug}, metadata: {project.Metadata.ToJson(indent: false)}";
+ await foreach (var issue in project.GetIssues()) {
+ // Console.WriteLine($" - {issue.Data.RawContent.ToJson(indent: false)}");
+ result += $"\n - {issue.Data.RawContent.ToJson(indent: false)}";
+ }
+
+ return result;
+ }
+
+ private async Task SummarizeAllProjects() {
+ var sw = Stopwatch.StartNew();
+ var tasks = new List<Task<string>>();
+ await foreach (var project in Client.GetProjects()) {
+ tasks.Add(SummarizeProject(project));
+ }
+
+ int projects = 0;
+ int issues = 0;
+ await foreach (var res in tasks.ToAsyncEnumerable()) {
+ Console.WriteLine(res + "\n");
+ projects++;
+ issues += res.Count(x => x == '\n');
}
+
+ Console.WriteLine($"Summarized {projects} projects with {issues} issues in {sw.Elapsed}");
+ }
+
+ private async Task CreateRandomIssues(BugMineProject project, int count) {
+ await Task.WhenAll(Enumerable.Range(0, count).Select(async i => {
+ await project.CreateIssue(new() {
+ Name = $"Issue {i} @ {DateTime.Now}"
+ });
+ }));
}
}
\ No newline at end of file
diff --git a/BugMine.Sdk/BugMineClient.cs b/BugMine.Sdk/BugMineClient.cs
index db47b85..21be614 100644
--- a/BugMine.Sdk/BugMineClient.cs
+++ b/BugMine.Sdk/BugMineClient.cs
@@ -11,10 +11,11 @@ namespace BugMine.Web.Classes;
public class BugMineClient(AuthenticatedHomeserverGeneric homeserver) {
public AuthenticatedHomeserverGeneric Homeserver { get; } = homeserver;
- public async IAsyncEnumerable<BugMineProject> GetProjects() {
+ public async IAsyncEnumerable<BugMineProject> GetProjects(SemaphoreSlim? semaphore = null) {
List<Task<BugMineProject>> tasks = [];
- await foreach (var room in homeserver.GetJoinedRoomsByType(BugMineProject.RoomType)) {
- tasks.Add(room.AsBugMineProject());
+ int count = 0;
+ await foreach (var room in homeserver.GetJoinedRoomsByType(BugMineProject.RoomType, 64)) {
+ tasks.Add(room.AsBugMineProject(semaphore));
}
var results = tasks.ToAsyncEnumerable();
@@ -46,10 +47,14 @@ public class BugMineClient(AuthenticatedHomeserverGeneric homeserver) {
]
};
- var response = await Homeserver.CreateRoom(crr);
- // await response.SendStateEventAsync(ProjectInfo.EventId, request);
+ var newRoom = await Homeserver.CreateRoom(crr);
+ var timeline = await newRoom.GetMessagesAsync();
+
+ await newRoom.SendStateEventAsync(BugMineRoomMetadata.EventId, new BugMineRoomMetadata() {
+ RoomCreationEventId = timeline.Chunk.Single(m => m.Type == "m.room.create").EventId!
+ });
- return await response.AsBugMineProject();
+ return await newRoom.AsBugMineProject();
}
public async Task<BugMineProject?> GetProject(string projectSlug) {
diff --git a/BugMine.Sdk/BugMineProject.cs b/BugMine.Sdk/BugMineProject.cs
index babb680..1f56659 100644
--- a/BugMine.Sdk/BugMineProject.cs
+++ b/BugMine.Sdk/BugMineProject.cs
@@ -1,6 +1,4 @@
-using System.Text.Json.Nodes;
-using LibMatrix.EventTypes.Spec.State;
-using LibMatrix.Homeservers;
+using ArcaneLibs.Extensions;
using LibMatrix.RoomTypes;
namespace BugMine.Web.Classes;
@@ -9,43 +7,51 @@ public class BugMineProject(GenericRoom room) {
public const string RoomType = "gay.rory.bugmine.project";
public GenericRoom Room { get; } = room;
public ProjectInfo Info { get; set; }
+ public BugMineRoomMetadata Metadata { get; set; }
public string ProjectSlug { get; set; }
public async Task<BugMineProject> InitializeAsync() {
- Info = (await Room.GetStateAsync<ProjectInfo>(ProjectInfo.EventId))!;
+ var infoTask = Room.GetStateAsync<ProjectInfo>(ProjectInfo.EventId);
+ var metadataTask = Room.GetStateAsync<BugMineRoomMetadata>(BugMineRoomMetadata.EventId)!;
var alias = await room.GetCanonicalAliasAsync();
if (alias != null)
ProjectSlug = alias.Alias?[1..] ?? room.RoomId;
else ProjectSlug = room.RoomId;
+ Info = (await infoTask)!;
+ Metadata = (await metadataTask)!;
return this;
}
public async Task<BugMineIssue> CreateIssue(BugMineIssueData issue) {
// add relation to room creation event
issue.RelatesTo = new() {
- EventId = await room.GetStateEventIdAsync(RoomCreateEventContent.EventId),
+ EventId = Metadata.RoomCreationEventId,
RelationType = "gay.rory.bugmine.issue"
};
var eventId = await Room.SendTimelineEventAsync(BugMineIssueData.EventId, issue);
- // return new BugMineIssueAccessor(Room, await Room.GetEventAsync<>(eventId));
var evt = await room.GetEventAsync(eventId.EventId);
- Console.WriteLine(evt);
+ Console.WriteLine(evt.ToJson(indent: false));
return new BugMineIssue(Room, evt);
}
public async IAsyncEnumerable<BugMineIssue> GetIssues() {
- var creationEventId = await room.GetStateEventIdAsync(RoomCreateEventContent.EventId);
- await foreach (var evt in room.GetRelatedEventsAsync(creationEventId, "gay.rory.bugmine.issue", BugMineIssueData.EventId)) {
+ await foreach (var evt in room.GetRelatedEventsAsync(Metadata.RoomCreationEventId, "gay.rory.bugmine.issue", BugMineIssueData.EventId)) {
yield return new BugMineIssue(Room, evt);
}
}
}
public static class ProjectRoomExtensions {
- public static async Task<BugMineProject> AsBugMineProject(this GenericRoom room) {
- return await new BugMineProject(room).InitializeAsync();
+ public static async Task<BugMineProject> AsBugMineProject(this GenericRoom room, SemaphoreSlim? semaphore = null) {
+ try {
+ await (semaphore?.WaitAsync() ?? Task.CompletedTask);
+ return await new BugMineProject(room).InitializeAsync();
+ }
+ finally {
+ semaphore?.Release();
+ }
}
}
\ No newline at end of file
diff --git a/BugMine.Sdk/Events/State/BugMineRoomMetadata.cs b/BugMine.Sdk/Events/State/BugMineRoomMetadata.cs
index a6686d3..734fd37 100644
--- a/BugMine.Sdk/Events/State/BugMineRoomMetadata.cs
+++ b/BugMine.Sdk/Events/State/BugMineRoomMetadata.cs
@@ -7,5 +7,5 @@ public class BugMineRoomMetadata : EventContent {
public const string EventId = "gay.rory.bugmine.room_metadata";
public string RoomCreationEventId { get; set; }
-
+
}
\ No newline at end of file
diff --git a/BugMine.Web/Pages/Auth/LegacyLogin.razor b/BugMine.Web/Pages/Auth/LegacyLogin.razor
index eec381a..bcfac61 100644
--- a/BugMine.Web/Pages/Auth/LegacyLogin.razor
+++ b/BugMine.Web/Pages/Auth/LegacyLogin.razor
@@ -48,6 +48,7 @@
await BugMineStorage.AddToken(result);
await BugMineStorage.SetCurrentToken(result);
+ NavigationManager.NavigateTo("/", forceLoad: true);
}
catch (Exception e) {
Console.WriteLine($"Failed to login to {record.Homeserver} as {record.Username}!");
diff --git a/BugMine.Web/Pages/Projects/Index.razor b/BugMine.Web/Pages/Projects/Index.razor
index 47c2f4a..8f46d02 100644
--- a/BugMine.Web/Pages/Projects/Index.razor
+++ b/BugMine.Web/Pages/Projects/Index.razor
@@ -43,12 +43,14 @@ else {
return;
}
StateHasChanged();
-
- await foreach (var project in Client.GetProjects()) {
+
+ int count = 0;
+ SemaphoreSlim semaphore = new(16, 16);
+ await foreach (var project in Client.GetProjects(semaphore)) {
Projects ??= [];
Projects.Add(project);
- StateHasChanged();
- // await Task.Delay(100);
+ if(count++ <= 250 || count % 4 == 0)
+ StateHasChanged();
}
Projects ??= [];
diff --git a/BugMine.Web/Pages/Projects/ViewProject.razor b/BugMine.Web/Pages/Projects/ViewProject.razor
index a778728..ec10d1c 100644
--- a/BugMine.Web/Pages/Projects/ViewProject.razor
+++ b/BugMine.Web/Pages/Projects/ViewProject.razor
@@ -1,6 +1,7 @@
@page "/Projects/{ProjectSlug}/"
@using LibMatrix
@using BugMine.Web.Classes.Exceptions
+@using ArcaneLibs.Extensions
<ProgressLog ></ProgressLog>
@@ -29,6 +30,11 @@ else {
<p>Loading issues, got @(Issues?.Count ?? 0) so far... <SimpleSpinner/></p>
}
@* <p>@Project.Description</p> *@
+ @if (Issues != null) {
+ @foreach(var issue in Issues) {
+ <pre>@issue.Data.RawContent.ToJson()</pre>
+ }
+ }
}
@code {
diff --git a/BugMine.sln b/BugMine.sln
index c6b4a4b..f6d49f3 100644
--- a/BugMine.sln
+++ b/BugMine.sln
@@ -18,6 +18,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BugMine.Sdk", "BugMine.Sdk\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BugMine.DevTools.CLI", "BugMine.DevTools.CLI\BugMine.DevTools.CLI.csproj", "{E4E398A6-98CE-4B78-9E80-42A03765CE07}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{E25BCB86-1123-481D-BA11-920B76006305}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.HomeserverEmulator", "LibMatrix\Tests\LibMatrix.HomeserverEmulator\LibMatrix.HomeserverEmulator.csproj", "{630BD644-B739-4876-B45C-085FF60C7269}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,11 +52,17 @@ Global
{E4E398A6-98CE-4B78-9E80-42A03765CE07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4E398A6-98CE-4B78-9E80-42A03765CE07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4E398A6-98CE-4B78-9E80-42A03765CE07}.Release|Any CPU.Build.0 = Release|Any CPU
+ {630BD644-B739-4876-B45C-085FF60C7269}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {630BD644-B739-4876-B45C-085FF60C7269}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {630BD644-B739-4876-B45C-085FF60C7269}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {630BD644-B739-4876-B45C-085FF60C7269}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F4E241C3-0300-4B87-8707-BCBDEF1F0185} = {8F4F6BEC-0C66-486B-A21A-1C35B2EDAD33}
{1CAA2B6D-0365-4C8B-96EE-26026514FEE2} = {8F4F6BEC-0C66-486B-A21A-1C35B2EDAD33}
{B00E29F5-1ED8-40A0-A70D-DE9F23FC572F} = {B00C5CB6-6200-4B41-96BE-C6EAF1085A14}
{2D6F31D7-3139-44EC-9D11-486282DD4ED1} = {B00C5CB6-6200-4B41-96BE-C6EAF1085A14}
+ {E25BCB86-1123-481D-BA11-920B76006305} = {8F4F6BEC-0C66-486B-A21A-1C35B2EDAD33}
+ {630BD644-B739-4876-B45C-085FF60C7269} = {E25BCB86-1123-481D-BA11-920B76006305}
EndGlobalSection
EndGlobal
diff --git a/LibMatrix b/LibMatrix
-Subproject 5ca0a45606ad2ca7e1ca45a3b27be08e9640dd9
+Subproject 508c694c3d551cddb3b15c1b0d4787dae3c0053
|