diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 980bf0975f27..3fd62e290faa 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -101,6 +101,8 @@ const Messages = { VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.', + COMMUNITY: 'This guild does not have the COMMUNITY feature enabled.', + DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete them", FETCH_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot fetch them", diff --git a/src/structures/Guild.js b/src/structures/Guild.js index ee0d70c1fd0f..bf811882fac1 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -170,8 +170,10 @@ class Guild extends Base { * * DISCOVERABLE * * FEATURABLE * * INVITE_SPLASH + * * MEMBER_VERIFICATION_GATE_ENABLED * * NEWS * * PARTNERED + * * PREVIEW_ENABLED * * RELAY_ENABLED * * VANITY_URL * * VERIFIED @@ -959,6 +961,52 @@ class Guild extends Base { .then(data => GuildAuditLogs.build(this, data)); } + /** + * Data for a field in Membership Screening + * @typedef {Object} GuildMembershipScreeningField + * @property {MembershipScreeningType} fieldType The type of the field + * @property {string} label The title of the field + * @property {string[]} [values] The list of values in the field + * @property {boolean} required Whether the user has to fill out this field + */ + + /** + * Data for the Guild Membership Screening object + * @typedef {Object} GuildMembershipScreening + * @property {boolean} enabled Whether membership screening is enabled + * @property {string} description The server description shown in the membership screening form + * @property {GuildMembershipScreeningField[]} formFields The steps in the membership screening form + */ + + /** + * Fetches the guild Membership Screening data. + * @returns {Promise} + * @example + * // Fetches the guild membership screening options + * guild.fetchMembershipScreening() + * .then(memberScreen => { + * const requiredFields = memberScreen.formFields.filter(field => field.required); + * console.log(`Screening has ${memberScreen.formFields.length} steps, ${requiredFields.length} are required`); + * }) + * .catch(console.error); + */ + async fetchMembershipScreening() { + if (!this.features.includes('COMMUNITY')) { + throw new Error('COMMUNITY'); + } + const data = await this.client.api.guilds(this.id)['member-verification'].get(); + return { + enabled: this.features.includes('MEMBER_VERIFICATION_GATE_ENABLED'), + description: data.description, + formFields: data.form_fields.map(field => ({ + fieldType: field.field_type, + label: field.label, + values: field.values, + required: field.required, + })), + }; + } + /** * Adds a user to the guild using OAuth2. Requires the `CREATE_INSTANT_INVITE` permission. * @param {UserResolvable} user User to add to the guild @@ -1422,6 +1470,45 @@ class Guild extends Base { .then(() => this); } + /** + * A `Partial` object is a representation of any existing object. + * This object contains between 1 and all of the original objects parameters. + * This is true regardless of whether the parameters are optional in the base object. + * @typedef {Object} Partial + */ + + /** + * Edits the guild's membership screening form. + * @param {Partial} memberScreen The membership screening data for the guild + * @returns {Promise} + */ + async setMembershipScreening(memberScreen) { + if (!this.features.includes('COMMUNITY')) { + throw new Error('COMMUNITY'); + } + const fields = memberScreen.formFields?.map(field => ({ + field_type: field.fieldType, + label: field.label, + values: field.values, + required: field.required, + })); + const data = {}; + if (typeof memberScreen.enabled !== 'undefined') data.enabled = memberScreen.enabled; + if (typeof memberScreen.description !== 'undefined') data.description = memberScreen.description; + if (typeof memberScreen.formFields !== 'undefined') data.form_fields = JSON.stringify(fields); + const res = await this.client.api.guilds(this.id)['member-verification'].patch({ data }); + return { + enabled: memberScreen.enabled ?? this.features.includes('MEMBER_VERIFICATION_GATE_ENABLED'), + description: res.description, + formFields: res.form_fields.map(field => ({ + fieldType: field.field_type, + label: field.label, + values: field.values, + required: field.required, + })), + }; + } + /** * Leaves the guild. * @returns {Promise} diff --git a/src/util/Constants.js b/src/util/Constants.js index 3af6122ace4e..b16719b484d8 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -512,6 +512,13 @@ exports.ExplicitContentFilterLevels = ['DISABLED', 'MEMBERS_WITHOUT_ROLES', 'ALL */ exports.VerificationLevels = ['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_HIGH']; +/** + * The type of membership screening. Here are the available types: + * * `TERMS`: Server Rules + * @typedef {string} MembershipScreeningType + */ +exports.MembershipScreeningType = ['TERMS']; + /** * An error encountered while performing an API request. Here are the potential errors: * * UNKNOWN_ACCOUNT diff --git a/typings/index.d.ts b/typings/index.d.ts index 943fbf27d920..6185444f7c83 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -519,6 +519,7 @@ declare module 'discord.js' { ExplicitContentFilterLevels: ExplicitContentFilterLevel[]; DefaultMessageNotifications: DefaultMessageNotifications[]; VerificationLevels: VerificationLevel[]; + MembershipScreeningType: MembershipScreeningType[]; MembershipStates: 'INVITED' | 'ACCEPTED'; }; @@ -602,6 +603,7 @@ declare module 'discord.js' { public readonly me: GuildMember | null; public memberCount: number; public members: GuildMemberManager; + public readonly membershipScreeningEnabled: boolean; public mfaLevel: number; public name: string; public readonly nameAcronym: string; @@ -647,6 +649,7 @@ declare module 'discord.js' { public fetchEmbed(): Promise; public fetchIntegrations(options?: FetchIntegrationsOptions): Promise>; public fetchInvites(): Promise>; + public fetchMembershipScreening(): Promise; public fetchPreview(): Promise; public fetchTemplates(): Promise>; public fetchVanityCode(): Promise; @@ -671,6 +674,7 @@ declare module 'discord.js' { reason?: string, ): Promise; public setIcon(icon: Base64Resolvable | null, reason?: string): Promise; + public setMembershipScreening(memberScreen: Partial): Promise; public setName(name: string, reason?: string): Promise; public setOwner(owner: GuildMemberResolvable, reason?: string): Promise; public setPreferredLocale(preferredLocale: string, reason?: string): Promise; @@ -2646,8 +2650,10 @@ declare module 'discord.js' { | 'DISCOVERABLE' | 'FEATURABLE' | 'INVITE_SPLASH' + | 'MEMBER_VERIFICATION_GATE_ENABLED' | 'NEWS' | 'PARTNERED' + | 'PREVIEW_ENABLED' | 'RELAY_ENABLED' | 'VANITY_URL' | 'VERIFIED' @@ -2664,6 +2670,19 @@ declare module 'discord.js' { type GuildMemberResolvable = GuildMember | UserResolvable; + interface GuildMembershipScreening { + description: string; + enabled: boolean; + formFields: GuildMembershipScreeningField[]; + } + + interface GuildMembershipScreeningField { + fieldType: MembershipScreeningType; + label: string; + required: boolean; + values?: string; + } + type GuildResolvable = Guild | GuildChannel | GuildMember | GuildEmoji | Invite | Role | Snowflake; interface GuildPruneMembersOptions { @@ -2745,6 +2764,8 @@ declare module 'discord.js' { type GuildTemplateResolvable = string; + type MembershipScreeningType = 'TERMS'; + type MembershipStates = 'INVITED' | 'ACCEPTED'; type MessageAdditions = MessageEmbed | MessageAttachment | (MessageEmbed | MessageAttachment)[];