From 3dfb7b81b0fe19d37a7bf1183e248ca10c56277c Mon Sep 17 00:00:00 2001 From: Rory& Date: Fri, 23 Feb 2024 11:23:27 +0000 Subject: HS emulator --- .../Services/RoomStore.cs | 83 ++++++++++++++++++++++ .../Services/TokenService.cs | 26 +++++++ .../Services/UserStore.cs | 72 +++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs create mode 100644 Tests/LibMatrix.HomeserverEmulator/Services/TokenService.cs create mode 100644 Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs (limited to 'Tests/LibMatrix.HomeserverEmulator/Services') diff --git a/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs b/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs new file mode 100644 index 0000000..b4624ab --- /dev/null +++ b/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs @@ -0,0 +1,83 @@ +using System.Collections.Concurrent; +using System.Security.Cryptography; +using LibMatrix.EventTypes; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.Responses; + +namespace LibMatrix.HomeserverEmulator.Services; + +public class RoomStore { + public ConcurrentBag _rooms = new(); + private Dictionary _roomsById = new(); + + private void RebuildIndexes() { + _roomsById = _rooms.ToDictionary(u => u.RoomId); + } + + public Room? GetRoomById(string roomId, bool createIfNotExists = false) { + if (_roomsById.TryGetValue(roomId, out var user)) { + return user; + } + + if (!createIfNotExists) + return null; + + return CreateRoom(new() { }); + } + + public Room CreateRoom(CreateRoomRequest request) { + var room = new Room { + RoomId = $"!{Guid.NewGuid().ToString()}" + }; + if (!string.IsNullOrWhiteSpace(request.Name)) + room.SetStateInternal(new StateEvent() { + Type = RoomNameEventContent.EventId, + TypedContent = new RoomNameEventContent() { + Name = request.Name + } + }); + + if (!string.IsNullOrWhiteSpace(request.RoomAliasName)) + room.SetStateInternal(new StateEvent() { + Type = RoomCanonicalAliasEventContent.EventId, + TypedContent = new RoomCanonicalAliasEventContent() { + Alias = $"#{request.RoomAliasName}:localhost" + } + }); + + foreach (var stateEvent in request.InitialState ?? []) { + room.SetStateInternal(stateEvent); + } + + _rooms.Add(room); + RebuildIndexes(); + return room; + } + + public class Room { + public string RoomId { get; set; } + public List State { get; set; } = new(); + public Dictionary Timeline { get; set; } = new(); + + internal StateEventResponse SetStateInternal(StateEvent request) { + var state = new StateEventResponse() { + Type = request.Type, + StateKey = request.StateKey, + RawContent = request.RawContent, + EventId = Guid.NewGuid().ToString() + }; + State.Add(state); + return state; + } + + public StateEventResponse AddUser(string userId) { + return SetStateInternal(new() { + Type = RoomMemberEventContent.EventId, + StateKey = userId, + TypedContent = new RoomMemberEventContent() { + Membership = "join" + } + }); + } + } +} \ No newline at end of file diff --git a/Tests/LibMatrix.HomeserverEmulator/Services/TokenService.cs b/Tests/LibMatrix.HomeserverEmulator/Services/TokenService.cs new file mode 100644 index 0000000..8115bee --- /dev/null +++ b/Tests/LibMatrix.HomeserverEmulator/Services/TokenService.cs @@ -0,0 +1,26 @@ +namespace LibMatrix.HomeserverEmulator.Services; + +public class TokenService(IHttpContextAccessor accessor) { + public string? GetAccessToken() { + var ctx = accessor.HttpContext; + if (ctx is null) return null; + //qry + if (ctx.Request.Query.TryGetValue("access_token", out var token)) { + return token; + } + //header + if (ctx.Request.Headers.TryGetValue("Authorization", out var auth)) { + var parts = auth.ToString().Split(' '); + if (parts.Length == 2 && parts[0] == "Bearer") { + return parts[1]; + } + } + return null; + } + + public string? GenerateServerName() { + var ctx = accessor.HttpContext; + if (ctx is null) return null; + return ctx.Request.Host.ToString(); + } +} \ No newline at end of file diff --git a/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs b/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs new file mode 100644 index 0000000..ca1c577 --- /dev/null +++ b/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs @@ -0,0 +1,72 @@ +using LibMatrix.EventTypes.Spec.State; + +namespace LibMatrix.HomeserverEmulator.Services; + +public class UserStore(RoomStore roomStore) { + public List _users = new(); + private Dictionary _usersById = new(); + private Dictionary _usersByToken = new(); + + private void RebuildIndexes() { + _usersById = _users.ToDictionary(u => u.UserId); + _usersByToken = _users.ToDictionary(u => u.AccessToken); + } + + public async Task GetUserById(string userId, bool createIfNotExists = false) { + if (_usersById.TryGetValue(userId, out var user)) { + return user; + } + + if (!createIfNotExists) + return null; + + return await CreateUser(userId, Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new Dictionary()); + } + + public async Task GetUserByToken(string token, bool createIfNotExists = false, string? serverName = null) { + if (_usersByToken.TryGetValue(token, out var user)) { + return user; + } + + if (!createIfNotExists) + return null; + if (string.IsNullOrWhiteSpace(serverName)) throw new NullReferenceException("Server name was not passed"); + var uid = $"@{Guid.NewGuid().ToString()}:{serverName}"; + return await CreateUser(uid, Guid.NewGuid().ToString(), token, new Dictionary()); + } + + public async Task CreateUser(string userId, string deviceId, string accessToken, Dictionary profile) { + if (!profile.ContainsKey("displayname")) profile.Add("displayname", userId.Split(":")[0]); + if (!profile.ContainsKey("avatar_url")) profile.Add("avatar_url", null); + var user = new User { + UserId = userId, + DeviceId = deviceId, + AccessToken = accessToken, + Profile = profile + }; + _users.Add(user); + RebuildIndexes(); + + if (roomStore._rooms.Count > 0) + foreach (var item in Random.Shared.GetItems(roomStore._rooms.ToArray(), Math.Min(roomStore._rooms.Count, 400))) { + item.AddUser(userId); + } + + int random = Random.Shared.Next(10); + for (int i = 0; i < random; i++) { + var room = roomStore.CreateRoom(new()); + room.AddUser(userId); + } + + return user; + } + + public class User { + public string UserId { get; set; } + public string AccessToken { get; set; } + public string DeviceId { get; set; } + public Dictionary Profile { get; set; } + + public List JoinedRooms { get; set; } = new(); + } +} \ No newline at end of file -- cgit 1.4.1