diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 3b829ee17..55e9e13dc 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -173,10 +173,12 @@ namespace Discord.API private async Task LogoutInternalAsync() { //An exception here will lock the client into the unusable LoggingOut state, but that's probably fine since our client is in an undefined state too. - if (LoginState == LoginState.LoggedOut) return; + if (LoginState == LoginState.LoggedOut) + return; LoginState = LoginState.LoggingOut; - try { _loginCancelToken?.Cancel(false); } + try + { _loginCancelToken?.Cancel(false); } catch { } await DisconnectInternalAsync(null).ConfigureAwait(false); @@ -398,7 +400,7 @@ namespace Discord.API Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); - if(args.Name.IsSpecified) + if (args.Name.IsSpecified) Preconditions.AtMost(args.Name.Value.Length, 100, nameof(args.Name)); options = RequestOptions.CreateOrClone(options); @@ -414,9 +416,9 @@ namespace Discord.API Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); - if(args.Name.IsSpecified) + if (args.Name.IsSpecified) Preconditions.AtMost(args.Name.Value.Length, 100, nameof(args.Name)); - if(args.Topic.IsSpecified) + if (args.Topic.IsSpecified) Preconditions.AtMost(args.Topic.Value.Length, 1024, nameof(args.Name)); Preconditions.AtLeast(args.SlowModeInterval, 0, nameof(args.SlowModeInterval)); @@ -798,9 +800,11 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendJsonAsync("POST", () => $"channels/{channelId}/messages", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); } + + /// Message content is too long, length must be less or equal to . /// This operation may only be called with a token. - public async Task CreateWebhookMessageAsync(ulong webhookId, CreateWebhookMessageParams args, RequestOptions options = null) + public async Task CreateWebhookMessageAsync(ulong webhookId, CreateWebhookMessageParams args, RequestOptions options = null, ulong? threadId = null) { if (AuthTokenType != TokenType.Webhook) throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); @@ -816,12 +820,12 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); var ids = new BucketIds(webhookId: webhookId); - return await SendJsonAsync("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); + return await SendJsonAsync("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); } /// Message content is too long, length must be less or equal to . /// This operation may only be called with a token. - public async Task ModifyWebhookMessageAsync(ulong webhookId, ulong messageId, ModifyWebhookMessageParams args, RequestOptions options = null) + public async Task ModifyWebhookMessageAsync(ulong webhookId, ulong messageId, ModifyWebhookMessageParams args, RequestOptions options = null, ulong? threadId = null) { if (AuthTokenType != TokenType.Webhook) throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); @@ -837,11 +841,11 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); var ids = new BucketIds(webhookId: webhookId); - await SendJsonAsync("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); + await SendJsonAsync("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}${WebhookQuery(false, threadId)}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); } /// This operation may only be called with a token. - public async Task DeleteWebhookMessageAsync(ulong webhookId, ulong messageId, RequestOptions options = null) + public async Task DeleteWebhookMessageAsync(ulong webhookId, ulong messageId, RequestOptions options = null, ulong? threadId = null) { if (AuthTokenType != TokenType.Webhook) throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); @@ -852,7 +856,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); var ids = new BucketIds(webhookId: webhookId); - await SendAsync("DELETE", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", ids, options: options).ConfigureAwait(false); + await SendAsync("DELETE", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}?{WebhookQuery(false, threadId)}", ids, options: options).ConfigureAwait(false); } /// Message content is too long, length must be less or equal to . @@ -873,7 +877,7 @@ namespace Discord.API /// Message content is too long, length must be less or equal to . /// This operation may only be called with a token. - public async Task UploadWebhookFileAsync(ulong webhookId, UploadWebhookFileParams args, RequestOptions options = null) + public async Task UploadWebhookFileAsync(ulong webhookId, UploadWebhookFileParams args, RequestOptions options = null, ulong? threadId = null) { if (AuthTokenType != TokenType.Webhook) throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); @@ -893,7 +897,7 @@ namespace Discord.API } var ids = new BucketIds(webhookId: webhookId); - return await SendMultipartAsync("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); + return await SendMultipartAsync("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); } public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) { @@ -1380,7 +1384,7 @@ namespace Discord.API if ((!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) && !args.File.IsSpecified) Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); - if(args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) + if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content)); options = RequestOptions.CreateOrClone(options); @@ -1400,7 +1404,7 @@ namespace Discord.API throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content)); options = RequestOptions.CreateOrClone(options); - + var ids = new BucketIds(); return await SendMultipartAsync("POST", () => $"webhooks/{CurrentApplicationId}/{token}?wait=true", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); } @@ -1729,8 +1733,10 @@ namespace Discord.API if (args.TargetType.IsSpecified) { Preconditions.NotEqual((int)args.TargetType.Value, (int)TargetUserType.Undefined, nameof(args.TargetType)); - if (args.TargetType.Value == TargetUserType.Stream) Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId)); - if (args.TargetType.Value == TargetUserType.EmbeddedApplication) Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetUserId)); + if (args.TargetType.Value == TargetUserType.Stream) + Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId)); + if (args.TargetType.Value == TargetUserType.EmbeddedApplication) + Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetUserId)); } options = RequestOptions.CreateOrClone(options); @@ -2414,6 +2420,18 @@ namespace Discord.API return (expr as MemberExpression).Member.Name; } + + private static string WebhookQuery(bool wait = false, ulong? threadId = null) + { + List querys = new List() { }; + if (wait) + querys.Add("wait=true"); + if (threadId.HasValue) + querys.Add($"thread_id={threadId}"); + + return $"{string.Join("&", querys)}"; + } + #endregion } } diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index 405100f89..556338956 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -88,8 +88,8 @@ namespace Discord.Webhook /// Returns the ID of the created message. public Task SendMessageAsync(string text = null, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null, - MessageComponent components = null, MessageFlags flags = MessageFlags.None) - => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, components, flags); + MessageComponent components = null, MessageFlags flags = MessageFlags.None, ulong? threadId = null) + => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, components, flags, threadId); /// /// Modifies a message posted using this webhook. @@ -103,8 +103,8 @@ namespace Discord.Webhook /// /// A task that represents the asynchronous modification operation. /// - public Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null) - => WebhookClientHelper.ModifyMessageAsync(this, messageId, func, options); + public Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null, ulong? threadId = null) + => WebhookClientHelper.ModifyMessageAsync(this, messageId, func, options, threadId); /// /// Deletes a message posted using this webhook. @@ -117,43 +117,43 @@ namespace Discord.Webhook /// /// A task that represents the asynchronous deletion operation. /// - public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) - => WebhookClientHelper.DeleteMessageAsync(this, messageId, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null, ulong ? threadId = null) + => WebhookClientHelper.DeleteMessageAsync(this, messageId, options, threadId); /// Sends a message to the channel for this webhook with an attachment. /// Returns the ID of the created message. public Task SendFileAsync(string filePath, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, - MessageComponent components = null, MessageFlags flags = MessageFlags.None) + MessageComponent components = null, MessageFlags flags = MessageFlags.None, ulong? threadId = null) => WebhookClientHelper.SendFileAsync(this, filePath, text, isTTS, embeds, username, avatarUrl, - allowedMentions, options, isSpoiler, components, flags); + allowedMentions, options, isSpoiler, components, flags, threadId); /// Sends a message to the channel for this webhook with an attachment. /// Returns the ID of the created message. public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, - MessageComponent components = null, MessageFlags flags = MessageFlags.None) + MessageComponent components = null, MessageFlags flags = MessageFlags.None, ulong? threadId = null) => WebhookClientHelper.SendFileAsync(this, stream, filename, text, isTTS, embeds, username, - avatarUrl, allowedMentions, options, isSpoiler, components, flags); + avatarUrl, allowedMentions, options, isSpoiler, components, flags, threadId); /// Sends a message to the channel for this webhook with an attachment. /// Returns the ID of the created message. public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageComponent components = null, - MessageFlags flags = MessageFlags.None) + MessageFlags flags = MessageFlags.None, ulong? threadId = null) => WebhookClientHelper.SendFileAsync(this, attachment, text, isTTS, embeds, username, - avatarUrl, allowedMentions, components, options, flags); + avatarUrl, allowedMentions, components, options, flags, threadId); /// Sends a message to the channel for this webhook with an attachment. /// Returns the ID of the created message. public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageComponent components = null, - MessageFlags flags = MessageFlags.None) + MessageFlags flags = MessageFlags.None, ulong? threadId = null) => WebhookClientHelper.SendFilesAsync(this, attachments, text, isTTS, embeds, username, avatarUrl, - allowedMentions, components, options, flags); + allowedMentions, components, options, flags, threadId); /// Modifies the properties of this webhook. diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs index 0a974a9d9..8ad74e7e7 100644 --- a/src/Discord.Net.Webhook/WebhookClientHelper.cs +++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs @@ -21,8 +21,8 @@ namespace Discord.Webhook return RestInternalWebhook.Create(client, model); } public static async Task SendMessageAsync(DiscordWebhookClient client, - string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, - AllowedMentions allowedMentions, RequestOptions options, MessageComponent components, MessageFlags flags) + string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, + AllowedMentions allowedMentions, RequestOptions options, MessageComponent components, MessageFlags flags, ulong? threadId = null) { var args = new CreateWebhookMessageParams { @@ -44,12 +44,13 @@ namespace Discord.Webhook if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds) throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds and none.", nameof(flags)); - - var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false); + + var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options, threadId: threadId).ConfigureAwait(false); return model.Id; } + public static async Task ModifyMessageAsync(DiscordWebhookClient client, ulong messageId, - Action func, RequestOptions options) + Action func, RequestOptions options, ulong? threadId) { var args = new WebhookMessageProperties(); func(args); @@ -94,35 +95,35 @@ namespace Discord.Webhook Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified, }; - await client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options) + await client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options, threadId) .ConfigureAwait(false); } - public static async Task DeleteMessageAsync(DiscordWebhookClient client, ulong messageId, RequestOptions options) + public static async Task DeleteMessageAsync(DiscordWebhookClient client, ulong messageId, RequestOptions options, ulong? threadId) { - await client.ApiClient.DeleteWebhookMessageAsync(client.Webhook.Id, messageId, options).ConfigureAwait(false); + await client.ApiClient.DeleteWebhookMessageAsync(client.Webhook.Id, messageId, options, threadId).ConfigureAwait(false); } public static async Task SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, - bool isSpoiler, MessageComponent components, MessageFlags flags = MessageFlags.None) + bool isSpoiler, MessageComponent components, MessageFlags flags = MessageFlags.None, ulong? threadId = null) { string filename = Path.GetFileName(filePath); using (var file = File.OpenRead(filePath)) - return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, isSpoiler, components, flags).ConfigureAwait(false); + return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, isSpoiler, components, flags, threadId).ConfigureAwait(false); } public static Task SendFileAsync(DiscordWebhookClient client, Stream stream, string filename, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, bool isSpoiler, - MessageComponent components, MessageFlags flags) - => SendFileAsync(client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags); + MessageComponent components, MessageFlags flags, ulong? threadId) + => SendFileAsync(client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags, threadId); public static Task SendFileAsync(DiscordWebhookClient client, FileAttachment attachment, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, - MessageComponent components, RequestOptions options, MessageFlags flags) - => SendFilesAsync(client, new FileAttachment[] { attachment }, text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags); + MessageComponent components, RequestOptions options, MessageFlags flags, ulong? threadId) + => SendFilesAsync(client, new FileAttachment[] { attachment }, text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags, threadId); public static async Task SendFilesAsync(DiscordWebhookClient client, IEnumerable attachments, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, MessageComponent components, RequestOptions options, - MessageFlags flags) + MessageFlags flags, ulong? threadId) { embeds ??= Array.Empty(); @@ -164,7 +165,7 @@ namespace Discord.Webhook MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, Flags = flags }; - var msg = await client.ApiClient.UploadWebhookFileAsync(client.Webhook.Id, args, options).ConfigureAwait(false); + var msg = await client.ApiClient.UploadWebhookFileAsync(client.Webhook.Id, args, options, threadId).ConfigureAwait(false); return msg.Id; }