Browse Source

Removed old bucket system, cleaned up api calls. Fixed compile errors.

tags/1.0-rc
RogueException 8 years ago
parent
commit
e038475ab4
59 changed files with 462 additions and 568 deletions
  1. +134
    -89
      src/Discord.Net.Core/API/DiscordRestApiClient.cs
  2. +0
    -30
      src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs
  3. +0
    -9
      src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs
  4. +0
    -9
      src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs
  5. +0
    -7
      src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs
  6. +0
    -14
      src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs
  7. +0
    -11
      src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs
  8. +16
    -120
      src/Discord.Net.Core/Net/Queue/RequestQueue.cs
  9. +19
    -32
      src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs
  10. +0
    -0
      src/Discord.Net.Core/Net/Queue/Requests/IQueuedRequest.cs
  11. +22
    -0
      src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs
  12. +24
    -0
      src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs
  13. +36
    -0
      src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs
  14. +5
    -4
      src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs
  15. +0
    -61
      src/Discord.Net.Core/Net/Queue/RestRequest.cs
  16. +2
    -2
      src/Discord.Net.Core/Net/RateLimitException.cs
  17. +8
    -8
      src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs
  18. +3
    -3
      src/Discord.Net.Core/Net/Rest/IRestClient.cs
  19. +14
    -0
      src/Discord.Net.Core/RequestOptions.cs
  20. +3
    -2
      src/Discord.Net.Rest/BaseDiscordClient.cs
  21. +14
    -14
      src/Discord.Net.Rest/ClientHelper.cs
  22. +3
    -3
      src/Discord.Net.Rest/DiscordRestClient.cs
  23. +22
    -22
      src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
  24. +2
    -2
      src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
  25. +2
    -2
      src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
  26. +2
    -2
      src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
  27. +2
    -2
      src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
  28. +2
    -2
      src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
  29. +2
    -2
      src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
  30. +21
    -21
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  31. +1
    -1
      src/Discord.Net.Rest/Entities/Guilds/RestBan.cs
  32. +2
    -2
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  33. +2
    -2
      src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs
  34. +2
    -2
      src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs
  35. +2
    -2
      src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs
  36. +3
    -3
      src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs
  37. +2
    -2
      src/Discord.Net.Rest/Entities/Invites/RestInvite.cs
  38. +2
    -2
      src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs
  39. +5
    -5
      src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
  40. +2
    -2
      src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
  41. +2
    -2
      src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
  42. +2
    -2
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  43. +2
    -2
      src/Discord.Net.Rest/Entities/RestApplication.cs
  44. +2
    -2
      src/Discord.Net.Rest/Entities/RestEntity.cs
  45. +2
    -2
      src/Discord.Net.Rest/Entities/Roles/RestRole.cs
  46. +2
    -2
      src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs
  47. +2
    -2
      src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs
  48. +2
    -2
      src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs
  49. +2
    -2
      src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs
  50. +2
    -2
      src/Discord.Net.Rest/Entities/Users/RestUser.cs
  51. +7
    -7
      src/Discord.Net.Rest/Entities/Users/UserHelper.cs
  52. +24
    -15
      src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs
  53. +1
    -1
      src/Discord.Net.Rpc/DiscordRpcClient.cs
  54. +0
    -2
      src/Discord.Net.Rpc/Entities/RpcGuild.cs
  55. +12
    -11
      src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs
  56. +2
    -2
      src/Discord.Net.WebSocket/ClientState.cs
  57. +13
    -13
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  58. +1
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs
  59. +1
    -1
      src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs

+ 134
- 89
src/Discord.Net.Core/API/DiscordRestApiClient.cs View File

@@ -113,7 +113,7 @@ namespace Discord.API
_authToken = token; _authToken = token;
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken));


CurrentUser = await GetMyUserAsync();
CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true });


LoginState = LoginState.LoggedIn; LoginState = LoginState.LoggedIn;
} }
@@ -155,75 +155,50 @@ namespace Discord.API
internal virtual Task ConnectInternalAsync() => Task.CompletedTask; internal virtual Task ConnectInternalAsync() => Task.CompletedTask;
internal virtual Task DisconnectInternalAsync() => 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 //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)
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;
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);
} }
private async Task<Stream> SendMultipartInternalAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly,
BucketGroup group, int bucketId, ulong guildId, RequestOptions options = null)
public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, RequestOptions options = null)
{ {
CheckState();
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(); 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(); stopwatch.Stop();


double milliseconds = ToMilliseconds(stopwatch); double milliseconds = ToMilliseconds(stopwatch);
@@ -235,6 +210,7 @@ namespace Discord.API
//Auth //Auth
public async Task ValidateTokenAsync(RequestOptions options = null) public async Task ValidateTokenAsync(RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
await SendAsync("GET", "auth/login", options: options).ConfigureAwait(false); 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) public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -253,6 +230,7 @@ namespace Discord.API
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -266,6 +244,7 @@ namespace Discord.API
public async Task<IReadOnlyCollection<Channel>> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null) public async Task<IReadOnlyCollection<Channel>> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<Channel>>("GET", $"guilds/{guildId}/channels", options: options).ConfigureAwait(false); 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.NotNull(args, nameof(args));
Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate));
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); 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) public async Task<Channel> DeleteChannelAsync(ulong channelId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Channel>("DELETE", $"channels/{channelId}", options: options).ConfigureAwait(false); return await SendAsync<Channel>("DELETE", $"channels/{channelId}", options: options).ConfigureAwait(false);
} }
@@ -290,8 +271,9 @@ namespace Discord.API
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); 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) 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.NotNull(args, nameof(args));
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); 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) 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.UserLimit, 0, nameof(args.Bitrate));
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); 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) public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable<ModifyGuildChannelsParams> args, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
options = RequestOptions.CreateOrClone(options);


var channels = args.ToArray(); var channels = args.ToArray();
switch (channels.Length) switch (channels.Length)
@@ -327,7 +312,7 @@ namespace Discord.API
await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false); await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false);
break; break;
default: default:
await SendAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false);
await SendJsonAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false);
break; break;
} }
} }
@@ -337,6 +322,7 @@ namespace Discord.API
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotEqual(messageId, 0, nameof(messageId)); Preconditions.NotEqual(messageId, 0, nameof(messageId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -350,6 +336,7 @@ namespace Discord.API
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit));
Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit));
options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch); int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch);
ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null;
@@ -383,13 +370,15 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));
if (args.Content.Length > DiscordConfig.MaxMessageSize) 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)); 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) public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null)
{ {
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


if (args.Content.GetValueOrDefault(null) == null) if (args.Content.GetValueOrDefault(null) == null)
args.Content = ""; 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)); 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) public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotEqual(messageId, 0, nameof(messageId)); Preconditions.NotEqual(messageId, 0, nameof(messageId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); 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.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));

Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds));
Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length));
options = RequestOptions.CreateOrClone(options);


switch (args.MessageIds.Length) switch (args.MessageIds.Length)
{ {
@@ -426,7 +416,7 @@ namespace Discord.API
await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false); await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false);
break; break;
default: 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; break;
} }
} }
@@ -441,19 +431,22 @@ namespace Discord.API
if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) 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)); 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) public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotEqual(messageId, 0, nameof(messageId)); Preconditions.NotEqual(messageId, 0, nameof(messageId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false);
} }
public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); 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(channelId, 0, nameof(channelId));
Preconditions.NotEqual(targetId, 0, nameof(targetId)); Preconditions.NotEqual(targetId, 0, nameof(targetId));
Preconditions.NotNull(args, nameof(args)); 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) public async Task DeleteChannelPermissionAsync(ulong channelId, ulong targetId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotEqual(targetId, 0, nameof(targetId)); Preconditions.NotEqual(targetId, 0, nameof(targetId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"channels/{channelId}/permissions/{targetId}", options: options).ConfigureAwait(false); 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(channelId, 0, nameof(channelId));
Preconditions.GreaterThan(messageId, 0, nameof(messageId)); Preconditions.GreaterThan(messageId, 0, nameof(messageId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("PUT", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); 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(channelId, 0, nameof(channelId));
Preconditions.NotEqual(messageId, 0, nameof(messageId)); Preconditions.NotEqual(messageId, 0, nameof(messageId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false);
} }
public async Task<IReadOnlyCollection<Message>> GetPinsAsync(ulong channelId, RequestOptions options = null) public async Task<IReadOnlyCollection<Message>> GetPinsAsync(ulong channelId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<Message>>("GET", $"channels/{channelId}/pins", options: options).ConfigureAwait(false); 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(channelId, 0, nameof(channelId));
Preconditions.GreaterThan(userId, 0, nameof(userId)); Preconditions.GreaterThan(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("PUT", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); 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(channelId, 0, nameof(channelId));
Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); 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) public async Task<Guild> GetGuildAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -531,18 +532,21 @@ namespace Discord.API
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));
Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); 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) public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Guild>("DELETE", $"guilds/{guildId}", options: options).ConfigureAwait(false); return await SendAsync<Guild>("DELETE", $"guilds/{guildId}", options: options).ConfigureAwait(false);
} }
public async Task<Guild> LeaveGuildAsync(ulong guildId, RequestOptions options = null) public async Task<Guild> LeaveGuildAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Guild>("DELETE", $"users/@me/guilds/{guildId}", options: options).ConfigureAwait(false); 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.NotNullOrEmpty(args.Name, nameof(args.Name));
Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId));
Preconditions.NotNull(args.RegionId, nameof(args.RegionId)); 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) public async Task<GetGuildPruneCountResponse> BeginGuildPruneAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); 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) public async Task<GetGuildPruneCountResponse> GetGuildPruneCountAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); 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 //Guild Bans
public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null) public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<Ban>>("GET", $"guilds/{guildId}/bans", options: options).ConfigureAwait(false); 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.NotEqual(userId, 0, nameof(userId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays)); 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) public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"guilds/{guildId}/bans/{userId}", options: options).ConfigureAwait(false); 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) public async Task<GuildEmbed> GetGuildEmbedAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -614,14 +625,16 @@ namespace Discord.API
{ {
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotEqual(guildId, 0, nameof(guildId)); 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 //Guild Integrations
public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null) public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<Integration>>("GET", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); 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.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotEqual(args.Id, 0, nameof(args.Id)); Preconditions.NotEqual(args.Id, 0, nameof(args.Id));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); 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(guildId, 0, nameof(guildId));
Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Integration>("DELETE", $"guilds/{guildId}/integrations/{integrationId}", options: options).ConfigureAwait(false); 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.NotNull(args, nameof(args));
Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior));
Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); 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) public async Task<Integration> SyncGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); Preconditions.NotEqual(integrationId, 0, nameof(integrationId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations/{integrationId}/sync", options: options).ConfigureAwait(false); 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) public async Task<Invite> GetInviteAsync(string inviteId, RequestOptions options = null)
{ {
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId));
options = RequestOptions.CreateOrClone(options);


//Remove trailing slash //Remove trailing slash
if (inviteId[inviteId.Length - 1] == '/') if (inviteId[inviteId.Length - 1] == '/')
@@ -680,12 +698,14 @@ namespace Discord.API
public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false);
} }
public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null)
{ {
Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(channelId, 0, nameof(channelId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); 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.NotNull(args, nameof(args));
Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge));
Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); 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) public async Task<Invite> DeleteInviteAsync(string inviteCode, RequestOptions options = null)
{ {
Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Invite>("DELETE", $"invites/{inviteCode}", options: options).ConfigureAwait(false); return await SendAsync<Invite>("DELETE", $"invites/{inviteCode}", options: options).ConfigureAwait(false);
} }
public async Task AcceptInviteAsync(string inviteCode, RequestOptions options = null) public async Task AcceptInviteAsync(string inviteCode, RequestOptions options = null)
{ {
Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode));
options = RequestOptions.CreateOrClone(options);


