@@ -16,6 +16,10 @@ namespace Discord.Rest
/// </summary>
public abstract class RestInteraction : RestEntity<ulong>, IDiscordInteraction
{
// Added so channel & guild methods don't need a client reference
private Func<RequestOptions, ulong, Task<IRestMessageChannel>> _getChannel = null;
private Func<RequestOptions, ulong, Task<RestGuild>> _getGuild = null;
/// <inheritdoc/>
public InteractionType Type { get; private set; }
@@ -31,6 +35,10 @@ namespace Discord.Rest
/// <summary>
/// Gets the user who invoked the interaction.
/// </summary>
/// <remarks>
/// If this user is an <see cref="RestGuildUser"/> and <see cref="DiscordRestConfig.APIOnRestInteractionCreation"/> is set to false,
/// <see cref="RestGuildUser.Guild"/> will return <see langword="null"/>
/// </remarks>
public RestUser User { get; private set; }
/// <inheritdoc/>
@@ -48,14 +56,38 @@ namespace Discord.Rest
public bool IsValidToken
=> InteractionHelper.CanRespondOrFollowup(this);
/// <summary>
/// Gets the ID of the channel this interaction was executed in.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the interaction was not executed in a guild.
/// </remarks>
public ulong? ChannelId { get; private set; } = null;
/// <summary>
/// Gets the channel that this interaction was executed in.
/// </summary>
/// <remarks>
/// <see langword="null"/> if <see cref="DiscordRestConfig.APIOnRestInteractionCreation"/> is set to false.
/// Call <see cref="GetChannelAsync"/> to set this property and get the interaction channel.
/// </remarks>
public IRestMessageChannel Channel { get; private set; }
/// <summary>
/// Gets the guild this interaction was executed in.
/// Gets the ID of the guild this interaction was executed in if applicable .
/// </summary>
/// <remarks>
/// <see langword="null"/> if the interaction was not executed in a guild.
/// </remarks>
public ulong? GuildId { get; private set; } = null;
/// <summary>
/// Gets the guild this interaction was executed in if applicable.
/// </summary>
/// <remarks>
/// This property will be <see langword="null"/> if <see cref="DiscordRestConfig.APIOnRestInteractionCreation"/> is set to false
/// or if the interaction was not executed in a guild.
/// </remarks>
public RestGuild Guild { get; private set; }
/// <inheritdoc/>
@@ -72,11 +104,11 @@ namespace Discord.Rest
: DateTime.UtcNow;
}
internal static async Task<RestInteraction> CreateAsync(DiscordRestClient client, Model model)
internal static async Task<RestInteraction> CreateAsync(DiscordRestClient client, Model model, bool doApiCall )
{
if(model.Type == InteractionType.Ping)
{
return await RestPingInteraction.CreateAsync(client, model);
return await RestPingInteraction.CreateAsync(client, model, doApiCall );
}
if (model.Type == InteractionType.ApplicationCommand)
@@ -90,26 +122,26 @@ namespace Discord.Rest
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),
ApplicationCommandType.Slash => await RestSlashCommand.CreateAsync(client, model, doApiCall ).ConfigureAwait(false),
ApplicationCommandType.Message => await RestMessageCommand.CreateAsync(client, model, doApiCall ).ConfigureAwait(false),
ApplicationCommandType.User => await RestUserCommand.CreateAsync(client, model, doApiCall ).ConfigureAwait(false),
_ => null
};
}
if (model.Type == InteractionType.MessageComponent)
return await RestMessageComponent.CreateAsync(client, model).ConfigureAwait(false);
return await RestMessageComponent.CreateAsync(client, model, doApiCall ).ConfigureAwait(false);
if (model.Type == InteractionType.ApplicationCommandAutocomplete)
return await RestAutocompleteInteraction.CreateAsync(client, model).ConfigureAwait(false);
return await RestAutocompleteInteraction.CreateAsync(client, model, doApiCall ).ConfigureAwait(false);
if (model.Type == InteractionType.ModalSubmit)
return await RestModal.CreateAsync(client, model).ConfigureAwait(false);
return await RestModal.CreateAsync(client, model, doApiCall ).ConfigureAwait(false);
return null;
}
internal virtual async Task UpdateAsync(DiscordRestClient discord, Model model)
internal virtual async Task UpdateAsync(DiscordRestClient discord, Model model, bool doApiCall )
{
IsDMInteraction = !model.GuildId.IsSpecified;
@@ -120,16 +152,23 @@ namespace Discord.Rest
Version = model.Version;
Type = model.Type;
if(Guild == null && model.GuildId.IsSpecified)
if (Guild == null && model.GuildId.IsSpecified)
{
Guild = await discord.GetGuildAsync(model.GuildId.Value);
GuildId = model.GuildId.Value;
if (doApiCall)
Guild = await discord.GetGuildAsync(model.GuildId.Value);
else
{
Guild = null;
_getGuild = new(async (opt, ul) => await discord.GetGuildAsync(ul, opt));
}
}
if (User == null)
{
if (model.Member.IsSpecified && model.GuildId.IsSpecified)
{
User = RestGuildUser.Create(Discord, Guild, model.Member.Value);
User = RestGuildUser.Create(Discord, Guild, model.Member.Value, (Guild is null) ? model.GuildId.Value : null );
}
else
{
@@ -137,18 +176,33 @@ namespace Discord.Rest
}
}
if(Channel == null && model.ChannelId.IsSpecified)
if (Channel == null && model.ChannelId.IsSpecified)
{
try
{
Channel = (IRestMessageChannel)await discord.GetChannelAsync(model.ChannelId.Value);
ChannelId = model.ChannelId.Value;
if (doApiCall)
Channel = (IRestMessageChannel)await discord.GetChannelAsync(model.ChannelId.Value);
else
{
_getChannel = new(async (opt, ul) =>
{
if (Guild is null)
return (IRestMessageChannel)await discord.GetChannelAsync(ul, opt);
else // get a guild channel if the guild is set.
return (IRestMessageChannel)await Guild.GetChannelAsync(ul, opt);
});
Channel = null;
}
}
catch(HttpException x) when(x.DiscordCode == DiscordErrorCode.MissingPermissions) { } // ignore
catch (HttpException x) when (x.DiscordCode == DiscordErrorCode.MissingPermissions) { } // ignore
}
UserLocale = model.UserLocale.IsSpecified
? model.UserLocale.Value
: null;
? model.UserLocale.Value
: null;
GuildLocale = model.GuildLocale.IsSpecified
? model.GuildLocale.Value
: null;
@@ -164,6 +218,59 @@ namespace Discord.Rest
return json.ToString();
}
/// <summary>
/// Gets the channel this interaction was executed in. Will be a DM channel if the interaction was executed in DM.
/// </summary>
/// <remarks>
/// Calling this method succesfully will populate the <see cref="Channel"/> property.
/// After this, further calls to this method will no longer call the API, and depend on the value set in <see cref="Channel"/>.
/// </remarks>
/// <param name="options">The request options for this <see langword="async"/> request.</param>
/// <returns>A Rest channel to send messages to.</returns>
/// <exception cref="InvalidOperationException">Thrown if no channel can be received.</exception>
public async Task<IRestMessageChannel> GetChannelAsync(RequestOptions options = null)
{
if (IsDMInteraction && Channel is null)
{
var channel = await User.CreateDMChannelAsync(options);
Channel = channel;
}
else if (Channel is null)
{
var channel = await _getChannel(options, ChannelId.Value);
if (channel is null)
throw new InvalidOperationException("The interaction channel was not able to be retrieved.");
Channel = channel;
_getChannel = null; // get rid of it, we don't need it anymore.
}
return Channel;
}
/// <summary>
/// Gets the guild this interaction was executed in if applicable.
/// </summary>
/// <remarks>
/// Calling this method succesfully will populate the <see cref="Guild"/> property.
/// After this, further calls to this method will no longer call the API, and depend on the value set in <see cref="Guild"/>.
/// </remarks>
/// <param name="options">The request options for this <see langword="async"/> request.</param>
/// <returns>The guild this interaction was executed in. <see langword="null"/> if the interaction was executed inside DM.</returns>
public async Task<RestGuild> GetGuildAsync(RequestOptions options)
{
if (IsDMInteraction)
return null;
if (Guild is null)
Guild = await _getGuild(options, GuildId.Value);
_getGuild = null; // get rid of it, we don't need it anymore.
return Guild;
}
/// <inheritdoc/>
public abstract string Defer(bool ephemeral = false, RequestOptions options = null);
/// <summary>