From eaec5ded0b480aba0e82c0463de5d4400043f1da Mon Sep 17 00:00:00 2001 From: Amy <amy@litdevs.org> Date: Thu, 15 Feb 2024 20:39:12 +0200 Subject: [PATCH] Channel messages rework --- SharpQuark/Lightquark.cs | 3 ++ SharpQuark/Methods/Channel.cs | 30 +++++++++--- SharpQuark/Objects/Channel.cs | 77 ++++++++++++++++++++++++++---- SharpQuark/Objects/Id/BaseId.cs | 19 ++++++++ SharpQuark/Objects/Id/ChannelId.cs | 17 ++++++- SharpQuark/Objects/Id/MessageId.cs | 17 ++++++- SharpQuark/Objects/Id/QuarkId.cs | 17 ++++++- SharpQuark/Objects/Id/UserId.cs | 17 ++++++- SharpQuark/Objects/Message.cs | 22 +++++++++ SharpQuark/SharpQuark.csproj | 4 +- 10 files changed, 202 insertions(+), 21 deletions(-) diff --git a/SharpQuark/Lightquark.cs b/SharpQuark/Lightquark.cs index 86cb2ea..80eabf4 100644 --- a/SharpQuark/Lightquark.cs +++ b/SharpQuark/Lightquark.cs @@ -1,6 +1,7 @@ using System.Net.Http.Headers; using System.Reflection; using Newtonsoft.Json; +using SharpQuark.Objects; using SharpQuark.Token; namespace SharpQuark; @@ -14,6 +15,7 @@ public partial class Lightquark private readonly Uri _baseUri; private readonly TokenCredential _tokenCredential; private readonly string _agent; + private List<Channel> _channels; public Lightquark(TokenCredential credential, NetworkInformation networkInformation, string? agent = null, string version = "v3", bool suppressStartupMessage = false) { @@ -22,6 +24,7 @@ public partial class Lightquark _agent = agent ?? $"SharpQuark {Assembly.GetExecutingAssembly().GetName().Version}"; if (networkInformation.BaseUrl == null) throw new Exception("Invalid network"); _baseUri = new Uri(networkInformation.BaseUrl); + _channels = new List<Channel>(); if (!suppressStartupMessage) { Console.WriteLine($"Running {_agent}"); diff --git a/SharpQuark/Methods/Channel.cs b/SharpQuark/Methods/Channel.cs index b207246..9c6ee22 100644 --- a/SharpQuark/Methods/Channel.cs +++ b/SharpQuark/Methods/Channel.cs @@ -7,25 +7,41 @@ namespace SharpQuark; public partial class Lightquark { - public async Task<ChannelApiResult> ChannelById(ChannelId channelId) + public async Task<Channel> ChannelById(ChannelId channelId) + { + var channel = await ChannelByIdRaw(channelId); + return channel.Response.Channel; + } + + public async Task<ChannelApiResult> ChannelByIdRaw(ChannelId channelId) { var rawApiResult = await Call($"/channel/{channelId}"); var parsedApiResult = JsonConvert.DeserializeObject<ChannelApiResult>(rawApiResult); - if (parsedApiResult != null) parsedApiResult.Response.Channel.Lq = this; - - return parsedApiResult ?? throw new Exception("/channel/{channelId} API Result is null"); + if (parsedApiResult == null) return parsedApiResult ?? throw new Exception("/channel/{channelId} API Result is null"); + + parsedApiResult.Response.Channel.Lq = this; + await parsedApiResult.Response.Channel.InitialLoad(); + + return parsedApiResult; } - public async Task<ChannelMessagesApiResult> ChannelMessages(ChannelId channelId) + internal async Task<ChannelMessagesApiResult> ChannelMessages(Channel channel) { - var rawApiResult = await Call($"/channel/{channelId}/messages"); + var rawApiResult = await Call($"/channel/{channel.Id}/messages"); var parsedApiResult = JsonConvert.DeserializeObject<ChannelMessagesApiResult>(rawApiResult); + + if (parsedApiResult == null) return parsedApiResult ?? throw new Exception("/channel/{channelId}/messages API Result is null"); - return parsedApiResult ?? throw new Exception("/channel/{channelId}/messages API Result is null"); + foreach (var message in parsedApiResult.Response.Messages) + { + message.Channel = channel; + } + + return parsedApiResult; } } \ No newline at end of file diff --git a/SharpQuark/Objects/Channel.cs b/SharpQuark/Objects/Channel.cs index 71346a5..a030ffb 100644 --- a/SharpQuark/Objects/Channel.cs +++ b/SharpQuark/Objects/Channel.cs @@ -1,23 +1,84 @@ -using Newtonsoft.Json; +using System.Diagnostics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using SharpQuark.Objects.Id; namespace SharpQuark.Objects; +public class ChannelConverter : JsonConverter +{ + private static readonly Dictionary<ChannelId, Channel> Instances = new(); + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Channel); + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + var jsonObject = JObject.Load(reader); + var channelId = jsonObject.GetValue("_id")?.ToObject<ChannelId>(); + + if (Instances.TryGetValue(channelId ?? throw new Exception("Channel ID null"), out var channel)) + return channel; + + // Deserialize other properties + var name = jsonObject.GetValue("name")?.ToObject<string>(); + var quarkId = jsonObject.GetValue("quark")?.ToObject<QuarkId>(); + var description = jsonObject.GetValue("description")?.ToObject<string>(); + + // Create new Channel instance + channel = new Channel(channelId, name ?? throw new Exception("Channel Name null"), quarkId ?? throw new Exception("Channel Quark ID null"), description); + Instances[channelId] = channel; + return channel; + } +} + +[JsonConverter(typeof(ChannelConverter))] public class Channel { [JsonProperty("_id")] - public required ChannelId Id; + public ChannelId Id; [JsonProperty("name")] - public required string Name; + public string Name; [JsonProperty("description")] - public string Description = string.Empty; + public string Description; [JsonProperty("quark")] - public required QuarkId QuarkId; + public QuarkId QuarkId; + [JsonProperty("messages")] + public SortedSet<Message> Messages; + + [JsonIgnore] public Lightquark Lq = null!; + + [JsonIgnore] private bool _initialLoad; - [JsonIgnore] public Lightquark? Lq; + internal Channel(ChannelId id, string name, QuarkId quarkId, string? description) + { + Id = id; + Name = name; + QuarkId = quarkId; + Description = description ?? string.Empty; + Messages = new SortedSet<Message>(new TimestampComparer()); + } + + private async Task<Message[]> GetMessages() + { + return (await Lq.ChannelMessages(this)).Response.Messages; + } - public async Task<Message[]> GetMessages() + public async Task InitialLoad() { - return (await Lq.ChannelMessages(Id)).Response.Messages; + if (_initialLoad) + { + Debug.WriteLine($"[c{Id}] Initial loaded before, skipping"); + return; + } + Messages.UnionWith(await GetMessages()); + _initialLoad = true; } }; \ No newline at end of file diff --git a/SharpQuark/Objects/Id/BaseId.cs b/SharpQuark/Objects/Id/BaseId.cs index 5081885..3e18eec 100644 --- a/SharpQuark/Objects/Id/BaseId.cs +++ b/SharpQuark/Objects/Id/BaseId.cs @@ -8,4 +8,23 @@ public class BaseId(string id) { return Id; } + + public override bool Equals(object? obj) + { + if (obj is BaseId other) + { + return Id == other.Id; + } + return false; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public int CompareTo(BaseId other) + { + return string.Compare(Id, other.Id, StringComparison.Ordinal); + } } \ No newline at end of file diff --git a/SharpQuark/Objects/Id/ChannelId.cs b/SharpQuark/Objects/Id/ChannelId.cs index febbe3d..04d7c7c 100644 --- a/SharpQuark/Objects/Id/ChannelId.cs +++ b/SharpQuark/Objects/Id/ChannelId.cs @@ -3,7 +3,22 @@ namespace SharpQuark.Objects.Id; [JsonConverter(typeof(ChannelIdConverter))] -public class ChannelId(string id) : BaseId(id); +public class ChannelId(string id) : BaseId(id) +{ + public override bool Equals(object? obj) + { + if (obj is ChannelId other) + { + return Id == other.Id; + } + return false; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } +} public class ChannelIdConverter : JsonConverter { diff --git a/SharpQuark/Objects/Id/MessageId.cs b/SharpQuark/Objects/Id/MessageId.cs index 94f4fdc..058311f 100644 --- a/SharpQuark/Objects/Id/MessageId.cs +++ b/SharpQuark/Objects/Id/MessageId.cs @@ -3,7 +3,22 @@ namespace SharpQuark.Objects.Id; [JsonConverter(typeof(MessageIdConverter))] -public class MessageId(string id) : BaseId(id); +public class MessageId(string id) : BaseId(id) +{ + public override bool Equals(object? obj) + { + if (obj is MessageId other) + { + return Id == other.Id; + } + return false; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } +} public class MessageIdConverter : JsonConverter { diff --git a/SharpQuark/Objects/Id/QuarkId.cs b/SharpQuark/Objects/Id/QuarkId.cs index 2ecffed..ef7c4fe 100644 --- a/SharpQuark/Objects/Id/QuarkId.cs +++ b/SharpQuark/Objects/Id/QuarkId.cs @@ -3,7 +3,22 @@ namespace SharpQuark.Objects.Id; [JsonConverter(typeof(QuarkIdConverter))] -public class QuarkId(string id) : BaseId(id); +public class QuarkId(string id) : BaseId(id) +{ + public override bool Equals(object? obj) + { + if (obj is QuarkId other) + { + return Id == other.Id; + } + return false; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } +} public class QuarkIdConverter : JsonConverter { diff --git a/SharpQuark/Objects/Id/UserId.cs b/SharpQuark/Objects/Id/UserId.cs index 5cbbcd3..010d2bc 100644 --- a/SharpQuark/Objects/Id/UserId.cs +++ b/SharpQuark/Objects/Id/UserId.cs @@ -3,7 +3,22 @@ namespace SharpQuark.Objects.Id; [JsonConverter(typeof(UserIdConverter))] -public class UserId(string id) : BaseId(id); +public class UserId(string id) : BaseId(id) +{ + public override bool Equals(object? obj) + { + if (obj is UserId other) + { + return Id == other.Id; + } + return false; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } +} public class UserIdConverter : JsonConverter { diff --git a/SharpQuark/Objects/Message.cs b/SharpQuark/Objects/Message.cs index 0d0045c..78ef6ce 100644 --- a/SharpQuark/Objects/Message.cs +++ b/SharpQuark/Objects/Message.cs @@ -19,6 +19,9 @@ public class Message [JsonProperty("timestamp")] public required long JsTimestamp; + + [JsonIgnore] + public DateTimeOffset Timestamp => DateTimeOffset.FromUnixTimeMilliseconds(JsTimestamp); [JsonProperty("edited")] public required bool Edited; @@ -31,4 +34,23 @@ public class Message [JsonProperty("author")] public required User Author; + + [JsonIgnore] public Channel Channel = null!; + +} + + +public class TimestampComparer : IComparer<Message> +{ + public int Compare(Message? x, Message? y) + { + if (x == null || y == null) + { + throw new ArgumentException("Messages cannot be null"); + } + + // First, compare by JsTimestamp + var timestampComparison = x.JsTimestamp.CompareTo(y.JsTimestamp); + return timestampComparison != 0 ? timestampComparison : x.Id.CompareTo(y.Id); + } } \ No newline at end of file diff --git a/SharpQuark/SharpQuark.csproj b/SharpQuark/SharpQuark.csproj index 8f54db0..334ae98 100644 --- a/SharpQuark/SharpQuark.csproj +++ b/SharpQuark/SharpQuark.csproj @@ -4,8 +4,8 @@ <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> - <AssemblyVersion>2024.2.11.1</AssemblyVersion> - <Version>2024.2.11.1-beta</Version> + <AssemblyVersion>2024.2.15.0</AssemblyVersion> + <Version>2024.2.15.0-beta</Version> </PropertyGroup> <ItemGroup> -- GitLab