await SendAsync("POST", $"invites/{inviteCode}", options: options).ConfigureAwait(false); await SendAsync("POST", $"invites/{inviteCode}", options: options).ConfigureAwait(false);
} }
@@ -716,6 +739,7 @@ namespace Discord.API
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -730,6 +754,7 @@ namespace Discord.API
Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit));
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit));
Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId));
options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(int.MaxValue); int limit = args.Limit.GetValueOrDefault(int.MaxValue);
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); ulong afterUserId = args.AfterUserId.GetValueOrDefault(0);
@@ -741,6 +766,7 @@ namespace Discord.API
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"guilds/{guildId}/members/{userId}", options: options).ConfigureAwait(false); 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(guildId, 0, nameof(guildId));
Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotEqual(userId, 0, nameof(userId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
options = RequestOptions.CreateOrClone(options);


bool isCurrentUser = userId == CurrentUser.Id; bool isCurrentUser = userId == CurrentUser.Id;


@@ -760,7 +787,7 @@ namespace Discord.API
} }
if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) 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) public async Task<IReadOnlyCollection<Role>> GetGuildRolesAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<Role>>("GET", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); return await SendAsync<IReadOnlyCollection<Role>>("GET", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false);
} }
public async Task<Role> CreateGuildRoleAsync(ulong guildId, RequestOptions options = null) public async Task<Role> CreateGuildRoleAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<Role>("POST", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); 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(guildId, 0, nameof(guildId));
Preconditions.NotEqual(roleId, 0, nameof(roleId)); Preconditions.NotEqual(roleId, 0, nameof(roleId));
options = RequestOptions.CreateOrClone(options);


await SendAsync("DELETE", $"guilds/{guildId}/roles/{roleId}", options: options).ConfigureAwait(false); 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.AtLeast(args.Color, 0, nameof(args.Color));
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); 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) public async Task<IReadOnlyCollection<Role>> ModifyGuildRolesAsync(ulong guildId, IEnumerable<ModifyGuildRolesParams> args, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
options = RequestOptions.CreateOrClone(options);


var roles = args.ToArray(); var roles = args.ToArray();
switch (roles.Length) switch (roles.Length)
@@ -808,7 +840,7 @@ namespace Discord.API
case 1: case 1:
return ImmutableArray.Create(await ModifyGuildRoleAsync(guildId, roles[0].Id, roles[0]).ConfigureAwait(false)); return ImmutableArray.Create(await ModifyGuildRoleAsync(guildId, roles[0].Id, roles[0]).ConfigureAwait(false));
default: 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) public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null)
{ {
Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -827,6 +860,7 @@ namespace Discord.API
{ {
Preconditions.NotNullOrEmpty(username, nameof(username)); Preconditions.NotNullOrEmpty(username, nameof(username));
Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator)); Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator));
options = RequestOptions.CreateOrClone(options);


try try
{ {
@@ -839,6 +873,7 @@ namespace Discord.API
{ {
Preconditions.NotNullOrEmpty(query, nameof(query)); Preconditions.NotNullOrEmpty(query, nameof(query));
Preconditions.AtLeast(limit, 0, nameof(limit)); 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); 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 //Current User/DMs
public async Task<User> GetMyUserAsync(RequestOptions options = null) public async Task<User> GetMyUserAsync(RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
return await SendAsync<User>("GET", "users/@me", options: options).ConfigureAwait(false); return await SendAsync<User>("GET", "users/@me", options: options).ConfigureAwait(false);
} }
public async Task<IReadOnlyCollection<Connection>> GetMyConnectionsAsync(RequestOptions options = null) 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); return await SendAsync<IReadOnlyCollection<Connection>>("GET", "users/@me/connections", options: options).ConfigureAwait(false);
} }
public async Task<IReadOnlyCollection<Channel>> GetMyPrivateChannelsAsync(RequestOptions options = null) 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); return await SendAsync<IReadOnlyCollection<Channel>>("GET", "users/@me/channels", options: options).ConfigureAwait(false);
} }
public async Task<IReadOnlyCollection<UserGuild>> GetMyGuildsAsync(RequestOptions options = null) 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); return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", "users/@me/guilds", options: options).ConfigureAwait(false);
} }
public async Task<Application> GetMyApplicationAsync(RequestOptions options = null) public async Task<Application> GetMyApplicationAsync(RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false); return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false);
} }
public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null)
{ {
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); 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) public async Task ModifyMyNickAsync(ulong guildId, ModifyCurrentUserNickParams args, RequestOptions options = null)
{ {
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotNull(args.Nickname, nameof(args.Nickname)); 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) public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null)
{ {
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId)); 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 //Voice Regions
public async Task<IReadOnlyCollection<VoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) 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); return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", "voice/regions", options: options).ConfigureAwait(false);
} }
public async Task<IReadOnlyCollection<VoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null) public async Task<IReadOnlyCollection<VoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null)
{ {
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
options = RequestOptions.CreateOrClone(options);


return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", $"guilds/{guildId}/regions", options: options).ConfigureAwait(false); return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", $"guilds/{guildId}/regions", options: options).ConfigureAwait(false);
} }


+ 0
- 30
src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs View File

@@ -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;
}
}
}

+ 0
- 9
src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs View File

@@ -1,9 +0,0 @@
namespace Discord.Net.Queue
{
public enum BucketGroup
{
Global,
Guild,
Channel
}
}

+ 0
- 9
src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs View File

@@ -1,9 +0,0 @@
namespace Discord.Net.Queue
{
public enum BucketTarget
{
Client,
Bot,
Both
}
}

+ 0
- 7
src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs View File

@@ -1,7 +0,0 @@
namespace Discord.Net.Queue
{
public enum ChannelBucket
{
SendEditMessage,
}
}

+ 0
- 14
src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs View File

@@ -1,14 +0,0 @@
namespace Discord.Net.Queue
{
public enum GlobalBucket
{
GeneralRest,
DirectMessage,
SendEditMessage,

GeneralGateway,
UpdateStatus,

GeneralRpc
}
}

+ 0
- 11
src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs View File

@@ -1,11 +0,0 @@
namespace Discord.Net.Queue
{
public enum GuildBucket
{
SendEditMessage,
DeleteMessage,
DeleteMessages,
ModifyMember,
Nickname
}
}

+ 16
- 120
src/Discord.Net.Core/Net/Queue/RequestQueue.cs View File

@@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -10,91 +8,23 @@ namespace Discord.Net.Queue
{ {
public class RequestQueue 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 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 CancellationTokenSource _clearToken;
private CancellationToken _parentToken; private CancellationToken _parentToken;
private CancellationToken _cancelToken; 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() public RequestQueue()
{ {
_lock = new SemaphoreSlim(1, 1); _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(); _clearToken = new CancellationTokenSource();
_cancelToken = CancellationToken.None; _cancelToken = CancellationToken.None;
_parentToken = CancellationToken.None; _parentToken = CancellationToken.None;

_buckets = new ConcurrentDictionary<string, RequestQueueBucket>();
} }
public async Task SetCancelTokenAsync(CancellationToken cancelToken) public async Task SetCancelTokenAsync(CancellationToken cancelToken)
{ {
@@ -107,63 +37,29 @@ namespace Discord.Net.Queue
finally { _lock.Release(); } 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; request.CancelToken = _cancelToken;
var bucket = GetBucket(group, bucketId, objId);
var bucket = GetOrCreateBucket(request.Options.BucketId);
return await bucket.SendAsync(request).ConfigureAwait(false); 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; request.CancelToken = _cancelToken;
var bucket = GetBucket(group, bucketId, objId);
var bucket = GetOrCreateBucket(request.Options.BucketId);
return await bucket.SendAsync(request).ConfigureAwait(false); 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)
{
//Assume this object is locked
RequestQueueBucket bucket;
_guildBuckets[(int)type].TryRemove(guildId, out bucket);
}
public void DestroyChannelBucket(ChannelBucket type, ulong channelId)
public void DestroyBucket(string id)
{ {
//Assume this object is locked //Assume this object is locked
RequestQueueBucket bucket; 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() public async Task ClearAsync()
@@ -181,9 +77,9 @@ namespace Discord.Net.Queue
finally { _lock.Release(); } 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);
} }
} }
} }

+ 19
- 32
src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;


namespace Discord.Net.Queue namespace Discord.Net.Queue
{ {
internal class RequestQueueBucket
public class RequestQueueBucket
{ {
private readonly RequestQueue _queue; private readonly RequestQueue _queue;
private readonly SemaphoreSlim _semaphore; private readonly SemaphoreSlim _semaphore;
@@ -15,16 +15,15 @@ namespace Discord.Net.Queue
private int _pauseEndTick; private int _pauseEndTick;
private TaskCompletionSource<byte> _resumeNotifier; private TaskCompletionSource<byte> _resumeNotifier;


public Bucket Definition { get; }
public string Id { get; }
public RequestQueueBucket Parent { 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; _queue = queue;
Definition = definition;
if (definition.WindowCount != 0)
_semaphore = new SemaphoreSlim(definition.WindowCount, definition.WindowCount);
Id = id;
_semaphore = new SemaphoreSlim(5, 5);
Parent = parent; Parent = parent;


_pauseLock = new object(); _pauseLock = new object();
@@ -44,12 +43,8 @@ namespace Discord.Net.Queue
{ {
//When a 429 occurs, we drop all our locks. //When a 429 occurs, we drop all our locks.
//This is generally safe though since 429s actually occuring should be very rare. //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(); 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) private void Pause(int milliseconds)
{ {
lock (_pauseLock) lock (_pauseLock)
@@ -151,13 +129,22 @@ namespace Discord.Net.Queue
int millis = unchecked(endTick.Value - Environment.TickCount); int millis = unchecked(endTick.Value - Environment.TickCount);
if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false))
throw new TimeoutException(); 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 else
await _semaphore.WaitAsync().ConfigureAwait(false); await _semaphore.WaitAsync().ConfigureAwait(false);
} }
private async Task QueueExitAsync() private async Task QueueExitAsync()
{ {
await Task.Delay(Definition.WindowSeconds * 1000).ConfigureAwait(false);
await Task.Delay(WindowSeconds * 1000).ConfigureAwait(false);
_semaphore.Release(); _semaphore.Release();
} }
} }


src/Discord.Net.Core/Net/Queue/IQueuedRequest.cs → src/Discord.Net.Core/Net/Queue/Requests/IQueuedRequest.cs View File


+ 22
- 0
src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs View 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);
}
}
}

