diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index 8946de96b..5c0aee850 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -113,7 +113,7 @@ namespace Discord.API _authToken = token; _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); - CurrentUser = await GetMyUserAsync(); + CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true }); LoginState = LoginState.LoggedIn; } @@ -155,75 +155,50 @@ namespace Discord.API internal virtual Task ConnectInternalAsync() => Task.CompletedTask; internal virtual Task DisconnectInternalAsync() => Task.CompletedTask; - //REST - public Task SendAsync(string method, string endpoint, - GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) - => SendInternalAsync(method, endpoint, null, true, BucketGroup.Global, (int)bucket, 0, ignoreState, options); - public Task SendAsync(string method, string endpoint, object payload, - GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) - => SendInternalAsync(method, endpoint, payload, true, BucketGroup.Global, (int)bucket, 0, ignoreState, options); - public async Task SendAsync(string method, string endpoint, - GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) where TResponse : class - => DeserializeJson(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Global, (int)bucket, 0, ignoreState, options).ConfigureAwait(false)); - public async Task SendAsync(string method, string endpoint, object payload, GlobalBucket bucket = - GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) where TResponse : class - => DeserializeJson(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 SendAsync(string method, string endpoint, - GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) where TResponse : class - => DeserializeJson(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options).ConfigureAwait(false)); - public async Task SendAsync(string method, string endpoint, object payload, - GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) where TResponse : class - => DeserializeJson(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 multipartArgs, - GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null) - => SendMultipartInternalAsync(method, endpoint, multipartArgs, true, BucketGroup.Global, (int)bucket, 0, options); - public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary multipartArgs, - GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null) where TResponse : class - => DeserializeJson(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Global, (int)bucket, 0, options).ConfigureAwait(false)); - - public Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary multipartArgs, - GuildBucket bucket, ulong guildId, RequestOptions options = null) - => SendMultipartInternalAsync(method, endpoint, multipartArgs, true, BucketGroup.Guild, (int)bucket, guildId, options); - public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary multipartArgs, - GuildBucket bucket, ulong guildId, RequestOptions options = null) where TResponse : class - => DeserializeJson(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Guild, (int)bucket, guildId, options).ConfigureAwait(false)); - //Core - private async Task 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 SendMultipartInternalAsync(string method, string endpoint, IReadOnlyDictionary multipartArgs, bool headerOnly, - BucketGroup group, int bucketId, ulong guildId, RequestOptions options = null) + public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary 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 SendAsync(string method, string endpoint, RequestOptions options = null) where TResponse : class + { + var request = new RestRequest(_restClient, method, endpoint, options); + return DeserializeJson(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); + } + public async Task SendJsonAsync(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(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); + } + public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary multipartArgs, RequestOptions options = null) + { + var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options); + return DeserializeJson(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); + } + + private async Task SendInternalAsync(string method, string endpoint, RestRequest request) + { + if (!request.Options.IgnoreState) + CheckState(); var stopwatch = Stopwatch.StartNew(); - var responseStream = await RequestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, multipartArgs, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false); - int bytes = headerOnly ? 0 : (int)responseStream.Length; + var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false); stopwatch.Stop(); double milliseconds = ToMilliseconds(stopwatch); @@ -235,6 +210,7 @@ namespace Discord.API //Auth public async Task ValidateTokenAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); await SendAsync("GET", "auth/login", options: options).ConfigureAwait(false); } @@ -242,6 +218,7 @@ namespace Discord.API public async Task GetChannelAsync(ulong channelId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); try { @@ -253,6 +230,7 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); try { @@ -266,6 +244,7 @@ namespace Discord.API public async Task> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"guilds/{guildId}/channels", options: options).ConfigureAwait(false); } @@ -275,12 +254,14 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); } public async Task DeleteChannelAsync(ulong channelId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("DELETE", $"channels/{channelId}", options: options).ConfigureAwait(false); } @@ -290,8 +271,9 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); } public async Task ModifyGuildChannelAsync(ulong channelId, ModifyTextChannelParams args, RequestOptions options = null) { @@ -299,8 +281,9 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); } public async Task ModifyGuildChannelAsync(ulong channelId, ModifyVoiceChannelParams args, RequestOptions options = null) { @@ -310,13 +293,15 @@ namespace Discord.API Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); } public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); + options = RequestOptions.CreateOrClone(options); var channels = args.ToArray(); switch (channels.Length) @@ -327,7 +312,7 @@ namespace Discord.API await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false); break; default: - await SendAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false); + await SendJsonAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false); break; } } @@ -337,6 +322,7 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(messageId, 0, nameof(messageId)); + options = RequestOptions.CreateOrClone(options); try { @@ -350,6 +336,7 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); + options = RequestOptions.CreateOrClone(options); int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch); ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; @@ -383,13 +370,15 @@ namespace Discord.API Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); if (args.Content.Length > DiscordConfig.MaxMessageSize) throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", $"channels/{channelId}/messages", args, options: options).ConfigureAwait(false); } public async Task UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); if (args.Content.GetValueOrDefault(null) == null) args.Content = ""; @@ -401,12 +390,13 @@ namespace Discord.API throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); } - return await SendMultipartAsync("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); + return await SendMultipartAsync("POST", $"channels/{channelId}/messages", args.ToDictionary(), options: options).ConfigureAwait(false); } public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(messageId, 0, nameof(messageId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); } @@ -414,9 +404,9 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); + options = RequestOptions.CreateOrClone(options); switch (args.MessageIds.Length) { @@ -426,7 +416,7 @@ namespace Discord.API await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false); break; default: - await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); + await SendJsonAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); break; } } @@ -441,19 +431,22 @@ namespace Discord.API if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); } + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); } public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(messageId, 0, nameof(messageId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); } public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); } @@ -464,13 +457,15 @@ namespace Discord.API Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(targetId, 0, nameof(targetId)); Preconditions.NotNull(args, nameof(args)); + options = RequestOptions.CreateOrClone(options); - await SendAsync("PUT", $"channels/{channelId}/permissions/{targetId}", args, options: options).ConfigureAwait(false); + await SendJsonAsync("PUT", $"channels/{channelId}/permissions/{targetId}", args, options: options).ConfigureAwait(false); } public async Task DeleteChannelPermissionAsync(ulong channelId, ulong targetId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(targetId, 0, nameof(targetId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"channels/{channelId}/permissions/{targetId}", options: options).ConfigureAwait(false); } @@ -480,6 +475,7 @@ namespace Discord.API { Preconditions.GreaterThan(channelId, 0, nameof(channelId)); Preconditions.GreaterThan(messageId, 0, nameof(messageId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("PUT", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); @@ -488,12 +484,14 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(messageId, 0, nameof(messageId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); } public async Task> GetPinsAsync(ulong channelId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"channels/{channelId}/pins", options: options).ConfigureAwait(false); } @@ -503,6 +501,7 @@ namespace Discord.API { Preconditions.GreaterThan(channelId, 0, nameof(channelId)); Preconditions.GreaterThan(userId, 0, nameof(userId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("PUT", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); @@ -511,6 +510,7 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(userId, 0, nameof(userId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); } @@ -519,6 +519,7 @@ namespace Discord.API public async Task GetGuildAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); try { @@ -531,18 +532,21 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("POST", "guilds", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", "guilds", args, options: options).ConfigureAwait(false); } public async Task DeleteGuildAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("DELETE", $"guilds/{guildId}", options: options).ConfigureAwait(false); } public async Task LeaveGuildAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("DELETE", $"users/@me/guilds/{guildId}", options: options).ConfigureAwait(false); } @@ -555,30 +559,34 @@ namespace Discord.API Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); Preconditions.NotNull(args.RegionId, nameof(args.RegionId)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); } public async Task BeginGuildPruneAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("POST", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); } public async Task GetGuildPruneCountAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("GET", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("GET", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); } //Guild Bans public async Task> GetGuildBansAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"guilds/{guildId}/bans", options: options).ConfigureAwait(false); } @@ -588,13 +596,15 @@ namespace Discord.API Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays)); + options = RequestOptions.CreateOrClone(options); - await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); + await SendJsonAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); } public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(userId, 0, nameof(userId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"guilds/{guildId}/bans/{userId}", options: options).ConfigureAwait(false); } @@ -603,6 +613,7 @@ namespace Discord.API public async Task GetGuildEmbedAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); try { @@ -614,14 +625,16 @@ namespace Discord.API { Preconditions.NotNull(args, nameof(args)); Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"guilds/{guildId}/embed", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"guilds/{guildId}/embed", args, options: options).ConfigureAwait(false); } //Guild Integrations public async Task> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); } @@ -630,6 +643,7 @@ namespace Discord.API Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); Preconditions.NotEqual(args.Id, 0, nameof(args.Id)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("POST", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); } @@ -637,6 +651,7 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("DELETE", $"guilds/{guildId}/integrations/{integrationId}", options: options).ConfigureAwait(false); } @@ -647,13 +662,15 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); } public async Task SyncGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("POST", $"guilds/{guildId}/integrations/{integrationId}/sync", options: options).ConfigureAwait(false); } @@ -662,6 +679,7 @@ namespace Discord.API public async Task GetInviteAsync(string inviteId, RequestOptions options = null) { Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); + options = RequestOptions.CreateOrClone(options); //Remove trailing slash if (inviteId[inviteId.Length - 1] == '/') @@ -680,12 +698,14 @@ namespace Discord.API public async Task> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); } public async Task> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); } @@ -695,18 +715,21 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); } public async Task DeleteInviteAsync(string inviteCode, RequestOptions options = null) { Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("DELETE", $"invites/{inviteCode}", options: options).ConfigureAwait(false); } public async Task AcceptInviteAsync(string inviteCode, RequestOptions options = null) { Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); + options = RequestOptions.CreateOrClone(options); await SendAsync("POST", $"invites/{inviteCode}", options: options).ConfigureAwait(false); } @@ -716,6 +739,7 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(userId, 0, nameof(userId)); + options = RequestOptions.CreateOrClone(options); try { @@ -730,6 +754,7 @@ namespace Discord.API Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); + options = RequestOptions.CreateOrClone(options); int limit = args.Limit.GetValueOrDefault(int.MaxValue); ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); @@ -741,6 +766,7 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(userId, 0, nameof(userId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"guilds/{guildId}/members/{userId}", options: options).ConfigureAwait(false); } @@ -749,6 +775,7 @@ namespace Discord.API Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotNull(args, nameof(args)); + options = RequestOptions.CreateOrClone(options); bool isCurrentUser = userId == CurrentUser.Id; @@ -760,7 +787,7 @@ namespace Discord.API } if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) { - await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false); + await SendJsonAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, options: options).ConfigureAwait(false); } } @@ -768,12 +795,14 @@ namespace Discord.API public async Task> GetGuildRolesAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); } public async Task CreateGuildRoleAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync("POST", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); } @@ -781,6 +810,7 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(roleId, 0, nameof(roleId)); + options = RequestOptions.CreateOrClone(options); await SendAsync("DELETE", $"guilds/{guildId}/roles/{roleId}", options: options).ConfigureAwait(false); } @@ -792,13 +822,15 @@ namespace Discord.API Preconditions.AtLeast(args.Color, 0, nameof(args.Color)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); } public async Task> ModifyGuildRolesAsync(ulong guildId, IEnumerable args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); + options = RequestOptions.CreateOrClone(options); var roles = args.ToArray(); switch (roles.Length) @@ -808,7 +840,7 @@ namespace Discord.API case 1: return ImmutableArray.Create(await ModifyGuildRoleAsync(guildId, roles[0].Id, roles[0]).ConfigureAwait(false)); default: - return await SendAsync>("PATCH", $"guilds/{guildId}/roles", args, options: options).ConfigureAwait(false); + return await SendJsonAsync>("PATCH", $"guilds/{guildId}/roles", args, options: options).ConfigureAwait(false); } } @@ -816,6 +848,7 @@ namespace Discord.API public async Task GetUserAsync(ulong userId, RequestOptions options = null) { Preconditions.NotEqual(userId, 0, nameof(userId)); + options = RequestOptions.CreateOrClone(options); try { @@ -827,6 +860,7 @@ namespace Discord.API { Preconditions.NotNullOrEmpty(username, nameof(username)); Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator)); + options = RequestOptions.CreateOrClone(options); try { @@ -839,6 +873,7 @@ namespace Discord.API { Preconditions.NotNullOrEmpty(query, nameof(query)); Preconditions.AtLeast(limit, 0, nameof(limit)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"users?q={Uri.EscapeDataString(query)}&limit={limit}", options: options).ConfigureAwait(false); } @@ -846,54 +881,64 @@ namespace Discord.API //Current User/DMs public async Task GetMyUserAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync("GET", "users/@me", options: options).ConfigureAwait(false); } public async Task> GetMyConnectionsAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", "users/@me/connections", options: options).ConfigureAwait(false); } public async Task> GetMyPrivateChannelsAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", "users/@me/channels", options: options).ConfigureAwait(false); } public async Task> GetMyGuildsAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", "users/@me/guilds", options: options).ConfigureAwait(false); } public async Task GetMyApplicationAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false); } public async Task ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("PATCH", "users/@me", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("PATCH", "users/@me", args, options: options).ConfigureAwait(false); } public async Task ModifyMyNickAsync(ulong guildId, ModifyCurrentUserNickParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args.Nickname, nameof(args.Nickname)); + options = RequestOptions.CreateOrClone(options); - await SendAsync("PATCH", $"guilds/{guildId}/members/@me/nick", args, options: options).ConfigureAwait(false); + await SendJsonAsync("PATCH", $"guilds/{guildId}/members/@me/nick", args, options: options).ConfigureAwait(false); } public async Task CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId)); + options = RequestOptions.CreateOrClone(options); - return await SendAsync("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); } //Voice Regions public async Task> GetVoiceRegionsAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", "voice/regions", options: options).ConfigureAwait(false); } public async Task> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); return await SendAsync>("GET", $"guilds/{guildId}/regions", options: options).ConfigureAwait(false); } diff --git a/src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs b/src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs deleted file mode 100644 index cfc53b0c8..000000000 --- a/src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Discord.Net.Queue -{ - public sealed class Bucket - { - /// Gets the unique identifier for this bucket. - public string Id { get; } - /// Gets the name of this bucket. - public string Name { get; } - /// Gets the amount of requests that may be sent per window. - public int WindowCount { get; } - /// Gets the length of this bucket's window, in seconds. - public int WindowSeconds { get; } - /// Gets the type of account this bucket affects. - public BucketTarget Target { get; } - /// Gets this bucket's parent. - 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; - } - } -} diff --git a/src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs b/src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs deleted file mode 100644 index e7b0a4181..000000000 --- a/src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Discord.Net.Queue -{ - public enum BucketGroup - { - Global, - Guild, - Channel - } -} diff --git a/src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs b/src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs deleted file mode 100644 index 0e5a5d552..000000000 --- a/src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Discord.Net.Queue -{ - public enum BucketTarget - { - Client, - Bot, - Both - } -} diff --git a/src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs b/src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs deleted file mode 100644 index 235e6dfdf..000000000 --- a/src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Discord.Net.Queue -{ - public enum ChannelBucket - { - SendEditMessage, - } -} diff --git a/src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs b/src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs deleted file mode 100644 index fe95ecb79..000000000 --- a/src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Discord.Net.Queue -{ - public enum GlobalBucket - { - GeneralRest, - DirectMessage, - SendEditMessage, - - GeneralGateway, - UpdateStatus, - - GeneralRpc - } -} diff --git a/src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs b/src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs deleted file mode 100644 index 4089fd1e7..000000000 --- a/src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Discord.Net.Queue -{ - public enum GuildBucket - { - SendEditMessage, - DeleteMessage, - DeleteMessages, - ModifyMember, - Nickname - } -} diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueue.cs b/src/Discord.Net.Core/Net/Queue/RequestQueue.cs index 5300905c8..d25c1f340 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueue.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueue.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -10,91 +8,23 @@ namespace Discord.Net.Queue { public class RequestQueue { - public event Func RateLimitTriggered; - - private readonly static ImmutableDictionary _globalLimits; - private readonly static ImmutableDictionary _guildLimits; - private readonly static ImmutableDictionary _channelLimits; + public event Func RateLimitTriggered; + private readonly SemaphoreSlim _lock; - private readonly RequestQueueBucket[] _globalBuckets; - private readonly ConcurrentDictionary[] _guildBuckets; - private readonly ConcurrentDictionary[] _channelBuckets; + private readonly ConcurrentDictionary _buckets; private CancellationTokenSource _clearToken; private CancellationToken _parentToken; private CancellationToken _cancelToken; - - static RequestQueue() - { - _globalLimits = new Dictionary - { - //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 - { - //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 - { - //REST - [ChannelBucket.SendEditMessage] = new Bucket("msg", 10, 10, BucketTarget.Client, GlobalBucket.SendEditMessage), - }.ToImmutableDictionary(); - } - - public static Bucket GetBucketInfo(GlobalBucket bucket) => _globalLimits[bucket]; - public static Bucket GetBucketInfo(GuildBucket bucket) => _guildLimits[bucket]; - public static Bucket GetBucketInfo(ChannelBucket bucket) => _channelLimits[bucket]; - + public RequestQueue() { _lock = new SemaphoreSlim(1, 1); - _globalBuckets = new RequestQueueBucket[_globalLimits.Count]; - foreach (var pair in _globalLimits) - { - //var target = _globalLimits[pair.Key].Target; - //if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot)) - _globalBuckets[(int)pair.Key] = CreateBucket(pair.Value); - } - - _guildBuckets = new ConcurrentDictionary[_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(); - } - - _channelBuckets = new ConcurrentDictionary[_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(); - } - _clearToken = new CancellationTokenSource(); _cancelToken = CancellationToken.None; _parentToken = CancellationToken.None; + + _buckets = new ConcurrentDictionary(); } public async Task SetCancelTokenAsync(CancellationToken cancelToken) { @@ -107,63 +37,29 @@ namespace Discord.Net.Queue finally { _lock.Release(); } } - public async Task SendAsync(RestRequest request, BucketGroup group, int bucketId, ulong objId) + public async Task SendAsync(RestRequest request) { request.CancelToken = _cancelToken; - var bucket = GetBucket(group, bucketId, objId); + var bucket = GetOrCreateBucket(request.Options.BucketId); return await bucket.SendAsync(request).ConfigureAwait(false); } - public async Task SendAsync(WebSocketRequest request, BucketGroup group, int bucketId, ulong objId) + public async Task SendAsync(WebSocketRequest request) { request.CancelToken = _cancelToken; - var bucket = GetBucket(group, bucketId, objId); + var bucket = GetOrCreateBucket(request.Options.BucketId); return await bucket.SendAsync(request).ConfigureAwait(false); } - private RequestQueueBucket CreateBucket(Bucket def) + private RequestQueueBucket GetOrCreateBucket(string id) { - var parent = def.Parent != null ? GetGlobalBucket(def.Parent.Value) : null; - return new RequestQueueBucket(this, def, parent); + return new RequestQueueBucket(this, id, null); } - public void DestroyGuildBucket(GuildBucket type, ulong guildId) - { - //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 RequestQueueBucket bucket; - _channelBuckets[(int)type].TryRemove(channelId, out bucket); - } - - private RequestQueueBucket GetBucket(BucketGroup group, int bucketId, ulong objId) - { - switch (group) - { - case BucketGroup.Global: - return GetGlobalBucket((GlobalBucket)bucketId); - case BucketGroup.Guild: - return GetGuildBucket((GuildBucket)bucketId, objId); - case BucketGroup.Channel: - return GetChannelBucket((ChannelBucket)bucketId, objId); - default: - throw new ArgumentException($"Unknown bucket group: {group}", nameof(group)); - } - } - private RequestQueueBucket GetGlobalBucket(GlobalBucket type) - { - return _globalBuckets[(int)type]; - } - private RequestQueueBucket GetGuildBucket(GuildBucket type, ulong guildId) - { - return _guildBuckets[(int)type].GetOrAdd(guildId, _ => CreateBucket(_guildLimits[type])); - } - private RequestQueueBucket GetChannelBucket(ChannelBucket type, ulong channelId) - { - return _channelBuckets[(int)type].GetOrAdd(channelId, _ => CreateBucket(_channelLimits[type])); + _buckets.TryRemove(id, out bucket); } public async Task ClearAsync() @@ -181,9 +77,9 @@ namespace Discord.Net.Queue finally { _lock.Release(); } } - internal async Task RaiseRateLimitTriggered(string id, Bucket bucket, int millis) + internal async Task RaiseRateLimitTriggered(string id, RequestQueueBucket bucket, int? millis) { - await RateLimitTriggered.Invoke(id, bucket, millis).ConfigureAwait(false); + await RateLimitTriggered(id, bucket, millis).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs index 2ec9a9e02..00668c315 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Discord.Net.Queue { - internal class RequestQueueBucket + public class RequestQueueBucket { private readonly RequestQueue _queue; private readonly SemaphoreSlim _semaphore; @@ -15,16 +15,15 @@ namespace Discord.Net.Queue private int _pauseEndTick; private TaskCompletionSource _resumeNotifier; - public Bucket Definition { get; } + public string Id { get; } public RequestQueueBucket Parent { get; } - public Task _resetTask { get; } + public int WindowSeconds { get; } - public RequestQueueBucket(RequestQueue queue, Bucket definition, RequestQueueBucket parent = null) + public RequestQueueBucket(RequestQueue queue, string id, RequestQueueBucket parent = null) { _queue = queue; - Definition = definition; - if (definition.WindowCount != 0) - _semaphore = new SemaphoreSlim(definition.WindowCount, definition.WindowCount); + Id = id; + _semaphore = new SemaphoreSlim(5, 5); Parent = parent; _pauseLock = new object(); @@ -44,12 +43,8 @@ namespace Discord.Net.Queue { //When a 429 occurs, we drop all our locks. //This is generally safe though since 429s actually occuring should be very rare. - RequestQueueBucket bucket; - bool success = FindBucket(ex.BucketId, out bucket); - - await _queue.RaiseRateLimitTriggered(ex.BucketId, success ? bucket.Definition : null, ex.RetryAfterMilliseconds).ConfigureAwait(false); - - bucket.Pause(ex.RetryAfterMilliseconds); + await _queue.RaiseRateLimitTriggered(Id, this, ex.RetryAfterMilliseconds).ConfigureAwait(false); + Pause(ex.RetryAfterMilliseconds); } } } @@ -107,24 +102,7 @@ namespace Discord.Net.Queue QueueExitAsync(); } } - - private bool FindBucket(string id, out RequestQueueBucket bucket) - { - //Keep going up until we find a bucket with matching id or we're at the topmost bucket - if (Definition.Id == id) - { - bucket = this; - return true; - } - else if (Parent == null) - { - bucket = this; - return false; - } - else - return Parent.FindBucket(id, out bucket); - } - + private void Pause(int milliseconds) { lock (_pauseLock) @@ -151,13 +129,22 @@ namespace Discord.Net.Queue int millis = unchecked(endTick.Value - Environment.TickCount); if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) throw new TimeoutException(); + + if (!await _semaphore.WaitAsync(0)) + { + await _queue.RaiseRateLimitTriggered(Id, this, null).ConfigureAwait(false); + + millis = unchecked(endTick.Value - Environment.TickCount); + if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) + throw new TimeoutException(); + } } else await _semaphore.WaitAsync().ConfigureAwait(false); } private async Task QueueExitAsync() { - await Task.Delay(Definition.WindowSeconds * 1000).ConfigureAwait(false); + await Task.Delay(WindowSeconds * 1000).ConfigureAwait(false); _semaphore.Release(); } } diff --git a/src/Discord.Net.Core/Net/Queue/IQueuedRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/IQueuedRequest.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/IQueuedRequest.cs rename to src/Discord.Net.Core/Net/Queue/Requests/IQueuedRequest.cs diff --git a/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs new file mode 100644 index 000000000..d715b790c --- /dev/null +++ b/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs @@ -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 SendAsync() + { + return await Client.SendAsync(Method, Endpoint, Json, Options).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs new file mode 100644 index 000000000..f30969e43 --- /dev/null +++ b/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs @@ -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 MultipartParams { get; } + + public MultipartRestRequest(IRestClient client, string method, string endpoint, IReadOnlyDictionary multipartParams, RequestOptions options) + : base(client, method, endpoint, options) + { + MultipartParams = multipartParams; + } + + public override async Task SendAsync() + { + return await Client.SendAsync(Method, Endpoint, MultipartParams, Options).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs new file mode 100644 index 000000000..655a79567 --- /dev/null +++ b/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs @@ -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 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(); + } + + public virtual async Task SendAsync() + { + return await Client.SendAsync(Method, Endpoint, Options).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs similarity index 82% rename from src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs rename to src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs index 83f40a4d6..796517c85 100644 --- a/src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs @@ -13,16 +13,17 @@ namespace Discord.Net.Queue public bool IsText { get; } public int? TimeoutTick { get; } public TaskCompletionSource Promise { get; } - public CancellationToken CancelToken { get; set; } - + public RequestOptions Options { get; } + public CancellationToken CancelToken { get; internal set; } + public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options) { - if (options == null) - options = RequestOptions.Default; + Preconditions.NotNull(options, nameof(options)); Client = client; Data = data; IsText = isText; + Options = options; TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; Promise = new TaskCompletionSource(); } diff --git a/src/Discord.Net.Core/Net/Queue/RestRequest.cs b/src/Discord.Net.Core/Net/Queue/RestRequest.cs deleted file mode 100644 index 4be4c746c..000000000 --- a/src/Discord.Net.Core/Net/Queue/RestRequest.cs +++ /dev/null @@ -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 MultipartParams { get; } - public TaskCompletionSource 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 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(); - } - - public async Task 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); - } - } -} diff --git a/src/Discord.Net.Core/Net/RateLimitException.cs b/src/Discord.Net.Core/Net/RateLimitException.cs index ff594155a..cb0ca7f28 100644 --- a/src/Discord.Net.Core/Net/RateLimitException.cs +++ b/src/Discord.Net.Core/Net/RateLimitException.cs @@ -4,13 +4,13 @@ namespace Discord.Net { public class HttpRateLimitException : HttpException { - public string BucketId { get; } + public string Id { get; } public int RetryAfterMilliseconds { get; } public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason) : base((HttpStatusCode)429, reason) { - BucketId = bucketId; + Id = bucketId; RetryAfterMilliseconds = retryAfterMilliseconds; } } diff --git a/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs b/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs index bcad2ece4..b06df37b8 100644 --- a/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs +++ b/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs @@ -67,22 +67,22 @@ namespace Discord.Net.Rest _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; } - public async Task SendAsync(string method, string endpoint, bool headerOnly = false) + public async Task SendAsync(string method, string endpoint, RequestOptions options) { string uri = Path.Combine(_baseUrl, endpoint); using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) - return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); + return await SendInternalAsync(restRequest, options).ConfigureAwait(false); } - public async Task SendAsync(string method, string endpoint, string json, bool headerOnly = false) + public async Task SendAsync(string method, string endpoint, string json, RequestOptions options) { string uri = Path.Combine(_baseUrl, endpoint); using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) { restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); - return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); + return await SendInternalAsync(restRequest, options).ConfigureAwait(false); } } - public async Task SendAsync(string method, string endpoint, IReadOnlyDictionary multipartParams, bool headerOnly = false) + public async Task SendAsync(string method, string endpoint, IReadOnlyDictionary multipartParams, RequestOptions options) { string uri = Path.Combine(_baseUrl, endpoint); using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) @@ -110,11 +110,11 @@ namespace Discord.Net.Rest } } restRequest.Content = content; - return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); + return await SendInternalAsync(restRequest, options).ConfigureAwait(false); } } - private async Task SendInternalAsync(HttpRequestMessage request, bool headerOnly) + private async Task SendInternalAsync(HttpRequestMessage request, RequestOptions options) { while (true) { @@ -154,7 +154,7 @@ namespace Discord.Net.Rest throw new HttpException(response.StatusCode, reason); } - if (headerOnly) + if (options.HeaderOnly) return null; else return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.Core/Net/Rest/IRestClient.cs b/src/Discord.Net.Core/Net/Rest/IRestClient.cs index 57b5f91ca..7f90060b6 100644 --- a/src/Discord.Net.Core/Net/Rest/IRestClient.cs +++ b/src/Discord.Net.Core/Net/Rest/IRestClient.cs @@ -11,8 +11,8 @@ namespace Discord.Net.Rest void SetHeader(string key, string value); void SetCancelToken(CancellationToken cancelToken); - Task SendAsync(string method, string endpoint, bool headerOnly = false); - Task SendAsync(string method, string endpoint, string json, bool headerOnly = false); - Task SendAsync(string method, string endpoint, IReadOnlyDictionary multipartParams, bool headerOnly = false); + Task SendAsync(string method, string endpoint, RequestOptions options); + Task SendAsync(string method, string endpoint, string json, RequestOptions options); + Task SendAsync(string method, string endpoint, IReadOnlyDictionary multipartParams, RequestOptions options); } } diff --git a/src/Discord.Net.Core/RequestOptions.cs b/src/Discord.Net.Core/RequestOptions.cs index 242642d56..452b75444 100644 --- a/src/Discord.Net.Core/RequestOptions.cs +++ b/src/Discord.Net.Core/RequestOptions.cs @@ -6,10 +6,24 @@ /// 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. public int? Timeout { get; set; } + public string BucketId { get; set; } + public bool HeaderOnly { get; internal set; } + + internal bool IgnoreState { get; set; } + + public static RequestOptions CreateOrClone(RequestOptions options) + { + if (options == null) + return new RequestOptions(); + else + return options.Clone(); + } public RequestOptions() { Timeout = 30000; } + + public RequestOptions Clone() => MemberwiseClone() as RequestOptions; } } diff --git a/src/Discord.Net.Rest/DiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs similarity index 96% rename from src/Discord.Net.Rest/DiscordClient.cs rename to src/Discord.Net.Rest/BaseDiscordClient.cs index 5bf0f2c89..b6ea634d7 100644 --- a/src/Discord.Net.Rest/DiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -8,7 +8,7 @@ using System.Collections.Immutable; namespace Discord.Rest { - public abstract class DiscordClient : IDiscordClient + public abstract class BaseDiscordClient : IDiscordClient { public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } private readonly AsyncEvent> _logEvent = new AsyncEvent>(); @@ -29,7 +29,7 @@ namespace Discord.Rest public ISelfUser CurrentUser { get; protected set; } /// Creates a new REST-only discord client. - internal DiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) + internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) { ApiClient = client; LogManager = new LogManager(config.LogLevel); @@ -73,6 +73,7 @@ namespace Discord.Rest try { + await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false); await OnLoginAsync(tokenType, token).ConfigureAwait(false); LoginState = LoginState.LoggedIn; } diff --git a/src/Discord.Net.Rest/ClientHelper.cs b/src/Discord.Net.Rest/ClientHelper.cs index 07868182d..02ccb2c5b 100644 --- a/src/Discord.Net.Rest/ClientHelper.cs +++ b/src/Discord.Net.Rest/ClientHelper.cs @@ -10,13 +10,13 @@ namespace Discord.Rest internal static class ClientHelper { //Applications - public static async Task GetApplicationInfoAsync(DiscordClient client) + public static async Task GetApplicationInfoAsync(BaseDiscordClient client) { var model = await client.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); return RestApplication.Create(client, model); } - public static async Task GetChannelAsync(DiscordClient client, + public static async Task GetChannelAsync(BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetChannelAsync(id).ConfigureAwait(false); @@ -24,19 +24,19 @@ namespace Discord.Rest return RestChannel.Create(client, model); return null; } - public static async Task> GetPrivateChannelsAsync(DiscordClient client) + public static async Task> GetPrivateChannelsAsync(BaseDiscordClient client) { var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); return models.Select(x => RestDMChannel.Create(client, x)).ToImmutableArray(); } - public static async Task> GetConnectionsAsync(DiscordClient client) + public static async Task> GetConnectionsAsync(BaseDiscordClient client) { var models = await client.ApiClient.GetMyConnectionsAsync().ConfigureAwait(false); return models.Select(x => RestConnection.Create(x)).ToImmutableArray(); } - public static async Task GetInviteAsync(DiscordClient client, + public static async Task GetInviteAsync(BaseDiscordClient client, string inviteId) { var model = await client.ApiClient.GetInviteAsync(inviteId).ConfigureAwait(false); @@ -45,7 +45,7 @@ namespace Discord.Rest return null; } - public static async Task GetGuildAsync(DiscordClient client, + public static async Task GetGuildAsync(BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetGuildAsync(id).ConfigureAwait(false); @@ -53,7 +53,7 @@ namespace Discord.Rest return RestGuild.Create(client, model); return null; } - public static async Task GetGuildEmbedAsync(DiscordClient client, + public static async Task GetGuildEmbedAsync(BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetGuildEmbedAsync(id).ConfigureAwait(false); @@ -61,12 +61,12 @@ namespace Discord.Rest return RestGuildEmbed.Create(model); return null; } - public static async Task> GetGuildSummariesAsync(DiscordClient client) + public static async Task> GetGuildSummariesAsync(BaseDiscordClient client) { var models = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); return models.Select(x => RestUserGuild.Create(client, x)).ToImmutableArray(); } - public static async Task> GetGuildsAsync(DiscordClient client) + public static async Task> GetGuildsAsync(BaseDiscordClient client) { var summaryModels = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); var guilds = ImmutableArray.CreateBuilder(summaryModels.Count); @@ -78,7 +78,7 @@ namespace Discord.Rest } return guilds.ToImmutable(); } - public static async Task CreateGuildAsync(DiscordClient client, + public static async Task CreateGuildAsync(BaseDiscordClient client, string name, IVoiceRegion region, Stream jpegIcon = null) { var args = new CreateGuildParams(name, region.Id); @@ -86,7 +86,7 @@ namespace Discord.Rest return RestGuild.Create(client, model); } - public static async Task GetUserAsync(DiscordClient client, + public static async Task GetUserAsync(BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetUserAsync(id).ConfigureAwait(false); @@ -94,7 +94,7 @@ namespace Discord.Rest return RestUser.Create(client, model); return null; } - public static async Task GetUserAsync(DiscordClient client, + public static async Task GetUserAsync(BaseDiscordClient client, string username, string discriminator) { var model = await client.ApiClient.GetUserAsync(username, discriminator).ConfigureAwait(false); @@ -103,12 +103,12 @@ namespace Discord.Rest return null; } - public static async Task> GetVoiceRegionsAsync(DiscordClient client) + public static async Task> GetVoiceRegionsAsync(BaseDiscordClient client) { var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); return models.Select(x => RestVoiceRegion.Create(client, x)).ToImmutableArray(); } - public static async Task GetVoiceRegionAsync(DiscordClient client, + public static async Task GetVoiceRegionAsync(BaseDiscordClient client, string id) { var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 8bc3ca9c3..6fa954116 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Discord.Rest { - public class DiscordRestClient : DiscordClient, IDiscordClient + public class DiscordRestClient : BaseDiscordClient, IDiscordClient { public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser; @@ -15,10 +15,10 @@ namespace Discord.Rest private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) => 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); + return Task.CompletedTask; } /// diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 63f68049f..af8ab3fc0 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -12,33 +12,33 @@ namespace Discord.Rest internal static class ChannelHelper { //General - public static async Task GetAsync(IGuildChannel channel, DiscordClient client) + public static async Task GetAsync(IGuildChannel channel, BaseDiscordClient client) { return await client.ApiClient.GetChannelAsync(channel.GuildId, channel.Id).ConfigureAwait(false); } - public static async Task GetAsync(IPrivateChannel channel, DiscordClient client) + public static async Task GetAsync(IPrivateChannel channel, BaseDiscordClient client) { 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); } - public static async Task ModifyAsync(IGuildChannel channel, DiscordClient client, + public static async Task ModifyAsync(IGuildChannel channel, BaseDiscordClient client, Action func) { var args = new ModifyGuildChannelParams(); func(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 func) { var args = new ModifyTextChannelParams(); func(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 func) { var args = new ModifyVoiceChannelParams(); @@ -47,12 +47,12 @@ namespace Discord.Rest } //Invites - public static async Task> GetInvitesAsync(IChannel channel, DiscordClient client) + public static async Task> GetInvitesAsync(IChannel channel, BaseDiscordClient client) { var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id); return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); } - public static async Task CreateInviteAsync(IChannel channel, DiscordClient client, + public static async Task CreateInviteAsync(IChannel channel, BaseDiscordClient client, int? maxAge, int? maxUses, bool isTemporary) { var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; @@ -65,13 +65,13 @@ namespace Discord.Rest } //Messages - public static async Task GetMessageAsync(IChannel channel, DiscordClient client, + public static async Task GetMessageAsync(IChannel channel, BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id).ConfigureAwait(false); return RestMessage.Create(client, model); } - public static IAsyncEnumerable> GetMessagesAsync(IChannel channel, DiscordClient client, + public static IAsyncEnumerable> GetMessagesAsync(IChannel channel, BaseDiscordClient client, ulong? fromMessageId = null, Direction dir = Direction.Before, int limit = DiscordConfig.MaxMessagesPerBatch) { //TODO: Test this with Around direction @@ -102,13 +102,13 @@ namespace Discord.Rest count: (uint)limit ); } - public static async Task> GetPinnedMessagesAsync(IChannel channel, DiscordClient client) + public static async Task> GetPinnedMessagesAsync(IChannel channel, BaseDiscordClient client) { var models = await client.ApiClient.GetPinsAsync(channel.Id).ConfigureAwait(false); return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); } - public static async Task SendMessageAsync(IChannel channel, DiscordClient client, + public static async Task SendMessageAsync(IChannel channel, BaseDiscordClient client, string text, bool isTTS) { var args = new CreateMessageParams(text) { IsTTS = isTTS }; @@ -116,14 +116,14 @@ namespace Discord.Rest return RestUserMessage.Create(client, model); } - public static Task SendFileAsync(IChannel channel, DiscordClient client, + public static Task SendFileAsync(IChannel channel, BaseDiscordClient client, string filePath, string text, bool isTTS) { string filename = Path.GetFileName(filePath); using (var file = File.OpenRead(filePath)) return SendFileAsync(channel, client, file, filename, text, isTTS); } - public static async Task SendFileAsync(IChannel channel, DiscordClient client, + public static async Task SendFileAsync(IChannel channel, BaseDiscordClient client, Stream stream, string filename, string text, bool isTTS) { var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; @@ -131,7 +131,7 @@ namespace Discord.Rest return RestUserMessage.Create(client, model); } - public static async Task DeleteMessagesAsync(IChannel channel, DiscordClient client, + public static async Task DeleteMessagesAsync(IChannel channel, BaseDiscordClient client, IEnumerable messages) { var args = new DeleteMessagesParams(messages.Select(x => x.Id).ToArray()); @@ -139,31 +139,31 @@ namespace Discord.Rest } //Permission Overwrites - public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordClient client, + public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, IUser user, OverwritePermissions perms) { var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue); 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) { var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue); 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) { 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) { await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, role.Id).ConfigureAwait(false); } //Users - public static async Task GetUserAsync(IGuildChannel channel, DiscordClient client, + public static async Task GetUserAsync(IGuildChannel channel, BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetGuildMemberAsync(channel.GuildId, id); @@ -175,7 +175,7 @@ namespace Discord.Rest return user; } - public static IAsyncEnumerable> GetUsersAsync(IGuildChannel channel, DiscordClient client, + public static IAsyncEnumerable> GetUsersAsync(IGuildChannel channel, BaseDiscordClient client, ulong? froUserId = null, uint? limit = DiscordConfig.MaxUsersPerBatch) { return new PagedAsyncEnumerable( @@ -203,7 +203,7 @@ namespace Discord.Rest } //Typing - public static IDisposable EnterTypingState(IChannel channel, DiscordClient client) + public static IDisposable EnterTypingState(IChannel channel, BaseDiscordClient client) { throw new NotImplementedException(); //TODO: Impl } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs index ada4008ec..868bc7e37 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs @@ -11,11 +11,11 @@ namespace Discord.Rest [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public abstract class RestChannel : RestEntity, IChannel, IUpdateable { - internal RestChannel(DiscordClient discord, ulong id) + internal RestChannel(BaseDiscordClient discord, ulong id) : base(discord, id) { } - internal static RestChannel Create(DiscordClient discord, Model model) + internal static RestChannel Create(BaseDiscordClient discord, Model model) { switch (model.Type) { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 973a001a8..6e7451f22 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -17,13 +17,13 @@ namespace Discord.Rest public IReadOnlyCollection Users => ImmutableArray.Create(CurrentUser, Recipient); - internal RestDMChannel(DiscordClient discord, ulong id, ulong recipientId) + internal RestDMChannel(BaseDiscordClient discord, ulong id, ulong recipientId) : base(discord, id) { Recipient = new RestUser(Discord, recipientId); 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index 00be0ca11..0324ba1fe 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -21,11 +21,11 @@ namespace Discord.Rest public IReadOnlyCollection Recipients => _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) { } - 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 4ec79496d..d6a4143d3 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -21,12 +21,12 @@ namespace Discord.Rest public string Name { 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) { GuildId = guildId; } - internal new static RestGuildChannel Create(DiscordClient discord, Model model) + internal new static RestGuildChannel Create(BaseDiscordClient discord, Model model) { switch (model.Type) { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 69917f862..6e15825e0 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -16,11 +16,11 @@ namespace Discord.Rest 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) { } - 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs index 2b23eaad7..4653ee381 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -16,11 +16,11 @@ namespace Discord.Rest public int Bitrate { 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) { } - 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index e88c38504..a00f6243f 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -13,7 +13,7 @@ namespace Discord.Rest internal static class GuildHelper { //General - public static async Task ModifyAsync(IGuild guild, DiscordClient client, + public static async Task ModifyAsync(IGuild guild, BaseDiscordClient client, Action 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); } - public static async Task ModifyEmbedAsync(IGuild guild, DiscordClient client, + public static async Task ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, Action func) { if (func == null) throw new NullReferenceException(nameof(func)); @@ -37,46 +37,46 @@ namespace Discord.Rest func(args); 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 args) { await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args).ConfigureAwait(false); } - public static async Task> ModifyRolesAsync(IGuild guild, DiscordClient client, + public static async Task> ModifyRolesAsync(IGuild guild, BaseDiscordClient client, IEnumerable args) { 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); } - 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); } //Bans - public static async Task> GetBansAsync(IGuild guild, DiscordClient client) + public static async Task> GetBansAsync(IGuild guild, BaseDiscordClient client) { var models = await client.ApiClient.GetGuildBansAsync(guild.Id); 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) { var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays }; 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) { await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId); } //Channels - public static async Task GetChannelAsync(IGuild guild, DiscordClient client, + public static async Task GetChannelAsync(IGuild guild, BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetChannelAsync(guild.Id, id).ConfigureAwait(false); @@ -84,12 +84,12 @@ namespace Discord.Rest return RestGuildChannel.Create(client, model); return null; } - public static async Task> GetChannelsAsync(IGuild guild, DiscordClient client) + public static async Task> GetChannelsAsync(IGuild guild, BaseDiscordClient client) { var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false); return models.Select(x => RestGuildChannel.Create(client, x)).ToImmutableArray(); } - public static async Task CreateTextChannelAsync(IGuild guild, DiscordClient client, + public static async Task CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, string 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); return RestTextChannel.Create(client, model); } - public static async Task CreateVoiceChannelAsync(IGuild guild, DiscordClient client, + public static async Task CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, string name) { if (name == null) throw new ArgumentNullException(nameof(name)); @@ -109,12 +109,12 @@ namespace Discord.Rest } //Integrations - public static async Task> GetIntegrationsAsync(IGuild guild, DiscordClient client) + public static async Task> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client) { var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id).ConfigureAwait(false); return models.Select(x => RestGuildIntegration.Create(client, x)).ToImmutableArray(); } - public static async Task CreateIntegrationAsync(IGuild guild, DiscordClient client, + public static async Task CreateIntegrationAsync(IGuild guild, BaseDiscordClient client, ulong id, string type) { var args = new CreateGuildIntegrationParams(id, type); @@ -123,14 +123,14 @@ namespace Discord.Rest } //Invites - public static async Task> GetInvitesAsync(IGuild guild, DiscordClient client) + public static async Task> GetInvitesAsync(IGuild guild, BaseDiscordClient client) { var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id).ConfigureAwait(false); return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); } //Roles - public static async Task CreateRoleAsync(IGuild guild, DiscordClient client, + public static async Task CreateRoleAsync(IGuild guild, BaseDiscordClient client, string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false) { if (name == null) throw new ArgumentNullException(nameof(name)); @@ -150,7 +150,7 @@ namespace Discord.Rest } //Users - public static async Task GetUserAsync(IGuild guild, DiscordClient client, + public static async Task GetUserAsync(IGuild guild, BaseDiscordClient client, ulong id) { var model = await client.ApiClient.GetGuildMemberAsync(guild.Id, id).ConfigureAwait(false); @@ -158,17 +158,17 @@ namespace Discord.Rest return RestGuildUser.Create(client, model); return null; } - public static async Task GetCurrentUserAsync(IGuild guild, DiscordClient client) + public static async Task GetCurrentUserAsync(IGuild guild, BaseDiscordClient client) { return await GetUserAsync(guild, client, client.CurrentUser.Id).ConfigureAwait(false); } - public static async Task> GetUsersAsync(IGuild guild, DiscordClient client) + public static async Task> GetUsersAsync(IGuild guild, BaseDiscordClient client) { var args = new GetGuildMembersParams(); var models = await client.ApiClient.GetGuildMembersAsync(guild.Id, args).ConfigureAwait(false); return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray(); } - public static async Task PruneUsersAsync(IGuild guild, DiscordClient client, + public static async Task PruneUsersAsync(IGuild guild, BaseDiscordClient client, int days = 30, bool simulate = false) { var args = new GuildPruneParams(days); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs b/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs index 0d919bf0f..d28856d6d 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs @@ -14,7 +14,7 @@ namespace Discord.Rest User = user; 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); } diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 48ddfa607..601329b5c 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -39,11 +39,11 @@ namespace Discord.Rest public IReadOnlyCollection Emojis => _emojis; public IReadOnlyCollection Features => _features; - internal RestGuild(DiscordClient client, ulong id) + internal RestGuild(BaseDiscordClient client, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs index bb40a4da4..79b75170b 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs @@ -25,11 +25,11 @@ namespace Discord.Rest public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); - internal RestGuildIntegration(DiscordClient discord, ulong id) + internal RestGuildIntegration(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs index fab07ee33..d222c2d1d 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs @@ -15,11 +15,11 @@ namespace Discord.Rest public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); - internal RestUserGuild(DiscordClient discord, ulong id) + internal RestUserGuild(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs b/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs index 465b2e185..47fd2cd19 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs @@ -13,11 +13,11 @@ namespace Discord public string SampleHostname { get; private set; } public int SamplePort { get; private set; } - internal RestVoiceRegion(DiscordClient client, string id) + internal RestVoiceRegion(BaseDiscordClient client, string 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs index 3ede5d042..23e0ddab7 100644 --- a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs +++ b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs @@ -5,15 +5,15 @@ namespace Discord.Rest { internal static class InviteHelper { - public static async Task GetAsync(IInvite invite, DiscordClient client) + public static async Task GetAsync(IInvite invite, BaseDiscordClient client) { 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); } - 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); } diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs index 3cf11b4c6..6beabacb1 100644 --- a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs @@ -16,11 +16,11 @@ namespace Discord.Rest public string Code => Id; public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; - internal RestInvite(DiscordClient discord, string id) + internal RestInvite(BaseDiscordClient discord, string 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs index 25ce2fb24..131211a39 100644 --- a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs @@ -18,11 +18,11 @@ namespace Discord.Rest public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); - internal RestInviteMetadata(DiscordClient discord, string id) + internal RestInviteMetadata(BaseDiscordClient discord, string 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index f1f0e8461..28338635b 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -6,26 +6,26 @@ namespace Discord.Rest { 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); } - public static async Task ModifyAsync(IMessage msg, DiscordClient client, Action func) + public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action func) { var args = new ModifyMessageParams(); func(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); } - 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); } - 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); } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index 33ce9c8f0..660cdadf3 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -29,12 +29,12 @@ namespace Discord.Rest 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) { ChannelId = channelId; } - internal static RestMessage Create(DiscordClient discord, Model model) + internal static RestMessage Create(BaseDiscordClient discord, Model model) { if (model.Type == MessageType.Default) return RestUserMessage.Create(discord, model); diff --git a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs index 837fd158f..9cef3f479 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs @@ -8,11 +8,11 @@ namespace Discord.Rest { 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) { } - 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index b81fbf17f..824de3c79 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -30,11 +30,11 @@ namespace Discord.Rest public override IReadOnlyCollection MentionedRoles => _mentionedRoles; public override IReadOnlyCollection MentionedUsers => _mentionedUsers; - internal RestUserMessage(DiscordClient discord, ulong id, ulong channelId) + internal RestUserMessage(BaseDiscordClient discord, ulong id, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/RestApplication.cs b/src/Discord.Net.Rest/Entities/RestApplication.cs index 035e6c212..e0b119381 100644 --- a/src/Discord.Net.Rest/Entities/RestApplication.cs +++ b/src/Discord.Net.Rest/Entities/RestApplication.cs @@ -17,11 +17,11 @@ namespace Discord.Rest public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); - internal RestApplication(DiscordClient discord, ulong id) + internal RestApplication(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/RestEntity.cs b/src/Discord.Net.Rest/Entities/RestEntity.cs index d5bad8147..adf7b6921 100644 --- a/src/Discord.Net.Rest/Entities/RestEntity.cs +++ b/src/Discord.Net.Rest/Entities/RestEntity.cs @@ -5,10 +5,10 @@ namespace Discord.Rest public abstract class RestEntity : IEntity where T : IEquatable { - public DiscordClient Discord { get; } + public BaseDiscordClient Discord { get; } public T Id { get; } - internal RestEntity(DiscordClient discord, T id) + internal RestEntity(BaseDiscordClient discord, T id) { Discord = discord; Id = id; diff --git a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs index b0f3255f4..35d4a14f5 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs @@ -21,11 +21,11 @@ namespace Discord.Rest public bool IsEveryone => Id == Guild.Id; public string Mention => MentionUtils.MentionRole(Id); - internal RestRole(DiscordClient discord, ulong id) + internal RestRole(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index dc66e9288..c9438f10d 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -7,11 +7,11 @@ namespace Discord.Rest internal static class RoleHelper { //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); } - public static async Task ModifyAsync(IRole role, DiscordClient client, + public static async Task ModifyAsync(IRole role, BaseDiscordClient client, Action func) { var args = new ModifyGuildRoleParams(); diff --git a/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs index a9ef2907d..951bd2e7c 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs @@ -6,11 +6,11 @@ namespace Discord.Rest [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestGroupUser : RestUser, IGroupUser { - internal RestGroupUser(DiscordClient discord, ulong id) + internal RestGroupUser(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index 489672e31..2fe25b416 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -21,11 +21,11 @@ namespace Discord.Rest public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); - internal RestGuildUser(DiscordClient discord, ulong id) + internal RestGuildUser(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs index 115db9ac5..17c24e7d2 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs @@ -13,11 +13,11 @@ namespace Discord.Rest public bool IsVerified { get; private set; } public bool IsMfaEnabled { get; private set; } - internal RestSelfUser(DiscordClient discord, ulong id) + internal RestSelfUser(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 492a18bd4..6a42de397 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -18,11 +18,11 @@ namespace Discord.Rest public virtual Game? Game => null; public virtual UserStatus Status => UserStatus.Unknown; - internal RestUser(DiscordClient discord, ulong id) + internal RestUser(BaseDiscordClient discord, ulong 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); entity.Update(model); diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs index da8e57fad..ddf8055d9 100644 --- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -8,22 +8,22 @@ namespace Discord.Rest { internal static class UserHelper { - public static async Task GetAsync(IUser user, DiscordClient client) + public static async Task GetAsync(IUser user, BaseDiscordClient client) { return await client.ApiClient.GetUserAsync(user.Id); } - public static async Task GetAsync(ISelfUser user, DiscordClient client) + public static async Task GetAsync(ISelfUser user, BaseDiscordClient client) { var model = await client.ApiClient.GetMyUserAsync(); if (model.Id != user.Id) throw new InvalidOperationException("Unable to update this object using a different token."); return model; } - public static async Task GetAsync(IGuildUser user, DiscordClient client) + public static async Task GetAsync(IGuildUser user, BaseDiscordClient client) { return await client.ApiClient.GetGuildMemberAsync(user.GuildId, user.Id); } - public static async Task ModifyAsync(ISelfUser user, DiscordClient client, Action func) + public static async Task ModifyAsync(ISelfUser user, BaseDiscordClient client, Action func) { if (user.Id != client.CurrentUser.Id) throw new InvalidOperationException("Unable to modify this object using a different token."); @@ -32,19 +32,19 @@ namespace Discord.Rest func(args); await client.ApiClient.ModifySelfAsync(args); } - public static async Task ModifyAsync(IGuildUser user, DiscordClient client, Action func) + public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action func) { var args = new ModifyGuildMemberParams(); func(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); } - public static async Task CreateDMChannelAsync(IUser user, DiscordClient client) + public static async Task CreateDMChannelAsync(IUser user, BaseDiscordClient client) { var args = new CreateDMChannelParams(user.Id); return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args)); diff --git a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs index 666d92105..d413fd815 100644 --- a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs @@ -206,19 +206,16 @@ namespace Discord.API } //Core - public Task SendRpcAsync(string cmd, object payload, GlobalBucket bucket = GlobalBucket.GeneralRpc, - Optional evt = default(Optional), bool ignoreState = false, RequestOptions options = null) + public async Task SendRpcAsync(string cmd, object payload, Optional evt = default(Optional), RequestOptions options = null) where TResponse : class - => SendRpcAsyncInternal(cmd, payload, BucketGroup.Global, (int)bucket, 0, evt, ignoreState, options); - public Task SendRpcAsync(string cmd, object payload, GuildBucket bucket, ulong guildId, - Optional evt = default(Optional), bool ignoreState = false, RequestOptions options = null) - where TResponse : class - => SendRpcAsyncInternal(cmd, payload, BucketGroup.Guild, (int)bucket, guildId, evt, ignoreState, options); - private async Task SendRpcAsyncInternal(string cmd, object payload, BucketGroup group, int bucketId, ulong guildId, - Optional evt, bool ignoreState, RequestOptions options) + { + options.IgnoreState = false; + return await SendRpcAsyncInternal(cmd, payload, evt, options).ConfigureAwait(false); + } + private async Task SendRpcAsyncInternal(string cmd, object payload, Optional evt, RequestOptions options) where TResponse : class { - if (!ignoreState) + if (!options.IgnoreState) CheckState(); byte[] bytes = null; @@ -233,7 +230,7 @@ namespace Discord.API var requestTracker = new RpcRequest(options); _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); return await requestTracker.Promise.Task.ConfigureAwait(false); } @@ -241,33 +238,37 @@ namespace Discord.API //Rpc public async Task SendAuthenticateAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new AuthenticateParams { AccessToken = _authToken }; - return await SendRpcAsync("AUTHENTICATE", msg, ignoreState: true, options: options).ConfigureAwait(false); + options.IgnoreState = true; + return await SendRpcAsync("AUTHENTICATE", msg, options: options).ConfigureAwait(false); } public async Task SendAuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new AuthorizeParams { ClientId = _clientId, Scopes = scopes, RpcToken = rpcToken != null ? rpcToken : Optional.Create() }; - if (options == null) - options = new RequestOptions(); if (options.Timeout == null) options.Timeout = 60000; //This requires manual input on the user's end, lets give them more time - return await SendRpcAsync("AUTHORIZE", msg, ignoreState: true, options: options).ConfigureAwait(false); + options.IgnoreState = true; + return await SendRpcAsync("AUTHORIZE", msg, options: options).ConfigureAwait(false); } public async Task SendGetGuildsAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendRpcAsync("GET_GUILDS", null, options: options).ConfigureAwait(false); } public async Task SendGetGuildAsync(ulong guildId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new GetGuildParams { GuildId = guildId @@ -276,6 +277,7 @@ namespace Discord.API } public async Task SendGetChannelsAsync(ulong guildId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new GetChannelsParams { GuildId = guildId @@ -284,6 +286,7 @@ namespace Discord.API } public async Task SendGetChannelAsync(ulong channelId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new GetChannelParams { ChannelId = channelId @@ -293,6 +296,7 @@ namespace Discord.API public async Task SendSetLocalVolumeAsync(int volume, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new SetLocalVolumeParams { Volume = volume @@ -301,6 +305,7 @@ namespace Discord.API } public async Task SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new SelectVoiceChannelParams { ChannelId = channelId @@ -310,6 +315,7 @@ namespace Discord.API public async Task SendChannelSubscribeAsync(string evt, ulong channelId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new ChannelSubscriptionParams { ChannelId = channelId @@ -318,6 +324,7 @@ namespace Discord.API } public async Task SendChannelUnsubscribeAsync(string evt, ulong channelId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new ChannelSubscriptionParams { ChannelId = channelId @@ -327,6 +334,7 @@ namespace Discord.API public async Task SendGuildSubscribeAsync(string evt, ulong guildId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new GuildSubscriptionParams { GuildId = guildId @@ -335,6 +343,7 @@ namespace Discord.API } public async Task SendGuildUnsubscribeAsync(string evt, ulong guildId, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new GuildSubscriptionParams { GuildId = guildId diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 8cbda4155..263d20b44 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace Discord.Rpc { - public partial class DiscordRpcClient : DiscordClient + public partial class DiscordRpcClient : BaseDiscordClient { private readonly Logger _rpcLogger; private readonly JsonSerializer _serializer; diff --git a/src/Discord.Net.Rpc/Entities/RpcGuild.cs b/src/Discord.Net.Rpc/Entities/RpcGuild.cs index c42afb975..4fbde60a9 100644 --- a/src/Discord.Net.Rpc/Entities/RpcGuild.cs +++ b/src/Discord.Net.Rpc/Entities/RpcGuild.cs @@ -22,7 +22,5 @@ namespace Discord.Rpc { Name = model.Name; } - - bool IEntity.IsAttached => false; }*/ } diff --git a/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs index 39c929ab6..cccda4290 100644 --- a/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs @@ -156,8 +156,9 @@ namespace Discord.API } //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(); @@ -166,25 +167,19 @@ namespace Discord.API payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload }; if (payload != null) 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); } //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 GetGatewayAsync(RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); return await SendAsync("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) { + options = RequestOptions.CreateOrClone(options); var props = new Dictionary { ["$device"] = "Discord.Net" @@ -203,6 +198,7 @@ namespace Discord.API } public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var msg = new ResumeParams() { Token = _authToken, @@ -213,10 +209,12 @@ namespace Discord.API } public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); } public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); var args = new StatusUpdateParams { IdleSince = idleSince, @@ -226,10 +224,12 @@ namespace Discord.API } public async Task SendRequestMembersAsync(IEnumerable guildIds, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); 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) { + options = RequestOptions.CreateOrClone(options); var payload = new VoiceStateUpdateParams { GuildId = guildId, @@ -241,6 +241,7 @@ namespace Discord.API } public async Task SendGuildSyncAsync(IEnumerable guildIds, RequestOptions options = null) { + options = RequestOptions.CreateOrClone(options); await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false); } } diff --git a/src/Discord.Net.WebSocket/DataStore.cs b/src/Discord.Net.WebSocket/ClientState.cs similarity index 98% rename from src/Discord.Net.WebSocket/DataStore.cs rename to src/Discord.Net.WebSocket/ClientState.cs index d2b93c671..11d952842 100644 --- a/src/Discord.Net.WebSocket/DataStore.cs +++ b/src/Discord.Net.WebSocket/ClientState.cs @@ -5,7 +5,7 @@ using System.Linq; 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 double AverageChannelsPerGuild = 10.22; //Source: Googie2149 @@ -29,7 +29,7 @@ namespace Discord.WebSocket _groupChannels.Select(x => GetChannel(x) as IPrivateChannel)) .ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count); - public DataStore(int guildCount, int dmChannelCount) + public ClientState(int guildCount, int dmChannelCount) { double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; double estimatedUsersCount = guildCount * AverageUsersPerGuild; diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 6f93f7fa6..c8f1a4535 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -19,7 +19,7 @@ using System.Threading.Tasks; namespace Discord.WebSocket { - public partial class DiscordSocketClient : DiscordClient, IDiscordClient + public partial class DiscordSocketClient : BaseDiscordClient, IDiscordClient { private readonly ConcurrentQueue _largeGuilds; private readonly Logger _gatewayLogger; @@ -49,14 +49,14 @@ namespace Discord.WebSocket internal int MessageCacheSize { get; private set; } internal int LargeThreshold { 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 WebSocketProvider WebSocketProvider { get; private set; } public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new SocketSelfUser CurrentUser => base.CurrentUser as SocketSelfUser; - public IReadOnlyCollection PrivateChannels => DataStore.PrivateChannels; - internal IReadOnlyCollection Guilds => DataStore.Guilds; + public IReadOnlyCollection PrivateChannels => State.PrivateChannels; + internal IReadOnlyCollection Guilds => State.Guilds; /// Creates a new REST/WebSocket discord client. public DiscordSocketClient() : this(new DiscordSocketConfig()) { } @@ -72,7 +72,7 @@ namespace Discord.WebSocket AudioMode = config.AudioMode; WebSocketProvider = config.WebSocketProvider; ConnectionTimeout = config.ConnectionTimeout; - DataStore = new DataStore(0, 0); + State = new ClientState(0, 0); _nextAudioId = 1; _gatewayLogger = LogManager.CreateLogger("Gateway"); @@ -111,7 +111,7 @@ namespace Discord.WebSocket 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); } protected override async Task OnLogoutAsync() @@ -242,7 +242,7 @@ namespace Discord.WebSocket while (_largeGuilds.TryDequeue(out guildId)) { } //Raise virtual GUILD_UNAVAILABLEs - foreach (var guild in DataStore.Guilds) + foreach (var guild in State.Guilds) { if (guild._available) await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false); @@ -322,7 +322,7 @@ namespace Discord.WebSocket /// public SocketGuild GetGuild(ulong id) { - return DataStore.GetGuild(id); + return State.GetGuild(id); } /// public Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) @@ -331,7 +331,7 @@ namespace Discord.WebSocket /// public IChannel GetChannel(ulong id) { - return DataStore.GetChannel(id); + return State.GetChannel(id); } /// @@ -345,12 +345,12 @@ namespace Discord.WebSocket /// public IUser GetUser(ulong id) { - return DataStore.GetUser(id); + return State.GetUser(id); } /// 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(); } /// @@ -486,7 +486,7 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_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); int unavailableGuilds = 0; @@ -505,7 +505,7 @@ namespace Discord.WebSocket _sessionId = data.SessionId; base.CurrentUser = currentUser; _unavailableGuilds = unavailableGuilds; - DataStore = dataStore; + State = dataStore; } catch (Exception ex) { diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs index e67899de9..ccbbd4850 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs @@ -15,6 +15,7 @@ namespace Discord.WebSocket internal SocketSelfUser(DiscordSocketClient discord, ulong id) : base(discord, id) { + Status = UserStatus.Online; } internal new static SocketSelfUser Create(DiscordSocketClient discord, Model model) { diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 87691f427..5ab68d556 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -15,7 +15,7 @@ namespace Discord.WebSocket public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); public virtual Game? Game => null; - public virtual UserStatus Status => UserStatus.Unknown; + public virtual UserStatus Status { get; internal set; } internal SocketUser(DiscordSocketClient discord, ulong id) : base(discord, id)