using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model = Discord.API.Interaction;
using DataModel = Discord.API.ApplicationCommandInteractionData;
using Newtonsoft.Json;
using Discord.Net;
namespace Discord.Rest
{
///
/// Represents a REST-based interaction.
///
public abstract class RestInteraction : RestEntity, IDiscordInteraction
{
///
public InteractionType Type { get; private set; }
///
public IDiscordInteractionData Data { get; private set; }
///
public string Token { get; private set; }
///
public int Version { get; private set; }
///
/// Gets the user who invoked the interaction.
///
public RestUser User { get; private set; }
///
public string UserLocale { get; private set; }
///
public string GuildLocale { get; private set; }
///
public DateTimeOffset CreatedAt { get; private set; }
///
/// Gets whether or not the token used to respond to this interaction is valid.
///
public bool IsValidToken
=> InteractionHelper.CanRespondOrFollowup(this);
///
/// Gets the channel that this interaction was executed in.
///
public IRestMessageChannel Channel { get; private set; }
///
/// Gets the guild this interaction was executed in.
///
public RestGuild Guild { get; private set; }
///
public bool HasResponded { get; protected set; }
///
public bool IsDMInteraction { get; private set; }
internal RestInteraction(BaseDiscordClient discord, ulong id)
: base(discord, id)
{
CreatedAt = discord.UseInteractionSnowflakeDate
? SnowflakeUtils.FromSnowflake(Id)
: DateTime.UtcNow;
}
internal static async Task CreateAsync(DiscordRestClient client, Model model)
{
if(model.Type == InteractionType.Ping)
{
return await RestPingInteraction.CreateAsync(client, model);
}
if (model.Type == InteractionType.ApplicationCommand)
{
var dataModel = model.Data.IsSpecified
? (DataModel)model.Data.Value
: null;
if (dataModel == null)
return null;
return dataModel.Type switch
{
ApplicationCommandType.Slash => await RestSlashCommand.CreateAsync(client, model).ConfigureAwait(false),
ApplicationCommandType.Message => await RestMessageCommand.CreateAsync(client, model).ConfigureAwait(false),
ApplicationCommandType.User => await RestUserCommand.CreateAsync(client, model).ConfigureAwait(false),
_ => null
};
}
if (model.Type == InteractionType.MessageComponent)
return await RestMessageComponent.CreateAsync(client, model).ConfigureAwait(false);
if (model.Type == InteractionType.ApplicationCommandAutocomplete)
return await RestAutocompleteInteraction.CreateAsync(client, model).ConfigureAwait(false);
if (model.Type == InteractionType.ModalSubmit)
return await RestModal.CreateAsync(client, model).ConfigureAwait(false);
return null;
}
internal virtual async Task UpdateAsync(DiscordRestClient discord, Model model)
{
IsDMInteraction = !model.GuildId.IsSpecified;
Data = model.Data.IsSpecified
? model.Data.Value
: null;
Token = model.Token;
Version = model.Version;
Type = model.Type;
if(Guild == null && model.GuildId.IsSpecified)
{
Guild = await discord.GetGuildAsync(model.GuildId.Value);
}
if (User == null)
{
if (model.Member.IsSpecified && model.GuildId.IsSpecified)
{
User = RestGuildUser.Create(Discord, Guild, model.Member.Value);
}
else
{
User = RestUser.Create(Discord, model.User.Value);
}
}
if(Channel == null && model.ChannelId.IsSpecified)
{
try
{
Channel = (IRestMessageChannel)await discord.GetChannelAsync(model.ChannelId.Value);
}
catch(HttpException x) when(x.DiscordCode == DiscordErrorCode.MissingPermissions) { } // ignore
}
UserLocale = model.UserLocale.IsSpecified
? model.UserLocale.Value
: null;
GuildLocale = model.GuildLocale.IsSpecified
? model.GuildLocale.Value
: null;
}
internal string SerializePayload(object payload)
{
var json = new StringBuilder();
using (var text = new StringWriter(json))
using (var writer = new JsonTextWriter(text))
DiscordRestClient.Serializer.Serialize(writer, payload);
return json.ToString();
}
///
public abstract string Defer(bool ephemeral = false, RequestOptions options = null);
///
/// Gets the original response for this interaction.
///
/// The request options for this request.
/// A that represents the initial response.
public Task GetOriginalResponseAsync(RequestOptions options = null)
=> InteractionHelper.GetOriginalResponseAsync(Discord, Channel, this, options);
///
/// Edits original response for this interaction.
///
/// A delegate containing the properties to modify the message with.
/// The request options for this request.
///
/// A task that represents an asynchronous send operation for delivering the message. The task result
/// contains the sent message.
///
public async Task ModifyOriginalResponseAsync(Action func, RequestOptions options = null)
{
var model = await InteractionHelper.ModifyInteractionResponseAsync(Discord, Token, func, options);
return RestInteractionMessage.Create(Discord, model, Token, Channel);
}
///
public abstract string RespondWithModal(Modal modal, RequestOptions options = null);
///
public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null);
///
/// Sends a followup message for this interaction.
///
/// The text of the message to be sent.
/// A array of embeds to send with this response. Max 10.
/// if the message should be read out by a text-to-speech reader, otherwise .
/// if the response should be hidden to everyone besides the invoker of the command, otherwise .
/// The allowed mentions for this response.
/// A to be sent with this response.
/// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.
/// The request options for this response.
///
/// A task that represents an asynchronous send operation for delivering the message. The task result
/// contains the sent message.
///
public abstract Task 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);
///
/// Sends a followup message for this interaction.
///
/// The text of the message to be sent.
/// The file to upload.
/// The file name of the attachment.
/// A array of embeds to send with this response. Max 10.
/// if the message should be read out by a text-to-speech reader, otherwise .
/// if the response should be hidden to everyone besides the invoker of the command, otherwise .
/// The allowed mentions for this response.
/// A to be sent with this response.
/// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.
/// The request options for this response.
///
/// A task that represents an asynchronous send operation for delivering the message. The task result
/// contains the sent message.
///
public abstract Task 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);
///
/// Sends a followup message for this interaction.
///
/// The text of the message to be sent.
/// The file to upload.
/// The file name of the attachment.
/// A array of embeds to send with this response. Max 10.
/// if the message should be read out by a text-to-speech reader, otherwise .
/// if the response should be hidden to everyone besides the invoker of the command, otherwise .
/// The allowed mentions for this response.
/// A to be sent with this response.
/// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.
/// The request options for this response.
///
/// A task that represents an asynchronous send operation for delivering the message. The task result
/// contains the sent message.
///
public abstract Task 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);
///
/// Sends a followup message for this interaction.
///
/// The attachment containing the file and description.
/// The text of the message to be sent.
/// A array of embeds to send with this response. Max 10.
/// if the message should be read out by a text-to-speech reader, otherwise .
/// if the response should be hidden to everyone besides the invoker of the command, otherwise .
/// The allowed mentions for this response.
/// The request options for this response.
/// A to be sent with this response.
/// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.
///
/// A task that represents an asynchronous send operation for delivering the message. The task result
/// contains the sent message.
///
public abstract Task 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);
///
/// Sends a followup message for this interaction.
///
/// A collection of attachments to upload.
/// The text of the message to be sent.
/// A array of embeds to send with this response. Max 10.
/// if the message should be read out by a text-to-speech reader, otherwise .
/// if the response should be hidden to everyone besides the invoker of the command, otherwise .
/// The allowed mentions for this response.
/// The request options for this response.
/// A to be sent with this response.
/// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.
///
/// A task that represents an asynchronous send operation for delivering the message. The task result
/// contains the sent message.
///
public abstract Task FollowupWithFilesAsync(IEnumerable 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);
///
public Task DeleteOriginalResponseAsync(RequestOptions options = null)
=> InteractionHelper.DeleteInteractionResponseAsync(Discord, this, options);
#region IDiscordInteraction
///
IUser IDiscordInteraction.User => User;
///
Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
=> Task.FromResult(Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options));
///
Task IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options)
=> Task.FromResult(Defer(ephemeral, options));
///
Task IDiscordInteraction.RespondWithModalAsync(Modal modal, RequestOptions options)
=> Task.FromResult(RespondWithModal(modal, options));
///
async Task 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);
///
async Task IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options)
=> await GetOriginalResponseAsync(options).ConfigureAwait(false);
///
async Task IDiscordInteraction.ModifyOriginalResponseAsync(Action func, RequestOptions options)
=> await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false);
///
async Task 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);
///
async Task 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);
///
async Task 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);
///
async Task IDiscordInteraction.FollowupWithFilesAsync(IEnumerable 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);
///
Task IDiscordInteraction.RespondWithFilesAsync(IEnumerable attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
#if NETCOREAPP3_0_OR_GREATER != true
///
Task IDiscordInteraction.RespondWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
///
Task IDiscordInteraction.RespondWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
///
Task IDiscordInteraction.RespondWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
#endif
#endregion
}
}