From 5495a35403d285007d67f503042720302efdf94c Mon Sep 17 00:00:00 2001 From: "Emma@Rory&" Date: Mon, 24 Jul 2023 20:56:42 +0200 Subject: Code cleanup --- MatrixRoomUtils.Core/Extensions/ClassCollector.cs | 10 +- .../Extensions/HttpClientExtensions.cs | 28 ++-- .../Extensions/JsonElementExtensions.cs | 154 ++++++++++++++++++--- MatrixRoomUtils.Core/Filters/SyncFilter.cs | 68 ++++----- .../Responses/CreateRoomRequest.cs | 2 +- .../Responses/StateEventResponse.cs | 20 +-- MatrixRoomUtils.Core/StateEvent.cs | 23 +-- MatrixRoomUtils.Core/StateEventStruct.cs | 12 -- .../StateEventTypes/Common/RoomEmotesEventData.cs | 16 +-- .../StateEventTypes/Spec/PowerLevelEventData.cs | 48 ------- .../StateEventTypes/Spec/RoomAvatarEventData.cs | 18 ++- .../StateEventTypes/Spec/RoomCreateEventData.cs | 14 +- .../Spec/RoomPowerLevelEventData.cs | 56 ++++++++ .../DefaultRoomCreationTemplate.cs | 4 +- MatrixRoomUtils.Web/Classes/RoomInfo.cs | 8 +- MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj | 1 - MatrixRoomUtils.Web/Pages/About.razor | 30 ---- .../Pages/RoomManager/RoomManagerSpace.razor | 96 ------------- .../Pages/RoomState/RoomStateEditorPage.razor | 145 ------------------- .../Pages/RoomState/RoomStateViewerPage.razor | 128 ----------------- MatrixRoomUtils.Web/Pages/Rooms/Index.razor | 72 ++++++---- MatrixRoomUtils.Web/Pages/Rooms/Space.razor | 96 +++++++++++++ MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor | 145 +++++++++++++++++++ MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor | 128 +++++++++++++++++ .../RoomListComponents/RoomListCategory.razor | 16 ++- .../Shared/RoomListComponents/RoomListSpace.razor | 18 ++- MatrixRoomUtils.Web/Shared/RoomListItem.razor | 55 ++++---- MatrixRoomUtils.Web/wwwroot/blobfox_outage.gif | Bin 0 -> 3574 bytes MatrixRoomUtils.Web/wwwroot/index.html | 2 - MatrixRoomUtils.sln.DotSettings.user | 4 +- README.MD | 12 ++ 31 files changed, 787 insertions(+), 642 deletions(-) delete mode 100644 MatrixRoomUtils.Core/StateEventStruct.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPowerLevelEventData.cs delete mode 100644 MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor delete mode 100644 MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor delete mode 100644 MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor create mode 100644 MatrixRoomUtils.Web/Pages/Rooms/Space.razor create mode 100644 MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor create mode 100644 MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor create mode 100644 MatrixRoomUtils.Web/wwwroot/blobfox_outage.gif diff --git a/MatrixRoomUtils.Core/Extensions/ClassCollector.cs b/MatrixRoomUtils.Core/Extensions/ClassCollector.cs index 9d3d3c0..d4ba838 100644 --- a/MatrixRoomUtils.Core/Extensions/ClassCollector.cs +++ b/MatrixRoomUtils.Core/Extensions/ClassCollector.cs @@ -19,12 +19,4 @@ public class ClassCollector where T : class { public List ResolveFromAssembly(Assembly assembly) => assembly.GetTypes() .Where(x => x is { IsClass: true, IsAbstract: false } && x.GetInterfaces().Contains(typeof(T))).ToList(); - // { - // List ret = new(); - // foreach (var x in assembly.GetTypes().Where(x => x is { IsClass: true, IsAbstract: false } && x.GetInterfaces().Contains(typeof(T))).ToList()) { - // // Console.WriteLine($"[!!] Found class {x.FullName}"); - // ret.Add(x); - // } - // return ret; - // } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs index 695e8e3..9ac9c6b 100644 --- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs @@ -19,40 +19,42 @@ public static class HttpClientExtensions { } public class MatrixHttpClient : HttpClient { - public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - Console.WriteLine($"Sending request to {request.RequestUri}"); - try - { - HttpRequestOptionsKey WebAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); + public override async Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) { + Console.WriteLine($"Sending request to {request.RequestUri}"); + try { + HttpRequestOptionsKey WebAssemblyEnableStreamingResponseKey = + new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); request.Options.Set(WebAssemblyEnableStreamingResponseKey, true); - // var asm = Assembly.Load("Microsoft.AspNetCore.Components.WebAssembly"); - // var browserHttpHandlerType = asm.GetType("Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions", true); - // var browserHttpHandlerMethod = browserHttpHandlerType.GetMethod("SetBrowserResponseStreamingEnabled", BindingFlags.Public | BindingFlags.Static); - // browserHttpHandlerMethod?.Invoke(null, new object[] {request, true}); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("Failed to set browser response streaming:"); Console.WriteLine(e); } + var a = await base.SendAsync(request, cancellationToken); if (!a.IsSuccessStatusCode) { var content = await a.Content.ReadAsStringAsync(cancellationToken); if (content.StartsWith('{')) { var ex = JsonSerializer.Deserialize(content); ex.RawContent = content; - Console.WriteLine($"Failed to send request: {ex}"); + Console.WriteLine($"Failed to send request: {ex}"); if (ex?.RetryAfterMs is not null) { await Task.Delay(ex.RetryAfterMs.Value, cancellationToken); - typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(request, 0); + typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance) + ?.SetValue(request, 0); return await SendAsync(request, cancellationToken); } + throw ex!; } + throw new InvalidDataException("Encountered invalid data:\n" + content); } + return a; } + // GetFromJsonAsync public async Task GetFromJsonAsync(string requestUri, CancellationToken cancellationToken = default) { var request = new HttpRequestMessage(HttpMethod.Get, requestUri); diff --git a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs index 36da644..7701c9e 100644 --- a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs @@ -3,37 +3,149 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.Extensions; public static class JsonElementExtensions { - public static bool FindExtraJsonElementFields([DisallowNull] this JsonElement? res, Type t) { - var props = t.GetProperties(); - var unknownPropertyFound = false; - foreach (var field in res.Value.EnumerateObject()) { - if (props.Any(x => x.GetCustomAttribute()?.Name == field.Name)) continue; - Console.WriteLine($"[!!] Unknown property {field.Name} in {t.Name}!"); + public static bool FindExtraJsonElementFields(this JsonElement obj, Type objectType, string objectPropertyName) { + if (objectPropertyName == "content" && objectType == typeof(JsonObject)) + objectType = typeof(StateEventResponse); + // if (t == typeof(JsonNode)) + // return false; + + Console.WriteLine($"{objectType.Name} {objectPropertyName}"); + bool unknownPropertyFound = false; + var mappedPropsDict = objectType.GetProperties() + .Where(x => x.GetCustomAttribute() is not null) + .ToDictionary(x => x.GetCustomAttribute()!.Name, x => x); + objectType.GetProperties().Where(x => !mappedPropsDict.ContainsKey(x.Name)) + .ToList().ForEach(x => mappedPropsDict.TryAdd(x.Name, x)); + + foreach (var field in obj.EnumerateObject()) { + if (mappedPropsDict.TryGetValue(field.Name, out var mappedProperty)) { + //dictionary + if (mappedProperty.PropertyType.IsGenericType && + mappedProperty.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { + unknownPropertyFound |= _checkDictionary(field, objectType, mappedProperty.PropertyType); + continue; + } + + if (mappedProperty.PropertyType.IsGenericType && + mappedProperty.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) { + unknownPropertyFound |= _checkList(field, objectType, mappedProperty.PropertyType); + continue; + } + + if (field.Name == "content" && (objectType == typeof(StateEventResponse) || objectType == typeof(StateEvent))) { + unknownPropertyFound |= field.FindExtraJsonPropertyFieldsByValueKind( + StateEvent.GetStateEventType(obj.GetProperty("type").GetString()), + mappedProperty.PropertyType); + continue; + } + + unknownPropertyFound |= + field.FindExtraJsonPropertyFieldsByValueKind(objectType, mappedProperty.PropertyType); + continue; + } + + Console.WriteLine($"[!!] Unknown property {field.Name} in {objectType.Name}!"); unknownPropertyFound = true; } - if (unknownPropertyFound) Console.WriteLine(res.Value.ToJson()); - return unknownPropertyFound; } - public static bool FindExtraJsonObjectFields([DisallowNull] this JsonObject? res, Type t) { - var props = t.GetProperties(); - var unknownPropertyFound = false; - foreach (var field in res) { - if (props.Any(x => x.GetCustomAttribute()?.Name == field.Key)) continue; - Console.WriteLine($"[!!] Unknown property {field.Key} in {t.Name}!"); - unknownPropertyFound = true; - // foreach (var propertyInfo in props) { - // Console.WriteLine($"[!!] Known property {propertyInfo.GetCustomAttribute()?.Name} in {t.Name}!"); - // } + + private static bool FindExtraJsonPropertyFieldsByValueKind(this JsonProperty field, Type containerType, + Type propertyType) { + if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + propertyType = propertyType.GetGenericArguments()[0]; } - if (unknownPropertyFound) Console.WriteLine(res.ToJson()); + bool switchResult = false; + switch (field.Value.ValueKind) { + case JsonValueKind.Array: + switchResult = field.Value.EnumerateArray().Aggregate(switchResult, + (current, element) => current | element.FindExtraJsonElementFields(propertyType, field.Name)); + break; + case JsonValueKind.Object: + switchResult |= field.Value.FindExtraJsonElementFields(propertyType, field.Name); + break; + case JsonValueKind.True: + case JsonValueKind.False: + return _checkBool(field, containerType, propertyType); + case JsonValueKind.String: + return _checkString(field, containerType, propertyType); + case JsonValueKind.Number: + return _checkNumber(field, containerType, propertyType); + case JsonValueKind.Undefined: + case JsonValueKind.Null: + break; + default: + throw new ArgumentOutOfRangeException(); + } - return unknownPropertyFound; + return switchResult; + } + + private static bool _checkBool(this JsonProperty field, Type containerType, Type propertyType) { + if (propertyType == typeof(bool)) return true; + Console.WriteLine( + $"[!!] Encountered bool for {field.Name} in {containerType.Name}, the class defines {propertyType.Name}!"); + return false; + } + + private static bool _checkString(this JsonProperty field, Type containerType, Type propertyType) { + if (propertyType == typeof(string)) return true; + // ReSharper disable once BuiltInTypeReferenceStyle + if (propertyType == typeof(String)) return true; + Console.WriteLine( + $"[!!] Encountered string for {field.Name} in {containerType.Name}, the class defines {propertyType.Name}!"); + return false; + } + + private static bool _checkNumber(this JsonProperty field, Type containerType, Type propertyType) { + if (propertyType == typeof(int) || + propertyType == typeof(double) || + propertyType == typeof(float) || + propertyType == typeof(decimal) || + propertyType == typeof(long) || + propertyType == typeof(short) || + propertyType == typeof(uint) || + propertyType == typeof(ulong) || + propertyType == typeof(ushort) || + propertyType == typeof(byte) || + propertyType == typeof(sbyte)) + return true; + Console.WriteLine( + $"[!!] Encountered number for {field.Name} in {containerType.Name}, the class defines {propertyType.Name}!"); + return false; + } + + private static bool _checkDictionary(this JsonProperty field, Type containerType, Type propertyType) { + var keyType = propertyType.GetGenericArguments()[0]; + var valueType = propertyType.GetGenericArguments()[1]; + valueType = Nullable.GetUnderlyingType(valueType) ?? valueType; + Console.WriteLine( + $"Encountered dictionary {field.Name} with key type {keyType.Name} and value type {valueType.Name}!"); + + return field.Value.EnumerateObject() + .Where(key => !valueType.IsPrimitive && valueType != typeof(string)) + .Aggregate(false, (current, key) => + current | key.FindExtraJsonPropertyFieldsByValueKind(containerType, valueType) + ); + } + + private static bool _checkList(this JsonProperty field, Type containerType, Type propertyType) { + var valueType = propertyType.GetGenericArguments()[0]; + valueType = Nullable.GetUnderlyingType(valueType) ?? valueType; + Console.WriteLine( + $"Encountered list {field.Name} with value type {valueType.Name}!"); + + return field.Value.EnumerateArray() + .Where(key => !valueType.IsPrimitive && valueType != typeof(string)) + .Aggregate(false, (current, key) => + current | key.FindExtraJsonElementFields(valueType, field.Name) + ); } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Core/Filters/SyncFilter.cs b/MatrixRoomUtils.Core/Filters/SyncFilter.cs index 7957898..bc81101 100644 --- a/MatrixRoomUtils.Core/Filters/SyncFilter.cs +++ b/MatrixRoomUtils.Core/Filters/SyncFilter.cs @@ -4,47 +4,50 @@ namespace MatrixRoomUtils.Core.Filters; public class SyncFilter { [JsonPropertyName("account_data")] - public AccountDataFilter? AccountData { get; set; } + public EventFilter? AccountData { get; set; } [JsonPropertyName("presence")] - public PresenceFilter? Presence { get; set; } + public EventFilter? Presence { get; set; } [JsonPropertyName("room")] public RoomFilter? Room { get; set; } -} -public class PresenceFilter { - [JsonPropertyName("not_types")] - public List? NotTypes { get; set; } -} + public class RoomFilter { + [JsonPropertyName("account_data")] + public StateFilter? AccountData { get; set; } -public class RoomFilter { - [JsonPropertyName("account_data")] - public AccountDataFilter? AccountData { get; set; } + [JsonPropertyName("ephemeral")] + public StateFilter? Ephemeral { get; set; } - [JsonPropertyName("ephemeral")] - public EphemeralFilter? Ephemeral { get; set; } + [JsonPropertyName("state")] + public StateFilter? State { get; set; } - public class EphemeralFilter { - [JsonPropertyName("not_types")] - public List? NotTypes { get; set; } - } + [JsonPropertyName("timeline")] + public StateFilter? Timeline { get; set; } - [JsonPropertyName("state")] - public StateFilter? State { get; set; } - public class StateFilter { - [JsonPropertyName("lazy_load_members")] - public bool? LazyLoadMembers { get; set; } + public class StateFilter : EventFilter { + [JsonPropertyName("contains_url")] + public bool? ContainsUrl { get; set; } - [JsonPropertyName("types")] - public List? Types { get; set; } - } + [JsonPropertyName("include_redundant_members")] + public bool? IncludeRedundantMembers { get; set; } + + [JsonPropertyName("lazy_load_members")] + public bool? LazyLoadMembers { get; set; } + + [JsonPropertyName("rooms")] + public List? Rooms { get; set; } + + [JsonPropertyName("not_rooms")] + public List? NotRooms { get; set; } - [JsonPropertyName("timeline")] - public TimelineFilter? Timeline { get; set; } + [JsonPropertyName("unread_thread_notifications")] + public bool? UnreadThreadNotifications { get; set; } + } + } - public class TimelineFilter { + public class EventFilter { [JsonPropertyName("limit")] public int? Limit { get; set; } @@ -53,10 +56,11 @@ public class RoomFilter { [JsonPropertyName("not_types")] public List? NotTypes { get; set; } + + [JsonPropertyName("senders")] + public List? Senders { get; set; } + + [JsonPropertyName("not_senders")] + public List? NotSenders { get; set; } } } - -public class AccountDataFilter { - [JsonPropertyName("not_types")] - public List? NotTypes { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs index be78a97..540a323 100644 --- a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs +++ b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs @@ -31,7 +31,7 @@ public class CreateRoomRequest { public string Visibility { get; set; } = null!; [JsonPropertyName("power_level_content_override")] - public PowerLevelEventData PowerLevelContentOverride { get; set; } = null!; + public RoomPowerLevelEventData PowerLevelContentOverride { get; set; } = null!; [JsonPropertyName("creation_content")] public JsonObject CreationContent { get; set; } = new(); diff --git a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs index 422a557..a7f9187 100644 --- a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs +++ b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Nodes; using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Interfaces; @@ -25,20 +26,23 @@ public class StateEventResponse : StateEvent { [JsonPropertyName("replaces_state")] public string ReplacesState { get; set; } - [JsonPropertyName("prev_content")] - public dynamic PrevContent { get; set; } - public class UnsignedData { [JsonPropertyName("age")] public ulong? Age { get; set; } - [JsonPropertyName("prev_content")] - public dynamic? PrevContent { get; set; } - [JsonPropertyName("redacted_because")] - public dynamic? RedactedBecause { get; set; } + 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; } } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs index 1c0a1cf..785c637 100644 --- a/MatrixRoomUtils.Core/StateEvent.cs +++ b/MatrixRoomUtils.Core/StateEvent.cs @@ -14,6 +14,17 @@ public class StateEvent { public static List KnownStateEventTypes = new ClassCollector().ResolveFromAllAccessibleAssemblies(); + public static Type GetStateEventType(string type) { + if (type == "m.receipt") { + return typeof(Dictionary); + } + + var eventType = KnownStateEventTypes.FirstOrDefault(x => + x.GetCustomAttributes()?.Any(y => y.EventName == type) ?? false); + + return eventType ?? typeof(object); + } + public object TypedContent { get { try { @@ -21,8 +32,9 @@ public class StateEvent { } catch (JsonException e) { Console.WriteLine(e); - Console.WriteLine("Content:\n"+ObjectExtensions.ToJson(RawContent)); + Console.WriteLine("Content:\n" + ObjectExtensions.ToJson(RawContent)); } + return null; } set => RawContent = JsonSerializer.Deserialize(JsonSerializer.Serialize(value)); @@ -65,12 +77,7 @@ public class StateEvent { [JsonIgnore] public Type GetType { get { - if (Type == "m.receipt") { - return typeof(Dictionary); - } - - var type = KnownStateEventTypes.FirstOrDefault(x => - x.GetCustomAttributes()?.Any(y => y.EventName == Type) ?? false); + var type = GetStateEventType(Type); //special handling for some types // if (type == typeof(RoomEmotesEventData)) { @@ -94,7 +101,7 @@ public class StateEvent { // } // } - return type ?? typeof(object); + return type; } } diff --git a/MatrixRoomUtils.Core/StateEventStruct.cs b/MatrixRoomUtils.Core/StateEventStruct.cs deleted file mode 100644 index cd301ac..0000000 --- a/MatrixRoomUtils.Core/StateEventStruct.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace MatrixRoomUtils.Core; - -public struct StateEventStruct { - public object content { get; set; } - public long origin_server_ts { get; set; } - public string sender { get; set; } - public string state_key { get; set; } - public string type { get; set; } - public string event_id { get; set; } - public string user_id { get; set; } - public string replaces_state { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs index 633998c..c263a3a 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs @@ -2,25 +2,25 @@ using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; -namespace MatrixRoomUtils.Core.StateEventTypes.Common; +namespace MatrixRoomUtils.Core.StateEventTypes.Common; [MatrixEvent(EventName = "im.ponies.room_emotes")] public class RoomEmotesEventData : IStateEventType { [JsonPropertyName("emoticons")] public Dictionary? Emoticons { get; set; } - + [JsonPropertyName("images")] public Dictionary? Images { get; set; } - + [JsonPropertyName("pack")] public PackInfo? Pack { get; set; } - + public class EmoticonData { [JsonPropertyName("url")] public string? Url { get; set; } } -} -public class PackInfo { - -} \ No newline at end of file + public class PackInfo { + + } +} diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEventData.cs deleted file mode 100644 index 6846db4..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEventData.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.power_levels")] -public class PowerLevelEventData : IStateEventType { - [JsonPropertyName("ban")] - public int Ban { get; set; } // = 50; - - [JsonPropertyName("events_default")] - public int EventsDefault { get; set; } // = 0; - - [JsonPropertyName("events")] - public Dictionary Events { get; set; } // = null!; - - [JsonPropertyName("invite")] - public int Invite { get; set; } // = 50; - - [JsonPropertyName("kick")] - public int Kick { get; set; } // = 50; - - [JsonPropertyName("notifications")] - public NotificationsPL NotificationsPl { get; set; } // = null!; - - [JsonPropertyName("redact")] - public int Redact { get; set; } // = 50; - - [JsonPropertyName("state_default")] - public int StateDefault { get; set; } // = 50; - - [JsonPropertyName("users")] - public Dictionary Users { get; set; } // = null!; - - [JsonPropertyName("users_default")] - public int UsersDefault { get; set; } // = 0; - - [Obsolete("Historical was a key related to MSC2716, a spec change on backfill that was dropped!", true)] - [JsonIgnore] - [JsonPropertyName("historical")] - public int Historical { get; set; } // = 50; - - public class NotificationsPL { - [JsonPropertyName("room")] - public int Room { get; set; } = 50; - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs index bab297b..a14e4c5 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs @@ -2,17 +2,27 @@ using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; -namespace MatrixRoomUtils.Core.StateEventTypes.Spec; +namespace MatrixRoomUtils.Core.StateEventTypes.Spec; [MatrixEvent(EventName = "m.room.avatar")] public class RoomAvatarEventData : IStateEventType { [JsonPropertyName("url")] public string? Url { get; set; } - + [JsonPropertyName("info")] public RoomAvatarInfo? Info { get; set; } public class RoomAvatarInfo { - + [JsonPropertyName("h")] + public int? Height { get; set; } + + [JsonPropertyName("w")] + public int? Width { get; set; } + + [JsonPropertyName("mimetype")] + public string? MimeType { get; set; } + + [JsonPropertyName("size")] + public int? Size { get; set; } } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs index 8b85d69..6127028 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; -namespace MatrixRoomUtils.Core.StateEventTypes.Spec; +namespace MatrixRoomUtils.Core.StateEventTypes.Spec; [MatrixEvent(EventName = "m.room.create")] public class RoomCreateEventData : IStateEventType { @@ -16,6 +16,12 @@ public class RoomCreateEventData : IStateEventType { public RoomCreatePredecessor? Predecessor { get; set; } [JsonPropertyName("type")] public string? Type { get; set; } - - public class RoomCreatePredecessor { } -} \ No newline at end of file + + public class RoomCreatePredecessor { + [JsonPropertyName("room_id")] + public string? RoomId { get; set; } + + [JsonPropertyName("event_id")] + public string? EventId { get; set; } + } +} diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPowerLevelEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPowerLevelEventData.cs new file mode 100644 index 0000000..1cde660 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPowerLevelEventData.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes.Spec; + +[MatrixEvent(EventName = "m.room.power_levels")] +public class RoomPowerLevelEventData : IStateEventType { + [JsonPropertyName("ban")] + public int Ban { get; set; } // = 50; + + [JsonPropertyName("events_default")] + public int EventsDefault { get; set; } // = 0; + + [JsonPropertyName("events")] + public Dictionary Events { get; set; } // = null!; + + [JsonPropertyName("invite")] + public int Invite { get; set; } // = 50; + + [JsonPropertyName("kick")] + public int Kick { get; set; } // = 50; + + [JsonPropertyName("notifications")] + public NotificationsPL NotificationsPl { get; set; } // = null!; + + [JsonPropertyName("redact")] + public int Redact { get; set; } // = 50; + + [JsonPropertyName("state_default")] + public int StateDefault { get; set; } // = 50; + + [JsonPropertyName("users")] + public Dictionary Users { get; set; } // = null!; + + [JsonPropertyName("users_default")] + public int UsersDefault { get; set; } // = 0; + + [Obsolete("Historical was a key related to MSC2716, a spec change on backfill that was dropped!", true)] + [JsonIgnore] + [JsonPropertyName("historical")] + public int Historical { get; set; } // = 50; + + public class NotificationsPL { + [JsonPropertyName("room")] + public int Room { get; set; } = 50; + } + + public bool IsUserAdmin(string userId) { + return Users.TryGetValue(userId, out var level) && level >= Events.Max(x=>x.Value); + } + + public bool UserHasPermission(string userId, string eventType) { + return Users.TryGetValue(userId, out var level) && level >= Events.GetValueOrDefault(eventType, EventsDefault); + } +} diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs index a519977..f895173 100644 --- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs +++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs @@ -48,7 +48,7 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { } }, Visibility = "public", - PowerLevelContentOverride = new PowerLevelEventData { + PowerLevelContentOverride = new RoomPowerLevelEventData { UsersDefault = 0, EventsDefault = 100, StateDefault = 50, @@ -56,7 +56,7 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { Redact = 50, Kick = 50, Ban = 50, - NotificationsPl = new PowerLevelEventData.NotificationsPL { + NotificationsPl = new RoomPowerLevelEventData.NotificationsPL { Room = 50 }, Events = new Dictionary { diff --git a/MatrixRoomUtils.Web/Classes/RoomInfo.cs b/MatrixRoomUtils.Web/Classes/RoomInfo.cs index 5ecc431..f9d5452 100644 --- a/MatrixRoomUtils.Web/Classes/RoomInfo.cs +++ b/MatrixRoomUtils.Web/Classes/RoomInfo.cs @@ -1,13 +1,14 @@ using MatrixRoomUtils.Core; +using MatrixRoomUtils.Core.Interfaces; using MatrixRoomUtils.Core.Responses; using MatrixRoomUtils.Core.RoomTypes; -namespace MatrixRoomUtils.Web.Classes; +namespace MatrixRoomUtils.Web.Classes; public class RoomInfo { public GenericRoom Room { get; set; } public List StateEvents { get; init; } = new(); - + public async Task GetStateEvent(string type, string stateKey = "") { var @event = StateEvents.FirstOrDefault(x => x.Type == type && x.StateKey == stateKey); if (@event is not null) return @event; @@ -23,7 +24,8 @@ public class RoomInfo { if (e is { ErrorCode: "M_NOT_FOUND" }) @event.TypedContent = default!; else throw; } + StateEvents.Add(@event); return @event; } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj index 13a120c..4b874c9 100644 --- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj +++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj @@ -11,7 +11,6 @@ - diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor index 971bd9b..48c7686 100644 --- a/MatrixRoomUtils.Web/Pages/About.razor +++ b/MatrixRoomUtils.Web/Pages/About.razor @@ -3,7 +3,6 @@ @using System.Net.Sockets @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage -@using XtermBlazor About @@ -22,10 +21,6 @@

This deployment also serves a copy of the compiled, hosting-ready binaries at /MRU-SRC.tar.xz!

} - - - - @code { private bool showBinDownload { get; set; } private bool showSrcDownload { get; set; } @@ -39,29 +34,4 @@ await base.OnInitializedAsync(); } - - private Xterm _terminal; - - private TerminalOptions _options = new TerminalOptions - { - CursorBlink = true, - CursorStyle = CursorStyle.Block, - Theme = - { - Background = "#17615e", - }, - }; - - private async Task OnFirstRender() { - var message = "Hello, World!\nThis is a terminal emulator!\n\nYou can type stuff here, and it will be sent to the server!\n\nThis is a test of the emergency broadcast system.\n\nThis is only a t"; - _terminal.Options.RendererType = RendererType.Dom; - _terminal.Options.ScreenReaderMode = true; -// TcpClient. - for (var i = 0; i < message.Length; i++) { - await _terminal.Write(message[i].ToString()); - - await Task.Delay(50); - _terminal.Options.Theme.Background = $"#{(i * 2):X6}"; - } - } } diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor deleted file mode 100644 index afa39b9..0000000 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor +++ /dev/null @@ -1,96 +0,0 @@ -@page "/RoomManager/Space/{RoomId}" -@using System.Text.Json -@using MatrixRoomUtils.Core.Responses -

Room manager - Viewing Space

- - -@foreach (var room in Rooms) { - -} - - -
-
- State list - @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) { -

@stateEvent.StateKey/@stateEvent.Type:

-
@stateEvent.RawContent.ToJson()
- } -
- -@code { - - [Parameter] - public string RoomId { get; set; } = "invalid!!!!!!"; - - private GenericRoom? Room { get; set; } - - private StateEventResponse[] States { get; set; } = Array.Empty(); - private List Rooms { get; } = new(); - private List ServersInSpace { get; } = new(); - - protected override async Task OnInitializedAsync() { - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; - - Room = await hs.GetRoom(RoomId.Replace('~', '.')); - - var state = Room.GetFullStateAsync(); - await foreach (var stateEvent in state) { - if (stateEvent.Type == "m.space.child") { - var roomId = stateEvent.StateKey; - var room = await hs.GetRoom(roomId); - if (room is not null) { - Rooms.Add(room); - } - } - else if (stateEvent.Type == "m.room.member") { - var serverName = stateEvent.StateKey.Split(':').Last(); - if (!ServersInSpace.Contains(serverName)) { - ServersInSpace.Add(serverName); - } - } - } - await base.OnInitializedAsync(); - - // var state = await Room.GetStateAsync(""); - // if (state is not null) { - // // Console.WriteLine(state.Value.ToJson()); - // States = state.Value.Deserialize()!; - // - // foreach (var stateEvent in States) { - // if (stateEvent.Type == "m.space.child") { - // // if (stateEvent.Content.ToJson().Length < 5) return; - // var roomId = stateEvent.StateKey; - // var room = await hs.GetRoom(roomId); - // if (room is not null) { - // Rooms.Add(room); - // } - // } - // else if (stateEvent.Type == "m.room.member") { - // var serverName = stateEvent.StateKey.Split(':').Last(); - // if (!ServersInSpace.Contains(serverName)) { - // ServersInSpace.Add(serverName); - // } - // } - // } - - // if(state.Value.TryGetProperty("Type", out var Type)) - // { - // } - // else - // { - // //this is fine, apprently... - // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!"); - // } - - // await base.OnInitializedAsync(); - } - - private async Task JoinAllRooms() { - foreach (var room in Rooms) { - room.JoinAsync(ServersInSpace.ToArray()); - } - } - -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor deleted file mode 100644 index 8b2ff0c..0000000 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor +++ /dev/null @@ -1,145 +0,0 @@ -@page "/Rooms/{RoomId}/State/Edit" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core.Responses -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -

Room state editor - Editing @RoomId

-
- -

@status

- - Show member events -
- - - @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).Distinct().OrderBy(x => x)) { - - Console.WriteLine(stateEvent); - } - -
- - - @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != shownStateKey).Select(x => x.Type).Distinct().OrderBy(x => x)) { - - } - -
- - - - - -@code { - //get room list - // - sync withroom list filter - // Type = support.feline.msc3784 - //support.feline.policy.lists.msc.v1 - - [Parameter] - public string? RoomId { get; set; } - - public List FilteredEvents { get; set; } = new(); - public List Events { get; set; } = new(); - public string status = ""; - - protected override async Task OnInitializedAsync() { - await base.OnInitializedAsync(); - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; - RoomId = RoomId.Replace('~', '.'); - await LoadStatesAsync(); - Console.WriteLine("Policy list editor initialized!"); - } - - private DateTime _lastUpdate = DateTime.Now; - - private async Task LoadStatesAsync() { - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - - var StateLoaded = 0; - var response = (await hs.GetRoom(RoomId)).GetFullStateAsync(); - await foreach (var _ev in response) { - // var e = new StateEventResponse { - // Type = _ev.Type, - // StateKey = _ev.StateKey, - // OriginServerTs = _ev.OriginServerTs, - // Content = _ev.Content - // }; - Events.Add(_ev); - if (string.IsNullOrEmpty(_ev.StateKey)) { - FilteredEvents.Add(_ev); - } - StateLoaded++; - if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) { - _lastUpdate = DateTime.Now; - status = $"Loaded {StateLoaded} state events"; - StateHasChanged(); - await Task.Delay(0); - } - } - - StateHasChanged(); - } - - private async Task RebuildFilteredData() { - status = "Rebuilding filtered data..."; - StateHasChanged(); - await Task.Delay(1); - var _FilteredEvents = Events; - if (!ShowMembershipEvents) - _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList(); - - status = "Done, rerendering!"; - StateHasChanged(); - await Task.Delay(1); - FilteredEvents = _FilteredEvents; - - if (_shownType is not null) - shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().RawContent.ToJson(indent: true, ignoreNull: true); - - StateHasChanged(); - } - - public struct PreRenderedStateEvent { - public string content { get; set; } - public long origin_server_ts { get; set; } - public string state_key { get; set; } - public string type { get; set; } - // public string Sender { get; set; } - // public string EventId { get; set; } - // public string UserId { get; set; } - // public string ReplacesState { get; set; } - } - - public bool ShowMembershipEvents { - get => _showMembershipEvents; - set { - _showMembershipEvents = value; - RebuildFilteredData(); - } - } - - private bool _showMembershipEvents; - private string _shownStateKey; - private string _shownType; - - private string shownStateKey { - get => _shownStateKey; - set { - _shownStateKey = value; - RebuildFilteredData(); - } - } - - private string shownType { - get => _shownType; - set { - _shownType = value; - RebuildFilteredData(); - } - } - - private string shownEventJson { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor deleted file mode 100644 index 09b38f0..0000000 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor +++ /dev/null @@ -1,128 +0,0 @@ -@page "/Rooms/{RoomId}/State/View" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core.Responses -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -

Room state viewer - Viewing @RoomId

-
- -

@status

- - Show member events - - - - - - - - - - @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey == "").OrderBy(x => x.OriginServerTs)) { - - - - - } - -
TypeContent
@stateEvent.Type -
@stateEvent.RawContent.ToJson()
-
- -@foreach (var group in FilteredEvents.GroupBy(x => x.StateKey).OrderBy(x => x.Key).Where(x => x.Key != "")) { -
- @group.Key - - - - - - - - - @foreach (var stateEvent in group.OrderBy(x => x.OriginServerTs)) { - - - - - } - -
TypeContent
@stateEvent.Type -
@stateEvent.RawContent.ToJson()
-
-
-} - - - -@code { - //get room list - // - sync withroom list filter - // Type = support.feline.msc3784 - //support.feline.policy.lists.msc.v1 - - [Parameter] - public string? RoomId { get; set; } - - public List FilteredEvents { get; set; } = new(); - public List Events { get; set; } = new(); - public string status = ""; - - protected override async Task OnInitializedAsync() { - await base.OnInitializedAsync(); - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; - await LoadStatesAsync(); - Console.WriteLine("Policy list editor initialized!"); - } - - private DateTime _lastUpdate = DateTime.Now; - - private async Task LoadStatesAsync() { - var StateLoaded = 0; - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; - var response = (await hs.GetRoom(RoomId)).GetFullStateAsync(); - await foreach (var _ev in response) { - Events.Add(_ev); - if (string.IsNullOrEmpty(_ev.StateKey)) { - FilteredEvents.Add(_ev); - } - StateLoaded++; - if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) { - _lastUpdate = DateTime.Now; - status = $"Loaded {StateLoaded} state events"; - StateHasChanged(); - await Task.Delay(0); - } - } - - StateHasChanged(); - } - - private async Task RebuildFilteredData() { - status = "Rebuilding filtered data..."; - StateHasChanged(); - await Task.Delay(1); - var _FilteredEvents = Events; - if (!ShowMembershipEvents) - _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList(); - - status = "Done, rerendering!"; - StateHasChanged(); - await Task.Delay(1); - FilteredEvents = _FilteredEvents; - StateHasChanged(); - } - - public bool ShowMembershipEvents { - get => _showMembershipEvents; - set { - _showMembershipEvents = value; - RebuildFilteredData(); - } - } - - private bool _showMembershipEvents; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor index 816299f..6d12dc2 100644 --- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor +++ b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor @@ -13,45 +13,54 @@ @code { + public List KnownRooms { get; set; } = new(); + private List Rooms { get; set; } = new(); private ProfileResponseEventData GlobalProfile { get; set; } - protected override async Task OnInitializedAsync() { - var hs = await MRUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; - GlobalProfile = await hs.GetProfile(hs.WhoAmI.UserId); - var filter = new SyncFilter() { + private SyncFilter filter = new() { + AccountData = new() { + NotTypes = new() { "*" }, + Limit = 1 + }, + Presence = new() { + NotTypes = new() { "*" }, + Limit = 1 + }, + Room = new() { AccountData = new() { - NotTypes = new() { "*" } + NotTypes = new() { "*" }, + Limit = 1 }, - Presence = new() { - NotTypes = new() { "*" } + Ephemeral = new() { + NotTypes = new() { "*" }, + Limit = 1 }, - Room = new RoomFilter() { - AccountData = new() { - NotTypes = new() { "*" } - }, - Ephemeral = new() { - NotTypes = new() { "*" } - }, - State = new RoomFilter.StateFilter() { - Types = new List() { - "m.room.name", - "m.room.avatar", - "m.room.create", - "org.matrix.mjolnir.shortcode", - } - }, - Timeline = new() { - NotTypes = new() { "*" }, - Limit = 1 + State = new() { + Types = new List() { + "m.room.name", + "m.room.avatar", + "m.room.create", + "org.matrix.mjolnir.shortcode", + "m.room.power_levels" } + }, + Timeline = new() { + NotTypes = new() { "*" }, + Limit = 1 } - }; + } + }; + + protected override async Task OnInitializedAsync() { + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + GlobalProfile = await hs.GetProfile(hs.WhoAmI.UserId); + Status = "Syncing..."; SyncResult? sync = null; string? nextBatch = null; - while (sync is null or { Rooms.Join.Count: > 10}) { + while (sync is null or { Rooms.Join.Count: >= 1}) { sync = await hs.SyncHelper.Sync(since: nextBatch, filter: filter, timeout: 0); nextBatch = sync?.NextBatch ?? nextBatch; if (sync is null) continue; @@ -70,11 +79,13 @@ StateEvents = new() }; Rooms.Add(room); + KnownRooms.Add(room); } room.StateEvents.AddRange(roomData.State.Events); } - Status = $"Got {Rooms.Count} rooms so far!"; + Status = $"Got {Rooms.Count} rooms so far! Next batch: {nextBatch}"; StateHasChanged(); + await Task.Delay(100); } Console.WriteLine("Sync done!"); Status = "Sync complete!"; @@ -103,8 +114,10 @@ } Console.WriteLine("Set stub data!"); Status = "Set stub data!"; + SemaphoreSlim semaphore = new(8, 8); var memberTasks = Rooms.Select(async roomInfo => { if (!roomInfo.StateEvents.Any(x => x.Type == "m.room.member" && x.StateKey == hs.WhoAmI.UserId)) { + await semaphore.WaitAsync(); roomInfo.StateEvents.Add(new StateEventResponse() { Type = "m.room.member", StateKey = hs.WhoAmI.UserId, @@ -112,6 +125,7 @@ Membership = "unknown" } }); + semaphore.Release(); } }).ToList(); await Task.WhenAll(memberTasks); diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Space.razor b/MatrixRoomUtils.Web/Pages/Rooms/Space.razor new file mode 100644 index 0000000..91f97d0 --- /dev/null +++ b/MatrixRoomUtils.Web/Pages/Rooms/Space.razor @@ -0,0 +1,96 @@ +@page "/Rooms/{RoomId}/Space" +@using System.Text.Json +@using MatrixRoomUtils.Core.Responses +

Room manager - Viewing Space

+ + +@foreach (var room in Rooms) { + +} + + +
+
+ State list + @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) { +

@stateEvent.StateKey/@stateEvent.Type:

+
@stateEvent.RawContent.ToJson()
+ } +
+ +@code { + + [Parameter] + public string RoomId { get; set; } = "invalid!!!!!!"; + + private GenericRoom? Room { get; set; } + + private StateEventResponse[] States { get; set; } = Array.Empty(); + private List Rooms { get; } = new(); + private List ServersInSpace { get; } = new(); + + protected override async Task OnInitializedAsync() { + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + + Room = await hs.GetRoom(RoomId.Replace('~', '.')); + + var state = Room.GetFullStateAsync(); + await foreach (var stateEvent in state) { + if (stateEvent.Type == "m.space.child") { + var roomId = stateEvent.StateKey; + var room = await hs.GetRoom(roomId); + if (room is not null) { + Rooms.Add(room); + } + } + else if (stateEvent.Type == "m.room.member") { + var serverName = stateEvent.StateKey.Split(':').Last(); + if (!ServersInSpace.Contains(serverName)) { + ServersInSpace.Add(serverName); + } + } + } + await base.OnInitializedAsync(); + + // var state = await Room.GetStateAsync(""); + // if (state is not null) { + // // Console.WriteLine(state.Value.ToJson()); + // States = state.Value.Deserialize()!; + // + // foreach (var stateEvent in States) { + // if (stateEvent.Type == "m.space.child") { + // // if (stateEvent.Content.ToJson().Length < 5) return; + // var roomId = stateEvent.StateKey; + // var room = await hs.GetRoom(roomId); + // if (room is not null) { + // Rooms.Add(room); + // } + // } + // else if (stateEvent.Type == "m.room.member") { + // var serverName = stateEvent.StateKey.Split(':').Last(); + // if (!ServersInSpace.Contains(serverName)) { + // ServersInSpace.Add(serverName); + // } + // } + // } + + // if(state.Value.TryGetProperty("Type", out var Type)) + // { + // } + // else + // { + // //this is fine, apprently... + // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!"); + // } + + // await base.OnInitializedAsync(); + } + + private async Task JoinAllRooms() { + foreach (var room in Rooms) { + room.JoinAsync(ServersInSpace.ToArray()); + } + } + +} diff --git a/MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor b/MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor new file mode 100644 index 0000000..8b2ff0c --- /dev/null +++ b/MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor @@ -0,0 +1,145 @@ +@page "/Rooms/{RoomId}/State/Edit" +@using System.Net.Http.Headers +@using System.Text.Json +@using MatrixRoomUtils.Core.Responses +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +

Room state editor - Editing @RoomId

+
+ +

@status

+ + Show member events +
+ + + @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).Distinct().OrderBy(x => x)) { + + Console.WriteLine(stateEvent); + } + +
+ + + @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != shownStateKey).Select(x => x.Type).Distinct().OrderBy(x => x)) { + + } + +
+ + + + + +@code { + //get room list + // - sync withroom list filter + // Type = support.feline.msc3784 + //support.feline.policy.lists.msc.v1 + + [Parameter] + public string? RoomId { get; set; } + + public List FilteredEvents { get; set; } = new(); + public List Events { get; set; } = new(); + public string status = ""; + + protected override async Task OnInitializedAsync() { + await base.OnInitializedAsync(); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + RoomId = RoomId.Replace('~', '.'); + await LoadStatesAsync(); + Console.WriteLine("Policy list editor initialized!"); + } + + private DateTime _lastUpdate = DateTime.Now; + + private async Task LoadStatesAsync() { + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + + var StateLoaded = 0; + var response = (await hs.GetRoom(RoomId)).GetFullStateAsync(); + await foreach (var _ev in response) { + // var e = new StateEventResponse { + // Type = _ev.Type, + // StateKey = _ev.StateKey, + // OriginServerTs = _ev.OriginServerTs, + // Content = _ev.Content + // }; + Events.Add(_ev); + if (string.IsNullOrEmpty(_ev.StateKey)) { + FilteredEvents.Add(_ev); + } + StateLoaded++; + if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) { + _lastUpdate = DateTime.Now; + status = $"Loaded {StateLoaded} state events"; + StateHasChanged(); + await Task.Delay(0); + } + } + + StateHasChanged(); + } + + private async Task RebuildFilteredData() { + status = "Rebuilding filtered data..."; + StateHasChanged(); + await Task.Delay(1); + var _FilteredEvents = Events; + if (!ShowMembershipEvents) + _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList(); + + status = "Done, rerendering!"; + StateHasChanged(); + await Task.Delay(1); + FilteredEvents = _FilteredEvents; + + if (_shownType is not null) + shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().RawContent.ToJson(indent: true, ignoreNull: true); + + StateHasChanged(); + } + + public struct PreRenderedStateEvent { + public string content { get; set; } + public long origin_server_ts { get; set; } + public string state_key { get; set; } + public string type { get; set; } + // public string Sender { get; set; } + // public string EventId { get; set; } + // public string UserId { get; set; } + // public string ReplacesState { get; set; } + } + + public bool ShowMembershipEvents { + get => _showMembershipEvents; + set { + _showMembershipEvents = value; + RebuildFilteredData(); + } + } + + private bool _showMembershipEvents; + private string _shownStateKey; + private string _shownType; + + private string shownStateKey { + get => _shownStateKey; + set { + _shownStateKey = value; + RebuildFilteredData(); + } + } + + private string shownType { + get => _shownType; + set { + _shownType = value; + RebuildFilteredData(); + } + } + + private string shownEventJson { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor b/MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor new file mode 100644 index 0000000..09b38f0 --- /dev/null +++ b/MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor @@ -0,0 +1,128 @@ +@page "/Rooms/{RoomId}/State/View" +@using System.Net.Http.Headers +@using System.Text.Json +@using MatrixRoomUtils.Core.Responses +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +

Room state viewer - Viewing @RoomId

+
+ +

@status

+ + Show member events + + + + + + + + + + @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey == "").OrderBy(x => x.OriginServerTs)) { + + + + + } + +
TypeContent
@stateEvent.Type +
@stateEvent.RawContent.ToJson()
+
+ +@foreach (var group in FilteredEvents.GroupBy(x => x.StateKey).OrderBy(x => x.Key).Where(x => x.Key != "")) { +
+ @group.Key + + + + + + + + + @foreach (var stateEvent in group.OrderBy(x => x.OriginServerTs)) { + + + + + } + +
TypeContent
@stateEvent.Type +
@stateEvent.RawContent.ToJson()
+
+
+} + + + +@code { + //get room list + // - sync withroom list filter + // Type = support.feline.msc3784 + //support.feline.policy.lists.msc.v1 + + [Parameter] + public string? RoomId { get; set; } + + public List FilteredEvents { get; set; } = new(); + public List Events { get; set; } = new(); + public string status = ""; + + protected override async Task OnInitializedAsync() { + await base.OnInitializedAsync(); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + await LoadStatesAsync(); + Console.WriteLine("Policy list editor initialized!"); + } + + private DateTime _lastUpdate = DateTime.Now; + + private async Task LoadStatesAsync() { + var StateLoaded = 0; + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + var response = (await hs.GetRoom(RoomId)).GetFullStateAsync(); + await foreach (var _ev in response) { + Events.Add(_ev); + if (string.IsNullOrEmpty(_ev.StateKey)) { + FilteredEvents.Add(_ev); + } + StateLoaded++; + if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) { + _lastUpdate = DateTime.Now; + status = $"Loaded {StateLoaded} state events"; + StateHasChanged(); + await Task.Delay(0); + } + } + + StateHasChanged(); + } + + private async Task RebuildFilteredData() { + status = "Rebuilding filtered data..."; + StateHasChanged(); + await Task.Delay(1); + var _FilteredEvents = Events; + if (!ShowMembershipEvents) + _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList(); + + status = "Done, rerendering!"; + StateHasChanged(); + await Task.Delay(1); + FilteredEvents = _FilteredEvents; + StateHasChanged(); + } + + public bool ShowMembershipEvents { + get => _showMembershipEvents; + set { + _showMembershipEvents = value; + RebuildFilteredData(); + } + } + + private bool _showMembershipEvents; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor index 709f2d7..b798d49 100644 --- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor +++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor @@ -6,13 +6,14 @@ @foreach (var room in rooms) {
- @if (RoomVersionDangerLevel(room) != 0) { - Upgrade room - } + @* @if (RoomVersionDangerLevel(room) != 0 && *@ + @* (room.StateEvents.FirstOrDefault(x=>x.Type == "m.room.power_levels")?.TypedContent is RoomPowerLevelEventData powerLevels && powerLevels.UserHasPermission(HomeServer.UserId, "m.room.tombstone"))) { *@ + @* Upgrade room *@ + @* } *@ View timeline View state Edit state - + @if (roomType == "Space") { } @@ -25,10 +26,13 @@ [Parameter] public KeyValuePair> Category { get; set; } - + [Parameter] public ProfileResponseEventData? GlobalProfile { get; set; } + [CascadingParameter] + public AuthenticatedHomeServer HomeServer { get; set; } = null!; + private string roomType => Category.Key; private List rooms => Category.Value; @@ -42,4 +46,4 @@ return 0; } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor index 5153658..a113f0b 100644 --- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor +++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor @@ -1,4 +1,4 @@ -Manage space +Manage space
@@ -16,6 +16,9 @@ [Parameter] public RoomInfo Space { get; set; } + [Parameter, CascadingParameter] + public List KnownRooms { get; set; } = new(); + [Parameter, CascadingParameter] public string? Breadcrumbs { get => _breadcrumbs + Space.Room.RoomId; @@ -30,9 +33,14 @@ var rooms = Space.Room.AsSpace.GetRoomsAsync(); await foreach (var room in rooms) { if (Breadcrumbs.Contains(room.RoomId)) continue; - Children.Add(new() { - Room = room - }); + RoomInfo roomInfo = KnownRooms.FirstOrDefault(x => x.Room.RoomId == room.RoomId); + if (roomInfo is null) { + roomInfo = new() { + Room = room + }; + KnownRooms.Add(roomInfo); + } + Children.Add(roomInfo); } await base.OnInitializedAsync(); } @@ -46,4 +54,4 @@ Console.WriteLine($"[RoomList] Rendering children of {Space.Room.RoomId}"); } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor index b89fb18..e12f622 100644 --- a/MatrixRoomUtils.Web/Shared/RoomListItem.razor +++ b/MatrixRoomUtils.Web/Shared/RoomListItem.razor @@ -32,7 +32,7 @@ [Parameter] public GenericRoom? Room { get; set; } - + [Parameter] public RoomInfo? RoomInfo { get; set; } @@ -69,20 +69,32 @@ if (Room is null && RoomId is null && RoomInfo is null) { throw new ArgumentNullException(nameof(RoomId)); } - + // sweep from roominfo to id if (RoomInfo is not null) Room = RoomInfo.Room; if(Room is not null) RoomId = Room.RoomId; - + //sweep from id to roominfo if(RoomId is not null) Room ??= await hs.GetRoom(RoomId); if(Room is not null) RoomInfo ??= new RoomInfo() { Room = Room }; - await CheckRoomVersion(); - await GetRoomInfo(); - await LoadOwnProfile(); + try { + await CheckRoomVersion(); + await GetRoomInfo(); + await LoadOwnProfile(); + } + catch (MatrixException e) { + if (e is not { ErrorCode: "M_FORBIDDEN" }) { + throw; + } + roomName = "Error: " + e.Message; + roomIcon = "/blobfox_outage.gif"; + } + catch (Exception e) { + Console.WriteLine($"Failed to load room info for {RoomId}: {e.Message}"); + } _semaphoreSlim.Release(); } @@ -104,24 +116,17 @@ } private async Task CheckRoomVersion() { - try { - var ce = (await RoomInfo.GetStateEvent("m.room.create")).TypedContent as RoomCreateEventData; - if (int.TryParse(ce.RoomVersion, out var rv)) { - if (rv < 10) - hasOldRoomVersion = true; - } - else // treat unstable room versions as dangerous - hasDangerousRoomVersion = true; - - if (RoomConstants.DangerousRoomVersions.Contains(ce.RoomVersion)) { - hasDangerousRoomVersion = true; - roomName = "Dangerous room: " + roomName; - } + var ce = (await RoomInfo.GetStateEvent("m.room.create")).TypedContent as RoomCreateEventData; + if (int.TryParse(ce.RoomVersion, out var rv)) { + if (rv < 10) + hasOldRoomVersion = true; } - catch (MatrixException e) { - if (e is not { ErrorCode: "M_FORBIDDEN" }) { - throw; - } + else // treat unstable room versions as dangerous + hasDangerousRoomVersion = true; + + if (RoomConstants.DangerousRoomVersions.Contains(ce.RoomVersion)) { + hasDangerousRoomVersion = true; + roomName = "Dangerous room: " + roomName; } } @@ -132,7 +137,7 @@ var state = (await RoomInfo.GetStateEvent("m.room.avatar")).TypedContent as RoomAvatarEventData; if (state?.Url is { } url) { roomIcon = MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, url); - Console.WriteLine($"Got avatar for room {RoomId}: {roomIcon} ({url})"); + // Console.WriteLine($"Got avatar for room {RoomId}: {roomIcon} ({url})"); } } catch (MatrixException e) { @@ -142,4 +147,4 @@ } } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Web/wwwroot/blobfox_outage.gif b/MatrixRoomUtils.Web/wwwroot/blobfox_outage.gif new file mode 100644 index 0000000..6f1e2ae Binary files /dev/null and b/MatrixRoomUtils.Web/wwwroot/blobfox_outage.gif differ diff --git a/MatrixRoomUtils.Web/wwwroot/index.html b/MatrixRoomUtils.Web/wwwroot/index.html index 9a85530..0439e62 100644 --- a/MatrixRoomUtils.Web/wwwroot/index.html +++ b/MatrixRoomUtils.Web/wwwroot/index.html @@ -10,8 +10,6 @@ - - diff --git a/MatrixRoomUtils.sln.DotSettings.user b/MatrixRoomUtils.sln.DotSettings.user index 785af7c..8e084e1 100644 --- a/MatrixRoomUtils.sln.DotSettings.user +++ b/MatrixRoomUtils.sln.DotSettings.user @@ -6,8 +6,8 @@ 12 True - True - True + + diff --git a/README.MD b/README.MD index 7bf6942..4c0a94b 100644 --- a/README.MD +++ b/README.MD @@ -11,3 +11,15 @@ git format-patch --output-directory "./patches" @{u}.. # Send patches ``` + +### Developer utility commands + +Error reporting upon file save: +```sh +inotifywait -rmqe CLOSE_WRITE --include '.*\.cs$' . | while read l; do clear; dotnet build --property WarningLevel=0; done +``` + +Hot rebuild on file save: +```sh +dotnet watch run --no-hot-reload --property WarningLevel=0 +``` -- cgit 1.5.1