Skip to content
Snippets Groups Projects
Unverified Commit 7bc408d2 authored by Amy's avatar Amy
Browse files

Gateway implementation

parent 900de787
Branches
No related tags found
No related merge requests found
using System.Net.Http.Headers;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Reflection;
using Newtonsoft.Json;
using SharpQuark.Objects;
using SharpQuark.Token;
using Websocket.Client;
namespace SharpQuark;
public class UnsupportedVersionException(string e) : Exception(e);
public partial class Lightquark
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 readonly string _version;
private readonly Uri _baseUri;
private readonly TokenCredential _tokenCredential;
private readonly string _agent;
private List<Channel> _channels;
private WebsocketClient? _client;
public readonly NetworkInformation Network;
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)
public Lightquark(TokenCredential credential, NetworkInformation networkInformation, string? agent = null, string version = "v3", bool suppressStartupMessage = false, bool connectWebsocket = true)
{
_id = _sid;
_sid++;
_tokenCredential = credential;
_version = version;
_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>();
Network = networkInformation;
if (!suppressStartupMessage)
{
Console.WriteLine($"Running {_agent}");
Console.WriteLine($"[{_id}] Running {_agent}");
}
// Gateway
if (!connectWebsocket) return;
_connectGateway();
}
private void _connectGateway()
{
if (Network.Gateway == null) throw new Exception("Network information missing gateway uri when trying to connect gateway.");
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);
// Reconnections
_client.ReconnectTimeout = TimeSpan.FromSeconds(30);
_client.ReconnectionHappened.Subscribe(info =>
Debug.WriteLine($"[{_id}] Reconnection happened, type: {info.Type}"));
_client.DisconnectionHappened.Subscribe(info =>
{
Debug.WriteLine($"[{_id}] Disconnection happened, type: {info.Type}");
if (info.Exception != null) Console.Error.WriteLine(info.Exception);
});
// Start
_client.MessageReceived.Subscribe(msg => GatewayMessage(msg));
_client.StartOrFail();
_heartbeatTimer = new Timer(
_ =>
{
SendGateway(new GatewayMessage("heartbeat", "alive"));
Debug.WriteLine($"[{_id}] Sent heartbeat message.");
},
null,
TimeSpan.Zero,
TimeSpan.FromSeconds(15));
}
private static readonly string[] UnsupportedVersions = ["v1", "v2"];
// ReSharper disable once MemberCanBePrivate.Global
public async Task<string> Call(string endpoint, string method = "GET", ApiCallOptions? options = null)
{
......@@ -53,7 +102,7 @@ public partial class Lightquark
}
// Use request specific version if available
var requestUri = new Uri(_baseUri, $"{options?.Version ?? _version}/{endpoint}");
var requestUri = new Uri(BaseUri, $"{options?.Version ?? _version}/{endpoint}");
var request = new HttpRequestMessage
{
......@@ -86,6 +135,15 @@ public partial class Lightquark
return resultString;
}
public void Dispose()
{
Debug.WriteLine($"[{_id}] Goodbye :(");
_http.Dispose();
_client?.Dispose();
_heartbeatTimer?.Dispose();
GC.SuppressFinalize(this);
}
}
public class ApiCallOptions
......
using System.Diagnostics;
using Newtonsoft.Json;
using SharpQuark.Objects;
using Websocket.Client;
namespace SharpQuark;
public partial class Lightquark
{
private void GatewayMessage(ResponseMessage message)
{
Task.Run(() =>
{
if (message.Text == null) return;
var baseMessage = JsonConvert.DeserializeObject<GatewayEventBase>(message.Text);
if (baseMessage == null) throw new Exception($"Invalid event from gateway {message.Text}");
switch (baseMessage.Event)
{
case "heartbeat":
Debug.WriteLine("Heartbeat received");
return;
default:
Console.WriteLine($"Unimplemented event received {baseMessage.Event}");
break;
}
});
}
private void SendGateway(GatewayMessage message)
{
if (_client == null) _connectGateway();
_client!.Send(JsonConvert.SerializeObject(message));
}
}
\ No newline at end of file
using Newtonsoft.Json;
using SharpQuark.ApiResult;
using SharpQuark.Objects;
using SharpQuark.Objects.Id;
namespace SharpQuark;
public partial class Lightquark
{
public async Task<User> UserMe()
{
var user = await UserMeRaw();
return user.Response.User;
}
// /user/me
public async Task<UserMeApiResult> UserMe()
public async Task<UserMeApiResult> UserMeRaw()
{
var rawApiResult = await Call("/user/me");
......@@ -16,8 +23,14 @@ public partial class Lightquark
return parsedApiResult ?? throw new Exception("/user/me API Result is null");
}
public async Task<User> UserById(UserId userId)
{
var user = await UserByIdRaw(userId);
return user.Response.User;
}
// /user/:userId
public async Task<UserApiResult> UserById(UserId userId)
public async Task<UserApiResult> UserByIdRaw(UserId userId)
{
var rawApiResult = await Call($"/user/{userId}");
......
using Newtonsoft.Json;
namespace SharpQuark.Objects;
public struct GatewayMessage(string @event, string message, string? state = null)
{
[JsonProperty("event")] public string Event = @event;
[JsonProperty("message")] public string Message = message;
[JsonProperty("state")] public string? State = state;
}
public class GatewayEventBase
{
[JsonProperty("eventId")] public required string Event;
}
\ No newline at end of file
......@@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Websocket.Client" Version="5.1.1" />
</ItemGroup>
</Project>
namespace SharpQuark.Token;
using SharpQuark.ApiResult;
namespace SharpQuark.Token;
public class TokenCredential(AccessToken accessToken, RefreshToken refreshToken)
{
......@@ -24,18 +26,27 @@ public class TokenCredential(AccessToken accessToken, RefreshToken refreshToken)
public static async Task<TokenCredential> Login(string email, string password, NetworkInformation networkInformation)
{
// Create temporary Lightquark instance
var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true);
var res = await tempLq.AuthToken(email, password);
AuthTokenApiResult res;
using (var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true, false))
{
res = await tempLq.AuthToken(email, password);
}
var accessToken = (AccessToken)Token.From(res.Response.AccessToken);
var refreshToken = (RefreshToken)Token.From(res.Response.RefreshToken);
return new TokenCredential(accessToken, refreshToken);
}
public static async Task<TokenCredential> Register(string email, string password, string username, NetworkInformation networkInformation)
{
// Create temporary Lightquark instance
var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true);
var res = await tempLq.AuthRegister(email, password, username);
AuthTokenApiResult res;
using (var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true))
{
res = await tempLq.AuthRegister(email, password, username);
}
var accessToken = (AccessToken)Token.From(res.Response.AccessToken);
var refreshToken = (RefreshToken)Token.From(res.Response.RefreshToken);
return new TokenCredential(accessToken, refreshToken);
......
......@@ -26,11 +26,11 @@ var lq = new Lightquark(tokens, netInfo);
// To get data about the logged in user
var userMe = await lq.UserMe();
Console.WriteLine(userMe.Response.User.Username); // Test_User
Console.WriteLine(userMe.Username); // Test_User
// To get data about a specific user
var userById = await lq.UserById("62b3515989cdb45c9e06e010");
Console.WriteLine($"{userById.Response.User.Status?.Type} {userById.Response.User.Status?.Content}"); // Playing Stardew Valley
var userById = await lq.UserById(new UserId("62b3515989cdb45c9e06e010"));
Console.WriteLine($"{userById.Status?.Type} {userById.Status?.Content}"); // Playing Stardew Valley
// Get a channel
var speakySpeaky = await lq.ChannelById(new ChannelId("643aa2e550c913775aec2057"));
......@@ -43,5 +43,4 @@ Console.WriteLine(speakySpeaky.Messages.Last().Content);
var speakySpeaky2 = await lq.ChannelById(new ChannelId("643aa2e550c913775aec2057"));
Console.WriteLine(speakySpeaky2.Messages.Last().Timestamp);
```
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment