From ae18c049338dfcb33f721eeeb0a05571e8bd87a9 Mon Sep 17 00:00:00 2001 From: Rory& Date: Thu, 30 May 2024 08:02:40 +0000 Subject: Rename StateEvent to LegacyMatrixEvent --- LibMatrix/EventIdResponse.cs | 1 + LibMatrix/Extensions/EnumerableExtensions.cs | 4 +- LibMatrix/Extensions/JsonElementExtensions.cs | 6 +- LibMatrix/Helpers/SyncHelper.cs | 4 +- LibMatrix/Helpers/SyncStateResolver.cs | 42 ++-- LibMatrix/LegacyMatrixEvent.cs | 225 ++++++++++++++++++ LibMatrix/MessagesResponse.cs | 4 +- LibMatrix/Responses/CreateRoomRequest.cs | 12 +- LibMatrix/Responses/SyncResponse.cs | 6 +- LibMatrix/RoomTypes/GenericRoom.cs | 26 +-- LibMatrix/StateEvent.cs | 257 --------------------- .../Controllers/Rooms/RoomAccountDataController.cs | 6 +- .../Controllers/Rooms/RoomMembersController.cs | 4 +- .../Controllers/Rooms/RoomStateController.cs | 4 +- .../Controllers/Rooms/RoomTimelineController.cs | 16 +- .../Extensions/EventExtensions.cs | 12 +- .../Services/PaginationTokenResolverService.cs | 2 +- .../Services/RoomStore.cs | 34 +-- .../Services/UserStore.cs | 8 +- .../Abstractions/RoomAbstraction.cs | 16 +- Tests/LibMatrix.Tests/Tests/RoomTests.cs | 4 +- .../Interfaces/CommandContext.cs | 2 +- .../Services/CommandListenerHostedService.cs | 4 +- .../Services/InviteListenerHostedService.cs | 2 +- 24 files changed, 335 insertions(+), 366 deletions(-) create mode 100644 LibMatrix/LegacyMatrixEvent.cs delete mode 100644 LibMatrix/StateEvent.cs diff --git a/LibMatrix/EventIdResponse.cs b/LibMatrix/EventIdResponse.cs index 4d715a4..4dca734 100644 --- a/LibMatrix/EventIdResponse.cs +++ b/LibMatrix/EventIdResponse.cs @@ -1,3 +1,4 @@ +#define BALLS using System.Text.Json.Serialization; namespace LibMatrix; diff --git a/LibMatrix/Extensions/EnumerableExtensions.cs b/LibMatrix/Extensions/EnumerableExtensions.cs index 42d9491..b5193ab 100644 --- a/LibMatrix/Extensions/EnumerableExtensions.cs +++ b/LibMatrix/Extensions/EnumerableExtensions.cs @@ -1,7 +1,7 @@ namespace LibMatrix.Extensions; public static class EnumerableExtensions { - public static void MergeStateEventLists(this IList oldState, IList newState) { + public static void MergeStateEventLists(this IList oldState, IList newState) { foreach (var stateEvent in newState) { var old = oldState.FirstOrDefault(x => x.Type == stateEvent.Type && x.StateKey == stateEvent.StateKey); if (old is null) { @@ -14,7 +14,7 @@ public static class EnumerableExtensions { } } - public static void MergeStateEventLists(this IList oldState, IList newState) { + public static void MergeStateEventLists(this IList oldState, IList newState) { foreach (var stateEvent in newState) { var old = oldState.FirstOrDefault(x => x.Type == stateEvent.Type && x.StateKey == stateEvent.StateKey); if (old is null) { diff --git a/LibMatrix/Extensions/JsonElementExtensions.cs b/LibMatrix/Extensions/JsonElementExtensions.cs index c4ed743..a2faddc 100644 --- a/LibMatrix/Extensions/JsonElementExtensions.cs +++ b/LibMatrix/Extensions/JsonElementExtensions.cs @@ -8,7 +8,7 @@ namespace LibMatrix.Extensions; public static class JsonElementExtensions { public static bool FindExtraJsonElementFields(this JsonElement obj, Type objectType, string objectPropertyName) { if (objectPropertyName == "content" && objectType == typeof(JsonObject)) - objectType = typeof(StateEventResponse); + objectType = typeof(LegacyMatrixEventResponse); // if (t == typeof(JsonNode)) // return false; @@ -35,9 +35,9 @@ public static class JsonElementExtensions { continue; } - if (field.Name == "content" && (objectType == typeof(StateEventResponse) || objectType == typeof(StateEvent))) { + if (field.Name == "content" && (objectType == typeof(LegacyMatrixEventResponse) || objectType == typeof(LegacyMatrixEvent))) { unknownPropertyFound |= field.FindExtraJsonPropertyFieldsByValueKind( - StateEvent.GetStateEventType(obj.GetProperty("type").GetString()!), // We expect type to always be present + LegacyMatrixEvent.GetStateEventType(obj.GetProperty("type").GetString()!), // We expect type to always be present mappedProperty.PropertyType); continue; } diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs index 1833bd0..5635955 100644 --- a/LibMatrix/Helpers/SyncHelper.cs +++ b/LibMatrix/Helpers/SyncHelper.cs @@ -204,12 +204,12 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg /// /// Event fired when a timeline event is received /// - public List> TimelineEventHandlers { get; } = new(); + public List> TimelineEventHandlers { get; } = new(); /// /// Event fired when an account data event is received /// - public List> AccountDataReceivedHandlers { get; } = new(); + public List> AccountDataReceivedHandlers { get; } = new(); private void Log(string message) { if (logger is null) Console.WriteLine(message); diff --git a/LibMatrix/Helpers/SyncStateResolver.cs b/LibMatrix/Helpers/SyncStateResolver.cs index 72d600d..b1f83ff 100644 --- a/LibMatrix/Helpers/SyncStateResolver.cs +++ b/LibMatrix/Helpers/SyncStateResolver.cs @@ -37,13 +37,13 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge oldState.NextBatch = newState.NextBatch ?? oldState.NextBatch; oldState.AccountData ??= new EventList(); - oldState.AccountData.Events ??= new List(); + oldState.AccountData.Events ??= new List(); if (newState.AccountData?.Events is not null) - oldState.AccountData.Events.MergeStateEventLists(newState.AccountData?.Events ?? new List()); + oldState.AccountData.Events.MergeStateEventLists(newState.AccountData?.Events ?? new List()); oldState.Presence ??= new SyncResponse.PresenceDataStructure(); if (newState.Presence?.Events is not null) - oldState.Presence.Events.MergeStateEventLists(newState.Presence?.Events ?? new List()); + oldState.Presence.Events.MergeStateEventLists(newState.Presence?.Events ?? new List()); oldState.DeviceOneTimeKeysCount ??= new Dictionary(); if (newState.DeviceOneTimeKeysCount is not null) @@ -55,9 +55,9 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge oldState.Rooms = MergeRoomsDataStructure(oldState.Rooms, newState.Rooms); oldState.ToDevice ??= new EventList(); - oldState.ToDevice.Events ??= new List(); + oldState.ToDevice.Events ??= new List(); if (newState.ToDevice?.Events is not null) - oldState.ToDevice.Events.MergeStateEventLists(newState.ToDevice?.Events ?? new List()); + oldState.ToDevice.Events.MergeStateEventLists(newState.ToDevice?.Events ?? new List()); oldState.DeviceLists ??= new SyncResponse.DeviceListsDataStructure(); if (newState.DeviceLists?.Changed is not null) @@ -97,22 +97,22 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge private SyncResponse.RoomsDataStructure.LeftRoomDataStructure MergeLeftRoomDataStructure(SyncResponse.RoomsDataStructure.LeftRoomDataStructure oldData, SyncResponse.RoomsDataStructure.LeftRoomDataStructure newData) { oldData.AccountData ??= new EventList(); - oldData.AccountData.Events ??= new List(); + oldData.AccountData.Events ??= new List(); oldData.Timeline ??= new SyncResponse.RoomsDataStructure.JoinedRoomDataStructure.TimelineDataStructure(); - oldData.Timeline.Events ??= new List(); + oldData.Timeline.Events ??= new List(); oldData.State ??= new EventList(); - oldData.State.Events ??= new List(); + oldData.State.Events ??= new List(); if (newData.AccountData?.Events is not null) - oldData.AccountData.Events.MergeStateEventLists(newData.AccountData?.Events ?? new List()); + oldData.AccountData.Events.MergeStateEventLists(newData.AccountData?.Events ?? new List()); if (newData.Timeline?.Events is not null) - oldData.Timeline.Events.MergeStateEventLists(newData.Timeline?.Events ?? new List()); + oldData.Timeline.Events.MergeStateEventLists(newData.Timeline?.Events ?? new List()); oldData.Timeline.Limited = newData.Timeline?.Limited ?? oldData.Timeline.Limited; oldData.Timeline.PrevBatch = newData.Timeline?.PrevBatch ?? oldData.Timeline.PrevBatch; if (newData.State?.Events is not null) - oldData.State.Events.MergeStateEventLists(newData.State?.Events ?? new List()); + oldData.State.Events.MergeStateEventLists(newData.State?.Events ?? new List()); return oldData; } @@ -120,9 +120,9 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge private SyncResponse.RoomsDataStructure.InvitedRoomDataStructure MergeInvitedRoomDataStructure(SyncResponse.RoomsDataStructure.InvitedRoomDataStructure oldData, SyncResponse.RoomsDataStructure.InvitedRoomDataStructure newData) { oldData.InviteState ??= new EventList(); - oldData.InviteState.Events ??= new List(); + oldData.InviteState.Events ??= new List(); if (newData.InviteState?.Events is not null) - oldData.InviteState.Events.MergeStateEventLists(newData.InviteState?.Events ?? new List()); + oldData.InviteState.Events.MergeStateEventLists(newData.InviteState?.Events ?? new List()); return oldData; } @@ -130,27 +130,27 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge private SyncResponse.RoomsDataStructure.JoinedRoomDataStructure MergeJoinedRoomDataStructure(SyncResponse.RoomsDataStructure.JoinedRoomDataStructure oldData, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure newData) { oldData.AccountData ??= new EventList(); - oldData.AccountData.Events ??= new List(); + oldData.AccountData.Events ??= new List(); oldData.Timeline ??= new SyncResponse.RoomsDataStructure.JoinedRoomDataStructure.TimelineDataStructure(); - oldData.Timeline.Events ??= new List(); + oldData.Timeline.Events ??= new List(); oldData.State ??= new EventList(); - oldData.State.Events ??= new List(); + oldData.State.Events ??= new List(); oldData.Ephemeral ??= new EventList(); - oldData.Ephemeral.Events ??= new List(); + oldData.Ephemeral.Events ??= new List(); if (newData.AccountData?.Events is not null) - oldData.AccountData.Events.MergeStateEventLists(newData.AccountData?.Events ?? new List()); + oldData.AccountData.Events.MergeStateEventLists(newData.AccountData?.Events ?? new List()); if (newData.Timeline?.Events is not null) - oldData.Timeline.Events.MergeStateEventLists(newData.Timeline?.Events ?? new List()); + oldData.Timeline.Events.MergeStateEventLists(newData.Timeline?.Events ?? new List()); oldData.Timeline.Limited = newData.Timeline?.Limited ?? oldData.Timeline.Limited; oldData.Timeline.PrevBatch = newData.Timeline?.PrevBatch ?? oldData.Timeline.PrevBatch; if (newData.State?.Events is not null) - oldData.State.Events.MergeStateEventLists(newData.State?.Events ?? new List()); + oldData.State.Events.MergeStateEventLists(newData.State?.Events ?? new List()); if (newData.Ephemeral?.Events is not null) - oldData.Ephemeral.Events.MergeStateEventLists(newData.Ephemeral?.Events ?? new List()); + oldData.Ephemeral.Events.MergeStateEventLists(newData.Ephemeral?.Events ?? new List()); oldData.UnreadNotifications ??= new SyncResponse.RoomsDataStructure.JoinedRoomDataStructure.UnreadNotificationsDataStructure(); oldData.UnreadNotifications.HighlightCount = newData.UnreadNotifications?.HighlightCount ?? oldData.UnreadNotifications.HighlightCount; diff --git a/LibMatrix/LegacyMatrixEvent.cs b/LibMatrix/LegacyMatrixEvent.cs new file mode 100644 index 0000000..1cfc879 --- /dev/null +++ b/LibMatrix/LegacyMatrixEvent.cs @@ -0,0 +1,225 @@ +using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using ArcaneLibs; +using ArcaneLibs.Attributes; +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes; +using LibMatrix.Extensions; + +namespace LibMatrix; + +public class LegacyMatrixEvent { + public static FrozenSet KnownStateEventTypes { get; } = new ClassCollector().ResolveFromAllAccessibleAssemblies().ToFrozenSet(); + + public static FrozenDictionary KnownStateEventTypesByName { get; } = KnownStateEventTypes.Aggregate( + new Dictionary(), + (dict, type) => { + var attrs = type.GetCustomAttributes(); + foreach (var attr in attrs) { + if (dict.TryGetValue(attr.EventName, out var existing)) + Console.WriteLine($"Duplicate event type '{attr.EventName}' registered for types '{existing.Name}' and '{type.Name}'"); + dict[attr.EventName] = type; + } + + return dict; + }).OrderBy(x => x.Key).ToFrozenDictionary(); + + public static Type GetStateEventType(string? type) => + string.IsNullOrWhiteSpace(type) ? typeof(UnknownEventContent) : KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownEventContent); + + [JsonIgnore] + public Type MappedType => GetStateEventType(Type); + + [JsonIgnore] + public bool IsLegacyType => MappedType.GetCustomAttributes().FirstOrDefault(x => x.EventName == Type)?.Legacy ?? false; + + [JsonIgnore] + public string FriendlyTypeName => MappedType.GetFriendlyNameOrNull() ?? Type; + + [JsonIgnore] + public string FriendlyTypeNamePlural => MappedType.GetFriendlyNamePluralOrNull() ?? Type; + + private static readonly JsonSerializerOptions TypedContentSerializerOptions = new() { + Converters = { + new JsonFloatStringConverter(), + new JsonDoubleStringConverter(), + new JsonDecimalStringConverter() + } + }; + + [JsonIgnore] + [SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")] + public EventContent? TypedContent { + get { + // if (Type == "m.receipt") { + // return null; + // } + try { + var mappedType = GetStateEventType(Type); + if (mappedType == typeof(UnknownEventContent)) + Console.WriteLine($"Warning: unknown event type '{Type}'"); + var deserialisedContent = (EventContent)RawContent.Deserialize(mappedType, TypedContentSerializerOptions)!; + return deserialisedContent; + } + catch (JsonException e) { + Console.WriteLine(e); + Console.WriteLine("Content:\n" + (RawContent?.ToJson() ?? "null")); + } + + return null; + } + set { + if (value is null) + RawContent?.Clear(); + else + RawContent = JsonSerializer.Deserialize(JsonSerializer.Serialize(value, value.GetType(), + new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); + } + } + + [JsonPropertyName("state_key")] + public string? StateKey { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("replaces_state")] + public string? ReplacesState { get; set; } + + private JsonObject? _rawContent; + + [JsonPropertyName("content")] + public JsonObject? RawContent { + get => _rawContent; + set => _rawContent = value; + } + + [JsonIgnore] + public string InternalSelfTypeName { + get { + var res = GetType().Name switch { + "StateEvent`1" => "StateEvent", + _ => GetType().Name + }; + return res; + } + } + + [JsonIgnore] + public string InternalContentTypeName => TypedContent?.GetType().Name ?? "null"; +} + +public class LegacyMatrixEventResponse : LegacyMatrixEvent { + [JsonPropertyName("origin_server_ts")] + public long? OriginServerTs { get; set; } + + [JsonPropertyName("room_id")] + public string? RoomId { get; set; } + + [JsonPropertyName("sender")] + public string? Sender { get; set; } + + [JsonPropertyName("unsigned")] + public UnsignedData? Unsigned { get; set; } + + [JsonPropertyName("event_id")] + public string? EventId { get; set; } + + public class UnsignedData { + [JsonPropertyName("age")] + public ulong? Age { get; set; } + + [JsonPropertyName("redacted_because")] + public object? RedactedBecause { get; set; } + + [JsonPropertyName("transaction_id")] + public string? TransactionId { get; set; } + + [JsonPropertyName("replaces_state")] + public string? ReplacesState { get; set; } + + [JsonPropertyName("prev_sender")] + public string? PrevSender { get; set; } + + [JsonPropertyName("prev_content")] + public JsonObject? PrevContent { get; set; } + } +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(ChunkedStateEventResponse))] +internal partial class ChunkedStateEventResponseSerializerContext : JsonSerializerContext; + +public class EventList { + public EventList() { } + + public EventList(List? events) { + Events = events; + } + + [JsonPropertyName("events")] + public List? Events { get; set; } = new(); +} + +public class ChunkedStateEventResponse { + [JsonPropertyName("chunk")] + public List? Chunk { get; set; } = new(); +} + +public class PaginatedChunkedStateEventResponse : ChunkedStateEventResponse { + [JsonPropertyName("start")] + public string? Start { get; set; } + + [JsonPropertyName("end")] + public string? End { get; set; } +} + +public class BatchedChunkedStateEventResponse : ChunkedStateEventResponse { + [JsonPropertyName("next_batch")] + public string? NextBatch { get; set; } + + [JsonPropertyName("prev_batch")] + public string? PrevBatch { get; set; } +} + +public class RecursedBatchedChunkedStateEventResponse : BatchedChunkedStateEventResponse { + [JsonPropertyName("recursion_depth")] + public int? RecursionDepth { get; set; } +} + +#region Unused code + +/* +public class StateEventContentPolymorphicTypeInfoResolver : DefaultJsonTypeInfoResolver +{ + public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) + { + JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); + + Type baseType = typeof(EventContent); + if (jsonTypeInfo.Type == baseType) { + jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions { + TypeDiscriminatorPropertyName = "type", + IgnoreUnrecognizedTypeDiscriminators = true, + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType, + + DerivedTypes = StateEvent.KnownStateEventTypesByName.Select(x => new JsonDerivedType(x.Value, x.Key)).ToList() + + // DerivedTypes = new ClassCollector() + // .ResolveFromAllAccessibleAssemblies() + // .SelectMany(t => t.GetCustomAttributes() + // .Select(a => new JsonDerivedType(t, attr.EventName)); + + }; + } + + return jsonTypeInfo; + } +} +*/ + +#endregion \ No newline at end of file diff --git a/LibMatrix/MessagesResponse.cs b/LibMatrix/MessagesResponse.cs index 526da74..97a6c78 100644 --- a/LibMatrix/MessagesResponse.cs +++ b/LibMatrix/MessagesResponse.cs @@ -10,8 +10,8 @@ public class MessagesResponse { public string? End { get; set; } [JsonPropertyName("chunk")] - public List Chunk { get; set; } = new(); + public List Chunk { get; set; } = new(); [JsonPropertyName("state")] - public List State { get; set; } = new(); + public List State { get; set; } = new(); } \ No newline at end of file diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs index 6f47183..f5218b9 100644 --- a/LibMatrix/Responses/CreateRoomRequest.cs +++ b/LibMatrix/Responses/CreateRoomRequest.cs @@ -29,7 +29,7 @@ public class CreateRoomRequest { // public string Preset { get; set; } = null!; [JsonPropertyName("initial_state")] - public List? InitialState { get; set; } + public List? InitialState { get; set; } /// /// One of: ["public", "private"] @@ -50,15 +50,15 @@ public class CreateRoomRequest { /// For use only when you can't use the CreationContent property /// - public StateEvent this[string eventType, string eventKey = ""] { + public LegacyMatrixEvent this[string eventType, string eventKey = ""] { get { var stateEvent = InitialState.FirstOrDefault(x => x.Type == eventType && x.StateKey == eventKey); if (stateEvent == null) - InitialState.Add(stateEvent = new StateEvent { + InitialState.Add(stateEvent = new LegacyMatrixEvent { Type = eventType, StateKey = eventKey, TypedContent = (EventContent)Activator.CreateInstance( - StateEvent.KnownStateEventTypes.FirstOrDefault(x => + LegacyMatrixEvent.KnownStateEventTypes.FirstOrDefault(x => x.GetCustomAttributes()? .Any(y => y.EventName == eventType) ?? false) ?? typeof(UnknownEventContent) )! @@ -118,7 +118,7 @@ public class CreateRoomRequest { } }, RoomAliasName = roomAliasName, - InitialState = new List() + InitialState = new List() }; return request; @@ -158,7 +158,7 @@ public class CreateRoomRequest { } }, RoomAliasName = roomAliasName, - InitialState = new List() + InitialState = new List() }; return request; diff --git a/LibMatrix/Responses/SyncResponse.cs b/LibMatrix/Responses/SyncResponse.cs index e4addb6..7a51afe 100644 --- a/LibMatrix/Responses/SyncResponse.cs +++ b/LibMatrix/Responses/SyncResponse.cs @@ -39,7 +39,7 @@ public class SyncResponse { // supporting classes public class PresenceDataStructure { [JsonPropertyName("events")] - public List Events { get; set; } = new(); + public List Events { get; set; } = new(); } public class RoomsDataStructure { @@ -85,13 +85,13 @@ public class SyncResponse { public class TimelineDataStructure { public TimelineDataStructure() { } - public TimelineDataStructure(List? events, bool? limited) { + public TimelineDataStructure(List? events, bool? limited) { Events = events; Limited = limited; } [JsonPropertyName("events")] - public List? Events { get; set; } + public List? Events { get; set; } [JsonPropertyName("prev_batch")] public string? PrevBatch { get; set; } diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs index f15327c..5d85abd 100644 --- a/LibMatrix/RoomTypes/GenericRoom.cs +++ b/LibMatrix/RoomTypes/GenericRoom.cs @@ -32,13 +32,13 @@ public class GenericRoom { public string RoomId { get; set; } - public async IAsyncEnumerable GetFullStateAsync() { - var result = Homeserver.ClientHttpClient.GetAsyncEnumerableFromJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state"); + public async IAsyncEnumerable GetFullStateAsync() { + var result = Homeserver.ClientHttpClient.GetAsyncEnumerableFromJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state"); await foreach (var resp in result) yield return resp; } - public Task> GetFullStateAsListAsync() => - Homeserver.ClientHttpClient.GetFromJsonAsync>($"/_matrix/client/v3/rooms/{RoomId}/state"); + public Task> GetFullStateAsListAsync() => + Homeserver.ClientHttpClient.GetFromJsonAsync>($"/_matrix/client/v3/rooms/{RoomId}/state"); public async Task GetStateAsync(string type, string stateKey = "") { if (string.IsNullOrEmpty(type)) throw new ArgumentNullException(nameof(type), "Event type must be specified"); @@ -68,7 +68,7 @@ public class GenericRoom { } } - public async Task GetStateEventAsync(string type, string stateKey = "") { + public async Task GetStateEventAsync(string type, string stateKey = "") { if (string.IsNullOrEmpty(type)) throw new ArgumentNullException(nameof(type), "Event type must be specified"); var url = $"/_matrix/client/v3/rooms/{RoomId}/state/{type}"; if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; @@ -81,7 +81,7 @@ public class GenericRoom { ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED }; // throw new InvalidDataException("Returned event type does not match requested type, or server does not support passing `format`."); - return resp.Deserialize(); + return resp.Deserialize(); } catch (MatrixException e) { // if (e is not { ErrorCodode: "M_NOT_FOUND" }) { @@ -132,7 +132,7 @@ public class GenericRoom { } } - public async Task GetStateEventOrNullAsync(string type, string stateKey = "") { + public async Task GetStateEventOrNullAsync(string type, string stateKey = "") { try { return await GetStateEventAsync(type, stateKey); } @@ -227,7 +227,7 @@ public class GenericRoom { return await res.Content.ReadFromJsonAsync() ?? throw new Exception("Failed to join room?"); } - public async IAsyncEnumerable GetMembersEnumerableAsync(bool joinedOnly = true) { + public async IAsyncEnumerable GetMembersEnumerableAsync(bool joinedOnly = true) { // var sw = Stopwatch.StartNew(); var res = await Homeserver.ClientHttpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members"); // if (sw.ElapsedMilliseconds > 1000) @@ -251,7 +251,7 @@ public class GenericRoom { // Console.WriteLine($"Members call iterated in {sw.GetElapsedAndRestart()}"); } - public async Task> GetMembersListAsync(bool joinedOnly = true) { + public async Task> GetMembersListAsync(bool joinedOnly = true) { // var sw = Stopwatch.StartNew(); var res = await Homeserver.ClientHttpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members"); // if (sw.ElapsedMilliseconds > 1000) @@ -265,7 +265,7 @@ public class GenericRoom { // if (sw.ElapsedMilliseconds > 100) // Console.WriteLine($"Members call deserialised in {sw.GetElapsedAndRestart()}"); // else sw.Restart(); - var members = new List(); + var members = new List(); foreach (var resp in result.Chunk) { if (resp?.Type != "m.room.member") continue; if (joinedOnly && resp.RawContent?["membership"]?.GetValue() != "join") continue; @@ -451,8 +451,8 @@ public class GenericRoom { } } - public Task GetEventAsync(string eventId) => - Homeserver.ClientHttpClient.GetFromJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}"); + public Task GetEventAsync(string eventId) => + Homeserver.ClientHttpClient.GetFromJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}"); public async Task RedactEventAsync(string eventToRedact, string reason) { var data = new { reason }; @@ -516,7 +516,7 @@ public class GenericRoom { #endregion - public async IAsyncEnumerable GetRelatedEventsAsync(string eventId, string? relationType = null, string? eventType = null, string? dir = "f", + public async IAsyncEnumerable GetRelatedEventsAsync(string eventId, string? relationType = null, string? eventType = null, string? dir = "f", string? from = null, int? chunkLimit = 100, bool? recurse = null, string? to = null) { var path = $"/_matrix/client/v1/rooms/{RoomId}/relations/{HttpUtility.UrlEncode(eventId)}"; if (!string.IsNullOrEmpty(relationType)) path += $"/{relationType}"; diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs deleted file mode 100644 index 81ee3fe..0000000 --- a/LibMatrix/StateEvent.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System.Collections.Frozen; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; -using ArcaneLibs; -using ArcaneLibs.Attributes; -using ArcaneLibs.Extensions; -using LibMatrix.EventTypes; -using LibMatrix.Extensions; - -namespace LibMatrix; - -public class StateEvent { - public static FrozenSet KnownStateEventTypes { get; } = new ClassCollector().ResolveFromAllAccessibleAssemblies().ToFrozenSet(); - - public static FrozenDictionary KnownStateEventTypesByName { get; } = KnownStateEventTypes.Aggregate( - new Dictionary(), - (dict, type) => { - var attrs = type.GetCustomAttributes(); - foreach (var attr in attrs) { - if (dict.TryGetValue(attr.EventName, out var existing)) - Console.WriteLine($"Duplicate event type '{attr.EventName}' registered for types '{existing.Name}' and '{type.Name}'"); - dict[attr.EventName] = type; - } - - return dict; - }).OrderBy(x => x.Key).ToFrozenDictionary(); - - public static Type GetStateEventType(string? type) => - string.IsNullOrWhiteSpace(type) ? typeof(UnknownEventContent) : KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownEventContent); - - [JsonIgnore] - public Type MappedType => GetStateEventType(Type); - - [JsonIgnore] - public bool IsLegacyType => MappedType.GetCustomAttributes().FirstOrDefault(x => x.EventName == Type)?.Legacy ?? false; - - [JsonIgnore] - public string FriendlyTypeName => MappedType.GetFriendlyNameOrNull() ?? Type; - - [JsonIgnore] - public string FriendlyTypeNamePlural => MappedType.GetFriendlyNamePluralOrNull() ?? Type; - - private static readonly JsonSerializerOptions TypedContentSerializerOptions = new() { - Converters = { - new JsonFloatStringConverter(), - new JsonDoubleStringConverter(), - new JsonDecimalStringConverter() - } - }; - - [JsonIgnore] - [SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")] - public EventContent? TypedContent { - get { - // if (Type == "m.receipt") { - // return null; - // } - try { - var mappedType = GetStateEventType(Type); - if (mappedType == typeof(UnknownEventContent)) - Console.WriteLine($"Warning: unknown event type '{Type}'"); - var deserialisedContent = (EventContent)RawContent.Deserialize(mappedType, TypedContentSerializerOptions)!; - return deserialisedContent; - } - catch (JsonException e) { - Console.WriteLine(e); - Console.WriteLine("Content:\n" + (RawContent?.ToJson() ?? "null")); - } - - return null; - } - set { - if (value is null) - RawContent?.Clear(); - else - RawContent = JsonSerializer.Deserialize(JsonSerializer.Serialize(value, value.GetType(), - new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); - } - } - - [JsonPropertyName("state_key")] - public string? StateKey { get; set; } - - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("replaces_state")] - public string? ReplacesState { get; set; } - - private JsonObject? _rawContent; - - [JsonPropertyName("content")] - public JsonObject? RawContent { - get => _rawContent; - set => _rawContent = value; - } - // - // [JsonIgnore] - // public new Type GetType { - // get { - // var type = GetStateEventType(Type); - // - // //special handling for some types - // // if (type == typeof(RoomEmotesEventContent)) { - // // RawContent["emote"] = RawContent["emote"]?.AsObject() ?? new JsonObject(); - // // } - // // - // // if (this is StateEventResponse stateEventResponse) { - // // if (type == null || type == typeof(object)) { - // // Console.WriteLine($"Warning: unknown event type '{Type}'!"); - // // Console.WriteLine(RawContent.ToJson()); - // // Directory.CreateDirectory($"unknown_state_events/{Type}"); - // // File.WriteAllText($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json", - // // RawContent.ToJson()); - // // Console.WriteLine($"Saved to unknown_state_events/{Type}/{stateEventResponse.EventId}.json"); - // // } - // // else if (RawContent is not null && RawContent.FindExtraJsonObjectFields(type)) { - // // Directory.CreateDirectory($"unknown_state_events/{Type}"); - // // File.WriteAllText($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json", - // // RawContent.ToJson()); - // // Console.WriteLine($"Saved to unknown_state_events/{Type}/{stateEventResponse.EventId}.json"); - // // } - // // } - // - // return type; - // } - // } - - //debug - [JsonIgnore] - public string InternalSelfTypeName { - get { - var res = GetType().Name switch { - "StateEvent`1" => "StateEvent", - _ => GetType().Name - }; - return res; - } - } - - [JsonIgnore] - public string InternalContentTypeName => TypedContent?.GetType().Name ?? "null"; -} - -public class StateEventResponse : StateEvent { - [JsonPropertyName("origin_server_ts")] - public long? OriginServerTs { get; set; } - - [JsonPropertyName("room_id")] - public string? RoomId { get; set; } - - [JsonPropertyName("sender")] - public string? Sender { get; set; } - - [JsonPropertyName("unsigned")] - public UnsignedData? Unsigned { get; set; } - - [JsonPropertyName("event_id")] - public string? EventId { get; set; } - - public class UnsignedData { - [JsonPropertyName("age")] - public ulong? Age { get; set; } - - [JsonPropertyName("redacted_because")] - public object? RedactedBecause { get; set; } - - [JsonPropertyName("transaction_id")] - public string? TransactionId { get; set; } - - [JsonPropertyName("replaces_state")] - public string? ReplacesState { get; set; } - - [JsonPropertyName("prev_sender")] - public string? PrevSender { get; set; } - - [JsonPropertyName("prev_content")] - public JsonObject? PrevContent { get; set; } - } -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(ChunkedStateEventResponse))] -internal partial class ChunkedStateEventResponseSerializerContext : JsonSerializerContext; - -public class EventList { - public EventList() { } - - public EventList(List? events) { - Events = events; - } - - [JsonPropertyName("events")] - public List? Events { get; set; } = new(); -} - -public class ChunkedStateEventResponse { - [JsonPropertyName("chunk")] - public List? Chunk { get; set; } = new(); -} - -public class PaginatedChunkedStateEventResponse : ChunkedStateEventResponse { - [JsonPropertyName("start")] - public string? Start { get; set; } - - [JsonPropertyName("end")] - public string? End { get; set; } -} - -public class BatchedChunkedStateEventResponse : ChunkedStateEventResponse { - [JsonPropertyName("next_batch")] - public string? NextBatch { get; set; } - - [JsonPropertyName("prev_batch")] - public string? PrevBatch { get; set; } -} - -public class RecursedBatchedChunkedStateEventResponse : BatchedChunkedStateEventResponse { - [JsonPropertyName("recursion_depth")] - public int? RecursionDepth { get; set; } -} - -#region Unused code - -/* -public class StateEventContentPolymorphicTypeInfoResolver : DefaultJsonTypeInfoResolver -{ - public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) - { - JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); - - Type baseType = typeof(EventContent); - if (jsonTypeInfo.Type == baseType) { - jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions { - TypeDiscriminatorPropertyName = "type", - IgnoreUnrecognizedTypeDiscriminators = true, - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType, - - DerivedTypes = StateEvent.KnownStateEventTypesByName.Select(x => new JsonDerivedType(x.Value, x.Key)).ToList() - - // DerivedTypes = new ClassCollector() - // .ResolveFromAllAccessibleAssemblies() - // .SelectMany(t => t.GetCustomAttributes() - // .Select(a => new JsonDerivedType(t, attr.EventName)); - - }; - } - - return jsonTypeInfo; - } -} -*/ - -#endregion \ No newline at end of file diff --git a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomAccountDataController.cs b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomAccountDataController.cs index bac803f..64d7353 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomAccountDataController.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomAccountDataController.cs @@ -38,7 +38,7 @@ public class RoomAccountDataController(ILogger logger if (!room.AccountData.ContainsKey(user.UserId)) room.AccountData[user.UserId] = new(); - room.AccountData[user.UserId].Add(new StateEventResponse() { + room.AccountData[user.UserId].Add(new LegacyMatrixEventResponse() { Type = "m.fully_read", StateKey = user.UserId, RawContent = new() { @@ -46,7 +46,7 @@ public class RoomAccountDataController(ILogger logger } }); - room.AccountData[user.UserId].Add(new StateEventResponse() { + room.AccountData[user.UserId].Add(new LegacyMatrixEventResponse() { Type = "m.read", StateKey = user.UserId, RawContent = new() { @@ -54,7 +54,7 @@ public class RoomAccountDataController(ILogger logger } }); - room.AccountData[user.UserId].Add(new StateEventResponse() { + room.AccountData[user.UserId].Add(new LegacyMatrixEventResponse() { Type = "m.read.private", StateKey = user.UserId, RawContent = new() { diff --git a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs index 7d735f7..6fbe696 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomMembersController.cs @@ -13,7 +13,7 @@ public class RoomMembersController( RoomStore roomStore, PaginationTokenResolverService paginationTokenResolver) : ControllerBase { [HttpGet("members")] - public async Task> GetMembers(string roomId, string? at = null, string? membership = null, string? not_membership = null) { + public async Task> GetMembers(string roomId, string? at = null, string? membership = null, string? not_membership = null) { var token = tokenService.GetAccessTokenOrNull(HttpContext); if (token == null) throw new MatrixException() { @@ -44,7 +44,7 @@ public class RoomMembersController( members = members.Where(x => (x.TypedContent as RoomMemberEventContent)?.Membership != not_membership).ToList(); if (at != null) { - StateEventResponse? evt = null; + LegacyMatrixEventResponse? evt = null; if (at.StartsWith('$')) evt = await paginationTokenResolver.ResolveTokenToEvent(at, room); diff --git a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomStateController.cs b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomStateController.cs index 74c70a3..dfe119f 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomStateController.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomStateController.cs @@ -10,7 +10,7 @@ namespace LibMatrix.HomeserverEmulator.Controllers.Rooms; [Route("/_matrix/client/{version}/rooms/{roomId}/state")] public class RoomStateController(ILogger logger, TokenService tokenService, UserStore userStore, RoomStore roomStore) : ControllerBase { [HttpGet("")] - public async Task> GetState(string roomId) { + public async Task> GetState(string roomId) { var token = tokenService.GetAccessTokenOrNull(HttpContext); if (token == null) throw new MatrixException() { @@ -103,7 +103,7 @@ public class RoomStateController(ILogger logger, TokenServi ErrorCode = "M_NOT_FOUND", Error = "Room not found" }; - var evt = room.SetStateInternal(new StateEvent() { Type = eventType, StateKey = stateKey, RawContent = request }.ToStateEvent(user, room)); + var evt = room.SetStateInternal(new LegacyMatrixEvent() { Type = eventType, StateKey = stateKey, RawContent = request }.ToStateEvent(user, room)); evt.Type = eventType; evt.StateKey = stateKey; return new EventIdResponse() { diff --git a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs index afd69d1..b545b3f 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomTimelineController.cs @@ -39,7 +39,7 @@ public class RoomTimelineController( Error = "User is not in the room" }; - var evt = new StateEvent() { + var evt = new LegacyMatrixEvent() { RawContent = content, Type = eventType }.ToStateEvent(user, room); @@ -98,7 +98,7 @@ public class RoomTimelineController( } [HttpGet("event/{eventId}")] - public async Task GetEvent(string roomId, string eventId) { + public async Task GetEvent(string roomId, string eventId) { var token = tokenService.GetAccessToken(HttpContext); var user = await userStore.GetUserByToken(token); @@ -221,7 +221,7 @@ public class RoomTimelineController( }; } - private async Task> GetRelationsInternal(string roomId, string eventId, string dir, string? from, int? limit, bool? recurse, string? to) { + private async Task> GetRelationsInternal(string roomId, string eventId, string dir, string? from, int? limit, bool? recurse, string? to) { var room = roomStore.GetRoomById(roomId); var evt = room.Timeline.SingleOrDefault(x => x.EventId == eventId); if (evt == null) @@ -249,7 +249,7 @@ public class RoomTimelineController( private void InternalSendMessage(RoomStore.Room room, RoomMessageEventContent content) { logger.LogInformation("Sending internal message: {content}", content.Body); - room.Timeline.Add(new StateEventResponse() { + room.Timeline.Add(new LegacyMatrixEventResponse() { Type = RoomMessageEventContent.EventId, TypedContent = content, Sender = $"@hse:{tokenService.GenerateServerName(HttpContext)}", @@ -259,7 +259,7 @@ public class RoomTimelineController( }); } - private async Task HandleHseCommand(StateEventResponse evt, RoomStore.Room room, UserStore.User user) { + private async Task HandleHseCommand(LegacyMatrixEventResponse evt, RoomStore.Room room, UserStore.User user) { try { var msgContent = evt.TypedContent as RoomMessageEventContent; var parts = msgContent.Body.Split('\n')[0].Split(" "); @@ -323,7 +323,7 @@ public class RoomTimelineController( if (Random.Shared.Next(100) > 75) { crq.CreationContent["type"] = "m.space"; foreach (var item in Random.Shared.GetItems(roomStore._rooms.ToArray(), 50)) { - crq.InitialState!.Add(new StateEvent() { + crq.InitialState!.Add(new LegacyMatrixEvent() { Type = "m.space.child", StateKey = item.RoomId, TypedContent = new SpaceChildEventContent() { @@ -358,7 +358,7 @@ public class RoomTimelineController( } } - private async Task HandleImportNhekoProfilesCommand(string[] args, StateEventResponse evt, RoomStore.Room room, UserStore.User user) { + private async Task HandleImportNhekoProfilesCommand(string[] args, LegacyMatrixEventResponse evt, RoomStore.Room room, UserStore.User user) { var msgContent = evt.TypedContent as RoomMessageEventContent; var parts = msgContent.Body.Split('\n'); @@ -396,7 +396,7 @@ public class RoomTimelineController( } } - private async Task HandleImportCommand(string[] args, StateEventResponse evt, RoomStore.Room room, UserStore.User user) { + private async Task HandleImportCommand(string[] args, LegacyMatrixEventResponse evt, RoomStore.Room room, UserStore.User user) { var roomId = args[0]; var profile = args[1]; diff --git a/Tests/LibMatrix.HomeserverEmulator/Extensions/EventExtensions.cs b/Tests/LibMatrix.HomeserverEmulator/Extensions/EventExtensions.cs index d938b1b..a4c6aa8 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Extensions/EventExtensions.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Extensions/EventExtensions.cs @@ -3,19 +3,19 @@ using LibMatrix.HomeserverEmulator.Services; namespace LibMatrix.HomeserverEmulator.Extensions; public static class EventExtensions { - public static StateEventResponse ToStateEvent(this StateEvent stateEvent, UserStore.User user, RoomStore.Room room) { - return new StateEventResponse { - RawContent = stateEvent.RawContent, + public static LegacyMatrixEventResponse ToStateEvent(this LegacyMatrixEvent legacyMatrixEvent, UserStore.User user, RoomStore.Room room) { + return new LegacyMatrixEventResponse { + RawContent = legacyMatrixEvent.RawContent, EventId = "$" + string.Join("", Random.Shared.GetItems("abcdefghijklmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789".ToCharArray(), 100)), RoomId = room.RoomId, Sender = user.UserId, - StateKey = stateEvent.StateKey, - Type = stateEvent.Type, + StateKey = legacyMatrixEvent.StateKey, + Type = legacyMatrixEvent.Type, OriginServerTs = DateTimeOffset.Now.ToUnixTimeMilliseconds() }; } - public static List GetCalculatedState(this IEnumerable events) { + public static List GetCalculatedState(this IEnumerable events) { return events.Where(s => s.StateKey != null) .OrderByDescending(s => s.OriginServerTs) .DistinctBy(x => (x.Type, x.StateKey)) diff --git a/Tests/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs b/Tests/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs index 0128ba6..6bc5a33 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Services/PaginationTokenResolverService.cs @@ -35,7 +35,7 @@ public class PaginationTokenResolverService(ILogger ResolveTokenToEvent(string token, RoomStore.Room room) { + public async Task 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); diff --git a/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs b/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs index 5cdc3ab..e0b6552 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Services/RoomStore.cs @@ -69,7 +69,7 @@ public class RoomStore { public Room CreateRoom(CreateRoomRequest request, UserStore.User? user = null) { var room = new Room(roomId: $"!{Guid.NewGuid().ToString()}"); - var newCreateEvent = new StateEvent() { + var newCreateEvent = new LegacyMatrixEvent() { Type = RoomCreateEventContent.EventId, RawContent = new() { } }; @@ -95,7 +95,7 @@ public class RoomStore { } if (!string.IsNullOrWhiteSpace(request.Name)) - room.SetStateInternal(new StateEvent() { + room.SetStateInternal(new LegacyMatrixEvent() { Type = RoomNameEventContent.EventId, TypedContent = new RoomNameEventContent() { Name = request.Name @@ -103,7 +103,7 @@ public class RoomStore { }); if (!string.IsNullOrWhiteSpace(request.RoomAliasName)) - room.SetStateInternal(new StateEvent() { + room.SetStateInternal(new LegacyMatrixEvent() { Type = RoomCanonicalAliasEventContent.EventId, TypedContent = new RoomCanonicalAliasEventContent() { Alias = $"#{request.RoomAliasName}:localhost" @@ -129,10 +129,10 @@ public class RoomStore { public class Room : NotifyPropertyChanged { private CancellationTokenSource _debounceCts = new(); - private ObservableCollection _timeline; - private ObservableDictionary> _accountData; + private ObservableCollection _timeline; + private ObservableDictionary> _accountData; private ObservableDictionary _readMarkers; - private FrozenSet _stateCache; + private FrozenSet _stateCache; private int _timelineHash; public Room(string roomId) { @@ -146,9 +146,9 @@ public class RoomStore { public string RoomId { get; set; } - public FrozenSet State => _timelineHash == _timeline.GetHashCode() ? _stateCache : RebuildState(); + public FrozenSet State => _timelineHash == _timeline.GetHashCode() ? _stateCache : RebuildState(); - public ObservableCollection Timeline { + public ObservableCollection Timeline { get => _timeline; set { if (Equals(value, _timeline)) return; @@ -171,7 +171,7 @@ public class RoomStore { } } - public ObservableDictionary> AccountData { + public ObservableDictionary> AccountData { get => _accountData; set { if (Equals(value, _accountData)) return; @@ -181,7 +181,7 @@ public class RoomStore { } } - public ImmutableList JoinedMembers => + public ImmutableList JoinedMembers => State.Where(s => s is { Type: RoomMemberEventContent.EventId, TypedContent: RoomMemberEventContent { Membership: "join" } }).ToImmutableList(); public ObservableDictionary ReadMarkers { @@ -194,8 +194,8 @@ public class RoomStore { } } - internal StateEventResponse SetStateInternal(StateEvent request, string? senderId = null, UserStore.User? user = null) { - var state = request as StateEventResponse ?? new StateEventResponse() { + internal LegacyMatrixEventResponse SetStateInternal(LegacyMatrixEvent request, string? senderId = null, UserStore.User? user = null) { + var state = request as LegacyMatrixEventResponse ?? new LegacyMatrixEventResponse() { Type = request.Type, StateKey = request.StateKey ?? "", EventId = "$" + Guid.NewGuid().ToString(), @@ -212,7 +212,7 @@ public class RoomStore { return state; } - public StateEventResponse AddUser(string userId) { + public LegacyMatrixEventResponse AddUser(string userId) { var state = SetStateInternal(new() { Type = RoomMemberEventContent.EventId, StateKey = userId, @@ -258,15 +258,15 @@ public class RoomStore { private SemaphoreSlim stateRebuildSemaphore = new(1, 1); - private FrozenSet RebuildState() { + private FrozenSet RebuildState() { stateRebuildSemaphore.Wait(); while (true) try { Console.WriteLine($"Rebuilding state for room {RoomId}"); // ReSharper disable once RedundantEnumerableCastCall - This sometimes happens when the collection is modified during enumeration - List? timeline = null; + List? timeline = null; lock (_timeline) { - timeline = Timeline.OfType().ToList(); + timeline = Timeline.OfType().ToList(); } foreach (var evt in timeline) { @@ -297,7 +297,7 @@ public class RoomStore { } } - public List GetRoomsByMember(string userId) { + public List GetRoomsByMember(string userId) { // return _rooms // // .Where(r => r.State.Any(s => s.Type == RoomMemberEventContent.EventId && s.StateKey == userId)) // .Select(r => (Room: r, MemberEvent: r.State.SingleOrDefault(s => s.Type == RoomMemberEventContent.EventId && s.StateKey == userId))) diff --git a/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs b/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs index 4ce9f92..7b2c07c 100644 --- a/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs +++ b/Tests/LibMatrix.HomeserverEmulator/Services/UserStore.cs @@ -71,13 +71,13 @@ public class UserStore { var user = new User() { UserId = userId, AccountData = new() { - new StateEventResponse() { + new LegacyMatrixEventResponse() { Type = "im.vector.analytics", RawContent = new JsonObject() { ["pseudonymousAnalyticsOptIn"] = false }, }, - new StateEventResponse() { + new LegacyMatrixEventResponse() { Type = "im.vector.web.settings", RawContent = new JsonObject() { ["developerMode"] = true @@ -116,7 +116,7 @@ public class UserStore { private ObservableDictionary _accessTokens; private ObservableDictionary _filters; private ObservableDictionary _profile; - private ObservableCollection _accountData; + private ObservableCollection _accountData; private ObservableDictionary _roomKeys; private ObservableDictionary _authorizedSessions; @@ -155,7 +155,7 @@ public class UserStore { } } - public ObservableCollection AccountData { + public ObservableCollection AccountData { get => _accountData; set { if (value == _accountData) return; diff --git a/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs b/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs index 2a380fc..4534ce6 100644 --- a/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs +++ b/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs @@ -14,29 +14,29 @@ public static class RoomAbstraction { // Visibility = CreateRoomVisibility.Public, RoomAliasName = Guid.NewGuid().ToString() }; - crq.InitialState ??= new List(); - crq.InitialState.Add(new StateEvent() { + crq.InitialState ??= new List(); + crq.InitialState.Add(new LegacyMatrixEvent() { Type = "m.room.topic", StateKey = "", TypedContent = new RoomTopicEventContent() { Topic = "LibMatrix Test Room " + DateTime.Now.ToString("O") } }); - crq.InitialState.Add(new StateEvent() { + crq.InitialState.Add(new LegacyMatrixEvent() { Type = "m.room.name", StateKey = "", TypedContent = new RoomNameEventContent() { Name = "LibMatrix Test Room " + DateTime.Now.ToString("O") } }); - crq.InitialState.Add(new StateEvent() { + crq.InitialState.Add(new LegacyMatrixEvent() { Type = "m.room.avatar", StateKey = "", TypedContent = new RoomAvatarEventContent() { Url = "mxc://conduit.rory.gay/r9KiT0f9eQbv8pv4RxwBZFuzhfKjGWHx" } }); - crq.InitialState.Add(new StateEvent() { + crq.InitialState.Add(new LegacyMatrixEvent() { Type = "m.room.aliases", StateKey = "", TypedContent = new RoomAliasEventContent() { @@ -60,7 +60,7 @@ public static class RoomAbstraction { Name = $"LibMatrix Test Space ({roomCount} children)", // Visibility = CreateRoomVisibility.Public, RoomAliasName = Guid.NewGuid().ToString(), - InitialState = new List() + InitialState = new List() }; crq.CreationContentBaseType.Type = "m.space"; @@ -72,7 +72,7 @@ public static class RoomAbstraction { })).ToAsyncEnumerable(); await foreach (var room in createRoomTasks) - crq.InitialState.Add(new StateEvent { + crq.InitialState.Add(new LegacyMatrixEvent { Type = "m.space.child", StateKey = room.RoomId, TypedContent = new SpaceChildEventContent() { @@ -85,7 +85,7 @@ public static class RoomAbstraction { if (addSpaces) for (var i = 0; i < roomCount; i++) { var space = await GetTestSpace(hs, roomCount - spaceSizeReduction, true, spaceSizeReduction); - crq.InitialState.Add(new StateEvent { + crq.InitialState.Add(new LegacyMatrixEvent { Type = "m.space.child", StateKey = space.RoomId, TypedContent = new SpaceChildEventContent() { diff --git a/Tests/LibMatrix.Tests/Tests/RoomTests.cs b/Tests/LibMatrix.Tests/Tests/RoomTests.cs index 4c8dcb4..f212af4 100644 --- a/Tests/LibMatrix.Tests/Tests/RoomTests.cs +++ b/Tests/LibMatrix.Tests/Tests/RoomTests.cs @@ -43,8 +43,8 @@ public class RoomTests : TestBed { [Fact] public async Task GetMembersAsync() { - Assert.True(StateEvent.KnownStateEventTypes is { Count: > 0 }, "StateEvent.KnownStateEventTypes is empty!"); - Assert.True(StateEvent.KnownStateEventTypesByName is { Count: > 0 }, "StateEvent.KnownStateEventTypesByName is empty!"); + Assert.True(LegacyMatrixEvent.KnownStateEventTypes is { Count: > 0 }, "StateEvent.KnownStateEventTypes is empty!"); + Assert.True(LegacyMatrixEvent.KnownStateEventTypesByName is { Count: > 0 }, "StateEvent.KnownStateEventTypesByName is empty!"); var hs = await HomeserverAbstraction.GetHomeserver(); var room = await RoomAbstraction.GetTestRoom(hs); diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs index c6abde2..541b720 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs @@ -6,7 +6,7 @@ namespace LibMatrix.Utilities.Bot.Interfaces; public class CommandContext { public required GenericRoom Room { get; set; } - public required StateEventResponse MessageEvent { get; set; } + public required LegacyMatrixEventResponse MessageEvent { get; set; } public string MessageContentWithoutReply => (MessageEvent.TypedContent as RoomMessageEventContent)! diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs index 601e598..b322362 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs @@ -90,7 +90,7 @@ public class CommandListenerHostedService : IHostedService { await _listenerTask.WaitAsync(cancellationToken); } - private async Task GetUsedPrefix(StateEventResponse evt) { + private async Task GetUsedPrefix(LegacyMatrixEventResponse evt) { var messageContent = evt.TypedContent as RoomMessageEventContent; var message = messageContent!.BodyWithoutReplyFallback; var prefix = _config.Prefixes.OrderByDescending(x => x.Length).FirstOrDefault(message.StartsWith); @@ -110,7 +110,7 @@ public class CommandListenerHostedService : IHostedService { return prefix; } - private async Task InvokeCommand(StateEventResponse evt, string usedPrefix) { + private async Task InvokeCommand(LegacyMatrixEventResponse evt, string usedPrefix) { var message = evt.TypedContent as RoomMessageEventContent; var room = _hs.GetRoom(evt.RoomId!); diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs index 7c5cc44..9d79e44 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs @@ -80,7 +80,7 @@ public class InviteHandlerHostedService : IHostedService { public class InviteEventArgs { public string RoomId { get; set; } - public StateEventResponse MemberEvent { get; set; } + public LegacyMatrixEventResponse MemberEvent { get; set; } public AuthenticatedHomeserverGeneric Homeserver { get; set; } } } \ No newline at end of file -- cgit 1.5.1