* Refactor Interactions * Remove ApplicationCommandExceptionpull/1958/head
| @@ -3,7 +3,7 @@ namespace Discord | |||
| /// <summary> | |||
| /// Represents a Message Command interaction. | |||
| /// </summary> | |||
| public interface IMessageCommandInteraction : IDiscordInteraction | |||
| public interface IMessageCommandInteraction : IApplicationCommandInteraction | |||
| { | |||
| /// <summary> | |||
| /// Gets the data associated with this interaction. | |||
| @@ -3,7 +3,7 @@ namespace Discord | |||
| /// <summary> | |||
| /// Represents a User Command interaction. | |||
| /// </summary> | |||
| public interface IUserCommandInteraction : IDiscordInteraction | |||
| public interface IUserCommandInteraction : IApplicationCommandInteraction | |||
| { | |||
| /// <summary> | |||
| /// Gets the data associated with this interaction. | |||
| @@ -0,0 +1,19 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| /// <summary> | |||
| /// Represents an application command interaction. | |||
| /// </summary> | |||
| public interface IApplicationCommandInteraction : IDiscordInteraction | |||
| { | |||
| /// <summary> | |||
| /// Gets the data of the application command interaction | |||
| /// </summary> | |||
| new IApplicationCommandInteractionData Data { get; } | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.IO; | |||
| using System.Threading.Tasks; | |||
| @@ -34,6 +35,11 @@ namespace Discord | |||
| /// </summary> | |||
| int Version { get; } | |||
| /// <summary> | |||
| /// Gets the user who invoked the interaction. | |||
| /// </summary> | |||
| IUser User { get; } | |||
| /// <summary> | |||
| /// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>. | |||
| /// </summary> | |||
| @@ -43,10 +49,14 @@ namespace Discord | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, | |||
| bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| Task<IUserMessage> RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, | |||
| bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| @@ -57,13 +67,14 @@ namespace Discord | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| @@ -76,13 +87,14 @@ namespace Discord | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| @@ -95,13 +107,50 @@ namespace Discord | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="attachment">The attachment containing the file and description.</param> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="attachments">A collection of attachments to upload.</param> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public Task<IUserMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| Task<IUserMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Gets the original response for this interaction. | |||
| @@ -115,14 +164,17 @@ namespace Discord | |||
| /// </summary> | |||
| /// <param name="func">A delegate containing the properties to modify the message with.</param> | |||
| /// <param name="options">The request options for this <see langword="async"/> request.</param> | |||
| /// <returns>A <see cref="IUserMessage"/> that represents the initial response.</returns> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous modification operation. The task result | |||
| /// contains the updated message. | |||
| /// </returns> | |||
| Task<IUserMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Acknowledges this interaction. | |||
| /// </summary> | |||
| /// <returns> | |||
| /// A task that represents the asynchronous operation of acknowledging the interaction. | |||
| /// A task that represents the asynchronous operation of deferring the interaction. | |||
| /// </returns> | |||
| Task DeferAsync(bool ephemeral = false, RequestOptions options = null); | |||
| } | |||
| @@ -3,7 +3,7 @@ namespace Discord | |||
| /// <summary> | |||
| /// Represents a slash command interaction. | |||
| /// </summary> | |||
| public interface ISlashCommandInteraction : IDiscordInteraction | |||
| public interface ISlashCommandInteraction : IApplicationCommandInteraction | |||
| { | |||
| /// <summary> | |||
| /// Gets the data associated with this interaction. | |||
| @@ -1,15 +0,0 @@ | |||
| using System; | |||
| using System.Linq; | |||
| namespace Discord.Net | |||
| { | |||
| [Obsolete("Please use HttpException instead of this. Will be removed in next major version.", false)] | |||
| public class ApplicationCommandException : HttpException | |||
| { | |||
| public ApplicationCommandException(HttpException httpError) | |||
| : base(httpError.HttpCode, httpError.Request, httpError.DiscordCode, httpError.Reason, httpError.Errors.ToArray()) | |||
| { | |||
| } | |||
| } | |||
| } | |||
| @@ -36,12 +36,12 @@ namespace Discord.Interactions | |||
| /// <inheritdoc cref="IDiscordInteraction.RespondAsync(string, Embed[], bool, bool, AllowedMentions, RequestOptions, MessageComponent, Embed)"/> | |||
| protected virtual async Task RespondAsync (string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => | |||
| await Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| await Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc cref="IDiscordInteraction.FollowupAsync(string, Embed[], bool, bool, AllowedMentions, RequestOptions, MessageComponent, Embed)"/> | |||
| protected virtual async Task<IUserMessage> FollowupAsync (string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => | |||
| await Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| await Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc cref="IMessageChannel.SendMessageAsync(string, bool, Embed, RequestOptions, AllowedMentions, MessageReference, MessageComponent, ISticker[], Embed[])"/> | |||
| protected virtual async Task<IUserMessage> ReplyAsync (string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, | |||
| @@ -53,7 +53,7 @@ namespace Discord.Interactions | |||
| if (Context.Interaction is not RestInteraction restInteraction) | |||
| throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method"); | |||
| await InteractionService._restResponseCallback(Context, restInteraction.Respond(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed)).ConfigureAwait(false); | |||
| await InteractionService._restResponseCallback(Context, restInteraction.Respond(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options)).ConfigureAwait(false); | |||
| } | |||
| } | |||
| } | |||
| @@ -21,6 +21,7 @@ namespace Discord.API.Rest | |||
| public Optional<Embed[]> Embeds { get; set; } | |||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | |||
| public Optional<ActionRowComponent[]> MessageComponents { get; set; } | |||
| public Optional<MessageFlags> Flags { get; set; } | |||
| public UploadWebhookFileParams(params FileAttachment[] files) | |||
| { | |||
| @@ -48,6 +49,8 @@ namespace Discord.API.Rest | |||
| payload["embeds"] = Embeds.Value; | |||
| if (AllowedMentions.IsSpecified) | |||
| payload["allowed_mentions"] = AllowedMentions.Value; | |||
| if (Flags.IsSpecified) | |||
| payload["flags"] = Flags.Value; | |||
| List<object> attachments = new(); | |||
| @@ -1198,25 +1198,25 @@ namespace Discord.API | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{CurrentUserId}/commands", command, new BucketIds(), options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{CurrentUserId}/commands", command, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<ApplicationCommand> ModifyGlobalApplicationCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<ApplicationCommand> ModifyGlobalApplicationUserCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<ApplicationCommand> ModifyGlobalApplicationMessageCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task DeleteGlobalApplicationCommandAsync(ulong commandId, RequestOptions options = null) | |||
| { | |||
| @@ -1229,7 +1229,7 @@ namespace Discord.API | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{CurrentUserId}/commands", commands, new BucketIds(), options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{CurrentUserId}/commands", commands, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<ApplicationCommand[]> GetGuildApplicationCommandsAsync(ulong guildId, RequestOptions options = null) | |||
| @@ -1271,7 +1271,7 @@ namespace Discord.API | |||
| var bucket = new BucketIds(guildId: guildId); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<ApplicationCommand> ModifyGuildApplicationCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null) | |||
| { | |||
| @@ -1279,7 +1279,7 @@ namespace Discord.API | |||
| var bucket = new BucketIds(guildId: guildId); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId, RequestOptions options = null) | |||
| { | |||
| @@ -1296,7 +1296,7 @@ namespace Discord.API | |||
| var bucket = new BucketIds(guildId: guildId); | |||
| return await TrySendApplicationCommandAsync(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options)).ConfigureAwait(false); | |||
| return await SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| @@ -1316,7 +1316,7 @@ namespace Discord.API | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await SendAsync<Message>("GET", () => $"webhooks/{CurrentUserId}/{interactionToken}/messages/@original", new BucketIds(), options: options).ConfigureAwait(false); | |||
| return await NullifyNotFound(SendAsync<Message>("GET", () => $"webhooks/{CurrentUserId}/{interactionToken}/messages/@original", new BucketIds(), options: options)).ConfigureAwait(false); | |||
| } | |||
| public async Task<Message> ModifyInteractionResponseAsync(ModifyInteractionResponseParams args, string interactionToken, RequestOptions options = null) | |||
| { | |||
| @@ -1347,6 +1347,21 @@ namespace Discord.API | |||
| return await SendMultipartAsync<Message>("POST", () => $"webhooks/{CurrentUserId}/{token}?wait=true", args.ToDictionary(), new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<Message> CreateInteractionFollowupMessageAsync(UploadWebhookFileParams args, string token, RequestOptions options = null) | |||
| { | |||
| if ((!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) && !args.Files.Any()) | |||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||
| 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(); | |||
| return await SendMultipartAsync<Message>("POST", () => $"webhooks/{CurrentUserId}/{token}?wait=true", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task<Message> ModifyInteractionFollowupMessageAsync(ModifyInteractionResponseParams args, ulong id, string token, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotNull(args, nameof(args)); | |||
| @@ -2222,40 +2237,6 @@ namespace Discord.API | |||
| return _serializer.Deserialize<T>(reader); | |||
| } | |||
| protected async Task<T> TrySendApplicationCommandAsync<T>(Task<T> sendTask) | |||
| { | |||
| try | |||
| { | |||
| var result = await sendTask.ConfigureAwait(false); | |||
| if (sendTask.Exception != null) | |||
| { | |||
| if (sendTask.Exception.InnerException is HttpException x) | |||
| { | |||
| if (x.HttpCode == HttpStatusCode.BadRequest) | |||
| { | |||
| var json = (x.Request as JsonRestRequest).Json; | |||
| throw new ApplicationCommandException(x); | |||
| } | |||
| } | |||
| throw sendTask.Exception; | |||
| } | |||
| else | |||
| return result; | |||
| } | |||
| catch (HttpException x) | |||
| { | |||
| if (x.HttpCode == HttpStatusCode.BadRequest) | |||
| { | |||
| var json = (x.Request as JsonRestRequest).Json; | |||
| throw new ApplicationCommandException(x); | |||
| } | |||
| throw; | |||
| } | |||
| } | |||
| protected async Task<T> NullifyNotFound<T>(Task<T> sendTask) where T : class | |||
| { | |||
| try | |||
| @@ -76,9 +76,9 @@ namespace Discord.Rest | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -132,37 +132,29 @@ namespace Discord.Rest | |||
| } | |||
| } | |||
| lock (_lock) | |||
| try | |||
| { | |||
| _hasResponded = true; | |||
| return SerializePayload(response); | |||
| } | |||
| finally | |||
| { | |||
| lock (_lock) | |||
| { | |||
| _hasResponded = true; | |||
| } | |||
| } | |||
| return SerializePayload(response); | |||
| } | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupAsync( | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -190,23 +182,8 @@ namespace Discord.Rest | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
| } | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="fileStream">The file to upload.</param> | |||
| /// <param name="fileName">The file name of the attachment.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| /// <inheritdoc/> | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| Stream fileStream, | |||
| string fileName, | |||
| string text = null, | |||
| @@ -214,9 +191,9 @@ namespace Discord.Rest | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -225,55 +202,59 @@ namespace Discord.Rest | |||
| if (embed != null) | |||
| embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| return FollowupWithFileAsync(new FileAttachment(fileStream, fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| /// <inheritdoc/> | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| string fileName = null, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
| fileName ??= Path.GetFileName(filePath); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| return FollowupWithFileAsync(new FileAttachment(File.OpenRead(filePath), fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="filePath">The file to upload.</param> | |||
| /// <param name="fileName">The file name of the attachment.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| /// <inheritdoc/> | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| FileAttachment attachment, | |||
| string text = null, | |||
| string fileName = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFilesAsync( | |||
| IEnumerable<FileAttachment> attachments, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -285,25 +266,35 @@ namespace Discord.Rest | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
| fileName ??= Path.GetFileName(filePath); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| foreach (var attachment in attachments) | |||
| { | |||
| Preconditions.NotNullOrEmpty(attachment.FileName, nameof(attachment.FileName), "File Name must not be empty or null"); | |||
| } | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| 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 flags = MessageFlags.None; | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| flags |= MessageFlags.Ephemeral; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
| var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray()) { Flags = flags, Content = text, IsTTS = isTTS, Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified }; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options).ConfigureAwait(false); | |||
| } | |||
| /// <summary> | |||
| @@ -41,5 +41,8 @@ namespace Discord.Rest | |||
| //IMessageCommandInteraction | |||
| /// <inheritdoc/> | |||
| IMessageCommandInteractionData IMessageCommandInteraction.Data => Data; | |||
| //IApplicationCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||
| } | |||
| } | |||
| @@ -44,5 +44,9 @@ namespace Discord.Rest | |||
| //IUserCommandInteractionData | |||
| /// <inheritdoc/> | |||
| IUserCommandInteractionData IUserCommandInteraction.Data => Data; | |||
| //IApplicationCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||
| } | |||
| } | |||
| @@ -34,17 +34,20 @@ namespace Discord.Rest | |||
| return client.ApiClient.BulkOverwriteGlobalApplicationCommandsAsync(Array.Empty<CreateApplicationCommandParams>(), options); | |||
| } | |||
| public static Task SendInteractionResponseAsync(BaseDiscordClient client, InteractionResponse response, | |||
| ulong interactionId, string interactionToken, RequestOptions options = null) | |||
| public static async Task<RestInteractionMessage> SendInteractionResponseAsync(BaseDiscordClient client, InteractionResponse response, | |||
| IDiscordInteraction interaction, IMessageChannel channel = null, RequestOptions options = null) | |||
| { | |||
| return client.ApiClient.CreateInteractionResponseAsync(response, interactionId, interactionToken, options); | |||
| await client.ApiClient.CreateInteractionResponseAsync(response, interaction.Id, interaction.Token, options).ConfigureAwait(false); | |||
| return RestInteractionMessage.Create(client, response, interaction, channel); | |||
| } | |||
| public static async Task<RestInteractionMessage> GetOriginalResponseAsync(BaseDiscordClient client, IMessageChannel channel, | |||
| IDiscordInteraction interaction, RequestOptions options = null) | |||
| { | |||
| var model = await client.ApiClient.GetInteractionResponseAsync(interaction.Token, options).ConfigureAwait(false); | |||
| return RestInteractionMessage.Create(client, model, interaction.Token, channel); | |||
| if(model != null) | |||
| return RestInteractionMessage.Create(client, model, interaction.Token, channel); | |||
| return null; | |||
| } | |||
| public static async Task<RestFollowupMessage> SendFollowupAsync(BaseDiscordClient client, CreateWebhookMessageParams args, | |||
| @@ -55,6 +58,15 @@ namespace Discord.Rest | |||
| var entity = RestFollowupMessage.Create(client, model, token, channel); | |||
| return entity; | |||
| } | |||
| public static async Task<RestFollowupMessage> SendFollowupAsync(BaseDiscordClient client, UploadWebhookFileParams args, | |||
| string token, IMessageChannel channel, RequestOptions options = null) | |||
| { | |||
| var model = await client.ApiClient.CreateInteractionFollowupMessageAsync(args, token, options).ConfigureAwait(false); | |||
| var entity = RestFollowupMessage.Create(client, model, token, channel); | |||
| return entity; | |||
| } | |||
| #endregion | |||
| #region Global commands | |||
| @@ -64,9 +64,9 @@ namespace Discord.Rest | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <returns> | |||
| /// A string that contains json to write back to the incoming http request. | |||
| /// </returns> | |||
| @@ -76,9 +76,9 @@ namespace Discord.Rest | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -237,29 +237,16 @@ namespace Discord.Rest | |||
| return SerializePayload(response); | |||
| } | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupAsync( | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -284,11 +271,11 @@ namespace Discord.Rest | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Message.Channel, options).ConfigureAwait(false); | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| Stream fileStream, | |||
| string fileName, | |||
| string text = null, | |||
| @@ -296,9 +283,9 @@ namespace Discord.Rest | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -307,40 +294,59 @@ namespace Discord.Rest | |||
| if (embed != null) | |||
| embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | |||
| Preconditions.NotNullOrWhitespace(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| return FollowupWithFileAsync(new FileAttachment(fileStream, fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| /// <inheritdoc/> | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| string fileName = null, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Message.Channel, options).ConfigureAwait(false); | |||
| fileName ??= Path.GetFileName(filePath); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| return FollowupWithFileAsync(new FileAttachment(File.OpenRead(filePath), fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| FileAttachment attachment, | |||
| string text = null, | |||
| string fileName = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFilesAsync( | |||
| IEnumerable<FileAttachment> attachments, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -352,22 +358,35 @@ namespace Discord.Rest | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNullOrWhitespace(filePath, nameof(filePath), "Path must exist"); | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| foreach (var attachment in attachments) | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| Preconditions.NotNullOrEmpty(attachment.FileName, nameof(attachment.FileName), "File Name must not be empty or null"); | |||
| } | |||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
| { | |||
| 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 flags = MessageFlags.None; | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| flags |= MessageFlags.Ephemeral; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Message.Channel, options).ConfigureAwait(false); | |||
| var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray()) { Flags = flags, Content = text, IsTTS = isTTS, Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified }; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options).ConfigureAwait(false); | |||
| } | |||
| /// <summary> | |||
| @@ -139,8 +139,7 @@ namespace Discord.Rest | |||
| /// <inheritdoc/> | |||
| public abstract string Defer(bool ephemeral = false, RequestOptions options = null); | |||
| /// <inheritdoc/> | |||
| public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| /// <summary> | |||
| /// Gets the original response for this interaction. | |||
| /// </summary> | |||
| @@ -154,14 +153,36 @@ namespace Discord.Rest | |||
| /// </summary> | |||
| /// <param name="func">A delegate containing the properties to modify the message with.</param> | |||
| /// <param name="options">The request options for this <see langword="async"/> request.</param> | |||
| /// <returns>A <see cref="RestInteractionMessage"/> that represents the initial response.</returns> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public async Task<RestInteractionMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null) | |||
| { | |||
| var model = await InteractionHelper.ModifyInteractionResponseAsync(Discord, Token, func, options); | |||
| return RestInteractionMessage.Create(Discord, model, Token, Channel); | |||
| } | |||
| /// <inheritdoc/> | |||
| public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent component = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| @@ -172,14 +193,16 @@ namespace Discord.Rest | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| @@ -190,45 +213,90 @@ namespace Discord.Rest | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="attachment">The attachment containing the file and description.</param> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="attachments">A collection of attachments to upload.</param> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| #region IDiscordInteraction | |||
| /// <inheritdoc/> | |||
| Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, RequestOptions options, MessageComponent component, Embed embed) | |||
| => Task.FromResult(Respond(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed)); | |||
| IUser IDiscordInteraction.User => User; | |||
| /// <inheritdoc/> | |||
| Task<IUserMessage> IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| { | |||
| return Task.FromResult<IUserMessage>(null); | |||
| } | |||
| /// <inheritdoc/> | |||
| Task IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options) | |||
| => Task.FromResult(Defer(ephemeral, options)); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, | |||
| RequestOptions options, MessageComponent component, Embed embed) | |||
| => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| MessageComponent component, Embed embed, RequestOptions options) | |||
| => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | |||
| => await GetOriginalResponseAsync(options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | |||
| => await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, | |||
| AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string text, string fileName, Embed[] embeds, bool isTTS, bool ephemeral, | |||
| AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -38,9 +38,11 @@ namespace Discord.Rest | |||
| } | |||
| public override string Defer(bool ephemeral = false, RequestOptions options = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
| public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
| public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent component = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); | |||
| public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); | |||
| } | |||
| } | |||
| @@ -102,31 +102,21 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public string Respond(RequestOptions options = null, params AutocompleteResult[] result) | |||
| => Respond(result, options); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot be deferred!", true)] | |||
| public override string Defer(bool ephemeral = false, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent component = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
| public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have normal responses!", true)] | |||
| public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| //IAutocompleteInteraction | |||
| /// <inheritdoc/> | |||
| @@ -44,5 +44,9 @@ namespace Discord.Rest | |||
| //ISlashCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData ISlashCommandInteraction.Data => Data; | |||
| //IApplicationCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||
| } | |||
| } | |||
| @@ -5,7 +5,7 @@ using Model = Discord.API.Message; | |||
| namespace Discord.Rest | |||
| { | |||
| /// <summary> | |||
| /// Represents a REST-based follow up message sent by a bot responding to a slash command. | |||
| /// Represents a REST-based follow up message sent by a bot responding to an interaction. | |||
| /// </summary> | |||
| public class RestFollowupMessage : RestUserMessage | |||
| { | |||
| @@ -1,15 +1,16 @@ | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| using Model = Discord.API.Message; | |||
| using MessageModel = Discord.API.Message; | |||
| using Model = Discord.API.InteractionResponse; | |||
| namespace Discord.Rest | |||
| { | |||
| /// <summary> | |||
| /// Represents the initial REST-based response to a slash command. | |||
| /// Represents the initial REST-based response to an interaction. | |||
| /// </summary> | |||
| public class RestInteractionMessage : RestUserMessage | |||
| { | |||
| // Token used to delete/modify this followup message | |||
| public InteractionResponseType ResponseType { get; private set; } | |||
| internal string Token { get; } | |||
| internal RestInteractionMessage(BaseDiscordClient discord, ulong id, IUser author, string token, IMessageChannel channel) | |||
| @@ -18,18 +19,31 @@ namespace Discord.Rest | |||
| Token = token; | |||
| } | |||
| internal static RestInteractionMessage Create(BaseDiscordClient discord, Model model, string token, IMessageChannel channel) | |||
| internal static RestInteractionMessage Create(BaseDiscordClient discord, MessageModel model, string token, IMessageChannel channel) | |||
| { | |||
| var entity = new RestInteractionMessage(discord, model.Id, model.Author.IsSpecified ? RestUser.Create(discord, model.Author.Value) : discord.CurrentUser, token, channel); | |||
| entity.Update(model); | |||
| return entity; | |||
| } | |||
| internal new void Update(Model model) | |||
| internal static RestInteractionMessage Create(BaseDiscordClient discord, Model model, IDiscordInteraction interaction, IMessageChannel channel) | |||
| { | |||
| var entity = new RestInteractionMessage(discord, interaction.Id, discord.CurrentUser, interaction.Token, channel); | |||
| entity.Update(model, interaction); | |||
| return entity; | |||
| } | |||
| internal new void Update(MessageModel model) | |||
| { | |||
| base.Update(model); | |||
| } | |||
| internal void Update(Model model, IDiscordInteraction interaction) | |||
| { | |||
| ResponseType = model.Type; | |||
| base.Update(model.ToMessage(interaction)); | |||
| } | |||
| /// <summary> | |||
| /// Deletes this object and all of it's children. | |||
| /// </summary> | |||
| @@ -170,5 +170,48 @@ namespace Discord.Rest | |||
| { | |||
| return new Overwrite(model.TargetId, model.TargetType, new OverwritePermissions(model.Allow, model.Deny)); | |||
| } | |||
| public static API.Message ToMessage(this API.InteractionResponse model, IDiscordInteraction interaction) | |||
| { | |||
| if (model.Data.IsSpecified) | |||
| { | |||
| var data = model.Data.Value; | |||
| var messageModel = new API.Message | |||
| { | |||
| IsTextToSpeech = data.TTS, | |||
| Content = data.Content, | |||
| Embeds = data.Embeds, | |||
| AllowedMentions = data.AllowedMentions, | |||
| Components = data.Components, | |||
| Flags = data.Flags, | |||
| }; | |||
| if(interaction is IApplicationCommandInteraction command) | |||
| { | |||
| messageModel.Interaction = new API.MessageInteraction | |||
| { | |||
| Id = command.Id, | |||
| Name = command.Data.Name, | |||
| Type = InteractionType.ApplicationCommand, | |||
| User = new API.User | |||
| { | |||
| Username = command.User.Username, | |||
| Avatar = command.User.AvatarId, | |||
| Bot = command.User.IsBot, | |||
| Discriminator = command.User.Discriminator, | |||
| PublicFlags = command.User.PublicFlags.HasValue ? command.User.PublicFlags.Value : Optional<UserProperties>.Unspecified, | |||
| Id = command.User.Id, | |||
| } | |||
| }; | |||
| } | |||
| return messageModel; | |||
| } | |||
| return new API.Message | |||
| { | |||
| Id = interaction.Id, | |||
| }; | |||
| } | |||
| } | |||
| } | |||
| @@ -41,5 +41,9 @@ namespace Discord.WebSocket | |||
| //IDiscordInteraction | |||
| /// <inheritdoc/> | |||
| IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
| //IApplicationCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||
| } | |||
| } | |||
| @@ -41,5 +41,9 @@ namespace Discord.WebSocket | |||
| //IDiscordInteraction | |||
| /// <inheritdoc/> | |||
| IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
| //IApplicationCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||
| } | |||
| } | |||
| @@ -72,15 +72,15 @@ namespace Discord.WebSocket | |||
| } | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task RespondAsync( | |||
| public override async Task<RestInteractionMessage> RespondAsync( | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -136,11 +136,16 @@ namespace Discord.WebSocket | |||
| } | |||
| } | |||
| await InteractionHelper.SendInteractionResponseAsync(Discord, response, Id, Token, options).ConfigureAwait(false); | |||
| lock (_lock) | |||
| try | |||
| { | |||
| HasResponded = true; | |||
| return await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false); | |||
| } | |||
| finally | |||
| { | |||
| lock (_lock) | |||
| { | |||
| HasResponded = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -231,7 +236,7 @@ namespace Discord.WebSocket | |||
| } | |||
| } | |||
| await InteractionHelper.SendInteractionResponseAsync(Discord, response, Id, Token, options).ConfigureAwait(false); | |||
| await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false); | |||
| lock (_lock) | |||
| { | |||
| @@ -246,9 +251,9 @@ namespace Discord.WebSocket | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -273,11 +278,11 @@ namespace Discord.WebSocket | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options).ConfigureAwait(false); | |||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| Stream fileStream, | |||
| string fileName, | |||
| string text = null, | |||
| @@ -285,9 +290,9 @@ namespace Discord.WebSocket | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -296,40 +301,59 @@ namespace Discord.WebSocket | |||
| if (embed != null) | |||
| embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | |||
| Preconditions.NotNullOrWhitespace(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| return FollowupWithFileAsync(new FileAttachment(fileStream, fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| /// <inheritdoc/> | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| string fileName = null, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options).ConfigureAwait(false); | |||
| fileName ??= Path.GetFileName(filePath); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| return FollowupWithFileAsync(new FileAttachment(File.OpenRead(filePath), fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| FileAttachment attachment, | |||
| string text = null, | |||
| string fileName = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFilesAsync( | |||
| IEnumerable<FileAttachment> attachments, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -341,22 +365,35 @@ namespace Discord.WebSocket | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNullOrWhitespace(filePath, nameof(filePath), "Path must exist"); | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| foreach (var attachment in attachments) | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| Preconditions.NotNullOrEmpty(attachment.FileName, nameof(attachment.FileName), "File Name must not be empty or null"); | |||
| } | |||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
| { | |||
| 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 flags = MessageFlags.None; | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| flags |= MessageFlags.Ephemeral; | |||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options).ConfigureAwait(false); | |||
| var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray()) { Flags = flags, Content = text, IsTTS = isTTS, Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified }; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options).ConfigureAwait(false); | |||
| } | |||
| /// <summary> | |||
| @@ -89,31 +89,20 @@ namespace Discord.WebSocket | |||
| /// </returns> | |||
| public Task RespondAsync(RequestOptions options = null, params AutocompleteResult[] result) | |||
| => RespondAsync(result, options); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot be deferred!", true)] | |||
| public override Task<RestInteractionMessage> RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| public override Task DeferAsync(bool ephemeral = false, RequestOptions options = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
| public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| /// <inheritdoc/> | |||
| [Obsolete("Autocomplete interactions cannot have normal responses!", true)] | |||
| public override Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
| => throw new NotSupportedException("Autocomplete interactions don't support this method!"); | |||
| //IAutocompleteInteraction | |||
| /// <inheritdoc/> | |||
| @@ -41,5 +41,9 @@ namespace Discord.WebSocket | |||
| //IDiscordInteraction | |||
| /// <inheritdoc/> | |||
| IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
| //IApplicationCommandInteraction | |||
| /// <inheritdoc/> | |||
| IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| using Discord.Net.Rest; | |||
| using Discord.Rest; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.IO; | |||
| using System.Linq; | |||
| using System.Threading.Tasks; | |||
| @@ -68,15 +69,15 @@ namespace Discord.WebSocket | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task RespondAsync( | |||
| public override async Task<RestInteractionMessage> RespondAsync( | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -130,11 +131,16 @@ namespace Discord.WebSocket | |||
| } | |||
| } | |||
| await InteractionHelper.SendInteractionResponseAsync(Discord, response, Id, Token, options).ConfigureAwait(false); | |||
| lock (_lock) | |||
| try | |||
| { | |||
| HasResponded = true; | |||
| return await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false); | |||
| } | |||
| finally | |||
| { | |||
| lock (_lock) | |||
| { | |||
| HasResponded = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -145,9 +151,9 @@ namespace Discord.WebSocket | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -176,7 +182,7 @@ namespace Discord.WebSocket | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| Stream fileStream, | |||
| string fileName, | |||
| string text = null, | |||
| @@ -184,9 +190,9 @@ namespace Discord.WebSocket | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -195,40 +201,59 @@ namespace Discord.WebSocket | |||
| if (embed != null) | |||
| embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| return FollowupWithFileAsync(new FileAttachment(fileStream, fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| /// <inheritdoc/> | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| string fileName = null, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); | |||
| fileName ??= Path.GetFileName(filePath); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| return FollowupWithFileAsync(new FileAttachment(File.OpenRead(filePath), fileName), text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| string filePath, | |||
| public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||
| FileAttachment attachment, | |||
| string text = null, | |||
| string fileName = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| RequestOptions options = null, | |||
| MessageComponent component = null, | |||
| Embed embed = null) | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override async Task<RestFollowupMessage> FollowupWithFilesAsync( | |||
| IEnumerable<FileAttachment> attachments, | |||
| string text = null, | |||
| Embed[] embeds = null, | |||
| bool isTTS = false, | |||
| bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, | |||
| MessageComponent components = null, | |||
| Embed embed = null, | |||
| RequestOptions options = null) | |||
| { | |||
| if (!IsValidToken) | |||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||
| @@ -240,25 +265,35 @@ namespace Discord.WebSocket | |||
| 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."); | |||
| Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
| Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
| fileName ??= Path.GetFileName(filePath); | |||
| Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
| foreach (var attachment in attachments) | |||
| { | |||
| Preconditions.NotNullOrEmpty(attachment.FileName, nameof(attachment.FileName), "File Name must not be empty or null"); | |||
| } | |||
| var args = new API.Rest.CreateWebhookMessageParams | |||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
| { | |||
| Content = text, | |||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
| IsTTS = isTTS, | |||
| Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
| File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified | |||
| }; | |||
| 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 flags = MessageFlags.None; | |||
| if (ephemeral) | |||
| args.Flags = MessageFlags.Ephemeral; | |||
| flags |= MessageFlags.Ephemeral; | |||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); | |||
| var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray()) { Flags = flags, Content = text, IsTTS = isTTS, Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified }; | |||
| return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options).ConfigureAwait(false); | |||
| } | |||
| /// <summary> | |||
| @@ -4,6 +4,7 @@ using System.Threading.Tasks; | |||
| using Model = Discord.API.Interaction; | |||
| using DataModel = Discord.API.ApplicationCommandInteractionData; | |||
| using System.IO; | |||
| using System.Collections.Generic; | |||
| namespace Discord.WebSocket | |||
| { | |||
| @@ -130,13 +131,13 @@ namespace Discord.WebSocket | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||
| /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||
| public abstract Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, | |||
| bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| public abstract Task<RestInteractionMessage> RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, | |||
| bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| @@ -146,14 +147,14 @@ namespace Discord.WebSocket | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| @@ -165,14 +166,14 @@ namespace Discord.WebSocket | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| @@ -184,14 +185,52 @@ namespace Discord.WebSocket | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <returns> | |||
| /// The sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="attachment">The attachment containing the file and description.</param> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Sends a followup message for this interaction. | |||
| /// </summary> | |||
| /// <param name="attachments">A collection of attachments to upload.</param> | |||
| /// <param name="text">The text of the message to be sent.</param> | |||
| /// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
| /// <param name="options">The request options for this response.</param> | |||
| /// <param name="components">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
| /// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
| /// <returns> | |||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | |||
| /// contains the sent message. | |||
| /// </returns> | |||
| public abstract Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); | |||
| /// <summary> | |||
| /// Gets the original response for this interaction. | |||
| @@ -222,32 +261,37 @@ namespace Discord.WebSocket | |||
| /// A task that represents the asynchronous operation of acknowledging the interaction. | |||
| /// </returns> | |||
| public abstract Task DeferAsync(bool ephemeral = false, RequestOptions options = null); | |||
| #endregion | |||
| #region IDiscordInteraction | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, | |||
| RequestOptions options, MessageComponent component, Embed embed) | |||
| => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
| => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
| IUser IDiscordInteraction.User => User; | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | |||
| => await GetOriginalResponseAsync(options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | |||
| => await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| /// <inheritdoc/> | |||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | |||
| => await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -88,5 +88,11 @@ namespace Discord.WebSocket | |||
| return base.Equals(obj); | |||
| } | |||
| /// <inheritdoc/> | |||
| public override int GetHashCode() | |||
| { | |||
| return base.GetHashCode(); | |||
| } | |||
| } | |||
| } | |||