| @@ -2504,9 +2504,8 @@ | |||
| <param name="result">An emoji.</param> | |||
| </member> | |||
| <member name="M:Discord.Emoji.Parse(System.String)"> | |||
| <summary> Parse an <see cref="T:Discord.Emoji"/> from its raw format. </summary> | |||
| <param name="text">The raw encoding of an emoji. For example: <c>:heart: or ❤</c></param> | |||
| <param name="result">An emoji.</param> | |||
| <summary> Parse an <see cref="T:Discord.Emoji"/> from its raw format.</summary> | |||
| <param name="emojiStr">The raw encoding of an emoji. For example: <c>:heart: or ❤</c></param> | |||
| <exception cref="T:System.FormatException">String is not emoji or unicode!</exception> | |||
| </member> | |||
| <member name="M:Discord.Emoji.GetHashCode"> | |||
| @@ -5040,6 +5039,14 @@ | |||
| Whether this button is disabled or not. | |||
| </summary> | |||
| </member> | |||
| <member name="M:Discord.ButtonComponent.ToBuilder"> | |||
| <summary> | |||
| Turns this button into a button builder. | |||
| </summary> | |||
| <returns> | |||
| A newly created button builder with the same properties as this button. | |||
| </returns> | |||
| </member> | |||
| <member name="T:Discord.ButtonStyle"> | |||
| <summary> | |||
| Represents different styles to use with buttons. You can see an example of the different styles at <see href="https://discord.com/developers/docs/interactions/message-components#buttons-button-styles"/> | |||
| @@ -5746,6 +5753,14 @@ | |||
| Whether this menu is disabled or not. | |||
| </summary> | |||
| </member> | |||
| <member name="M:Discord.SelectMenu.ToBuilder"> | |||
| <summary> | |||
| Turns this select menu into a builder. | |||
| </summary> | |||
| <returns> | |||
| A newly create builder with the same properties as this select menu. | |||
| </returns> | |||
| </member> | |||
| <member name="T:Discord.SelectMenuOption"> | |||
| <summary> | |||
| Represents a choice for a <see cref="T:Discord.SelectMenu"/>. | |||
| @@ -11813,6 +11828,56 @@ | |||
| and an optional reason. | |||
| </summary> | |||
| </member> | |||
| <member name="T:Discord.IRateLimitInfo"> | |||
| <summary> | |||
| Represents a generic ratelimit info. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.IsGlobal"> | |||
| <summary> | |||
| Gets whether or not this ratelimit info is global. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.Limit"> | |||
| <summary> | |||
| Gets the number of requests that can be made. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.Remaining"> | |||
| <summary> | |||
| Gets the number of remaining requests that can be made. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.RetryAfter"> | |||
| <summary> | |||
| Gets the total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond ratelimit precision | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.Reset"> | |||
| <summary> | |||
| Gets the <see cref="T:System.DateTimeOffset"/> at which the rate limit resets | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.ResetAfter"> | |||
| <summary> | |||
| Gets the absolute time when this ratelimit resets. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.Bucket"> | |||
| <summary> | |||
| Gets a unique string denoting the rate limit being encountered (non-inclusive of major parameters in the route path). | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.Lag"> | |||
| <summary> | |||
| Gets the amount of lag for the request. This is used to denote the precise time of when the ratelimit expires. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.IRateLimitInfo.Endpoint"> | |||
| <summary> | |||
| Gets the endpoint that this ratelimit info came from. | |||
| </summary> | |||
| </member> | |||
| <member name="T:Discord.RequestOptions"> | |||
| <summary> | |||
| Represents options that should be used when sending a request. | |||
| @@ -11870,6 +11935,11 @@ | |||
| hosting system is known to have a desynced clock. | |||
| </remarks> | |||
| </member> | |||
| <member name="P:Discord.RequestOptions.RatelimitCallback"> | |||
| <summary> | |||
| Gets or sets the callback to execute regarding ratelimits for this request. | |||
| </summary> | |||
| </member> | |||
| <member name="M:Discord.RequestOptions.#ctor"> | |||
| <summary> | |||
| Initializes a new <see cref="T:Discord.RequestOptions" /> class with the default request timeout set in | |||
| @@ -0,0 +1,59 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| /// <summary> | |||
| /// Represents a generic ratelimit info. | |||
| /// </summary> | |||
| public interface IRateLimitInfo | |||
| { | |||
| /// <summary> | |||
| /// Gets whether or not this ratelimit info is global. | |||
| /// </summary> | |||
| bool IsGlobal { get; } | |||
| /// <summary> | |||
| /// Gets the number of requests that can be made. | |||
| /// </summary> | |||
| int? Limit { get; } | |||
| /// <summary> | |||
| /// Gets the number of remaining requests that can be made. | |||
| /// </summary> | |||
| int? Remaining { get; } | |||
| /// <summary> | |||
| /// Gets the total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond ratelimit precision | |||
| /// </summary> | |||
| int? RetryAfter { get; } | |||
| /// <summary> | |||
| /// Gets the <see cref="DateTimeOffset"/> at which the rate limit resets | |||
| /// </summary> | |||
| DateTimeOffset? Reset { get; } | |||
| /// <summary> | |||
| /// Gets the absolute time when this ratelimit resets. | |||
| /// </summary> | |||
| TimeSpan? ResetAfter { get; } | |||
| /// <summary> | |||
| /// Gets a unique string denoting the rate limit being encountered (non-inclusive of major parameters in the route path). | |||
| /// </summary> | |||
| string Bucket { get; } | |||
| /// <summary> | |||
| /// Gets the amount of lag for the request. This is used to denote the precise time of when the ratelimit expires. | |||
| /// </summary> | |||
| TimeSpan? Lag { get; } | |||
| /// <summary> | |||
| /// Gets the endpoint that this ratelimit info came from. | |||
| /// </summary> | |||
| string Endpoint { get; } | |||
| } | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| using Discord.Net; | |||
| using System; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| @@ -57,6 +59,11 @@ namespace Discord | |||
| /// </remarks> | |||
| public bool? UseSystemClock { get; set; } | |||
| /// <summary> | |||
| /// Gets or sets the callback to execute regarding ratelimits for this request. | |||
| /// </summary> | |||
| public Func<IRateLimitInfo, Task> RatelimitCallback { get; set; } | |||
| internal bool IgnoreState { get; set; } | |||
| internal BucketId BucketId { get; set; } | |||
| internal bool IsClientBucket { get; set; } | |||
| @@ -71,6 +78,17 @@ namespace Discord | |||
| return options.Clone(); | |||
| } | |||
| internal void ExecuteRatelimitCallback(IRateLimitInfo info) | |||
| { | |||
| if (RatelimitCallback != null) | |||
| { | |||
| _ = Task.Run(async () => | |||
| { | |||
| await RatelimitCallback(info); | |||
| }); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// Initializes a new <see cref="RequestOptions" /> class with the default request timeout set in | |||
| /// <see cref="DiscordConfig"/>. | |||
| @@ -149,6 +149,38 @@ | |||
| <member name="M:Discord.Net.Converters.ImageConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)"> | |||
| <exception cref="T:System.InvalidOperationException">Cannot read from image.</exception> | |||
| </member> | |||
| <member name="T:Discord.Net.RateLimitInfo"> | |||
| <summary> | |||
| Represents a REST-Based ratelimit info. | |||
| </summary> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.IsGlobal"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.Limit"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.Remaining"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.RetryAfter"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.Reset"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.ResetAfter"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.Bucket"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.Lag"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Net.RateLimitInfo.Endpoint"> | |||
| <inheritdoc/> | |||
| </member> | |||
| <member name="P:Discord.Rest.BaseDiscordClient.LoginState"> | |||
| <summary> | |||
| Gets the login state of the client. | |||
| @@ -65,7 +65,9 @@ namespace Discord.Net.Queue | |||
| try | |||
| { | |||
| var response = await request.SendAsync().ConfigureAwait(false); | |||
| info = new RateLimitInfo(response.Headers); | |||
| info = new RateLimitInfo(response.Headers, request.Endpoint); | |||
| request.Options.ExecuteRatelimitCallback(info); | |||
| if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300) | |||
| { | |||
| @@ -4,19 +4,42 @@ using System.Globalization; | |||
| namespace Discord.Net | |||
| { | |||
| internal struct RateLimitInfo | |||
| /// <summary> | |||
| /// Represents a REST-Based ratelimit info. | |||
| /// </summary> | |||
| public struct RateLimitInfo : IRateLimitInfo | |||
| { | |||
| /// <inheritdoc/> | |||
| public bool IsGlobal { get; } | |||
| /// <inheritdoc/> | |||
| public int? Limit { get; } | |||
| /// <inheritdoc/> | |||
| public int? Remaining { get; } | |||
| /// <inheritdoc/> | |||
| public int? RetryAfter { get; } | |||
| /// <inheritdoc/> | |||
| public DateTimeOffset? Reset { get; } | |||
| /// <inheritdoc/> | |||
| public TimeSpan? ResetAfter { get; } | |||
| /// <inheritdoc/> | |||
| public string Bucket { get; } | |||
| /// <inheritdoc/> | |||
| public TimeSpan? Lag { get; } | |||
| internal RateLimitInfo(Dictionary<string, string> headers) | |||
| /// <inheritdoc/> | |||
| public string Endpoint { get; } | |||
| internal RateLimitInfo(Dictionary<string, string> headers, string endpoint) | |||
| { | |||
| Endpoint = endpoint; | |||
| IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && | |||
| bool.TryParse(temp, out var isGlobal) && isGlobal; | |||
| Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && | |||