diff --git a/package.json b/package.json index 67cbfe7c9..2478340f2 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ ] } }, - "prettier":{ + "prettier": { "singleQuote": true, "printWidth": 120, "trailingComma": "all", diff --git a/src/index.js b/src/index.js index 3a05137ba..32b98a075 100644 --- a/src/index.js +++ b/src/index.js @@ -56,6 +56,7 @@ module.exports = { splitMessage: Util.splitMessage, // Structures + Application: require('./structures/interfaces/Application'), Base: require('./structures/Base'), Activity: require('./structures/Presence').Activity, APIMessage: require('./structures/APIMessage'), diff --git a/src/structures/ClientApplication.js b/src/structures/ClientApplication.js index 753d90dab..7e35b9ea3 100644 --- a/src/structures/ClientApplication.js +++ b/src/structures/ClientApplication.js @@ -1,46 +1,15 @@ 'use strict'; -const Base = require('./Base'); const Team = require('./Team'); -const { ClientApplicationAssetTypes, Endpoints } = require('../util/Constants'); -const Snowflake = require('../util/Snowflake'); - -const AssetTypes = Object.keys(ClientApplicationAssetTypes); +const Application = require('./interfaces/Application'); /** * Represents a Client OAuth2 Application. - * @extends {Base} + * @extends {Application} */ -class ClientApplication extends Base { - constructor(client, data) { - super(client); - this._patch(data); - } - +class ClientApplication extends Application { _patch(data) { - /** - * The ID of the app - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The name of the app - * @type {string} - */ - this.name = data.name; - - /** - * The app's description - * @type {string} - */ - this.description = data.description; - - /** - * The app's icon hash - * @type {string} - */ - this.icon = data.icon; + super._patch(data); /** * The app's cover image @@ -72,85 +41,6 @@ class ClientApplication extends Base { */ this.owner = data.team ? new Team(this.client, data.team) : data.owner ? this.client.users.add(data.owner) : null; } - - /** - * The timestamp the app was created at - * @type {number} - * @readonly - */ - get createdTimestamp() { - return Snowflake.deconstruct(this.id).timestamp; - } - - /** - * The time the app was created at - * @type {Date} - * @readonly - */ - get createdAt() { - return new Date(this.createdTimestamp); - } - - /** - * A link to the application's icon. - * @param {ImageURLOptions} [options={}] Options for the Image URL - * @returns {?string} URL to the icon - */ - iconURL({ format, size } = {}) { - if (!this.icon) return null; - return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size }); - } - - /** - * A link to this application's cover image. - * @param {ImageURLOptions} [options={}] Options for the Image URL - * @returns {?string} URL to the cover image - */ - coverImage({ format, size } = {}) { - if (!this.cover) return null; - return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size }); - } - - /** - * Asset data. - * @typedef {Object} ClientAsset - * @property {Snowflake} id The asset ID - * @property {string} name The asset name - * @property {string} type The asset type - */ - - /** - * Gets the clients rich presence assets. - * @returns {Promise>} - */ - fetchAssets() { - return this.client.api.oauth2 - .applications(this.id) - .assets.get() - .then(assets => - assets.map(a => ({ - id: a.id, - name: a.name, - type: AssetTypes[a.type - 1], - })), - ); - } - - /** - * When concatenated with a string, this automatically returns the application's name instead of the - * ClientApplication object. - * @returns {string} - * @example - * // Logs: Application name: My App - * console.log(`Application name: ${application}`); - */ - toString() { - return this.name; - } - - toJSON() { - return super.toJSON({ createdTimestamp: true }); - } } module.exports = ClientApplication; diff --git a/src/structures/Guild.js b/src/structures/Guild.js index e66e6a612..ce1f83b5f 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -698,6 +698,8 @@ class Guild extends Base { /** * Fetches a collection of integrations to this guild. * Resolves with a collection mapping integrations by their ids. + * @param {Object} [options] Options for fetching integrations + * @param {boolean} [options.includeApplications] Whether to include bot and Oauth2 webhook integrations * @returns {Promise>} * @example * // Fetch integrations @@ -705,10 +707,14 @@ class Guild extends Base { * .then(integrations => console.log(`Fetched ${integrations.size} integrations`)) * .catch(console.error); */ - fetchIntegrations() { + fetchIntegrations({ includeApplications = false } = {}) { return this.client.api .guilds(this.id) - .integrations.get() + .integrations.get({ + query: { + include_applications: includeApplications, + }, + }) .then(data => data.reduce( (collection, integration) => collection.set(integration.id, new Integration(this.client, integration, this)), diff --git a/src/structures/Integration.js b/src/structures/Integration.js index 0bd673133..1c7a0d411 100644 --- a/src/structures/Integration.js +++ b/src/structures/Integration.js @@ -1,6 +1,7 @@ 'use strict'; const Base = require('./Base'); +const IntegrationApplication = require('./IntegrationApplication'); /** * The information account for an integration @@ -92,6 +93,20 @@ class Integration extends Base { * @type {number} */ this.expireGracePeriod = data.expire_grace_period; + + if ('application' in data) { + if (this.application) { + this.application._patch(data.application); + } else { + /** + * The application for this integration + * @type {?IntegrationApplication} + */ + this.application = new IntegrationApplication(this.client, data.application); + } + } else if (!this.application) { + this.application = null; + } } /** diff --git a/src/structures/IntegrationApplication.js b/src/structures/IntegrationApplication.js new file mode 100644 index 000000000..40f433abd --- /dev/null +++ b/src/structures/IntegrationApplication.js @@ -0,0 +1,25 @@ +'use strict'; + +const Application = require('./interfaces/Application'); + +/** + * Represents an Integration's OAuth2 Application. + * @extends {Application} + */ +class IntegrationApplication extends Application { + _patch(data) { + super._patch(data); + + if (typeof data.bot !== 'undefined') { + /** + * The bot {@link User user} for this application + * @type {?User} + */ + this.bot = this.client.users.add(data.bot); + } else if (!this.bot) { + this.bot = null; + } + } +} + +module.exports = IntegrationApplication; diff --git a/src/structures/interfaces/Application.js b/src/structures/interfaces/Application.js new file mode 100644 index 000000000..9781bfa73 --- /dev/null +++ b/src/structures/interfaces/Application.js @@ -0,0 +1,125 @@ +'use strict'; + +const { ClientApplicationAssetTypes, Endpoints } = require('../../util/Constants'); +const Snowflake = require('../../util/Snowflake'); +const Base = require('../Base'); + +const AssetTypes = Object.keys(ClientApplicationAssetTypes); + +/** + * Represents an OAuth2 Application. + * @abstract + */ +class Application extends Base { + constructor(client, data) { + super(client); + this._patch(data); + } + + _patch(data) { + /** + * The ID of the app + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The name of the app + * @type {string} + */ + this.name = data.name; + + /** + * The app's description + * @type {string} + */ + this.description = data.description; + + /** + * The app's icon hash + * @type {string} + */ + this.icon = data.icon; + } + + /** + * The timestamp the app was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the app was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * A link to the application's icon. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} URL to the icon + */ + iconURL({ format, size } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size }); + } + + /** + * A link to this application's cover image. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} URL to the cover image + */ + coverImage({ format, size } = {}) { + if (!this.cover) return null; + return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size }); + } + + /** + * Asset data. + * @typedef {Object} ApplicationAsset + * @property {Snowflake} id The asset ID + * @property {string} name The asset name + * @property {string} type The asset type + */ + + /** + * Gets the clients rich presence assets. + * @returns {Promise>} + */ + fetchAssets() { + return this.client.api.oauth2 + .applications(this.id) + .assets.get() + .then(assets => + assets.map(a => ({ + id: a.id, + name: a.name, + type: AssetTypes[a.type - 1], + })), + ); + } + + /** + * When concatenated with a string, this automatically returns the application's name instead of the + * Oauth2Application object. + * @returns {string} + * @example + * // Logs: Application name: My App + * console.log(`Application name: ${application}`); + */ + toString() { + return this.name; + } + + toJSON() { + return super.toJSON({ createdTimestamp: true }); + } +} + +module.exports = Application; diff --git a/typings/index.d.ts b/typings/index.d.ts index 4901b8d97..783238b18 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -94,6 +94,21 @@ declare module 'discord.js' { public split(): APIMessage[]; } + export abstract class Application { + constructor(client: Client, data: object); + public readonly createdAt: Date; + public readonly createdTimestamp: number; + public description: string; + public icon: string; + public id: Snowflake; + public name: string; + public coverImage(options?: ImageURLOptions): string; + public fetchAssets(): Promise; + public iconURL(options?: ImageURLOptions): string; + public toJSON(): object; + public toString(): string; + } + export class Base { constructor(client: Client); public readonly client: Client; @@ -229,24 +244,12 @@ declare module 'discord.js' { public removeAllListeners(event?: Exclude): this; } - export class ClientApplication extends Base { - constructor(client: Client, data: object); + export class ClientApplication extends Application { public botPublic: boolean | null; public botRequireCodeGrant: boolean | null; public cover: string | null; - public readonly createdAt: Date; - public readonly createdTimestamp: number; - public description: string; - public icon: string; - public id: Snowflake; - public name: string; public owner: User | Team | null; public rpcOrigins: string[]; - public coverImage(options?: ImageURLOptions): string; - public fetchAssets(): Promise; - public iconURL(options?: ImageURLOptions): string; - public toJSON(): object; - public toString(): string; } export class ClientUser extends User { @@ -690,7 +693,7 @@ declare module 'discord.js' { public fetchBan(user: UserResolvable): Promise<{ user: User; reason: string }>; public fetchBans(): Promise>; public fetchEmbed(): Promise; - public fetchIntegrations(): Promise>; + public fetchIntegrations(options?: FetchIntegrationsOptions): Promise>; public fetchInvites(): Promise>; public fetchPreview(): Promise; public fetchVanityCode(): Promise; @@ -915,6 +918,7 @@ declare module 'discord.js' { export class Integration extends Base { constructor(client: Client, data: object, guild: Guild); public account: IntegrationAccount; + public application: IntegrationApplication | null; public enabled: boolean; public expireBehavior: number; public expireGracePeriod: number; @@ -931,6 +935,10 @@ declare module 'discord.js' { public sync(): Promise; } + export class IntegrationApplication extends Application { + public bot: User | null; + } + export class Intents extends BitField { public static FLAGS: Record; public static PRIVILEGED: number; @@ -2175,6 +2183,12 @@ declare module 'discord.js' { type APIMessageContentResolvable = string | number | boolean | bigint | symbol | readonly StringResolvable[]; + interface ApplicationAsset { + name: string; + id: Snowflake; + type: 'BIG' | 'SMALL'; + } + interface AuditLogChange { key: string; old?: any; @@ -2239,12 +2253,6 @@ declare module 'discord.js' { type ChannelResolvable = Channel | Snowflake; - interface ClientApplicationAsset { - name: string; - id: Snowflake; - type: 'BIG' | 'SMALL'; - } - interface ClientEvents { channelCreate: [Channel]; channelDelete: [Channel | PartialDMChannel]; @@ -2453,6 +2461,10 @@ declare module 'discord.js' { User: typeof User; } + interface FetchIntegrationsOptions { + includeApplications?: boolean; + } + interface FetchMemberOptions { user: UserResolvable; cache?: boolean;