* feature: Webhook message edit & delete functionality * PR fixes: Rename Edit* to Modify*; Add more detailed docstrings; Small validation fixes * Fix spacing around docstrings * Make ModifyWebhookMessageParams.Content Optional<string> * Change the Webhook message edit functionality to use a object delegate method instead providing the all parameters Co-authored-by: Desmont <desmont@users.noreply.github.com>tags/2.4.0
| @@ -0,0 +1,16 @@ | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Rest | |||||
| { | |||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| internal class ModifyWebhookMessageParams | |||||
| { | |||||
| [JsonProperty("content")] | |||||
| public Optional<string> Content { get; set; } | |||||
| [JsonProperty("embeds")] | |||||
| public Optional<Embed[]> Embeds { get; set; } | |||||
| [JsonProperty("allowed_mentions")] | |||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -523,6 +523,43 @@ namespace Discord.API | |||||
| var ids = new BucketIds(webhookId: webhookId); | var ids = new BucketIds(webhookId: webhookId); | ||||
| return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> | |||||
| public async Task ModifyWebhookMessageAsync(ulong webhookId, ulong messageId, ModifyWebhookMessageParams args, RequestOptions options = null) | |||||
| { | |||||
| if (AuthTokenType != TokenType.Webhook) | |||||
| throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); | |||||
| Preconditions.NotNull(args, nameof(args)); | |||||
| Preconditions.NotEqual(webhookId, 0, nameof(webhookId)); | |||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
| if (args.Embeds.IsSpecified) | |||||
| Preconditions.AtMost(args.Embeds.Value.Length, 10, nameof(args.Embeds), "A max of 10 Embeds are allowed."); | |||||
| 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); | |||||
| var ids = new BucketIds(webhookId: webhookId); | |||||
| await SendJsonAsync<Message>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | |||||
| } | |||||
| /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> | |||||
| public async Task DeleteWebhookMessageAsync(ulong webhookId, ulong messageId, RequestOptions options = null) | |||||
| { | |||||
| if (AuthTokenType != TokenType.Webhook) | |||||
| throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); | |||||
| Preconditions.NotEqual(webhookId, 0, nameof(webhookId)); | |||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(webhookId: webhookId); | |||||
| await SendAsync("DELETE", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -91,6 +91,35 @@ namespace Discord.Webhook | |||||
| string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null) | string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null) | ||||
| => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options); | => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options); | ||||
| /// <summary> | |||||
| /// Modifies a message posted using this webhook. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method can only modify messages that were sent using the same webhook. | |||||
| /// </remarks> | |||||
| /// <param name="messageId">ID of the modified message.</param> | |||||
| /// <param name="func">A delegate containing the properties to modify the message with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous modification operation. | |||||
| /// </returns> | |||||
| public Task ModifyMessageAsync(ulong messageId, Action<WebhookMessageProperties> func, RequestOptions options = null) | |||||
| => WebhookClientHelper.ModifyMessageAsync(this, messageId, func, options); | |||||
| /// <summary> | |||||
| /// Deletes a message posted using this webhook. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method can only delete messages that were sent using the same webhook. | |||||
| /// </remarks> | |||||
| /// <param name="messageId">ID of the deleted message.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous deletion operation. | |||||
| /// </returns> | |||||
| public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | |||||
| => WebhookClientHelper.DeleteMessageAsync(this, messageId, options); | |||||
| /// <summary> Sends a message to the channel for this webhook with an attachment. </summary> | /// <summary> Sends a message to the channel for this webhook with an attachment. </summary> | ||||
| /// <returns> Returns the ID of the created message. </returns> | /// <returns> Returns the ID of the created message. </returns> | ||||
| public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false, | public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false, | ||||
| @@ -0,0 +1,26 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord.Webhook | |||||
| { | |||||
| /// <summary> | |||||
| /// Properties that are used to modify an Webhook message with the specified changes. | |||||
| /// </summary> | |||||
| public class WebhookMessageProperties | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets or sets the content of the message. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This must be less than the constant defined by <see cref="DiscordConfig.MaxMessageSize"/>. | |||||
| /// </remarks> | |||||
| public Optional<string> Content { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the embed array that the message should display. | |||||
| /// </summary> | |||||
| public Optional<IEnumerable<Embed>> Embeds { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the allowed mentions of the message. | |||||
| /// </summary> | |||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -36,7 +36,59 @@ namespace Discord.Webhook | |||||
| 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).ConfigureAwait(false); | ||||
| return model.Id; | return model.Id; | ||||
| } | } | ||||
| public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS, | |||||
| public static async Task ModifyMessageAsync(DiscordWebhookClient client, ulong messageId, | |||||
| Action<WebhookMessageProperties> func, RequestOptions options) | |||||
| { | |||||
| var args = new WebhookMessageProperties(); | |||||
| func(args); | |||||
| if (args.AllowedMentions.IsSpecified) | |||||
| { | |||||
| var allowedMentions = args.AllowedMentions.Value; | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), | |||||
| "A max of 100 role Ids are allowed."); | |||||
| Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), | |||||
| "A max of 100 user Ids are allowed."); | |||||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||||
| if (allowedMentions?.AllowedTypes != null) | |||||
| { | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) && | |||||
| allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", | |||||
| nameof(allowedMentions)); | |||||
| } | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) && | |||||
| allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", | |||||
| nameof(allowedMentions)); | |||||
| } | |||||
| } | |||||
| } | |||||
| var apiArgs = new ModifyWebhookMessageParams | |||||
| { | |||||
| Content = args.Content.IsSpecified ? args.Content.Value : Optional.Create<string>(), | |||||
| Embeds = | |||||
| args.Embeds.IsSpecified | |||||
| ? args.Embeds.Value.Select(embed => embed.ToModel()).ToArray() | |||||
| : Optional.Create<API.Embed[]>(), | |||||
| AllowedMentions = args.AllowedMentions.IsSpecified | |||||
| ? args.AllowedMentions.Value.ToModel() | |||||
| : Optional.Create<API.AllowedMentions>() | |||||
| }; | |||||
| await client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options) | |||||
| .ConfigureAwait(false); | |||||
| } | |||||
| public static async Task DeleteMessageAsync(DiscordWebhookClient client, ulong messageId, RequestOptions options) | |||||
| { | |||||
| await client.ApiClient.DeleteWebhookMessageAsync(client.Webhook.Id, messageId, options).ConfigureAwait(false); | |||||
| } | |||||
| public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS, | |||||
| IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, bool isSpoiler) | IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, bool isSpoiler) | ||||
| { | { | ||||
| string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||