* feature: add DiscordSocketRestClient this resolves #803. Users can access a DiscordSocketRestClient from the new `DiscordSocketClient.Rest` property. DiscordSocketRestClient is a wrapper over DiscordRestClient with certain state-modifying methods, such as Login/Logout disabled, to prevent users from breaking the client state. DiscordSocketRestClient uses the same API client as the DiscordSocketClient, allowing for shared ratelimiting - meaning users can now force HTTP requests without needing to wory about running into 429s. * fix: disallow users from bypassing shadowed loginpull/1214/head
@@ -65,7 +65,7 @@ namespace Discord.Rest | |||||
} | } | ||||
finally { _stateLock.Release(); } | finally { _stateLock.Release(); } | ||||
} | } | ||||
private async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken) | |||||
internal virtual async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken) | |||||
{ | { | ||||
if (_isFirstLogin) | if (_isFirstLogin) | ||||
{ | { | ||||
@@ -118,7 +118,7 @@ namespace Discord.Rest | |||||
} | } | ||||
finally { _stateLock.Release(); } | finally { _stateLock.Release(); } | ||||
} | } | ||||
private async Task LogoutInternalAsync() | |||||
internal virtual async Task LogoutInternalAsync() | |||||
{ | { | ||||
if (LoginState == LoginState.LoggedOut) return; | if (LoginState == LoginState.LoggedOut) return; | ||||
LoginState = LoginState.LoggingOut; | LoginState = LoginState.LoggingOut; | ||||
@@ -24,6 +24,8 @@ namespace Discord.Rest | |||||
/// </summary> | /// </summary> | ||||
/// <param name="config">The configuration to be used with the client.</param> | /// <param name="config">The configuration to be used with the client.</param> | ||||
public DiscordRestClient(DiscordRestConfig config) : base(config, CreateApiClient(config)) { } | public DiscordRestClient(DiscordRestConfig config) : base(config, CreateApiClient(config)) { } | ||||
// used for socket client rest access | |||||
internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient api) : base(config, api) { } | |||||
private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | ||||
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); | => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); | ||||
@@ -42,9 +42,10 @@ namespace Discord.WebSocket | |||||
private int _nextAudioId; | private int _nextAudioId; | ||||
private DateTimeOffset? _statusSince; | private DateTimeOffset? _statusSince; | ||||
private RestApplication _applicationInfo; | private RestApplication _applicationInfo; | ||||
private bool _isDisposed; | private bool _isDisposed; | ||||
/// <summary> Provides access to a REST-only client with a shared state from this client. </summary> | |||||
public DiscordSocketRestClient Rest { get; } | |||||
/// <summary> Gets the shard of of this client. </summary> | /// <summary> Gets the shard of of this client. </summary> | ||||
public int ShardId { get; } | public int ShardId { get; } | ||||
/// <summary> Gets the current connection state of this client. </summary> | /// <summary> Gets the current connection state of this client. </summary> | ||||
@@ -128,6 +129,7 @@ namespace Discord.WebSocket | |||||
AlwaysDownloadUsers = config.AlwaysDownloadUsers; | AlwaysDownloadUsers = config.AlwaysDownloadUsers; | ||||
HandlerTimeout = config.HandlerTimeout; | HandlerTimeout = config.HandlerTimeout; | ||||
State = new ClientState(0, 0); | State = new ClientState(0, 0); | ||||
Rest = new DiscordSocketRestClient(config, ApiClient); | |||||
_heartbeatTimes = new ConcurrentQueue<long>(); | _heartbeatTimes = new ConcurrentQueue<long>(); | ||||
_stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
@@ -0,0 +1,20 @@ | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using Discord.Rest; | |||||
namespace Discord.WebSocket | |||||
{ | |||||
public class DiscordSocketRestClient : DiscordRestClient | |||||
{ | |||||
internal DiscordSocketRestClient(DiscordRestConfig config, API.DiscordRestApiClient api) : base(config, api) { } | |||||
public new Task LoginAsync(TokenType tokenType, string token, bool validateToken = true) | |||||
=> throw new NotSupportedException("The Socket REST wrapper cannot be used to log in or out."); | |||||
internal override Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken) | |||||
=> throw new NotSupportedException("The Socket REST wrapper cannot be used to log in or out."); | |||||
public new Task LogoutAsync() | |||||
=> throw new NotSupportedException("The Socket REST wrapper cannot be used to log in or out."); | |||||
internal override Task LogoutInternalAsync() | |||||
=> throw new NotSupportedException("The Socket REST wrapper cannot be used to log in or out."); | |||||
} | |||||
} |