diff --git a/MatrixUtils.LibDMSpace/DMSpaceConfiguration.cs b/MatrixUtils.LibDMSpace/DMSpaceConfiguration.cs
new file mode 100644
index 0000000..571d09d
--- /dev/null
+++ b/MatrixUtils.LibDMSpace/DMSpaceConfiguration.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace MatrixUtils.LibDMSpace;
+
+//gay.rory.dm_space
+public class DMSpaceConfiguration {
+ public const string EventId = "gay.rory.dm_space";
+
+ [JsonPropertyName("dm_space_id")]
+ public string? DMSpaceId { get; set; }
+
+}
diff --git a/MatrixUtils.LibDMSpace/DMSpaceRoom.cs b/MatrixUtils.LibDMSpace/DMSpaceRoom.cs
new file mode 100644
index 0000000..3fb0ab6
--- /dev/null
+++ b/MatrixUtils.LibDMSpace/DMSpaceRoom.cs
@@ -0,0 +1,83 @@
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.Homeservers;
+using LibMatrix.RoomTypes;
+using MatrixUtils.LibDMSpace.StateEvents;
+
+namespace MatrixUtils.LibDMSpace;
+
+public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomId) : SpaceRoom(homeserver, roomId) {
+ private readonly GenericRoom _room;
+
+ public async Task<DMSpaceInfo?> GetDmSpaceInfo() {
+ return await GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId);
+ }
+
+ public async IAsyncEnumerable<GenericRoom> GetChildrenAsync(bool includeRemoved = false) {
+ var rooms = new List<GenericRoom>();
+ var state = GetFullStateAsync();
+ await foreach (var stateEvent in state) {
+ if (stateEvent!.Type != "m.space.child") continue;
+ if (stateEvent.RawContent!.ToJson() != "{}" || includeRemoved)
+ yield return homeserver.GetRoom(stateEvent.StateKey);
+ }
+ }
+
+ public async Task<EventIdResponse> AddChildAsync(GenericRoom room) {
+ var members = room.GetMembersEnumerableAsync(true);
+ Dictionary<string, int> memberCountByHs = new();
+ await foreach (var member in members) {
+ var server = member.StateKey.Split(':')[1];
+ if (memberCountByHs.ContainsKey(server)) memberCountByHs[server]++;
+ else memberCountByHs[server] = 1;
+ }
+
+ var resp = await SendStateEventAsync("m.space.child", room.RoomId, new {
+ via = memberCountByHs
+ .OrderByDescending(x => x.Value)
+ .Select(x => x.Key)
+ .Take(10)
+ });
+ return resp;
+ }
+
+ public async Task ImportNativeDMs() {
+ var dmSpaceInfo = await GetDmSpaceInfo();
+ if (dmSpaceInfo is null) throw new NullReferenceException("DM Space is not configured!");
+ if (dmSpaceInfo.LayerByUser)
+ await ImportNativeDMsIntoLayers();
+ else await ImportNativeDMsWithoutLayers();
+ }
+
+ #region Import Native DMs
+
+ private async Task ImportNativeDMsWithoutLayers() {
+ var mdirect = await homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
+ foreach (var (userId, dmRooms) in mdirect) {
+ foreach (var roomid in dmRooms) {
+ var dri = new DMRoomInfo() {
+ RemoteUsers = new() {
+ userId
+ }
+ };
+ // Add all DM room members
+ var members = homeserver.GetRoom(roomid).GetMembersEnumerableAsync();
+ await foreach (var member in members)
+ if (member.StateKey != userId)
+ dri.RemoteUsers.Add(member.StateKey);
+ // Remove members of DM space
+ members = GetMembersEnumerableAsync();
+ await foreach (var member in members)
+ if (dri.RemoteUsers.Contains(member.StateKey))
+ dri.RemoteUsers.Remove(member.StateKey);
+ await SendStateEventAsync(DMRoomInfo.EventId, roomid, dri);
+ }
+ }
+ }
+
+ private async Task ImportNativeDMsIntoLayers() {
+ var mdirect = await homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
+ }
+
+ #endregion
+}
diff --git a/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj b/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj
new file mode 100644
index 0000000..72c1666
--- /dev/null
+++ b/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <LinkIncremental>true</LinkIncremental>
+ <LangVersion>preview</LangVersion>
+ <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj"/>
+ </ItemGroup>
+</Project>
diff --git a/MatrixUtils.LibDMSpace/StateEvents/DMRoomInfo.cs b/MatrixUtils.LibDMSpace/StateEvents/DMRoomInfo.cs
new file mode 100644
index 0000000..5aa62d7
--- /dev/null
+++ b/MatrixUtils.LibDMSpace/StateEvents/DMRoomInfo.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+using LibMatrix.EventTypes;
+using LibMatrix.Interfaces;
+
+namespace MatrixUtils.LibDMSpace.StateEvents;
+
+[MatrixEvent(EventName = EventId)]
+public class DMRoomInfo : TimelineEventContent {
+ public const string EventId = "gay.rory.dm_room_info";
+ [JsonPropertyName("remote_users")]
+ public List<string> RemoteUsers { get; set; }
+
+
+}
diff --git a/MatrixUtils.LibDMSpace/StateEvents/DMSpaceInfo.cs b/MatrixUtils.LibDMSpace/StateEvents/DMSpaceInfo.cs
new file mode 100644
index 0000000..0df1913
--- /dev/null
+++ b/MatrixUtils.LibDMSpace/StateEvents/DMSpaceInfo.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+using LibMatrix.EventTypes;
+using LibMatrix.Interfaces;
+
+namespace MatrixUtils.LibDMSpace.StateEvents;
+
+[MatrixEvent(EventName = EventId)]
+public class DMSpaceInfo : TimelineEventContent {
+ public const string EventId = "gay.rory.dm_space_info";
+
+ [JsonPropertyName("is_layered")]
+ public bool LayerByUser { get; set; }
+
+}
|