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: user-installable apps #10227

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/discord.js/src/structures/ApplicationCommand.js
Expand Up @@ -145,12 +145,35 @@ class ApplicationCommand extends Base {
* Whether the command can be used in DMs
* <info>This property is always `null` on guild commands</info>
* @type {?boolean}
* @deprecated Use {@link ApplicationCommand#contexts} instead.
*/
this.dmPermission = data.dm_permission;
} else {
this.dmPermission ??= null;
}

if ('integration_types' in data) {
/**
* Installation context(s) where the command is available
* <info>Only for globally-scoped commands</info>
* @type {?ApplicationIntegrationType[]}
*/
this.integrationTypes = data.integration_types;
} else {
this.integrationTypes ??= null;
}

if ('contexts' in data) {
/**
* Interaction context(s) where the command can be used
* <info>Only for globally-scoped commands</info>
* @type {?InteractionContextType[]}
*/
this.contexts = data.contexts;
} else {
this.contexts ??= null;
}

if ('version' in data) {
/**
* Autoincrementing version identifier updated during substantial record changes
Expand Down
4 changes: 2 additions & 2 deletions packages/discord.js/src/structures/BaseInteraction.js
Expand Up @@ -75,9 +75,9 @@ class BaseInteraction extends Base {

/**
* Set of permissions the application or bot has within the channel the interaction was sent from
* @type {?Readonly<PermissionsBitField>}
* @type {Readonly<PermissionsBitField>}
*/
this.appPermissions = data.app_permissions ? new PermissionsBitField(data.app_permissions).freeze() : null;
this.appPermissions = new PermissionsBitField(data.app_permissions).freeze();

/**
* The permissions of the member, if one exists, in the channel this interaction was executed in
Expand Down
10 changes: 10 additions & 0 deletions packages/discord.js/src/structures/ClientApplication.js
Expand Up @@ -61,6 +61,16 @@ class ClientApplication extends Application {
this.installParams ??= null;
}

if ('integration_types_config' in data) {
/**
* Default scopes and permissions for each supported installation context
* @type {APIApplicationIntegrationTypesConfigMap}
*/
this.integrationTypesConfig = data.integration_types_config;
} else {
this.integrationTypesConfig ??= null;
}

if ('custom_install_url' in data) {
/**
* This application's custom installation URL
Expand Down
12 changes: 12 additions & 0 deletions packages/discord.js/src/structures/CommandInteraction.js
Expand Up @@ -45,6 +45,18 @@ class CommandInteraction extends BaseInteraction {
*/
this.commandGuildId = data.data.guild_id ?? null;

/**
* Mapping of installation contexts that the interaction was authorized for to related user or guild IDs
* @type {APIAuthorizingIntegrationOwnersMap}
*/
this.authorizingIntegrationOwners = data.authorizingIntegrationOwners;

/**
* Context where the interaction was triggered from
* @type {?InteractionContextType}
*/
this.context = data.context ?? null;

/**
* Whether the reply to this interaction has been deferred
* @type {boolean}
Expand Down
31 changes: 31 additions & 0 deletions packages/discord.js/src/structures/Message.js
Expand Up @@ -383,6 +383,36 @@ class Message extends Base {
this.channel?.messages._add({ guild_id: data.message_reference?.guild_id, ...data.referenced_message });
}

if (data.interaction_metadata) {
/**
* Partial data of the interaction that this message is a result of
* @typedef {Object} MessageInteractionMetadata
* @property {Snowflake} id The interaction's id
* @property {InteractionType} type The type of the interaction
* @property {User} user The user that invoked the interaction
* @property {APIAuthorizingIntegrationOwnersMap} authorizingIntegrationOwners
* IDs for installation context(s) related to an interaction
* @property {?Snowflake} originalResponseMessageId
* ID of the original response message, present only on follow-up messages
* @property {?Snowflake} interactedMessageId
* ID of the message that contained interactive component,
* present only on messages created from component interactions
* @property {?MessageInteractionMetadata} triggeringInteractionMetadata
* Metadata for the interaction that was used to open the modal, present only on modal submit interactions
*/
this.interactionMetadata = {
id: data.interaction_metadata.id,
type: data.interaction_metadata.type,
user: this.client.users._add(data.interaction_metadata.user),
authorizingIntegrationOwners: data.interaction_metadata.authorizing_integration_owners,
originalResponseMessageId: data.interaction_metadata.original_response_message_id ?? null,
interactedMessageId: data.interaction_metadata.interacted_message_id ?? null,
triggeringInteractionMetadata: data.interaction_metadata.triggering_interaction_metadata ?? null,
};
} else {
this.interactionMetadata ??= null;
}

/**
* Partial data of the interaction that a message is a reply to
* @typedef {Object} MessageInteraction
Expand All @@ -391,6 +421,7 @@ class Message extends Base {
* @property {string} commandName The name of the interaction's application command,
* as well as the subcommand and subcommand group, where applicable
* @property {User} user The user that invoked the interaction
* @deprecated Use {@link Message#interactionMetadata} instead.
*/

if (data.interaction) {
Expand Down
22 changes: 21 additions & 1 deletion packages/discord.js/typings/index.d.ts
Expand Up @@ -175,6 +175,9 @@ import {
SKUType,
APIEntitlement,
EntitlementType,
ApplicationIntegrationType,
InteractionContextType,
APIAuthorizingIntegrationOwnersMap,
APIPoll,
PollLayoutType,
APIPollAnswer,
Expand Down Expand Up @@ -438,17 +441,20 @@ export abstract class Application extends Base {
export class ApplicationCommand<PermissionsFetchType = {}> extends Base {
private constructor(client: Client<true>, data: RawApplicationCommandData, guild?: Guild, guildId?: Snowflake);
public applicationId: Snowflake;
public contexts: InteractionContextType[] | null;
public get createdAt(): Date;
public get createdTimestamp(): number;
public defaultMemberPermissions: Readonly<PermissionsBitField> | null;
public description: string;
public descriptionLocalizations: LocalizationMap | null;
public descriptionLocalized: string | null;
/** @deprecated Use {@link ApplicationCommand.contexts} instead */
public dmPermission: boolean | null;
public guild: Guild | null;
public guildId: Snowflake | null;
public get manager(): ApplicationCommandManager;
public id: Snowflake;
public integrationTypes: ApplicationIntegrationType[] | null;
public name: string;
public nameLocalizations: LocalizationMap | null;
public nameLocalized: string | null;
Expand Down Expand Up @@ -550,6 +556,7 @@ export type GuildCacheMessage<Cached extends CacheType> = CacheTypeReducer<
export type BooleanCache<Cached extends CacheType> = Cached extends 'cached' ? true : false;

export abstract class CommandInteraction<Cached extends CacheType = CacheType> extends BaseInteraction<Cached> {
public authorizingIntegrationOwners: APIAuthorizingIntegrationOwnersMap;
public type: InteractionType.ApplicationCommand;
public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null;
public options: Omit<
Expand All @@ -574,6 +581,7 @@ export abstract class CommandInteraction<Cached extends CacheType = CacheType> e
public commandName: string;
public commandType: ApplicationCommandType;
public commandGuildId: Snowflake | null;
public context: InteractionContextType | null;
public deferred: boolean;
public ephemeral: boolean | null;
public replied: boolean;
Expand Down Expand Up @@ -1887,7 +1895,7 @@ export class BaseInteraction<Cached extends CacheType = CacheType> extends Base
public type: InteractionType;
public user: User;
public version: number;
public appPermissions: CacheTypeReducer<Cached, Readonly<PermissionsBitField>>;
public appPermissions: Readonly<PermissionsBitField>;
public memberPermissions: CacheTypeReducer<Cached, Readonly<PermissionsBitField>>;
public locale: Locale;
public guildLocale: CacheTypeReducer<Cached, Locale>;
Expand Down Expand Up @@ -2098,7 +2106,9 @@ export class Message<InGuild extends boolean = boolean> extends Base {
public get guild(): If<InGuild, Guild>;
public get hasThread(): boolean;
public id: Snowflake;
/** @deprecated Use {@link Message.interactionMetadata} instead */
public interaction: MessageInteraction | null;
public interactionMetadata: MessageInteractionMetadata | null;
public get member(): GuildMember | null;
public mentions: MessageMentions<InGuild>;
public nonce: string | number | null;
Expand Down Expand Up @@ -6273,6 +6283,16 @@ export interface MessageComponentCollectorOptions<Interaction extends CollectedM
export interface MessageChannelComponentCollectorOptions<Interaction extends CollectedMessageInteraction>
extends Omit<InteractionCollectorOptions<Interaction>, 'channel' | 'guild' | 'interactionType'> {}

export interface MessageInteractionMetadata {
id: Snowflake;
type: InteractionType;
user: User;
authorizingIntegrationOwners: APIAuthorizingIntegrationOwnersMap;
originalResponseMessageId: Snowflake | null;
interactedMessageId: Snowflake | null;
triggeringInteractionMetadata: MessageInteractionMetadata | null;
}

export interface MessageInteraction {
id: Snowflake;
type: InteractionType;
Expand Down