diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js index 23617c058..929376b28 100644 --- a/packages/discord.js/src/index.js +++ b/packages/discord.js/src/index.js @@ -105,6 +105,8 @@ exports.ActionRow = require('./structures/ActionRow.js').ActionRow; exports.Activity = require('./structures/Presence.js').Activity; exports.AnnouncementChannel = require('./structures/AnnouncementChannel.js').AnnouncementChannel; exports.AnonymousGuild = require('./structures/AnonymousGuild.js').AnonymousGuild; +exports.AuthorizingIntegrationOwners = + require('./structures/AuthorizingIntegrationOwners.js').AuthorizingIntegrationOwners; exports.Application = require('./structures/interfaces/Application.js').Application; exports.ApplicationCommand = require('./structures/ApplicationCommand.js').ApplicationCommand; exports.ApplicationEmoji = require('./structures/ApplicationEmoji.js').ApplicationEmoji; diff --git a/packages/discord.js/src/structures/AuthorizingIntegrationOwners.js b/packages/discord.js/src/structures/AuthorizingIntegrationOwners.js new file mode 100644 index 000000000..1a72ced17 --- /dev/null +++ b/packages/discord.js/src/structures/AuthorizingIntegrationOwners.js @@ -0,0 +1,64 @@ +'use strict'; + +const { ApplicationIntegrationType } = require('discord-api-types/v10'); +const { Base } = require('./Base.js'); + +/** + * Represents the owners of an authorizing integration. + * + * @extends {Base} + */ +class AuthorizingIntegrationOwners extends Base { + constructor(client, data) { + super(client); + + Object.defineProperty(this, 'data', { value: data }); + + // Support accessing authorizingIntegrationOwners[ApplicationIntegrationType.GuildInstall] or similar, + forward compatibility if new installation types get added + for (const value of Object.values(ApplicationIntegrationType)) { + if (typeof value !== 'number') { + continue; + } + + Object.defineProperty(this, value, { value: this.data[value] }); + } + + /** + * The id of the guild where the integration is installed, if applicable. + * + * @type {?Snowflake} + */ + this.guildId = this.data[ApplicationIntegrationType.GuildInstall] ?? null; + + /** + * The id of the user on which the integration is installed, if applicable. + * + * @type {?Snowflake} + */ + this.userId = this.data[ApplicationIntegrationType.UserInstall] ?? null; + } + + /** + * The guild where the integration is installed, if applicable. + * + * @type {?Guild} + */ + get guild() { + return (this.guildId && this.client.guilds.cache.get(this.guildId)) ?? null; + } + + /** + * The user on which the integration is installed, if applicable. + * + * @type {?User} + */ + get user() { + return (this.userId && this.client.users.cache.get(this.userId)) ?? null; + } + + toJSON() { + return this.data; + } +} + +exports.AuthorizingIntegrationOwners = AuthorizingIntegrationOwners; diff --git a/packages/discord.js/src/structures/BaseInteraction.js b/packages/discord.js/src/structures/BaseInteraction.js index 49bc76519..0f2d3e1ca 100644 --- a/packages/discord.js/src/structures/BaseInteraction.js +++ b/packages/discord.js/src/structures/BaseInteraction.js @@ -5,6 +5,7 @@ const { DiscordSnowflake } = require('@sapphire/snowflake'); const { InteractionType, ApplicationCommandType, ComponentType } = require('discord-api-types/v10'); const { SelectMenuTypes } = require('../util/Constants.js'); const { PermissionsBitField } = require('../util/PermissionsBitField.js'); +const { AuthorizingIntegrationOwners } = require('./AuthorizingIntegrationOwners.js'); const { Base } = require('./Base.js'); /** @@ -123,12 +124,15 @@ class BaseInteraction extends Base { ); /** - * Mapping of installation contexts that the interaction was authorized for the related user or guild ids + * Mapping of integration types that the application was authorized for the related user or guild ids * - * @type {APIAuthorizingIntegrationOwnersMap} + * @type {AuthorizingIntegrationOwners} * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object} */ - this.authorizingIntegrationOwners = data.authorizing_integration_owners; + this.authorizingIntegrationOwners = new AuthorizingIntegrationOwners( + this.client, + data.authorizing_integration_owners, + ); /** * Context where the interaction was triggered from diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index ce778a215..2b4b87919 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -414,8 +414,8 @@ class Message extends Base { * @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 {AuthorizingIntegrationOwners} authorizingIntegrationOwners + * Mapping of integration types that the application was authorized for the related user or guild ids * @property {?Snowflake} originalResponseMessageId * Id of the original response message. Present only on follow-up messages * @property {?Snowflake} interactedMessageId diff --git a/packages/discord.js/src/util/APITypes.js b/packages/discord.js/src/util/APITypes.js index ceafa189e..1e11f8692 100644 --- a/packages/discord.js/src/util/APITypes.js +++ b/packages/discord.js/src/util/APITypes.js @@ -35,11 +35,6 @@ * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ApplicationIntegrationType} */ -/** - * @external APIAuthorizingIntegrationOwnersMap - * @see {@link https://discord-api-types.dev/api/discord-api-types-v10#APIAuthorizingIntegrationOwnersMap} - */ - /** * @external APIAutoModerationAction * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIAutoModerationAction} diff --git a/packages/discord.js/src/util/Transformers.js b/packages/discord.js/src/util/Transformers.js index 2b73fa3bb..d0f20339a 100644 --- a/packages/discord.js/src/util/Transformers.js +++ b/packages/discord.js/src/util/Transformers.js @@ -2,6 +2,7 @@ const { isJSONEncodable } = require('@discordjs/util'); const snakeCase = require('lodash.snakecase'); +const { AuthorizingIntegrationOwners } = require('../structures/AuthorizingIntegrationOwners.js'); /** * Transforms camel-cased keys into snake cased keys @@ -48,7 +49,10 @@ function _transformAPIMessageInteractionMetadata(client, messageInteractionMetad id: messageInteractionMetadata.id, type: messageInteractionMetadata.type, user: client.users._add(messageInteractionMetadata.user), - authorizingIntegrationOwners: messageInteractionMetadata.authorizing_integration_owners, + authorizingIntegrationOwners: new AuthorizingIntegrationOwners( + client, + messageInteractionMetadata.authorizing_integration_owners, + ), originalResponseMessageId: messageInteractionMetadata.original_response_message_id ?? null, interactedMessageId: messageInteractionMetadata.interacted_message_id ?? null, triggeringInteractionMetadata: messageInteractionMetadata.triggering_interaction_metadata diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 6957c552f..4d78d3d98 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1927,7 +1927,7 @@ export class BaseInteraction extends Base private readonly _cacheType: Cached; protected constructor(client: Client, data: GatewayInteractionCreateDispatchData); public applicationId: Snowflake; - public authorizingIntegrationOwners: APIAuthorizingIntegrationOwnersMap; + public authorizingIntegrationOwners: AuthorizingIntegrationOwners; public get channel(): CacheTypeReducer< Cached, GuildTextBasedChannel | null, @@ -2265,6 +2265,22 @@ export class Message extends Base { public inGuild(): this is Message; } +export class AuthorizingIntegrationOwners extends Base { + private constructor(client: Client, data: APIAuthorizingIntegrationOwnersMap); + private readonly data: APIAuthorizingIntegrationOwnersMap; + + // Getters from types + public readonly [ApplicationIntegrationType.GuildInstall]?: Snowflake; + public readonly [ApplicationIntegrationType.UserInstall]?: Snowflake; + + public readonly guildId: Snowflake | null; + public get guild(): Guild | null; + public readonly userId: Snowflake | null; + public get user(): User | null; + + public toJSON(): APIAuthorizingIntegrationOwnersMap; +} + export class Attachment { private constructor(data: APIAttachment); private readonly attachment: BufferResolvable | Stream; @@ -6642,7 +6658,7 @@ export interface MessageChannelComponentCollectorOptions< } export interface MessageInteractionMetadata { - authorizingIntegrationOwners: APIAuthorizingIntegrationOwnersMap; + authorizingIntegrationOwners: AuthorizingIntegrationOwners; id: Snowflake; interactedMessageId: Snowflake | null; originalResponseMessageId: Snowflake | null; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 7969242ea..71aeb858a 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -24,6 +24,7 @@ import { ApplicationCommandOptionType, ApplicationCommandPermissionType, ApplicationCommandType, + ApplicationIntegrationType, AuditLogEvent, ButtonStyle, ChannelType, @@ -200,6 +201,7 @@ import type { VoiceChannel, Invite, GuildInvite, + AuthorizingIntegrationOwners, } from './index.js'; import { ActionRowBuilder, @@ -3044,3 +3046,12 @@ await textChannel.send({ ], flags: MessageFlags.IsVoiceMessage, }); + +declare const authorizingIntegrationOwners: AuthorizingIntegrationOwners; +{ + expectType(authorizingIntegrationOwners.guildId); + expectType(authorizingIntegrationOwners.guild); + expectType(authorizingIntegrationOwners.userId); + expectType(authorizingIntegrationOwners.user); + expectType(authorizingIntegrationOwners[ApplicationIntegrationType.GuildInstall]); +}