feat: add rest client to get gateway url

This commit is contained in:
Stevan Freeborn
2025-05-13 15:55:50 -05:00
parent a25e3194db
commit df9b3cd7fc
7 changed files with 188 additions and 20 deletions
@@ -5,44 +5,73 @@ internal class DiscordGatewayClient : IDiscordGatewayClient
private readonly DiscordGatewayClientOptions _options;
private readonly IWebSocketFactory _webSocketFactory;
private readonly ILogger<DiscordGatewayClient> _logger;
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Converters =
{
new DiscordEventConverter(),
},
};
private readonly IDiscordRestClient _discordRestClient;
// private readonly JsonSerializerOptions _jsonSerializerOptions = new()
// {
// PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
// ReferenceHandler = ReferenceHandler.IgnoreCycles,
// Converters =
// {
// new DiscordEventConverter(),
// },
// };
private readonly AsyncLock _lock = new();
private string _gatewayUrl = string.Empty;
private IWebSocket? _webSocket;
private Task? _receiveTask;
private DateTime _timeLastHeartbeatSent = DateTime.MinValue;
private DateTime _timeLastHeartbeatAcknowledged = DateTime.MinValue;
private CancellationTokenSource? _heartbeatCts;
private Task? _heartbeatTask;
private string _sessionId = string.Empty;
private string _resumeGatewayUrl = string.Empty;
// private DateTime _timeLastHeartbeatSent = DateTime.MinValue;
// private DateTime _timeLastHeartbeatAcknowledged = DateTime.MinValue;
// private CancellationTokenSource? _heartbeatCts;
// private Task? _heartbeatTask;
// private string _sessionId = string.Empty;
// private string _resumeGatewayUrl = string.Empty;
public DiscordGatewayClient(
DiscordGatewayClientOptions options,
IWebSocketFactory webSocketFactory,
ILogger<DiscordGatewayClient> logger
ILogger<DiscordGatewayClient> logger,
IDiscordRestClient discordRestClient
)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_webSocketFactory = webSocketFactory ?? throw new ArgumentNullException(nameof(webSocketFactory));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_discordRestClient = discordRestClient ?? throw new ArgumentNullException(nameof(discordRestClient));
}
public async Task ConnectAsync(CancellationToken cancellationToken)
{
using (await _lock.LockAsync(cancellationToken))
{
if (string.IsNullOrEmpty(_gatewayUrl))
{
_gatewayUrl = await _discordRestClient.GetGatewayUrlAsync(cancellationToken);
}
_webSocket = _webSocketFactory.Create();
var uri = new Uri(_gatewayUrl);
await _webSocket.ConnectAsync(uri, cancellationToken);
_logger.LogInformation("Connected to Discord Gateway at {GatewayUrl}", _gatewayUrl);
_receiveTask = ReceiveMessagesAsync(cancellationToken);
}
}
private static Task ReceiveMessagesAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
_heartbeatCts?.Cancel();
_heartbeatCts?.Dispose();
_heartbeatTask?.Dispose();
// _heartbeatCts?.Cancel();
// _heartbeatCts?.Dispose();
// _heartbeatTask?.Dispose();
_receiveTask?.Dispose();
_webSocket?.Dispose();
_lock.Dispose();
}
}
@@ -0,0 +1,43 @@
namespace StevesBot.Worker.Discord;
internal class DiscordRestClient : IDiscordRestClient
{
private readonly ILogger<DiscordRestClient> _logger;
private readonly HttpClient _httpClient;
public DiscordRestClient(
ILogger<DiscordRestClient> logger,
HttpClient httpClient
)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<string> GetGatewayUrlAsync(CancellationToken cancellationToken)
{
var uri = new Uri("gateway", UriKind.Relative);
var response = await _httpClient.GetAsync(uri, cancellationToken);
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Failed to get gateway URL: {StatusCode}", response.StatusCode);
throw new DiscordRestClientException("Failed to get gateway URL.");
}
var gatewayResponse = await response.Content.ReadFromJsonAsync<GatewayResponse>(cancellationToken);
if (gatewayResponse is null)
{
_logger.LogError("Failed to deserialize gateway response.");
throw new DiscordRestClientException("Failed to deserialize gateway response.");
}
return gatewayResponse.Url;
}
}
internal record GatewayResponse(
[property: JsonPropertyName("url")] string Url
);
@@ -0,0 +1,16 @@
namespace StevesBot.Worker.Discord;
internal class DiscordRestClientException : Exception
{
public DiscordRestClientException()
{
}
public DiscordRestClientException(string message) : base(message)
{
}
public DiscordRestClientException(string message, Exception innerException) : base(message, innerException)
{
}
}
@@ -2,4 +2,5 @@ namespace StevesBot.Worker.Discord;
internal interface IDiscordGatewayClient : IDisposable
{
Task ConnectAsync(CancellationToken cancellationToken);
}
@@ -0,0 +1,6 @@
namespace StevesBot.Worker.Discord;
internal interface IDiscordRestClient
{
Task<string> GetGatewayUrlAsync(CancellationToken cancellationToken);
}
+1 -1
View File
@@ -1,8 +1,8 @@
global using System.Net.Http.Json;
global using System.Net.WebSockets;
global using System.Text.Json;
global using System.Text.Json.Serialization;
global using StevesBot.Worker.Discord.Events;
global using StevesBot.Worker.Threading;
global using StevesBot.Worker.WebSockets;