Removed old bucket system, cleaned up api calls. Fixed compile errors.
This commit is contained in:
@@ -113,7 +113,7 @@ namespace Discord.API
|
||||
_authToken = token;
|
||||
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken));
|
||||
|
||||
CurrentUser = await GetMyUserAsync();
|
||||
CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true });
|
||||
|
||||
LoginState = LoginState.LoggedIn;
|
||||
}
|
||||
@@ -155,75 +155,50 @@ namespace Discord.API
|
||||
internal virtual Task ConnectInternalAsync() => Task.CompletedTask;
|
||||
internal virtual Task DisconnectInternalAsync() => Task.CompletedTask;
|
||||
|
||||
//REST
|
||||
public Task SendAsync(string method, string endpoint,
|
||||
GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null)
|
||||
=> SendInternalAsync(method, endpoint, null, true, BucketGroup.Global, (int)bucket, 0, ignoreState, options);
|
||||
public Task SendAsync(string method, string endpoint, object payload,
|
||||
GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null)
|
||||
=> SendInternalAsync(method, endpoint, payload, true, BucketGroup.Global, (int)bucket, 0, ignoreState, options);
|
||||
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint,
|
||||
GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) where TResponse : class
|
||||
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Global, (int)bucket, 0, ignoreState, options).ConfigureAwait(false));
|
||||
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket =
|
||||
GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) where TResponse : class
|
||||
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, payload, false, BucketGroup.Global, (int)bucket, 0, ignoreState, options).ConfigureAwait(false));
|
||||
|
||||
public Task SendAsync(string method, string endpoint,
|
||||
GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null)
|
||||
=> SendInternalAsync(method, endpoint, null, true, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options);
|
||||
public Task SendAsync(string method, string endpoint, object payload,
|
||||
GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null)
|
||||
=> SendInternalAsync(method, endpoint, payload, true, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options);
|
||||
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint,
|
||||
GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) where TResponse : class
|
||||
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options).ConfigureAwait(false));
|
||||
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload,
|
||||
GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) where TResponse : class
|
||||
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, payload, false, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options).ConfigureAwait(false));
|
||||
|
||||
//REST - Multipart
|
||||
public Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
|
||||
GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null)
|
||||
=> SendMultipartInternalAsync(method, endpoint, multipartArgs, true, BucketGroup.Global, (int)bucket, 0, options);
|
||||
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
|
||||
GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null) where TResponse : class
|
||||
=> DeserializeJson<TResponse>(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Global, (int)bucket, 0, options).ConfigureAwait(false));
|
||||
|
||||
public Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
|
||||
GuildBucket bucket, ulong guildId, RequestOptions options = null)
|
||||
=> SendMultipartInternalAsync(method, endpoint, multipartArgs, true, BucketGroup.Guild, (int)bucket, guildId, options);
|
||||
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
|
||||
GuildBucket bucket, ulong guildId, RequestOptions options = null) where TResponse : class
|
||||
=> DeserializeJson<TResponse>(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Guild, (int)bucket, guildId, options).ConfigureAwait(false));
|
||||
|
||||
//Core
|
||||
private async Task<Stream> SendInternalAsync(string method, string endpoint, object payload, bool headerOnly,
|
||||
BucketGroup group, int bucketId, ulong guildId, bool ignoreState, RequestOptions options = null)
|
||||
public async Task SendAsync(string method, string endpoint, RequestOptions options = null)
|
||||
{
|
||||
if (!ignoreState)
|
||||
options.HeaderOnly = true;
|
||||
var request = new RestRequest(_restClient, method, endpoint, options);
|
||||
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendJsonAsync(string method, string endpoint, object payload, RequestOptions options = null)
|
||||
{
|
||||
options.HeaderOnly = true;
|
||||
var json = payload != null ? SerializeJson(payload) : null;
|
||||
var request = new JsonRestRequest(_restClient, method, endpoint, json, options);
|
||||
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, RequestOptions options = null)
|
||||
{
|
||||
options.HeaderOnly = true;
|
||||
var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options);
|
||||
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, RequestOptions options = null) where TResponse : class
|
||||
{
|
||||
var request = new RestRequest(_restClient, method, endpoint, options);
|
||||
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false));
|
||||
}
|
||||
public async Task<TResponse> SendJsonAsync<TResponse>(string method, string endpoint, object payload, RequestOptions options = null) where TResponse : class
|
||||
{
|
||||
var json = payload != null ? SerializeJson(payload) : null;
|
||||
var request = new JsonRestRequest(_restClient, method, endpoint, json, options);
|
||||
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false));
|
||||
}
|
||||
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, RequestOptions options = null)
|
||||
{
|
||||
var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options);
|
||||
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false));
|
||||
}
|
||||
|
||||
private async Task<Stream> SendInternalAsync(string method, string endpoint, RestRequest request)
|
||||
{
|
||||
if (!request.Options.IgnoreState)
|
||||
CheckState();
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
string json = null;
|
||||
if (payload != null)
|
||||
json = SerializeJson(payload);
|
||||
var responseStream = await RequestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, json, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false);
|
||||
stopwatch.Stop();
|
||||
|
||||
double milliseconds = ToMilliseconds(stopwatch);
|
||||
await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false);
|
||||
|
||||
return responseStream;
|
||||
}
|
||||
private async Task<Stream> SendMultipartInternalAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly,
|
||||
BucketGroup group, int bucketId, ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
CheckState();
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var responseStream = await RequestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, multipartArgs, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false);
|
||||
int bytes = headerOnly ? 0 : (int)responseStream.Length;
|
||||
var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false);
|
||||
stopwatch.Stop();
|
||||
|
||||
double milliseconds = ToMilliseconds(stopwatch);
|
||||
@@ -235,6 +210,7 @@ namespace Discord.API
|
||||
//Auth
|
||||
public async Task ValidateTokenAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
await SendAsync("GET", "auth/login", options: options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -242,6 +218,7 @@ namespace Discord.API
|
||||
public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -253,6 +230,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -266,6 +244,7 @@ namespace Discord.API
|
||||
public async Task<IReadOnlyCollection<Channel>> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<Channel>>("GET", $"guilds/{guildId}/channels", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -275,12 +254,14 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate));
|
||||
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Channel> DeleteChannelAsync(ulong channelId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Channel>("DELETE", $"channels/{channelId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -290,8 +271,9 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
|
||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, ModifyTextChannelParams args, RequestOptions options = null)
|
||||
{
|
||||
@@ -299,8 +281,9 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
|
||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, ModifyVoiceChannelParams args, RequestOptions options = null)
|
||||
{
|
||||
@@ -310,13 +293,15 @@ namespace Discord.API
|
||||
Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate));
|
||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
|
||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable<ModifyGuildChannelsParams> args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
var channels = args.ToArray();
|
||||
switch (channels.Length)
|
||||
@@ -327,7 +312,7 @@ namespace Discord.API
|
||||
await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
await SendAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false);
|
||||
await SendJsonAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -337,6 +322,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(messageId, 0, nameof(messageId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -350,6 +336,7 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit));
|
||||
Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch);
|
||||
ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null;
|
||||
@@ -383,13 +370,15 @@ namespace Discord.API
|
||||
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));
|
||||
if (args.Content.Length > DiscordConfig.MaxMessageSize)
|
||||
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Message>("POST", $"channels/{channelId}/messages", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
if (args.Content.GetValueOrDefault(null) == null)
|
||||
args.Content = "";
|
||||
@@ -401,12 +390,13 @@ namespace Discord.API
|
||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
|
||||
}
|
||||
|
||||
return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false);
|
||||
return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(messageId, 0, nameof(messageId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -414,9 +404,9 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
|
||||
Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds));
|
||||
Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
switch (args.MessageIds.Length)
|
||||
{
|
||||
@@ -426,7 +416,7 @@ namespace Discord.API
|
||||
await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false);
|
||||
await SendJsonAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -441,19 +431,22 @@ namespace Discord.API
|
||||
if (args.Content.Value.Length > DiscordConfig.MaxMessageSize)
|
||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
|
||||
}
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(messageId, 0, nameof(messageId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -464,13 +457,15 @@ namespace Discord.API
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(targetId, 0, nameof(targetId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("PUT", $"channels/{channelId}/permissions/{targetId}", args, options: options).ConfigureAwait(false);
|
||||
await SendJsonAsync("PUT", $"channels/{channelId}/permissions/{targetId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task DeleteChannelPermissionAsync(ulong channelId, ulong targetId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(targetId, 0, nameof(targetId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"channels/{channelId}/permissions/{targetId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -480,6 +475,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.GreaterThan(channelId, 0, nameof(channelId));
|
||||
Preconditions.GreaterThan(messageId, 0, nameof(messageId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("PUT", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false);
|
||||
|
||||
@@ -488,12 +484,14 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(messageId, 0, nameof(messageId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<Message>> GetPinsAsync(ulong channelId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<Message>>("GET", $"channels/{channelId}/pins", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -503,6 +501,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.GreaterThan(channelId, 0, nameof(channelId));
|
||||
Preconditions.GreaterThan(userId, 0, nameof(userId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("PUT", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false);
|
||||
|
||||
@@ -511,6 +510,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -519,6 +519,7 @@ namespace Discord.API
|
||||
public async Task<Guild> GetGuildAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -531,18 +532,21 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));
|
||||
Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Guild>("POST", "guilds", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Guild>("POST", "guilds", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Guild>("DELETE", $"guilds/{guildId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Guild> LeaveGuildAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Guild>("DELETE", $"users/@me/guilds/{guildId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -555,30 +559,34 @@ namespace Discord.API
|
||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
|
||||
Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId));
|
||||
Preconditions.NotNull(args.RegionId, nameof(args.RegionId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<GetGuildPruneCountResponse> BeginGuildPruneAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.Days, 0, nameof(args.Days));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<GetGuildPruneCountResponse>("POST", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<GetGuildPruneCountResponse>("POST", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<GetGuildPruneCountResponse> GetGuildPruneCountAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.Days, 0, nameof(args.Days));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<GetGuildPruneCountResponse>("GET", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<GetGuildPruneCountResponse>("GET", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//Guild Bans
|
||||
public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<Ban>>("GET", $"guilds/{guildId}/bans", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -588,13 +596,15 @@ namespace Discord.API
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false);
|
||||
await SendJsonAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"guilds/{guildId}/bans/{userId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -603,6 +613,7 @@ namespace Discord.API
|
||||
public async Task<GuildEmbed> GetGuildEmbedAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -614,14 +625,16 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<GuildEmbed>("PATCH", $"guilds/{guildId}/embed", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<GuildEmbed>("PATCH", $"guilds/{guildId}/embed", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//Guild Integrations
|
||||
public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<Integration>>("GET", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -630,6 +643,7 @@ namespace Discord.API
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.NotEqual(args.Id, 0, nameof(args.Id));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -637,6 +651,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Integration>("DELETE", $"guilds/{guildId}/integrations/{integrationId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -647,13 +662,15 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior));
|
||||
Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Integration> SyncGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations/{integrationId}/sync", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -662,6 +679,7 @@ namespace Discord.API
|
||||
public async Task<Invite> GetInviteAsync(string inviteId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
//Remove trailing slash
|
||||
if (inviteId[inviteId.Length - 1] == '/')
|
||||
@@ -680,12 +698,14 @@ namespace Discord.API
|
||||
public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(channelId, 0, nameof(channelId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -695,18 +715,21 @@ namespace Discord.API
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge));
|
||||
Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Invite> DeleteInviteAsync(string inviteCode, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Invite>("DELETE", $"invites/{inviteCode}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task AcceptInviteAsync(string inviteCode, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("POST", $"invites/{inviteCode}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -716,6 +739,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -730,6 +754,7 @@ namespace Discord.API
|
||||
Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit));
|
||||
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit));
|
||||
Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
int limit = args.Limit.GetValueOrDefault(int.MaxValue);
|
||||
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0);
|
||||
@@ -741,6 +766,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"guilds/{guildId}/members/{userId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -749,6 +775,7 @@ namespace Discord.API
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
bool isCurrentUser = userId == CurrentUser.Id;
|
||||
|
||||
@@ -760,7 +787,7 @@ namespace Discord.API
|
||||
}
|
||||
if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified)
|
||||
{
|
||||
await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false);
|
||||
await SendJsonAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,12 +795,14 @@ namespace Discord.API
|
||||
public async Task<IReadOnlyCollection<Role>> GetGuildRolesAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<Role>>("GET", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Role> CreateGuildRoleAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Role>("POST", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -781,6 +810,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotEqual(roleId, 0, nameof(roleId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("DELETE", $"guilds/{guildId}/roles/{roleId}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -792,13 +822,15 @@ namespace Discord.API
|
||||
Preconditions.AtLeast(args.Color, 0, nameof(args.Color));
|
||||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
|
||||
Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<Role>> ModifyGuildRolesAsync(ulong guildId, IEnumerable<ModifyGuildRolesParams> args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
var roles = args.ToArray();
|
||||
switch (roles.Length)
|
||||
@@ -808,7 +840,7 @@ namespace Discord.API
|
||||
case 1:
|
||||
return ImmutableArray.Create(await ModifyGuildRoleAsync(guildId, roles[0].Id, roles[0]).ConfigureAwait(false));
|
||||
default:
|
||||
return await SendAsync<IReadOnlyCollection<Role>>("PATCH", $"guilds/{guildId}/roles", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<IReadOnlyCollection<Role>>("PATCH", $"guilds/{guildId}/roles", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,6 +848,7 @@ namespace Discord.API
|
||||
public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(userId, 0, nameof(userId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -827,6 +860,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(username, nameof(username));
|
||||
Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -839,6 +873,7 @@ namespace Discord.API
|
||||
{
|
||||
Preconditions.NotNullOrEmpty(query, nameof(query));
|
||||
Preconditions.AtLeast(limit, 0, nameof(limit));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<User>>("GET", $"users?q={Uri.EscapeDataString(query)}&limit={limit}", options: options).ConfigureAwait(false);
|
||||
}
|
||||
@@ -846,54 +881,64 @@ namespace Discord.API
|
||||
//Current User/DMs
|
||||
public async Task<User> GetMyUserAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
return await SendAsync<User>("GET", "users/@me", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<Connection>> GetMyConnectionsAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
return await SendAsync<IReadOnlyCollection<Connection>>("GET", "users/@me/connections", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<Channel>> GetMyPrivateChannelsAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
return await SendAsync<IReadOnlyCollection<Channel>>("GET", "users/@me/channels", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<UserGuild>> GetMyGuildsAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", "users/@me/guilds", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Application> GetMyApplicationAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task ModifyMyNickAsync(ulong guildId, ModifyCurrentUserNickParams args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.NotNull(args.Nickname, nameof(args.Nickname));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
await SendAsync("PATCH", $"guilds/{guildId}/members/@me/nick", args, options: options).ConfigureAwait(false);
|
||||
await SendJsonAsync("PATCH", $"guilds/{guildId}/members/@me/nick", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotNull(args, nameof(args));
|
||||
Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false);
|
||||
return await SendJsonAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//Voice Regions
|
||||
public async Task<IReadOnlyCollection<VoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null)
|
||||
{
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", "voice/regions", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<IReadOnlyCollection<VoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null)
|
||||
{
|
||||
Preconditions.NotEqual(guildId, 0, nameof(guildId));
|
||||
options = RequestOptions.CreateOrClone(options);
|
||||
|
||||
return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", $"guilds/{guildId}/regions", options: options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public sealed class Bucket
|
||||
{
|
||||
/// <summary> Gets the unique identifier for this bucket. </summary>
|
||||
public string Id { get; }
|
||||
/// <summary> Gets the name of this bucket. </summary>
|
||||
public string Name { get; }
|
||||
/// <summary> Gets the amount of requests that may be sent per window. </summary>
|
||||
public int WindowCount { get; }
|
||||
/// <summary> Gets the length of this bucket's window, in seconds. </summary>
|
||||
public int WindowSeconds { get; }
|
||||
/// <summary> Gets the type of account this bucket affects. </summary>
|
||||
public BucketTarget Target { get; }
|
||||
/// <summary> Gets this bucket's parent. </summary>
|
||||
public GlobalBucket? Parent { get; }
|
||||
|
||||
internal Bucket(string id, int windowCount, int windowSeconds, BucketTarget target, GlobalBucket? parent = null)
|
||||
: this(id, id, windowCount, windowSeconds, target, parent) { }
|
||||
internal Bucket(string id, string name, int windowCount, int windowSeconds, BucketTarget target, GlobalBucket? parent = null)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
WindowCount = windowCount;
|
||||
WindowSeconds = windowSeconds;
|
||||
Target = target;
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public enum BucketGroup
|
||||
{
|
||||
Global,
|
||||
Guild,
|
||||
Channel
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public enum BucketTarget
|
||||
{
|
||||
Client,
|
||||
Bot,
|
||||
Both
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public enum ChannelBucket
|
||||
{
|
||||
SendEditMessage,
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public enum GlobalBucket
|
||||
{
|
||||
GeneralRest,
|
||||
DirectMessage,
|
||||
SendEditMessage,
|
||||
|
||||
GeneralGateway,
|
||||
UpdateStatus,
|
||||
|
||||
GeneralRpc
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public enum GuildBucket
|
||||
{
|
||||
SendEditMessage,
|
||||
DeleteMessage,
|
||||
DeleteMessages,
|
||||
ModifyMember,
|
||||
Nickname
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -10,91 +8,23 @@ namespace Discord.Net.Queue
|
||||
{
|
||||
public class RequestQueue
|
||||
{
|
||||
public event Func<string, Bucket, int, Task> RateLimitTriggered;
|
||||
|
||||
private readonly static ImmutableDictionary<GlobalBucket, Bucket> _globalLimits;
|
||||
private readonly static ImmutableDictionary<GuildBucket, Bucket> _guildLimits;
|
||||
private readonly static ImmutableDictionary<ChannelBucket, Bucket> _channelLimits;
|
||||
public event Func<string, RequestQueueBucket, int?, Task> RateLimitTriggered;
|
||||
|
||||
private readonly SemaphoreSlim _lock;
|
||||
private readonly RequestQueueBucket[] _globalBuckets;
|
||||
private readonly ConcurrentDictionary<ulong, RequestQueueBucket>[] _guildBuckets;
|
||||
private readonly ConcurrentDictionary<ulong, RequestQueueBucket>[] _channelBuckets;
|
||||
private readonly ConcurrentDictionary<string, RequestQueueBucket> _buckets;
|
||||
private CancellationTokenSource _clearToken;
|
||||
private CancellationToken _parentToken;
|
||||
private CancellationToken _cancelToken;
|
||||
|
||||
static RequestQueue()
|
||||
{
|
||||
_globalLimits = new Dictionary<GlobalBucket, Bucket>
|
||||
{
|
||||
//REST
|
||||
[GlobalBucket.GeneralRest] = new Bucket(null, "rest", 0, 0, BucketTarget.Both), //No Limit
|
||||
//[GlobalBucket.Login] = new BucketDefinition(1, 1),
|
||||
[GlobalBucket.DirectMessage] = new Bucket("bot:msg:dm", 5, 5, BucketTarget.Bot),
|
||||
[GlobalBucket.SendEditMessage] = new Bucket("bot:msg:global", 50, 10, BucketTarget.Bot),
|
||||
//[GlobalBucket.Username] = new Bucket("bot:msg:global", 2, 3600, BucketTarget.Both),
|
||||
|
||||
//Gateway
|
||||
[GlobalBucket.GeneralGateway] = new Bucket(null, "gateway", 120, 60, BucketTarget.Both),
|
||||
[GlobalBucket.UpdateStatus] = new Bucket(null, "status", 5, 1, BucketTarget.Both, GlobalBucket.GeneralGateway),
|
||||
|
||||
//Rpc
|
||||
[GlobalBucket.GeneralRpc] = new Bucket(null, "rpc", 120, 60, BucketTarget.Both)
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
_guildLimits = new Dictionary<GuildBucket, Bucket>
|
||||
{
|
||||
//REST
|
||||
[GuildBucket.SendEditMessage] = new Bucket("bot:msg:server", 5, 5, BucketTarget.Bot, GlobalBucket.SendEditMessage),
|
||||
[GuildBucket.DeleteMessage] = new Bucket("dmsg", 5, 1, BucketTarget.Bot),
|
||||
[GuildBucket.DeleteMessages] = new Bucket("bdmsg", 1, 1, BucketTarget.Bot),
|
||||
[GuildBucket.ModifyMember] = new Bucket("guild_member", 10, 10, BucketTarget.Bot),
|
||||
[GuildBucket.Nickname] = new Bucket("guild_member_nick", 1, 1, BucketTarget.Bot)
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
//Client-Only
|
||||
_channelLimits = new Dictionary<ChannelBucket, Bucket>
|
||||
{
|
||||
//REST
|
||||
[ChannelBucket.SendEditMessage] = new Bucket("msg", 10, 10, BucketTarget.Client, GlobalBucket.SendEditMessage),
|
||||
}.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public static Bucket GetBucketInfo(GlobalBucket bucket) => _globalLimits[bucket];
|
||||
public static Bucket GetBucketInfo(GuildBucket bucket) => _guildLimits[bucket];
|
||||
public static Bucket GetBucketInfo(ChannelBucket bucket) => _channelLimits[bucket];
|
||||
|
||||
|
||||
public RequestQueue()
|
||||
{
|
||||
_lock = new SemaphoreSlim(1, 1);
|
||||
|
||||
_globalBuckets = new RequestQueueBucket[_globalLimits.Count];
|
||||
foreach (var pair in _globalLimits)
|
||||
{
|
||||
//var target = _globalLimits[pair.Key].Target;
|
||||
//if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot))
|
||||
_globalBuckets[(int)pair.Key] = CreateBucket(pair.Value);
|
||||
}
|
||||
|
||||
_guildBuckets = new ConcurrentDictionary<ulong, RequestQueueBucket>[_guildLimits.Count];
|
||||
for (int i = 0; i < _guildLimits.Count; i++)
|
||||
{
|
||||
//var target = _guildLimits[(GuildBucket)i].Target;
|
||||
//if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot))
|
||||
_guildBuckets[i] = new ConcurrentDictionary<ulong, RequestQueueBucket>();
|
||||
}
|
||||
|
||||
_channelBuckets = new ConcurrentDictionary<ulong, RequestQueueBucket>[_channelLimits.Count];
|
||||
for (int i = 0; i < _channelLimits.Count; i++)
|
||||
{
|
||||
//var target = _channelLimits[(GuildBucket)i].Target;
|
||||
//if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot))
|
||||
_channelBuckets[i] = new ConcurrentDictionary<ulong, RequestQueueBucket>();
|
||||
}
|
||||
|
||||
_clearToken = new CancellationTokenSource();
|
||||
_cancelToken = CancellationToken.None;
|
||||
_parentToken = CancellationToken.None;
|
||||
|
||||
_buckets = new ConcurrentDictionary<string, RequestQueueBucket>();
|
||||
}
|
||||
public async Task SetCancelTokenAsync(CancellationToken cancelToken)
|
||||
{
|
||||
@@ -107,63 +37,29 @@ namespace Discord.Net.Queue
|
||||
finally { _lock.Release(); }
|
||||
}
|
||||
|
||||
public async Task<Stream> SendAsync(RestRequest request, BucketGroup group, int bucketId, ulong objId)
|
||||
public async Task<Stream> SendAsync(RestRequest request)
|
||||
{
|
||||
request.CancelToken = _cancelToken;
|
||||
var bucket = GetBucket(group, bucketId, objId);
|
||||
var bucket = GetOrCreateBucket(request.Options.BucketId);
|
||||
return await bucket.SendAsync(request).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Stream> SendAsync(WebSocketRequest request, BucketGroup group, int bucketId, ulong objId)
|
||||
public async Task<Stream> SendAsync(WebSocketRequest request)
|
||||
{
|
||||
request.CancelToken = _cancelToken;
|
||||
var bucket = GetBucket(group, bucketId, objId);
|
||||
var bucket = GetOrCreateBucket(request.Options.BucketId);
|
||||
return await bucket.SendAsync(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private RequestQueueBucket CreateBucket(Bucket def)
|
||||
private RequestQueueBucket GetOrCreateBucket(string id)
|
||||
{
|
||||
var parent = def.Parent != null ? GetGlobalBucket(def.Parent.Value) : null;
|
||||
return new RequestQueueBucket(this, def, parent);
|
||||
return new RequestQueueBucket(this, id, null);
|
||||
}
|
||||
|
||||
public void DestroyGuildBucket(GuildBucket type, ulong guildId)
|
||||
public void DestroyBucket(string id)
|
||||
{
|
||||
//Assume this object is locked
|
||||
RequestQueueBucket bucket;
|
||||
_guildBuckets[(int)type].TryRemove(guildId, out bucket);
|
||||
}
|
||||
public void DestroyChannelBucket(ChannelBucket type, ulong channelId)
|
||||
{
|
||||
//Assume this object is locked
|
||||
RequestQueueBucket bucket;
|
||||
_channelBuckets[(int)type].TryRemove(channelId, out bucket);
|
||||
}
|
||||
|
||||
private RequestQueueBucket GetBucket(BucketGroup group, int bucketId, ulong objId)
|
||||
{
|
||||
switch (group)
|
||||
{
|
||||
case BucketGroup.Global:
|
||||
return GetGlobalBucket((GlobalBucket)bucketId);
|
||||
case BucketGroup.Guild:
|
||||
return GetGuildBucket((GuildBucket)bucketId, objId);
|
||||
case BucketGroup.Channel:
|
||||
return GetChannelBucket((ChannelBucket)bucketId, objId);
|
||||
default:
|
||||
throw new ArgumentException($"Unknown bucket group: {group}", nameof(group));
|
||||
}
|
||||
}
|
||||
private RequestQueueBucket GetGlobalBucket(GlobalBucket type)
|
||||
{
|
||||
return _globalBuckets[(int)type];
|
||||
}
|
||||
private RequestQueueBucket GetGuildBucket(GuildBucket type, ulong guildId)
|
||||
{
|
||||
return _guildBuckets[(int)type].GetOrAdd(guildId, _ => CreateBucket(_guildLimits[type]));
|
||||
}
|
||||
private RequestQueueBucket GetChannelBucket(ChannelBucket type, ulong channelId)
|
||||
{
|
||||
return _channelBuckets[(int)type].GetOrAdd(channelId, _ => CreateBucket(_channelLimits[type]));
|
||||
_buckets.TryRemove(id, out bucket);
|
||||
}
|
||||
|
||||
public async Task ClearAsync()
|
||||
@@ -181,9 +77,9 @@ namespace Discord.Net.Queue
|
||||
finally { _lock.Release(); }
|
||||
}
|
||||
|
||||
internal async Task RaiseRateLimitTriggered(string id, Bucket bucket, int millis)
|
||||
internal async Task RaiseRateLimitTriggered(string id, RequestQueueBucket bucket, int? millis)
|
||||
{
|
||||
await RateLimitTriggered.Invoke(id, bucket, millis).ConfigureAwait(false);
|
||||
await RateLimitTriggered(id, bucket, millis).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
internal class RequestQueueBucket
|
||||
public class RequestQueueBucket
|
||||
{
|
||||
private readonly RequestQueue _queue;
|
||||
private readonly SemaphoreSlim _semaphore;
|
||||
@@ -15,16 +15,15 @@ namespace Discord.Net.Queue
|
||||
private int _pauseEndTick;
|
||||
private TaskCompletionSource<byte> _resumeNotifier;
|
||||
|
||||
public Bucket Definition { get; }
|
||||
public string Id { get; }
|
||||
public RequestQueueBucket Parent { get; }
|
||||
public Task _resetTask { get; }
|
||||
public int WindowSeconds { get; }
|
||||
|
||||
public RequestQueueBucket(RequestQueue queue, Bucket definition, RequestQueueBucket parent = null)
|
||||
public RequestQueueBucket(RequestQueue queue, string id, RequestQueueBucket parent = null)
|
||||
{
|
||||
_queue = queue;
|
||||
Definition = definition;
|
||||
if (definition.WindowCount != 0)
|
||||
_semaphore = new SemaphoreSlim(definition.WindowCount, definition.WindowCount);
|
||||
Id = id;
|
||||
_semaphore = new SemaphoreSlim(5, 5);
|
||||
Parent = parent;
|
||||
|
||||
_pauseLock = new object();
|
||||
@@ -44,12 +43,8 @@ namespace Discord.Net.Queue
|
||||
{
|
||||
//When a 429 occurs, we drop all our locks.
|
||||
//This is generally safe though since 429s actually occuring should be very rare.
|
||||
RequestQueueBucket bucket;
|
||||
bool success = FindBucket(ex.BucketId, out bucket);
|
||||
|
||||
await _queue.RaiseRateLimitTriggered(ex.BucketId, success ? bucket.Definition : null, ex.RetryAfterMilliseconds).ConfigureAwait(false);
|
||||
|
||||
bucket.Pause(ex.RetryAfterMilliseconds);
|
||||
await _queue.RaiseRateLimitTriggered(Id, this, ex.RetryAfterMilliseconds).ConfigureAwait(false);
|
||||
Pause(ex.RetryAfterMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,24 +102,7 @@ namespace Discord.Net.Queue
|
||||
QueueExitAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private bool FindBucket(string id, out RequestQueueBucket bucket)
|
||||
{
|
||||
//Keep going up until we find a bucket with matching id or we're at the topmost bucket
|
||||
if (Definition.Id == id)
|
||||
{
|
||||
bucket = this;
|
||||
return true;
|
||||
}
|
||||
else if (Parent == null)
|
||||
{
|
||||
bucket = this;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return Parent.FindBucket(id, out bucket);
|
||||
}
|
||||
|
||||
|
||||
private void Pause(int milliseconds)
|
||||
{
|
||||
lock (_pauseLock)
|
||||
@@ -151,13 +129,22 @@ namespace Discord.Net.Queue
|
||||
int millis = unchecked(endTick.Value - Environment.TickCount);
|
||||
if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false))
|
||||
throw new TimeoutException();
|
||||
|
||||
if (!await _semaphore.WaitAsync(0))
|
||||
{
|
||||
await _queue.RaiseRateLimitTriggered(Id, this, null).ConfigureAwait(false);
|
||||
|
||||
millis = unchecked(endTick.Value - Environment.TickCount);
|
||||
if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false))
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
else
|
||||
await _semaphore.WaitAsync().ConfigureAwait(false);
|
||||
}
|
||||
private async Task QueueExitAsync()
|
||||
{
|
||||
await Task.Delay(Definition.WindowSeconds * 1000).ConfigureAwait(false);
|
||||
await Task.Delay(WindowSeconds * 1000).ConfigureAwait(false);
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
22
src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs
Normal file
22
src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Discord.Net.Rest;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public class JsonRestRequest : RestRequest
|
||||
{
|
||||
public string Json { get; }
|
||||
|
||||
public JsonRestRequest(IRestClient client, string method, string endpoint, string json, RequestOptions options)
|
||||
: base(client, method, endpoint, options)
|
||||
{
|
||||
Json = json;
|
||||
}
|
||||
|
||||
public override async Task<Stream> SendAsync()
|
||||
{
|
||||
return await Client.SendAsync(Method, Endpoint, Json, Options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Discord.Net.Rest;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public class MultipartRestRequest : RestRequest
|
||||
{
|
||||
public IReadOnlyDictionary<string, object> MultipartParams { get; }
|
||||
|
||||
public MultipartRestRequest(IRestClient client, string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, RequestOptions options)
|
||||
: base(client, method, endpoint, options)
|
||||
{
|
||||
MultipartParams = multipartParams;
|
||||
}
|
||||
|
||||
public override async Task<Stream> SendAsync()
|
||||
{
|
||||
return await Client.SendAsync(Method, Endpoint, MultipartParams, Options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs
Normal file
36
src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Discord.Net.Rest;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public class RestRequest : IQueuedRequest
|
||||
{
|
||||
public IRestClient Client { get; }
|
||||
public string Method { get; }
|
||||
public string Endpoint { get; }
|
||||
public int? TimeoutTick { get; }
|
||||
public TaskCompletionSource<Stream> Promise { get; }
|
||||
public RequestOptions Options { get; }
|
||||
public CancellationToken CancelToken { get; internal set; }
|
||||
|
||||
public RestRequest(IRestClient client, string method, string endpoint, RequestOptions options)
|
||||
{
|
||||
Preconditions.NotNull(options, nameof(options));
|
||||
|
||||
Client = client;
|
||||
Method = method;
|
||||
Endpoint = endpoint;
|
||||
Options = options;
|
||||
TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null;
|
||||
Promise = new TaskCompletionSource<Stream>();
|
||||
}
|
||||
|
||||
public virtual async Task<Stream> SendAsync()
|
||||
{
|
||||
return await Client.SendAsync(Method, Endpoint, Options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,17 @@ namespace Discord.Net.Queue
|
||||
public bool IsText { get; }
|
||||
public int? TimeoutTick { get; }
|
||||
public TaskCompletionSource<Stream> Promise { get; }
|
||||
public CancellationToken CancelToken { get; set; }
|
||||
|
||||
public RequestOptions Options { get; }
|
||||
public CancellationToken CancelToken { get; internal set; }
|
||||
|
||||
public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
options = RequestOptions.Default;
|
||||
Preconditions.NotNull(options, nameof(options));
|
||||
|
||||
Client = client;
|
||||
Data = data;
|
||||
IsText = isText;
|
||||
Options = options;
|
||||
TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null;
|
||||
Promise = new TaskCompletionSource<Stream>();
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using Discord.Net.Rest;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Net.Queue
|
||||
{
|
||||
public class RestRequest : IQueuedRequest
|
||||
{
|
||||
public IRestClient Client { get; }
|
||||
public string Method { get; }
|
||||
public string Endpoint { get; }
|
||||
public string Json { get; }
|
||||
public bool HeaderOnly { get; }
|
||||
public int? TimeoutTick { get; }
|
||||
public IReadOnlyDictionary<string, object> MultipartParams { get; }
|
||||
public TaskCompletionSource<Stream> Promise { get; }
|
||||
public CancellationToken CancelToken { get; set; }
|
||||
|
||||
public bool IsMultipart => MultipartParams != null;
|
||||
|
||||
public RestRequest(IRestClient client, string method, string endpoint, string json, bool headerOnly, RequestOptions options)
|
||||
: this(client, method, endpoint, headerOnly, options)
|
||||
{
|
||||
Json = json;
|
||||
}
|
||||
|
||||
public RestRequest(IRestClient client, string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly, RequestOptions options)
|
||||
: this(client, method, endpoint, headerOnly, options)
|
||||
{
|
||||
MultipartParams = multipartParams;
|
||||
}
|
||||
|
||||
private RestRequest(IRestClient client, string method, string endpoint, bool headerOnly, RequestOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
options = RequestOptions.Default;
|
||||
|
||||
Client = client;
|
||||
Method = method;
|
||||
Endpoint = endpoint;
|
||||
Json = null;
|
||||
MultipartParams = null;
|
||||
HeaderOnly = headerOnly;
|
||||
TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null;
|
||||
Promise = new TaskCompletionSource<Stream>();
|
||||
}
|
||||
|
||||
public async Task<Stream> SendAsync()
|
||||
{
|
||||
if (IsMultipart)
|
||||
return await Client.SendAsync(Method, Endpoint, MultipartParams, HeaderOnly).ConfigureAwait(false);
|
||||
else if (Json != null)
|
||||
return await Client.SendAsync(Method, Endpoint, Json, HeaderOnly).ConfigureAwait(false);
|
||||
else
|
||||
return await Client.SendAsync(Method, Endpoint, HeaderOnly).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ namespace Discord.Net
|
||||
{
|
||||
public class HttpRateLimitException : HttpException
|
||||
{
|
||||
public string BucketId { get; }
|
||||
public string Id { get; }
|
||||
public int RetryAfterMilliseconds { get; }
|
||||
|
||||
public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason)
|
||||
: base((HttpStatusCode)429, reason)
|
||||
{
|
||||
BucketId = bucketId;
|
||||
Id = bucketId;
|
||||
RetryAfterMilliseconds = retryAfterMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,22 +67,22 @@ namespace Discord.Net.Rest
|
||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
||||
}
|
||||
|
||||
public async Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false)
|
||||
public async Task<Stream> SendAsync(string method, string endpoint, RequestOptions options)
|
||||
{
|
||||
string uri = Path.Combine(_baseUrl, endpoint);
|
||||
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
|
||||
return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false);
|
||||
return await SendInternalAsync(restRequest, options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false)
|
||||
public async Task<Stream> SendAsync(string method, string endpoint, string json, RequestOptions options)
|
||||
{
|
||||
string uri = Path.Combine(_baseUrl, endpoint);
|
||||
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
|
||||
{
|
||||
restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false);
|
||||
return await SendInternalAsync(restRequest, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public async Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false)
|
||||
public async Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, RequestOptions options)
|
||||
{
|
||||
string uri = Path.Combine(_baseUrl, endpoint);
|
||||
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
|
||||
@@ -110,11 +110,11 @@ namespace Discord.Net.Rest
|
||||
}
|
||||
}
|
||||
restRequest.Content = content;
|
||||
return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false);
|
||||
return await SendInternalAsync(restRequest, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Stream> SendInternalAsync(HttpRequestMessage request, bool headerOnly)
|
||||
private async Task<Stream> SendInternalAsync(HttpRequestMessage request, RequestOptions options)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
@@ -154,7 +154,7 @@ namespace Discord.Net.Rest
|
||||
throw new HttpException(response.StatusCode, reason);
|
||||
}
|
||||
|
||||
if (headerOnly)
|
||||
if (options.HeaderOnly)
|
||||
return null;
|
||||
else
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace Discord.Net.Rest
|
||||
void SetHeader(string key, string value);
|
||||
void SetCancelToken(CancellationToken cancelToken);
|
||||
|
||||
Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false);
|
||||
Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false);
|
||||
Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false);
|
||||
Task<Stream> SendAsync(string method, string endpoint, RequestOptions options);
|
||||
Task<Stream> SendAsync(string method, string endpoint, string json, RequestOptions options);
|
||||
Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, RequestOptions options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,24 @@
|
||||
|
||||
/// <summary> The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. </summary>
|
||||
public int? Timeout { get; set; }
|
||||
public string BucketId { get; set; }
|
||||
public bool HeaderOnly { get; internal set; }
|
||||
|
||||
internal bool IgnoreState { get; set; }
|
||||
|
||||
public static RequestOptions CreateOrClone(RequestOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
return new RequestOptions();
|
||||
else
|
||||
return options.Clone();
|
||||
}
|
||||
|
||||
public RequestOptions()
|
||||
{
|
||||
Timeout = 30000;
|
||||
}
|
||||
|
||||
public RequestOptions Clone() => MemberwiseClone() as RequestOptions;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user