@@ -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"> | ||||
@@ -5029,6 +5028,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"/> | ||||
@@ -5735,6 +5742,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"/>. | ||||
@@ -11802,6 +11817,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. | ||||
@@ -11859,6 +11924,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) && | ||||