about summary refs log tree commit diff
path: root/Utilities/LibMatrix.HomeserverEmulator
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/LibMatrix.HomeserverEmulator')
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/AuthController.cs13
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/DirectoryController.cs93
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/HEAdmin/HEAdminController.cs18
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/HEClient/HEClientController.cs34
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/HEDebug/HEDebugController.cs34
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/LegacyController.cs6
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Media/MediaController.cs14
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs2
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs28
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs3
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/SyncController.cs50
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/AccountDataController.cs4
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/FilterController.cs4
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/ProfileController.cs4
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/UserController.cs6
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/VersionsController.cs7
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Controllers/WellKnownController.cs64
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/LibMatrix.HomeserverEmulator.csproj6
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Program.cs9
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Services/HSEConfiguration.cs18
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Services/MediaStore.cs16
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs6
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Services/RoomStore.cs69
-rw-r--r--Utilities/LibMatrix.HomeserverEmulator/Services/UserStore.cs20
24 files changed, 322 insertions, 206 deletions
diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/AuthController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/AuthController.cs

index 5550c26..d0eaed4 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/AuthController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/AuthController.cs
@@ -1,26 +1,21 @@ -using System.Security.Cryptography; using System.Text.Json.Nodes; using LibMatrix.HomeserverEmulator.Services; using LibMatrix.Responses; -using LibMatrix.Services; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; [ApiController] [Route("/_matrix/client/{version}/")] -public class AuthController(ILogger<AuthController> logger, UserStore userStore, TokenService tokenService, HSEConfiguration config) : ControllerBase { +public class AuthController(ILogger<AuthController> logger, UserStore userStore, TokenService tokenService, HseConfiguration config) : ControllerBase { [HttpPost("login")] public async Task<LoginResponse> Login(LoginRequest request) { if (!request.Identifier.User.StartsWith('@')) request.Identifier.User = $"@{request.Identifier.User}:{tokenService.GenerateServerName(HttpContext)}"; - if (request.Identifier.User.EndsWith("localhost")) - request.Identifier.User = request.Identifier.User.Replace("localhost", tokenService.GenerateServerName(HttpContext)); + // if (request.Identifier.User.EndsWith("localhost")) + // request.Identifier.User = request.Identifier.User.Replace("localhost", tokenService.GenerateServerName(HttpContext)); - var user = await userStore.GetUserById(request.Identifier.User); - if (user is null) { - user = await userStore.CreateUser(request.Identifier.User); - } + var user = await userStore.GetUserById(request.Identifier.User) ?? await userStore.CreateUser(request.Identifier.User); return user.Login(); } diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/DirectoryController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/DirectoryController.cs
index b29edf5..d497ca6 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/DirectoryController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/DirectoryController.cs
@@ -1,11 +1,9 @@ -using System.Text.Json.Nodes; using System.Text.Json.Serialization; using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Services; using LibMatrix.Homeservers; using LibMatrix.Responses; -using LibMatrix.Services; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; @@ -35,6 +33,70 @@ public class DirectoryController(ILogger<DirectoryController> logger, RoomStore }; } + [HttpGet("client/v3/publicRooms")] + public async Task<PublicRoomDirectoryResult> GetPublicRooms(int limit = 100, string? server = null, string? since = null) { + var rooms = roomStore._rooms.OrderByDescending(x => x.JoinedMembers.Count).AsEnumerable(); + + if (since != null) { + rooms = rooms.SkipWhile(x => x.RoomId != since).Skip(1); + } + + if (server != null) { + rooms = rooms.Where(x => x.State.Any(y => y.Type == RoomMemberEventContent.EventId && y.StateKey!.EndsWith(server))); + } + + var count = rooms.Count(); + rooms = rooms.Take(limit); + + return new PublicRoomDirectoryResult() { + Chunk = rooms.Select(x => new PublicRoomDirectoryResult.PublicRoomListItem() { + RoomId = x.RoomId, + Name = x.State.FirstOrDefault(y => y.Type == RoomNameEventContent.EventId)?.RawContent?["name"]?.ToString(), + Topic = x.State.FirstOrDefault(y => y.Type == RoomTopicEventContent.EventId)?.RawContent?["topic"]?.ToString(), + AvatarUrl = x.State.FirstOrDefault(y => y.Type == RoomAvatarEventContent.EventId)?.RawContent?["url"]?.ToString(), + GuestCanJoin = x.State.Any(y => y.Type == RoomGuestAccessEventContent.EventId && y.RawContent?["guest_access"]?.ToString() == "can_join"), + NumJoinedMembers = x.JoinedMembers.Count, + WorldReadable = x.State.Any(y => y.Type == RoomHistoryVisibilityEventContent.EventId && y.RawContent?["history_visibility"]?.ToString() == "world_readable"), + JoinRule = x.State.FirstOrDefault(y => y.Type == RoomJoinRulesEventContent.EventId)?.RawContent?["join_rule"]?.ToString(), + CanonicalAlias = x.State.FirstOrDefault(y => y.Type == RoomCanonicalAliasEventContent.EventId)?.RawContent?["alias"]?.ToString() + }).ToList(), + NextBatch = count > limit ? rooms.Last().RoomId : null, + TotalRoomCountEstimate = count + }; + } + + [HttpPost("client/v3/publicRooms")] + public async Task<PublicRoomDirectoryResult> GetFilteredPublicRooms([FromBody] PublicRoomDirectoryRequest request, [FromQuery] string? server = null) { + var rooms = roomStore._rooms.OrderByDescending(x => x.JoinedMembers.Count).AsEnumerable(); + + if (request.Since != null) { + rooms = rooms.SkipWhile(x => x.RoomId != request.Since).Skip(1); + } + + if (server != null) { + rooms = rooms.Where(x => x.State.Any(y => y.Type == RoomMemberEventContent.EventId && y.StateKey!.EndsWith(server))); + } + + var count = rooms.Count(); + rooms = rooms.Take(request.Limit ?? 100); + + return new PublicRoomDirectoryResult() { + Chunk = rooms.Select(x => new PublicRoomDirectoryResult.PublicRoomListItem() { + RoomId = x.RoomId, + Name = x.State.FirstOrDefault(y => y.Type == RoomNameEventContent.EventId)?.RawContent?["name"]?.ToString(), + Topic = x.State.FirstOrDefault(y => y.Type == RoomTopicEventContent.EventId)?.RawContent?["topic"]?.ToString(), + AvatarUrl = x.State.FirstOrDefault(y => y.Type == RoomAvatarEventContent.EventId)?.RawContent?["url"]?.ToString(), + GuestCanJoin = x.State.Any(y => y.Type == RoomGuestAccessEventContent.EventId && y.RawContent?["guest_access"]?.ToString() == "can_join"), + NumJoinedMembers = x.JoinedMembers.Count, + WorldReadable = x.State.Any(y => y.Type == RoomHistoryVisibilityEventContent.EventId && y.RawContent?["history_visibility"]?.ToString() == "world_readable"), + JoinRule = x.State.FirstOrDefault(y => y.Type == RoomJoinRulesEventContent.EventId)?.RawContent?["join_rule"]?.ToString(), + CanonicalAlias = x.State.FirstOrDefault(y => y.Type == RoomCanonicalAliasEventContent.EventId)?.RawContent?["alias"]?.ToString() + }).ToList(), + NextBatch = count > request.Limit ? rooms.Last().RoomId : null, + TotalRoomCountEstimate = count + }; + } + #endregion #region User directory @@ -64,4 +126,29 @@ public class DirectoryController(ILogger<DirectoryController> logger, RoomStore } #endregion +} + +public class PublicRoomDirectoryRequest { + [JsonPropertyName("filter")] + public PublicRoomDirectoryFilter Filter { get; set; } + + [JsonPropertyName("include_all_networks")] + public bool IncludeAllNetworks { get; set; } + + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + [JsonPropertyName("since")] + public string? Since { get; set; } + + [JsonPropertyName("third_party_instance_id")] + public string? ThirdPartyInstanceId { get; set; } + + public class PublicRoomDirectoryFilter { + [JsonPropertyName("generic_search_term")] + public string? GenericSearchTerm { get; set; } + + [JsonPropertyName("room_types")] + public List<string>? RoomTypes { get; set; } + } } \ No newline at end of file diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEAdmin/HEAdminController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEAdmin/HEAdminController.cs new file mode 100644
index 0000000..1fb3251 --- /dev/null +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEAdmin/HEAdminController.cs
@@ -0,0 +1,18 @@ +using LibMatrix.HomeserverEmulator.Services; +using Microsoft.AspNetCore.Mvc; + +namespace LibMatrix.HomeserverEmulator.Controllers; + +[ApiController] +[Route("/_hse/admin")] +public class HEAdminController(ILogger<HEAdminController> logger, UserStore userStore, RoomStore roomStore) : ControllerBase { + [HttpGet("users")] + public async Task<List<UserStore.User>> GetUsers() { + return userStore._users.ToList(); + } + + [HttpGet("rooms")] + public async Task<List<RoomStore.Room>> GetRooms() { + return roomStore._rooms.ToList(); + } +} \ No newline at end of file diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEClient/HEClientController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEClient/HEClientController.cs new file mode 100644
index 0000000..85e4ddb --- /dev/null +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEClient/HEClientController.cs
@@ -0,0 +1,34 @@ +using ArcaneLibs.Collections; +using LibMatrix.HomeserverEmulator.Services; +using LibMatrix.Responses; +using Microsoft.AspNetCore.Mvc; + +namespace LibMatrix.HomeserverEmulator.Controllers; + +[ApiController] +[Route("/_hse/client/v1/external_profiles")] +public class HEClientController(ILogger<HEClientController> logger, UserStore userStore, TokenService tokenService) : ControllerBase { + [HttpGet] + public async Task<ObservableDictionary<string, LoginResponse>> GetExternalProfiles() { + var token = tokenService.GetAccessToken(HttpContext); + var user = await userStore.GetUserByToken(token); + + return user.AuthorizedSessions; + } + + [HttpPut("{name}")] + public async Task PutExternalProfile(string name, [FromBody] LoginResponse sessionData) { + var token = tokenService.GetAccessToken(HttpContext); + var user = await userStore.GetUserByToken(token); + + user.AuthorizedSessions[name] = sessionData; + } + + [HttpDelete("{name}")] + public async Task DeleteExternalProfile(string name) { + var token = tokenService.GetAccessToken(HttpContext); + var user = await userStore.GetUserByToken(token); + + user.AuthorizedSessions.Remove(name); + } +} \ No newline at end of file diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEDebug/HEDebugController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEDebug/HEDebugController.cs
index 9e0c17c..ce47245 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEDebug/HEDebugController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/HEDebug/HEDebugController.cs
@@ -1,18 +1,18 @@ -using LibMatrix.HomeserverEmulator.Services; -using Microsoft.AspNetCore.Mvc; - -namespace LibMatrix.HomeserverEmulator.Controllers; - -[ApiController] -[Route("/_hsEmulator")] -public class HEDebugController(ILogger<HEDebugController> logger, UserStore userStore, RoomStore roomStore) : ControllerBase { - [HttpGet("users")] - public async Task<List<UserStore.User>> GetUsers() { - return userStore._users.ToList(); - } - - [HttpGet("rooms")] - public async Task<List<RoomStore.Room>> GetRooms() { - return roomStore._rooms.ToList(); - } +using LibMatrix.HomeserverEmulator.Services; +using Microsoft.AspNetCore.Mvc; + +namespace LibMatrix.HomeserverEmulator.Controllers; + +[ApiController] +[Route("/_hsEmulator")] +public class HEDebugController(ILogger<HEDebugController> logger, UserStore userStore, RoomStore roomStore) : ControllerBase { + [HttpGet("users")] + public async Task<List<UserStore.User>> GetUsers() { + return userStore._users.ToList(); + } + + [HttpGet("rooms")] + public async Task<List<RoomStore.Room>> GetRooms() { + return roomStore._rooms.ToList(); + } } \ No newline at end of file diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/LegacyController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/LegacyController.cs
index 1fb427e..245770e 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/LegacyController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/LegacyController.cs
@@ -1,12 +1,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Text.Json.Nodes; using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Services; -using LibMatrix.Responses; -using LibMatrix.Services; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Media/MediaController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Media/MediaController.cs
index 59d37ff..6048bbb 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Media/MediaController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Media/MediaController.cs
@@ -1,9 +1,9 @@ using System.Text.Json.Nodes; using System.Text.RegularExpressions; -using ArcaneLibs.Collections; using LibMatrix.HomeserverEmulator.Services; using LibMatrix.Services; using Microsoft.AspNetCore.Mvc; +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member namespace LibMatrix.HomeserverEmulator.Controllers.Media; @@ -13,12 +13,12 @@ public class MediaController( ILogger<MediaController> logger, TokenService tokenService, UserStore userStore, - HSEConfiguration cfg, + HseConfiguration cfg, HomeserverResolverService hsResolver, MediaStore mediaStore) : ControllerBase { [HttpPost("upload")] - public async Task<object> UploadMedia([FromHeader(Name = "Content-Type")] string ContentType, [FromQuery] string filename, [FromBody] Stream file) { + public async Task<object> UploadMedia([FromHeader(Name = "Content-Type")] string contentType, [FromQuery] string filename, [FromBody] Stream file) { var token = tokenService.GetAccessTokenOrNull(HttpContext); if (token == null) throw new MatrixException() { @@ -76,7 +76,9 @@ public class MediaController( if (cfg.StoreData) { var path = Path.Combine(cfg.DataStoragePath, "media", serverName, mediaId); if (!System.IO.File.Exists(path)) { - var mediaUrl = await hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + // var mediaUrl = await hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + var homeserver = (await hsResolver.ResolveHomeserverFromWellKnown(serverName)).Client; + var mediaUrl = homeserver is null ? null : $"{homeserver}/_matrix/media/v3/download/"; if (mediaUrl is null) throw new MatrixException() { ErrorCode = "M_NOT_FOUND", @@ -91,7 +93,9 @@ public class MediaController( return new FileStream(path, FileMode.Open); } else { - var mediaUrl = await hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + // var mediaUrl = await hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + var homeserver = (await hsResolver.ResolveHomeserverFromWellKnown(serverName)).Client; + var mediaUrl = homeserver is null ? null : $"{homeserver}/_matrix/media/v3/download/"; if (mediaUrl is null) throw new MatrixException() { ErrorCode = "M_NOT_FOUND", diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs
index 7d735f7..6c57cc4 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs
@@ -1,4 +1,4 @@ -using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Services; using Microsoft.AspNetCore.Mvc; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs
index 7a16ace..61195b8 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs
@@ -1,10 +1,10 @@ -using System.Collections.Immutable; using System.Diagnostics; using System.Text.Json.Nodes; using ArcaneLibs; using ArcaneLibs.Extensions; using LibMatrix.EventTypes.Spec; -using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; +using LibMatrix.EventTypes.Spec.State.Space; using LibMatrix.Helpers; using LibMatrix.HomeserverEmulator.Extensions; using LibMatrix.HomeserverEmulator.Services; @@ -21,6 +21,7 @@ public class RoomTimelineController( TokenService tokenService, UserStore userStore, RoomStore roomStore, + HseConfiguration hseConfig, HomeserverProviderService hsProvider) : ControllerBase { [HttpPut("send/{eventType}/{txnId}")] public async Task<EventIdResponse> SendMessage(string roomId, string eventType, string txnId, [FromBody] JsonObject content) { @@ -34,21 +35,21 @@ public class RoomTimelineController( Error = "Room not found" }; - if (!room.JoinedMembers.Any(x => x.StateKey == user.UserId)) - throw new MatrixException() { - ErrorCode = "M_FORBIDDEN", - Error = "User is not in the room" - }; - var evt = new StateEvent() { RawContent = content, Type = eventType }.ToStateEvent(user, room); - room.Timeline.Add(evt); if (evt.Type == RoomMessageEventContent.EventId && (evt.TypedContent as RoomMessageEventContent).Body.StartsWith("!hse")) _ = Task.Run(() => HandleHseCommand(evt, room, user)); - // else + + if (!room.JoinedMembers.Any(x => x.StateKey == user.UserId)) + throw new MatrixException() { + ErrorCode = "M_FORBIDDEN", + Error = "User is not in the room" + }; + + room.Timeline.Add(evt); return new() { EventId = evt.EventId @@ -256,7 +257,8 @@ public class RoomTimelineController( room.Timeline.Add(new StateEventResponse() { Type = RoomMessageEventContent.EventId, TypedContent = content, - Sender = $"@hse:{tokenService.GenerateServerName(HttpContext)}", + // Sender = $"@hse:{tokenService.GenerateServerName(HttpContext)}", + Sender = $"@hse:{hseConfig.ServerName}", RoomId = room.RoomId, EventId = "$" + string.Join("", Random.Shared.GetItems("abcdefghijklmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789".ToCharArray(), 100)), OriginServerTs = DateTimeOffset.Now.ToUnixTimeMilliseconds() @@ -299,7 +301,7 @@ public class RoomTimelineController( InternalSendMessage(room, url + "&i=" + i); if (i % 5000 == 0 || i == 9999) { - Thread.Sleep(5000); + // Thread.Sleep(1000); do { InternalSendMessage(room, @@ -320,7 +322,7 @@ public class RoomTimelineController( var count = 1000; for (int i = 0; i < count; i++) { var crq = new CreateRoomRequest() { - Name = "Test room", + Name = $"Test room {i}", CreationContent = new() { ["version"] = "11" }, diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs
index c24e6e9..9dae2e5 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs
@@ -1,5 +1,4 @@ using System.Text.Json.Serialization; -using LibMatrix.EventTypes.Spec.State; using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Services; using LibMatrix.Responses; @@ -62,7 +61,7 @@ public class RoomsController(ILogger<RoomsController> logger, TokenService token var room = new RoomStore.Room($"!{Guid.NewGuid()}:{tokenService.GenerateServerName(HttpContext)}"); var eventTypesToTransfer = new[] { - RoomServerACLEventContent.EventId, + RoomServerAclEventContent.EventId, RoomEncryptionEventContent.EventId, RoomNameEventContent.EventId, RoomAvatarEventContent.EventId, diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/SyncController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/SyncController.cs
index f585eed..86c9f6a 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/SyncController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/SyncController.cs
@@ -1,7 +1,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Extensions; using LibMatrix.HomeserverEmulator.Services; using LibMatrix.Responses; @@ -11,7 +11,7 @@ namespace LibMatrix.HomeserverEmulator.Controllers; [ApiController] [Route("/_matrix/client/{version}/")] -public class SyncController(ILogger<SyncController> logger, TokenService tokenService, UserStore userStore, RoomStore roomStore, HSEConfiguration cfg) : ControllerBase { +public class SyncController(ILogger<SyncController> logger, TokenService tokenService, UserStore userStore, RoomStore roomStore, HseConfiguration cfg) : ControllerBase { [HttpGet("sync")] [SuppressMessage("ReSharper.DPA", "DPA0011: High execution time of MVC action", Justification = "Endpoint is expected to wait until data is available or timeout.")] public async Task<SyncResponse> Sync([FromQuery] string? since = null, [FromQuery] int? timeout = 5000) { @@ -34,10 +34,18 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe newSyncState = syncState.Clone(); var newSyncToken = Guid.NewGuid().ToString(); + long stallTime = 100; do { syncResp = IncrementalSync(user, session, syncState); syncResp.NextBatch = newSyncToken; - } while (!await HasDataOrStall(syncResp) && sw.ElapsedMilliseconds < timeout); + + var hasData = await HasDataOrStall(syncResp); + if (!hasData) { + await Task.Delay((int)Math.Min(stallTime, Math.Max(0, (timeout ?? 10) - sw.ElapsedMilliseconds))); + stallTime *= 2; + } + else break; + } while (sw.ElapsedMilliseconds < timeout); if (sw.ElapsedMilliseconds > timeout) { logger.LogTrace("Sync timed out after {Elapsed}", sw.Elapsed); @@ -49,6 +57,7 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe session.SyncStates[syncResp.NextBatch] = RecalculateSyncStates(newSyncState, syncResp); logger.LogTrace("Responding to sync after {totalElapsed}", sw.Elapsed); + // logger.LogTrace(syncResp.ToJson(ignoreNull: true)); return syncResp; } @@ -135,6 +144,7 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe }; // step 1: check previously synced rooms + int updatedRooms = 0; foreach (var (roomId, roomPosition) in syncState.RoomPositions) { var room = roomStore.GetRoomById(roomId); if (room == null) { @@ -147,9 +157,11 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe var newTimelineEvents = room.Timeline.Skip(roomPosition.TimelinePosition).ToList(); var newAccountDataEvents = room.AccountData[user.UserId].Skip(roomPosition.AccountDataPosition).ToList(); if (newTimelineEvents.Count == 0 && newAccountDataEvents.Count == 0) continue; + if (updatedRooms++ >= 50) break; // performance cap data.Join[room.RoomId] = new() { State = new(newTimelineEvents.GetCalculatedState()), - Timeline = new(newTimelineEvents, false) + Timeline = new(newTimelineEvents, false), + AccountData = new(newAccountDataEvents) }; } } @@ -160,11 +172,11 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe } // step 2: check newly joined rooms - var untrackedRooms = roomStore._rooms.Where(r => !syncState.RoomPositions.ContainsKey(r.RoomId)).ToList(); + // var untrackedRooms = roomStore._rooms.Where(r => !syncState.RoomPositions.ContainsKey(r.RoomId)).ToList(); var allJoinedRooms = roomStore.GetRoomsByMember(user.UserId).ToArray(); if (allJoinedRooms.Length == 0) return data; - var rooms = Random.Shared.GetItems(allJoinedRooms, Math.Min(allJoinedRooms.Length, 50)); + var rooms = Random.Shared.GetItems(allJoinedRooms, Math.Min(allJoinedRooms.Length, 1)); foreach (var membership in rooms) { var membershipContent = membership.TypedContent as RoomMemberEventContent ?? throw new InvalidOperationException("Membership event content is not RoomMemberEventContent"); @@ -211,7 +223,7 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe private bool HasData(SyncResponse resp) { return resp.Rooms?.Invite?.Count > 0 || resp.Rooms?.Join?.Count > 0 || resp.Rooms?.Leave?.Count > 0; } - + private async Task<bool> HasDataOrStall(SyncResponse resp) { // logger.LogTrace("Checking if sync response has data: {resp}", resp.ToJson(indent: false, ignoreNull: true)); // if (resp.AccountData?.Events?.Count > 0) return true; @@ -246,29 +258,23 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe // }; var hasData = resp is { - AccountData: { - Events: { Count: > 0 } - } + AccountData.Events.Count: > 0 } or { - Presence: { - Events: { Count: > 0 } - } + Presence.Events.Count: > 0 } or { DeviceLists: { - Changed: { Count: > 0 }, - Left: { Count: > 0 } + Changed.Count: > 0, + Left.Count: > 0 } } or { - ToDevice: { - Events: { Count: > 0 } - } + ToDevice.Events.Count: > 0 } or { Rooms: { - Invite: { Count: > 0 } + Invite.Count: > 0 } or { - Join: { Count: > 0 } + Join.Count: > 0 } or { - Leave: { Count: > 0 } + Leave.Count: > 0 } }; @@ -278,7 +284,7 @@ public class SyncController(ILogger<SyncController> logger, TokenService tokenSe if (!hasData) { // logger.LogDebug($"Sync response has no data, stalling for 1000ms: {resp.ToJson(indent: false, ignoreNull: true)}"); - await Task.Delay(10); + // await Task.Delay(100); } return hasData; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/AccountDataController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/AccountDataController.cs
index a32d283..41cf62a 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/AccountDataController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/AccountDataController.cs
@@ -1,9 +1,5 @@ using System.Text.Json.Nodes; -using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; -using LibMatrix.Filters; using LibMatrix.HomeserverEmulator.Services; -using LibMatrix.Responses; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/FilterController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/FilterController.cs
index bdee7bc..f163a8e 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/FilterController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/FilterController.cs
@@ -1,9 +1,5 @@ -using System.Text.Json.Nodes; -using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; using LibMatrix.Filters; using LibMatrix.HomeserverEmulator.Services; -using LibMatrix.Responses; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/ProfileController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/ProfileController.cs
index 98c41da..7afdcb5 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/ProfileController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/ProfileController.cs
@@ -1,9 +1,5 @@ using System.Text.Json.Nodes; -using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; -using LibMatrix.Filters; using LibMatrix.HomeserverEmulator.Services; -using LibMatrix.Responses; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/UserController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/UserController.cs
index 2be3896..40f3667 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/UserController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Users/UserController.cs
@@ -1,10 +1,6 @@ -using System.Text.Json.Nodes; using System.Text.Json.Serialization; -using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; -using LibMatrix.Filters; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Services; -using LibMatrix.Responses; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/VersionsController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/VersionsController.cs
index 0c3bde6..93e4b4f 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/VersionsController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/VersionsController.cs
@@ -1,8 +1,6 @@ -using System.Text.Json.Nodes; using System.Text.Json.Serialization; using LibMatrix.Homeservers; using LibMatrix.Responses; -using LibMatrix.Services; using Microsoft.AspNetCore.Mvc; namespace LibMatrix.HomeserverEmulator.Controllers; @@ -30,6 +28,11 @@ public class VersionsController(ILogger<WellKnownController> logger) : Controlle "v1.6", "v1.7", "v1.8", + "v1.9", + "v1.10", + "v1.11", + "v1.12", + "v1.13" }, UnstableFeatures = new() }; diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/WellKnownController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/WellKnownController.cs
index 97e460d..3cec712 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/WellKnownController.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/WellKnownController.cs
@@ -1,32 +1,34 @@ -using System.Text.Json.Nodes; -using LibMatrix.Services; -using Microsoft.AspNetCore.Mvc; - -namespace LibMatrix.HomeserverEmulator.Controllers; - -[ApiController] -[Route("/.well-known/matrix/")] -public class WellKnownController(ILogger<WellKnownController> logger) : ControllerBase { - [HttpGet("client")] - public JsonObject GetClientWellKnown() { - var obj = new JsonObject() { - ["m.homeserver"] = new JsonObject() { - ["base_url"] = $"{Request.Scheme}://{Request.Host}" - } - }; - - logger.LogInformation("Serving client well-known: {}", obj); - - return obj; - } - [HttpGet("server")] - public JsonObject GetServerWellKnown() { - var obj = new JsonObject() { - ["m.server"] = $"{Request.Scheme}://{Request.Host}" - }; - - logger.LogInformation("Serving server well-known: {}", obj); - - return obj; - } +using System.Text.Json.Nodes; +using Microsoft.AspNetCore.Mvc; + +namespace LibMatrix.HomeserverEmulator.Controllers; + +[ApiController] +[Route("/.well-known/matrix/")] +public class WellKnownController(ILogger<WellKnownController> logger) : ControllerBase { + [HttpGet("client")] + public JsonObject GetClientWellKnown() { + var obj = new JsonObject() { + ["m.homeserver"] = new JsonObject() { + // ["base_url"] = $"{Request.Scheme}://{Request.Host}" + ["base_url"] = $"https://{Request.Host}" + } + }; + + logger.LogInformation("Serving client well-known: {}", obj); + + return obj; + } + + [HttpGet("server")] + public JsonObject GetServerWellKnown() { + var obj = new JsonObject() { + // ["m.server"] = $"{Request.Scheme}://{Request.Host}" + ["m.server"] = $"https://{Request.Host}" + }; + + logger.LogInformation("Serving server well-known: {}", obj); + + return obj; + } } \ No newline at end of file diff --git a/Utilities/LibMatrix.HomeserverEmulator/LibMatrix.HomeserverEmulator.csproj b/Utilities/LibMatrix.HomeserverEmulator/LibMatrix.HomeserverEmulator.csproj
index f5ff2d1..5178012 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/LibMatrix.HomeserverEmulator.csproj +++ b/Utilities/LibMatrix.HomeserverEmulator/LibMatrix.HomeserverEmulator.csproj
@@ -11,8 +11,8 @@ <ItemGroup> <PackageReference Include="EasyCompressor.LZMA" Version="2.0.2" /> - <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="7.0.0" /> + <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" /> </ItemGroup> <ItemGroup> @@ -1027,6 +1027,8 @@ <_ContentIncludedByDefault Remove="data\rooms\!ffda8b8c-baf0-4938-bda5-12d30ef39fe8.json" /> <_ContentIncludedByDefault Remove="data\rooms\!ffe1fed4-8e49-4584-8f74-a1f9a58b70ed.json" /> <_ContentIncludedByDefault Remove="data\rooms\!ffffc7e7-4b1e-40a9-9f24-ebd95267e758.json" /> + <_ContentIncludedByDefault Remove="data\users\@emma:hse.localhost\tokens.json" /> + <_ContentIncludedByDefault Remove="data\users\@emma:hse.localhost\user.json" /> </ItemGroup> </Project> diff --git a/Utilities/LibMatrix.HomeserverEmulator/Program.cs b/Utilities/LibMatrix.HomeserverEmulator/Program.cs
index 9ea6fce..c72df5a 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Program.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Program.cs
@@ -1,7 +1,6 @@ using System.Net.Mime; using System.Text.Json.Serialization; using LibMatrix; -using LibMatrix.HomeserverEmulator.Extensions; using LibMatrix.HomeserverEmulator.Services; using LibMatrix.Services; using Microsoft.AspNetCore.Diagnostics; @@ -32,7 +31,7 @@ builder.Services.AddSwaggerGen(c => { }); builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); -builder.Services.AddSingleton<HSEConfiguration>(); +builder.Services.AddSingleton<HseConfiguration>(); builder.Services.AddSingleton<UserStore>(); builder.Services.AddSingleton<RoomStore>(); builder.Services.AddSingleton<MediaStore>(); @@ -77,7 +76,7 @@ app.UseExceptionHandler(exceptionHandlerApp => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); if (exceptionHandlerPathFeature?.Error is not null) - Console.WriteLine(exceptionHandlerPathFeature.Error.ToString()!); + Console.WriteLine(exceptionHandlerPathFeature.Error.ToString()); if (exceptionHandlerPathFeature?.Error is MatrixException mxe) { context.Response.StatusCode = mxe.ErrorCode switch { @@ -86,14 +85,14 @@ app.UseExceptionHandler(exceptionHandlerApp => { _ => StatusCodes.Status500InternalServerError }; context.Response.ContentType = MediaTypeNames.Application.Json; - await context.Response.WriteAsync(mxe.GetAsJson()!); + await context.Response.WriteAsync(mxe.GetAsJson()); } else { context.Response.StatusCode = StatusCodes.Status500InternalServerError; context.Response.ContentType = MediaTypeNames.Application.Json; await context.Response.WriteAsync(new MatrixException() { ErrorCode = "M_UNKNOWN", - Error = exceptionHandlerPathFeature?.Error.ToString() + Error = exceptionHandlerPathFeature?.Error?.ToString() ?? "Unknown error" }.GetAsJson()); } }); diff --git a/Utilities/LibMatrix.HomeserverEmulator/Services/HSEConfiguration.cs b/Utilities/LibMatrix.HomeserverEmulator/Services/HSEConfiguration.cs
index bcfb629..04ce050 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Services/HSEConfiguration.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Services/HSEConfiguration.cs
@@ -4,12 +4,12 @@ using ArcaneLibs.Extensions; namespace LibMatrix.HomeserverEmulator.Services; -public class HSEConfiguration { - private static ILogger<HSEConfiguration> _logger; - public static HSEConfiguration Current { get; set; } +public class HseConfiguration { + private static ILogger<HseConfiguration> _logger; + public static HseConfiguration Current { get; set; } [RequiresUnreferencedCode("Uses reflection binding")] - public HSEConfiguration(ILogger<HSEConfiguration> logger, IConfiguration config, HostBuilderContext host) { + public HseConfiguration(ILogger<HseConfiguration> logger, IConfiguration config, HostBuilderContext host) { Current = this; _logger = logger; logger.LogInformation("Loading configuration for environment: {}...", host.HostingEnvironment.EnvironmentName); @@ -22,15 +22,15 @@ public class HSEConfiguration { _logger.LogInformation("Configuration loaded: {}", this.ToJson()); } - public string CacheStoragePath { get; set; } + public required string CacheStoragePath { get; set; } - public string DataStoragePath { get; set; } + public required string DataStoragePath { get; set; } - public bool StoreData { get; set; } = true; + public required bool StoreData { get; set; } = true; - public string ServerName { get; set; } = "localhost"; + public required string ServerName { get; set; } = "localhost"; - public bool UnknownSyncTokenIsInitialSync { get; set; } = true; + public required bool UnknownSyncTokenIsInitialSync { get; set; } = true; private static string ExpandPath(string path, bool retry = true) { _logger.LogInformation("Expanding path `{}`", path); diff --git a/Utilities/LibMatrix.HomeserverEmulator/Services/MediaStore.cs b/Utilities/LibMatrix.HomeserverEmulator/Services/MediaStore.cs
index 00f2a42..7945d3a 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Services/MediaStore.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Services/MediaStore.cs
@@ -4,18 +4,18 @@ using LibMatrix.Services; namespace LibMatrix.HomeserverEmulator.Services; public class MediaStore { - private readonly HSEConfiguration _config; + private readonly HseConfiguration _config; private readonly HomeserverResolverService _hsResolver; - private List<MediaInfo> index = new(); + private List<MediaInfo> _mediaIndex = new(); - public MediaStore(HSEConfiguration config, HomeserverResolverService hsResolver) { + public MediaStore(HseConfiguration config, HomeserverResolverService hsResolver) { _config = config; _hsResolver = hsResolver; if (config.StoreData) { var path = Path.Combine(config.DataStoragePath, "media"); if (!Directory.Exists(path)) Directory.CreateDirectory(path); if (File.Exists(Path.Combine(path, "index.json"))) - index = JsonSerializer.Deserialize<List<MediaInfo>>(File.ReadAllText(Path.Combine(path, "index.json"))); + _mediaIndex = JsonSerializer.Deserialize<List<MediaInfo>>(File.ReadAllText(Path.Combine(path, "index.json"))); } else Console.WriteLine("Data storage is disabled, not loading rooms from disk"); @@ -36,7 +36,9 @@ public class MediaStore { if (_config.StoreData) { var path = Path.Combine(_config.DataStoragePath, "media", serverName, mediaId); if (!File.Exists(path)) { - var mediaUrl = await _hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + // var mediaUrl = await _hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + var homeserver = (await _hsResolver.ResolveHomeserverFromWellKnown(serverName)).Client; + var mediaUrl = homeserver is null ? null : $"{homeserver}/_matrix/media/v3/download/"; if (mediaUrl is null) throw new MatrixException() { ErrorCode = "M_NOT_FOUND", @@ -50,7 +52,9 @@ public class MediaStore { return new FileStream(path, FileMode.Open); } else { - var mediaUrl = await _hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + // var mediaUrl = await _hsResolver.ResolveMediaUri(serverName, $"mxc://{serverName}/{mediaId}"); + var homeserver = (await _hsResolver.ResolveHomeserverFromWellKnown(serverName)).Client; + var mediaUrl = homeserver is null ? null : $"{homeserver}/_matrix/media/v3/download/"; if (mediaUrl is null) throw new MatrixException() { ErrorCode = "M_NOT_FOUND", diff --git a/Utilities/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs b/Utilities/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs
index 0128ba6..0603a2d 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs
@@ -35,15 +35,15 @@ public class PaginationTokenResolverService(ILogger<PaginationTokenResolverServi } } - public async Task<StateEventResponse?> ResolveTokenToEvent(string token, RoomStore.Room room) { + public Task<StateEventResponse?> ResolveTokenToEvent(string token, RoomStore.Room room) { if (token.StartsWith('$')) { //we have an event ID logger.LogTrace("ResolveTokenToEvent(EventId({token}), Room({room})): searching for event...", token, room.RoomId); var evt = room.Timeline.SingleOrDefault(x => x.EventId == token); - if (evt is not null) return evt; + if (evt is not null) return Task.FromResult(evt); logger.LogTrace("ResolveTokenToEvent({token}, Room({room})): event not in requested room...", token, room.RoomId); - return null; + return Task.FromResult<StateEventResponse?>(null); } else { // we have a sync token diff --git a/Utilities/LibMatrix.HomeserverEmulator/Services/RoomStore.cs b/Utilities/LibMatrix.HomeserverEmulator/Services/RoomStore.cs
index c798cce..b6fe7c2 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Services/RoomStore.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Services/RoomStore.cs
@@ -2,13 +2,12 @@ using System.Collections.Concurrent; using System.Collections.Frozen; using System.Collections.Immutable; using System.Collections.ObjectModel; -using System.Collections.Specialized; using System.Text.Json; using System.Text.Json.Nodes; using ArcaneLibs; using ArcaneLibs.Collections; using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.HomeserverEmulator.Controllers.Rooms; using LibMatrix.Responses; @@ -19,14 +18,14 @@ public class RoomStore { public ConcurrentBag<Room> _rooms = new(); private FrozenDictionary<string, Room> _roomsById = FrozenDictionary<string, Room>.Empty; - public RoomStore(ILogger<RoomStore> logger, HSEConfiguration config) { + public RoomStore(ILogger<RoomStore> logger, HseConfiguration config) { _logger = logger; if (config.StoreData) { var path = Path.Combine(config.DataStoragePath, "rooms"); if (!Directory.Exists(path)) Directory.CreateDirectory(path); foreach (var file in Directory.GetFiles(path)) { var room = JsonSerializer.Deserialize<Room>(File.ReadAllText(file)); - if (room is not null) _rooms.Add(room); + if (room is not null && room.State.Any(x => x.Type == RoomCreateEventContent.EventId)) _rooms.Add(room); } } else @@ -35,25 +34,8 @@ public class RoomStore { RebuildIndexes(); } - private SemaphoreSlim a = new(1, 1); private void RebuildIndexes() { - // a.Wait(); - // lock (_roomsById) - // _roomsById = new ConcurrentDictionary<string, Room>(_rooms.ToDictionary(u => u.RoomId)); - // foreach (var room in _rooms) { - // _roomsById.AddOrUpdate(room.RoomId, room, (key, old) => room); - // } - // - // var roomsArr = _rooms.ToArray(); - // foreach (var (id, room) in _roomsById) { - // if (!roomsArr.Any(x => x.RoomId == id)) - // _roomsById.TryRemove(id, out _); - // } - - // _roomsById = new ConcurrentDictionary<string, Room>(_rooms.ToDictionary(u => u.RoomId)); _roomsById = _rooms.ToFrozenDictionary(u => u.RoomId); - - // a.Release(); } public Room? GetRoomById(string roomId, bool createIfNotExists = false) { @@ -64,18 +46,19 @@ public class RoomStore { if (!createIfNotExists) return null; - return CreateRoom(new() { }); + return CreateRoom(new()); } public Room CreateRoom(CreateRoomRequest request, UserStore.User? user = null) { var room = new Room(roomId: $"!{Guid.NewGuid().ToString()}"); var newCreateEvent = new StateEvent() { Type = RoomCreateEventContent.EventId, - RawContent = new() { } + RawContent = new() }; foreach (var (key, value) in request.CreationContent) { newCreateEvent.RawContent[key] = value.DeepClone(); + Console.WriteLine($"RawContent[{key}] = {value.DeepClone().ToJson(ignoreNull: true)}"); } if (user != null) { @@ -207,7 +190,7 @@ public class RoomStore { : JsonSerializer.Deserialize<JsonObject>(JsonSerializer.Serialize(request.TypedContent))) }; Timeline.Add(state); - if(state.StateKey != null) + if (state.StateKey != null) RebuildState(); return state; } @@ -226,32 +209,36 @@ public class RoomStore { } // public async Task SaveDebounced() { - // if (!HSEConfiguration.Current.StoreData) return; - // await _debounceCts.CancelAsync(); - // _debounceCts = new CancellationTokenSource(); - // try { - // await Task.Delay(250, _debounceCts.Token); - // // Ensure all state events are in the timeline - // State.Where(s => !Timeline.Contains(s)).ToList().ForEach(s => Timeline.Add(s)); - // var path = Path.Combine(HSEConfiguration.Current.DataStoragePath, "rooms", $"{RoomId}.json"); - // Console.WriteLine($"Saving room {RoomId} to {path}!"); - // await File.WriteAllTextAsync(path, this.ToJson(ignoreNull: true)); - // } - // catch (TaskCanceledException) { } + // if (!HSEConfiguration.Current.StoreData) return; + // await _debounceCts.CancelAsync(); + // _debounceCts = new CancellationTokenSource(); + // try { + // await Task.Delay(250, _debounceCts.Token); + // // Ensure all state events are in the timeline + // State.Where(s => !Timeline.Contains(s)).ToList().ForEach(s => Timeline.Add(s)); + // var path = Path.Combine(HSEConfiguration.Current.DataStoragePath, "rooms", $"{RoomId}.json"); + // Console.WriteLine($"Saving room {RoomId} to {path}!"); + // await File.WriteAllTextAsync(path, this.ToJson(ignoreNull: true)); + // } + // catch (TaskCanceledException) { } // } private SemaphoreSlim saveSemaphore = new(1, 1); + private CancellationTokenSource _saveCts = new(); + public async Task SaveDebounced() { Task.Run(async () => { - await saveSemaphore.WaitAsync(); + // await saveSemaphore.WaitAsync(); + await _saveCts.CancelAsync(); + _saveCts = new(); try { - var path = Path.Combine(HSEConfiguration.Current.DataStoragePath, "rooms", $"{RoomId}.json"); - Console.WriteLine($"Saving room {RoomId} to {path}!"); - await File.WriteAllTextAsync(path, this.ToJson(ignoreNull: true)); + var path = Path.Combine(HseConfiguration.Current.DataStoragePath, "rooms", $"{RoomId}.json"); + // Console.WriteLine($"Saving room {RoomId} to {path}!"); + await File.WriteAllTextAsync(path, this.ToJson(ignoreNull: true), _saveCts.Token); } finally { - saveSemaphore.Release(); + // saveSemaphore.Release(); } }); } diff --git a/Utilities/LibMatrix.HomeserverEmulator/Services/UserStore.cs b/Utilities/LibMatrix.HomeserverEmulator/Services/UserStore.cs
index 4684b01..7f211e3 100644 --- a/Utilities/LibMatrix.HomeserverEmulator/Services/UserStore.cs +++ b/Utilities/LibMatrix.HomeserverEmulator/Services/UserStore.cs
@@ -5,7 +5,6 @@ using System.Text.Json.Nodes; using ArcaneLibs; using ArcaneLibs.Collections; using ArcaneLibs.Extensions; -using LibMatrix.EventTypes.Spec.State; using LibMatrix.Filters; using LibMatrix.Responses; @@ -13,14 +12,14 @@ namespace LibMatrix.HomeserverEmulator.Services; public class UserStore { public ConcurrentBag<User> _users = new(); - private readonly HSEConfiguration _config; + private readonly HseConfiguration _config; private readonly RoomStore _roomStore; - public UserStore(HSEConfiguration config, RoomStore roomStore) { + public UserStore(HseConfiguration config, RoomStore roomStore) { _config = config; _roomStore = roomStore; if (config.StoreData) { - var dataDir = Path.Combine(HSEConfiguration.Current.DataStoragePath, "users"); + var dataDir = Path.Combine(HseConfiguration.Current.DataStoragePath, "users"); if (!Directory.Exists(dataDir)) Directory.CreateDirectory(dataDir); foreach (var userId in Directory.GetDirectories(dataDir)) { var tokensDir = Path.Combine(dataDir, userId, "tokens.json"); @@ -138,7 +137,7 @@ public class UserStore { private ObservableDictionary<string, object> _profile; private ObservableCollection<StateEventResponse> _accountData; private ObservableDictionary<string, RoomKeysResponse> _roomKeys; - private ObservableDictionary<string, AuthorizedSession> _authorizedSessions; + private ObservableDictionary<string, LoginResponse> _authorizedSessions; public string UserId { get => _userId; @@ -195,7 +194,7 @@ public class UserStore { } } - public ObservableDictionary<string, AuthorizedSession> AuthorizedSessions { + public ObservableDictionary<string, LoginResponse> AuthorizedSessions { get => _authorizedSessions; set { if (value == _authorizedSessions) return; @@ -208,12 +207,12 @@ public class UserStore { public bool IsGuest { get; set; } public async Task SaveDebounced() { - if (!HSEConfiguration.Current.StoreData) return; + if (!HseConfiguration.Current.StoreData) return; await _debounceCts.CancelAsync(); _debounceCts = new CancellationTokenSource(); try { await Task.Delay(250, _debounceCts.Token); - var dataDir = Path.Combine(HSEConfiguration.Current.DataStoragePath, "users", _userId); + var dataDir = Path.Combine(HseConfiguration.Current.DataStoragePath, "users", _userId); if (!Directory.Exists(dataDir)) Directory.CreateDirectory(dataDir); var tokensDir = Path.Combine(dataDir, "tokens.json"); var path = Path.Combine(dataDir, $"user.json"); @@ -264,10 +263,5 @@ public class UserStore { UserId = UserId }; } - - public class AuthorizedSession { - public string Homeserver { get; set; } - public string AccessToken { get; set; } - } } } \ No newline at end of file