| @@ -2504,9 +2504,8 @@ | |||||
| <param name="result">An emoji.</param> | <param name="result">An emoji.</param> | ||||
| </member> | </member> | ||||
| <member name="M:Discord.Emoji.Parse(System.String)"> | <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> | <exception cref="T:System.FormatException">String is not emoji or unicode!</exception> | ||||
| </member> | </member> | ||||
| <member name="M:Discord.Emoji.GetHashCode"> | <member name="M:Discord.Emoji.GetHashCode"> | ||||
| @@ -5040,6 +5039,14 @@ | |||||
| Whether this button is disabled or not. | Whether this button is disabled or not. | ||||
| </summary> | </summary> | ||||
| </member> | </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"> | <member name="T:Discord.ButtonStyle"> | ||||
| <summary> | <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"/> | 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. | Whether this menu is disabled or not. | ||||
| </summary> | </summary> | ||||
| </member> | </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"> | <member name="T:Discord.SelectMenuOption"> | ||||
| <summary> | <summary> | ||||
| Represents a choice for a <see cref="T:Discord.SelectMenu"/>. | Represents a choice for a <see cref="T:Discord.SelectMenu"/>. | ||||
| @@ -11813,6 +11828,56 @@ | |||||
| and an optional reason. | and an optional reason. | ||||
| </summary> | </summary> | ||||
| </member> | </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"> | <member name="T:Discord.RequestOptions"> | ||||
| <summary> | <summary> | ||||
| Represents options that should be used when sending a request. | Represents options that should be used when sending a request. | ||||
| @@ -11870,6 +11935,11 @@ | |||||
| hosting system is known to have a desynced clock. | hosting system is known to have a desynced clock. | ||||
| </remarks> | </remarks> | ||||
| </member> | </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"> | <member name="M:Discord.RequestOptions.#ctor"> | ||||
| <summary> | <summary> | ||||
| Initializes a new <see cref="T:Discord.RequestOptions" /> class with the default request timeout set in | 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 Discord.Net; | ||||
| using System; | |||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -57,6 +59,11 @@ namespace Discord | |||||
| /// </remarks> | /// </remarks> | ||||
| public bool? UseSystemClock { get; set; } | 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 bool IgnoreState { get; set; } | ||||
| internal BucketId BucketId { get; set; } | internal BucketId BucketId { get; set; } | ||||
| internal bool IsClientBucket { get; set; } | internal bool IsClientBucket { get; set; } | ||||
| @@ -71,6 +78,17 @@ namespace Discord | |||||
| return options.Clone(); | return options.Clone(); | ||||
| } | } | ||||
| internal void ExecuteRatelimitCallback(IRateLimitInfo info) | |||||
| { | |||||
| if (RatelimitCallback != null) | |||||
| { | |||||
| _ = Task.Run(async () => | |||||
| { | |||||
| await RatelimitCallback(info); | |||||
| }); | |||||
| } | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a new <see cref="RequestOptions" /> class with the default request timeout set in | /// Initializes a new <see cref="RequestOptions" /> class with the default request timeout set in | ||||
| /// <see cref="DiscordConfig"/>. | /// <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)"> | <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> | <exception cref="T:System.InvalidOperationException">Cannot read from image.</exception> | ||||
| </member> | </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"> | <member name="P:Discord.Rest.BaseDiscordClient.LoginState"> | ||||
| <summary> | <summary> | ||||
| Gets the login state of the client. | Gets the login state of the client. | ||||
| @@ -65,7 +65,9 @@ namespace Discord.Net.Queue | |||||
| try | try | ||||
| { | { | ||||
| var response = await request.SendAsync().ConfigureAwait(false); | 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) | if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300) | ||||
| { | { | ||||
| @@ -4,19 +4,42 @@ using System.Globalization; | |||||
| namespace Discord.Net | namespace Discord.Net | ||||
| { | { | ||||
| internal struct RateLimitInfo | |||||
| /// <summary> | |||||
| /// Represents a REST-Based ratelimit info. | |||||
| /// </summary> | |||||
| public struct RateLimitInfo : IRateLimitInfo | |||||
| { | { | ||||
| /// <inheritdoc/> | |||||
| public bool IsGlobal { get; } | public bool IsGlobal { get; } | ||||
| /// <inheritdoc/> | |||||
| public int? Limit { get; } | public int? Limit { get; } | ||||
| /// <inheritdoc/> | |||||
| public int? Remaining { get; } | public int? Remaining { get; } | ||||
| /// <inheritdoc/> | |||||
| public int? RetryAfter { get; } | public int? RetryAfter { get; } | ||||
| /// <inheritdoc/> | |||||
| public DateTimeOffset? Reset { get; } | public DateTimeOffset? Reset { get; } | ||||
| /// <inheritdoc/> | |||||
| public TimeSpan? ResetAfter { get; } | public TimeSpan? ResetAfter { get; } | ||||
| /// <inheritdoc/> | |||||
| public string Bucket { get; } | public string Bucket { get; } | ||||
| /// <inheritdoc/> | |||||
| public TimeSpan? Lag { get; } | 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) && | IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && | ||||
| bool.TryParse(temp, out var isGlobal) && isGlobal; | bool.TryParse(temp, out var isGlobal) && isGlobal; | ||||
| Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && | Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && | ||||