diff --git a/LibMatrix/Helpers/RoomBuilder.cs b/LibMatrix/Helpers/RoomBuilder.cs
index a292f33..1e33bb5 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,13 +199,14 @@ 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;
+ 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)) {
@@ -233,6 +241,7 @@ public class RoomBuilder {
}
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 +264,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]
diff --git a/LibMatrix/Responses/Federation/SignedObject.cs b/LibMatrix/Responses/Federation/SignedObject.cs
index 3f6ffd6..517bb1f 100644
--- a/LibMatrix/Responses/Federation/SignedObject.cs
+++ b/LibMatrix/Responses/Federation/SignedObject.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
@@ -19,7 +20,7 @@ public class SignedObject<T> {
}
[JsonExtensionData]
- public required JsonObject Content { get; set; }
+ public JsonObject Content { get; set; } = null!;
[JsonIgnore]
public T TypedContent {
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 6d9a499..550eb58 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -321,6 +321,9 @@ public class GenericRoom {
public Task<RoomCreateEventContent?> GetCreateEventAsync() =>
GetStateAsync<RoomCreateEventContent>("m.room.create");
+ public Task<RoomPolicyServerEventContent?> GetPolicyServerAsync() =>
+ GetStateAsync<RoomPolicyServerEventContent>(RoomPolicyServerEventContent.EventId);
+
public async Task<string?> GetRoomType() {
var res = await GetStateAsync<RoomCreateEventContent>("m.room.create");
return res.Type;
@@ -393,7 +396,7 @@ public class GenericRoom {
new UserIdAndReason { UserId = userId, Reason = reason });
public async Task InviteUserAsync(string userId, string? reason = null, bool skipExisting = true) {
- if (skipExisting && await GetStateOrNullAsync<RoomMemberEventContent>("m.room.member", userId) is not { Membership: "leave" or "ban" or "join" })
+ if (skipExisting && await GetStateOrNullAsync<RoomMemberEventContent>("m.room.member", userId) is { Membership: "ban" or "join" })
return;
await Homeserver.ClientHttpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/invite", new UserIdAndReason(userId, reason));
}
diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs
index c5e9d9c..8764096 100644
--- a/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs
+++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs
@@ -14,15 +14,17 @@ public class WellKnownResolverService {
private readonly ClientWellKnownResolver _clientWellKnownResolver;
private readonly SupportWellKnownResolver _supportWellKnownResolver;
private readonly ServerWellKnownResolver _serverWellKnownResolver;
+ private readonly PolicyServerWellKnownResolver _policyServerWellKnownResolver;
private readonly WellKnownResolverConfiguration _configuration;
public WellKnownResolverService(ILogger<WellKnownResolverService> logger, ClientWellKnownResolver clientWellKnownResolver, SupportWellKnownResolver supportWellKnownResolver,
- WellKnownResolverConfiguration configuration, ServerWellKnownResolver serverWellKnownResolver) {
+ WellKnownResolverConfiguration configuration, ServerWellKnownResolver serverWellKnownResolver, PolicyServerWellKnownResolver policyServerWellKnownResolver) {
_logger = logger;
_clientWellKnownResolver = clientWellKnownResolver;
_supportWellKnownResolver = supportWellKnownResolver;
_configuration = configuration;
_serverWellKnownResolver = serverWellKnownResolver;
+ _policyServerWellKnownResolver = policyServerWellKnownResolver;
if (logger is NullLogger<WellKnownResolverService>) {
var stackFrame = new StackTrace(true).GetFrame(1);
Console.WriteLine(
@@ -31,16 +33,26 @@ public class WellKnownResolverService {
}
public async Task<WellKnownRecords> TryResolveWellKnownRecords(string homeserver, bool includeClient = true, bool includeServer = true, bool includeSupport = true,
- WellKnownResolverConfiguration? config = null) {
+ bool includePolicyServer = true, WellKnownResolverConfiguration? config = null) {
WellKnownRecords records = new();
_logger.LogDebug($"Resolving well-knowns for {homeserver}");
- var clientTask = _clientWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration);
- var serverTask = _serverWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration);
- var supportTask = _supportWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration);
+ var clientTask = includeClient
+ ? _clientWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration)
+ : Task.FromResult<WellKnownResolutionResult<ClientWellKnown?>>(null!);
+ var serverTask = includeServer
+ ? _serverWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration)
+ : Task.FromResult<WellKnownResolutionResult<ServerWellKnown?>>(null!);
+ var supportTask = includeSupport
+ ? _supportWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration)
+ : Task.FromResult<WellKnownResolutionResult<SupportWellKnown?>>(null!);
+ var policyServerTask = includePolicyServer
+ ? _policyServerWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration)
+ : Task.FromResult<WellKnownResolutionResult<PolicyServerWellKnown?>>(null!);
if (includeClient && await clientTask is { } clientResult) records.ClientWellKnown = clientResult;
if (includeServer && await serverTask is { } serverResult) records.ServerWellKnown = serverResult;
if (includeSupport && await supportTask is { } supportResult) records.SupportWellKnown = supportResult;
+ if (includePolicyServer && await policyServerTask is { } policyServerResult) records.PolicyServerWellKnown = policyServerResult;
return records;
}
@@ -49,6 +61,7 @@ public class WellKnownResolverService {
public WellKnownResolutionResult<ClientWellKnown?>? ClientWellKnown { get; set; }
public WellKnownResolutionResult<ServerWellKnown?>? ServerWellKnown { get; set; }
public WellKnownResolutionResult<SupportWellKnown?>? SupportWellKnown { get; set; }
+ public WellKnownResolutionResult<PolicyServerWellKnown?>? PolicyServerWellKnown { get; set; }
}
public class WellKnownResolutionResult<T> {
diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs
index f52b217..678c077 100644
--- a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs
+++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs
@@ -1,10 +1,10 @@
using System.Text.Json.Serialization;
using ArcaneLibs.Collections;
-using LibMatrix.Extensions;
using Microsoft.Extensions.Logging;
using WellKnownType = LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ClientWellKnown;
-using ResultType =
- LibMatrix.Services.WellKnownResolver.WellKnownResolverService.WellKnownResolutionResult<LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ClientWellKnown?>;
+using ResultType = LibMatrix.Services.WellKnownResolver.WellKnownResolverService.WellKnownResolutionResult<
+ LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ClientWellKnown?
+>;
namespace LibMatrix.Services.WellKnownResolver.WellKnownResolvers;
@@ -14,7 +14,7 @@ public class ClientWellKnownResolver(ILogger<ClientWellKnownResolver> logger, We
StoreNulls = false
};
- public Task<WellKnownResolverService.WellKnownResolutionResult<ClientWellKnown>> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) {
+ public Task<ResultType> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) {
config ??= configuration;
return ClientWellKnownCache.TryGetOrAdd(homeserver, async () => {
logger.LogTrace($"Resolving client well-known: {homeserver}");
@@ -23,7 +23,6 @@ public class ClientWellKnownResolver(ILogger<ClientWellKnownResolver> logger, We
await TryGetWellKnownFromUrl($"https://{homeserver}/.well-known/matrix/client", WellKnownResolverService.WellKnownSource.Https);
if (result.Content != null) return result;
-
return result;
});
}
diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/PolicyServerWellKnownResolver.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/PolicyServerWellKnownResolver.cs
new file mode 100644
index 0000000..f7ffd62
--- /dev/null
+++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/PolicyServerWellKnownResolver.cs
@@ -0,0 +1,28 @@
+using System.Text.Json.Serialization;
+using Microsoft.Extensions.Logging;
+using WellKnownType = LibMatrix.Services.WellKnownResolver.WellKnownResolvers.PolicyServerWellKnown;
+using ResultType = LibMatrix.Services.WellKnownResolver.WellKnownResolverService.WellKnownResolutionResult<
+ LibMatrix.Services.WellKnownResolver.WellKnownResolvers.PolicyServerWellKnown?
+>;
+
+namespace LibMatrix.Services.WellKnownResolver.WellKnownResolvers;
+
+public class PolicyServerWellKnownResolver(ILogger<PolicyServerWellKnownResolver> logger, WellKnownResolverConfiguration configuration) : BaseWellKnownResolver<WellKnownType> {
+ public Task<ResultType> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) {
+ config ??= configuration;
+ return WellKnownCache.TryGetOrAdd(homeserver, async () => {
+ logger.LogTrace($"Resolving support well-known: {homeserver}");
+
+ ResultType result = await TryGetWellKnownFromUrl($"https://{homeserver}/.well-known/matrix/policy_server", WellKnownResolverService.WellKnownSource.Https);
+ if (result.Content != null)
+ return result;
+
+ return null;
+ });
+ }
+}
+
+public class PolicyServerWellKnown {
+ [JsonPropertyName("public_key")]
+ public string PublicKey { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs
index a48d846..f4be57d 100644
--- a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs
+++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs
@@ -2,8 +2,9 @@ using System.Text.Json.Serialization;
using ArcaneLibs.Collections;
using Microsoft.Extensions.Logging;
using WellKnownType = LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ServerWellKnown;
-using ResultType =
- LibMatrix.Services.WellKnownResolver.WellKnownResolverService.WellKnownResolutionResult<LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ServerWellKnown?>;
+using ResultType = LibMatrix.Services.WellKnownResolver.WellKnownResolverService.WellKnownResolutionResult<
+ LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ServerWellKnown?
+>;
namespace LibMatrix.Services.WellKnownResolver.WellKnownResolvers;
@@ -13,7 +14,7 @@ public class ServerWellKnownResolver(ILogger<ServerWellKnownResolver> logger, We
StoreNulls = false
};
- public Task<WellKnownResolverService.WellKnownResolutionResult<ServerWellKnown>> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) {
+ public Task<ResultType> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) {
config ??= configuration;
return ClientWellKnownCache.TryGetOrAdd(homeserver, async () => {
logger.LogTrace($"Resolving client well-known: {homeserver}");
diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/SupportWellKnownResolver.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/SupportWellKnownResolver.cs
index 99313db..4faff62 100644
--- a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/SupportWellKnownResolver.cs
+++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/SupportWellKnownResolver.cs
@@ -1,5 +1,3 @@
-using System.Diagnostics;
-using System.Net.Http.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using WellKnownType = LibMatrix.Services.WellKnownResolver.WellKnownResolvers.SupportWellKnown;
@@ -16,7 +14,7 @@ public class SupportWellKnownResolver(ILogger<SupportWellKnownResolver> logger,
logger.LogTrace($"Resolving support well-known: {homeserver}");
ResultType result = await TryGetWellKnownFromUrl($"https://{homeserver}/.well-known/matrix/support", WellKnownResolverService.WellKnownSource.Https);
- if (result.Content != null)
+ if (result.Content != null)
return result;
return null;
|