@@ -9,10 +9,12 @@ | |||||
- User | - User | ||||
- Voice | - Voice | ||||
- Webhook | - Webhook | ||||
- Ratelimiter with refit | |||||
- Gateway | - Gateway | ||||
- Models | - Models | ||||
- Client | - Client | ||||
- Socket | - Socket | ||||
- use token | |||||
* Receive | * Receive | ||||
* Compression | * Compression | ||||
- Voice (long) | - Voice (long) | ||||
@@ -24,8 +26,11 @@ | |||||
- Emoji | - Emoji | ||||
- Guild | - Guild | ||||
- User | - User | ||||
- Utilities | |||||
- Token Validation (port from @ChrisJ) | |||||
- Tests | - Tests | ||||
- Unit test Gateway stability / deadlockability? | - Unit test Gateway stability / deadlockability? | ||||
- Port ChrisJ's token validator tests | |||||
- Extensions | - Extensions | ||||
- Commands | - Commands | ||||
? design - use finite's or quahu's | ? design - use finite's or quahu's | ||||
@@ -7,4 +7,14 @@ | |||||
<RootNamespace>Discord</RootNamespace> | <RootNamespace>Discord</RootNamespace> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<PackageReference Include="refit" Version="5.0.23" /> | |||||
<PackageReference Include="System.Text.Json" Version="4.7.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Folder Include="Entities\" /> | |||||
<Folder Include="..\..\..\..\..\%2540discord\%2540next\Discord.Net\src\Discord.Net\Rest\Requests\" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -1,25 +1,20 @@ | |||||
using Discord.Rest; | using Discord.Rest; | ||||
using Discord.Socket; | using Discord.Socket; | ||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
internal class DiscordClient : IDiscordClient | internal class DiscordClient : IDiscordClient | ||||
{ | { | ||||
public DiscordRestApi Rest => _restApi; | |||||
public DiscordGatewayApi Gateway => _gatewayApi; | |||||
public DiscordRestApi Rest { get; } | |||||
public DiscordGatewayApi Gateway { get; } | |||||
private readonly DiscordConfig _config; | private readonly DiscordConfig _config; | ||||
private readonly DiscordRestApi _restApi; | |||||
private readonly DiscordGatewayApi _gatewayApi; | |||||
public DiscordClient(DiscordConfig config, DiscordRestApi restApi, DiscordGatewayApi gatewayApi) | public DiscordClient(DiscordConfig config, DiscordRestApi restApi, DiscordGatewayApi gatewayApi) | ||||
{ | { | ||||
_config = config; | _config = config; | ||||
_restApi = restApi; | |||||
_gatewayApi = gatewayApi; | |||||
Rest = restApi; | |||||
Gateway = gatewayApi; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -27,7 +27,9 @@ namespace Discord | |||||
/// The URI to use when connecting to the gateway. If specified, this will override the URI Discord instructs us to use. | /// The URI to use when connecting to the gateway. If specified, this will override the URI Discord instructs us to use. | ||||
/// </summary> | /// </summary> | ||||
public Uri? GatewayUri = null; | public Uri? GatewayUri = null; | ||||
/// <summary> | |||||
/// SocketFactory gets or sets how a WebSocket will be created. | |||||
/// </summary> | |||||
public SocketFactory SocketFactory { get; set; } = DefaultSocketFactory.Create; | public SocketFactory SocketFactory { get; set; } = DefaultSocketFactory.Create; | ||||
} | } | ||||
} | } |
@@ -1,7 +1,4 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using System.Net.Http.Headers; | |||||
using Discord.Rest; | using Discord.Rest; | ||||
using Discord.Socket; | using Discord.Socket; | ||||
@@ -9,10 +6,15 @@ namespace Discord | |||||
{ | { | ||||
internal interface IDiscordClient | internal interface IDiscordClient | ||||
{ | { | ||||
static IDiscordClient Create(DiscordConfig config) | |||||
static IDiscordClient Create(string token, DiscordConfig? config = default) | |||||
{ | { | ||||
var rest = new DiscordRestApi(config); | |||||
var gateway = new DiscordGatewayApi(config); | |||||
config = config ?? new DiscordConfig(); | |||||
// todo: validate token | |||||
var tokenHeader = AuthenticationHeaderValue.Parse(token); | |||||
var rest = new DiscordRestApi(config, tokenHeader); | |||||
var gateway = new DiscordGatewayApi(config, token); | |||||
return new DiscordClient(config, rest, gateway); | return new DiscordClient(config, rest, gateway); | ||||
} | } | ||||
@@ -0,0 +1,23 @@ | |||||
using System.Net.Http; | |||||
using System.Net.Http.Headers; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.Rest | |||||
{ | |||||
internal sealed class DiscordHttpClientHandler : HttpClientHandler | |||||
{ | |||||
private readonly AuthenticationHeaderValue _token; | |||||
public DiscordHttpClientHandler(AuthenticationHeaderValue token) | |||||
{ | |||||
_token = token; | |||||
} | |||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |||||
{ | |||||
request.Headers.Authorization = _token; | |||||
return base.SendAsync(request, cancellationToken); | |||||
} | |||||
} | |||||
} |
@@ -2,6 +2,7 @@ using System.Text.Json; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Refit; | using Refit; | ||||
using Discord.Rest.Models; | using Discord.Rest.Models; | ||||
using System.Net.Http.Headers; | |||||
// This is essentially a reimplementation of Wumpus.Net.Rest | // This is essentially a reimplementation of Wumpus.Net.Rest | ||||
namespace Discord.Rest | namespace Discord.Rest | ||||
@@ -10,7 +11,7 @@ namespace Discord.Rest | |||||
{ | { | ||||
private readonly IDiscordRestApi _api; | private readonly IDiscordRestApi _api; | ||||
public DiscordRestApi(DiscordConfig config) | |||||
public DiscordRestApi(DiscordConfig config, AuthenticationHeaderValue token) | |||||
{ | { | ||||
var jsonOptions = new JsonSerializerOptions(); | var jsonOptions = new JsonSerializerOptions(); | ||||
var refitSettings = new RefitSettings | var refitSettings = new RefitSettings | ||||
@@ -1,11 +1,8 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Net.Http; | using System.Net.Http; | ||||
using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||
using System.Text; | using System.Text; | ||||
using System.Text.Json; | using System.Text.Json; | ||||
using System.Text.Json.Serialization; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Refit; | using Refit; | ||||
@@ -6,18 +6,23 @@ namespace Discord.Socket | |||||
{ | { | ||||
public class DiscordGatewayApi | public class DiscordGatewayApi | ||||
{ | { | ||||
static readonly Uri DefaultGatewayUri = new Uri("wss://gateway.discord.gg"); | |||||
private readonly DiscordConfig _config; | |||||
private readonly string _token; | |||||
ISocket Socket { get; set; } | |||||
public ISocket Socket { get; set; } | |||||
public DiscordGatewayApi(DiscordConfig config) | |||||
public DiscordGatewayApi(DiscordConfig config, string token) | |||||
{ | { | ||||
_config = config; | |||||
_token = token; | |||||
Socket = config.SocketFactory(OnAborted, OnPacket); | Socket = config.SocketFactory(OnAborted, OnPacket); | ||||
} | } | ||||
public async Task ConnectAsync(Uri? gatewayUri) | public async Task ConnectAsync(Uri? gatewayUri) | ||||
{ | { | ||||
await Socket.ConnectAsync(gatewayUri ?? DefaultGatewayUri, CancellationToken.None).ConfigureAwait(false); | |||||
var baseUri = _config.GatewayUri ?? (gatewayUri ?? DiscordConfig.DefaultGatewayUri); | |||||
await Socket.ConnectAsync(baseUri, CancellationToken.None).ConfigureAwait(false); | |||||
} | } | ||||
public void OnAborted(Exception error) | public void OnAborted(Exception error) | ||||
@@ -88,6 +88,8 @@ namespace Discord.Socket.Providers | |||||
} | } | ||||
State = SocketState.Open; | State = SocketState.Open; | ||||
_receiveTask = ReceiveAsync(); | |||||
// TODO: this should not be expected to fail | // TODO: this should not be expected to fail | ||||
_stateLock.Release(); | _stateLock.Release(); | ||||
openLock.Dispose(); | openLock.Dispose(); | ||||