+ 24
- 0
src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs View File

@@ -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
- 0
src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs View 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);
}
}
}

src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs → src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs View File

@@ -13,16 +13,17 @@ namespace Discord.Net.Queue
public bool IsText { get; } public bool IsText { get; }
public int? TimeoutTick { get; } public int? TimeoutTick { get; }
public TaskCompletionSource<Stream> Promise { 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) public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options)
{ {
if (options == null)
options = RequestOptions.Default;
Preconditions.NotNull(options, nameof(options));


Client = client; Client = client;
Data = data; Data = data;
IsText = isText; IsText = isText;
Options = options;
TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null;
Promise = new TaskCompletionSource<Stream>(); Promise = new TaskCompletionSource<Stream>();
} }

+ 0
- 61
src/Discord.Net.Core/Net/Queue/RestRequest.cs View File

@@ -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);
}
}
}

+ 2
- 2
src/Discord.Net.Core/Net/RateLimitException.cs View File

@@ -4,13 +4,13 @@ namespace Discord.Net
{ {
public class HttpRateLimitException : HttpException public class HttpRateLimitException : HttpException
{ {
public string BucketId { get; }
public string Id { get; }
public int RetryAfterMilliseconds { get; } public int RetryAfterMilliseconds { get; }


public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason) public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason)
: base((HttpStatusCode)429, reason) : base((HttpStatusCode)429, reason)
{ {
BucketId = bucketId;
Id = bucketId;
RetryAfterMilliseconds = retryAfterMilliseconds; RetryAfterMilliseconds = retryAfterMilliseconds;
} }
} }


+ 8
- 8
src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs View File

@@ -67,22 +67,22 @@ namespace Discord.Net.Rest
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; _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); string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) 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); string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
{ {
restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); 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); string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
@@ -110,11 +110,11 @@ namespace Discord.Net.Rest
} }
} }
restRequest.Content = content; 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) while (true)
{ {
@@ -154,7 +154,7 @@ namespace Discord.Net.Rest
throw new HttpException(response.StatusCode, reason); throw new HttpException(response.StatusCode, reason);
} }


if (headerOnly)
if (options.HeaderOnly)
return null; return null;
else else
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);


+ 3
- 3
src/Discord.Net.Core/Net/Rest/IRestClient.cs View File

@@ -11,8 +11,8 @@ namespace Discord.Net.Rest
void SetHeader(string key, string value); void SetHeader(string key, string value);
void SetCancelToken(CancellationToken cancelToken); 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);
} }
} }

+ 14
- 0
src/Discord.Net.Core/RequestOptions.cs View File

@@ -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> /// <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 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() public RequestOptions()
{ {
Timeout = 30000; Timeout = 30000;
} }

public RequestOptions Clone() => MemberwiseClone() as RequestOptions;
} }
} }

src/Discord.Net.Rest/DiscordClient.cs → src/Discord.Net.Rest/BaseDiscordClient.cs View File

@@ -8,7 +8,7 @@ using System.Collections.Immutable;


namespace Discord.Rest namespace Discord.Rest
{ {
public abstract class DiscordClient : IDiscordClient
public abstract class BaseDiscordClient : IDiscordClient
{ {
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>(); private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();
@@ -29,7 +29,7 @@ namespace Discord.Rest
public ISelfUser CurrentUser { get; protected set; } public ISelfUser CurrentUser { get; protected set; }
/// <summary> Creates a new REST-only discord client. </summary> /// <summary> Creates a new REST-only discord client. </summary>
internal DiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client)
internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client)
{ {
ApiClient = client; ApiClient = client;
LogManager = new LogManager(config.LogLevel); LogManager = new LogManager(config.LogLevel);
@@ -73,6 +73,7 @@ namespace Discord.Rest


try try
{ {
await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false);
await OnLoginAsync(tokenType, token).ConfigureAwait(false); await OnLoginAsync(tokenType, token).ConfigureAwait(false);
LoginState = LoginState.LoggedIn; LoginState = LoginState.LoggedIn;
} }

+ 14
- 14
src/Discord.Net.Rest/ClientHelper.cs View File

@@ -10,13 +10,13 @@ namespace Discord.Rest
internal static class ClientHelper internal static class ClientHelper
{ {
//Applications //Applications
public static async Task<RestApplication> GetApplicationInfoAsync(DiscordClient client)
public static async Task<RestApplication> GetApplicationInfoAsync(BaseDiscordClient client)
{ {
var model = await client.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); var model = await client.ApiClient.GetMyApplicationAsync().ConfigureAwait(false);
return RestApplication.Create(client, model); return RestApplication.Create(client, model);
} }


public static async Task<RestChannel> GetChannelAsync(DiscordClient client,
public static async Task<RestChannel> GetChannelAsync(BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetChannelAsync(id).ConfigureAwait(false); var model = await client.ApiClient.GetChannelAsync(id).ConfigureAwait(false);
@@ -24,19 +24,19 @@ namespace Discord.Rest
return RestChannel.Create(client, model); return RestChannel.Create(client, model);
return null; return null;
} }
public static async Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync(DiscordClient client)
public static async Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync(BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false);
return models.Select(x => RestDMChannel.Create(client, x)).ToImmutableArray(); return models.Select(x => RestDMChannel.Create(client, x)).ToImmutableArray();
} }
public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(DiscordClient client)
public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetMyConnectionsAsync().ConfigureAwait(false); var models = await client.ApiClient.GetMyConnectionsAsync().ConfigureAwait(false);
return models.Select(x => RestConnection.Create(x)).ToImmutableArray(); return models.Select(x => RestConnection.Create(x)).ToImmutableArray();
} }
public static async Task<RestInvite> GetInviteAsync(DiscordClient client,
public static async Task<RestInvite> GetInviteAsync(BaseDiscordClient client,
string inviteId) string inviteId)
{ {
var model = await client.ApiClient.GetInviteAsync(inviteId).ConfigureAwait(false); var model = await client.ApiClient.GetInviteAsync(inviteId).ConfigureAwait(false);
@@ -45,7 +45,7 @@ namespace Discord.Rest
return null; return null;
} }
public static async Task<RestGuild> GetGuildAsync(DiscordClient client,
public static async Task<RestGuild> GetGuildAsync(BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetGuildAsync(id).ConfigureAwait(false); var model = await client.ApiClient.GetGuildAsync(id).ConfigureAwait(false);
@@ -53,7 +53,7 @@ namespace Discord.Rest
return RestGuild.Create(client, model); return RestGuild.Create(client, model);
return null; return null;
} }
public static async Task<RestGuildEmbed?> GetGuildEmbedAsync(DiscordClient client,
public static async Task<RestGuildEmbed?> GetGuildEmbedAsync(BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetGuildEmbedAsync(id).ConfigureAwait(false); var model = await client.ApiClient.GetGuildEmbedAsync(id).ConfigureAwait(false);
@@ -61,12 +61,12 @@ namespace Discord.Rest
return RestGuildEmbed.Create(model); return RestGuildEmbed.Create(model);
return null; return null;
} }
public static async Task<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(DiscordClient client)
public static async Task<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); var models = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false);
return models.Select(x => RestUserGuild.Create(client, x)).ToImmutableArray(); return models.Select(x => RestUserGuild.Create(client, x)).ToImmutableArray();
} }
public static async Task<IReadOnlyCollection<RestGuild>> GetGuildsAsync(DiscordClient client)
public static async Task<IReadOnlyCollection<RestGuild>> GetGuildsAsync(BaseDiscordClient client)
{ {
var summaryModels = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); var summaryModels = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false);
var guilds = ImmutableArray.CreateBuilder<RestGuild>(summaryModels.Count); var guilds = ImmutableArray.CreateBuilder<RestGuild>(summaryModels.Count);
@@ -78,7 +78,7 @@ namespace Discord.Rest
} }
return guilds.ToImmutable(); return guilds.ToImmutable();
} }
public static async Task<RestGuild> CreateGuildAsync(DiscordClient client,
public static async Task<RestGuild> CreateGuildAsync(BaseDiscordClient client,
string name, IVoiceRegion region, Stream jpegIcon = null) string name, IVoiceRegion region, Stream jpegIcon = null)
{ {
var args = new CreateGuildParams(name, region.Id); var args = new CreateGuildParams(name, region.Id);
@@ -86,7 +86,7 @@ namespace Discord.Rest
return RestGuild.Create(client, model); return RestGuild.Create(client, model);
} }
public static async Task<RestUser> GetUserAsync(DiscordClient client,
public static async Task<RestUser> GetUserAsync(BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetUserAsync(id).ConfigureAwait(false); var model = await client.ApiClient.GetUserAsync(id).ConfigureAwait(false);
@@ -94,7 +94,7 @@ namespace Discord.Rest
return RestUser.Create(client, model); return RestUser.Create(client, model);
return null; return null;
} }
public static async Task<RestUser> GetUserAsync(DiscordClient client,
public static async Task<RestUser> GetUserAsync(BaseDiscordClient client,
string username, string discriminator) string username, string discriminator)
{ {
var model = await client.ApiClient.GetUserAsync(username, discriminator).ConfigureAwait(false); var model = await client.ApiClient.GetUserAsync(username, discriminator).ConfigureAwait(false);
@@ -103,12 +103,12 @@ namespace Discord.Rest
return null; return null;
} }


public static async Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(DiscordClient client)
public static async Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false);
return models.Select(x => RestVoiceRegion.Create(client, x)).ToImmutableArray(); return models.Select(x => RestVoiceRegion.Create(client, x)).ToImmutableArray();
} }
public static async Task<RestVoiceRegion> GetVoiceRegionAsync(DiscordClient client,
public static async Task<RestVoiceRegion> GetVoiceRegionAsync(BaseDiscordClient client,
string id) string id)
{ {
var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false);


+ 3
- 3
src/Discord.Net.Rest/DiscordRestClient.cs View File

@@ -5,7 +5,7 @@ using System.Threading.Tasks;


namespace Discord.Rest namespace Discord.Rest
{ {
public class DiscordRestClient : DiscordClient, IDiscordClient
public class DiscordRestClient : BaseDiscordClient, IDiscordClient
{ {
public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser; public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser;


@@ -15,10 +15,10 @@ namespace Discord.Rest
private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config)
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, requestQueue: new RequestQueue()); => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, requestQueue: new RequestQueue());


protected override async Task OnLoginAsync(TokenType tokenType, string token)
protected override Task OnLoginAsync(TokenType tokenType, string token)
{ {
await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false);
base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser);
return Task.CompletedTask;
} }


/// <inheritdoc /> /// <inheritdoc />


+ 22
- 22
src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs View File

