diff --git a/src/index.js b/src/index.js index 8425c93b8e48..c8bb9803110b 100644 --- a/src/index.js +++ b/src/index.js @@ -73,6 +73,8 @@ exports.Activity = require('./structures/Presence').Activity; exports.AnonymousGuild = require('./structures/AnonymousGuild'); exports.Application = require('./structures/interfaces/Application'); exports.ApplicationCommand = require('./structures/ApplicationCommand'); +exports.ApplicationRoleConnectionMetadata = + require('./structures/ApplicationRoleConnectionMetadata').ApplicationRoleConnectionMetadata; exports.AutocompleteInteraction = require('./structures/AutocompleteInteraction'); exports.AutoModerationActionExecution = require('./structures/AutoModerationActionExecution'); exports.AutoModerationRule = require('./structures/AutoModerationRule'); diff --git a/src/structures/ApplicationRoleConnectionMetadata.js b/src/structures/ApplicationRoleConnectionMetadata.js new file mode 100644 index 000000000000..aaa7da6fc059 --- /dev/null +++ b/src/structures/ApplicationRoleConnectionMetadata.js @@ -0,0 +1,48 @@ +'use strict'; + +const { ApplicationRoleConnectionMetadataTypes } = require('../util/Constants'); + +/** + * Role connection metadata object for an application. + */ +class ApplicationRoleConnectionMetadata { + constructor(data) { + /** + * The name of this metadata field + * @type {string} + */ + this.name = data.name; + + /** + * The name localizations for this metadata field + * @type {?Object} + */ + this.nameLocalizations = data.name_localizations ?? null; + + /** + * The description of this metadata field + * @type {string} + */ + this.description = data.description; + + /** + * The description localizations for this metadata field + * @type {?Object} + */ + this.descriptionLocalizations = data.description_localizations ?? null; + + /** + * The dictionary key for this metadata field + * @type {string} + */ + this.key = data.key; + + /** + * The type of this metadata field + * @type {ApplicationRoleConnectionMetadataType} + */ + this.type = typeof data.type === 'number' ? ApplicationRoleConnectionMetadataTypes[data.type] : data.type; + } +} + +exports.ApplicationRoleConnectionMetadata = ApplicationRoleConnectionMetadata; diff --git a/src/structures/ClientApplication.js b/src/structures/ClientApplication.js index 601314ab55f1..cae8b6d704b3 100644 --- a/src/structures/ClientApplication.js +++ b/src/structures/ClientApplication.js @@ -1,9 +1,11 @@ 'use strict'; +const { ApplicationRoleConnectionMetadata } = require('./ApplicationRoleConnectionMetadata'); const Team = require('./Team'); const Application = require('./interfaces/Application'); const ApplicationCommandManager = require('../managers/ApplicationCommandManager'); const ApplicationFlags = require('../util/ApplicationFlags'); +const { ApplicationRoleConnectionMetadataTypes } = require('../util/Constants'); const Permissions = require('../util/Permissions'); /** @@ -136,6 +138,48 @@ class ClientApplication extends Application { this._patch(app); return this; } + + /** + * Gets this application's role connection metadata records + * @returns {Promise} + */ + async fetchRoleConnectionMetadataRecords() { + const metadata = await this.client.api.applications(this.client.user.id)('role-connections').metadata.get(); + return metadata.map(data => new ApplicationRoleConnectionMetadata(data)); + } + + /** + * Data for creating or editing an application role connection metadata. + * @typedef {Object} ApplicationRoleConnectionMetadataEditOptions + * @property {string} name The name of the metadata field + * @property {?Object} [nameLocalizations] The name localizations for the metadata field + * @property {string} description The description of the metadata field + * @property {?Object} [descriptionLocalizations] The description localizations for the metadata field + * @property {string} key The dictionary key of the metadata field + * @property {ApplicationRoleConnectionMetadataType} type The type of the metadata field + */ + + /** + * Updates this application's role connection metadata records + * @param {ApplicationRoleConnectionMetadataEditOptions[]} records The new role connection metadata records + * @returns {Promise} + */ + async editRoleConnectionMetadataRecords(records) { + const newRecords = await this.client.api + .applications(this.client.user.id)('role-connections') + .metadata.put({ + data: records.map(record => ({ + type: typeof record.type === 'string' ? ApplicationRoleConnectionMetadataTypes[record.type] : record.type, + key: record.key, + name: record.name, + name_localizations: record.nameLocalizations, + description: record.description, + description_localizations: record.descriptionLocalizations, + })), + }); + + return newRecords.map(data => new ApplicationRoleConnectionMetadata(data)); + } } module.exports = ClientApplication; diff --git a/src/structures/interfaces/Application.js b/src/structures/interfaces/Application.js index fcac84d81fb0..d4344232586a 100644 --- a/src/structures/interfaces/Application.js +++ b/src/structures/interfaces/Application.js @@ -56,6 +56,16 @@ class Application extends Base { } else { this.icon ??= null; } + + if ('role_connections_verification_url' in data) { + /** + * This application's role connection verification entry point URL + * @type {?string} + */ + this.roleConnectionsVerificationURL = data.role_connections_verification_url; + } else { + this.roleConnectionsVerificationURL ??= null; + } } /** diff --git a/src/util/Constants.js b/src/util/Constants.js index 06a1f1720132..f5f3a7f81277 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -499,6 +499,7 @@ exports.WSEvents = keyMirror([ * * `guilds.join`: allows the bot to join the user to any guild it is in using Guild#addMember * * `gdm.join`: allows joining the user to a group dm * * `webhook.incoming`: generates a webhook to a channel + * * `role_connections.write`: allows your app to update a user's connection and metadata for the app * @typedef {string} InviteScope * @see {@link https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes} */ @@ -515,6 +516,7 @@ exports.InviteScopes = [ 'guilds.join', 'gdm.join', 'webhook.incoming', + 'role_connections.write', ]; // TODO: change Integration#expireBehavior to this and clean up Integration @@ -1245,6 +1247,35 @@ exports.ApplicationCommandOptionTypes = createEnum([ */ exports.ApplicationCommandPermissionTypes = createEnum([null, 'ROLE', 'USER']); +/** + * Each metadata type offers a comparison operation that allows + * guilds to configure role requirements based on metadata values stored by the bot. + * Bots specify a metadata value for each user and guilds specify + * the required guild's configured value within the guild role settings. + * All available channel types: + * * INTEGER_LESS_THAN_OR_EQUAL + * * INTEGER_GREATER_THAN_OR_EQUAL + * * INTEGER_EQUAL + * * INTEGER_NOT_EQUAL + * * DATATIME_LESS_THAN_OR_EQUAL + * * DATATIME_GREATER_THAN_OR_EQUAL + * * BOOLEAN_EQUAL + * * BOOLEAN_NOT_EQUAL + * @typedef {string} ApplicationRoleConnectionMetadataType + * @see{@link https://discord.com/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-type} + */ +exports.ApplicationRoleConnectionMetadataTypes = createEnum( + null, + 'INTEGER_LESS_THAN_OR_EQUAL', + 'INTEGER_GREATER_THAN_OR_EQUAL', + 'INTEGER_EQUAL', + 'INTEGER_NOT_EQUAL', + 'DATATIME_LESS_THAN_OR_EQUAL', + 'DATATIME_GREATER_THAN_OR_EQUAL', + 'BOOLEAN_EQUAL', + 'BOOLEAN_NOT_EQUAL', +); + /** * The type of an {@link AutoModerationRuleTriggerTypes} object: * * KEYWORD @@ -1487,6 +1518,7 @@ function createEnum(keys) { * The type of an {@link ApplicationCommandPermissions} object. * @property {Object} ApplicationCommandTypes * The type of an {@link ApplicationCommand} object. + * @property {Object} ApplicationRoleConnectionMetadataTypes * @property {Object} AutoModerationRuleTriggerTypes Characterizes the type * of content which can trigger the rule. * @property {Object} AutoModerationActionTypes diff --git a/typings/enums.d.ts b/typings/enums.d.ts index fd8d06c897c3..74072c4b4de4 100644 --- a/typings/enums.d.ts +++ b/typings/enums.d.ts @@ -252,3 +252,14 @@ export const enum WebhookTypes { 'Channel Follower' = 2, Application = 3, } + +export enum ApplicationRoleConnectionMetadataTypes { + INTEGER_LESS_THAN_OR_EQUAL = 1, + INTEGER_GREATER_THAN_OR_EQUAL, + INTEGER_EQUAL, + INTEGER_NOT_EQUAL, + DATATIME_LESS_THAN_OR_EQUAL, + DATATIME_GREATER_THAN_OR_EQUAL, + BOOLEAN_EQUAL, + BOOLEAN_NOT_EQUAL, +} diff --git a/typings/index.d.ts b/typings/index.d.ts index 4911d25510c9..1592763ac33a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -96,8 +96,10 @@ import { VideoQualityModes, SortOrderType, ForumLayoutType, + ApplicationRoleConnectionMetadataTypes, } from './enums'; import { + APIApplicationRoleConnectionMetadata, APIAutoModerationRule, GatewayAutoModerationActionExecutionDispatchData, RawActivityData, @@ -217,6 +219,7 @@ export abstract class Application extends Base { public icon: string | null; public id: Snowflake; public name: string | null; + public roleConnectionsVerificationURL: string | null; public coverURL(options?: StaticImageURLOptions): string | null; /** @deprecated This method is deprecated as it is unsupported and will be removed in the next major version. */ public fetchAssets(): Promise; @@ -288,6 +291,16 @@ export class ApplicationCommand extends Base { private static isAPICommandData(command: object): command is RESTPostAPIApplicationCommandsJSONBody; } +export class ApplicationRoleConnectionMetadata { + private constructor(data: APIApplicationRoleConnectionMetadata); + public name: string; + public nameLocalizations: LocalizationMap | null; + public description: string; + public descriptionLocalizations: LocalizationMap | null; + public key: string; + public type: ApplicationRoleConnectionMetadataTypes; +} + export type ApplicationResolvable = Application | Activity | Snowflake; export type AutoModerationRuleResolvable = AutoModerationRule | Snowflake; @@ -667,6 +680,10 @@ export class ClientApplication extends Application { public readonly partial: boolean; public rpcOrigins: string[]; public fetch(): Promise; + public fetchRoleConnectionMetadataRecords(): Promise; + public editRoleConnectionMetadataRecords( + records: ApplicationRoleConnectionMetadataEditOptions[], + ): Promise; } export class ClientPresence extends Presence { @@ -4211,6 +4228,15 @@ export type ApplicationFlagsString = | 'GATEWAY_MESSAGE_CONTENT' | 'GATEWAY_MESSAGE_CONTENT_LIMITED'; +export interface ApplicationRoleConnectionMetadataEditOptions { + name: string; + nameLocalizations?: LocalizationMap | null; + description: string; + descriptionLocalizations?: LocalizationMap | null; + key: string; + type: ApplicationRoleConnectionMetadataTypes; +} + export interface AutoModerationAction { type: AutoModerationActionType | AutoModerationActionTypes; metadata: AutoModerationActionMetadata; @@ -5593,7 +5619,8 @@ export type InviteScope = | 'guilds' | 'guilds.join' | 'gdm.join' - | 'webhook.incoming'; + | 'webhook.incoming' + | 'role_connections.write'; export interface LifetimeFilterOptions { excludeFromSweep?: (value: V, key: K, collection: LimitedCollection) => boolean; diff --git a/typings/rawDataTypes.d.ts b/typings/rawDataTypes.d.ts index 456d5c6c8a16..5b7aa1757cc3 100644 --- a/typings/rawDataTypes.d.ts +++ b/typings/rawDataTypes.d.ts @@ -80,6 +80,7 @@ import { APITextInputComponent, APIModalActionRowComponent, APIModalSubmitInteraction, + LocalizationMap } from 'discord-api-types/v9'; import { GuildChannel, Guild, PermissionOverwrites, InteractionType } from '.'; import type { @@ -89,6 +90,7 @@ import type { AutoModerationRuleTriggerTypes, InteractionTypes, MessageComponentTypes, + ApplicationRoleConnectionMetadataTypes } from './enums'; export type RawActivityData = GatewayActivity; @@ -268,3 +270,12 @@ export interface APIAutoModerationRuleTriggerMetadata { regex_patterns?: string[]; mention_total_limit?: number; } + +export interface APIApplicationRoleConnectionMetadata { + type: ApplicationRoleConnectionMetadataTypes; + key: string; + name: string; + name_localizations?: LocalizationMap; + description: string; + description_localizations?: LocalizationMap; +}