From 65afd375022b126ecf12c56ef5704d1c1d60902f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 2 Dec 2018 13:37:25 -0500 Subject: [PATCH] feature: add DiscordSocketRestClient (#1198) * 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 login --- src/Discord.Net.Rest/BaseDiscordClient.cs | 4 ++-- src/Discord.Net.Rest/DiscordRestClient.cs | 2 ++ .../DiscordSocketClient.cs | 4 +++- .../DiscordSocketRestClient.cs | 20 +++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/Discord.Net.WebSocket/DiscordSocketRestClient.cs diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index fc938d04d..1837e38c0 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -65,7 +65,7 @@ namespace Discord.Rest } 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) { @@ -118,7 +118,7 @@ namespace Discord.Rest } finally { _stateLock.Release(); } } - private async Task LogoutInternalAsync() + internal virtual async Task LogoutInternalAsync() { if (LoginState == LoginState.LoggedOut) return; LoginState = LoginState.LoggingOut; diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index f96d5dd0b..29bf89c50 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -24,6 +24,8 @@ namespace Discord.Rest /// /// The configuration to be used with the client. 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) => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 1e431ec92..196aedf47 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -42,9 +42,10 @@ namespace Discord.WebSocket private int _nextAudioId; private DateTimeOffset? _statusSince; private RestApplication _applicationInfo; - private bool _isDisposed; + /// Provides access to a REST-only client with a shared state from this client. + public DiscordSocketRestClient Rest { get; } /// Gets the shard of of this client. public int ShardId { get; } /// Gets the current connection state of this client. @@ -128,6 +129,7 @@ namespace Discord.WebSocket AlwaysDownloadUsers = config.AlwaysDownloadUsers; HandlerTimeout = config.HandlerTimeout; State = new ClientState(0, 0); + Rest = new DiscordSocketRestClient(config, ApiClient); _heartbeatTimes = new ConcurrentQueue(); _stateLock = new SemaphoreSlim(1, 1); diff --git a/src/Discord.Net.WebSocket/DiscordSocketRestClient.cs b/src/Discord.Net.WebSocket/DiscordSocketRestClient.cs new file mode 100644 index 000000000..5107629a8 --- /dev/null +++ b/src/Discord.Net.WebSocket/DiscordSocketRestClient.cs @@ -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."); + } +}