| @@ -113,7 +113,7 @@ namespace Discord.API | |||||
| _authToken = token; | _authToken = token; | ||||
| _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); | _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); | ||||
| CurrentUser = await GetMyUserAsync(); | |||||
| CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true }); | |||||
| LoginState = LoginState.LoggedIn; | LoginState = LoginState.LoggedIn; | ||||
| } | } | ||||
| @@ -155,75 +155,50 @@ namespace Discord.API | |||||
| internal virtual Task ConnectInternalAsync() => Task.CompletedTask; | internal virtual Task ConnectInternalAsync() => Task.CompletedTask; | ||||
| internal virtual Task DisconnectInternalAsync() => Task.CompletedTask; | internal virtual Task DisconnectInternalAsync() => Task.CompletedTask; | ||||
| //REST | |||||
| public Task SendAsync(string method, string endpoint, | |||||
| GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) | |||||
| => SendInternalAsync(method, endpoint, null, true, BucketGroup.Global, (int)bucket, 0, ignoreState, options); | |||||
| public Task SendAsync(string method, string endpoint, object payload, | |||||
| GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) | |||||
| => SendInternalAsync(method, endpoint, payload, true, BucketGroup.Global, (int)bucket, 0, ignoreState, options); | |||||
| public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, | |||||
| GlobalBucket bucket = GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) where TResponse : class | |||||
| => DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Global, (int)bucket, 0, ignoreState, options).ConfigureAwait(false)); | |||||
| public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket = | |||||
| GlobalBucket.GeneralRest, bool ignoreState = false, RequestOptions options = null) where TResponse : class | |||||
| => DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, payload, false, BucketGroup.Global, (int)bucket, 0, ignoreState, options).ConfigureAwait(false)); | |||||
| public Task SendAsync(string method, string endpoint, | |||||
| GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) | |||||
| => SendInternalAsync(method, endpoint, null, true, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options); | |||||
| public Task SendAsync(string method, string endpoint, object payload, | |||||
| GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) | |||||
| => SendInternalAsync(method, endpoint, payload, true, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options); | |||||
| public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, | |||||
| GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) where TResponse : class | |||||
| => DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options).ConfigureAwait(false)); | |||||
| public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload, | |||||
| GuildBucket bucket, ulong guildId, bool ignoreState = false, RequestOptions options = null) where TResponse : class | |||||
| => DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, payload, false, BucketGroup.Guild, (int)bucket, guildId, ignoreState, options).ConfigureAwait(false)); | |||||
| //REST - Multipart | |||||
| public Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||||
| GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null) | |||||
| => SendMultipartInternalAsync(method, endpoint, multipartArgs, true, BucketGroup.Global, (int)bucket, 0, options); | |||||
| public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||||
| GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null) where TResponse : class | |||||
| => DeserializeJson<TResponse>(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Global, (int)bucket, 0, options).ConfigureAwait(false)); | |||||
| public Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||||
| GuildBucket bucket, ulong guildId, RequestOptions options = null) | |||||
| => SendMultipartInternalAsync(method, endpoint, multipartArgs, true, BucketGroup.Guild, (int)bucket, guildId, options); | |||||
| public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||||
| GuildBucket bucket, ulong guildId, RequestOptions options = null) where TResponse : class | |||||
| => DeserializeJson<TResponse>(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Guild, (int)bucket, guildId, options).ConfigureAwait(false)); | |||||
| //Core | //Core | ||||
| private async Task<Stream> SendInternalAsync(string method, string endpoint, object payload, bool headerOnly, | |||||
| BucketGroup group, int bucketId, ulong guildId, bool ignoreState, RequestOptions options = null) | |||||
| public async Task SendAsync(string method, string endpoint, RequestOptions options = null) | |||||
| { | { | ||||
| if (!ignoreState) | |||||
| CheckState(); | |||||
| var stopwatch = Stopwatch.StartNew(); | |||||
| string json = null; | |||||
| if (payload != null) | |||||
| json = SerializeJson(payload); | |||||
| var responseStream = await RequestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, json, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false); | |||||
| stopwatch.Stop(); | |||||
| double milliseconds = ToMilliseconds(stopwatch); | |||||
| await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false); | |||||
| return responseStream; | |||||
| options.HeaderOnly = true; | |||||
| var request = new RestRequest(_restClient, method, endpoint, options); | |||||
| await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | |||||
| } | |||||
| public async Task SendJsonAsync(string method, string endpoint, object payload, RequestOptions options = null) | |||||
| { | |||||
| options.HeaderOnly = true; | |||||
| var json = payload != null ? SerializeJson(payload) : null; | |||||
| var request = new JsonRestRequest(_restClient, method, endpoint, json, options); | |||||
| await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | |||||
| } | } | ||||
| private async Task<Stream> SendMultipartInternalAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly, | |||||
| BucketGroup group, int bucketId, ulong guildId, RequestOptions options = null) | |||||
| public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, RequestOptions options = null) | |||||
| { | { | ||||
| CheckState(); | |||||
| options.HeaderOnly = true; | |||||
| var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options); | |||||
| await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | |||||
| } | |||||
| public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, RequestOptions options = null) where TResponse : class | |||||
| { | |||||
| var request = new RestRequest(_restClient, method, endpoint, options); | |||||
| return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | |||||
| } | |||||
| public async Task<TResponse> SendJsonAsync<TResponse>(string method, string endpoint, object payload, RequestOptions options = null) where TResponse : class | |||||
| { | |||||
| var json = payload != null ? SerializeJson(payload) : null; | |||||
| var request = new JsonRestRequest(_restClient, method, endpoint, json, options); | |||||
| return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | |||||
| } | |||||
| public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, RequestOptions options = null) | |||||
| { | |||||
| var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options); | |||||
| return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | |||||
| } | |||||
| private async Task<Stream> SendInternalAsync(string method, string endpoint, RestRequest request) | |||||
| { | |||||
| if (!request.Options.IgnoreState) | |||||
| CheckState(); | |||||
| var stopwatch = Stopwatch.StartNew(); | var stopwatch = Stopwatch.StartNew(); | ||||
| var responseStream = await RequestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, multipartArgs, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false); | |||||
| int bytes = headerOnly ? 0 : (int)responseStream.Length; | |||||
| var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false); | |||||
| stopwatch.Stop(); | stopwatch.Stop(); | ||||
| double milliseconds = ToMilliseconds(stopwatch); | double milliseconds = ToMilliseconds(stopwatch); | ||||
| @@ -235,6 +210,7 @@ namespace Discord.API | |||||
| //Auth | //Auth | ||||
| public async Task ValidateTokenAsync(RequestOptions options = null) | public async Task ValidateTokenAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("GET", "auth/login", options: options).ConfigureAwait(false); | await SendAsync("GET", "auth/login", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -242,6 +218,7 @@ namespace Discord.API | |||||
| public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null) | public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -253,6 +230,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -266,6 +244,7 @@ namespace Discord.API | |||||
| public async Task<IReadOnlyCollection<Channel>> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null) | public async Task<IReadOnlyCollection<Channel>> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Channel>>("GET", $"guilds/{guildId}/channels", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Channel>>("GET", $"guilds/{guildId}/channels", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -275,12 +254,14 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); | Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); | ||||
| Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Channel> DeleteChannelAsync(ulong channelId, RequestOptions options = null) | public async Task<Channel> DeleteChannelAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Channel>("DELETE", $"channels/{channelId}", options: options).ConfigureAwait(false); | return await SendAsync<Channel>("DELETE", $"channels/{channelId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -290,8 +271,9 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | ||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, ModifyTextChannelParams args, RequestOptions options = null) | public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, ModifyTextChannelParams args, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -299,8 +281,9 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | ||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, ModifyVoiceChannelParams args, RequestOptions options = null) | public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, ModifyVoiceChannelParams args, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -310,13 +293,15 @@ namespace Discord.API | |||||
| Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); | Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); | ||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | ||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable<ModifyGuildChannelsParams> args, RequestOptions options = null) | public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable<ModifyGuildChannelsParams> args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var channels = args.ToArray(); | var channels = args.ToArray(); | ||||
| switch (channels.Length) | switch (channels.Length) | ||||
| @@ -327,7 +312,7 @@ namespace Discord.API | |||||
| await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false); | await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false); | ||||
| break; | break; | ||||
| default: | default: | ||||
| await SendAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false); | |||||
| await SendJsonAsync("PATCH", $"guilds/{guildId}/channels", channels, options: options).ConfigureAwait(false); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -337,6 +322,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | Preconditions.NotEqual(messageId, 0, nameof(messageId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -350,6 +336,7 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); | Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); | ||||
| Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); | Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch); | int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch); | ||||
| ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; | ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; | ||||
| @@ -383,13 +370,15 @@ namespace Discord.API | |||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | ||||
| if (args.Content.Length > DiscordConfig.MaxMessageSize) | if (args.Content.Length > DiscordConfig.MaxMessageSize) | ||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Message>("POST", $"channels/{channelId}/messages", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| if (args.Content.GetValueOrDefault(null) == null) | if (args.Content.GetValueOrDefault(null) == null) | ||||
| args.Content = ""; | args.Content = ""; | ||||
| @@ -401,12 +390,13 @@ namespace Discord.API | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
| } | } | ||||
| return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); | |||||
| return await SendMultipartAsync<Message>("POST", $"channels/{channelId}/messages", args.ToDictionary(), options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | Preconditions.NotEqual(messageId, 0, nameof(messageId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -414,9 +404,9 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); | Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); | ||||
| Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); | Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| switch (args.MessageIds.Length) | switch (args.MessageIds.Length) | ||||
| { | { | ||||
| @@ -426,7 +416,7 @@ namespace Discord.API | |||||
| await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false); | await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false); | ||||
| break; | break; | ||||
| default: | default: | ||||
| await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); | |||||
| await SendJsonAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -441,19 +431,22 @@ namespace Discord.API | |||||
| if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | ||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
| } | } | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Message>("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | Preconditions.NotEqual(messageId, 0, nameof(messageId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); | await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) | public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); | await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -464,13 +457,15 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(targetId, 0, nameof(targetId)); | Preconditions.NotEqual(targetId, 0, nameof(targetId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("PUT", $"channels/{channelId}/permissions/{targetId}", args, options: options).ConfigureAwait(false); | |||||
| await SendJsonAsync("PUT", $"channels/{channelId}/permissions/{targetId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task DeleteChannelPermissionAsync(ulong channelId, ulong targetId, RequestOptions options = null) | public async Task DeleteChannelPermissionAsync(ulong channelId, ulong targetId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(targetId, 0, nameof(targetId)); | Preconditions.NotEqual(targetId, 0, nameof(targetId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"channels/{channelId}/permissions/{targetId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"channels/{channelId}/permissions/{targetId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -480,6 +475,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.GreaterThan(channelId, 0, nameof(channelId)); | Preconditions.GreaterThan(channelId, 0, nameof(channelId)); | ||||
| Preconditions.GreaterThan(messageId, 0, nameof(messageId)); | Preconditions.GreaterThan(messageId, 0, nameof(messageId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("PUT", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); | await SendAsync("PUT", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); | ||||
| @@ -488,12 +484,14 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | Preconditions.NotEqual(messageId, 0, nameof(messageId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<Message>> GetPinsAsync(ulong channelId, RequestOptions options = null) | public async Task<IReadOnlyCollection<Message>> GetPinsAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Message>>("GET", $"channels/{channelId}/pins", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Message>>("GET", $"channels/{channelId}/pins", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -503,6 +501,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.GreaterThan(channelId, 0, nameof(channelId)); | Preconditions.GreaterThan(channelId, 0, nameof(channelId)); | ||||
| Preconditions.GreaterThan(userId, 0, nameof(userId)); | Preconditions.GreaterThan(userId, 0, nameof(userId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("PUT", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); | await SendAsync("PUT", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); | ||||
| @@ -511,6 +510,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -519,6 +519,7 @@ namespace Discord.API | |||||
| public async Task<Guild> GetGuildAsync(ulong guildId, RequestOptions options = null) | public async Task<Guild> GetGuildAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -531,18 +532,21 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | ||||
| Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); | Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Guild>("POST", "guilds", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Guild>("POST", "guilds", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null) | public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Guild>("DELETE", $"guilds/{guildId}", options: options).ConfigureAwait(false); | return await SendAsync<Guild>("DELETE", $"guilds/{guildId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<Guild> LeaveGuildAsync(ulong guildId, RequestOptions options = null) | public async Task<Guild> LeaveGuildAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Guild>("DELETE", $"users/@me/guilds/{guildId}", options: options).ConfigureAwait(false); | return await SendAsync<Guild>("DELETE", $"users/@me/guilds/{guildId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -555,30 +559,34 @@ namespace Discord.API | |||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | ||||
| Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); | Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); | ||||
| Preconditions.NotNull(args.RegionId, nameof(args.RegionId)); | Preconditions.NotNull(args.RegionId, nameof(args.RegionId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<GetGuildPruneCountResponse> BeginGuildPruneAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null) | public async Task<GetGuildPruneCountResponse> BeginGuildPruneAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); | Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetGuildPruneCountResponse>("POST", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<GetGuildPruneCountResponse>("POST", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<GetGuildPruneCountResponse> GetGuildPruneCountAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null) | public async Task<GetGuildPruneCountResponse> GetGuildPruneCountAsync(ulong guildId, GuildPruneParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); | Preconditions.AtLeast(args.Days, 0, nameof(args.Days)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetGuildPruneCountResponse>("GET", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<GetGuildPruneCountResponse>("GET", $"guilds/{guildId}/prune", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| //Guild Bans | //Guild Bans | ||||
| public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null) | public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Ban>>("GET", $"guilds/{guildId}/bans", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Ban>>("GET", $"guilds/{guildId}/bans", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -588,13 +596,15 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays)); | Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); | |||||
| await SendJsonAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null) | public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"guilds/{guildId}/bans/{userId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"guilds/{guildId}/bans/{userId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -603,6 +613,7 @@ namespace Discord.API | |||||
| public async Task<GuildEmbed> GetGuildEmbedAsync(ulong guildId, RequestOptions options = null) | public async Task<GuildEmbed> GetGuildEmbedAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -614,14 +625,16 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GuildEmbed>("PATCH", $"guilds/{guildId}/embed", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<GuildEmbed>("PATCH", $"guilds/{guildId}/embed", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| //Guild Integrations | //Guild Integrations | ||||
| public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null) | public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Integration>>("GET", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Integration>>("GET", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -630,6 +643,7 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotEqual(args.Id, 0, nameof(args.Id)); | Preconditions.NotEqual(args.Id, 0, nameof(args.Id)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); | return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -637,6 +651,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Integration>("DELETE", $"guilds/{guildId}/integrations/{integrationId}", options: options).ConfigureAwait(false); | return await SendAsync<Integration>("DELETE", $"guilds/{guildId}/integrations/{integrationId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -647,13 +662,15 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); | Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); | ||||
| Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); | Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Integration> SyncGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null) | public async Task<Integration> SyncGuildIntegrationAsync(ulong guildId, ulong integrationId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations/{integrationId}/sync", options: options).ConfigureAwait(false); | return await SendAsync<Integration>("POST", $"guilds/{guildId}/integrations/{integrationId}/sync", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -662,6 +679,7 @@ namespace Discord.API | |||||
| public async Task<Invite> GetInviteAsync(string inviteId, RequestOptions options = null) | public async Task<Invite> GetInviteAsync(string inviteId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); | Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| //Remove trailing slash | //Remove trailing slash | ||||
| if (inviteId[inviteId.Length - 1] == '/') | if (inviteId[inviteId.Length - 1] == '/') | ||||
| @@ -680,12 +698,14 @@ namespace Discord.API | |||||
| public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) | public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) | public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -695,18 +715,21 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); | Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); | ||||
| Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); | Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Invite> DeleteInviteAsync(string inviteCode, RequestOptions options = null) | public async Task<Invite> DeleteInviteAsync(string inviteCode, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); | Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Invite>("DELETE", $"invites/{inviteCode}", options: options).ConfigureAwait(false); | return await SendAsync<Invite>("DELETE", $"invites/{inviteCode}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task AcceptInviteAsync(string inviteCode, RequestOptions options = null) | public async Task AcceptInviteAsync(string inviteCode, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); | Preconditions.NotNullOrEmpty(inviteCode, nameof(inviteCode)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("POST", $"invites/{inviteCode}", options: options).ConfigureAwait(false); | await SendAsync("POST", $"invites/{inviteCode}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -716,6 +739,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -730,6 +754,7 @@ namespace Discord.API | |||||
| Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); | Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); | ||||
| Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); | Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); | ||||
| Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); | Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| int limit = args.Limit.GetValueOrDefault(int.MaxValue); | int limit = args.Limit.GetValueOrDefault(int.MaxValue); | ||||
| ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); | ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); | ||||
| @@ -741,6 +766,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"guilds/{guildId}/members/{userId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"guilds/{guildId}/members/{userId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -749,6 +775,7 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| bool isCurrentUser = userId == CurrentUser.Id; | bool isCurrentUser = userId == CurrentUser.Id; | ||||
| @@ -760,7 +787,7 @@ namespace Discord.API | |||||
| } | } | ||||
| if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) | if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) | ||||
| { | { | ||||
| await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false); | |||||
| await SendJsonAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -768,12 +795,14 @@ namespace Discord.API | |||||
| public async Task<IReadOnlyCollection<Role>> GetGuildRolesAsync(ulong guildId, RequestOptions options = null) | public async Task<IReadOnlyCollection<Role>> GetGuildRolesAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Role>>("GET", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Role>>("GET", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<Role> CreateGuildRoleAsync(ulong guildId, RequestOptions options = null) | public async Task<Role> CreateGuildRoleAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Role>("POST", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); | return await SendAsync<Role>("POST", $"guilds/{guildId}/roles", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -781,6 +810,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(roleId, 0, nameof(roleId)); | Preconditions.NotEqual(roleId, 0, nameof(roleId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", $"guilds/{guildId}/roles/{roleId}", options: options).ConfigureAwait(false); | await SendAsync("DELETE", $"guilds/{guildId}/roles/{roleId}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -792,13 +822,15 @@ namespace Discord.API | |||||
| Preconditions.AtLeast(args.Color, 0, nameof(args.Color)); | Preconditions.AtLeast(args.Color, 0, nameof(args.Color)); | ||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | ||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<IReadOnlyCollection<Role>> ModifyGuildRolesAsync(ulong guildId, IEnumerable<ModifyGuildRolesParams> args, RequestOptions options = null) | public async Task<IReadOnlyCollection<Role>> ModifyGuildRolesAsync(ulong guildId, IEnumerable<ModifyGuildRolesParams> args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var roles = args.ToArray(); | var roles = args.ToArray(); | ||||
| switch (roles.Length) | switch (roles.Length) | ||||
| @@ -808,7 +840,7 @@ namespace Discord.API | |||||
| case 1: | case 1: | ||||
| return ImmutableArray.Create(await ModifyGuildRoleAsync(guildId, roles[0].Id, roles[0]).ConfigureAwait(false)); | return ImmutableArray.Create(await ModifyGuildRoleAsync(guildId, roles[0].Id, roles[0]).ConfigureAwait(false)); | ||||
| default: | default: | ||||
| return await SendAsync<IReadOnlyCollection<Role>>("PATCH", $"guilds/{guildId}/roles", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<IReadOnlyCollection<Role>>("PATCH", $"guilds/{guildId}/roles", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -816,6 +848,7 @@ namespace Discord.API | |||||
| public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -827,6 +860,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotNullOrEmpty(username, nameof(username)); | Preconditions.NotNullOrEmpty(username, nameof(username)); | ||||
| Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator)); | Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | try | ||||
| { | { | ||||
| @@ -839,6 +873,7 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotNullOrEmpty(query, nameof(query)); | Preconditions.NotNullOrEmpty(query, nameof(query)); | ||||
| Preconditions.AtLeast(limit, 0, nameof(limit)); | Preconditions.AtLeast(limit, 0, nameof(limit)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<User>>("GET", $"users?q={Uri.EscapeDataString(query)}&limit={limit}", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<User>>("GET", $"users?q={Uri.EscapeDataString(query)}&limit={limit}", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -846,54 +881,64 @@ namespace Discord.API | |||||
| //Current User/DMs | //Current User/DMs | ||||
| public async Task<User> GetMyUserAsync(RequestOptions options = null) | public async Task<User> GetMyUserAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<User>("GET", "users/@me", options: options).ConfigureAwait(false); | return await SendAsync<User>("GET", "users/@me", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<Connection>> GetMyConnectionsAsync(RequestOptions options = null) | public async Task<IReadOnlyCollection<Connection>> GetMyConnectionsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Connection>>("GET", "users/@me/connections", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Connection>>("GET", "users/@me/connections", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<Channel>> GetMyPrivateChannelsAsync(RequestOptions options = null) | public async Task<IReadOnlyCollection<Channel>> GetMyPrivateChannelsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<Channel>>("GET", "users/@me/channels", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Channel>>("GET", "users/@me/channels", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<UserGuild>> GetMyGuildsAsync(RequestOptions options = null) | public async Task<IReadOnlyCollection<UserGuild>> GetMyGuildsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", "users/@me/guilds", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", "users/@me/guilds", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<Application> GetMyApplicationAsync(RequestOptions options = null) | public async Task<Application> GetMyApplicationAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false); | return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) | public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); | Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task ModifyMyNickAsync(ulong guildId, ModifyCurrentUserNickParams args, RequestOptions options = null) | public async Task ModifyMyNickAsync(ulong guildId, ModifyCurrentUserNickParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotNull(args.Nickname, nameof(args.Nickname)); | Preconditions.NotNull(args.Nickname, nameof(args.Nickname)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("PATCH", $"guilds/{guildId}/members/@me/nick", args, options: options).ConfigureAwait(false); | |||||
| await SendJsonAsync("PATCH", $"guilds/{guildId}/members/@me/nick", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) | public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId)); | Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); | |||||
| return await SendJsonAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| //Voice Regions | //Voice Regions | ||||
| public async Task<IReadOnlyCollection<VoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | public async Task<IReadOnlyCollection<VoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", "voice/regions", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", "voice/regions", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<VoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null) | public async Task<IReadOnlyCollection<VoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", $"guilds/{guildId}/regions", options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", $"guilds/{guildId}/regions", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -1,30 +0,0 @@ | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public sealed class Bucket | |||||
| { | |||||
| /// <summary> Gets the unique identifier for this bucket. </summary> | |||||
| public string Id { get; } | |||||
| /// <summary> Gets the name of this bucket. </summary> | |||||
| public string Name { get; } | |||||
| /// <summary> Gets the amount of requests that may be sent per window. </summary> | |||||
| public int WindowCount { get; } | |||||
| /// <summary> Gets the length of this bucket's window, in seconds. </summary> | |||||
| public int WindowSeconds { get; } | |||||
| /// <summary> Gets the type of account this bucket affects. </summary> | |||||
| public BucketTarget Target { get; } | |||||
| /// <summary> Gets this bucket's parent. </summary> | |||||
| public GlobalBucket? Parent { get; } | |||||
| internal Bucket(string id, int windowCount, int windowSeconds, BucketTarget target, GlobalBucket? parent = null) | |||||
| : this(id, id, windowCount, windowSeconds, target, parent) { } | |||||
| internal Bucket(string id, string name, int windowCount, int windowSeconds, BucketTarget target, GlobalBucket? parent = null) | |||||
| { | |||||
| Id = id; | |||||
| Name = name; | |||||
| WindowCount = windowCount; | |||||
| WindowSeconds = windowSeconds; | |||||
| Target = target; | |||||
| Parent = parent; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,9 +0,0 @@ | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public enum BucketGroup | |||||
| { | |||||
| Global, | |||||
| Guild, | |||||
| Channel | |||||
| } | |||||
| } | |||||
| @@ -1,9 +0,0 @@ | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public enum BucketTarget | |||||
| { | |||||
| Client, | |||||
| Bot, | |||||
| Both | |||||
| } | |||||
| } | |||||
| @@ -1,7 +0,0 @@ | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public enum ChannelBucket | |||||
| { | |||||
| SendEditMessage, | |||||
| } | |||||
| } | |||||
| @@ -1,14 +0,0 @@ | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public enum GlobalBucket | |||||
| { | |||||
| GeneralRest, | |||||
| DirectMessage, | |||||
| SendEditMessage, | |||||
| GeneralGateway, | |||||
| UpdateStatus, | |||||
| GeneralRpc | |||||
| } | |||||
| } | |||||
| @@ -1,11 +0,0 @@ | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public enum GuildBucket | |||||
| { | |||||
| SendEditMessage, | |||||
| DeleteMessage, | |||||
| DeleteMessages, | |||||
| ModifyMember, | |||||
| Nickname | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,5 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.IO; | using System.IO; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -10,91 +8,23 @@ namespace Discord.Net.Queue | |||||
| { | { | ||||
| public class RequestQueue | public class RequestQueue | ||||
| { | { | ||||
| public event Func<string, Bucket, int, Task> RateLimitTriggered; | |||||
| private readonly static ImmutableDictionary<GlobalBucket, Bucket> _globalLimits; | |||||
| private readonly static ImmutableDictionary<GuildBucket, Bucket> _guildLimits; | |||||
| private readonly static ImmutableDictionary<ChannelBucket, Bucket> _channelLimits; | |||||
| public event Func<string, RequestQueueBucket, int?, Task> RateLimitTriggered; | |||||
| private readonly SemaphoreSlim _lock; | private readonly SemaphoreSlim _lock; | ||||
| private readonly RequestQueueBucket[] _globalBuckets; | |||||
| private readonly ConcurrentDictionary<ulong, RequestQueueBucket>[] _guildBuckets; | |||||
| private readonly ConcurrentDictionary<ulong, RequestQueueBucket>[] _channelBuckets; | |||||
| private readonly ConcurrentDictionary<string, RequestQueueBucket> _buckets; | |||||
| private CancellationTokenSource _clearToken; | private CancellationTokenSource _clearToken; | ||||
| private CancellationToken _parentToken; | private CancellationToken _parentToken; | ||||
| private CancellationToken _cancelToken; | private CancellationToken _cancelToken; | ||||
| static RequestQueue() | |||||
| { | |||||
| _globalLimits = new Dictionary<GlobalBucket, Bucket> | |||||
| { | |||||
| //REST | |||||
| [GlobalBucket.GeneralRest] = new Bucket(null, "rest", 0, 0, BucketTarget.Both), //No Limit | |||||
| //[GlobalBucket.Login] = new BucketDefinition(1, 1), | |||||
| [GlobalBucket.DirectMessage] = new Bucket("bot:msg:dm", 5, 5, BucketTarget.Bot), | |||||
| [GlobalBucket.SendEditMessage] = new Bucket("bot:msg:global", 50, 10, BucketTarget.Bot), | |||||
| //[GlobalBucket.Username] = new Bucket("bot:msg:global", 2, 3600, BucketTarget.Both), | |||||
| //Gateway | |||||
| [GlobalBucket.GeneralGateway] = new Bucket(null, "gateway", 120, 60, BucketTarget.Both), | |||||
| [GlobalBucket.UpdateStatus] = new Bucket(null, "status", 5, 1, BucketTarget.Both, GlobalBucket.GeneralGateway), | |||||
| //Rpc | |||||
| [GlobalBucket.GeneralRpc] = new Bucket(null, "rpc", 120, 60, BucketTarget.Both) | |||||
| }.ToImmutableDictionary(); | |||||
| _guildLimits = new Dictionary<GuildBucket, Bucket> | |||||
| { | |||||
| //REST | |||||
| [GuildBucket.SendEditMessage] = new Bucket("bot:msg:server", 5, 5, BucketTarget.Bot, GlobalBucket.SendEditMessage), | |||||
| [GuildBucket.DeleteMessage] = new Bucket("dmsg", 5, 1, BucketTarget.Bot), | |||||
| [GuildBucket.DeleteMessages] = new Bucket("bdmsg", 1, 1, BucketTarget.Bot), | |||||
| [GuildBucket.ModifyMember] = new Bucket("guild_member", 10, 10, BucketTarget.Bot), | |||||
| [GuildBucket.Nickname] = new Bucket("guild_member_nick", 1, 1, BucketTarget.Bot) | |||||
| }.ToImmutableDictionary(); | |||||
| //Client-Only | |||||
| _channelLimits = new Dictionary<ChannelBucket, Bucket> | |||||
| { | |||||
| //REST | |||||
| [ChannelBucket.SendEditMessage] = new Bucket("msg", 10, 10, BucketTarget.Client, GlobalBucket.SendEditMessage), | |||||
| }.ToImmutableDictionary(); | |||||
| } | |||||
| public static Bucket GetBucketInfo(GlobalBucket bucket) => _globalLimits[bucket]; | |||||
| public static Bucket GetBucketInfo(GuildBucket bucket) => _guildLimits[bucket]; | |||||
| public static Bucket GetBucketInfo(ChannelBucket bucket) => _channelLimits[bucket]; | |||||
| public RequestQueue() | public RequestQueue() | ||||
| { | { | ||||
| _lock = new SemaphoreSlim(1, 1); | _lock = new SemaphoreSlim(1, 1); | ||||
| _globalBuckets = new RequestQueueBucket[_globalLimits.Count]; | |||||
| foreach (var pair in _globalLimits) | |||||
| { | |||||
| //var target = _globalLimits[pair.Key].Target; | |||||
| //if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot)) | |||||
| _globalBuckets[(int)pair.Key] = CreateBucket(pair.Value); | |||||
| } | |||||
| _guildBuckets = new ConcurrentDictionary<ulong, RequestQueueBucket>[_guildLimits.Count]; | |||||
| for (int i = 0; i < _guildLimits.Count; i++) | |||||
| { | |||||
| //var target = _guildLimits[(GuildBucket)i].Target; | |||||
| //if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot)) | |||||
| _guildBuckets[i] = new ConcurrentDictionary<ulong, RequestQueueBucket>(); | |||||
| } | |||||
| _channelBuckets = new ConcurrentDictionary<ulong, RequestQueueBucket>[_channelLimits.Count]; | |||||
| for (int i = 0; i < _channelLimits.Count; i++) | |||||
| { | |||||
| //var target = _channelLimits[(GuildBucket)i].Target; | |||||
| //if (target == BucketTarget.Both || (target == BucketTarget.Bot && isBot) || (target == BucketTarget.Client && !isBot)) | |||||
| _channelBuckets[i] = new ConcurrentDictionary<ulong, RequestQueueBucket>(); | |||||
| } | |||||
| _clearToken = new CancellationTokenSource(); | _clearToken = new CancellationTokenSource(); | ||||
| _cancelToken = CancellationToken.None; | _cancelToken = CancellationToken.None; | ||||
| _parentToken = CancellationToken.None; | _parentToken = CancellationToken.None; | ||||
| _buckets = new ConcurrentDictionary<string, RequestQueueBucket>(); | |||||
| } | } | ||||
| public async Task SetCancelTokenAsync(CancellationToken cancelToken) | public async Task SetCancelTokenAsync(CancellationToken cancelToken) | ||||
| { | { | ||||
| @@ -107,63 +37,29 @@ namespace Discord.Net.Queue | |||||
| finally { _lock.Release(); } | finally { _lock.Release(); } | ||||
| } | } | ||||
| public async Task<Stream> SendAsync(RestRequest request, BucketGroup group, int bucketId, ulong objId) | |||||
| public async Task<Stream> SendAsync(RestRequest request) | |||||
| { | { | ||||
| request.CancelToken = _cancelToken; | request.CancelToken = _cancelToken; | ||||
| var bucket = GetBucket(group, bucketId, objId); | |||||
| var bucket = GetOrCreateBucket(request.Options.BucketId); | |||||
| return await bucket.SendAsync(request).ConfigureAwait(false); | return await bucket.SendAsync(request).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<Stream> SendAsync(WebSocketRequest request, BucketGroup group, int bucketId, ulong objId) | |||||
| public async Task<Stream> SendAsync(WebSocketRequest request) | |||||
| { | { | ||||
| request.CancelToken = _cancelToken; | request.CancelToken = _cancelToken; | ||||
| var bucket = GetBucket(group, bucketId, objId); | |||||
| var bucket = GetOrCreateBucket(request.Options.BucketId); | |||||
| return await bucket.SendAsync(request).ConfigureAwait(false); | return await bucket.SendAsync(request).ConfigureAwait(false); | ||||
| } | } | ||||
| private RequestQueueBucket CreateBucket(Bucket def) | |||||
| private RequestQueueBucket GetOrCreateBucket(string id) | |||||
| { | { | ||||
| var parent = def.Parent != null ? GetGlobalBucket(def.Parent.Value) : null; | |||||
| return new RequestQueueBucket(this, def, parent); | |||||
| return new RequestQueueBucket(this, id, null); | |||||
| } | } | ||||
| public void DestroyGuildBucket(GuildBucket type, ulong guildId) | |||||
| { | |||||
| //Assume this object is locked | |||||
| RequestQueueBucket bucket; | |||||
| _guildBuckets[(int)type].TryRemove(guildId, out bucket); | |||||
| } | |||||
| public void DestroyChannelBucket(ChannelBucket type, ulong channelId) | |||||
| public void DestroyBucket(string id) | |||||
| { | { | ||||
| //Assume this object is locked | //Assume this object is locked | ||||
| RequestQueueBucket bucket; | RequestQueueBucket bucket; | ||||
| _channelBuckets[(int)type].TryRemove(channelId, out bucket); | |||||
| } | |||||
| private RequestQueueBucket GetBucket(BucketGroup group, int bucketId, ulong objId) | |||||
| { | |||||
| switch (group) | |||||
| { | |||||
| case BucketGroup.Global: | |||||
| return GetGlobalBucket((GlobalBucket)bucketId); | |||||
| case BucketGroup.Guild: | |||||
| return GetGuildBucket((GuildBucket)bucketId, objId); | |||||
| case BucketGroup.Channel: | |||||
| return GetChannelBucket((ChannelBucket)bucketId, objId); | |||||
| default: | |||||
| throw new ArgumentException($"Unknown bucket group: {group}", nameof(group)); | |||||
| } | |||||
| } | |||||
| private RequestQueueBucket GetGlobalBucket(GlobalBucket type) | |||||
| { | |||||
| return _globalBuckets[(int)type]; | |||||
| } | |||||
| private RequestQueueBucket GetGuildBucket(GuildBucket type, ulong guildId) | |||||
| { | |||||
| return _guildBuckets[(int)type].GetOrAdd(guildId, _ => CreateBucket(_guildLimits[type])); | |||||
| } | |||||
| private RequestQueueBucket GetChannelBucket(ChannelBucket type, ulong channelId) | |||||
| { | |||||
| return _channelBuckets[(int)type].GetOrAdd(channelId, _ => CreateBucket(_channelLimits[type])); | |||||
| _buckets.TryRemove(id, out bucket); | |||||
| } | } | ||||
| public async Task ClearAsync() | public async Task ClearAsync() | ||||
| @@ -181,9 +77,9 @@ namespace Discord.Net.Queue | |||||
| finally { _lock.Release(); } | finally { _lock.Release(); } | ||||
| } | } | ||||
| internal async Task RaiseRateLimitTriggered(string id, Bucket bucket, int millis) | |||||
| internal async Task RaiseRateLimitTriggered(string id, RequestQueueBucket bucket, int? millis) | |||||
| { | { | ||||
| await RateLimitTriggered.Invoke(id, bucket, millis).ConfigureAwait(false); | |||||
| await RateLimitTriggered(id, bucket, millis).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -7,7 +7,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net.Queue | namespace Discord.Net.Queue | ||||
| { | { | ||||
| internal class RequestQueueBucket | |||||
| public class RequestQueueBucket | |||||
| { | { | ||||
| private readonly RequestQueue _queue; | private readonly RequestQueue _queue; | ||||
| private readonly SemaphoreSlim _semaphore; | private readonly SemaphoreSlim _semaphore; | ||||
| @@ -15,16 +15,15 @@ namespace Discord.Net.Queue | |||||
| private int _pauseEndTick; | private int _pauseEndTick; | ||||
| private TaskCompletionSource<byte> _resumeNotifier; | private TaskCompletionSource<byte> _resumeNotifier; | ||||
| public Bucket Definition { get; } | |||||
| public string Id { get; } | |||||
| public RequestQueueBucket Parent { get; } | public RequestQueueBucket Parent { get; } | ||||
| public Task _resetTask { get; } | |||||
| public int WindowSeconds { get; } | |||||
| public RequestQueueBucket(RequestQueue queue, Bucket definition, RequestQueueBucket parent = null) | |||||
| public RequestQueueBucket(RequestQueue queue, string id, RequestQueueBucket parent = null) | |||||
| { | { | ||||
| _queue = queue; | _queue = queue; | ||||
| Definition = definition; | |||||
| if (definition.WindowCount != 0) | |||||
| _semaphore = new SemaphoreSlim(definition.WindowCount, definition.WindowCount); | |||||
| Id = id; | |||||
| _semaphore = new SemaphoreSlim(5, 5); | |||||
| Parent = parent; | Parent = parent; | ||||
| _pauseLock = new object(); | _pauseLock = new object(); | ||||
| @@ -44,12 +43,8 @@ namespace Discord.Net.Queue | |||||
| { | { | ||||
| //When a 429 occurs, we drop all our locks. | //When a 429 occurs, we drop all our locks. | ||||
| //This is generally safe though since 429s actually occuring should be very rare. | //This is generally safe though since 429s actually occuring should be very rare. | ||||
| RequestQueueBucket bucket; | |||||
| bool success = FindBucket(ex.BucketId, out bucket); | |||||
| await _queue.RaiseRateLimitTriggered(ex.BucketId, success ? bucket.Definition : null, ex.RetryAfterMilliseconds).ConfigureAwait(false); | |||||
| bucket.Pause(ex.RetryAfterMilliseconds); | |||||
| await _queue.RaiseRateLimitTriggered(Id, this, ex.RetryAfterMilliseconds).ConfigureAwait(false); | |||||
| Pause(ex.RetryAfterMilliseconds); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -107,24 +102,7 @@ namespace Discord.Net.Queue | |||||
| QueueExitAsync(); | QueueExitAsync(); | ||||
| } | } | ||||
| } | } | ||||
| private bool FindBucket(string id, out RequestQueueBucket bucket) | |||||
| { | |||||
| //Keep going up until we find a bucket with matching id or we're at the topmost bucket | |||||
| if (Definition.Id == id) | |||||
| { | |||||
| bucket = this; | |||||
| return true; | |||||
| } | |||||
| else if (Parent == null) | |||||
| { | |||||
| bucket = this; | |||||
| return false; | |||||
| } | |||||
| else | |||||
| return Parent.FindBucket(id, out bucket); | |||||
| } | |||||
| private void Pause(int milliseconds) | private void Pause(int milliseconds) | ||||
| { | { | ||||
| lock (_pauseLock) | lock (_pauseLock) | ||||
| @@ -151,13 +129,22 @@ namespace Discord.Net.Queue | |||||
| int millis = unchecked(endTick.Value - Environment.TickCount); | int millis = unchecked(endTick.Value - Environment.TickCount); | ||||
| if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) | if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) | ||||
| throw new TimeoutException(); | throw new TimeoutException(); | ||||
| if (!await _semaphore.WaitAsync(0)) | |||||
| { | |||||
| await _queue.RaiseRateLimitTriggered(Id, this, null).ConfigureAwait(false); | |||||
| millis = unchecked(endTick.Value - Environment.TickCount); | |||||
| if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) | |||||
| throw new TimeoutException(); | |||||
| } | |||||
| } | } | ||||
| else | else | ||||
| await _semaphore.WaitAsync().ConfigureAwait(false); | await _semaphore.WaitAsync().ConfigureAwait(false); | ||||
| } | } | ||||
| private async Task QueueExitAsync() | private async Task QueueExitAsync() | ||||
| { | { | ||||
| await Task.Delay(Definition.WindowSeconds * 1000).ConfigureAwait(false); | |||||
| await Task.Delay(WindowSeconds * 1000).ConfigureAwait(false); | |||||
| _semaphore.Release(); | _semaphore.Release(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,22 @@ | |||||
| using Discord.Net.Rest; | |||||
| using System.IO; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public class JsonRestRequest : RestRequest | |||||
| { | |||||
| public string Json { get; } | |||||
| public JsonRestRequest(IRestClient client, string method, string endpoint, string json, RequestOptions options) | |||||
| : base(client, method, endpoint, options) | |||||
| { | |||||
| Json = json; | |||||
| } | |||||
| public override async Task<Stream> SendAsync() | |||||
| { | |||||
| return await Client.SendAsync(Method, Endpoint, Json, Options).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| using Discord.Net.Rest; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.IO; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public class MultipartRestRequest : RestRequest | |||||
| { | |||||
| public IReadOnlyDictionary<string, object> MultipartParams { get; } | |||||
| public MultipartRestRequest(IRestClient client, string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, RequestOptions options) | |||||
| : base(client, method, endpoint, options) | |||||
| { | |||||
| MultipartParams = multipartParams; | |||||
| } | |||||
| public override async Task<Stream> SendAsync() | |||||
| { | |||||
| return await Client.SendAsync(Method, Endpoint, MultipartParams, Options).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,36 @@ | |||||
| using Discord.Net.Rest; | |||||
| using System; | |||||
| using System.IO; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public class RestRequest : IQueuedRequest | |||||
| { | |||||
| public IRestClient Client { get; } | |||||
| public string Method { get; } | |||||
| public string Endpoint { get; } | |||||
| public int? TimeoutTick { get; } | |||||
| public TaskCompletionSource<Stream> Promise { get; } | |||||
| public RequestOptions Options { get; } | |||||
| public CancellationToken CancelToken { get; internal set; } | |||||
| public RestRequest(IRestClient client, string method, string endpoint, RequestOptions options) | |||||
| { | |||||
| Preconditions.NotNull(options, nameof(options)); | |||||
| Client = client; | |||||
| Method = method; | |||||
| Endpoint = endpoint; | |||||
| Options = options; | |||||
| TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; | |||||
| Promise = new TaskCompletionSource<Stream>(); | |||||
| } | |||||
| public virtual async Task<Stream> SendAsync() | |||||
| { | |||||
| return await Client.SendAsync(Method, Endpoint, Options).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -13,16 +13,17 @@ namespace Discord.Net.Queue | |||||
| public bool IsText { get; } | public bool IsText { get; } | ||||
| public int? TimeoutTick { get; } | public int? TimeoutTick { get; } | ||||
| public TaskCompletionSource<Stream> Promise { get; } | public TaskCompletionSource<Stream> Promise { get; } | ||||
| public CancellationToken CancelToken { get; set; } | |||||
| public RequestOptions Options { get; } | |||||
| public CancellationToken CancelToken { get; internal set; } | |||||
| public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options) | public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options) | ||||
| { | { | ||||
| if (options == null) | |||||
| options = RequestOptions.Default; | |||||
| Preconditions.NotNull(options, nameof(options)); | |||||
| Client = client; | Client = client; | ||||
| Data = data; | Data = data; | ||||
| IsText = isText; | IsText = isText; | ||||
| Options = options; | |||||
| TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; | TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; | ||||
| Promise = new TaskCompletionSource<Stream>(); | Promise = new TaskCompletionSource<Stream>(); | ||||
| } | } | ||||
| @@ -1,61 +0,0 @@ | |||||
| using Discord.Net.Rest; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.IO; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Net.Queue | |||||
| { | |||||
| public class RestRequest : IQueuedRequest | |||||
| { | |||||
| public IRestClient Client { get; } | |||||
| public string Method { get; } | |||||
| public string Endpoint { get; } | |||||
| public string Json { get; } | |||||
| public bool HeaderOnly { get; } | |||||
| public int? TimeoutTick { get; } | |||||
| public IReadOnlyDictionary<string, object> MultipartParams { get; } | |||||
| public TaskCompletionSource<Stream> Promise { get; } | |||||
| public CancellationToken CancelToken { get; set; } | |||||
| public bool IsMultipart => MultipartParams != null; | |||||
| public RestRequest(IRestClient client, string method, string endpoint, string json, bool headerOnly, RequestOptions options) | |||||
| : this(client, method, endpoint, headerOnly, options) | |||||
| { | |||||
| Json = json; | |||||
| } | |||||
| public RestRequest(IRestClient client, string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly, RequestOptions options) | |||||
| : this(client, method, endpoint, headerOnly, options) | |||||
| { | |||||
| MultipartParams = multipartParams; | |||||
| } | |||||
| private RestRequest(IRestClient client, string method, string endpoint, bool headerOnly, RequestOptions options) | |||||
| { | |||||
| if (options == null) | |||||
| options = RequestOptions.Default; | |||||
| Client = client; | |||||
| Method = method; | |||||
| Endpoint = endpoint; | |||||
| Json = null; | |||||
| MultipartParams = null; | |||||
| HeaderOnly = headerOnly; | |||||
| TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; | |||||
| Promise = new TaskCompletionSource<Stream>(); | |||||
| } | |||||
| public async Task<Stream> SendAsync() | |||||
| { | |||||
| if (IsMultipart) | |||||
| return await Client.SendAsync(Method, Endpoint, MultipartParams, HeaderOnly).ConfigureAwait(false); | |||||
| else if (Json != null) | |||||
| return await Client.SendAsync(Method, Endpoint, Json, HeaderOnly).ConfigureAwait(false); | |||||
| else | |||||
| return await Client.SendAsync(Method, Endpoint, HeaderOnly).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -4,13 +4,13 @@ namespace Discord.Net | |||||
| { | { | ||||
| public class HttpRateLimitException : HttpException | public class HttpRateLimitException : HttpException | ||||
| { | { | ||||
| public string BucketId { get; } | |||||
| public string Id { get; } | |||||
| public int RetryAfterMilliseconds { get; } | public int RetryAfterMilliseconds { get; } | ||||
| public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason) | public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason) | ||||
| : base((HttpStatusCode)429, reason) | : base((HttpStatusCode)429, reason) | ||||
| { | { | ||||
| BucketId = bucketId; | |||||
| Id = bucketId; | |||||
| RetryAfterMilliseconds = retryAfterMilliseconds; | RetryAfterMilliseconds = retryAfterMilliseconds; | ||||
| } | } | ||||
| } | } | ||||
| @@ -67,22 +67,22 @@ namespace Discord.Net.Rest | |||||
| _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; | _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; | ||||
| } | } | ||||
| public async Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false) | |||||
| public async Task<Stream> SendAsync(string method, string endpoint, RequestOptions options) | |||||
| { | { | ||||
| string uri = Path.Combine(_baseUrl, endpoint); | string uri = Path.Combine(_baseUrl, endpoint); | ||||
| using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | ||||
| return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); | |||||
| return await SendInternalAsync(restRequest, options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false) | |||||
| public async Task<Stream> SendAsync(string method, string endpoint, string json, RequestOptions options) | |||||
| { | { | ||||
| string uri = Path.Combine(_baseUrl, endpoint); | string uri = Path.Combine(_baseUrl, endpoint); | ||||
| using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | ||||
| { | { | ||||
| restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); | restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); | ||||
| return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); | |||||
| return await SendInternalAsync(restRequest, options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| public async Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false) | |||||
| public async Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, RequestOptions options) | |||||
| { | { | ||||
| string uri = Path.Combine(_baseUrl, endpoint); | string uri = Path.Combine(_baseUrl, endpoint); | ||||
| using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | ||||
| @@ -110,11 +110,11 @@ namespace Discord.Net.Rest | |||||
| } | } | ||||
| } | } | ||||
| restRequest.Content = content; | restRequest.Content = content; | ||||
| return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); | |||||
| return await SendInternalAsync(restRequest, options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| private async Task<Stream> SendInternalAsync(HttpRequestMessage request, bool headerOnly) | |||||
| private async Task<Stream> SendInternalAsync(HttpRequestMessage request, RequestOptions options) | |||||
| { | { | ||||
| while (true) | while (true) | ||||
| { | { | ||||
| @@ -154,7 +154,7 @@ namespace Discord.Net.Rest | |||||
| throw new HttpException(response.StatusCode, reason); | throw new HttpException(response.StatusCode, reason); | ||||
| } | } | ||||
| if (headerOnly) | |||||
| if (options.HeaderOnly) | |||||
| return null; | return null; | ||||
| else | else | ||||
| return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); | return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); | ||||
| @@ -11,8 +11,8 @@ namespace Discord.Net.Rest | |||||
| void SetHeader(string key, string value); | void SetHeader(string key, string value); | ||||
| void SetCancelToken(CancellationToken cancelToken); | void SetCancelToken(CancellationToken cancelToken); | ||||
| Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false); | |||||
| Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false); | |||||
| Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false); | |||||
| Task<Stream> SendAsync(string method, string endpoint, RequestOptions options); | |||||
| Task<Stream> SendAsync(string method, string endpoint, string json, RequestOptions options); | |||||
| Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, RequestOptions options); | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,10 +6,24 @@ | |||||
| /// <summary> The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. </summary> | /// <summary> The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. </summary> | ||||
| public int? Timeout { get; set; } | public int? Timeout { get; set; } | ||||
| public string BucketId { get; set; } | |||||
| public bool HeaderOnly { get; internal set; } | |||||
| internal bool IgnoreState { get; set; } | |||||
| public static RequestOptions CreateOrClone(RequestOptions options) | |||||
| { | |||||
| if (options == null) | |||||
| return new RequestOptions(); | |||||
| else | |||||
| return options.Clone(); | |||||
| } | |||||
| public RequestOptions() | public RequestOptions() | ||||
| { | { | ||||
| Timeout = 30000; | Timeout = 30000; | ||||
| } | } | ||||
| public RequestOptions Clone() => MemberwiseClone() as RequestOptions; | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,7 +8,7 @@ using System.Collections.Immutable; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| public abstract class DiscordClient : IDiscordClient | |||||
| public abstract class BaseDiscordClient : IDiscordClient | |||||
| { | { | ||||
| public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } | public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } | ||||
| private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>(); | private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>(); | ||||
| @@ -29,7 +29,7 @@ namespace Discord.Rest | |||||
| public ISelfUser CurrentUser { get; protected set; } | public ISelfUser CurrentUser { get; protected set; } | ||||
| /// <summary> Creates a new REST-only discord client. </summary> | /// <summary> Creates a new REST-only discord client. </summary> | ||||
| internal DiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) | |||||
| internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) | |||||
| { | { | ||||
| ApiClient = client; | ApiClient = client; | ||||
| LogManager = new LogManager(config.LogLevel); | LogManager = new LogManager(config.LogLevel); | ||||
| @@ -73,6 +73,7 @@ namespace Discord.Rest | |||||
| try | try | ||||
| { | { | ||||
| await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false); | |||||
| await OnLoginAsync(tokenType, token).ConfigureAwait(false); | await OnLoginAsync(tokenType, token).ConfigureAwait(false); | ||||
| LoginState = LoginState.LoggedIn; | LoginState = LoginState.LoggedIn; | ||||
| } | } | ||||
| @@ -10,13 +10,13 @@ namespace Discord.Rest | |||||
| internal static class ClientHelper | internal static class ClientHelper | ||||
| { | { | ||||
| //Applications | //Applications | ||||
| public static async Task<RestApplication> GetApplicationInfoAsync(DiscordClient client) | |||||
| public static async Task<RestApplication> GetApplicationInfoAsync(BaseDiscordClient client) | |||||
| { | { | ||||
| var model = await client.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); | var model = await client.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); | ||||
| return RestApplication.Create(client, model); | return RestApplication.Create(client, model); | ||||
| } | } | ||||
| public static async Task<RestChannel> GetChannelAsync(DiscordClient client, | |||||
| public static async Task<RestChannel> GetChannelAsync(BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetChannelAsync(id).ConfigureAwait(false); | var model = await client.ApiClient.GetChannelAsync(id).ConfigureAwait(false); | ||||
| @@ -24,19 +24,19 @@ namespace Discord.Rest | |||||
| return RestChannel.Create(client, model); | return RestChannel.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync(DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync(BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); | var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); | ||||
| return models.Select(x => RestDMChannel.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestDMChannel.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetMyConnectionsAsync().ConfigureAwait(false); | var models = await client.ApiClient.GetMyConnectionsAsync().ConfigureAwait(false); | ||||
| return models.Select(x => RestConnection.Create(x)).ToImmutableArray(); | return models.Select(x => RestConnection.Create(x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestInvite> GetInviteAsync(DiscordClient client, | |||||
| public static async Task<RestInvite> GetInviteAsync(BaseDiscordClient client, | |||||
| string inviteId) | string inviteId) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetInviteAsync(inviteId).ConfigureAwait(false); | var model = await client.ApiClient.GetInviteAsync(inviteId).ConfigureAwait(false); | ||||
| @@ -45,7 +45,7 @@ namespace Discord.Rest | |||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<RestGuild> GetGuildAsync(DiscordClient client, | |||||
| public static async Task<RestGuild> GetGuildAsync(BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetGuildAsync(id).ConfigureAwait(false); | var model = await client.ApiClient.GetGuildAsync(id).ConfigureAwait(false); | ||||
| @@ -53,7 +53,7 @@ namespace Discord.Rest | |||||
| return RestGuild.Create(client, model); | return RestGuild.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<RestGuildEmbed?> GetGuildEmbedAsync(DiscordClient client, | |||||
| public static async Task<RestGuildEmbed?> GetGuildEmbedAsync(BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetGuildEmbedAsync(id).ConfigureAwait(false); | var model = await client.ApiClient.GetGuildEmbedAsync(id).ConfigureAwait(false); | ||||
| @@ -61,12 +61,12 @@ namespace Discord.Rest | |||||
| return RestGuildEmbed.Create(model); | return RestGuildEmbed.Create(model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); | var models = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); | ||||
| return models.Select(x => RestUserGuild.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestUserGuild.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestGuild>> GetGuildsAsync(DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestGuild>> GetGuildsAsync(BaseDiscordClient client) | |||||
| { | { | ||||
| var summaryModels = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); | var summaryModels = await client.ApiClient.GetMyGuildsAsync().ConfigureAwait(false); | ||||
| var guilds = ImmutableArray.CreateBuilder<RestGuild>(summaryModels.Count); | var guilds = ImmutableArray.CreateBuilder<RestGuild>(summaryModels.Count); | ||||
| @@ -78,7 +78,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| return guilds.ToImmutable(); | return guilds.ToImmutable(); | ||||
| } | } | ||||
| public static async Task<RestGuild> CreateGuildAsync(DiscordClient client, | |||||
| public static async Task<RestGuild> CreateGuildAsync(BaseDiscordClient client, | |||||
| string name, IVoiceRegion region, Stream jpegIcon = null) | string name, IVoiceRegion region, Stream jpegIcon = null) | ||||
| { | { | ||||
| var args = new CreateGuildParams(name, region.Id); | var args = new CreateGuildParams(name, region.Id); | ||||
| @@ -86,7 +86,7 @@ namespace Discord.Rest | |||||
| return RestGuild.Create(client, model); | return RestGuild.Create(client, model); | ||||
| } | } | ||||
| public static async Task<RestUser> GetUserAsync(DiscordClient client, | |||||
| public static async Task<RestUser> GetUserAsync(BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetUserAsync(id).ConfigureAwait(false); | var model = await client.ApiClient.GetUserAsync(id).ConfigureAwait(false); | ||||
| @@ -94,7 +94,7 @@ namespace Discord.Rest | |||||
| return RestUser.Create(client, model); | return RestUser.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<RestUser> GetUserAsync(DiscordClient client, | |||||
| public static async Task<RestUser> GetUserAsync(BaseDiscordClient client, | |||||
| string username, string discriminator) | string username, string discriminator) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetUserAsync(username, discriminator).ConfigureAwait(false); | var model = await client.ApiClient.GetUserAsync(username, discriminator).ConfigureAwait(false); | ||||
| @@ -103,12 +103,12 @@ namespace Discord.Rest | |||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); | var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); | ||||
| return models.Select(x => RestVoiceRegion.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestVoiceRegion.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestVoiceRegion> GetVoiceRegionAsync(DiscordClient client, | |||||
| public static async Task<RestVoiceRegion> GetVoiceRegionAsync(BaseDiscordClient client, | |||||
| string id) | string id) | ||||
| { | { | ||||
| var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); | var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); | ||||
| @@ -5,7 +5,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| public class DiscordRestClient : DiscordClient, IDiscordClient | |||||
| public class DiscordRestClient : BaseDiscordClient, IDiscordClient | |||||
| { | { | ||||
| public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser; | public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser; | ||||
| @@ -15,10 +15,10 @@ namespace Discord.Rest | |||||
| private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | ||||
| => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, requestQueue: new RequestQueue()); | => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, requestQueue: new RequestQueue()); | ||||
| protected override async Task OnLoginAsync(TokenType tokenType, string token) | |||||
| protected override Task OnLoginAsync(TokenType tokenType, string token) | |||||
| { | { | ||||
| await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false); | |||||
| base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); | base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); | ||||
| return Task.CompletedTask; | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -12,33 +12,33 @@ namespace Discord.Rest | |||||
| internal static class ChannelHelper | internal static class ChannelHelper | ||||
| { | { | ||||
| //General | //General | ||||
| public static async Task<Model> GetAsync(IGuildChannel channel, DiscordClient client) | |||||
| public static async Task<Model> GetAsync(IGuildChannel channel, BaseDiscordClient client) | |||||
| { | { | ||||
| return await client.ApiClient.GetChannelAsync(channel.GuildId, channel.Id).ConfigureAwait(false); | return await client.ApiClient.GetChannelAsync(channel.GuildId, channel.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task<Model> GetAsync(IPrivateChannel channel, DiscordClient client) | |||||
| public static async Task<Model> GetAsync(IPrivateChannel channel, BaseDiscordClient client) | |||||
| { | { | ||||
| return await client.ApiClient.GetChannelAsync(channel.Id).ConfigureAwait(false); | return await client.ApiClient.GetChannelAsync(channel.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task DeleteAsync(IChannel channel, DiscordClient client) | |||||
| public static async Task DeleteAsync(IChannel channel, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.DeleteChannelAsync(channel.Id).ConfigureAwait(false); | await client.ApiClient.DeleteChannelAsync(channel.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task ModifyAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static async Task ModifyAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| Action<ModifyGuildChannelParams> func) | Action<ModifyGuildChannelParams> func) | ||||
| { | { | ||||
| var args = new ModifyGuildChannelParams(); | var args = new ModifyGuildChannelParams(); | ||||
| func(args); | func(args); | ||||
| await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); | await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); | ||||
| } | } | ||||
| public static async Task ModifyAsync(ITextChannel channel, DiscordClient client, | |||||
| public static async Task ModifyAsync(ITextChannel channel, BaseDiscordClient client, | |||||
| Action<ModifyTextChannelParams> func) | Action<ModifyTextChannelParams> func) | ||||
| { | { | ||||
| var args = new ModifyTextChannelParams(); | var args = new ModifyTextChannelParams(); | ||||
| func(args); | func(args); | ||||
| await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); | await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); | ||||
| } | } | ||||
| public static async Task ModifyAsync(IVoiceChannel channel, DiscordClient client, | |||||
| public static async Task ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, | |||||
| Action<ModifyVoiceChannelParams> func) | Action<ModifyVoiceChannelParams> func) | ||||
| { | { | ||||
| var args = new ModifyVoiceChannelParams(); | var args = new ModifyVoiceChannelParams(); | ||||
| @@ -47,12 +47,12 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Invites | //Invites | ||||
| public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IChannel channel, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IChannel channel, BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id); | var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id); | ||||
| return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestInviteMetadata> CreateInviteAsync(IChannel channel, DiscordClient client, | |||||
| public static async Task<RestInviteMetadata> CreateInviteAsync(IChannel channel, BaseDiscordClient client, | |||||
| int? maxAge, int? maxUses, bool isTemporary) | int? maxAge, int? maxUses, bool isTemporary) | ||||
| { | { | ||||
| var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; | var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; | ||||
| @@ -65,13 +65,13 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Messages | //Messages | ||||
| public static async Task<RestMessage> GetMessageAsync(IChannel channel, DiscordClient client, | |||||
| public static async Task<RestMessage> GetMessageAsync(IChannel channel, BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id).ConfigureAwait(false); | var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id).ConfigureAwait(false); | ||||
| return RestMessage.Create(client, model); | return RestMessage.Create(client, model); | ||||
| } | } | ||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IChannel channel, DiscordClient client, | |||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IChannel channel, BaseDiscordClient client, | |||||
| ulong? fromMessageId = null, Direction dir = Direction.Before, int limit = DiscordConfig.MaxMessagesPerBatch) | ulong? fromMessageId = null, Direction dir = Direction.Before, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| { | { | ||||
| //TODO: Test this with Around direction | //TODO: Test this with Around direction | ||||
| @@ -102,13 +102,13 @@ namespace Discord.Rest | |||||
| count: (uint)limit | count: (uint)limit | ||||
| ); | ); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(IChannel channel, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(IChannel channel, BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetPinsAsync(channel.Id).ConfigureAwait(false); | var models = await client.ApiClient.GetPinsAsync(channel.Id).ConfigureAwait(false); | ||||
| return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestUserMessage> SendMessageAsync(IChannel channel, DiscordClient client, | |||||
| public static async Task<RestUserMessage> SendMessageAsync(IChannel channel, BaseDiscordClient client, | |||||
| string text, bool isTTS) | string text, bool isTTS) | ||||
| { | { | ||||
| var args = new CreateMessageParams(text) { IsTTS = isTTS }; | var args = new CreateMessageParams(text) { IsTTS = isTTS }; | ||||
| @@ -116,14 +116,14 @@ namespace Discord.Rest | |||||
| return RestUserMessage.Create(client, model); | return RestUserMessage.Create(client, model); | ||||
| } | } | ||||
| public static Task<RestUserMessage> SendFileAsync(IChannel channel, DiscordClient client, | |||||
| public static Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client, | |||||
| string filePath, string text, bool isTTS) | string filePath, string text, bool isTTS) | ||||
| { | { | ||||
| string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
| using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
| return SendFileAsync(channel, client, file, filename, text, isTTS); | return SendFileAsync(channel, client, file, filename, text, isTTS); | ||||
| } | } | ||||
| public static async Task<RestUserMessage> SendFileAsync(IChannel channel, DiscordClient client, | |||||
| public static async Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client, | |||||
| Stream stream, string filename, string text, bool isTTS) | Stream stream, string filename, string text, bool isTTS) | ||||
| { | { | ||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; | var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; | ||||
| @@ -131,7 +131,7 @@ namespace Discord.Rest | |||||
| return RestUserMessage.Create(client, model); | return RestUserMessage.Create(client, model); | ||||
| } | } | ||||
| public static async Task DeleteMessagesAsync(IChannel channel, DiscordClient client, | |||||
| public static async Task DeleteMessagesAsync(IChannel channel, BaseDiscordClient client, | |||||
| IEnumerable<IMessage> messages) | IEnumerable<IMessage> messages) | ||||
| { | { | ||||
| var args = new DeleteMessagesParams(messages.Select(x => x.Id).ToArray()); | var args = new DeleteMessagesParams(messages.Select(x => x.Id).ToArray()); | ||||
| @@ -139,31 +139,31 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Permission Overwrites | //Permission Overwrites | ||||
| public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| IUser user, OverwritePermissions perms) | IUser user, OverwritePermissions perms) | ||||
| { | { | ||||
| var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue); | var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue); | ||||
| await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args).ConfigureAwait(false); | await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| IRole role, OverwritePermissions perms) | IRole role, OverwritePermissions perms) | ||||
| { | { | ||||
| var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue); | var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue); | ||||
| await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args).ConfigureAwait(false); | await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| IUser user) | IUser user) | ||||
| { | { | ||||
| await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, user.Id).ConfigureAwait(false); | await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, user.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| IRole role) | IRole role) | ||||
| { | { | ||||
| await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, role.Id).ConfigureAwait(false); | await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, role.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| //Users | //Users | ||||
| public static async Task<RestGuildUser> GetUserAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static async Task<RestGuildUser> GetUserAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetGuildMemberAsync(channel.GuildId, id); | var model = await client.ApiClient.GetGuildMemberAsync(channel.GuildId, id); | ||||
| @@ -175,7 +175,7 @@ namespace Discord.Rest | |||||
| return user; | return user; | ||||
| } | } | ||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuildChannel channel, DiscordClient client, | |||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuildChannel channel, BaseDiscordClient client, | |||||
| ulong? froUserId = null, uint? limit = DiscordConfig.MaxUsersPerBatch) | ulong? froUserId = null, uint? limit = DiscordConfig.MaxUsersPerBatch) | ||||
| { | { | ||||
| return new PagedAsyncEnumerable<RestGuildUser>( | return new PagedAsyncEnumerable<RestGuildUser>( | ||||
| @@ -203,7 +203,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Typing | //Typing | ||||
| public static IDisposable EnterTypingState(IChannel channel, DiscordClient client) | |||||
| public static IDisposable EnterTypingState(IChannel channel, BaseDiscordClient client) | |||||
| { | { | ||||
| throw new NotImplementedException(); //TODO: Impl | throw new NotImplementedException(); //TODO: Impl | ||||
| } | } | ||||
| @@ -11,11 +11,11 @@ namespace Discord.Rest | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public abstract class RestChannel : RestEntity<ulong>, IChannel, IUpdateable | public abstract class RestChannel : RestEntity<ulong>, IChannel, IUpdateable | ||||
| { | { | ||||
| internal RestChannel(DiscordClient discord, ulong id) | |||||
| internal RestChannel(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestChannel Create(DiscordClient discord, Model model) | |||||
| internal static RestChannel Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| switch (model.Type) | switch (model.Type) | ||||
| { | { | ||||
| @@ -17,13 +17,13 @@ namespace Discord.Rest | |||||
| public IReadOnlyCollection<RestUser> Users => ImmutableArray.Create(CurrentUser, Recipient); | public IReadOnlyCollection<RestUser> Users => ImmutableArray.Create(CurrentUser, Recipient); | ||||
| internal RestDMChannel(DiscordClient discord, ulong id, ulong recipientId) | |||||
| internal RestDMChannel(BaseDiscordClient discord, ulong id, ulong recipientId) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| Recipient = new RestUser(Discord, recipientId); | Recipient = new RestUser(Discord, recipientId); | ||||
| CurrentUser = new RestUser(Discord, discord.CurrentUser.Id); | CurrentUser = new RestUser(Discord, discord.CurrentUser.Id); | ||||
| } | } | ||||
| internal new static RestDMChannel Create(DiscordClient discord, Model model) | |||||
| internal new static RestDMChannel Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestDMChannel(discord, model.Id, model.Recipients.Value[0].Id); | var entity = new RestDMChannel(discord, model.Id, model.Recipients.Value[0].Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -21,11 +21,11 @@ namespace Discord.Rest | |||||
| public IReadOnlyCollection<RestGroupUser> Recipients | public IReadOnlyCollection<RestGroupUser> Recipients | ||||
| => _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1); | => _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1); | ||||
| internal RestGroupChannel(DiscordClient discord, ulong id) | |||||
| internal RestGroupChannel(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestGroupChannel Create(DiscordClient discord, Model model) | |||||
| internal new static RestGroupChannel Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestGroupChannel(discord, model.Id); | var entity = new RestGroupChannel(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -21,12 +21,12 @@ namespace Discord.Rest | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| public int Position { get; private set; } | public int Position { get; private set; } | ||||
| internal RestGuildChannel(DiscordClient discord, ulong id, ulong guildId) | |||||
| internal RestGuildChannel(BaseDiscordClient discord, ulong id, ulong guildId) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| GuildId = guildId; | GuildId = guildId; | ||||
| } | } | ||||
| internal new static RestGuildChannel Create(DiscordClient discord, Model model) | |||||
| internal new static RestGuildChannel Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| switch (model.Type) | switch (model.Type) | ||||
| { | { | ||||
| @@ -16,11 +16,11 @@ namespace Discord.Rest | |||||
| public string Mention => MentionUtils.MentionChannel(Id); | public string Mention => MentionUtils.MentionChannel(Id); | ||||
| internal RestTextChannel(DiscordClient discord, ulong id, ulong guildId) | |||||
| internal RestTextChannel(BaseDiscordClient discord, ulong id, ulong guildId) | |||||
| : base(discord, id, guildId) | : base(discord, id, guildId) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestTextChannel Create(DiscordClient discord, Model model) | |||||
| internal new static RestTextChannel Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestTextChannel(discord, model.Id, model.GuildId.Value); | var entity = new RestTextChannel(discord, model.Id, model.GuildId.Value); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -16,11 +16,11 @@ namespace Discord.Rest | |||||
| public int Bitrate { get; private set; } | public int Bitrate { get; private set; } | ||||
| public int UserLimit { get; private set; } | public int UserLimit { get; private set; } | ||||
| internal RestVoiceChannel(DiscordClient discord, ulong id, ulong guildId) | |||||
| internal RestVoiceChannel(BaseDiscordClient discord, ulong id, ulong guildId) | |||||
| : base(discord, id, guildId) | : base(discord, id, guildId) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestVoiceChannel Create(DiscordClient discord, Model model) | |||||
| internal new static RestVoiceChannel Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestVoiceChannel(discord, model.Id, model.GuildId.Value); | var entity = new RestVoiceChannel(discord, model.Id, model.GuildId.Value); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -13,7 +13,7 @@ namespace Discord.Rest | |||||
| internal static class GuildHelper | internal static class GuildHelper | ||||
| { | { | ||||
| //General | //General | ||||
| public static async Task<Model> ModifyAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client, | |||||
| Action<ModifyGuildParams> func) | Action<ModifyGuildParams> func) | ||||
| { | { | ||||
| if (func == null) throw new NullReferenceException(nameof(func)); | if (func == null) throw new NullReferenceException(nameof(func)); | ||||
| @@ -28,7 +28,7 @@ namespace Discord.Rest | |||||
| return await client.ApiClient.ModifyGuildAsync(guild.Id, args).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildAsync(guild.Id, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, | |||||
| Action<ModifyGuildEmbedParams> func) | Action<ModifyGuildEmbedParams> func) | ||||
| { | { | ||||
| if (func == null) throw new NullReferenceException(nameof(func)); | if (func == null) throw new NullReferenceException(nameof(func)); | ||||
| @@ -37,46 +37,46 @@ namespace Discord.Rest | |||||
| func(args); | func(args); | ||||
| return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, args).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task ModifyChannelsAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task ModifyChannelsAsync(IGuild guild, BaseDiscordClient client, | |||||
| IEnumerable<ModifyGuildChannelsParams> args) | IEnumerable<ModifyGuildChannelsParams> args) | ||||
| { | { | ||||
| await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args).ConfigureAwait(false); | await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RoleModel>> ModifyRolesAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<IReadOnlyCollection<RoleModel>> ModifyRolesAsync(IGuild guild, BaseDiscordClient client, | |||||
| IEnumerable<ModifyGuildRolesParams> args) | IEnumerable<ModifyGuildRolesParams> args) | ||||
| { | { | ||||
| return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, args).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task LeaveAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task LeaveAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.LeaveGuildAsync(guild.Id).ConfigureAwait(false); | await client.ApiClient.LeaveGuildAsync(guild.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task DeleteAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task DeleteAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.DeleteGuildAsync(guild.Id).ConfigureAwait(false); | await client.ApiClient.DeleteGuildAsync(guild.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| //Bans | //Bans | ||||
| public static async Task<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetGuildBansAsync(guild.Id); | var models = await client.ApiClient.GetGuildBansAsync(guild.Id); | ||||
| return models.Select(x => RestBan.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestBan.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task AddBanAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task AddBanAsync(IGuild guild, BaseDiscordClient client, | |||||
| ulong userId, int pruneDays) | ulong userId, int pruneDays) | ||||
| { | { | ||||
| var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays }; | var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays }; | ||||
| await client.ApiClient.CreateGuildBanAsync(guild.Id, userId, args); | await client.ApiClient.CreateGuildBanAsync(guild.Id, userId, args); | ||||
| } | } | ||||
| public static async Task RemoveBanAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task RemoveBanAsync(IGuild guild, BaseDiscordClient client, | |||||
| ulong userId) | ulong userId) | ||||
| { | { | ||||
| await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId); | await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId); | ||||
| } | } | ||||
| //Channels | //Channels | ||||
| public static async Task<RestGuildChannel> GetChannelAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<RestGuildChannel> GetChannelAsync(IGuild guild, BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetChannelAsync(guild.Id, id).ConfigureAwait(false); | var model = await client.ApiClient.GetChannelAsync(guild.Id, id).ConfigureAwait(false); | ||||
| @@ -84,12 +84,12 @@ namespace Discord.Rest | |||||
| return RestGuildChannel.Create(client, model); | return RestGuildChannel.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false); | var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false); | ||||
| return models.Select(x => RestGuildChannel.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestGuildChannel.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, | |||||
| string name) | string name) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| @@ -98,7 +98,7 @@ namespace Discord.Rest | |||||
| var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args).ConfigureAwait(false); | var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args).ConfigureAwait(false); | ||||
| return RestTextChannel.Create(client, model); | return RestTextChannel.Create(client, model); | ||||
| } | } | ||||
| public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, | |||||
| string name) | string name) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| @@ -109,12 +109,12 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Integrations | //Integrations | ||||
| public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id).ConfigureAwait(false); | var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id).ConfigureAwait(false); | ||||
| return models.Select(x => RestGuildIntegration.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestGuildIntegration.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestGuildIntegration> CreateIntegrationAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<RestGuildIntegration> CreateIntegrationAsync(IGuild guild, BaseDiscordClient client, | |||||
| ulong id, string type) | ulong id, string type) | ||||
| { | { | ||||
| var args = new CreateGuildIntegrationParams(id, type); | var args = new CreateGuildIntegrationParams(id, type); | ||||
| @@ -123,14 +123,14 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Invites | //Invites | ||||
| public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id).ConfigureAwait(false); | var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id).ConfigureAwait(false); | ||||
| return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| //Roles | //Roles | ||||
| public static async Task<RestRole> CreateRoleAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client, | |||||
| string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false) | string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| @@ -150,7 +150,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Users | //Users | ||||
| public static async Task<RestGuildUser> GetUserAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<RestGuildUser> GetUserAsync(IGuild guild, BaseDiscordClient client, | |||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetGuildMemberAsync(guild.Id, id).ConfigureAwait(false); | var model = await client.ApiClient.GetGuildMemberAsync(guild.Id, id).ConfigureAwait(false); | ||||
| @@ -158,17 +158,17 @@ namespace Discord.Rest | |||||
| return RestGuildUser.Create(client, model); | return RestGuildUser.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<RestGuildUser> GetCurrentUserAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task<RestGuildUser> GetCurrentUserAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| return await GetUserAsync(guild, client, client.CurrentUser.Id).ConfigureAwait(false); | return await GetUserAsync(guild, client, client.CurrentUser.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuild guild, DiscordClient client) | |||||
| public static async Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuild guild, BaseDiscordClient client) | |||||
| { | { | ||||
| var args = new GetGuildMembersParams(); | var args = new GetGuildMembersParams(); | ||||
| var models = await client.ApiClient.GetGuildMembersAsync(guild.Id, args).ConfigureAwait(false); | var models = await client.ApiClient.GetGuildMembersAsync(guild.Id, args).ConfigureAwait(false); | ||||
| return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray(); | return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<int> PruneUsersAsync(IGuild guild, DiscordClient client, | |||||
| public static async Task<int> PruneUsersAsync(IGuild guild, BaseDiscordClient client, | |||||
| int days = 30, bool simulate = false) | int days = 30, bool simulate = false) | ||||
| { | { | ||||
| var args = new GuildPruneParams(days); | var args = new GuildPruneParams(days); | ||||
| @@ -14,7 +14,7 @@ namespace Discord.Rest | |||||
| User = user; | User = user; | ||||
| Reason = reason; | Reason = reason; | ||||
| } | } | ||||
| internal static RestBan Create(DiscordClient client, Model model) | |||||
| internal static RestBan Create(BaseDiscordClient client, Model model) | |||||
| { | { | ||||
| return new RestBan(RestUser.Create(client, model.User), model.Reason); | return new RestBan(RestUser.Create(client, model.User), model.Reason); | ||||
| } | } | ||||
| @@ -39,11 +39,11 @@ namespace Discord.Rest | |||||
| public IReadOnlyCollection<Emoji> Emojis => _emojis; | public IReadOnlyCollection<Emoji> Emojis => _emojis; | ||||
| public IReadOnlyCollection<string> Features => _features; | public IReadOnlyCollection<string> Features => _features; | ||||
| internal RestGuild(DiscordClient client, ulong id) | |||||
| internal RestGuild(BaseDiscordClient client, ulong id) | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestGuild Create(DiscordClient discord, Model model) | |||||
| internal static RestGuild Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestGuild(discord, model.Id); | var entity = new RestGuild(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -25,11 +25,11 @@ namespace Discord.Rest | |||||
| public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); | public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); | ||||
| internal RestGuildIntegration(DiscordClient discord, ulong id) | |||||
| internal RestGuildIntegration(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestGuildIntegration Create(DiscordClient discord, Model model) | |||||
| internal static RestGuildIntegration Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestGuildIntegration(discord, model.Id); | var entity = new RestGuildIntegration(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -15,11 +15,11 @@ namespace Discord.Rest | |||||
| public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); | public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); | ||||
| internal RestUserGuild(DiscordClient discord, ulong id) | |||||
| internal RestUserGuild(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestUserGuild Create(DiscordClient discord, Model model) | |||||
| internal static RestUserGuild Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestUserGuild(discord, model.Id); | var entity = new RestUserGuild(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -13,11 +13,11 @@ namespace Discord | |||||
| public string SampleHostname { get; private set; } | public string SampleHostname { get; private set; } | ||||
| public int SamplePort { get; private set; } | public int SamplePort { get; private set; } | ||||
| internal RestVoiceRegion(DiscordClient client, string id) | |||||
| internal RestVoiceRegion(BaseDiscordClient client, string id) | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestVoiceRegion Create(DiscordClient client, Model model) | |||||
| internal static RestVoiceRegion Create(BaseDiscordClient client, Model model) | |||||
| { | { | ||||
| var entity = new RestVoiceRegion(client, model.Id); | var entity = new RestVoiceRegion(client, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -5,15 +5,15 @@ namespace Discord.Rest | |||||
| { | { | ||||
| internal static class InviteHelper | internal static class InviteHelper | ||||
| { | { | ||||
| public static async Task<Model> GetAsync(IInvite invite, DiscordClient client) | |||||
| public static async Task<Model> GetAsync(IInvite invite, BaseDiscordClient client) | |||||
| { | { | ||||
| return await client.ApiClient.GetInviteAsync(invite.Code).ConfigureAwait(false); | return await client.ApiClient.GetInviteAsync(invite.Code).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task AcceptAsync(IInvite invite, DiscordClient client) | |||||
| public static async Task AcceptAsync(IInvite invite, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.AcceptInviteAsync(invite.Code).ConfigureAwait(false); | await client.ApiClient.AcceptInviteAsync(invite.Code).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task DeleteAsync(IInvite invite, DiscordClient client) | |||||
| public static async Task DeleteAsync(IInvite invite, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.DeleteInviteAsync(invite.Code).ConfigureAwait(false); | await client.ApiClient.DeleteInviteAsync(invite.Code).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -16,11 +16,11 @@ namespace Discord.Rest | |||||
| public string Code => Id; | public string Code => Id; | ||||
| public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; | public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; | ||||
| internal RestInvite(DiscordClient discord, string id) | |||||
| internal RestInvite(BaseDiscordClient discord, string id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestInvite Create(DiscordClient discord, Model model) | |||||
| internal static RestInvite Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestInvite(discord, model.Code); | var entity = new RestInvite(discord, model.Code); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -18,11 +18,11 @@ namespace Discord.Rest | |||||
| public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); | public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); | ||||
| internal RestInviteMetadata(DiscordClient discord, string id) | |||||
| internal RestInviteMetadata(BaseDiscordClient discord, string id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestInviteMetadata Create(DiscordClient discord, Model model) | |||||
| internal static RestInviteMetadata Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestInviteMetadata(discord, model.Code); | var entity = new RestInviteMetadata(discord, model.Code); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -6,26 +6,26 @@ namespace Discord.Rest | |||||
| { | { | ||||
| internal static class MessageHelper | internal static class MessageHelper | ||||
| { | { | ||||
| public static async Task GetAsync(IMessage msg, DiscordClient client) | |||||
| public static async Task GetAsync(IMessage msg, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.GetChannelMessageAsync(msg.ChannelId, msg.Id); | await client.ApiClient.GetChannelMessageAsync(msg.ChannelId, msg.Id); | ||||
| } | } | ||||
| public static async Task ModifyAsync(IMessage msg, DiscordClient client, Action<ModifyMessageParams> func) | |||||
| public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action<ModifyMessageParams> func) | |||||
| { | { | ||||
| var args = new ModifyMessageParams(); | var args = new ModifyMessageParams(); | ||||
| func(args); | func(args); | ||||
| await client.ApiClient.ModifyMessageAsync(msg.ChannelId, msg.Id, args); | await client.ApiClient.ModifyMessageAsync(msg.ChannelId, msg.Id, args); | ||||
| } | } | ||||
| public static async Task DeleteAsync(IMessage msg, DiscordClient client) | |||||
| public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.DeleteMessageAsync(msg.ChannelId, msg.Id); | await client.ApiClient.DeleteMessageAsync(msg.ChannelId, msg.Id); | ||||
| } | } | ||||
| public static async Task PinAsync(IMessage msg, DiscordClient client) | |||||
| public static async Task PinAsync(IMessage msg, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.AddPinAsync(msg.ChannelId, msg.Id); | await client.ApiClient.AddPinAsync(msg.ChannelId, msg.Id); | ||||
| } | } | ||||
| public static async Task UnpinAsync(IMessage msg, DiscordClient client) | |||||
| public static async Task UnpinAsync(IMessage msg, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id); | await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id); | ||||
| } | } | ||||
| @@ -29,12 +29,12 @@ namespace Discord.Rest | |||||
| public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
| internal RestMessage(DiscordClient discord, ulong id, ulong channelId) | |||||
| internal RestMessage(BaseDiscordClient discord, ulong id, ulong channelId) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| ChannelId = channelId; | ChannelId = channelId; | ||||
| } | } | ||||
| internal static RestMessage Create(DiscordClient discord, Model model) | |||||
| internal static RestMessage Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| if (model.Type == MessageType.Default) | if (model.Type == MessageType.Default) | ||||
| return RestUserMessage.Create(discord, model); | return RestUserMessage.Create(discord, model); | ||||
| @@ -8,11 +8,11 @@ namespace Discord.Rest | |||||
| { | { | ||||
| public MessageType Type { get; private set; } | public MessageType Type { get; private set; } | ||||
| internal RestSystemMessage(DiscordClient discord, ulong id, ulong channelId) | |||||
| internal RestSystemMessage(BaseDiscordClient discord, ulong id, ulong channelId) | |||||
| : base(discord, id, channelId) | : base(discord, id, channelId) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestSystemMessage Create(DiscordClient discord, Model model) | |||||
| internal new static RestSystemMessage Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestSystemMessage(discord, model.Id, model.ChannelId); | var entity = new RestSystemMessage(discord, model.Id, model.ChannelId); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -30,11 +30,11 @@ namespace Discord.Rest | |||||
| public override IReadOnlyCollection<IRole> MentionedRoles => _mentionedRoles; | public override IReadOnlyCollection<IRole> MentionedRoles => _mentionedRoles; | ||||
| public override IReadOnlyCollection<IUser> MentionedUsers => _mentionedUsers; | public override IReadOnlyCollection<IUser> MentionedUsers => _mentionedUsers; | ||||
| internal RestUserMessage(DiscordClient discord, ulong id, ulong channelId) | |||||
| internal RestUserMessage(BaseDiscordClient discord, ulong id, ulong channelId) | |||||
| : base(discord, id, channelId) | : base(discord, id, channelId) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestUserMessage Create(DiscordClient discord, Model model) | |||||
| internal new static RestUserMessage Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestUserMessage(discord, model.Id, model.ChannelId); | var entity = new RestUserMessage(discord, model.Id, model.ChannelId); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -17,11 +17,11 @@ namespace Discord.Rest | |||||
| public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); | public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); | ||||
| internal RestApplication(DiscordClient discord, ulong id) | |||||
| internal RestApplication(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestApplication Create(DiscordClient discord, Model model) | |||||
| internal static RestApplication Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestApplication(discord, model.Id); | var entity = new RestApplication(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -5,10 +5,10 @@ namespace Discord.Rest | |||||
| public abstract class RestEntity<T> : IEntity<T> | public abstract class RestEntity<T> : IEntity<T> | ||||
| where T : IEquatable<T> | where T : IEquatable<T> | ||||
| { | { | ||||
| public DiscordClient Discord { get; } | |||||
| public BaseDiscordClient Discord { get; } | |||||
| public T Id { get; } | public T Id { get; } | ||||
| internal RestEntity(DiscordClient discord, T id) | |||||
| internal RestEntity(BaseDiscordClient discord, T id) | |||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| Id = id; | Id = id; | ||||
| @@ -21,11 +21,11 @@ namespace Discord.Rest | |||||
| public bool IsEveryone => Id == Guild.Id; | public bool IsEveryone => Id == Guild.Id; | ||||
| public string Mention => MentionUtils.MentionRole(Id); | public string Mention => MentionUtils.MentionRole(Id); | ||||
| internal RestRole(DiscordClient discord, ulong id) | |||||
| internal RestRole(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestRole Create(DiscordClient discord, Model model) | |||||
| internal static RestRole Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestRole(discord, model.Id); | var entity = new RestRole(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -7,11 +7,11 @@ namespace Discord.Rest | |||||
| internal static class RoleHelper | internal static class RoleHelper | ||||
| { | { | ||||
| //General | //General | ||||
| public static async Task DeleteAsync(IRole role, DiscordClient client) | |||||
| public static async Task DeleteAsync(IRole role, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id).ConfigureAwait(false); | await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task ModifyAsync(IRole role, DiscordClient client, | |||||
| public static async Task ModifyAsync(IRole role, BaseDiscordClient client, | |||||
| Action<ModifyGuildRoleParams> func) | Action<ModifyGuildRoleParams> func) | ||||
| { | { | ||||
| var args = new ModifyGuildRoleParams(); | var args = new ModifyGuildRoleParams(); | ||||
| @@ -6,11 +6,11 @@ namespace Discord.Rest | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestGroupUser : RestUser, IGroupUser | public class RestGroupUser : RestUser, IGroupUser | ||||
| { | { | ||||
| internal RestGroupUser(DiscordClient discord, ulong id) | |||||
| internal RestGroupUser(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestGroupUser Create(DiscordClient discord, Model model) | |||||
| internal new static RestGroupUser Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestGroupUser(discord, model.Id); | var entity = new RestGroupUser(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -21,11 +21,11 @@ namespace Discord.Rest | |||||
| public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); | public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); | ||||
| internal RestGuildUser(DiscordClient discord, ulong id) | |||||
| internal RestGuildUser(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestGuildUser Create(DiscordClient discord, Model model) | |||||
| internal static RestGuildUser Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestGuildUser(discord, model.User.Id); | var entity = new RestGuildUser(discord, model.User.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -13,11 +13,11 @@ namespace Discord.Rest | |||||
| public bool IsVerified { get; private set; } | public bool IsVerified { get; private set; } | ||||
| public bool IsMfaEnabled { get; private set; } | public bool IsMfaEnabled { get; private set; } | ||||
| internal RestSelfUser(DiscordClient discord, ulong id) | |||||
| internal RestSelfUser(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal new static RestSelfUser Create(DiscordClient discord, Model model) | |||||
| internal new static RestSelfUser Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestSelfUser(discord, model.Id); | var entity = new RestSelfUser(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -18,11 +18,11 @@ namespace Discord.Rest | |||||
| public virtual Game? Game => null; | public virtual Game? Game => null; | ||||
| public virtual UserStatus Status => UserStatus.Unknown; | public virtual UserStatus Status => UserStatus.Unknown; | ||||
| internal RestUser(DiscordClient discord, ulong id) | |||||
| internal RestUser(BaseDiscordClient discord, ulong id) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| internal static RestUser Create(DiscordClient discord, Model model) | |||||
| internal static RestUser Create(BaseDiscordClient discord, Model model) | |||||
| { | { | ||||
| var entity = new RestUser(discord, model.Id); | var entity = new RestUser(discord, model.Id); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -8,22 +8,22 @@ namespace Discord.Rest | |||||
| { | { | ||||
| internal static class UserHelper | internal static class UserHelper | ||||
| { | { | ||||
| public static async Task<Model> GetAsync(IUser user, DiscordClient client) | |||||
| public static async Task<Model> GetAsync(IUser user, BaseDiscordClient client) | |||||
| { | { | ||||
| return await client.ApiClient.GetUserAsync(user.Id); | return await client.ApiClient.GetUserAsync(user.Id); | ||||
| } | } | ||||
| public static async Task<Model> GetAsync(ISelfUser user, DiscordClient client) | |||||
| public static async Task<Model> GetAsync(ISelfUser user, BaseDiscordClient client) | |||||
| { | { | ||||
| var model = await client.ApiClient.GetMyUserAsync(); | var model = await client.ApiClient.GetMyUserAsync(); | ||||
| if (model.Id != user.Id) | if (model.Id != user.Id) | ||||
| throw new InvalidOperationException("Unable to update this object using a different token."); | throw new InvalidOperationException("Unable to update this object using a different token."); | ||||
| return model; | return model; | ||||
| } | } | ||||
| public static async Task<MemberModel> GetAsync(IGuildUser user, DiscordClient client) | |||||
| public static async Task<MemberModel> GetAsync(IGuildUser user, BaseDiscordClient client) | |||||
| { | { | ||||
| return await client.ApiClient.GetGuildMemberAsync(user.GuildId, user.Id); | return await client.ApiClient.GetGuildMemberAsync(user.GuildId, user.Id); | ||||
| } | } | ||||
| public static async Task ModifyAsync(ISelfUser user, DiscordClient client, Action<ModifyCurrentUserParams> func) | |||||
| public static async Task ModifyAsync(ISelfUser user, BaseDiscordClient client, Action<ModifyCurrentUserParams> func) | |||||
| { | { | ||||
| if (user.Id != client.CurrentUser.Id) | if (user.Id != client.CurrentUser.Id) | ||||
| throw new InvalidOperationException("Unable to modify this object using a different token."); | throw new InvalidOperationException("Unable to modify this object using a different token."); | ||||
| @@ -32,19 +32,19 @@ namespace Discord.Rest | |||||
| func(args); | func(args); | ||||
| await client.ApiClient.ModifySelfAsync(args); | await client.ApiClient.ModifySelfAsync(args); | ||||
| } | } | ||||
| public static async Task ModifyAsync(IGuildUser user, DiscordClient client, Action<ModifyGuildMemberParams> func) | |||||
| public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action<ModifyGuildMemberParams> func) | |||||
| { | { | ||||
| var args = new ModifyGuildMemberParams(); | var args = new ModifyGuildMemberParams(); | ||||
| func(args); | func(args); | ||||
| await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, args); | await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, args); | ||||
| } | } | ||||
| public static async Task KickAsync(IGuildUser user, DiscordClient client) | |||||
| public static async Task KickAsync(IGuildUser user, BaseDiscordClient client) | |||||
| { | { | ||||
| await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id); | await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id); | ||||
| } | } | ||||
| public static async Task<IDMChannel> CreateDMChannelAsync(IUser user, DiscordClient client) | |||||
| public static async Task<IDMChannel> CreateDMChannelAsync(IUser user, BaseDiscordClient client) | |||||
| { | { | ||||
| var args = new CreateDMChannelParams(user.Id); | var args = new CreateDMChannelParams(user.Id); | ||||
| return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args)); | return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args)); | ||||
| @@ -206,19 +206,16 @@ namespace Discord.API | |||||
| } | } | ||||
| //Core | //Core | ||||
| public Task<TResponse> SendRpcAsync<TResponse>(string cmd, object payload, GlobalBucket bucket = GlobalBucket.GeneralRpc, | |||||
| Optional<string> evt = default(Optional<string>), bool ignoreState = false, RequestOptions options = null) | |||||
| public async Task<TResponse> SendRpcAsync<TResponse>(string cmd, object payload, Optional<string> evt = default(Optional<string>), RequestOptions options = null) | |||||
| where TResponse : class | where TResponse : class | ||||
| => SendRpcAsyncInternal<TResponse>(cmd, payload, BucketGroup.Global, (int)bucket, 0, evt, ignoreState, options); | |||||
| public Task<TResponse> SendRpcAsync<TResponse>(string cmd, object payload, GuildBucket bucket, ulong guildId, | |||||
| Optional<string> evt = default(Optional<string>), bool ignoreState = false, RequestOptions options = null) | |||||
| where TResponse : class | |||||
| => SendRpcAsyncInternal<TResponse>(cmd, payload, BucketGroup.Guild, (int)bucket, guildId, evt, ignoreState, options); | |||||
| private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, BucketGroup group, int bucketId, ulong guildId, | |||||
| Optional<string> evt, bool ignoreState, RequestOptions options) | |||||
| { | |||||
| options.IgnoreState = false; | |||||
| return await SendRpcAsyncInternal<TResponse>(cmd, payload, evt, options).ConfigureAwait(false); | |||||
| } | |||||
| private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, Optional<string> evt, RequestOptions options) | |||||
| where TResponse : class | where TResponse : class | ||||
| { | { | ||||
| if (!ignoreState) | |||||
| if (!options.IgnoreState) | |||||
| CheckState(); | CheckState(); | ||||
| byte[] bytes = null; | byte[] bytes = null; | ||||
| @@ -233,7 +230,7 @@ namespace Discord.API | |||||
| var requestTracker = new RpcRequest<TResponse>(options); | var requestTracker = new RpcRequest<TResponse>(options); | ||||
| _requests[guid] = requestTracker; | _requests[guid] = requestTracker; | ||||
| await _requestQueue.SendAsync(new WebSocketRequest(_webSocketClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false); | |||||
| await _requestQueue.SendAsync(new WebSocketRequest(_webSocketClient, bytes, true, options)).ConfigureAwait(false); | |||||
| await _sentRpcMessageEvent.InvokeAsync(cmd).ConfigureAwait(false); | await _sentRpcMessageEvent.InvokeAsync(cmd).ConfigureAwait(false); | ||||
| return await requestTracker.Promise.Task.ConfigureAwait(false); | return await requestTracker.Promise.Task.ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -241,33 +238,37 @@ namespace Discord.API | |||||
| //Rpc | //Rpc | ||||
| public async Task<AuthenticateResponse> SendAuthenticateAsync(RequestOptions options = null) | public async Task<AuthenticateResponse> SendAuthenticateAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new AuthenticateParams | var msg = new AuthenticateParams | ||||
| { | { | ||||
| AccessToken = _authToken | AccessToken = _authToken | ||||
| }; | }; | ||||
| return await SendRpcAsync<AuthenticateResponse>("AUTHENTICATE", msg, ignoreState: true, options: options).ConfigureAwait(false); | |||||
| options.IgnoreState = true; | |||||
| return await SendRpcAsync<AuthenticateResponse>("AUTHENTICATE", msg, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<AuthorizeResponse> SendAuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) | public async Task<AuthorizeResponse> SendAuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new AuthorizeParams | var msg = new AuthorizeParams | ||||
| { | { | ||||
| ClientId = _clientId, | ClientId = _clientId, | ||||
| Scopes = scopes, | Scopes = scopes, | ||||
| RpcToken = rpcToken != null ? rpcToken : Optional.Create<string>() | RpcToken = rpcToken != null ? rpcToken : Optional.Create<string>() | ||||
| }; | }; | ||||
| if (options == null) | |||||
| options = new RequestOptions(); | |||||
| if (options.Timeout == null) | if (options.Timeout == null) | ||||
| options.Timeout = 60000; //This requires manual input on the user's end, lets give them more time | options.Timeout = 60000; //This requires manual input on the user's end, lets give them more time | ||||
| return await SendRpcAsync<AuthorizeResponse>("AUTHORIZE", msg, ignoreState: true, options: options).ConfigureAwait(false); | |||||
| options.IgnoreState = true; | |||||
| return await SendRpcAsync<AuthorizeResponse>("AUTHORIZE", msg, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<GetGuildsResponse> SendGetGuildsAsync(RequestOptions options = null) | public async Task<GetGuildsResponse> SendGetGuildsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendRpcAsync<GetGuildsResponse>("GET_GUILDS", null, options: options).ConfigureAwait(false); | return await SendRpcAsync<GetGuildsResponse>("GET_GUILDS", null, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<RpcGuild> SendGetGuildAsync(ulong guildId, RequestOptions options = null) | public async Task<RpcGuild> SendGetGuildAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new GetGuildParams | var msg = new GetGuildParams | ||||
| { | { | ||||
| GuildId = guildId | GuildId = guildId | ||||
| @@ -276,6 +277,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task<GetChannelsResponse> SendGetChannelsAsync(ulong guildId, RequestOptions options = null) | public async Task<GetChannelsResponse> SendGetChannelsAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new GetChannelsParams | var msg = new GetChannelsParams | ||||
| { | { | ||||
| GuildId = guildId | GuildId = guildId | ||||
| @@ -284,6 +286,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task<RpcChannel> SendGetChannelAsync(ulong channelId, RequestOptions options = null) | public async Task<RpcChannel> SendGetChannelAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new GetChannelParams | var msg = new GetChannelParams | ||||
| { | { | ||||
| ChannelId = channelId | ChannelId = channelId | ||||
| @@ -293,6 +296,7 @@ namespace Discord.API | |||||
| public async Task<SetLocalVolumeResponse> SendSetLocalVolumeAsync(int volume, RequestOptions options = null) | public async Task<SetLocalVolumeResponse> SendSetLocalVolumeAsync(int volume, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new SetLocalVolumeParams | var msg = new SetLocalVolumeParams | ||||
| { | { | ||||
| Volume = volume | Volume = volume | ||||
| @@ -301,6 +305,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task<RpcChannel> SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null) | public async Task<RpcChannel> SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new SelectVoiceChannelParams | var msg = new SelectVoiceChannelParams | ||||
| { | { | ||||
| ChannelId = channelId | ChannelId = channelId | ||||
| @@ -310,6 +315,7 @@ namespace Discord.API | |||||
| public async Task<SubscriptionResponse> SendChannelSubscribeAsync(string evt, ulong channelId, RequestOptions options = null) | public async Task<SubscriptionResponse> SendChannelSubscribeAsync(string evt, ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new ChannelSubscriptionParams | var msg = new ChannelSubscriptionParams | ||||
| { | { | ||||
| ChannelId = channelId | ChannelId = channelId | ||||
| @@ -318,6 +324,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task<SubscriptionResponse> SendChannelUnsubscribeAsync(string evt, ulong channelId, RequestOptions options = null) | public async Task<SubscriptionResponse> SendChannelUnsubscribeAsync(string evt, ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new ChannelSubscriptionParams | var msg = new ChannelSubscriptionParams | ||||
| { | { | ||||
| ChannelId = channelId | ChannelId = channelId | ||||
| @@ -327,6 +334,7 @@ namespace Discord.API | |||||
| public async Task<SubscriptionResponse> SendGuildSubscribeAsync(string evt, ulong guildId, RequestOptions options = null) | public async Task<SubscriptionResponse> SendGuildSubscribeAsync(string evt, ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new GuildSubscriptionParams | var msg = new GuildSubscriptionParams | ||||
| { | { | ||||
| GuildId = guildId | GuildId = guildId | ||||
| @@ -335,6 +343,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task<SubscriptionResponse> SendGuildUnsubscribeAsync(string evt, ulong guildId, RequestOptions options = null) | public async Task<SubscriptionResponse> SendGuildUnsubscribeAsync(string evt, ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new GuildSubscriptionParams | var msg = new GuildSubscriptionParams | ||||
| { | { | ||||
| GuildId = guildId | GuildId = guildId | ||||
| @@ -11,7 +11,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| public partial class DiscordRpcClient : DiscordClient | |||||
| public partial class DiscordRpcClient : BaseDiscordClient | |||||
| { | { | ||||
| private readonly Logger _rpcLogger; | private readonly Logger _rpcLogger; | ||||
| private readonly JsonSerializer _serializer; | private readonly JsonSerializer _serializer; | ||||
| @@ -22,7 +22,5 @@ namespace Discord.Rpc | |||||
| { | { | ||||
| Name = model.Name; | Name = model.Name; | ||||
| } | } | ||||
| bool IEntity<ulong>.IsAttached => false; | |||||
| }*/ | }*/ | ||||
| } | } | ||||
| @@ -156,8 +156,9 @@ namespace Discord.API | |||||
| } | } | ||||
| //Core | //Core | ||||
| private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload, | |||||
| BucketGroup group, int bucketId, ulong guildId, RequestOptions options) | |||||
| public Task SendGatewayAsync(GatewayOpCode opCode, object payload, RequestOptions options = null) | |||||
| => SendGatewayInternalAsync(opCode, payload, options); | |||||
| private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload, RequestOptions options) | |||||
| { | { | ||||
| CheckState(); | CheckState(); | ||||
| @@ -166,25 +167,19 @@ namespace Discord.API | |||||
| payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload }; | payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload }; | ||||
| if (payload != null) | if (payload != null) | ||||
| bytes = Encoding.UTF8.GetBytes(SerializeJson(payload)); | bytes = Encoding.UTF8.GetBytes(SerializeJson(payload)); | ||||
| await RequestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false); | |||||
| await RequestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options)).ConfigureAwait(false); | |||||
| await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | ||||
| } | } | ||||
| //Gateway | //Gateway | ||||
| public Task SendGatewayAsync(GatewayOpCode opCode, object payload, | |||||
| GlobalBucket bucket = GlobalBucket.GeneralGateway, RequestOptions options = null) | |||||
| => SendGatewayInternalAsync(opCode, payload, BucketGroup.Global, (int)bucket, 0, options); | |||||
| public Task SendGatewayAsync(GatewayOpCode opCode, object payload, | |||||
| GuildBucket bucket, ulong guildId, RequestOptions options = null) | |||||
| => SendGatewayInternalAsync(opCode, payload, BucketGroup.Guild, (int)bucket, guildId, options); | |||||
| public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null) | public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetGatewayResponse>("GET", "gateway", options: options).ConfigureAwait(false); | return await SendAsync<GetGatewayResponse>("GET", "gateway", options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, int shardID = 0, int totalShards = 1, RequestOptions options = null) | public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, int shardID = 0, int totalShards = 1, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var props = new Dictionary<string, string> | var props = new Dictionary<string, string> | ||||
| { | { | ||||
| ["$device"] = "Discord.Net" | ["$device"] = "Discord.Net" | ||||
| @@ -203,6 +198,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) | public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var msg = new ResumeParams() | var msg = new ResumeParams() | ||||
| { | { | ||||
| Token = _authToken, | Token = _authToken, | ||||
| @@ -213,10 +209,12 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null) | public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); | await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null) | public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var args = new StatusUpdateParams | var args = new StatusUpdateParams | ||||
| { | { | ||||
| IdleSince = idleSince, | IdleSince = idleSince, | ||||
| @@ -226,10 +224,12 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null) | public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false); | await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null) | public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var payload = new VoiceStateUpdateParams | var payload = new VoiceStateUpdateParams | ||||
| { | { | ||||
| GuildId = guildId, | GuildId = guildId, | ||||
| @@ -241,6 +241,7 @@ namespace Discord.API | |||||
| } | } | ||||
| public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null) | public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false); | await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| } | } | ||||
| @@ -5,7 +5,7 @@ using System.Linq; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| internal class DataStore | |||||
| internal class ClientState | |||||
| { | { | ||||
| private const int CollectionConcurrencyLevel = 1; //WebSocket updater/event handler. //TODO: Needs profiling, increase to 2? | private const int CollectionConcurrencyLevel = 1; //WebSocket updater/event handler. //TODO: Needs profiling, increase to 2? | ||||
| private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149 | private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149 | ||||
| @@ -29,7 +29,7 @@ namespace Discord.WebSocket | |||||
| _groupChannels.Select(x => GetChannel(x) as IPrivateChannel)) | _groupChannels.Select(x => GetChannel(x) as IPrivateChannel)) | ||||
| .ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count); | .ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count); | ||||
| public DataStore(int guildCount, int dmChannelCount) | |||||
| public ClientState(int guildCount, int dmChannelCount) | |||||
| { | { | ||||
| double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; | double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; | ||||
| double estimatedUsersCount = guildCount * AverageUsersPerGuild; | double estimatedUsersCount = guildCount * AverageUsersPerGuild; | ||||
| @@ -19,7 +19,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| public partial class DiscordSocketClient : DiscordClient, IDiscordClient | |||||
| public partial class DiscordSocketClient : BaseDiscordClient, IDiscordClient | |||||
| { | { | ||||
| private readonly ConcurrentQueue<ulong> _largeGuilds; | private readonly ConcurrentQueue<ulong> _largeGuilds; | ||||
| private readonly Logger _gatewayLogger; | private readonly Logger _gatewayLogger; | ||||
| @@ -49,14 +49,14 @@ namespace Discord.WebSocket | |||||
| internal int MessageCacheSize { get; private set; } | internal int MessageCacheSize { get; private set; } | ||||
| internal int LargeThreshold { get; private set; } | internal int LargeThreshold { get; private set; } | ||||
| internal AudioMode AudioMode { get; private set; } | internal AudioMode AudioMode { get; private set; } | ||||
| internal DataStore DataStore { get; private set; } | |||||
| internal ClientState State { get; private set; } | |||||
| internal int ConnectionTimeout { get; private set; } | internal int ConnectionTimeout { get; private set; } | ||||
| internal WebSocketProvider WebSocketProvider { get; private set; } | internal WebSocketProvider WebSocketProvider { get; private set; } | ||||
| public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; | public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; | ||||
| public new SocketSelfUser CurrentUser => base.CurrentUser as SocketSelfUser; | public new SocketSelfUser CurrentUser => base.CurrentUser as SocketSelfUser; | ||||
| public IReadOnlyCollection<IPrivateChannel> PrivateChannels => DataStore.PrivateChannels; | |||||
| internal IReadOnlyCollection<SocketGuild> Guilds => DataStore.Guilds; | |||||
| public IReadOnlyCollection<IPrivateChannel> PrivateChannels => State.PrivateChannels; | |||||
| internal IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | |||||
| /// <summary> Creates a new REST/WebSocket discord client. </summary> | /// <summary> Creates a new REST/WebSocket discord client. </summary> | ||||
| public DiscordSocketClient() : this(new DiscordSocketConfig()) { } | public DiscordSocketClient() : this(new DiscordSocketConfig()) { } | ||||
| @@ -72,7 +72,7 @@ namespace Discord.WebSocket | |||||
| AudioMode = config.AudioMode; | AudioMode = config.AudioMode; | ||||
| WebSocketProvider = config.WebSocketProvider; | WebSocketProvider = config.WebSocketProvider; | ||||
| ConnectionTimeout = config.ConnectionTimeout; | ConnectionTimeout = config.ConnectionTimeout; | ||||
| DataStore = new DataStore(0, 0); | |||||
| State = new ClientState(0, 0); | |||||
| _nextAudioId = 1; | _nextAudioId = 1; | ||||
| _gatewayLogger = LogManager.CreateLogger("Gateway"); | _gatewayLogger = LogManager.CreateLogger("Gateway"); | ||||
| @@ -111,7 +111,7 @@ namespace Discord.WebSocket | |||||
| protected override async Task OnLoginAsync(TokenType tokenType, string token) | protected override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
| { | { | ||||
| var voiceRegions = await ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); | |||||
| var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true}).ConfigureAwait(false); | |||||
| _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id); | _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id); | ||||
| } | } | ||||
| protected override async Task OnLogoutAsync() | protected override async Task OnLogoutAsync() | ||||
| @@ -242,7 +242,7 @@ namespace Discord.WebSocket | |||||
| while (_largeGuilds.TryDequeue(out guildId)) { } | while (_largeGuilds.TryDequeue(out guildId)) { } | ||||
| //Raise virtual GUILD_UNAVAILABLEs | //Raise virtual GUILD_UNAVAILABLEs | ||||
| foreach (var guild in DataStore.Guilds) | |||||
| foreach (var guild in State.Guilds) | |||||
| { | { | ||||
| if (guild._available) | if (guild._available) | ||||
| await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false); | await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false); | ||||
| @@ -322,7 +322,7 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public SocketGuild GetGuild(ulong id) | public SocketGuild GetGuild(ulong id) | ||||
| { | { | ||||
| return DataStore.GetGuild(id); | |||||
| return State.GetGuild(id); | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<RestGuild> CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) | public Task<RestGuild> CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) | ||||
| @@ -331,7 +331,7 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IChannel GetChannel(ulong id) | public IChannel GetChannel(ulong id) | ||||
| { | { | ||||
| return DataStore.GetChannel(id); | |||||
| return State.GetChannel(id); | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -345,12 +345,12 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IUser GetUser(ulong id) | public IUser GetUser(ulong id) | ||||
| { | { | ||||
| return DataStore.GetUser(id); | |||||
| return State.GetUser(id); | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IUser GetUser(string username, string discriminator) | public IUser GetUser(string username, string discriminator) | ||||
| { | { | ||||
| return DataStore.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault(); | |||||
| return State.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault(); | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -486,7 +486,7 @@ namespace Discord.WebSocket | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); | var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); | ||||
| var dataStore = new DataStore(data.Guilds.Length, data.PrivateChannels.Length); | |||||
| var dataStore = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); | |||||
| var currentUser = SocketSelfUser.Create(this, data.User); | var currentUser = SocketSelfUser.Create(this, data.User); | ||||
| int unavailableGuilds = 0; | int unavailableGuilds = 0; | ||||
| @@ -505,7 +505,7 @@ namespace Discord.WebSocket | |||||
| _sessionId = data.SessionId; | _sessionId = data.SessionId; | ||||
| base.CurrentUser = currentUser; | base.CurrentUser = currentUser; | ||||
| _unavailableGuilds = unavailableGuilds; | _unavailableGuilds = unavailableGuilds; | ||||
| DataStore = dataStore; | |||||
| State = dataStore; | |||||
| } | } | ||||
| catch (Exception ex) | catch (Exception ex) | ||||
| { | { | ||||
| @@ -15,6 +15,7 @@ namespace Discord.WebSocket | |||||
| internal SocketSelfUser(DiscordSocketClient discord, ulong id) | internal SocketSelfUser(DiscordSocketClient discord, ulong id) | ||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| Status = UserStatus.Online; | |||||
| } | } | ||||
| internal new static SocketSelfUser Create(DiscordSocketClient discord, Model model) | internal new static SocketSelfUser Create(DiscordSocketClient discord, Model model) | ||||
| { | { | ||||
| @@ -15,7 +15,7 @@ namespace Discord.WebSocket | |||||
| public string Discriminator => DiscriminatorValue.ToString("D4"); | public string Discriminator => DiscriminatorValue.ToString("D4"); | ||||
| public string Mention => MentionUtils.MentionUser(Id); | public string Mention => MentionUtils.MentionUser(Id); | ||||
| public virtual Game? Game => null; | public virtual Game? Game => null; | ||||
| public virtual UserStatus Status => UserStatus.Unknown; | |||||
| public virtual UserStatus Status { get; internal set; } | |||||
| internal SocketUser(DiscordSocketClient discord, ulong id) | internal SocketUser(DiscordSocketClient discord, ulong id) | ||||
| : base(discord, id) | : base(discord, id) | ||||