diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index 4b956c06..95dea7ba 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -69,7 +69,17 @@ public class DiscordClient .Pipe(s => TimeSpan.FromSeconds(double.Parse(s, CultureInfo.InvariantCulture))); if (remainingRequestCount <= 0 && resetAfterDelay is not null) - await Task.Delay(resetAfterDelay.Value, innerCancellationToken); + { + var delay = + // Adding a small buffer to the reset time reduces the chance of getting + // rate limited again, because it allows for more requests to be released. + (resetAfterDelay.Value + TimeSpan.FromSeconds(1)) + // Sometimes Discord returns an absurdly high value for the reset time, which + // is not actually enforced by the server. So we cap it at a reasonable value. + .Clamp(TimeSpan.Zero, TimeSpan.FromSeconds(60)); + + await Task.Delay(delay, innerCancellationToken); + } return response; }, cancellationToken); diff --git a/DiscordChatExporter.Core/Utils/Extensions/TimeSpanExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/TimeSpanExtensions.cs new file mode 100644 index 00000000..2f316245 --- /dev/null +++ b/DiscordChatExporter.Core/Utils/Extensions/TimeSpanExtensions.cs @@ -0,0 +1,17 @@ +using System; + +namespace DiscordChatExporter.Core.Utils.Extensions; + +public static class TimeSpanExtensions +{ + public static TimeSpan Clamp(this TimeSpan value, TimeSpan min, TimeSpan max) + { + if (value < min) + return min; + + if (value > max) + return max; + + return value; + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Utils/Http.cs b/DiscordChatExporter.Core/Utils/Http.cs index b566b661..9b8c3f21 100644 --- a/DiscordChatExporter.Core/Utils/Http.cs +++ b/DiscordChatExporter.Core/Utils/Http.cs @@ -41,7 +41,10 @@ public static class Http { // If rate-limited, use retry-after header as the guide if (result.Result.Headers.RetryAfter?.Delta is { } retryAfter) - return retryAfter; + { + // Add some buffer just in case + return retryAfter + TimeSpan.FromSeconds(1); + } return TimeSpan.FromSeconds(Math.Pow(2, i) + 1); },