* Refactor Interactions * Remove ApplicationCommandExceptionpull/1958/head
@@ -3,7 +3,7 @@ namespace Discord | |||||
/// <summary> | /// <summary> | ||||
/// Represents a Message Command interaction. | /// Represents a Message Command interaction. | ||||
/// </summary> | /// </summary> | ||||
public interface IMessageCommandInteraction : IDiscordInteraction | |||||
public interface IMessageCommandInteraction : IApplicationCommandInteraction | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Gets the data associated with this interaction. | /// Gets the data associated with this interaction. | ||||
@@ -3,7 +3,7 @@ namespace Discord | |||||
/// <summary> | /// <summary> | ||||
/// Represents a User Command interaction. | /// Represents a User Command interaction. | ||||
/// </summary> | /// </summary> | ||||
public interface IUserCommandInteraction : IDiscordInteraction | |||||
public interface IUserCommandInteraction : IApplicationCommandInteraction | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Gets the data associated with this interaction. | /// 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; | ||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -34,6 +35,11 @@ namespace Discord | |||||
/// </summary> | /// </summary> | ||||
int Version { get; } | int Version { get; } | ||||
/// <summary> | |||||
/// Gets the user who invoked the interaction. | |||||
/// </summary> | |||||
IUser User { get; } | |||||
/// <summary> | /// <summary> | ||||
/// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>. | /// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>. | ||||
/// </summary> | /// </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="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="allowedMentions">The allowed mentions for this response.</param> | ||||
/// <param name="options">The request options 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="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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// 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="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="allowedMentions">The allowed mentions for this response.</param> | ||||
/// <param name="options">The request options 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="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> | /// <returns> | ||||
/// The sent message. | |||||
/// A task that represents an asynchronous send operation for delivering the message. The task result | |||||
/// contains the sent message. | |||||
/// </returns> | /// </returns> | ||||
Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | 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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// 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="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="allowedMentions">The allowed mentions for this response.</param> | ||||
/// <param name="options">The request options 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="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> | /// <returns> | ||||
/// The sent message. | |||||
/// A task that represents an asynchronous send operation for delivering the message. The task result | |||||
/// contains the sent message. | |||||
/// </returns> | /// </returns> | ||||
public Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | 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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// 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="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="allowedMentions">The allowed mentions for this response.</param> | ||||
/// <param name="options">The request options 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> | /// <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> | /// <returns> | ||||
/// The sent message. | |||||
/// A task that represents an asynchronous send operation for delivering the message. The task result | |||||
/// contains the sent message. | |||||
/// </returns> | /// </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> | /// <summary> | ||||
/// Gets the original response for this interaction. | /// Gets the original response for this interaction. | ||||
@@ -115,14 +164,17 @@ namespace Discord | |||||
/// </summary> | /// </summary> | ||||
/// <param name="func">A delegate containing the properties to modify the message with.</param> | /// <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> | /// <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); | Task<IUserMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null); | ||||
/// <summary> | /// <summary> | ||||
/// Acknowledges this interaction. | /// Acknowledges this interaction. | ||||
/// </summary> | /// </summary> | ||||
/// <returns> | /// <returns> | ||||
/// A task that represents the asynchronous operation of acknowledging the interaction. | |||||
/// A task that represents the asynchronous operation of deferring the interaction. | |||||
/// </returns> | /// </returns> | ||||
Task DeferAsync(bool ephemeral = false, RequestOptions options = null); | Task DeferAsync(bool ephemeral = false, RequestOptions options = null); | ||||
} | } | ||||
@@ -3,7 +3,7 @@ namespace Discord | |||||
/// <summary> | /// <summary> | ||||
/// Represents a slash command interaction. | /// Represents a slash command interaction. | ||||
/// </summary> | /// </summary> | ||||
public interface ISlashCommandInteraction : IDiscordInteraction | |||||
public interface ISlashCommandInteraction : IApplicationCommandInteraction | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Gets the data associated with this interaction. | /// 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)"/> | /// <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, | 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) => | 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)"/> | /// <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, | 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) => | 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[])"/> | /// <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, | 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) | 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"); | 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<Embed[]> Embeds { get; set; } | ||||
public Optional<AllowedMentions> AllowedMentions { get; set; } | public Optional<AllowedMentions> AllowedMentions { get; set; } | ||||
public Optional<ActionRowComponent[]> MessageComponents { get; set; } | public Optional<ActionRowComponent[]> MessageComponents { get; set; } | ||||
public Optional<MessageFlags> Flags { get; set; } | |||||
public UploadWebhookFileParams(params FileAttachment[] files) | public UploadWebhookFileParams(params FileAttachment[] files) | ||||
{ | { | ||||
@@ -48,6 +49,8 @@ namespace Discord.API.Rest | |||||
payload["embeds"] = Embeds.Value; | payload["embeds"] = Embeds.Value; | ||||
if (AllowedMentions.IsSpecified) | if (AllowedMentions.IsSpecified) | ||||
payload["allowed_mentions"] = AllowedMentions.Value; | payload["allowed_mentions"] = AllowedMentions.Value; | ||||
if (Flags.IsSpecified) | |||||
payload["flags"] = Flags.Value; | |||||
List<object> attachments = new(); | List<object> attachments = new(); | ||||
@@ -1198,25 +1198,25 @@ namespace Discord.API | |||||
options = RequestOptions.CreateOrClone(options); | 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) | public async Task<ApplicationCommand> ModifyGlobalApplicationCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) | ||||
{ | { | ||||
options = RequestOptions.CreateOrClone(options); | 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) | public async Task<ApplicationCommand> ModifyGlobalApplicationUserCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) | ||||
{ | { | ||||
options = RequestOptions.CreateOrClone(options); | 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) | public async Task<ApplicationCommand> ModifyGlobalApplicationMessageCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null) | ||||
{ | { | ||||
options = RequestOptions.CreateOrClone(options); | 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) | public async Task DeleteGlobalApplicationCommandAsync(ulong commandId, RequestOptions options = null) | ||||
{ | { | ||||
@@ -1229,7 +1229,7 @@ namespace Discord.API | |||||
{ | { | ||||
options = RequestOptions.CreateOrClone(options); | 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) | public async Task<ApplicationCommand[]> GetGuildApplicationCommandsAsync(ulong guildId, RequestOptions options = null) | ||||
@@ -1271,7 +1271,7 @@ namespace Discord.API | |||||
var bucket = new BucketIds(guildId: guildId); | 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) | 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); | 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) | public async Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId, RequestOptions options = null) | ||||
{ | { | ||||
@@ -1296,7 +1296,7 @@ namespace Discord.API | |||||
var bucket = new BucketIds(guildId: guildId); | 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 | #endregion | ||||
@@ -1316,7 +1316,7 @@ namespace Discord.API | |||||
options = RequestOptions.CreateOrClone(options); | 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) | 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); | 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) | public async Task<Message> ModifyInteractionFollowupMessageAsync(ModifyInteractionResponseParams args, ulong id, string token, RequestOptions options = null) | ||||
{ | { | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
@@ -2222,40 +2237,6 @@ namespace Discord.API | |||||
return _serializer.Deserialize<T>(reader); | 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 | protected async Task<T> NullifyNotFound<T>(Task<T> sendTask) where T : class | ||||
{ | { | ||||
try | try | ||||
@@ -76,9 +76,9 @@ namespace Discord.Rest | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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( | public override async Task<RestFollowupMessage> FollowupAsync( | ||||
string text = null, | string text = null, | ||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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); | 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, | Stream fileStream, | ||||
string fileName, | string fileName, | ||||
string text = null, | string text = null, | ||||
@@ -214,9 +191,9 @@ namespace Discord.Rest | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | |||||
Embed embed = null) | |||||
MessageComponent components = null, | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -225,55 +202,59 @@ namespace Discord.Rest | |||||
if (embed != null) | if (embed != null) | ||||
embeds = new[] { embed }.Concat(embeds).ToArray(); | 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.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | ||||
Preconditions.NotNullOrEmpty(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, 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 text = null, | ||||
string fileName = null, | |||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | 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) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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?.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(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.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) | 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> | /// <summary> | ||||
@@ -41,5 +41,8 @@ namespace Discord.Rest | |||||
//IMessageCommandInteraction | //IMessageCommandInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
IMessageCommandInteractionData IMessageCommandInteraction.Data => Data; | IMessageCommandInteractionData IMessageCommandInteraction.Data => Data; | ||||
//IApplicationCommandInteraction | |||||
/// <inheritdoc/> | |||||
IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||||
} | } | ||||
} | } |
@@ -44,5 +44,9 @@ namespace Discord.Rest | |||||
//IUserCommandInteractionData | //IUserCommandInteractionData | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
IUserCommandInteractionData IUserCommandInteraction.Data => Data; | 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); | 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, | public static async Task<RestInteractionMessage> GetOriginalResponseAsync(BaseDiscordClient client, IMessageChannel channel, | ||||
IDiscordInteraction interaction, RequestOptions options = null) | IDiscordInteraction interaction, RequestOptions options = null) | ||||
{ | { | ||||
var model = await client.ApiClient.GetInteractionResponseAsync(interaction.Token, options).ConfigureAwait(false); | 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, | 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); | var entity = RestFollowupMessage.Create(client, model, token, channel); | ||||
return entity; | 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 | #endregion | ||||
#region Global commands | #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="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="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="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="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="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> | /// <returns> | ||||
/// A string that contains json to write back to the incoming http request. | /// A string that contains json to write back to the incoming http request. | ||||
/// </returns> | /// </returns> | ||||
@@ -76,9 +76,9 @@ namespace Discord.Rest | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -237,29 +237,16 @@ namespace Discord.Rest | |||||
return SerializePayload(response); | 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( | public override async Task<RestFollowupMessage> FollowupAsync( | ||||
string text = null, | string text = null, | ||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -284,11 +271,11 @@ namespace Discord.Rest | |||||
if (ephemeral) | if (ephemeral) | ||||
args.Flags = MessageFlags.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/> | /// <inheritdoc/> | ||||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
Stream fileStream, | Stream fileStream, | ||||
string fileName, | string fileName, | ||||
string text = null, | string text = null, | ||||
@@ -296,9 +283,9 @@ namespace Discord.Rest | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | |||||
Embed embed = null) | |||||
MessageComponent components = null, | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -307,40 +294,59 @@ namespace Discord.Rest | |||||
if (embed != null) | if (embed != null) | ||||
embeds = new[] { embed }.Concat(embeds).ToArray(); | 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.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/> | /// <inheritdoc/> | ||||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
string filePath, | |||||
public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
FileAttachment attachment, | |||||
string text = null, | string text = null, | ||||
string fileName = null, | |||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | 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) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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?.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(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.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) | 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> | /// <summary> | ||||
@@ -139,8 +139,7 @@ namespace Discord.Rest | |||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
public abstract string Defer(bool ephemeral = false, RequestOptions options = null); | 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> | /// <summary> | ||||
/// Gets the original response for this interaction. | /// Gets the original response for this interaction. | ||||
/// </summary> | /// </summary> | ||||
@@ -154,14 +153,36 @@ namespace Discord.Rest | |||||
/// </summary> | /// </summary> | ||||
/// <param name="func">A delegate containing the properties to modify the message with.</param> | /// <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> | /// <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) | public async Task<RestInteractionMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null) | ||||
{ | { | ||||
var model = await InteractionHelper.ModifyInteractionResponseAsync(Discord, Token, func, options); | var model = await InteractionHelper.ModifyInteractionResponseAsync(Discord, Token, func, options); | ||||
return RestInteractionMessage.Create(Discord, model, Token, Channel); | return RestInteractionMessage.Create(Discord, model, Token, Channel); | ||||
} | } | ||||
/// <inheritdoc/> | /// <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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// Sends a followup message for this interaction. | ||||
/// </summary> | /// </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="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="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="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="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> | /// <returns> | ||||
/// The sent message. | |||||
/// A task that represents an asynchronous send operation for delivering the message. The task result | |||||
/// contains the sent message. | |||||
/// </returns> | /// </returns> | ||||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | 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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// Sends a followup message for this interaction. | ||||
/// </summary> | /// </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="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="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="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="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> | /// <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> | /// <returns> | ||||
/// The sent message. | |||||
/// A task that represents an asynchronous send operation for delivering the message. The task result | |||||
/// contains the sent message. | |||||
/// </returns> | /// </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 | #region IDiscordInteraction | ||||
/// <inheritdoc/> | /// <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 IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options) | ||||
=> Task.FromResult(Defer(ephemeral, options)); | => Task.FromResult(Defer(ephemeral, options)); | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, | 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/> | /// <inheritdoc/> | ||||
async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | ||||
=> await GetOriginalResponseAsync(options).ConfigureAwait(false); | => await GetOriginalResponseAsync(options).ConfigureAwait(false); | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | ||||
=> await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false); | => await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false); | ||||
/// <inheritdoc/> | /// <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/> | /// <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 | #endregion | ||||
} | } | ||||
} | } |
@@ -38,9 +38,11 @@ namespace Discord.Rest | |||||
} | } | ||||
public override string Defer(bool ephemeral = false, RequestOptions options = null) => throw new NotSupportedException(); | 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> | /// </returns> | ||||
public string Respond(RequestOptions options = null, params AutocompleteResult[] result) | public string Respond(RequestOptions options = null, params AutocompleteResult[] result) | ||||
=> Respond(result, options); | => Respond(result, options); | ||||
/// <inheritdoc/> | |||||
[Obsolete("Autocomplete interactions cannot be deferred!", true)] | |||||
public override string Defer(bool ephemeral = false, RequestOptions options = null) | 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 | //IAutocompleteInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
@@ -44,5 +44,9 @@ namespace Discord.Rest | |||||
//ISlashCommandInteraction | //ISlashCommandInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
IApplicationCommandInteractionData ISlashCommandInteraction.Data => Data; | IApplicationCommandInteractionData ISlashCommandInteraction.Data => Data; | ||||
//IApplicationCommandInteraction | |||||
/// <inheritdoc/> | |||||
IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||||
} | } | ||||
} | } |
@@ -5,7 +5,7 @@ using Model = Discord.API.Message; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
/// <summary> | /// <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> | /// </summary> | ||||
public class RestFollowupMessage : RestUserMessage | public class RestFollowupMessage : RestUserMessage | ||||
{ | { | ||||
@@ -1,15 +1,16 @@ | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Model = Discord.API.Message; | |||||
using MessageModel = Discord.API.Message; | |||||
using Model = Discord.API.InteractionResponse; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Represents the initial REST-based response to a slash command. | |||||
/// Represents the initial REST-based response to an interaction. | |||||
/// </summary> | /// </summary> | ||||
public class RestInteractionMessage : RestUserMessage | public class RestInteractionMessage : RestUserMessage | ||||
{ | { | ||||
// Token used to delete/modify this followup message | |||||
public InteractionResponseType ResponseType { get; private set; } | |||||
internal string Token { get; } | internal string Token { get; } | ||||
internal RestInteractionMessage(BaseDiscordClient discord, ulong id, IUser author, string token, IMessageChannel channel) | internal RestInteractionMessage(BaseDiscordClient discord, ulong id, IUser author, string token, IMessageChannel channel) | ||||
@@ -18,18 +19,31 @@ namespace Discord.Rest | |||||
Token = token; | 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); | var entity = new RestInteractionMessage(discord, model.Id, model.Author.IsSpecified ? RestUser.Create(discord, model.Author.Value) : discord.CurrentUser, token, channel); | ||||
entity.Update(model); | entity.Update(model); | ||||
return entity; | 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); | base.Update(model); | ||||
} | } | ||||
internal void Update(Model model, IDiscordInteraction interaction) | |||||
{ | |||||
ResponseType = model.Type; | |||||
base.Update(model.ToMessage(interaction)); | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// Deletes this object and all of it's children. | /// Deletes this object and all of it's children. | ||||
/// </summary> | /// </summary> | ||||
@@ -170,5 +170,48 @@ namespace Discord.Rest | |||||
{ | { | ||||
return new Overwrite(model.TargetId, model.TargetType, new OverwritePermissions(model.Allow, model.Deny)); | 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 | //IDiscordInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
IDiscordInteractionData IDiscordInteraction.Data => Data; | IDiscordInteractionData IDiscordInteraction.Data => Data; | ||||
//IApplicationCommandInteraction | |||||
/// <inheritdoc/> | |||||
IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||||
} | } | ||||
} | } |
@@ -41,5 +41,9 @@ namespace Discord.WebSocket | |||||
//IDiscordInteraction | //IDiscordInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
IDiscordInteractionData IDiscordInteraction.Data => Data; | IDiscordInteractionData IDiscordInteraction.Data => Data; | ||||
//IApplicationCommandInteraction | |||||
/// <inheritdoc/> | |||||
IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||||
} | } | ||||
} | } |
@@ -72,15 +72,15 @@ namespace Discord.WebSocket | |||||
} | } | ||||
} | } | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
public override async Task RespondAsync( | |||||
public override async Task<RestInteractionMessage> RespondAsync( | |||||
string text = null, | string text = null, | ||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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) | lock (_lock) | ||||
{ | { | ||||
@@ -246,9 +251,9 @@ namespace Discord.WebSocket | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -273,11 +278,11 @@ namespace Discord.WebSocket | |||||
if (ephemeral) | if (ephemeral) | ||||
args.Flags = MessageFlags.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/> | /// <inheritdoc/> | ||||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
Stream fileStream, | Stream fileStream, | ||||
string fileName, | string fileName, | ||||
string text = null, | string text = null, | ||||
@@ -285,9 +290,9 @@ namespace Discord.WebSocket | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | |||||
Embed embed = null) | |||||
MessageComponent components = null, | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -296,40 +301,59 @@ namespace Discord.WebSocket | |||||
if (embed != null) | if (embed != null) | ||||
embeds = new[] { embed }.Concat(embeds).ToArray(); | 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.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/> | /// <inheritdoc/> | ||||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
string filePath, | |||||
public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
FileAttachment attachment, | |||||
string text = null, | string text = null, | ||||
string fileName = null, | |||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | 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) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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?.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(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.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) | 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> | /// <summary> | ||||
@@ -89,31 +89,20 @@ namespace Discord.WebSocket | |||||
/// </returns> | /// </returns> | ||||
public Task RespondAsync(RequestOptions options = null, params AutocompleteResult[] result) | public Task RespondAsync(RequestOptions options = null, params AutocompleteResult[] result) | ||||
=> RespondAsync(result, options); | => 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) | 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 | //IAutocompleteInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
@@ -41,5 +41,9 @@ namespace Discord.WebSocket | |||||
//IDiscordInteraction | //IDiscordInteraction | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
IDiscordInteractionData IDiscordInteraction.Data => Data; | IDiscordInteractionData IDiscordInteraction.Data => Data; | ||||
//IApplicationCommandInteraction | |||||
/// <inheritdoc/> | |||||
IApplicationCommandInteractionData IApplicationCommandInteraction.Data => Data; | |||||
} | } | ||||
} | } |
@@ -1,6 +1,7 @@ | |||||
using Discord.Net.Rest; | using Discord.Net.Rest; | ||||
using Discord.Rest; | using Discord.Rest; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -68,15 +69,15 @@ namespace Discord.WebSocket | |||||
} | } | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
public override async Task RespondAsync( | |||||
public override async Task<RestInteractionMessage> RespondAsync( | |||||
string text = null, | string text = null, | ||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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 isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | MessageComponent component = null, | ||||
Embed embed = null) | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -176,7 +182,7 @@ namespace Discord.WebSocket | |||||
} | } | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
Stream fileStream, | Stream fileStream, | ||||
string fileName, | string fileName, | ||||
string text = null, | string text = null, | ||||
@@ -184,9 +190,9 @@ namespace Discord.WebSocket | |||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | AllowedMentions allowedMentions = null, | ||||
RequestOptions options = null, | |||||
MessageComponent component = null, | |||||
Embed embed = null) | |||||
MessageComponent components = null, | |||||
Embed embed = null, | |||||
RequestOptions options = null) | |||||
{ | { | ||||
if (!IsValidToken) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
@@ -195,40 +201,59 @@ namespace Discord.WebSocket | |||||
if (embed != null) | if (embed != null) | ||||
embeds = new[] { embed }.Concat(embeds).ToArray(); | 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.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | ||||
Preconditions.NotNullOrEmpty(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); | |||||
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/> | /// <inheritdoc/> | ||||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
string filePath, | |||||
public override Task<RestFollowupMessage> FollowupWithFileAsync( | |||||
FileAttachment attachment, | |||||
string text = null, | string text = null, | ||||
string fileName = null, | |||||
Embed[] embeds = null, | Embed[] embeds = null, | ||||
bool isTTS = false, | bool isTTS = false, | ||||
bool ephemeral = false, | bool ephemeral = false, | ||||
AllowedMentions allowedMentions = null, | 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) | if (!IsValidToken) | ||||
throw new InvalidOperationException("Interaction token is no longer valid"); | 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?.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(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.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) | 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> | /// <summary> | ||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks; | |||||
using Model = Discord.API.Interaction; | using Model = Discord.API.Interaction; | ||||
using DataModel = Discord.API.ApplicationCommandInteractionData; | using DataModel = Discord.API.ApplicationCommandInteractionData; | ||||
using System.IO; | using System.IO; | ||||
using System.Collections.Generic; | |||||
namespace Discord.WebSocket | 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="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="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="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="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="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> | /// <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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// 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="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="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="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="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> | /// <returns> | ||||
/// The sent message. | /// The sent message. | ||||
/// </returns> | /// </returns> | ||||
public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | 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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// 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="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="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="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="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> | /// <returns> | ||||
/// The sent message. | /// The sent message. | ||||
/// </returns> | /// </returns> | ||||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | 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> | /// <summary> | ||||
/// Sends a followup message for this interaction. | /// 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="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="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="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="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> | /// <returns> | ||||
/// The sent message. | /// The sent message. | ||||
/// </returns> | /// </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> | /// <summary> | ||||
/// Gets the original response for this interaction. | /// 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. | /// A task that represents the asynchronous operation of acknowledging the interaction. | ||||
/// </returns> | /// </returns> | ||||
public abstract Task DeferAsync(bool ephemeral = false, RequestOptions options = null); | public abstract Task DeferAsync(bool ephemeral = false, RequestOptions options = null); | ||||
#endregion | #endregion | ||||
#region IDiscordInteraction | #region IDiscordInteraction | ||||
/// <inheritdoc/> | /// <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/> | /// <inheritdoc/> | ||||
async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | ||||
=> await GetOriginalResponseAsync(options).ConfigureAwait(false); | => await GetOriginalResponseAsync(options).ConfigureAwait(false); | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | ||||
=> await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false); | => 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 | #endregion | ||||
} | } | ||||
} | } |
@@ -88,5 +88,11 @@ namespace Discord.WebSocket | |||||
return base.Equals(obj); | return base.Equals(obj); | ||||
} | } | ||||
/// <inheritdoc/> | |||||
public override int GetHashCode() | |||||
{ | |||||
return base.GetHashCode(); | |||||
} | |||||
} | } | ||||
} | } |