@@ -12,33 +12,33 @@ namespace Discord.Rest
internal static class ChannelHelper internal static class ChannelHelper
{ {
//General //General
public static async Task<Model> GetAsync(IGuildChannel channel, DiscordClient client)
public static async Task<Model> GetAsync(IGuildChannel channel, BaseDiscordClient client)
{ {
return await client.ApiClient.GetChannelAsync(channel.GuildId, channel.Id).ConfigureAwait(false); return await client.ApiClient.GetChannelAsync(channel.GuildId, channel.Id).ConfigureAwait(false);
} }
public static async Task<Model> GetAsync(IPrivateChannel channel, DiscordClient client)
public static async Task<Model> GetAsync(IPrivateChannel channel, BaseDiscordClient client)
{ {
return await client.ApiClient.GetChannelAsync(channel.Id).ConfigureAwait(false); return await client.ApiClient.GetChannelAsync(channel.Id).ConfigureAwait(false);
} }
public static async Task DeleteAsync(IChannel channel, DiscordClient client)
public static async Task DeleteAsync(IChannel channel, BaseDiscordClient client)
{ {
await client.ApiClient.DeleteChannelAsync(channel.Id).ConfigureAwait(false); await client.ApiClient.DeleteChannelAsync(channel.Id).ConfigureAwait(false);
} }
public static async Task ModifyAsync(IGuildChannel channel, DiscordClient client,
public static async Task ModifyAsync(IGuildChannel channel, BaseDiscordClient client,
Action<ModifyGuildChannelParams> func) Action<ModifyGuildChannelParams> func)
{ {
var args = new ModifyGuildChannelParams(); var args = new ModifyGuildChannelParams();
func(args); func(args);
await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args);
} }
public static async Task ModifyAsync(ITextChannel channel, DiscordClient client,
public static async Task ModifyAsync(ITextChannel channel, BaseDiscordClient client,
Action<ModifyTextChannelParams> func) Action<ModifyTextChannelParams> func)
{ {
var args = new ModifyTextChannelParams(); var args = new ModifyTextChannelParams();
func(args); func(args);
await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args);
} }
public static async Task ModifyAsync(IVoiceChannel channel, DiscordClient client,
public static async Task ModifyAsync(IVoiceChannel channel, BaseDiscordClient client,
Action<ModifyVoiceChannelParams> func) Action<ModifyVoiceChannelParams> func)
{ {
var args = new ModifyVoiceChannelParams(); var args = new ModifyVoiceChannelParams();
@@ -47,12 +47,12 @@ namespace Discord.Rest
} }


//Invites //Invites
public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IChannel channel, DiscordClient client)
public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IChannel channel, BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id); var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id);
return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray();
} }
public static async Task<RestInviteMetadata> CreateInviteAsync(IChannel channel, DiscordClient client,
public static async Task<RestInviteMetadata> CreateInviteAsync(IChannel channel, BaseDiscordClient client,
int? maxAge, int? maxUses, bool isTemporary) int? maxAge, int? maxUses, bool isTemporary)
{ {
var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; var args = new CreateChannelInviteParams { IsTemporary = isTemporary };
@@ -65,13 +65,13 @@ namespace Discord.Rest
} }


//Messages //Messages
public static async Task<RestMessage> GetMessageAsync(IChannel channel, DiscordClient client,
public static async Task<RestMessage> GetMessageAsync(IChannel channel, BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id).ConfigureAwait(false); var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id).ConfigureAwait(false);
return RestMessage.Create(client, model); return RestMessage.Create(client, model);
} }
public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IChannel channel, DiscordClient client,
public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IChannel channel, BaseDiscordClient client,
ulong? fromMessageId = null, Direction dir = Direction.Before, int limit = DiscordConfig.MaxMessagesPerBatch) ulong? fromMessageId = null, Direction dir = Direction.Before, int limit = DiscordConfig.MaxMessagesPerBatch)
{ {
//TODO: Test this with Around direction //TODO: Test this with Around direction
@@ -102,13 +102,13 @@ namespace Discord.Rest
count: (uint)limit count: (uint)limit
); );
} }
public static async Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(IChannel channel, DiscordClient client)
public static async Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(IChannel channel, BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetPinsAsync(channel.Id).ConfigureAwait(false); var models = await client.ApiClient.GetPinsAsync(channel.Id).ConfigureAwait(false);
return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray();
} }


public static async Task<RestUserMessage> SendMessageAsync(IChannel channel, DiscordClient client,
public static async Task<RestUserMessage> SendMessageAsync(IChannel channel, BaseDiscordClient client,
string text, bool isTTS) string text, bool isTTS)
{ {
var args = new CreateMessageParams(text) { IsTTS = isTTS }; var args = new CreateMessageParams(text) { IsTTS = isTTS };
@@ -116,14 +116,14 @@ namespace Discord.Rest
return RestUserMessage.Create(client, model); return RestUserMessage.Create(client, model);
} }


public static Task<RestUserMessage> SendFileAsync(IChannel channel, DiscordClient client,
public static Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client,
string filePath, string text, bool isTTS) string filePath, string text, bool isTTS)
{ {
string filename = Path.GetFileName(filePath); string filename = Path.GetFileName(filePath);
using (var file = File.OpenRead(filePath)) using (var file = File.OpenRead(filePath))
return SendFileAsync(channel, client, file, filename, text, isTTS); return SendFileAsync(channel, client, file, filename, text, isTTS);
} }
public static async Task<RestUserMessage> SendFileAsync(IChannel channel, DiscordClient client,
public static async Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client,
Stream stream, string filename, string text, bool isTTS) Stream stream, string filename, string text, bool isTTS)
{ {
var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS };
@@ -131,7 +131,7 @@ namespace Discord.Rest
return RestUserMessage.Create(client, model); return RestUserMessage.Create(client, model);
} }


public static async Task DeleteMessagesAsync(IChannel channel, DiscordClient client,
public static async Task DeleteMessagesAsync(IChannel channel, BaseDiscordClient client,
IEnumerable<IMessage> messages) IEnumerable<IMessage> messages)
{ {
var args = new DeleteMessagesParams(messages.Select(x => x.Id).ToArray()); var args = new DeleteMessagesParams(messages.Select(x => x.Id).ToArray());
@@ -139,31 +139,31 @@ namespace Discord.Rest
} }


//Permission Overwrites //Permission Overwrites
public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordClient client,
public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
IUser user, OverwritePermissions perms) IUser user, OverwritePermissions perms)
{ {
var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue); var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue);
await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args).ConfigureAwait(false); await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args).ConfigureAwait(false);
} }
public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordClient client,
public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
IRole role, OverwritePermissions perms) IRole role, OverwritePermissions perms)
{ {
var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue); var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue);
await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args).ConfigureAwait(false); await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args).ConfigureAwait(false);
} }
public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, DiscordClient client,
public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
IUser user) IUser user)
{ {
await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, user.Id).ConfigureAwait(false); await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, user.Id).ConfigureAwait(false);
} }
public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, DiscordClient client,
public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
IRole role) IRole role)
{ {
await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, role.Id).ConfigureAwait(false); await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, role.Id).ConfigureAwait(false);
} }


