Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: entry-point command #10640

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions packages/discord.js/src/client/actions/InteractionCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ChatInputCommandInteraction = require('../../structures/ChatInputCommandIn
const MentionableSelectMenuInteraction = require('../../structures/MentionableSelectMenuInteraction');
const MessageContextMenuCommandInteraction = require('../../structures/MessageContextMenuCommandInteraction');
const ModalSubmitInteraction = require('../../structures/ModalSubmitInteraction');
const PrimaryEntryPointCommandInteraction = require('../../structures/PrimaryEntryPointCommandInteraction');
const RoleSelectMenuInteraction = require('../../structures/RoleSelectMenuInteraction');
const StringSelectMenuInteraction = require('../../structures/StringSelectMenuInteraction');
const UserContextMenuCommandInteraction = require('../../structures/UserContextMenuCommandInteraction');
Expand Down Expand Up @@ -38,6 +39,9 @@ class InteractionCreateAction extends Action {
if (channel && !channel.isTextBased()) return;
InteractionClass = MessageContextMenuCommandInteraction;
break;
case ApplicationCommandType.PrimaryEntryPoint:
InteractionClass = PrimaryEntryPointCommandInteraction;
break;
default:
client.emit(
Events.Debug,
Expand Down
1 change: 1 addition & 0 deletions packages/discord.js/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ exports.PartialGroupDMChannel = require('./structures/PartialGroupDMChannel');
exports.PermissionOverwrites = require('./structures/PermissionOverwrites');
exports.Poll = require('./structures/Poll').Poll;
exports.PollAnswer = require('./structures/PollAnswer').PollAnswer;
exports.PrimaryEntryPointCommandInteraction = require('./structures/PrimaryEntryPointCommandInteraction');
exports.Presence = require('./structures/Presence').Presence;
exports.ReactionCollector = require('./structures/ReactionCollector');
exports.ReactionEmoji = require('./structures/ReactionEmoji');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ class ApplicationCommandManager extends CachedManager {
default_member_permissions,
integration_types: command.integrationTypes ?? command.integration_types,
contexts: command.contexts,
handler: command.handler,
};
}
}
Expand Down
29 changes: 26 additions & 3 deletions packages/discord.js/src/structures/ApplicationCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@ class ApplicationCommand extends Base {
this.contexts ??= null;
}

if ('handler' in data) {
/**
* Determines whether the interaction is handled by the app's interactions handler or by Discord.
* <info>Only available for {@link ApplicationCommandType.PrimaryEntryPoint} command
* and for apps with {@link ApplicationFlags.Embedded} flag (i.e, applications that have an Activity)
* </info>
* @type {?EntryPointCommandHandlerType}
*/
this.handler = data.handler;
} else {
this.handler ??= null;
}

if ('version' in data) {
/**
* Autoincrementing version identifier updated during substantial record changes
Expand Down Expand Up @@ -204,14 +217,18 @@ class ApplicationCommand extends Base {
* @property {string} name The name of the command, must be in all lowercase if type is
* {@link ApplicationCommandType.ChatInput}
* @property {Object<Locale, string>} [nameLocalizations] The localizations for the command name
* @property {string} description The description of the command, if type is {@link ApplicationCommandType.ChatInput}
* @property {string} description The description of the command,
* if type is {@link ApplicationCommandType.ChatInput} or {@link ApplicationCommandType.PrimaryEntryPoint}
* @property {boolean} [nsfw] Whether the command is age-restricted
* @property {Object<Locale, string>} [descriptionLocalizations] The localizations for the command description,
* if type is {@link ApplicationCommandType.ChatInput}
* if type is {@link ApplicationCommandType.ChatInput} or {@link ApplicationCommandType.PrimaryEntryPoint}
* @property {ApplicationCommandType} [type=ApplicationCommandType.ChatInput] The type of the command
* @property {ApplicationCommandOptionData[]} [options] Options for the command
* @property {?PermissionResolvable} [defaultMemberPermissions] The bitfield used to determine the default permissions
* a member needs in order to run the command
* @property {ApplicationIntegrationType[]} [integrationTypes] Installation context(s) where the command is available
* @property {InteractionContextType[]} [contexts] Interaction context(s) where the command can be used
* @property {EntryPointCommandHandlerType} [handler] Determines whether the interaction is handled by the app's
*/

/**
Expand Down Expand Up @@ -395,7 +412,8 @@ class ApplicationCommand extends Base {
this.descriptionLocalizations ?? {},
) ||
!isEqual(command.integrationTypes ?? command.integration_types ?? [], this.integrationTypes ?? []) ||
!isEqual(command.contexts ?? [], this.contexts ?? [])
!isEqual(command.contexts ?? [], this.contexts ?? []) ||
('handler' in command && command.handler !== this.handler)
) {
return false;
}
Expand Down Expand Up @@ -595,3 +613,8 @@ module.exports = ApplicationCommand;
* @external ApplicationCommandOptionAllowedChannelTypes
* @see {@link https://discord.js.org/docs/packages/builders/stable/ApplicationCommandOptionAllowedChannelTypes:TypeAlias}
*/

/**
* @external EntryPointCommandHandlerType
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/EntryPointCommandHandlerType}
*/
10 changes: 10 additions & 0 deletions packages/discord.js/src/structures/BaseInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,16 @@ class BaseInteraction extends Base {
);
}

/**
* Indicates whether this interaction is a {@link PrimaryEntryPointCommandInteraction}
* @returns {boolean}
*/
isPrimaryEntryPointCommand() {
return (
this.type === InteractionType.ApplicationCommand && this.commandType === ApplicationCommandType.PrimaryEntryPoint
);
}

/**
* Indicates whether this interaction is a {@link MessageComponentInteraction}
* @returns {boolean}
Expand Down
1 change: 1 addition & 0 deletions packages/discord.js/src/structures/CommandInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class CommandInteraction extends BaseInteraction {
editReply() {}
deleteReply() {}
followUp() {}
launchActivity() {}
showModal() {}
awaitModalSubmit() {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class MessageComponentInteraction extends BaseInteraction {
followUp() {}
deferUpdate() {}
update() {}
launchActivity() {}
showModal() {}
awaitModalSubmit() {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class ModalSubmitInteraction extends BaseInteraction {
followUp() {}
deferUpdate() {}
update() {}
launchActivity() {}
}

InteractionResponses.applyToClass(ModalSubmitInteraction, 'showModal');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

const CommandInteraction = require('./CommandInteraction');
/**
* Represents a primary entry point command interaction.
* @extends {CommandInteraction}
*/
class PrimaryEntryPointCommandInteraction extends CommandInteraction {}

module.exports = PrimaryEntryPointCommandInteraction;
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class InteractionResponses {
* @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response
*/

/**
* Options for launching activity in response to a {@link BaseInteraction}
* @typedef {Object} LaunchActivityOptions
* @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response
*/

/**
* Options for showing a modal in response to a {@link BaseInteraction}
* @typedef {Object} ShowModalOptions
Expand Down Expand Up @@ -265,6 +271,25 @@ class InteractionResponses {
return options.withResponse ? new InteractionCallbackResponse(this.client, response) : undefined;
}

/**
* Launches activity, if enabled
* @param {LaunchActivityOptions} [options={}] Options for launching the activity
* @returns {Promise<InteractionCallbackResponse|undefined>}
*/
async launchActivity({ withResponse } = {}) {
if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied);
const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
query: makeURLSearchParams({ with_response: withResponse ?? false }),
body: {
type: InteractionResponseType.LaunchActivity,
},
auth: false,
});
this.replied = true;

