From a9b0569ac66d066119b857eab099125a35e878f7 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Tue, 13 Aug 2019 22:21:07 -0700 Subject: [PATCH 1/2] support X-RateLimit-Reset sending integer or float values This changes the way that the X-RateLimit-Request header is parsed, so that it will work with both integer seconds and float values with seconds and milliseconds --- src/Discord.Net.Rest/Net/RateLimitInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Rest/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs index d31cc5cddf..f193ce6ec7 100644 --- a/src/Discord.Net.Rest/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -21,7 +21,7 @@ internal RateLimitInfo(Dictionary headers) Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && int.TryParse(temp, out var remaining) ? remaining : (int?)null; Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && - int.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeSeconds(reset) : (DateTimeOffset?)null; + float.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeMilliseconds((long)(reset * 1000)) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) && int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; Lag = headers.TryGetValue("Date", out temp) && From 0c7d59040803ff12319579e19512c0c6cf76f882 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Tue, 13 Aug 2019 23:05:29 -0700 Subject: [PATCH 2/2] Add RateLimitPrecision enum, set X-RateLimit-Precision Adds the RateLimitPrecision enum, with Second and Millisecond values. (Do we want to use an extension method to convert it into a string, or is ToString().ToLower() fine?) Adds RateLimitPrecision as a parameter to DiscordRestApiClient, and to DiscordConfig so that it can set the X-RateLimit-Precision header. --- src/Discord.Net.Core/DiscordConfig.cs | 16 ++++++++++++++-- src/Discord.Net.Core/RateLimitPrecision.cs | 18 ++++++++++++++++++ src/Discord.Net.Rest/DiscordRestApiClient.cs | 7 +++++-- src/Discord.Net.WebSocket/BaseSocketClient.cs | 3 ++- .../DiscordShardedClient.cs | 3 ++- .../DiscordSocketApiClient.cs | 5 +++-- .../DiscordSocketClient.cs | 3 ++- 7 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 src/Discord.Net.Core/RateLimitPrecision.cs diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index d0c1c3a84b..9381cb255d 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -36,7 +36,7 @@ public class DiscordConfig typeof(DiscordConfig).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion ?? typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ?? "Unknown"; - + /// /// Gets the user agent that Discord.Net uses in its clients. /// @@ -123,7 +123,7 @@ public class DiscordConfig /// The currently set . /// public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry; - + /// /// Gets or sets the minimum log level severity that will be sent to the Log event. /// @@ -140,5 +140,17 @@ public class DiscordConfig /// the API version it uses on startup. /// internal bool DisplayInitialLog { get; set; } = true; + + /// + /// Gets or sets the level of precision of the rate limit reset response. + /// + /// + /// If set to , this value will be rounded up to the + /// nearest second. + /// + /// + /// The currently set . + /// + public RateLimitPrecision RateLimitPrecision { get; set; } = RateLimitPrecision.Second; } } diff --git a/src/Discord.Net.Core/RateLimitPrecision.cs b/src/Discord.Net.Core/RateLimitPrecision.cs new file mode 100644 index 0000000000..fe3c1b90ef --- /dev/null +++ b/src/Discord.Net.Core/RateLimitPrecision.cs @@ -0,0 +1,18 @@ +namespace Discord +{ + /// + /// Specifies the level of precision to request in the rate limit + /// response header. + /// + public enum RateLimitPrecision + { + /// + /// Specifies precision rounded up to the nearest whole second + /// + Second, + /// + /// Specifies precision rounded to the nearest millisecond. + /// + Millisecond + } +} diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 2757c0f340..c76f318350 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -45,17 +45,19 @@ internal class DiscordRestApiClient : IDisposable internal string AuthToken { get; private set; } internal IRestClient RestClient { get; private set; } internal ulong? CurrentUserId { get; set; } - + public RateLimitPrecision RateLimitPrecision { get; private set; } + internal JsonSerializer Serializer => _serializer; /// Unknown OAuth token type. public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, - JsonSerializer serializer = null) + JsonSerializer serializer = null, RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second) { _restClientProvider = restClientProvider; UserAgent = userAgent; DefaultRetryMode = defaultRetryMode; _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; + RateLimitPrecision = rateLimitPrecision; RequestQueue = new RequestQueue(); _stateLock = new SemaphoreSlim(1, 1); @@ -71,6 +73,7 @@ internal void SetBaseUrl(string baseUrl) RestClient.SetHeader("accept", "*/*"); RestClient.SetHeader("user-agent", UserAgent); RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); + RestClient.SetHeader("X-RateLimit-Precision", RateLimitPrecision.ToString().ToLower()); } /// Unknown OAuth token type. internal static string GetPrefixedToken(TokenType tokenType, string token) diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index c33d4d84f8..5f7c48417f 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -80,7 +80,8 @@ public abstract partial class BaseSocketClient : BaseDiscordClient, IDiscordClie internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) : base(config, client) => BaseConfig = config; private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); + => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, + rateLimitPrecision: config.RateLimitPrecision); /// /// Gets a Discord application information for the logged-in user. diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index bac06a26db..0877abfd93 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -85,7 +85,8 @@ private DiscordShardedClient(int[] ids, DiscordSocketConfig config, API.DiscordS } } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, + rateLimitPrecision: config.RateLimitPrecision); internal override async Task OnLoginAsync(TokenType tokenType, string token) { diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 894ae66a85..d0bc476865 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -38,8 +38,9 @@ internal class DiscordSocketApiClient : DiscordRestApiClient public ConnectionState ConnectionState { get; private set; } public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, - string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) - : base(restClientProvider, userAgent, defaultRetryMode, serializer) + string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, + RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second) + : base(restClientProvider, userAgent, defaultRetryMode, serializer, rateLimitPrecision) { _gatewayUrl = url; if (url != null) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 70a135c010..b25f611565 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -176,7 +176,8 @@ private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClie _largeGuilds = new ConcurrentQueue(); } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost); + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost, + rateLimitPrecision: config.RateLimitPrecision); /// internal override void Dispose(bool disposing) {