From a39f27f1cc595a76b87ac435ade5cb87249cfd39 Mon Sep 17 00:00:00 2001 From: Emilia <emilia@jumpsca.re> Date: Sat, 25 May 2024 17:06:24 +0300 Subject: [PATCH] something along the lines of v4 support --- SharpQuark/ApiResult/UserApiResult.cs | 2 +- SharpQuark/Lightquark.cs | 13 ++-- SharpQuark/Methods/Gateway.cs | 3 + SharpQuark/NetworkInformation.cs | 4 +- SharpQuark/Objects/Channel.cs | 10 +-- SharpQuark/Objects/GatewayMessage.cs | 19 +++++- SharpQuark/Objects/Id/QuarkId.cs | 2 +- SharpQuark/Objects/Message.cs | 17 ++++- SharpQuark/Objects/Quark.cs | 92 +++++++++++++++++++++++++++ SharpQuark/Token/Token.cs | 1 + SharpQuark/Token/TokenCredential.cs | 4 +- 11 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 SharpQuark/Objects/Quark.cs diff --git a/SharpQuark/ApiResult/UserApiResult.cs b/SharpQuark/ApiResult/UserApiResult.cs index ca9e70d..49d967b 100644 --- a/SharpQuark/ApiResult/UserApiResult.cs +++ b/SharpQuark/ApiResult/UserApiResult.cs @@ -17,7 +17,7 @@ public class UserApiResult : BaseApiResult public class UserMeResponse : Response { - [JsonProperty("jwtData")] + [JsonProperty("user")] public required User User; } diff --git a/SharpQuark/Lightquark.cs b/SharpQuark/Lightquark.cs index e4209d7..1d57f0d 100644 --- a/SharpQuark/Lightquark.cs +++ b/SharpQuark/Lightquark.cs @@ -16,7 +16,7 @@ public partial class Lightquark : IDisposable private static int _sid; private readonly int _id; private readonly HttpClient _http = new(); - private static readonly string[] UnsupportedVersions = ["v1", "v2"]; + private static readonly string[] UnsupportedVersions = ["v1", "v2", "v3"]; private readonly string _version; private readonly TokenCredential _tokenCredential; private readonly string _agent; @@ -25,7 +25,7 @@ public partial class Lightquark : IDisposable private Uri BaseUri => new (Network.BaseUrl ?? throw new Exception("Invalid network")); private Timer? _heartbeatTimer; - public Lightquark(TokenCredential credential, NetworkInformation networkInformation, string? agent = null, string version = "v3", bool suppressStartupMessage = false, bool connectWebsocket = true) + public Lightquark(TokenCredential credential, NetworkInformation networkInformation, string? agent = null, string version = "v4", bool suppressStartupMessage = false, bool connectWebsocket = true) { _id = _sid; _sid++; @@ -50,7 +50,6 @@ public partial class Lightquark : IDisposable var clientFactory = new Func<ClientWebSocket>(() => { var client = new ClientWebSocket(); - client.Options.AddSubProtocol(_tokenCredential.AccessToken.ToString()!); return client; }); _client = new WebsocketClient(new Uri(Network.Gateway), clientFactory); @@ -67,9 +66,11 @@ public partial class Lightquark : IDisposable }); // Start - _client.MessageReceived.Subscribe(msg => GatewayMessage(msg)); + _client.MessageReceived.Subscribe(GatewayMessage); _client.StartOrFail(); + SendGateway(new AuthenticateGatewayMessage("authenticate", _tokenCredential.AccessToken)); + _heartbeatTimer = new Timer( _ => { @@ -77,7 +78,7 @@ public partial class Lightquark : IDisposable Debug.WriteLine($"[{_id}] Sent heartbeat message."); }, null, - TimeSpan.Zero, + TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(15)); } @@ -93,7 +94,7 @@ public partial class Lightquark : IDisposable } // Prevent falling over for predictable reasons if (UnsupportedVersions.Any(v => v == (options?.Version ?? _version))) - throw new UnsupportedVersionException("SharpQuark does not support API versions 1 and 2 due to authentication differences"); + throw new UnsupportedVersionException("SharpQuark only supports version 4."); // Allow using both "/user/me" and "user/me" if (endpoint.StartsWith('/')) diff --git a/SharpQuark/Methods/Gateway.cs b/SharpQuark/Methods/Gateway.cs index 1f6c704..e0b7fbb 100644 --- a/SharpQuark/Methods/Gateway.cs +++ b/SharpQuark/Methods/Gateway.cs @@ -20,6 +20,9 @@ public partial class Lightquark case "heartbeat": Debug.WriteLine("Heartbeat received"); return; + case "authenticate": + Debug.WriteLine("Authenticated"); + return; default: Console.WriteLine($"Unimplemented event received {baseMessage.Event}"); break; diff --git a/SharpQuark/NetworkInformation.cs b/SharpQuark/NetworkInformation.cs index 2eea2de..78b3975 100644 --- a/SharpQuark/NetworkInformation.cs +++ b/SharpQuark/NetworkInformation.cs @@ -31,10 +31,10 @@ public class NetworkInformation [JsonProperty("capabilities")] public object? Capabilities; - public static async Task<NetworkInformation> GetNetwork(string baseUrl) + public static async Task<NetworkInformation> GetNetwork(string baseUrl, string version = "v4") { var baseUri = new Uri(baseUrl); - var res = await Http.GetAsync(new Uri(baseUri, "/v3/network")); + var res = await Http.GetAsync(new Uri(baseUri, $"/{version}/network")); var rawApiResult = await res.Content.ReadAsStringAsync(); var parsedApiResult = JsonConvert.DeserializeObject<NetworkInformation>(rawApiResult); return parsedApiResult ?? throw new InvalidOperationException(); diff --git a/SharpQuark/Objects/Channel.cs b/SharpQuark/Objects/Channel.cs index a030ffb..64f94c9 100644 --- a/SharpQuark/Objects/Channel.cs +++ b/SharpQuark/Objects/Channel.cs @@ -29,11 +29,11 @@ public class ChannelConverter : JsonConverter // Deserialize other properties var name = jsonObject.GetValue("name")?.ToObject<string>(); - var quarkId = jsonObject.GetValue("quark")?.ToObject<QuarkId>(); + var quark = jsonObject.GetValue("quark")?.ToObject<Quark>(); 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); + channel = new Channel(channelId, name ?? throw new Exception("Channel Name null"), quark ?? throw new Exception("Channel Quark ID null"), description); Instances[channelId] = channel; return channel; } @@ -49,7 +49,7 @@ public class Channel [JsonProperty("description")] public string Description; [JsonProperty("quark")] - public QuarkId QuarkId; + public Quark Quark; [JsonProperty("messages")] public SortedSet<Message> Messages; @@ -57,11 +57,11 @@ public class Channel [JsonIgnore] private bool _initialLoad; - internal Channel(ChannelId id, string name, QuarkId quarkId, string? description) + internal Channel(ChannelId id, string name, Quark quark, string? description) { Id = id; Name = name; - QuarkId = quarkId; + Quark = quark; Description = description ?? string.Empty; Messages = new SortedSet<Message>(new TimestampComparer()); } diff --git a/SharpQuark/Objects/GatewayMessage.cs b/SharpQuark/Objects/GatewayMessage.cs index 6d2bc11..a17f20e 100644 --- a/SharpQuark/Objects/GatewayMessage.cs +++ b/SharpQuark/Objects/GatewayMessage.cs @@ -1,16 +1,29 @@ using Newtonsoft.Json; +using SharpQuark.Token; namespace SharpQuark.Objects; -public struct GatewayMessage(string @event, string message, string? state = null) +public class GatewayMessage(string @event, string? message, string? state = null) { [JsonProperty("event")] public string Event = @event; - [JsonProperty("message")] public string Message = message; + [JsonProperty("message")] public string? Message = message; [JsonProperty("state")] public string? State = state; } +// public class AuthenticateGatewayMessage(string @event, AccessToken token, string? state = null) +// { +// [JsonProperty("eventId")] public required string Event = @event; +// [JsonProperty("token")] public string Token = token.ToString()!; +// [JsonProperty("state")] public string? State = state; +// } + +public class AuthenticateGatewayMessage(string @event, AccessToken token, string? state = null) : GatewayMessage(@event, null, state) +{ + [JsonProperty("token")] public string Token = token.ToString()!; +} + public class GatewayEventBase { - [JsonProperty("eventId")] public required string Event; + [JsonProperty("event")] public required string Event; [JsonProperty("state")] public string? State; } \ No newline at end of file diff --git a/SharpQuark/Objects/Id/QuarkId.cs b/SharpQuark/Objects/Id/QuarkId.cs index ef7c4fe..e83b1fd 100644 --- a/SharpQuark/Objects/Id/QuarkId.cs +++ b/SharpQuark/Objects/Id/QuarkId.cs @@ -33,7 +33,7 @@ public class QuarkIdConverter : JsonConverter { return new QuarkId((string?)reader.Value ?? string.Empty); } - throw new JsonSerializationException("Unexpected token type."); + throw new JsonSerializationException($"Unexpected token type. ({reader.TokenType})"); } public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) diff --git a/SharpQuark/Objects/Message.cs b/SharpQuark/Objects/Message.cs index 78ef6ce..88772ae 100644 --- a/SharpQuark/Objects/Message.cs +++ b/SharpQuark/Objects/Message.cs @@ -27,7 +27,7 @@ public class Message public required bool Edited; [JsonProperty("attachments")] - public required string[] AttachmentLinks; // TODO: Attachment objects with metadata + public required Attachment[] Attachment; [JsonProperty("specialAttributes")] public object[] SpecialAttributes = []; @@ -39,6 +39,21 @@ public class Message } +public class Attachment +{ + [JsonProperty("url")] + public required Uri Uri; + + [JsonProperty("size")] + public long Size; + + [JsonProperty("type")] + public required string ContentType; + + [JsonProperty("filename")] + public required string FileName; +} + public class TimestampComparer : IComparer<Message> { diff --git a/SharpQuark/Objects/Quark.cs b/SharpQuark/Objects/Quark.cs new file mode 100644 index 0000000..f5ebc00 --- /dev/null +++ b/SharpQuark/Objects/Quark.cs @@ -0,0 +1,92 @@ +using System.Diagnostics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SharpQuark.Objects.Id; + +namespace SharpQuark.Objects; + +public class QuarkConverter : JsonConverter +{ + private static readonly Dictionary<QuarkId, Quark> Instances = new(); + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Quark); + } + + 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 quarkId = jsonObject.GetValue("_id")?.ToObject<QuarkId>(); + + if (Instances.TryGetValue(quarkId ?? throw new Exception("Quark ID null"), out var quark)) + return quark; + + // Deserialize other properties + var name = jsonObject.GetValue("name")?.ToObject<string>(); + var iconUri = jsonObject.GetValue("iconUri")?.ToObject<Uri>(); + var invite = jsonObject.GetValue("invite")?.ToObject<string>(); + var owners = jsonObject.GetValue("owners")?.ToObject<UserId[]>(); + + // Create new Channel instance + quark = new Quark(quarkId, + name ?? throw new Exception("Quark Name null"), + iconUri ?? throw new Exception("Quark Icon null"), + invite ?? throw new Exception("Quark Invite null"), + owners ?? throw new Exception("Quark Owners null")); + Instances[quarkId] = quark; + return quark; + } +} + +[JsonConverter(typeof(QuarkConverter))] +public class Quark +{ + [JsonProperty("_id")] + public QuarkId Id; + [JsonProperty("name")] + public string Name; + [JsonProperty("iconUri")] + public Uri Icon; + [JsonProperty("invite")] + public string Invite; + [JsonProperty("owners")] + public UserId[] Owners; + // [JsonProperty("channels")] + // public Channel[] Channels; + + [JsonIgnore] public Lightquark Lq = null!; + + [JsonIgnore] private bool _initialLoad; + + internal Quark(QuarkId id, string name, Uri iconUri, string invite, UserId[] owners) + { + Id = id; + Name = name; + Icon = iconUri; + Invite = invite; + Owners = owners; + // Channels = []; + } + + // private async Task<Channel[]> GetChannels() + // { + // return (await Lq.QuarkChannels(this)).Response.Messages; + // } + + public async Task InitialLoad() + { + if (_initialLoad) + { + Debug.WriteLine($"[q{Id}] Initial loaded before, skipping"); + return; + } + // TODO: get channels + _initialLoad = true; + } +}; \ No newline at end of file diff --git a/SharpQuark/Token/Token.cs b/SharpQuark/Token/Token.cs index 0254244..09b183d 100644 --- a/SharpQuark/Token/Token.cs +++ b/SharpQuark/Token/Token.cs @@ -39,6 +39,7 @@ public class Token public static Token From(string token) { var tokenParts = token.Split("."); + Console.WriteLine(token); if (tokenParts.Length != 5) throw new TokenException($"Wrong token part count: expected 5, got {tokenParts.Length}"); var prefixPart = tokenParts[0]; diff --git a/SharpQuark/Token/TokenCredential.cs b/SharpQuark/Token/TokenCredential.cs index e7bd788..240c4d8 100644 --- a/SharpQuark/Token/TokenCredential.cs +++ b/SharpQuark/Token/TokenCredential.cs @@ -27,7 +27,7 @@ public class TokenCredential(AccessToken accessToken, RefreshToken refreshToken) { // Create temporary Lightquark instance AuthTokenApiResult res; - using (var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true, false)) + using (var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v4", true, false)) { res = await tempLq.AuthToken(email, password); } @@ -42,7 +42,7 @@ public class TokenCredential(AccessToken accessToken, RefreshToken refreshToken) { // Create temporary Lightquark instance AuthTokenApiResult res; - using (var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true)) + using (var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v4", true)) { res = await tempLq.AuthRegister(email, password, username); } -- GitLab