return withResponse ? new InteractionCallbackResponse(this.client, response) : undefined;
}

/**
* Shows a modal component
* @param {ModalBuilder|ModalComponentData|APIModalInteractionResponseCallbackData} modal The modal to show
Expand Down Expand Up @@ -328,6 +353,7 @@ class InteractionResponses {
'followUp',
'deferUpdate',
'update',
'launchActivity',
'showModal',
'awaitModalSubmit',
];
Expand Down
71 changes: 53 additions & 18 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ import {
RESTAPIInteractionCallbackActivityInstanceResource,
VoiceChannelEffectSendAnimationType,
GatewayVoiceChannelEffectSendDispatchData,
EntryPointCommandHandlerType,
APIPrimaryEntryPointCommandInteractionData,
} from 'discord-api-types/v10';
import { ChildProcess } from 'node:child_process';
import { EventEmitter } from 'node:events';
Expand Down Expand Up @@ -439,6 +441,7 @@ export class ApplicationCommand<PermissionsFetchType = {}> extends Base {
public get manager(): ApplicationCommandManager;
public id: Snowflake;
public integrationTypes: ApplicationIntegrationType[] | null;
public handler: EntryPointCommandHandlerType | null;
public name: string;
public nameLocalizations: LocalizationMap | null;
public nameLocalized: string | null;
Expand Down Expand Up @@ -541,23 +544,6 @@ export type BooleanCache<Cached extends CacheType> = Cached extends 'cached' ? t
export abstract class CommandInteraction<Cached extends CacheType = CacheType> extends BaseInteraction<Cached> {
public type: InteractionType.ApplicationCommand;
public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null;
public options: Omit<
CommandInteractionOptionResolver<Cached>,
| 'getMessage'
| 'getFocused'
| 'getMentionable'
| 'getRole'
| 'getUser'
| 'getMember'
| 'getAttachment'
| 'getNumber'
| 'getInteger'
| 'getString'
| 'getChannel'
| 'getBoolean'
| 'getSubcommandGroup'
| 'getSubcommand'
>;
public channelId: Snowflake;
public commandId: Snowflake;
public commandName: string;
Expand Down Expand Up @@ -586,6 +572,9 @@ export abstract class CommandInteraction<Cached extends CacheType = CacheType> e
public reply(
options: string | MessagePayload | InteractionReplyOptions,
): Promise<InteractionCallbackResponse | undefined>;
public launchActivity(options: LaunchActivityOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public launchActivity(options?: LaunchActivityOptions & { withResponse?: false }): Promise<undefined>;
public launchActivity(options?: LaunchActivityOptions): Promise<InteractionCallbackResponse | undefined>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
Expand Down Expand Up @@ -1301,6 +1290,23 @@ export class CommandInteractionOptionResolver<Cached extends CacheType = CacheTy
}

export class ContextMenuCommandInteraction<Cached extends CacheType = CacheType> extends CommandInteraction<Cached> {
public options: Omit<
CommandInteractionOptionResolver<Cached>,
| 'getMessage'
| 'getFocused'
| 'getMentionable'
| 'getRole'
| 'getUser'
| 'getMember'
| 'getAttachment'
| 'getNumber'
| 'getInteger'
| 'getString'
| 'getChannel'
| 'getBoolean'
| 'getSubcommandGroup'
| 'getSubcommand'
>;
public commandType: ApplicationCommandType.Message | ApplicationCommandType.User;
public targetId: Snowflake;
public inGuild(): this is ContextMenuCommandInteraction<'raw' | 'cached'>;
Expand All @@ -1309,6 +1315,15 @@ export class ContextMenuCommandInteraction<Cached extends CacheType = CacheType>
private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption<Cached>[];
}

export class PrimaryEntryPointCommandInteraction<
Cached extends CacheType = CacheType,
> extends CommandInteraction<Cached> {
public commandType: ApplicationCommandType.PrimaryEntryPoint;
public inGuild(): this is PrimaryEntryPointCommandInteraction<'raw' | 'cached'>;
public inCachedGuild(): this is PrimaryEntryPointCommandInteraction<'cached'>;
public inRawGuild(): this is PrimaryEntryPointCommandInteraction<'raw'>;
}

// tslint:disable-next-line no-empty-interface
export interface DMChannel
extends Omit<
Expand Down Expand Up @@ -1922,6 +1937,7 @@ export type Interaction<Cached extends CacheType = CacheType> =
| ChatInputCommandInteraction<Cached>
| MessageContextMenuCommandInteraction<Cached>
| UserContextMenuCommandInteraction<Cached>
| PrimaryEntryPointCommandInteraction<Cached>
| SelectMenuInteraction<Cached>
| ButtonInteraction<Cached>
| AutocompleteInteraction<Cached>
Expand Down Expand Up @@ -1970,6 +1986,7 @@ export class BaseInteraction<Cached extends CacheType = CacheType> extends Base
public isChatInputCommand(): this is ChatInputCommandInteraction<Cached>;
public isCommand(): this is CommandInteraction<Cached>;
public isContextMenuCommand(): this is ContextMenuCommandInteraction<Cached>;
public isPrimaryEntryPointCommand(): this is PrimaryEntryPointCommandInteraction<Cached>;
public isMessageComponent(): this is MessageComponentInteraction<Cached>;
public isMessageContextMenuCommand(): this is MessageContextMenuCommandInteraction<Cached>;
public isModalSubmit(): this is ModalSubmitInteraction<Cached>;
Expand Down Expand Up @@ -2347,6 +2364,9 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
public update(
options: string | MessagePayload | InteractionUpdateOptions,
): Promise<InteractionCallbackResponse | undefined>;
public launchActivity(options: LaunchActivityOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public launchActivity(options?: LaunchActivityOptions & { withResponse?: false }): Promise<undefined>;
public launchActivity(options?: LaunchActivityOptions): Promise<InteractionCallbackResponse | undefined>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
Expand Down Expand Up @@ -2575,6 +2595,9 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
): Promise<InteractionCallbackResponse>;
public deferUpdate(options?: InteractionDeferUpdateOptions & { withResponse: false }): Promise<undefined>;
public deferUpdate(options?: InteractionDeferUpdateOptions): Promise<InteractionCallbackResponse | undefined>;
public launchActivity(options: LaunchActivityOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public launchActivity(options?: LaunchActivityOptions & { withResponse?: false }): Promise<undefined>;
public launchActivity(options?: LaunchActivityOptions): Promise<InteractionCallbackResponse | undefined>;
public inGuild(): this is ModalSubmitInteraction<'raw' | 'cached'>;
public inCachedGuild(): this is ModalSubmitInteraction<'cached'>;
public inRawGuild(): this is ModalSubmitInteraction<'raw'>;
Expand Down Expand Up @@ -4702,10 +4725,18 @@ export interface ChatInputApplicationCommandData extends BaseApplicationCommandD
options?: readonly ApplicationCommandOptionData[];
}

export interface PrimaryEntryPointCommandData extends BaseApplicationCommandData {
description?: string;
descriptionLocalizations?: LocalizationMap;
type: ApplicationCommandType.PrimaryEntryPoint;
handler?: EntryPointCommandHandlerType;
}

export type ApplicationCommandData =
| UserApplicationCommandData
| MessageApplicationCommandData
| ChatInputApplicationCommandData;
| ChatInputApplicationCommandData
| PrimaryEntryPointCommandData;

export interface ApplicationCommandChannelOptionData extends BaseApplicationCommandOptionsData {
type: CommandOptionChannelResolvableType;
Expand Down Expand Up @@ -6708,6 +6739,10 @@ export interface ShowModalOptions {
withResponse?: boolean;
}

export interface LaunchActivityOptions {
withResponse?: boolean;
}

export { Snowflake };

export type StageInstanceResolvable = StageInstance | Snowflake;
Expand Down
Loading
Loading