//Users //Users
public static async Task<RestGuildUser> GetUserAsync(IGuildChannel channel, DiscordClient client,
public static async Task<RestGuildUser> GetUserAsync(IGuildChannel channel, BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetGuildMemberAsync(channel.GuildId, id); var model = await client.ApiClient.GetGuildMemberAsync(channel.GuildId, id);
@@ -175,7 +175,7 @@ namespace Discord.Rest


return user; return user;
} }
public static IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuildChannel channel, DiscordClient client,
public static IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuildChannel channel, BaseDiscordClient client,
ulong? froUserId = null, uint? limit = DiscordConfig.MaxUsersPerBatch) ulong? froUserId = null, uint? limit = DiscordConfig.MaxUsersPerBatch)
{ {
return new PagedAsyncEnumerable<RestGuildUser>( return new PagedAsyncEnumerable<RestGuildUser>(
@@ -203,7 +203,7 @@ namespace Discord.Rest
} }


//Typing //Typing
public static IDisposable EnterTypingState(IChannel channel, DiscordClient client)
public static IDisposable EnterTypingState(IChannel channel, BaseDiscordClient client)
{ {
throw new NotImplementedException(); //TODO: Impl throw new NotImplementedException(); //TODO: Impl
} }


+ 2
- 2
src/Discord.Net.Rest/Entities/Channels/RestChannel.cs View File

@@ -11,11 +11,11 @@ namespace Discord.Rest
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public abstract class RestChannel : RestEntity<ulong>, IChannel, IUpdateable public abstract class RestChannel : RestEntity<ulong>, IChannel, IUpdateable
{ {
internal RestChannel(DiscordClient discord, ulong id)
internal RestChannel(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestChannel Create(DiscordClient discord, Model model)
internal static RestChannel Create(BaseDiscordClient discord, Model model)
{ {
switch (model.Type) switch (model.Type)
{ {


+ 2
- 2
src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs View File

@@ -17,13 +17,13 @@ namespace Discord.Rest


public IReadOnlyCollection<RestUser> Users => ImmutableArray.Create(CurrentUser, Recipient); public IReadOnlyCollection<RestUser> Users => ImmutableArray.Create(CurrentUser, Recipient);


internal RestDMChannel(DiscordClient discord, ulong id, ulong recipientId)
internal RestDMChannel(BaseDiscordClient discord, ulong id, ulong recipientId)
: base(discord, id) : base(discord, id)
{ {
Recipient = new RestUser(Discord, recipientId); Recipient = new RestUser(Discord, recipientId);
CurrentUser = new RestUser(Discord, discord.CurrentUser.Id); CurrentUser = new RestUser(Discord, discord.CurrentUser.Id);
} }
internal new static RestDMChannel Create(DiscordClient discord, Model model)
internal new static RestDMChannel Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestDMChannel(discord, model.Id, model.Recipients.Value[0].Id); var entity = new RestDMChannel(discord, model.Id, model.Recipients.Value[0].Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs View File

@@ -21,11 +21,11 @@ namespace Discord.Rest
public IReadOnlyCollection<RestGroupUser> Recipients public IReadOnlyCollection<RestGroupUser> Recipients
=> _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1); => _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1);


internal RestGroupChannel(DiscordClient discord, ulong id)
internal RestGroupChannel(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal new static RestGroupChannel Create(DiscordClient discord, Model model)
internal new static RestGroupChannel Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestGroupChannel(discord, model.Id); var entity = new RestGroupChannel(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs View File

@@ -21,12 +21,12 @@ namespace Discord.Rest
public string Name { get; private set; } public string Name { get; private set; }
public int Position { get; private set; } public int Position { get; private set; }


internal RestGuildChannel(DiscordClient discord, ulong id, ulong guildId)
internal RestGuildChannel(BaseDiscordClient discord, ulong id, ulong guildId)
: base(discord, id) : base(discord, id)
{ {
GuildId = guildId; GuildId = guildId;
} }
internal new static RestGuildChannel Create(DiscordClient discord, Model model)
internal new static RestGuildChannel Create(BaseDiscordClient discord, Model model)
{ {
switch (model.Type) switch (model.Type)
{ {


+ 2
- 2
src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs View File

@@ -16,11 +16,11 @@ namespace Discord.Rest


public string Mention => MentionUtils.MentionChannel(Id); public string Mention => MentionUtils.MentionChannel(Id);


internal RestTextChannel(DiscordClient discord, ulong id, ulong guildId)
internal RestTextChannel(BaseDiscordClient discord, ulong id, ulong guildId)
: base(discord, id, guildId) : base(discord, id, guildId)
{ {
} }
internal new static RestTextChannel Create(DiscordClient discord, Model model)
internal new static RestTextChannel Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestTextChannel(discord, model.Id, model.GuildId.Value); var entity = new RestTextChannel(discord, model.Id, model.GuildId.Value);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs View File

@@ -16,11 +16,11 @@ namespace Discord.Rest
public int Bitrate { get; private set; } public int Bitrate { get; private set; }
public int UserLimit { get; private set; } public int UserLimit { get; private set; }


internal RestVoiceChannel(DiscordClient discord, ulong id, ulong guildId)
internal RestVoiceChannel(BaseDiscordClient discord, ulong id, ulong guildId)
: base(discord, id, guildId) : base(discord, id, guildId)
{ {
} }
internal new static RestVoiceChannel Create(DiscordClient discord, Model model)
internal new static RestVoiceChannel Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestVoiceChannel(discord, model.Id, model.GuildId.Value); var entity = new RestVoiceChannel(discord, model.Id, model.GuildId.Value);
entity.Update(model); entity.Update(model);


+ 21
- 21
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -13,7 +13,7 @@ namespace Discord.Rest
internal static class GuildHelper internal static class GuildHelper
{ {
//General //General
public static async Task<Model> ModifyAsync(IGuild guild, DiscordClient client,
public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client,
Action<ModifyGuildParams> func) Action<ModifyGuildParams> func)
{ {
if (func == null) throw new NullReferenceException(nameof(func)); if (func == null) throw new NullReferenceException(nameof(func));
@@ -28,7 +28,7 @@ namespace Discord.Rest


return await client.ApiClient.ModifyGuildAsync(guild.Id, args).ConfigureAwait(false); return await client.ApiClient.ModifyGuildAsync(guild.Id, args).ConfigureAwait(false);
} }
public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, DiscordClient client,
public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, BaseDiscordClient client,
Action<ModifyGuildEmbedParams> func) Action<ModifyGuildEmbedParams> func)
{ {
if (func == null) throw new NullReferenceException(nameof(func)); if (func == null) throw new NullReferenceException(nameof(func));
@@ -37,46 +37,46 @@ namespace Discord.Rest
func(args); func(args);
return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, args).ConfigureAwait(false); return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, args).ConfigureAwait(false);
} }
public static async Task ModifyChannelsAsync(IGuild guild, DiscordClient client,
public static async Task ModifyChannelsAsync(IGuild guild, BaseDiscordClient client,
IEnumerable<ModifyGuildChannelsParams> args) IEnumerable<ModifyGuildChannelsParams> args)
{ {
await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args).ConfigureAwait(false); await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args).ConfigureAwait(false);
} }
public static async Task<IReadOnlyCollection<RoleModel>> ModifyRolesAsync(IGuild guild, DiscordClient client,
public static async Task<IReadOnlyCollection<RoleModel>> ModifyRolesAsync(IGuild guild, BaseDiscordClient client,
IEnumerable<ModifyGuildRolesParams> args) IEnumerable<ModifyGuildRolesParams> args)
{ {
return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, args).ConfigureAwait(false); return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, args).ConfigureAwait(false);
} }
public static async Task LeaveAsync(IGuild guild, DiscordClient client)
public static async Task LeaveAsync(IGuild guild, BaseDiscordClient client)
{ {
await client.ApiClient.LeaveGuildAsync(guild.Id).ConfigureAwait(false); await client.ApiClient.LeaveGuildAsync(guild.Id).ConfigureAwait(false);
} }
public static async Task DeleteAsync(IGuild guild, DiscordClient client)
public static async Task DeleteAsync(IGuild guild, BaseDiscordClient client)
{ {
await client.ApiClient.DeleteGuildAsync(guild.Id).ConfigureAwait(false); await client.ApiClient.DeleteGuildAsync(guild.Id).ConfigureAwait(false);
} }


//Bans //Bans
public static async Task<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild guild, DiscordClient client)
public static async Task<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild guild, BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetGuildBansAsync(guild.Id); var models = await client.ApiClient.GetGuildBansAsync(guild.Id);
return models.Select(x => RestBan.Create(client, x)).ToImmutableArray(); return models.Select(x => RestBan.Create(client, x)).ToImmutableArray();
} }
public static async Task AddBanAsync(IGuild guild, DiscordClient client,
public static async Task AddBanAsync(IGuild guild, BaseDiscordClient client,
ulong userId, int pruneDays) ulong userId, int pruneDays)
{ {
var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays }; var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays };
await client.ApiClient.CreateGuildBanAsync(guild.Id, userId, args); await client.ApiClient.CreateGuildBanAsync(guild.Id, userId, args);
} }
public static async Task RemoveBanAsync(IGuild guild, DiscordClient client,
public static async Task RemoveBanAsync(IGuild guild, BaseDiscordClient client,
ulong userId) ulong userId)
{ {
await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId); await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId);
} }


//Channels //Channels
public static async Task<RestGuildChannel> GetChannelAsync(IGuild guild, DiscordClient client,
public static async Task<RestGuildChannel> GetChannelAsync(IGuild guild, BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetChannelAsync(guild.Id, id).ConfigureAwait(false); var model = await client.ApiClient.GetChannelAsync(guild.Id, id).ConfigureAwait(false);
@@ -84,12 +84,12 @@ namespace Discord.Rest
return RestGuildChannel.Create(client, model); return RestGuildChannel.Create(client, model);
return null; return null;
} }
public static async Task<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync(IGuild guild, DiscordClient client)
public static async Task<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync(IGuild guild, BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false); var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false);
return models.Select(x => RestGuildChannel.Create(client, x)).ToImmutableArray(); return models.Select(x => RestGuildChannel.Create(client, x)).ToImmutableArray();
} }
public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, DiscordClient client,
public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client,
string name) string name)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
@@ -98,7 +98,7 @@ namespace Discord.Rest
var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args).ConfigureAwait(false); var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args).ConfigureAwait(false);
return RestTextChannel.Create(client, model); return RestTextChannel.Create(client, model);
} }
public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, DiscordClient client,
public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client,
string name) string name)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
@@ -109,12 +109,12 @@ namespace Discord.Rest
} }


//Integrations //Integrations
public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, DiscordClient client)
public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id).ConfigureAwait(false); var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id).ConfigureAwait(false);
return models.Select(x => RestGuildIntegration.Create(client, x)).ToImmutableArray(); return models.Select(x => RestGuildIntegration.Create(client, x)).ToImmutableArray();
} }
public static async Task<RestGuildIntegration> CreateIntegrationAsync(IGuild guild, DiscordClient client,
public static async Task<RestGuildIntegration> CreateIntegrationAsync(IGuild guild, BaseDiscordClient client,
ulong id, string type) ulong id, string type)
{ {
var args = new CreateGuildIntegrationParams(id, type); var args = new CreateGuildIntegrationParams(id, type);
@@ -123,14 +123,14 @@ namespace Discord.Rest
} }


//Invites //Invites
public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuild guild, DiscordClient client)
public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuild guild, BaseDiscordClient client)
{ {
var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id).ConfigureAwait(false); var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id).ConfigureAwait(false);
return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray();
} }


//Roles //Roles
public static async Task<RestRole> CreateRoleAsync(IGuild guild, DiscordClient client,
public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client,
string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false) string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
@@ -150,7 +150,7 @@ namespace Discord.Rest
} }


//Users //Users
public static async Task<RestGuildUser> GetUserAsync(IGuild guild, DiscordClient client,
public static async Task<RestGuildUser> GetUserAsync(IGuild guild, BaseDiscordClient client,
ulong id) ulong id)
{ {
var model = await client.ApiClient.GetGuildMemberAsync(guild.Id, id).ConfigureAwait(false); var model = await client.ApiClient.GetGuildMemberAsync(guild.Id, id).ConfigureAwait(false);
@@ -158,17 +158,17 @@ namespace Discord.Rest
return RestGuildUser.Create(client, model); return RestGuildUser.Create(client, model);
return null; return null;
} }
public static async Task<RestGuildUser> GetCurrentUserAsync(IGuild guild, DiscordClient client)
public static async Task<RestGuildUser> GetCurrentUserAsync(IGuild guild, BaseDiscordClient client)
{ {
return await GetUserAsync(guild, client, client.CurrentUser.Id).ConfigureAwait(false); return await GetUserAsync(guild, client, client.CurrentUser.Id).ConfigureAwait(false);
} }
public static async Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuild guild, DiscordClient client)
public static async Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuild guild, BaseDiscordClient client)
{ {
var args = new GetGuildMembersParams(); var args = new GetGuildMembersParams();
var models = await client.ApiClient.GetGuildMembersAsync(guild.Id, args).ConfigureAwait(false); var models = await client.ApiClient.GetGuildMembersAsync(guild.Id, args).ConfigureAwait(false);
return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray(); return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray();
} }
public static async Task<int> PruneUsersAsync(IGuild guild, DiscordClient client,
public static async Task<int> PruneUsersAsync(IGuild guild, BaseDiscordClient client,
int days = 30, bool simulate = false) int days = 30, bool simulate = false)
{ {
var args = new GuildPruneParams(days); var args = new GuildPruneParams(days);


+ 1
- 1
src/Discord.Net.Rest/Entities/Guilds/RestBan.cs View File

@@ -14,7 +14,7 @@ namespace Discord.Rest
User = user; User = user;
Reason = reason; Reason = reason;
} }
internal static RestBan Create(DiscordClient client, Model model)
internal static RestBan Create(BaseDiscordClient client, Model model)
{ {
return new RestBan(RestUser.Create(client, model.User), model.Reason); return new RestBan(RestUser.Create(client, model.User), model.Reason);
} }


+ 2
- 2
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -39,11 +39,11 @@ namespace Discord.Rest
public IReadOnlyCollection<Emoji> Emojis => _emojis; public IReadOnlyCollection<Emoji> Emojis => _emojis;
public IReadOnlyCollection<string> Features => _features; public IReadOnlyCollection<string> Features => _features;


internal RestGuild(DiscordClient client, ulong id)
internal RestGuild(BaseDiscordClient client, ulong id)
: base(client, id) : base(client, id)
{ {
} }
internal static RestGuild Create(DiscordClient discord, Model model)
internal static RestGuild Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestGuild(discord, model.Id); var entity = new RestGuild(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs View File

@@ -25,11 +25,11 @@ namespace Discord.Rest


public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks);


