diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject d3f0ba9cb7ec36ab2f9574fd563c467b61512b9
+Subproject 5099e9feeade98f562723e102bd58887aa2c125
diff --git a/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj b/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj
index 682b91b..a2b22b5 100644
--- a/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj
+++ b/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20250630-114950" Condition="'$(Configuration)' == 'Release'" />
+ <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20250806-011111" Condition="'$(Configuration)' == 'Release'" />
<ProjectReference Include="..\ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj" Condition="'$(Configuration)' == 'Debug'"/>
</ItemGroup>
diff --git a/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs b/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
index d75b19f..24b8f90 100644
--- a/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
@@ -124,6 +124,19 @@ public abstract class PolicyRuleEventContent : EventContent {
return Recommendation;
}
+
+ public string? GetSpecRecommendation() {
+ if (Recommendation is "m.ban" or "org.matrix.mjolnir.ban")
+ return PolicyRecommendationTypes.Ban;
+
+ if (Recommendation is "m.mute" or "support.feline.policy.recommendation_mute")
+ return PolicyRecommendationTypes.Mute;
+
+ if (Recommendation is "m.takedown" or "org.matrix.msc4204.takedown")
+ return PolicyRecommendationTypes.Takedown;
+
+ return Recommendation;
+ }
}
public static class PolicyRecommendationTypes {
@@ -137,7 +150,10 @@ public static class PolicyRecommendationTypes {
/// </summary>
public static string Mute = "support.feline.policy.recommendation_mute"; //stable prefix: m.mute, msc pending
- public static string Takedown = "m.takedown"; //unstable prefix: org.matrix.msc4204.takedown
+ /// <summary>
+ /// Take down the user with all means available
+ /// </summary>
+ public static string Takedown = "org.matrix.msc4204.takedown"; //stable prefix: m.takedown, msc pending
}
public class PolicyHash {
diff --git a/LibMatrix.Federation/LibMatrix.Federation.csproj b/LibMatrix.Federation/LibMatrix.Federation.csproj
index 78086bb..af09d85 100644
--- a/LibMatrix.Federation/LibMatrix.Federation.csproj
+++ b/LibMatrix.Federation/LibMatrix.Federation.csproj
@@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" />
+ <PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="10.0.0-preview.5.25277.114" />
</ItemGroup>
diff --git a/LibMatrix/Helpers/RoomBuilder.cs b/LibMatrix/Helpers/RoomBuilder.cs
index 8843a21..2d8d730 100644
--- a/LibMatrix/Helpers/RoomBuilder.cs
+++ b/LibMatrix/Helpers/RoomBuilder.cs
@@ -86,7 +86,7 @@ public class RoomBuilder {
public Dictionary<string, object> AdditionalCreationContent { get; set; } = new();
public List<string> AdditionalCreators { get; set; } = new();
- public async Task<GenericRoom> Create(AuthenticatedHomeserverGeneric homeserver) {
+ public virtual async Task<GenericRoom> Create(AuthenticatedHomeserverGeneric homeserver) {
var crq = new CreateRoomRequest() {
PowerLevelContentOverride = new() {
EventsDefault = 1000000,
diff --git a/LibMatrix/Helpers/RoomUpgradeBuilder.cs b/LibMatrix/Helpers/RoomUpgradeBuilder.cs
index cbc7876..64ec364 100644
--- a/LibMatrix/Helpers/RoomUpgradeBuilder.cs
+++ b/LibMatrix/Helpers/RoomUpgradeBuilder.cs
@@ -1,71 +1,155 @@
+using System.Diagnostics;
using System.Text.Json.Serialization;
+using ArcaneLibs;
using LibMatrix.EventTypes.Spec;
+using LibMatrix.EventTypes.Spec.State.Policy;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Homeservers;
using LibMatrix.RoomTypes;
namespace LibMatrix.Helpers;
-public class RoomUpgradeBuilder(GenericRoom oldRoom) : RoomBuilder {
- public GenericRoom OldRoom { get; } = oldRoom;
+public class RoomUpgradeBuilder : RoomBuilder {
public RoomUpgradeOptions UpgradeOptions { get; set; } = new();
+ public string OldRoomId { get; set; } = string.Empty;
+ public bool CanUpgrade { get; private set; }
+ public Dictionary<string, object> AdditionalTombstoneContent { get; set; } = new();
+
+ public async Task ImportAsync(GenericRoom OldRoom) {
+ var sw = Stopwatch.StartNew();
+ var total = 0;
+
+ var 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.GetRoomCreatorsAsync()).Contains(OldRoom.Homeserver.UserId)
+ )
+ || (OldRoom.IsV12PlusRoomId && (await OldRoom.GetRoomCreatorsAsync()).Contains(OldRoom.Homeserver.UserId));
+
+ await foreach (var srcEvt in OldRoom.GetFullStateAsync()) {
+ total++;
+ if (srcEvt is null) continue;
+ var evt = srcEvt;
+
+ if (UpgradeOptions.UpgradeUnstableValues) {
+ evt = UpgradeUnstableValues(evt);
+ }
- public async Task ImportAsync() {
- await foreach (var evt in OldRoom.GetFullStateAsync()) {
- if (evt is null) continue;
if (evt.StateKey == "") {
- if (evt.TypedContent is RoomCreateEventContent createEvt)
+ if (evt.Type == RoomCreateEventContent.EventId)
foreach (var (key, value) in evt.RawContent) {
if (key == "version") continue;
if (key == "type")
Type = value!.GetValue<string>();
else AdditionalCreationContent[key] = value;
}
- else if (evt.TypedContent is RoomNameEventContent name)
- Name = name;
- else if (evt.TypedContent is RoomTopicEventContent topic)
- Topic = topic;
- else if (evt.TypedContent is RoomAvatarEventContent avatar)
- Avatar = avatar;
- else if (evt.TypedContent is RoomCanonicalAliasEventContent alias) {
- CanonicalAlias = alias;
- AliasLocalPart = alias.Alias?.Split(':',2).FirstOrDefault()?[1..] ?? string.Empty;
+ else if (evt.Type == RoomNameEventContent.EventId)
+ Name = evt.ContentAs<RoomNameEventContent>()!;
+ else if (evt.Type == RoomTopicEventContent.EventId)
+ Topic = evt.ContentAs<RoomTopicEventContent>()!;
+ else if (evt.Type == RoomAvatarEventContent.EventId)
+ Avatar = evt.ContentAs<RoomAvatarEventContent>()!;
+ else if (evt.Type == RoomCanonicalAliasEventContent.EventId) {
+ CanonicalAlias = evt.ContentAs<RoomCanonicalAliasEventContent>()!;
+ AliasLocalPart = CanonicalAlias.Alias?.Split(':', 2).FirstOrDefault()?[1..] ?? string.Empty;
}
- else if (evt.TypedContent is RoomJoinRulesEventContent joinRules)
- JoinRules = joinRules;
- else if (evt.TypedContent is RoomHistoryVisibilityEventContent historyVisibility)
- HistoryVisibility = historyVisibility;
- else if (evt.TypedContent is RoomGuestAccessEventContent guestAccess)
- GuestAccess = guestAccess;
- else if (evt.TypedContent is RoomServerAclEventContent serverAcls)
- ServerAcls = serverAcls;
- else if (evt.TypedContent is RoomPowerLevelEventContent powerLevels) {
- if (UpgradeOptions.InvitePowerlevelUsers && powerLevels.Users != null)
- foreach (var (userId, level) in powerLevels.Users)
- if (level > powerLevels.UsersDefault)
+ else if (evt.Type == RoomJoinRulesEventContent.EventId)
+ JoinRules = evt.ContentAs<RoomJoinRulesEventContent>()!;
+ else if (evt.Type == RoomHistoryVisibilityEventContent.EventId)
+ HistoryVisibility = evt.ContentAs<RoomHistoryVisibilityEventContent>()!;
+ else if (evt.Type == RoomGuestAccessEventContent.EventId)
+ GuestAccess = evt.ContentAs<RoomGuestAccessEventContent>()!;
+ else if (evt.Type == RoomServerAclEventContent.EventId)
+ ServerAcls = evt.ContentAs<RoomServerAclEventContent>()!;
+ else if (evt.Type == RoomPowerLevelEventContent.EventId) {
+ PowerLevels = evt.ContentAs<RoomPowerLevelEventContent>()!;
+ if (UpgradeOptions.InvitePowerlevelUsers && PowerLevels.Users != null)
+ foreach (var (userId, level) in PowerLevels.Users)
+ if (level > PowerLevels.UsersDefault)
Invites.Add(userId, "Room upgrade (had a power level)");
-
- PowerLevels = powerLevels;
}
- else if (evt.TypedContent is RoomEncryptionEventContent encryption)
- Encryption = encryption;
- else if (evt.TypedContent is RoomPinnedEventContent) ; // Discard as you can't cross reference pinned events
- else InitialState.Add(evt);
+ else if (evt.Type == RoomEncryptionEventContent.EventId)
+ Encryption = evt.ContentAs<RoomEncryptionEventContent>();
+ else if (evt.Type == RoomPinnedEventContent.EventId) ; // Discard as you can't cross reference pinned events
+ else
+ InitialState.Add(new() {
+ Type = evt.Type,
+ StateKey = evt.StateKey,
+ RawContent = evt.RawContent
+ });
}
else if (evt.Type == RoomMemberEventContent.EventId) {
- if (UpgradeOptions.InviteMembers && evt.TypedContent is RoomMemberEventContent { Membership: "join" or "invite" })
- if (!Invites.ContainsKey(evt.StateKey))
- Invites.Add(evt.StateKey, "Room upgrade");
- else if (UpgradeOptions.MigrateBans && evt.TypedContent is RoomMemberEventContent { Membership: "ban" } bannedMember)
- Bans.Add(evt.StateKey, bannedMember.Reason);
+ if (UpgradeOptions.InviteMembers && evt.TypedContent is RoomMemberEventContent { Membership: "join" or "invite" } invitedMember) {
+ Invites.TryAdd(evt.StateKey!, invitedMember.Reason ?? "Room upgrade");
+ }
+ else if (UpgradeOptions.MigrateBans && evt.TypedContent is RoomMemberEventContent { Membership: "ban" } bannedMember)
+ Bans.TryAdd(evt.StateKey!, bannedMember.Reason);
}
- else InitialState.Add(evt);
+ else if (!UpgradeOptions.MigrateEmptyStateEvents && evt.RawContent.Count == 0) { } // skip empty state events
+ else if (basePolicyTypes.Contains(evt.MappedType)) ImportPolicyEventAsync(evt);
+ else
+ InitialState.Add(new() {
+ Type = evt.Type,
+ StateKey = evt.StateKey,
+ RawContent = evt.RawContent
+ });
+ }
+
+ Console.WriteLine($"Imported {total} state events from old room {OldRoom.RoomId} in {sw.ElapsedMilliseconds}ms");
+ }
+
+ private StateEventResponse UpgradeUnstableValues(StateEventResponse evt) {
+
+ return evt;
+ }
+
+ private void ImportPolicyEventAsync(StateEventResponse evt) {
+ var msc4321Options = UpgradeOptions.Msc4321PolicyListUpgradeOptions;
+ if (msc4321Options is { Enable: true, UpgradeType: Msc4321PolicyListUpgradeOptions.Msc4321PolicyListUpgradeType.Transition })
+ return; // this upgrade type doesnt copy policies
+ if (msc4321Options.Enable) {
+ evt.RawContent["org.matrix.msc4321.original_sender"] = evt.Sender;
+ evt.RawContent["org.matrix.msc4321.original_timestamp"] = evt.OriginServerTs;
+ evt.RawContent["org.matrix.msc4321.original_event_id"] = evt.EventId;
}
+ InitialState.Add(new() {
+ Type = evt.Type,
+ StateKey = evt.StateKey,
+ RawContent = evt.RawContent
+ });
+ }
+
+ public override async Task<GenericRoom> Create(AuthenticatedHomeserverGeneric homeserver) {
+ var room = await base.Create(homeserver);
+ if (CanUpgrade || UpgradeOptions.ForceUpgrade) {
+ if (UpgradeOptions.RoomUpgradeNotice != null) {
+ var noticeContent = await UpgradeOptions.RoomUpgradeNotice(room);
+ await room.SendMessageEventAsync(noticeContent);
+ }
+
+ var tombstoneContent = new RoomTombstoneEventContent {
+ Body = "This room has been upgraded to a new version.",
+ ReplacementRoom = room.RoomId
+ };
+
+ tombstoneContent.AdditionalData ??= [];
+ foreach (var (key, value) in AdditionalTombstoneContent)
+ tombstoneContent.AdditionalData[key] = value;
+
+ await room.SendStateEventAsync(RoomTombstoneEventContent.EventId, tombstoneContent);
+ }
+ return room;
}
public class RoomUpgradeOptions {
public bool InviteMembers { get; set; }
public bool InvitePowerlevelUsers { get; set; }
public bool MigrateBans { get; set; }
+ public bool MigrateEmptyStateEvents { get; set; }
+ public bool UpgradeUnstableValues { get; set; }
+ public bool ForceUpgrade { get; set; }
+ public Msc4321PolicyListUpgradeOptions Msc4321PolicyListUpgradeOptions { get; set; } = new();
[JsonIgnore]
public Func<GenericRoom, Task<RoomMessageEventContent>> RoomUpgradeNotice { get; set; } = async newRoom => new MessageBuilder()
@@ -78,4 +162,22 @@ public class RoomUpgradeBuilder(GenericRoom oldRoom) : RoomBuilder {
.WithMention(newRoom.RoomId, await newRoom.GetNameOrFallbackAsync(), vias: (await newRoom.GetHomeserversInRoom()).ToArray(), useLinkInPlainText: true)
.Build();
}
+
+ public class Msc4321PolicyListUpgradeOptions {
+ public bool Enable { get; set; } = true;
+ public Msc4321PolicyListUpgradeType UpgradeType { get; set; } = Msc4321PolicyListUpgradeType.Move;
+
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum Msc4321PolicyListUpgradeType {
+ /// <summary>
+ /// Copy policies, unwatch old list
+ /// </summary>
+ Move,
+
+ /// <summary>
+ /// Don't copy policies
+ /// </summary>
+ Transition
+ }
+ }
}
\ No newline at end of file
diff --git a/LibMatrix/LibMatrix.csproj b/LibMatrix/LibMatrix.csproj
index 7fceb6f..c1049b5 100644
--- a/LibMatrix/LibMatrix.csproj
+++ b/LibMatrix/LibMatrix.csproj
@@ -12,8 +12,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
<ProjectReference Include="..\LibMatrix.EventTypes\LibMatrix.EventTypes.csproj"/>
</ItemGroup>
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 7fc942e..7d21d68 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -656,7 +656,10 @@ public class GenericRoom {
public SpaceRoom AsSpace() => new SpaceRoom(Homeserver, RoomId);
public PolicyRoom AsPolicyRoom() => new PolicyRoom(Homeserver, RoomId);
- private bool IsV12PlusRoomId => !RoomId.Contains(':');
+ /// <summary>
+ /// Unsafe: does not actually check if the room is v12, it just checks the room ID format as an estimation.
+ /// </summary>
+ public bool IsV12PlusRoomId => !RoomId.Contains(':');
/// <summary>
/// Gets the list of room creators for this room.
diff --git a/Utilities/LibMatrix.DebugDataValidationApi/LibMatrix.DebugDataValidationApi.csproj b/Utilities/LibMatrix.DebugDataValidationApi/LibMatrix.DebugDataValidationApi.csproj
index 8e4299b..92da98f 100644
--- a/Utilities/LibMatrix.DebugDataValidationApi/LibMatrix.DebugDataValidationApi.csproj
+++ b/Utilities/LibMatrix.DebugDataValidationApi/LibMatrix.DebugDataValidationApi.csproj
@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
</ItemGroup>
diff --git a/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj b/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj
index 8b9aa6d..8a0107b 100644
--- a/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj
+++ b/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj
@@ -18,14 +18,14 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="ArcaneLibs.StringNormalisation" Version="1.0.0-preview.20250630-114950" Condition="'$(Configuration)' == 'Release'" />
+ <PackageReference Include="ArcaneLibs.StringNormalisation" Version="1.0.0-preview.20250806-011111" Condition="'$(Configuration)' == 'Release'" />
<ProjectReference Include="..\..\ArcaneLibs\ArcaneLibs.StringNormalisation\ArcaneLibs.StringNormalisation.csproj" Condition="'$(Configuration)' == 'Debug'"/>
<ProjectReference Include="..\..\LibMatrix\LibMatrix.csproj"/>
<ProjectReference Include="..\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj" />
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
diff --git a/Utilities/LibMatrix.E2eeTestKit/LibMatrix.E2eeTestKit.csproj b/Utilities/LibMatrix.E2eeTestKit/LibMatrix.E2eeTestKit.csproj
index eea3d2f..0da4c48 100644
--- a/Utilities/LibMatrix.E2eeTestKit/LibMatrix.E2eeTestKit.csproj
+++ b/Utilities/LibMatrix.E2eeTestKit/LibMatrix.E2eeTestKit.csproj
@@ -8,8 +8,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.7" />
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.7" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.8" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.8" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
diff --git a/Utilities/LibMatrix.FederationTest/LibMatrix.FederationTest.csproj b/Utilities/LibMatrix.FederationTest/LibMatrix.FederationTest.csproj
index aa5647c..954a437 100644
--- a/Utilities/LibMatrix.FederationTest/LibMatrix.FederationTest.csproj
+++ b/Utilities/LibMatrix.FederationTest/LibMatrix.FederationTest.csproj
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
diff --git a/Utilities/LibMatrix.TestDataGenerator/LibMatrix.TestDataGenerator.csproj b/Utilities/LibMatrix.TestDataGenerator/LibMatrix.TestDataGenerator.csproj
index abb3169..93de8ab 100644
--- a/Utilities/LibMatrix.TestDataGenerator/LibMatrix.TestDataGenerator.csproj
+++ b/Utilities/LibMatrix.TestDataGenerator/LibMatrix.TestDataGenerator.csproj
@@ -17,7 +17,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings*.json">
diff --git a/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj b/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj
index 1c7d4fc..615808d 100644
--- a/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj
+++ b/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj
@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
</ItemGroup>
|