diff --git a/examples/userCommand.js b/examples/userCommand.js new file mode 100644 index 000000000..e60469304 --- /dev/null +++ b/examples/userCommand.js @@ -0,0 +1,61 @@ +const Eris = require("eris"); + +const Constants = Eris.Constants; + +const bot = new Eris.Client("Bot token", { + intents: [], +}); + +bot.on("ready", async () => { + console.log("connected!"); + bot.createCommand({ + type: Constants.ApplicationCommandTypes.CHAT_INPUT, + name: "ping", + description: "Reply with pong", + options: [], + /* The following 2 lines set the state of the application command as both user installable and guild installable */ + contexts: [ + Constants.ApplicationCommandContextType.GUILD, + Constants.ApplicationCommandContextType.BOT_DM, + Constants.ApplicationCommandContextType.PRIVATE, + ], + integrationTypes: [ + Constants.ApplicationCommandIntegrationTypes.GUILD_INSTALL, + Constants.ApplicationCommandIntegrationTypes.USER_INSTALL, + ], + }); +}); + +bot.on("interactionCreate", (interaction) => { + if (interaction instanceof Eris.CommandInteraction) { + if (interaction.data.name === "ping") { + let where = ""; + /** + * .context includes information of where the command was executed. + * if .context matches ... + * Constants.ApplicationCommandContextType.GUILD it means it is executed from a server + * Constants.ApplicationCommandContextType.BOT_DM it means it is executed from the bot's DM + * Constants.ApplicationCommandContextType.PRIVATE it means it is executed as a user installable + * + * Incase of BOT_DM and PRIVATE, the CommandInteraction will lose all its guild related properties such as the guild information and member data + */ + const context = interaction.context; + + // Check where the command is sent from. + // Keep in mind, context will be the same for commands that are ran as user-installables and guild-invited bots unless user-installables are ran in DMs or are sent in the bot's DMs + if (context === Constants.ApplicationCommandContextType.GUILD) { + where = "as a server interaction."; + } else if (context === Constants.ApplicationCommandContextType.BOT_DM) { + where = "as a bot DM interaction."; + } else if (context === Constants.ApplicationCommandContextType.PRIVATE) { + where = "as a private interaction (user installable)."; + } else { + where = "as a server interaction."; + } + + interaction.createMessage(`🏓 pong ${where}`); + } + } +}); + +bot.connect(); diff --git a/index.d.ts b/index.d.ts index 7734d865a..9bac2e921 100644 --- a/index.d.ts +++ b/index.d.ts @@ -20,6 +20,8 @@ declare namespace Eris { // TYPES // Application Commands + type ApplicationCommandContextTypes = (typeof Constants["ApplicationCommandContextType"][keyof typeof Constants["ApplicationCommandContextType"]]); + type ApplicationCommandIntegrationTypes = (typeof Constants["ApplicationCommandIntegrationTypes"][keyof typeof Constants["ApplicationCommandIntegrationTypes"]]); type ApplicationCommandOptions = ApplicationCommandOptionsSubCommand | ApplicationCommandOptionsSubCommandGroup | ApplicationCommandOptionsWithValue; type ApplicationCommandOptionsBoolean = ApplicationCommandOption; type ApplicationCommandOptionsChannel = ApplicationCommandOption; @@ -168,6 +170,7 @@ declare namespace Eris { type SelectMenuNonResolvedTypes = Constants["ComponentTypes"][keyof Pick]; type SelectMenuResolvedTypes = Constants["ComponentTypes"][keyof Pick]; type SelectMenuTypes = SelectMenuNonResolvedTypes | SelectMenuResolvedTypes; + type MessageInteractionMetadata = MessageComponentInteractionMetadata | ApplicationCommandInteractionMetadata | ModelSubmitInteractionMetadata; // Permission type PermissionType = Constants["PermissionOverwriteTypes"][keyof Constants["PermissionOverwriteTypes"]]; @@ -228,7 +231,9 @@ declare namespace Eris { } /** Generic T is `true` if creating Guild scoped commands, and `false` if not */ interface ApplicationCommandCreateOptions extends ApplicationCommandEditOptions { + contexts?: ApplicationCommandContextTypes[]; description: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : "" | void; + integrationTypes?: ApplicationCommandIntegrationTypes[]; name: string; type?: U; } @@ -1357,6 +1362,10 @@ declare namespace Eris { target_id?: string; options: InteractionDataOptions[]; } + interface AuthorizingIntegrationOwners { + guildInstall?: string; + userInstall: string; + } interface CommandInteractionData { id: string; name: string; @@ -1605,6 +1614,26 @@ declare namespace Eris { type: InteractionTypes; user: User; } + interface BaseMessageMetadata { + authorizingIntegrationOwners: AuthorizingIntegrationOwners; + id: string; + originalResponseMessageID?: string; + type: InteractionTypes; + user: User; + } + interface ApplicationCommandInteractionMetadata extends BaseMessageMetadata { + targetMessageID?: string; + targetUser?: User; + type: Constants["InteractionTypes"]["APPLICATION_COMMAND"]; + } + interface MessageComponentInteractionMetadata extends BaseMessageMetadata { + interactedMessageID: string; + type: Constants["InteractionTypes"]["MESSAGE_COMPONENT"]; + } + interface ModelSubmitInteractionMetadata extends BaseMessageMetadata { + triggeringInteractionMetadata: MessageComponentInteractionMetadata | ApplicationCommandInteractionMetadata; + type: Constants["InteractionTypes"]["MODAL_SUBMIT"]; + } interface MessageReference extends MessageReferenceBase { channelID: string; } @@ -2002,12 +2031,23 @@ declare namespace Eris { [key: string]: unknown; } interface OAuthApplicationInfo { + approximate_guild_count?: number; + approximate_user_install_count?: number; bot?: PartialUser; bot_public: boolean; bot_require_code_grant: boolean; + custom_install_url?: string; description: string; icon: string | null; id: string; + integration_types_config: { + 0?: OAuthApplicationIntegrationTypeConfiguration; + 1?: OAuthApplicationIntegrationTypeConfiguration; + }; // TODO: Configure types for this properly + install_params?: { + scopes: string[]; + permissions: string; + }; name: string; owner: PartialUser; privacy_policy_url?: string; @@ -2017,6 +2057,12 @@ declare namespace Eris { terms_of_service_url?: string; verify_key: string; } + interface OAuthApplicationIntegrationTypeConfiguration { + oauth2_install_params?: { + scopes: string[]; + permissions: string; + }; + } interface OAuthTeamInfo { icon: string | null; id: string; @@ -2050,6 +2096,7 @@ declare namespace Eris { /** Generic T is `true` if a Guild scoped command, and `false` if not */ export class ApplicationCommand extends Base { applicationID: string; + contexts?: ApplicationCommandContextTypes[] | null; defaultMemberPermissions: Permission; /** @deprecated */ defaultPermission?: boolean | null; @@ -2057,6 +2104,7 @@ declare namespace Eris { descriptionLocalizations?: U extends "CHAT_INPUT" ? Record | null : null; dmPermission?: boolean; guild: T extends true ? PossiblyUncachedGuild : never; + integrationTypes?: ApplicationCommandIntegrationTypes[]; name: string; nameLocalizations?: Record | null; nsfw?: boolean; @@ -2933,6 +2981,7 @@ declare namespace Eris { export class AutocompleteInteraction extends Interaction { appPermissions?: Permission; channel: T; + context: ApplicationCommandContextTypes; data: AutocompleteInteractionData; guildID: T extends AnyGuildChannel ? string : undefined; member: T extends AnyGuildChannel ? Member : undefined; @@ -2945,6 +2994,7 @@ declare namespace Eris { export class Interaction extends Base { acknowledged: boolean; applicationID: string; + authorizingIntegrationOwners: AuthorizingIntegrationOwners; id: string; token: string; type: number; @@ -2961,6 +3011,7 @@ declare namespace Eris { export class CommandInteraction extends Interaction { appPermissions?: Permission; channel: T; + context: ApplicationCommandContextTypes; data: CommandInteractionData; guildID: T extends AnyGuildChannel ? string : undefined; member: T extends AnyGuildChannel ? Member : undefined; @@ -2981,6 +3032,7 @@ declare namespace Eris { export class ComponentInteraction extends Interaction { appPermissions?: Permission; channel: T; + context: ApplicationCommandContextTypes; data: ComponentInteractionButtonData | ComponentInteractionSelectMenuData; guildID: T extends AnyGuildChannel ? string : undefined; member: T extends AnyGuildChannel ? Member : undefined; @@ -3120,7 +3172,9 @@ declare namespace Eris { flags: number; guildID: T extends GuildTextableWithThreads ? string : undefined; id: string; + /** @deprecated */ interaction: MessageInteraction | null; + interactionMetadata: MessageInteractionMetadata | null; jumpLink: string; member: T extends GuildTextableWithThreads ? Member : null; mentionEveryone: boolean; @@ -3161,6 +3215,7 @@ declare namespace Eris { export class ModalSubmitInteraction extends Interaction { channel: T; + context: ApplicationCommandContextTypes; data: ModalSubmitInteractionData; guildID: T extends AnyGuildChannel ? string : undefined; member: T extends AnyGuildChannel ? Member : undefined; diff --git a/lib/Client.js b/lib/Client.js index 9c8696776..89bb8a402 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -650,11 +650,13 @@ class Client extends EventEmitter { /** * Create a global application command * @arg {Object} command A command object + * @arg {Number[]?} [command.contexts] Configure if the command is a user-installable GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) * @arg {BigInt | Number | String | Permission?} command.defaultMemberPermissions The default permissions the user must have to use the command * @arg {Boolean} [command.defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description * @arg {Boolean?} [command.dmPermission=true] Whether the command is available in DMs with the app + * @arg {Number[]?} [command.integrationTypes] Configure the installation state of the slash command GUILD_INSTALL=0 USER_INSTALL=1 [more info](https://discord.com/developers/docs/resources/application#application-object-application-integration-types) * @arg {String} command.name The command name * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name * @arg {Boolean} [command.nsfw=false] Whether the command is age-restricted @@ -680,6 +682,7 @@ class Client extends EventEmitter { command.default_permission = command.defaultPermission; } command.dm_permission = command.dmPermission; + command.integration_types = command.integrationTypes; return this.requestHandler.request("POST", Endpoints.COMMANDS(this.application.id), true, command).then((command) => new ApplicationCommand(command, this)); } diff --git a/lib/Constants.d.ts b/lib/Constants.d.ts index 402157741..f229d36f9 100644 --- a/lib/Constants.d.ts +++ b/lib/Constants.d.ts @@ -22,6 +22,15 @@ export default interface Constants { CUSTOM: 4; COMPETING: 5; }; + ApplicationCommandContextType: { + GUILD: 0; + BOT_DM: 1; + PRIVATE: 2; + }; + ApplicationCommandIntegrationTypes: { + GUILD_INSTALL: 0; + USER_INSTALL: 1; + }; ApplicationCommandOptionTypes: { SUB_COMMAND: 1; SUB_COMMAND_GROUP: 2; @@ -578,11 +587,11 @@ export default interface Constants { sendVoiceMessages: 70368744177664n; setVoiceChannelStatus: 281474976710656n; sendPolls: 562949953421312n; + allGuild: 29697484783806n; + allText: 633854226857041n; + allVoice: 954930478188305n; + all: 985162418487295n; useExternalApps: 1125899906842624n; - allGuild: 1155597391626430n; - allText: 1759754133699665n; - allVoice: 2080830385030929n; - all: 2111062325329919n; }; PollLayoutTypes: { DEFAULT: 1; diff --git a/lib/Constants.js b/lib/Constants.js index c0de89331..e79e96d88 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -26,6 +26,17 @@ module.exports.ActivityTypes = { COMPETING: 5, }; +module.exports.ApplicationCommandContextType = { + GUILD: 0, + BOT_DM: 1, + PRIVATE: 2, +}; + +module.exports.ApplicationCommandIntegrationTypes = { + GUILD_INSTALL: 0, + USER_INSTALL: 1, +}; + module.exports.ApplicationCommandOptionTypes = { SUB_COMMAND: 1, SUB_COMMAND_GROUP: 2, diff --git a/lib/structures/ApplicationCommand.js b/lib/structures/ApplicationCommand.js index e3cd35aa4..4de7f5c81 100644 --- a/lib/structures/ApplicationCommand.js +++ b/lib/structures/ApplicationCommand.js @@ -6,6 +6,7 @@ const Permission = require("./Permission"); /** * Represents an application command * @prop {String} applicationID The ID of the application that this command belongs to + * @prop {Number[]?} contexts The context of the command (if the command is a user-installable or a guild-installable) * @prop {Permission?} defaultMemberPermissions The permissions required by default for this command to be usable * @prop {Boolean?} defaultPermission [DEPRECATED] Indicates whether the command is enabled by default when the application is added to a guild * @prop {String} description The description of the command (empty for user & message commands) @@ -13,6 +14,7 @@ const Permission = require("./Permission"); * @prop {Boolean?} dmPermission If this command can be used in direct messages (global commands only) * @prop {Guild?} guild The guild associated with this command (guild commands only) * @prop {String} id The ID of the application command + * @prop {Number[]?} integrationTypes The installation state of the slash command GUILD_INSTALL=0 USER_INSTALL=1 [more info](https://discord.com/developers/docs/resources/application#application-object-application-integration-types) * @prop {String} name The name of the command * @prop {Object?} nameLocalizations A map of [locales](https://discord.com/developers/docs/reference#locales) to names for that locale * @prop {Boolean?} nsfw Indicates whether the command is age-restricted @@ -56,6 +58,12 @@ class ApplicationCommand extends Base { if (data.nsfw !== undefined) { this.nsfw = data.nsfw; } + if (data.contexts !== undefined) { + this.contexts = data.contexts; + } + if (data.integration_types !== undefined) { + this.integrationTypes = data.integration_types; + } } /** diff --git a/lib/structures/AutocompleteInteraction.js b/lib/structures/AutocompleteInteraction.js index 283053e5d..88a8aad78 100644 --- a/lib/structures/AutocompleteInteraction.js +++ b/lib/structures/AutocompleteInteraction.js @@ -2,6 +2,7 @@ const Interaction = require("./Interaction"); const Permission = require("./Permission"); +const Guild = require("./Guild"); const { InteractionResponseTypes } = require("../Constants"); /** @@ -9,6 +10,7 @@ const { InteractionResponseTypes } = require("../Constants"); * @extends Interaction * @prop {Permission?} appPermissions The permissions the app or bot has within the channel the interaction was sent from * @prop {DMChannel | TextChannel | NewsChannel} channel The channel the interaction was created in. Can be partial with only the id if the channel is not cached + * @prop {Number} context Where the interaction is sent from GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) * @prop {Object} data The data attached to the interaction * @prop {String} data.id The ID of the Application Command * @prop {String} data.name The command name @@ -32,6 +34,7 @@ class AutocompleteInteraction extends Interaction { id: data.channel_id, }; + this.context = data.context; this.data = data.data; if (data.guild_id !== undefined) { @@ -43,7 +46,7 @@ class AutocompleteInteraction extends Interaction { if (this.channel.guild) { this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this._client.guilds.get(data.guild_id) || new Guild(data.guild, this._client); this.member = guild.members.update(data.member, guild); } } diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index a28ce5941..980a863d5 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -8,6 +8,7 @@ const Channel = require("./Channel"); const Message = require("./Message"); const Collection = require("../util/Collection"); const Permission = require("./Permission"); +const Guild = require("./Guild"); const { InteractionResponseTypes } = require("../Constants"); @@ -16,6 +17,7 @@ const { InteractionResponseTypes } = require("../Constants"); * @extends Interaction * @prop {Permission?} appPermissions The permissions the app or bot has within the channel the interaction was sent from * @prop {DMChannel | TextChannel | NewsChannel} channel The channel the interaction was created in. Can be partial with only the id if the channel is not cached + * @prop {Number} context Where the interaction is sent from GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) * @prop {Object} data The data attached to the interaction * @prop {String} data.id The ID of the Application Command * @prop {String} data.name The command name @@ -44,6 +46,7 @@ class CommandInteraction extends Interaction { }; this.data = JSON.parse(JSON.stringify(data.data)); + this.context = data.context; if (data.data.resolved !== undefined) { // Users @@ -104,7 +107,7 @@ class CommandInteraction extends Interaction { if (this.channel.guild) { this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this._client.guilds.get(data.guild_id) || new Guild(data.guild, this._client); this.member = guild.members.update(data.member, guild); } } diff --git a/lib/structures/ComponentInteraction.js b/lib/structures/ComponentInteraction.js index 6fcad5dcd..8e14b160d 100644 --- a/lib/structures/ComponentInteraction.js +++ b/lib/structures/ComponentInteraction.js @@ -8,6 +8,7 @@ const Member = require("./Member"); const Channel = require("./Channel"); const Message = require("./Message"); const Permission = require("./Permission"); +const Guild = require("./Guild"); const { InteractionResponseTypes } = require("../Constants"); /** @@ -15,6 +16,7 @@ const { InteractionResponseTypes } = require("../Constants"); * @extends Interaction * @prop {Permission?} appPermissions The permissions the app or bot has within the channel the interaction was sent from * @prop {DMChannel | TextChannel | NewsChannel} channel The channel the interaction was created in. Can be partial with only the id if the channel is not cached + * @prop {Number} context Where the component interaction is selected from. [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) * @prop {Object} data The data attached to the interaction * @prop {Number} data.component_type The type of Message Component * @prop {String} data.custom_id The ID of the Message Component @@ -37,6 +39,8 @@ class ComponentInteraction extends Interaction { id: data.channel_id, }; + this.context = data.context; + this.data = JSON.parse(JSON.stringify(data.data)); if (data.data.resolved !== undefined) { @@ -89,7 +93,7 @@ class ComponentInteraction extends Interaction { if (this.channel.guild) { this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this._client.guilds.get(data.guild_id) || new Guild(data.guild, this._client); this.member = guild.members.update(data.member, guild); } } diff --git a/lib/structures/Interaction.js b/lib/structures/Interaction.js index 04b54519f..55ed0ab9a 100644 --- a/lib/structures/Interaction.js +++ b/lib/structures/Interaction.js @@ -1,12 +1,13 @@ "use strict"; const Base = require("./Base"); -const { InteractionTypes } = require("../Constants"); +const { InteractionTypes, ApplicationCommandIntegrationTypes } = require("../Constants"); /** * Represents an interaction. You also probably want to look at PingInteraction, CommandInteraction, ComponentInteraction, AutocompleteInteraction, ModalSubmitInteraction, and UnknownInteraction. * @prop {Boolean} acknowledged Whether or not the interaction has been acknowledged * @prop {String} applicationID The ID of the interaction's application + * @prop {Object} authorizingIntegrationOwners An object containing the values IDs of the user and/or guild that authorized the app. guildInstall for GuildID and userInstall for UserID * @prop {Number} createdAt Timestamp of the interaction's creation * @prop {String} id The ID of the interaction * @prop {String} token The interaction token (Interaction tokens are valid for 15 minutes after initial response and can be used to send followup messages but you must send an initial response within 3 seconds of receiving the event. If the 3 second deadline is exceeded, the token will be invalidated.) @@ -19,6 +20,10 @@ class Interaction extends Base { this._client = client; this.applicationID = data.application_id; + this.authorizingIntegrationOwners = { + userInstall: data.authorizing_integration_owners[ApplicationCommandIntegrationTypes.USER_INSTALL], + guildInstall: data.authorizing_integration_owners[ApplicationCommandIntegrationTypes.GUILD_INSTALL], + }; this.token = data.token; this.type = data.type; this.version = data.version; diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 0f3d8aed3..f806ee8b7 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -2,9 +2,67 @@ const Base = require("./Base"); const Endpoints = require("../rest/Endpoints"); -const { MessageFlags } = require("../Constants"); +const { MessageFlags, InteractionTypes } = require("../Constants"); const User = require("./User"); +function parseInteractionMetadata(client, data) { + switch (data.type) { + case InteractionTypes.APPLICATION_COMMAND: { + return parseMetadataApplicationCommand(client, data); + } + case InteractionTypes.MESSAGE_COMPONENT: { + return parseMetadataMessageComponent(client, data); + } + default: { // model submit + return parseMetadataModelSubmit(client, data); + } + } +} + +function parseBaseMetadata(client, data) { + const baseMetadata = { + authorizingIntegrationOwners: data.authorizing_integration_owners, + id: data.id, + originalResponseMessageID: data.original_response_message_id, + type: data.type, + user: client.users.update(data.user, client), + }; + + return baseMetadata; +} + +function parseMetadataApplicationCommand(client, data) { + const baseData = parseBaseMetadata(client, data); + if (data.target_user) { + baseData.targetUser = client.users.update(data.target_user, client); + } + baseData.targetMessageID = data.target_message_id; + return baseData; +} + +function parseMetadataMessageComponent(client, data) { + const baseData = parseBaseMetadata(client, data); + return { + ...baseData, + interactedMessageID: data.interacted_message_id, + }; +} + +function parseMetadataModelSubmit(client, data) { + const baseData = parseBaseMetadata(client, data); + + switch (data.triggering_interaction_metadata.type) { + case InteractionTypes.APPLICATION_COMMAND: + baseData.triggeringInteractionMetadata = parseMetadataApplicationCommand(client, data.triggering_interaction_metadata); + break; + default: // type message component + baseData.triggeringInteractionMetadata = parseMetadataMessageComponent(client, data.triggering_interaction_metadata); + break; + } + + return baseData; +} + /** * Represents a message * @prop {Object?} activity The activity specified in the message @@ -13,7 +71,7 @@ const User = require("./User"); * @prop {Array} attachments Array of attachments * @prop {User} author The message author * @prop {Object} call The call associated with the message - * @prop {Number?} call.endedTimestamp The time when the call ended + * @prop {Number?} call.endedTimestamp The time when the call end * @prop {Array} call.participants An array of user IDs that participated in the call * @prop {DMChannel | TextChannel | NewsChannel} channel The channel the message is in. Can be partial with only the id if the channel is not cached. * @prop {Array} channelMentions Array of mentions channels' ids @@ -27,12 +85,28 @@ const User = require("./User"); * @prop {Number} flags Message flags (see constants) * @prop {String?} guildID The ID of the guild this message is in (undefined if in DMs) * @prop {String} id The ID of the message - * @prop {Object?} interaction An object containing info about the interaction the message is responding to, if applicable + * @prop {Object?} interaction [DEPRECATED] An object containing info about the interaction the message is responding to, if applicable * @prop {String} interaction.id The ID of the interaction * @prop {Member?} interaction.member The member who invoked the interaction * @prop {String} interaction.name The name of the command * @prop {Number} interaction.type The type of interaction * @prop {User} interaction.user The user who invoked the interaction + * @prop {Object?} interactionMetadata An object containing info about the interaction the message is responding to, if applicable + * @prop {String} interactionMetadata.authorizingIntegrationOwners authorizingIntegrationOwners An object containing the values IDs of the user and/or guild that authorized the app. guildInstall for GuildID and userInstall for UserID + * @prop {String} interactionMetadata.id The ID of the interaction that the message is replying to + * @prop {string?} interactionMetadata.interactedMessageID ID of the message that contained the interactive component + * @prop {String} interactionMetadata.originalResponseMessageID The ID of the original message, if applicable + * @prop {Number} interactionMetadata.type The type of the interaction + * @prop {Object?} interactionMetadata.targetMessageID The ID of the message the command was run on, present only on message command interactions. + * @prop {Object?} interactionMetadata.targetUser The user the command was run on, present only on user command interaction + * @prop {Object?} interactionMetadata.triggeringInteractionMetadata Metadata for the interaction that was used to open the modal, of type `MessageComponentInteractionMetadata` or `ApplicationCommandInteractionMetadata`; + * @prop {Object} interactionMetadata.user The user who originally used the interaction + * @prop {String} interactionMetadata.triggeringInteractionMetadata.id The ID of the interaction that the message is replying to + * @prop {string?} interactionMetadata.triggeringInteractionMetadata.interactedMessageID ID of the message that contained the interactive component + * @prop {Object} interactionMetadata.triggeringInteractionMetadata.user The user who originally used the interaction + * @prop {Number} interactionMetadata.triggeringInteractionMetadata.type The type of the interaction + * @prop {Object?} interactionMetadata.triggeringInteractionMetadata.targetMessageID The ID of the message the command was run on, present only on message command interactions. + * @prop {Object?} interactionMetadata.triggeringInteractionMetadata.targetUser The user the command was run on, present only on user command interaction * @prop {String} jumpLink The url used by Discord clients to jump to this message * @prop {Member?} member The message author with server-specific data * @prop {Boolean} mentionEveryone Whether the message mentions everyone/here or not @@ -149,6 +223,12 @@ class Message extends Base { this.interaction = null; } + if (data.interaction_metadata) { + this.interactionMetadata = parseInteractionMetadata(this._client, data.interaction_metadata); + } else { + this.interactionMetadata = null; + } + if (this.channel.guild) { if (data.member) { data.member.id = this.author.id; diff --git a/lib/structures/ModalSubmitInteraction.js b/lib/structures/ModalSubmitInteraction.js index 46dd6c192..89aee47ae 100644 --- a/lib/structures/ModalSubmitInteraction.js +++ b/lib/structures/ModalSubmitInteraction.js @@ -1,12 +1,14 @@ "use strict"; const Interaction = require("./Interaction"); +const Guild = require("./Guild"); const { InteractionResponseTypes } = require("../Constants"); /** * Represents a modal submit interaction. See Interaction for more properties * @extends Interaction * @prop {DMChannel | TextChannel | NewsChannel} channel The channel the interaction was created in. Can be partial with only the id if the channel is not cached + * @prop {Number} context Where the interaction is sent from GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) * @prop {Object} data The data attached to the interaction * @prop {String} data.custom_id The ID of the Modal * @prop {Array} data.components The values submitted by the user @@ -23,6 +25,7 @@ class ModalSubmitInteraction extends Interaction { }; this.data = data.data; + this.context = data.context; if (data.guild_id !== undefined) { this.guildID = data.guild_id; @@ -33,7 +36,7 @@ class ModalSubmitInteraction extends Interaction { if (this.channel.guild) { this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this._client.guilds.get(data.guild_id) || new Guild(data.guild, this._client); this.member = guild.members.update(data.member, guild); } }