internal RestGuildIntegration(DiscordClient discord, ulong id)
internal RestGuildIntegration(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestGuildIntegration Create(DiscordClient discord, Model model)
internal static RestGuildIntegration Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestGuildIntegration(discord, model.Id); var entity = new RestGuildIntegration(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs View File

@@ -15,11 +15,11 @@ namespace Discord.Rest


public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId);


internal RestUserGuild(DiscordClient discord, ulong id)
internal RestUserGuild(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestUserGuild Create(DiscordClient discord, Model model)
internal static RestUserGuild Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestUserGuild(discord, model.Id); var entity = new RestUserGuild(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs View File

@@ -13,11 +13,11 @@ namespace Discord
public string SampleHostname { get; private set; } public string SampleHostname { get; private set; }
public int SamplePort { get; private set; } public int SamplePort { get; private set; }


internal RestVoiceRegion(DiscordClient client, string id)
internal RestVoiceRegion(BaseDiscordClient client, string id)
: base(client, id) : base(client, id)
{ {
} }
internal static RestVoiceRegion Create(DiscordClient client, Model model)
internal static RestVoiceRegion Create(BaseDiscordClient client, Model model)
{ {
var entity = new RestVoiceRegion(client, model.Id); var entity = new RestVoiceRegion(client, model.Id);
entity.Update(model); entity.Update(model);


+ 3
- 3
src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs View File

@@ -5,15 +5,15 @@ namespace Discord.Rest
{ {
internal static class InviteHelper internal static class InviteHelper
{ {
public static async Task<Model> GetAsync(IInvite invite, DiscordClient client)
public static async Task<Model> GetAsync(IInvite invite, BaseDiscordClient client)
{ {
return await client.ApiClient.GetInviteAsync(invite.Code).ConfigureAwait(false); return await client.ApiClient.GetInviteAsync(invite.Code).ConfigureAwait(false);
} }
public static async Task AcceptAsync(IInvite invite, DiscordClient client)
public static async Task AcceptAsync(IInvite invite, BaseDiscordClient client)
{ {
await client.ApiClient.AcceptInviteAsync(invite.Code).ConfigureAwait(false); await client.ApiClient.AcceptInviteAsync(invite.Code).ConfigureAwait(false);
} }
public static async Task DeleteAsync(IInvite invite, DiscordClient client)
public static async Task DeleteAsync(IInvite invite, BaseDiscordClient client)
{ {
await client.ApiClient.DeleteInviteAsync(invite.Code).ConfigureAwait(false); await client.ApiClient.DeleteInviteAsync(invite.Code).ConfigureAwait(false);
} }


+ 2
- 2
src/Discord.Net.Rest/Entities/Invites/RestInvite.cs View File

@@ -16,11 +16,11 @@ namespace Discord.Rest
public string Code => Id; public string Code => Id;
public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; public string Url => $"{DiscordConfig.InviteUrl}/{Code}";


internal RestInvite(DiscordClient discord, string id)
internal RestInvite(BaseDiscordClient discord, string id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestInvite Create(DiscordClient discord, Model model)
internal static RestInvite Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestInvite(discord, model.Code); var entity = new RestInvite(discord, model.Code);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs View File

@@ -18,11 +18,11 @@ namespace Discord.Rest


public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks);


internal RestInviteMetadata(DiscordClient discord, string id)
internal RestInviteMetadata(BaseDiscordClient discord, string id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestInviteMetadata Create(DiscordClient discord, Model model)
internal static RestInviteMetadata Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestInviteMetadata(discord, model.Code); var entity = new RestInviteMetadata(discord, model.Code);
entity.Update(model); entity.Update(model);


+ 5
- 5
src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs View File

@@ -6,26 +6,26 @@ namespace Discord.Rest
{ {
internal static class MessageHelper internal static class MessageHelper
{ {
public static async Task GetAsync(IMessage msg, DiscordClient client)
public static async Task GetAsync(IMessage msg, BaseDiscordClient client)
{ {
await client.ApiClient.GetChannelMessageAsync(msg.ChannelId, msg.Id); await client.ApiClient.GetChannelMessageAsync(msg.ChannelId, msg.Id);
} }
public static async Task ModifyAsync(IMessage msg, DiscordClient client, Action<ModifyMessageParams> func)
public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action<ModifyMessageParams> func)
{ {
var args = new ModifyMessageParams(); var args = new ModifyMessageParams();
func(args); func(args);
await client.ApiClient.ModifyMessageAsync(msg.ChannelId, msg.Id, args); await client.ApiClient.ModifyMessageAsync(msg.ChannelId, msg.Id, args);
} }
public static async Task DeleteAsync(IMessage msg, DiscordClient client)
public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client)
{ {
await client.ApiClient.DeleteMessageAsync(msg.ChannelId, msg.Id); await client.ApiClient.DeleteMessageAsync(msg.ChannelId, msg.Id);
} }


public static async Task PinAsync(IMessage msg, DiscordClient client)
public static async Task PinAsync(IMessage msg, BaseDiscordClient client)
{ {
await client.ApiClient.AddPinAsync(msg.ChannelId, msg.Id); await client.ApiClient.AddPinAsync(msg.ChannelId, msg.Id);
} }
public static async Task UnpinAsync(IMessage msg, DiscordClient client)
public static async Task UnpinAsync(IMessage msg, BaseDiscordClient client)
{ {
await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id); await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id);
} }


+ 2
- 2
src/Discord.Net.Rest/Entities/Messages/RestMessage.cs View File

@@ -29,12 +29,12 @@ namespace Discord.Rest


public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);


internal RestMessage(DiscordClient discord, ulong id, ulong channelId)
internal RestMessage(BaseDiscordClient discord, ulong id, ulong channelId)
: base(discord, id) : base(discord, id)
{ {
ChannelId = channelId; ChannelId = channelId;
} }
internal static RestMessage Create(DiscordClient discord, Model model)
internal static RestMessage Create(BaseDiscordClient discord, Model model)
{ {
if (model.Type == MessageType.Default) if (model.Type == MessageType.Default)
return RestUserMessage.Create(discord, model); return RestUserMessage.Create(discord, model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs View File

@@ -8,11 +8,11 @@ namespace Discord.Rest
{ {
public MessageType Type { get; private set; } public MessageType Type { get; private set; }


internal RestSystemMessage(DiscordClient discord, ulong id, ulong channelId)
internal RestSystemMessage(BaseDiscordClient discord, ulong id, ulong channelId)
: base(discord, id, channelId) : base(discord, id, channelId)
{ {
} }
internal new static RestSystemMessage Create(DiscordClient discord, Model model)
internal new static RestSystemMessage Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestSystemMessage(discord, model.Id, model.ChannelId); var entity = new RestSystemMessage(discord, model.Id, model.ChannelId);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs View File

@@ -30,11 +30,11 @@ namespace Discord.Rest
public override IReadOnlyCollection<IRole> MentionedRoles => _mentionedRoles; public override IReadOnlyCollection<IRole> MentionedRoles => _mentionedRoles;
public override IReadOnlyCollection<IUser> MentionedUsers => _mentionedUsers; public override IReadOnlyCollection<IUser> MentionedUsers => _mentionedUsers;


internal RestUserMessage(DiscordClient discord, ulong id, ulong channelId)
internal RestUserMessage(BaseDiscordClient discord, ulong id, ulong channelId)
: base(discord, id, channelId) : base(discord, id, channelId)
{ {
} }
internal new static RestUserMessage Create(DiscordClient discord, Model model)
internal new static RestUserMessage Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestUserMessage(discord, model.Id, model.ChannelId); var entity = new RestUserMessage(discord, model.Id, model.ChannelId);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/RestApplication.cs View File

@@ -17,11 +17,11 @@ namespace Discord.Rest


public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId);


internal RestApplication(DiscordClient discord, ulong id)
internal RestApplication(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestApplication Create(DiscordClient discord, Model model)
internal static RestApplication Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestApplication(discord, model.Id); var entity = new RestApplication(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/RestEntity.cs View File

@@ -5,10 +5,10 @@ namespace Discord.Rest
public abstract class RestEntity<T> : IEntity<T> public abstract class RestEntity<T> : IEntity<T>
where T : IEquatable<T> where T : IEquatable<T>
{ {
public DiscordClient Discord { get; }
public BaseDiscordClient Discord { get; }
public T Id { get; } public T Id { get; }


internal RestEntity(DiscordClient discord, T id)
internal RestEntity(BaseDiscordClient discord, T id)
{ {
Discord = discord; Discord = discord;
Id = id; Id = id;


+ 2
- 2
src/Discord.Net.Rest/Entities/Roles/RestRole.cs View File

@@ -21,11 +21,11 @@ namespace Discord.Rest
public bool IsEveryone => Id == Guild.Id; public bool IsEveryone => Id == Guild.Id;
public string Mention => MentionUtils.MentionRole(Id); public string Mention => MentionUtils.MentionRole(Id);


internal RestRole(DiscordClient discord, ulong id)
internal RestRole(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestRole Create(DiscordClient discord, Model model)
internal static RestRole Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestRole(discord, model.Id); var entity = new RestRole(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs View File

@@ -7,11 +7,11 @@ namespace Discord.Rest
internal static class RoleHelper internal static class RoleHelper
{ {
//General //General
public static async Task DeleteAsync(IRole role, DiscordClient client)
public static async Task DeleteAsync(IRole role, BaseDiscordClient client)
{ {
await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id).ConfigureAwait(false); await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id).ConfigureAwait(false);
} }
public static async Task ModifyAsync(IRole role, DiscordClient client,
public static async Task ModifyAsync(IRole role, BaseDiscordClient client,
Action<ModifyGuildRoleParams> func) Action<ModifyGuildRoleParams> func)
{ {
var args = new ModifyGuildRoleParams(); var args = new ModifyGuildRoleParams();


+ 2
- 2
src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs View File

@@ -6,11 +6,11 @@ namespace Discord.Rest
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestGroupUser : RestUser, IGroupUser public class RestGroupUser : RestUser, IGroupUser
{ {
internal RestGroupUser(DiscordClient discord, ulong id)
internal RestGroupUser(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal new static RestGroupUser Create(DiscordClient discord, Model model)
internal new static RestGroupUser Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestGroupUser(discord, model.Id); var entity = new RestGroupUser(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs View File

@@ -21,11 +21,11 @@ namespace Discord.Rest
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks);


internal RestGuildUser(DiscordClient discord, ulong id)
internal RestGuildUser(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestGuildUser Create(DiscordClient discord, Model model)
internal static RestGuildUser Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestGuildUser(discord, model.User.Id); var entity = new RestGuildUser(discord, model.User.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs View File

@@ -13,11 +13,11 @@ namespace Discord.Rest
public bool IsVerified { get; private set; } public bool IsVerified { get; private set; }
public bool IsMfaEnabled { get; private set; } public bool IsMfaEnabled { get; private set; }


internal RestSelfUser(DiscordClient discord, ulong id)
internal RestSelfUser(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal new static RestSelfUser Create(DiscordClient discord, Model model)
internal new static RestSelfUser Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestSelfUser(discord, model.Id); var entity = new RestSelfUser(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 2
- 2
src/Discord.Net.Rest/Entities/Users/RestUser.cs View File

@@ -18,11 +18,11 @@ namespace Discord.Rest
public virtual Game? Game => null; public virtual Game? Game => null;
public virtual UserStatus Status => UserStatus.Unknown; public virtual UserStatus Status => UserStatus.Unknown;


internal RestUser(DiscordClient discord, ulong id)
internal RestUser(BaseDiscordClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
} }
internal static RestUser Create(DiscordClient discord, Model model)
internal static RestUser Create(BaseDiscordClient discord, Model model)
{ {
var entity = new RestUser(discord, model.Id); var entity = new RestUser(discord, model.Id);
entity.Update(model); entity.Update(model);


+ 7
- 7
src/Discord.Net.Rest/Entities/Users/UserHelper.cs View File

@@ -8,22 +8,22 @@ namespace Discord.Rest
{ {
internal static class UserHelper internal static class UserHelper
{ {
public static async Task<Model> GetAsync(IUser user, DiscordClient client)
public static async Task<Model> GetAsync(IUser user, BaseDiscordClient client)
{ {
return await client.ApiClient.GetUserAsync(user.Id); return await client.ApiClient.GetUserAsync(user.Id);
} }
public static async Task<Model> GetAsync(ISelfUser user, DiscordClient client)
public static async Task<Model> GetAsync(ISelfUser user, BaseDiscordClient client)
{ {
var model = await client.ApiClient.GetMyUserAsync(); var model = await client.ApiClient.GetMyUserAsync();
if (model.Id != user.Id) if (model.Id != user.Id)
throw new InvalidOperationException("Unable to update this object using a different token."); throw new InvalidOperationException("Unable to update this object using a different token.");
return model; return model;
} }
public static async Task<MemberModel> GetAsync(IGuildUser user, DiscordClient client)
public static async Task<MemberModel> GetAsync(IGuildUser user, BaseDiscordClient client)
{ {
return await client.ApiClient.GetGuildMemberAsync(user.GuildId, user.Id); return await client.ApiClient.GetGuildMemberAsync(user.GuildId, user.Id);
} }
public static async Task ModifyAsync(ISelfUser user, DiscordClient client, Action<ModifyCurrentUserParams> func)
public static async Task ModifyAsync(ISelfUser user, BaseDiscordClient client, Action<ModifyCurrentUserParams> func)
{ {
if (user.Id != client.CurrentUser.Id) if (user.Id != client.CurrentUser.Id)
throw new InvalidOperationException("Unable to modify this object using a different token."); throw new InvalidOperationException("Unable to modify this object using a different token.");
@@ -32,19 +32,19 @@ namespace Discord.Rest
func(args); func(args);
await client.ApiClient.ModifySelfAsync(args); await client.ApiClient.ModifySelfAsync(args);
} }
public static async Task ModifyAsync(IGuildUser user, DiscordClient client, Action<ModifyGuildMemberParams> func)
public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action<ModifyGuildMemberParams> func)
{ {
var args = new ModifyGuildMemberParams(); var args = new ModifyGuildMemberParams();
func(args); func(args);
await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, args); await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, args);
} }


public static async Task KickAsync(IGuildUser user, DiscordClient client)
public static async Task KickAsync(IGuildUser user, BaseDiscordClient client)
{ {
await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id); await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id);
} }


public static async Task<IDMChannel> CreateDMChannelAsync(IUser user, DiscordClient client)
public static async Task<IDMChannel> CreateDMChannelAsync(IUser user, BaseDiscordClient client)
{ {
var args = new CreateDMChannelParams(user.Id); var args = new CreateDMChannelParams(user.Id);
return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args)); return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args));


+ 24
- 15
src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs View File

@@ -206,19 +206,16 @@ namespace Discord.API
} }


//Core //Core
public Task<TResponse> SendRpcAsync<TResponse>(string cmd, object payload, GlobalBucket bucket = GlobalBucket.GeneralRpc,
Optional<string> evt = default(Optional<string>), bool ignoreState = false, RequestOptions options = null)
public async Task<TResponse> SendRpcAsync<TResponse>(string cmd, object payload, Optional<string> evt = default(Optional<string>), RequestOptions options = null)
where TResponse : class where TResponse : class
=> SendRpcAsyncInternal<TResponse>(cmd, payload, BucketGroup.Global, (int)bucket, 0, evt, ignoreState, options);
public Task<TResponse> SendRpcAsync<TResponse>(string cmd, object payload, GuildBucket bucket, ulong guildId,
Optional<string> evt = default(Optional<string>), bool ignoreState = false, RequestOptions options = null)
where TResponse : class
=> SendRpcAsyncInternal<TResponse>(cmd, payload, BucketGroup.Guild, (int)bucket, guildId, evt, ignoreState, options);
private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, BucketGroup group, int bucketId, ulong guildId,
Optional<string> evt, bool ignoreState, RequestOptions options)
{
options.IgnoreState = false;
return await SendRpcAsyncInternal<TResponse>(cmd, payload, evt, options).ConfigureAwait(false);
}
private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, Optional<string> evt, RequestOptions options)
where TResponse : class where TResponse : class
{ {
if (!ignoreState)
if (!options.IgnoreState)
CheckState(); CheckState();


byte[] bytes = null; byte[] bytes = null;
@@ -233,7 +230,7 @@ namespace Discord.API
var requestTracker = new RpcRequest<TResponse>(options); var requestTracker = new RpcRequest<TResponse>(options);
_requests[guid] = requestTracker; _requests[guid] = requestTracker;


await _requestQueue.SendAsync(new WebSocketRequest(_webSocketClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false);
await _requestQueue.SendAsync(new WebSocketRequest(_webSocketClient, bytes, true, options)).ConfigureAwait(false);
await _sentRpcMessageEvent.InvokeAsync(cmd).ConfigureAwait(false); await _sentRpcMessageEvent.InvokeAsync(cmd).ConfigureAwait(false);
return await requestTracker.Promise.Task.ConfigureAwait(false); return await requestTracker.Promise.Task.ConfigureAwait(false);
} }
@@ -241,33 +238,37 @@ namespace Discord.API
//Rpc //Rpc
public async Task<AuthenticateResponse> SendAuthenticateAsync(RequestOptions options = null) public async Task<AuthenticateResponse> SendAuthenticateAsync(RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new AuthenticateParams var msg = new AuthenticateParams
{ {
AccessToken = _authToken AccessToken = _authToken
}; };
return await SendRpcAsync<AuthenticateResponse>("AUTHENTICATE", msg, ignoreState: true, options: options).ConfigureAwait(false);
options.IgnoreState = true;
return await SendRpcAsync<AuthenticateResponse>("AUTHENTICATE", msg, options: options).ConfigureAwait(false);
} }
public async Task<AuthorizeResponse> SendAuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) public async Task<AuthorizeResponse> SendAuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new AuthorizeParams var msg = new AuthorizeParams
{ {
ClientId = _clientId, ClientId = _clientId,
Scopes = scopes, Scopes = scopes,
RpcToken = rpcToken != null ? rpcToken : Optional.Create<string>() RpcToken = rpcToken != null ? rpcToken : Optional.Create<string>()
}; };
if (options == null)
options = new RequestOptions();
if (options.Timeout == null) if (options.Timeout == null)
options.Timeout = 60000; //This requires manual input on the user's end, lets give them more time options.Timeout = 60000; //This requires manual input on the user's end, lets give them more time
return await SendRpcAsync<AuthorizeResponse>("AUTHORIZE", msg, ignoreState: true, options: options).ConfigureAwait(false);
options.IgnoreState = true;
return await SendRpcAsync<AuthorizeResponse>("AUTHORIZE", msg, options: options).ConfigureAwait(false);
} }


public async Task<GetGuildsResponse> SendGetGuildsAsync(RequestOptions options = null) public async Task<GetGuildsResponse> SendGetGuildsAsync(RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
return await SendRpcAsync<GetGuildsResponse>("GET_GUILDS", null, options: options).ConfigureAwait(false); return await SendRpcAsync<GetGuildsResponse>("GET_GUILDS", null, options: options).ConfigureAwait(false);
} }
public async Task<RpcGuild> SendGetGuildAsync(ulong guildId, RequestOptions options = null) public async Task<RpcGuild> SendGetGuildAsync(ulong guildId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new GetGuildParams var msg = new GetGuildParams
{ {
GuildId = guildId GuildId = guildId
@@ -276,6 +277,7 @@ namespace Discord.API
} }
public async Task<GetChannelsResponse> SendGetChannelsAsync(ulong guildId, RequestOptions options = null) public async Task<GetChannelsResponse> SendGetChannelsAsync(ulong guildId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new GetChannelsParams var msg = new GetChannelsParams
{ {
GuildId = guildId GuildId = guildId
@@ -284,6 +286,7 @@ namespace Discord.API
} }
public async Task<RpcChannel> SendGetChannelAsync(ulong channelId, RequestOptions options = null) public async Task<RpcChannel> SendGetChannelAsync(ulong channelId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new GetChannelParams var msg = new GetChannelParams
{ {
ChannelId = channelId ChannelId = channelId
@@ -293,6 +296,7 @@ namespace Discord.API


public async Task<SetLocalVolumeResponse> SendSetLocalVolumeAsync(int volume, RequestOptions options = null) public async Task<SetLocalVolumeResponse> SendSetLocalVolumeAsync(int volume, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new SetLocalVolumeParams var msg = new SetLocalVolumeParams
{ {
Volume = volume Volume = volume
@@ -301,6 +305,7 @@ namespace Discord.API
} }
public async Task<RpcChannel> SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null) public async Task<RpcChannel> SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new SelectVoiceChannelParams var msg = new SelectVoiceChannelParams
{ {
ChannelId = channelId ChannelId = channelId
@@ -310,6 +315,7 @@ namespace Discord.API


public async Task<SubscriptionResponse> SendChannelSubscribeAsync(string evt, ulong channelId, RequestOptions options = null) public async Task<SubscriptionResponse> SendChannelSubscribeAsync(string evt, ulong channelId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new ChannelSubscriptionParams var msg = new ChannelSubscriptionParams
{ {
ChannelId = channelId ChannelId = channelId
@@ -318,6 +324,7 @@ namespace Discord.API
} }
public async Task<SubscriptionResponse> SendChannelUnsubscribeAsync(string evt, ulong channelId, RequestOptions options = null) public async Task<SubscriptionResponse> SendChannelUnsubscribeAsync(string evt, ulong channelId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new ChannelSubscriptionParams var msg = new ChannelSubscriptionParams
{ {
ChannelId = channelId ChannelId = channelId
@@ -327,6 +334,7 @@ namespace Discord.API


public async Task<SubscriptionResponse> SendGuildSubscribeAsync(string evt, ulong guildId, RequestOptions options = null) public async Task<SubscriptionResponse> SendGuildSubscribeAsync(string evt, ulong guildId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new GuildSubscriptionParams var msg = new GuildSubscriptionParams
{ {
GuildId = guildId GuildId = guildId
@@ -335,6 +343,7 @@ namespace Discord.API
} }
public async Task<SubscriptionResponse> SendGuildUnsubscribeAsync(string evt, ulong guildId, RequestOptions options = null) public async Task<SubscriptionResponse> SendGuildUnsubscribeAsync(string evt, ulong guildId, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new GuildSubscriptionParams var msg = new GuildSubscriptionParams
{ {
GuildId = guildId GuildId = guildId


+ 1
- 1
src/Discord.Net.Rpc/DiscordRpcClient.cs View File

@@ -11,7 +11,7 @@ using System.Threading.Tasks;


namespace Discord.Rpc namespace Discord.Rpc
{ {
public partial class DiscordRpcClient : DiscordClient
public partial class DiscordRpcClient : BaseDiscordClient
{ {
private readonly Logger _rpcLogger; private readonly Logger _rpcLogger;
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;


+ 0
- 2
src/Discord.Net.Rpc/Entities/RpcGuild.cs View File

@@ -22,7 +22,5 @@ namespace Discord.Rpc
{ {
Name = model.Name; Name = model.Name;
} }

bool IEntity<ulong>.IsAttached => false;
}*/ }*/
} }

+ 12
- 11
src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs View File

@@ -156,8 +156,9 @@ namespace Discord.API
} }


//Core //Core
private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload,
BucketGroup group, int bucketId, ulong guildId, RequestOptions options)
public Task SendGatewayAsync(GatewayOpCode opCode, object payload, RequestOptions options = null)
=> SendGatewayInternalAsync(opCode, payload, options);
private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload, RequestOptions options)
{ {
CheckState(); CheckState();


@@ -166,25 +167,19 @@ namespace Discord.API
payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload }; payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload };
if (payload != null) if (payload != null)
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload)); bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
await RequestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false);
await RequestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options)).ConfigureAwait(false);
await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false);
} }


//Gateway //Gateway
public Task SendGatewayAsync(GatewayOpCode opCode, object payload,
GlobalBucket bucket = GlobalBucket.GeneralGateway, RequestOptions options = null)
=> SendGatewayInternalAsync(opCode, payload, BucketGroup.Global, (int)bucket, 0, options);

public Task SendGatewayAsync(GatewayOpCode opCode, object payload,
GuildBucket bucket, ulong guildId, RequestOptions options = null)
=> SendGatewayInternalAsync(opCode, payload, BucketGroup.Guild, (int)bucket, guildId, options);

public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null) public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
return await SendAsync<GetGatewayResponse>("GET", "gateway", options: options).ConfigureAwait(false); return await SendAsync<GetGatewayResponse>("GET", "gateway", options: options).ConfigureAwait(false);
} }
public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, int shardID = 0, int totalShards = 1, RequestOptions options = null) public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, int shardID = 0, int totalShards = 1, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var props = new Dictionary<string, string> var props = new Dictionary<string, string>
{ {
["$device"] = "Discord.Net" ["$device"] = "Discord.Net"
@@ -203,6 +198,7 @@ namespace Discord.API
} }
public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var msg = new ResumeParams() var msg = new ResumeParams()
{ {
Token = _authToken, Token = _authToken,
@@ -213,10 +209,12 @@ namespace Discord.API
} }
public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null) public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false);
} }
public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null) public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var args = new StatusUpdateParams var args = new StatusUpdateParams
{ {
IdleSince = idleSince, IdleSince = idleSince,
@@ -226,10 +224,12 @@ namespace Discord.API
} }
public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null) public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false); await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
} }
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null) public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
var payload = new VoiceStateUpdateParams var payload = new VoiceStateUpdateParams
{ {
GuildId = guildId, GuildId = guildId,
@@ -241,6 +241,7 @@ namespace Discord.API
} }
public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null) public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
{ {
options = RequestOptions.CreateOrClone(options);
await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false); await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false);
} }
} }


src/Discord.Net.WebSocket/DataStore.cs → src/Discord.Net.WebSocket/ClientState.cs View File

@@ -5,7 +5,7 @@ using System.Linq;


namespace Discord.WebSocket namespace Discord.WebSocket
{ {
internal class DataStore
internal class ClientState
{ {
private const int CollectionConcurrencyLevel = 1; //WebSocket updater/event handler. //TODO: Needs profiling, increase to 2? private const int CollectionConcurrencyLevel = 1; //WebSocket updater/event handler. //TODO: Needs profiling, increase to 2?
private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149 private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149
@@ -29,7 +29,7 @@ namespace Discord.WebSocket
_groupChannels.Select(x => GetChannel(x) as IPrivateChannel)) _groupChannels.Select(x => GetChannel(x) as IPrivateChannel))
.ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count); .ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count);


public DataStore(int guildCount, int dmChannelCount)
public ClientState(int guildCount, int dmChannelCount)
{ {
double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount;
double estimatedUsersCount = guildCount * AverageUsersPerGuild; double estimatedUsersCount = guildCount * AverageUsersPerGuild;

+ 13
- 13
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -19,7 +19,7 @@ using System.Threading.Tasks;


namespace Discord.WebSocket namespace Discord.WebSocket
{ {
public partial class DiscordSocketClient : DiscordClient, IDiscordClient
public partial class DiscordSocketClient : BaseDiscordClient, IDiscordClient
{ {
private readonly ConcurrentQueue<ulong> _largeGuilds; private readonly ConcurrentQueue<ulong> _largeGuilds;
private readonly Logger _gatewayLogger; private readonly Logger _gatewayLogger;
@@ -49,14 +49,14 @@ namespace Discord.WebSocket
internal int MessageCacheSize { get; private set; } internal int MessageCacheSize { get; private set; }
internal int LargeThreshold { get; private set; } internal int LargeThreshold { get; private set; }
internal AudioMode AudioMode { get; private set; } internal AudioMode AudioMode { get; private set; }
internal DataStore DataStore { get; private set; }
internal ClientState State { get; private set; }
internal int ConnectionTimeout { get; private set; } internal int ConnectionTimeout { get; private set; }
internal WebSocketProvider WebSocketProvider { get; private set; } internal WebSocketProvider WebSocketProvider { get; private set; }


public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
public new SocketSelfUser CurrentUser => base.CurrentUser as SocketSelfUser; public new SocketSelfUser CurrentUser => base.CurrentUser as SocketSelfUser;
public IReadOnlyCollection<IPrivateChannel> PrivateChannels => DataStore.PrivateChannels;
internal IReadOnlyCollection<SocketGuild> Guilds => DataStore.Guilds;
public IReadOnlyCollection<IPrivateChannel> PrivateChannels => State.PrivateChannels;
internal IReadOnlyCollection<SocketGuild> Guilds => State.Guilds;


/// <summary> Creates a new REST/WebSocket discord client. </summary> /// <summary> Creates a new REST/WebSocket discord client. </summary>
public DiscordSocketClient() : this(new DiscordSocketConfig()) { } public DiscordSocketClient() : this(new DiscordSocketConfig()) { }
@@ -72,7 +72,7 @@ namespace Discord.WebSocket
AudioMode = config.AudioMode; AudioMode = config.AudioMode;
WebSocketProvider = config.WebSocketProvider; WebSocketProvider = config.WebSocketProvider;
ConnectionTimeout = config.ConnectionTimeout; ConnectionTimeout = config.ConnectionTimeout;
DataStore = new DataStore(0, 0);
State = new ClientState(0, 0);
_nextAudioId = 1; _nextAudioId = 1;
_gatewayLogger = LogManager.CreateLogger("Gateway"); _gatewayLogger = LogManager.CreateLogger("Gateway");
@@ -111,7 +111,7 @@ namespace Discord.WebSocket
protected override async Task OnLoginAsync(TokenType tokenType, string token) protected override async Task OnLoginAsync(TokenType tokenType, string token)
{ {
var voiceRegions = await ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false);
var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true}).ConfigureAwait(false);
_voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id); _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id);
} }
protected override async Task OnLogoutAsync() protected override async Task OnLogoutAsync()
@@ -242,7 +242,7 @@ namespace Discord.WebSocket
while (_largeGuilds.TryDequeue(out guildId)) { } while (_largeGuilds.TryDequeue(out guildId)) { }


//Raise virtual GUILD_UNAVAILABLEs //Raise virtual GUILD_UNAVAILABLEs
foreach (var guild in DataStore.Guilds)
foreach (var guild in State.Guilds)
{ {
if (guild._available) if (guild._available)
await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false); await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false);
@@ -322,7 +322,7 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public SocketGuild GetGuild(ulong id) public SocketGuild GetGuild(ulong id)
{ {
return DataStore.GetGuild(id);
return State.GetGuild(id);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<RestGuild> CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) public Task<RestGuild> CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null)
@@ -331,7 +331,7 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public IChannel GetChannel(ulong id) public IChannel GetChannel(ulong id)
{ {
return DataStore.GetChannel(id);
return State.GetChannel(id);
} }


/// <inheritdoc /> /// <inheritdoc />
@@ -345,12 +345,12 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public IUser GetUser(ulong id) public IUser GetUser(ulong id)
{ {
return DataStore.GetUser(id);
return State.GetUser(id);
} }
/// <inheritdoc /> /// <inheritdoc />
public IUser GetUser(string username, string discriminator) public IUser GetUser(string username, string discriminator)
{ {
return DataStore.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault();
return State.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault();
} }


/// <inheritdoc /> /// <inheritdoc />
@@ -486,7 +486,7 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);


var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
var dataStore = new DataStore(data.Guilds.Length, data.PrivateChannels.Length);
var dataStore = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);


var currentUser = SocketSelfUser.Create(this, data.User); var currentUser = SocketSelfUser.Create(this, data.User);
int unavailableGuilds = 0; int unavailableGuilds = 0;
@@ -505,7 +505,7 @@ namespace Discord.WebSocket
_sessionId = data.SessionId; _sessionId = data.SessionId;
base.CurrentUser = currentUser; base.CurrentUser = currentUser;
_unavailableGuilds = unavailableGuilds; _unavailableGuilds = unavailableGuilds;
DataStore = dataStore;
State = dataStore;
} }
catch (Exception ex) catch (Exception ex)
{ {


+ 1
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs View File

@@ -15,6 +15,7 @@ namespace Discord.WebSocket
internal SocketSelfUser(DiscordSocketClient discord, ulong id) internal SocketSelfUser(DiscordSocketClient discord, ulong id)
: base(discord, id) : base(discord, id)
{ {
Status = UserStatus.Online;
} }
internal new static SocketSelfUser Create(DiscordSocketClient discord, Model model) internal new static SocketSelfUser Create(DiscordSocketClient discord, Model model)
{ {


+ 1
- 1
src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs View File

@@ -15,7 +15,7 @@ namespace Discord.WebSocket
public string Discriminator => DiscriminatorValue.ToString("D4"); public string Discriminator => DiscriminatorValue.ToString("D4");
public string Mention => MentionUtils.MentionUser(Id); public string Mention => MentionUtils.MentionUser(Id);
public virtual Game? Game => null; public virtual Game? Game => null;
public virtual UserStatus Status => UserStatus.Unknown;
public virtual UserStatus Status { get; internal set; }


internal SocketUser(DiscordSocketClient discord, ulong id) internal SocketUser(DiscordSocketClient discord, ulong id)
: base(discord, id) : base(discord, id)


Loading…
Cancel
Save