From 4340b1899470c06f170817dbc1200040619fbf8d Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Sat, 14 Oct 2023 23:19:25 +0200 Subject: Handle floats etc in requests --- .../State/RoomInfo/RoomPowerLevelEventContent.cs | 30 +++++++++-- LibMatrix/Extensions/HttpClientExtensions.cs | 58 ++++++++++++++++++++-- LibMatrix/RoomTypes/GenericRoom.cs | 5 +- LibMatrix/StateEvent.cs | 8 ++- 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/LibMatrix/EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs b/LibMatrix/EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs index 7f37271..a1bf517 100644 --- a/LibMatrix/EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs +++ b/LibMatrix/EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs @@ -3,13 +3,15 @@ using LibMatrix.Interfaces; namespace LibMatrix.EventTypes.Spec.State; -[MatrixEvent(EventName = "m.room.power_levels")] +[MatrixEvent(EventName = EventId)] public class RoomPowerLevelEventContent : EventContent { + public const string EventId = "m.room.power_levels"; + [JsonPropertyName("ban")] public long? Ban { get; set; } = 50; [JsonPropertyName("events_default")] - public long EventsDefault { get; set; } = 0; + public long? EventsDefault { get; set; } = 0; [JsonPropertyName("events")] public Dictionary? Events { get; set; } // = null!; @@ -46,10 +48,32 @@ public class RoomPowerLevelEventContent : EventContent { } public bool IsUserAdmin(string userId) { + if(userId is null) throw new ArgumentNullException(nameof(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); + if(userId is null) throw new ArgumentNullException(nameof(userId)); + return Users.TryGetValue(userId, out var level) && level >= Events.GetValueOrDefault(eventType, EventsDefault ?? 0); + } + + public long GetUserPowerLevel(string userId) { + if(userId is null) throw new ArgumentNullException(nameof(userId)); + return Users.TryGetValue(userId, out var level) ? level : UsersDefault ?? UsersDefault ?? 0; + } + + public long GetEventPowerLevel(string eventType) { + return Events.TryGetValue(eventType, out var level) ? level : EventsDefault ?? EventsDefault ?? 0; + } + + public void SetUserPowerLevel(string userId, long powerLevel) { + if(userId is null) throw new ArgumentNullException(nameof(userId)); + Users ??= new(); + if (Users.TryGetValue(userId, out var level)) { + Users[userId] = powerLevel; + } + else { + Users.Add(userId, powerLevel); + } } } diff --git a/LibMatrix/Extensions/HttpClientExtensions.cs b/LibMatrix/Extensions/HttpClientExtensions.cs index 62e9a98..bffd74a 100644 --- a/LibMatrix/Extensions/HttpClientExtensions.cs +++ b/LibMatrix/Extensions/HttpClientExtensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Net.Http.Headers; using System.Reflection; using System.Text; @@ -26,6 +27,15 @@ public static class HttpClientExtensions { public class MatrixHttpClient : HttpClient { internal string? AssertedUserId { get; set; } + private JsonSerializerOptions GetJsonSerializerOptions(JsonSerializerOptions? options = null) { + options ??= new JsonSerializerOptions(); + options.Converters.Add(new JsonFloatStringConverter()); + options.Converters.Add(new JsonDoubleStringConverter()); + options.Converters.Add(new JsonDecimalStringConverter()); + options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; + return options; + } + public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null"); @@ -68,7 +78,8 @@ public class MatrixHttpClient : HttpClient { } // GetFromJsonAsync - public async Task GetFromJsonAsync(string requestUri, CancellationToken cancellationToken = default) { + public async Task GetFromJsonAsync(string requestUri, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { + options = GetJsonSerializerOptions(options); var request = new HttpRequestMessage(HttpMethod.Get, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await SendAsync(request, cancellationToken); @@ -82,7 +93,7 @@ public class MatrixHttpClient : HttpClient { Console.WriteLine("[!!] Checking sync response failed: " + e); } #endif - return await JsonSerializer.DeserializeAsync(responseStream, cancellationToken: cancellationToken) ?? + return await JsonSerializer.DeserializeAsync(responseStream, options, cancellationToken: cancellationToken) ?? throw new InvalidOperationException("Failed to deserialize response"); } @@ -97,19 +108,58 @@ public class MatrixHttpClient : HttpClient { public new async Task PutAsJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, T value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { + options = GetJsonSerializerOptions(options); var request = new HttpRequestMessage(HttpMethod.Put, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options ?? new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), + request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options), Encoding.UTF8, "application/json"); return await SendAsync(request, cancellationToken); } public async Task PostAsJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, T value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { + options ??= new(); + options.Converters.Add(new JsonFloatStringConverter()); + options.Converters.Add(new JsonDoubleStringConverter()); + options.Converters.Add(new JsonDecimalStringConverter()); + options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; var request = new HttpRequestMessage(HttpMethod.Post, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options ?? new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), + request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options), Encoding.UTF8, "application/json"); return await SendAsync(request, cancellationToken); } + + public async IAsyncEnumerable GetAsyncEnumerableFromJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, JsonSerializerOptions? options = null) { + options = GetJsonSerializerOptions(options); + var res = await GetAsync(requestUri); + var result = JsonSerializer.DeserializeAsyncEnumerable(await res.Content.ReadAsStreamAsync(), options); + await foreach (var resp in result) { + yield return resp; + } + } +} + +public class JsonFloatStringConverter : JsonConverter { + public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => float.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); +} + +public class JsonDoubleStringConverter : JsonConverter { + public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => double.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); +} + +public class JsonDecimalStringConverter : JsonConverter { + public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => decimal.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); } diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs index 106b2f6..d11b28d 100644 --- a/LibMatrix/RoomTypes/GenericRoom.cs +++ b/LibMatrix/RoomTypes/GenericRoom.cs @@ -27,9 +27,8 @@ public class GenericRoom { public string RoomId { get; set; } public async IAsyncEnumerable GetFullStateAsync() { - var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/state"); - var result = - JsonSerializer.DeserializeAsyncEnumerable(await res.Content.ReadAsStreamAsync()); + var result = _httpClient.GetAsyncEnumerableFromJsonAsync( + $"/_matrix/client/v3/rooms/{RoomId}/state"); await foreach (var resp in result) { yield return resp; } diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs index e846811..fb4dd71 100644 --- a/LibMatrix/StateEvent.cs +++ b/LibMatrix/StateEvent.cs @@ -5,6 +5,7 @@ using System.Text.Json.Serialization; using ArcaneLibs; using ArcaneLibs.Extensions; using LibMatrix.EventTypes; +using LibMatrix.Extensions; using LibMatrix.Interfaces; namespace LibMatrix; @@ -41,7 +42,12 @@ public class StateEvent { return null!; } try { - return (EventContent)RawContent.Deserialize(GetType)!; + var jse = new JsonSerializerOptions(); + jse ??= new JsonSerializerOptions(); + jse.Converters.Add(new JsonFloatStringConverter()); + jse.Converters.Add(new JsonDoubleStringConverter()); + jse.Converters.Add(new JsonDecimalStringConverter()); + return (EventContent)RawContent.Deserialize(GetType, jse)!; } catch (JsonException e) { Console.WriteLine(e); -- cgit 1.4.1