DiscordApi/Discord.API/Gateway/GatewayClient.cs

91 lines
3.4 KiB
C#

using System.Collections.Specialized;
using System.Text.Json;
using Serilog;
using Websocket.Client;
namespace Discord.API;
public class GatewayClient : AbstractGateway {
private const int ApiVersion = 10;
private string ApiKey;
private ulong? Sequence = null;
public GatewayClient(string url, string api_key, TimeProvider time_provider)
: this(url, api_key, time_provider, uri => new WebsocketClient(uri))
{ }
internal GatewayClient(string url, string api_key, TimeProvider time_provider, Func<Uri, IWebsocketClient> websocket_client_factory)
: base(websocket_client_factory.Invoke(BuildUrl(url)), time_provider){
this.ApiKey=api_key;
Log.Debug("GATEWAY: Created new gateway, with url: {url}", WebsocketClient.Url);
}
private static Uri BuildUrl(string url){
UriBuilder uriBuilder = new(url);
NameValueCollection query = System.Web.HttpUtility.ParseQueryString("");
query.Add("v", ApiVersion.ToString());
query.Add("encoding", "json");
uriBuilder.Query=query.ToString();
return uriBuilder.Uri;
}
protected override void DisconnectHandler(DisconnectionInfo info){
Log.Information("GATEWAY: Disconnected. Type: {DisconnectionType}", info.Type);
}
protected override void ReconnectHandler(ReconnectionInfo info){
Log.Information("GATEWAY: (Re)Connected to server. Url: {url}, Type: {Type}", WebsocketClient.Url, info.Type);
}
protected override void MessageReceivedHandler(ResponseMessage msg){
if(msg.MessageType != System.Net.WebSockets.WebSocketMessageType.Text) return;
try{
GatewayPacket packet = JsonSerializer.Deserialize(msg.Text!, SourceGenerationContext.Default.GatewayPacket)
?? throw new Exception("Failed to deserialize packet"); // This can be optimized //TODO
Log.Debug("GATEWAY: Packet received");
switch(packet){
case HelloPacket helloPacket:
HelloPacketHandler(helloPacket);
break;
case HeartbeatAckPacket:
HeartbeatAckHandler();
break;
default:
Log.Debug("GATEWAY: Packet not handled");
break;
}
}catch(Exception ex){
Log.Warning(ex, "GATEWAY: Error processing gateway event");
}
}
private void HelloPacketHandler(HelloPacket packet){
StartHeartbeat((int)packet.Data.HeartbeatInterval);
Log.Debug("GATEWAY: Hello packet received, heartbeat interval: {heartbeat_ms}", packet.Data.HeartbeatInterval);
}
private void HeartbeatAckHandler(){
Log.Debug("GATEWAY: Heartbeat ACK received");
Log.Information("GATEWAY: Heartbeat ping: {ping_ms} ms", HeartbeatAckReceived());
}
protected override Task SendHeartbeat()
{
HeartbeatPacket packet = new(){
Sequence = Sequence
};
WebsocketClient.Send(JsonSerializer.Serialize(packet, SourceGenerationContext.Default.HeartbeatPacket));
Log.Debug("GATEWAY: Heartbeat sent");
return Task.CompletedTask;
}
protected override Task<bool> MissingHeartbeatAckHandler()
{
_ = WebsocketClient.Reconnect();
Log.Debug("GATEWAY: Heartbeat ack missed. Reconnecting");
return Task.FromResult(true);
}
}