diff --git a/LibMatrix/Helpers/MessageBuilder.cs b/LibMatrix/Helpers/MessageBuilder.cs
index f753bf7..c6f8ea1 100644
--- a/LibMatrix/Helpers/MessageBuilder.cs
+++ b/LibMatrix/Helpers/MessageBuilder.cs
@@ -96,7 +96,7 @@ public class MessageBuilder(string msgType = "m.text", string format = "org.matr
}
public MessageBuilder WithMention(string id, string? displayName = null, string[]? vias = null, bool useIdInPlainText = false, bool useLinkInPlainText = false) {
- if (!useLinkInPlainText) Content.Body += $"@{(useIdInPlainText ? id : displayName ?? id)}";
+ if (!useLinkInPlainText) Content.Body += $"{(useIdInPlainText ? id : displayName ?? id)}";
else {
Content.Body += $"https://matrix.to/#/{id}";
if (vias is { Length: > 0 }) Content.Body += $"?via={string.Join("&via=", vias)}";
diff --git a/LibMatrix/Helpers/RoomBuilder.cs b/LibMatrix/Helpers/RoomBuilder.cs
index a292f33..ed47eb2 100644
--- a/LibMatrix/Helpers/RoomBuilder.cs
+++ b/LibMatrix/Helpers/RoomBuilder.cs
@@ -2,7 +2,9 @@ using System.Diagnostics;
using System.Runtime.Intrinsics.X86;
using System.Text.RegularExpressions;
using ArcaneLibs.Extensions;
+using LibMatrix.EventTypes.Spec;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.EventTypes.Spec.State.Space;
using LibMatrix.Homeservers;
using LibMatrix.Responses;
using LibMatrix.RoomTypes;
@@ -85,6 +87,11 @@ public class RoomBuilder {
{ RoomTombstoneEventContent.EventId, 150 },
{ RoomPolicyServerEventContent.EventId, 100 },
{ RoomPinnedEventContent.EventId, 50 },
+ { RoomTopicEventContent.EventId, 50 },
+ { SpaceChildEventContent.EventId, 100 },
+ { SpaceParentEventContent.EventId, 100 },
+ { RoomMessageReactionEventContent.EventId, 0 },
+ { RoomRedactionEventContent.EventId, 0 },
// recommended extensions
{ "im.vector.modular.widgets", 50 },
// { "m.reaction", 0 }, // we probably don't want these to end up as room state
@@ -99,6 +106,7 @@ public class RoomBuilder {
public List<string> AdditionalCreators { get; set; } = new();
public virtual async Task<GenericRoom> Create(AuthenticatedHomeserverGeneric homeserver) {
+ Console.WriteLine($"Creating room on {homeserver.ServerName}...");
var crq = new CreateRoomRequest {
PowerLevelContentOverride = new() {
EventsDefault = 1000000,
@@ -154,8 +162,6 @@ public class RoomBuilder {
var room = await homeserver.CreateRoom(crq);
- Console.WriteLine("Press any key to continue...");
- Console.ReadKey(true);
await SetBasicRoomInfoAsync(room);
await SetStatesAsync(room, ImportantState);
await SetAccessAsync(room);
@@ -167,6 +173,7 @@ public class RoomBuilder {
private async Task SendInvites(GenericRoom room) {
if (Invites.Count == 0) return;
+ Console.WriteLine($"Sending {Invites.Count} invites for room {room.RoomId}");
if (SynapseAdminAutoAcceptLocalInvites && room.Homeserver is AuthenticatedHomeserverSynapse synapse) {
var localJoinTasks = Invites.Where(u => UserId.Parse(u.Key).ServerName == synapse.ServerName).Select(async entry => {
@@ -192,14 +199,15 @@ public class RoomBuilder {
catch (MatrixException e) {
Console.Error.WriteLine("Failed to invite {0} to {1}: {2}", kvp.Key, room.RoomId, e.Message);
}
- });
+ }).ToList();
await Task.WhenAll(inviteTasks);
}
private async Task SetStatesAsync(GenericRoom room, List<MatrixEvent> state) {
if (state.Count == 0) return;
- await room.BulkSendEventsAsync(state);
+ Console.WriteLine($"Setting {state.Count} state events for {room.RoomId}...");
+ // await room.BulkSendEventsAsync(state);
// We chunk this up to try to avoid hitting reverse proxy timeouts
// foreach (var group in state.Chunk(chunkSize)) {
// var sw = Stopwatch.StartNew();
@@ -209,30 +217,35 @@ public class RoomBuilder {
// Console.WriteLine($"Warning: Sending {group.Length} state events took {sw.ElapsedMilliseconds}ms, which is quite long. Reducing chunk size to {chunkSize}.");
// }
// }
- // int chunkSize = 50;
- // for (int i = 0; i < state.Count; i += chunkSize) {
- // var chunk = state.Skip(i).Take(chunkSize).ToList();
- // if (chunk.Count == 0) continue;
- //
- // var sw = Stopwatch.StartNew();
- // await room.BulkSendEventsAsync(chunk, forceSyncInterval: chunk.Count + 1);
- // Console.WriteLine($"Sent {chunk.Count} state events in {sw.ElapsedMilliseconds}ms. {state.Count - (i + chunk.Count)} remaining.");
- // // if (sw.ElapsedMilliseconds > 45000) {
- // // chunkSize = Math.Max(chunkSize / 3, 1);
- // // Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is dangerously long. Reducing chunk size to {chunkSize}.");
- // // }
- // // else if (sw.ElapsedMilliseconds > 30000) {
- // // chunkSize = Math.Max(chunkSize / 2, 1);
- // // Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is quite long. Reducing chunk size to {chunkSize}.");
- // // }
- // // else if (sw.ElapsedMilliseconds < 10000) {
- // // chunkSize = Math.Min((int)(chunkSize * 1.2), 1000);
- // // Console.WriteLine($"Info: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, increasing chunk size to {chunkSize}.");
- // // }
- // }
+ int chunkSize = 767;
+ for (int i = 0; i < state.Count; i += chunkSize) {
+ var chunk = state.Skip(i).Take(chunkSize).ToList();
+ if (chunk.Count == 0) continue;
+
+ var sw = Stopwatch.StartNew();
+ await room.BulkSendEventsAsync(chunk, forceSyncInterval: chunk.Count + 1);
+ Console.WriteLine($"Sent {chunk.Count} state events in {sw.ElapsedMilliseconds}ms. {state.Count - (i + chunk.Count)} remaining.");
+ if (sw.ElapsedMilliseconds > 50000) {
+ chunkSize = Math.Max((int)(chunkSize / 1.2), 1);
+ Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is dangerously long. Reducing chunk size to {chunkSize}.");
+ }
+ // else if (sw.ElapsedMilliseconds > 30000) {
+ // chunkSize = Math.Max(chunkSize / 2, 1);
+ // Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is quite long. Reducing chunk size to {chunkSize}.");
+ // }
+ else if (sw.ElapsedMilliseconds < 5000) {
+ chunkSize = Math.Min((int)(chunkSize * 1.5), 1000);
+ Console.WriteLine($"Info: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, increasing chunk size to {chunkSize}.");
+ }
+ else if (sw.ElapsedMilliseconds < 10000) {
+ chunkSize = Math.Min((int)(chunkSize * 1.2), 1000);
+ Console.WriteLine($"Info: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, increasing chunk size to {chunkSize}.");
+ }
+ }
}
private async Task SetBasicRoomInfoAsync(GenericRoom room) {
+ Console.WriteLine($"Setting basic room info for {room.RoomId}...");
if (!string.IsNullOrWhiteSpace(Name.Name))
await room.SendStateEventAsync(RoomNameEventContent.EventId, Name);
@@ -255,6 +268,7 @@ public class RoomBuilder {
}
private async Task SetAccessAsync(GenericRoom room) {
+ Console.WriteLine($"Setting access settings for {room.RoomId}...");
if (!V12PlusRoomVersions.Contains(Version))
PowerLevels.Users![room.Homeserver.WhoAmI.UserId] = OwnPowerLevel;
else {
diff --git a/LibMatrix/Helpers/RoomUpgradeBuilder.cs b/LibMatrix/Helpers/RoomUpgradeBuilder.cs
index ced0ef3..ae71f8a 100644
--- a/LibMatrix/Helpers/RoomUpgradeBuilder.cs
+++ b/LibMatrix/Helpers/RoomUpgradeBuilder.cs
@@ -15,7 +15,7 @@ namespace LibMatrix.Helpers;
public class RoomUpgradeBuilder : RoomBuilder {
public RoomUpgradeOptions UpgradeOptions { get; set; } = new();
public string OldRoomId { get; set; } = string.Empty;
- public bool CanUpgrade { get; private set; }
+ public bool CanUpgrade { get; set; }
public Dictionary<string, object> AdditionalTombstoneContent { get; set; } = new();
private List<Type> basePolicyTypes = [];
@@ -27,7 +27,7 @@ public class RoomUpgradeBuilder : RoomBuilder {
basePolicyTypes = ClassCollector<PolicyRuleEventContent>.ResolveFromAllAccessibleAssemblies().ToList();
Console.WriteLine($"Found {basePolicyTypes.Count} policy types in {sw.ElapsedMilliseconds}ms");
CanUpgrade = (
- (await OldRoom.GetPowerLevelsAsync())?.UserHasStatePermission(OldRoom.Homeserver.UserId, RoomTombstoneEventContent.EventId)
+ (await OldRoom.GetPowerLevelsAsync())?.UserHasStatePermission(OldRoom.Homeserver.UserId, RoomTombstoneEventContent.EventId, true)
?? (await OldRoom.GetRoomCreatorsAsync()).Contains(OldRoom.Homeserver.UserId)
)
|| (OldRoom.IsV12PlusRoomId && (await OldRoom.GetRoomCreatorsAsync()).Contains(OldRoom.Homeserver.UserId));
@@ -186,6 +186,16 @@ public class RoomUpgradeBuilder : RoomBuilder {
await oldRoom.SendStateEventAsync(RoomTombstoneEventContent.EventId, tombstoneContent);
}
+ var oldPls = await oldRoom.GetPowerLevelsAsync();
+ if (oldPls?.UserHasStatePermission(oldRoom.Homeserver.UserId, RoomJoinRulesEventContent.EventId, true) ?? true) {
+ var oldJoinRules = await oldRoom.GetJoinRuleAsync();
+ var restrictContent = new RoomJoinRulesEventContent {
+ JoinRule = RoomJoinRulesEventContent.JoinRules.Restricted,
+ Allow = (oldJoinRules?.Allow ?? []).Append(new() { RoomId = room.RoomId, Type = "m.room_membership" }).ToList()
+ };
+ await oldRoom.SendStateEventAsync(RoomJoinRulesEventContent.EventId, restrictContent);
+ }
+
return room;
}
@@ -198,6 +208,7 @@ public class RoomUpgradeBuilder : RoomBuilder {
public bool UpgradeUnstableValues { get; set; }
public bool ForceUpgrade { get; set; }
public bool NoopUpgrade { get; set; }
+ public bool RestrictOldRoom { get; set; }
public Msc4321PolicyListUpgradeOptions Msc4321PolicyListUpgradeOptions { get; set; } = new();
[JsonIgnore]
|