feat: onboarding mode and edit method (#9647)

* feat: onboarding mode and edit method

* feat(guild): add `editOnboarding`

* fix: use discord-api-types

* types: make arrays readonly

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* fix: bring up to date

* docs: id is a snowflake

* fix: requested changes

* refactor: make most options optional

* refactor: provide GuildEmoji or Emoji instance

* revert: changes to Util

* fix: rebase leftovers

* fix: allow passing option id

* fix: requested changes

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Almeida
2023-10-17 20:30:10 +01:00
committed by GitHub
parent e307581442
commit 7671a836f4
8 changed files with 185 additions and 20 deletions

View File

@@ -2,6 +2,7 @@
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const { ChannelType, GuildPremiumTier, Routes, GuildFeature } = require('discord-api-types/v10');
const AnonymousGuild = require('./AnonymousGuild');
const GuildAuditLogs = require('./GuildAuditLogs');
@@ -28,7 +29,7 @@ const VoiceStateManager = require('../managers/VoiceStateManager');
const DataResolver = require('../util/DataResolver');
const Status = require('../util/Status');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
const { discordSort, getSortableGroupTypes } = require('../util/Util');
const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util');
/**
* Represents a guild (or a server) on Discord.
@@ -881,6 +882,85 @@ class Guild extends AnonymousGuild {
return this.client.actions.GuildUpdate.handle(data).updated;
}
/**
* Options used to edit the guild onboarding.
* @typedef {Object} GuildOnboardingEditOptions
* @property {GuildOnboardingPromptData[]|Collection<Snowflake, GuildOnboardingPrompt>} [prompts]
* The prompts shown during onboarding and in customize community
* @property {ChannelResolvable[]|Collection<Snowflake, GuildChannel>} [defaultChannels]
* The channels that new members get opted into automatically
* @property {boolean} [enabled] Whether the onboarding is enabled
* @property {GuildOnboardingMode} [mode] The mode to edit the guild onboarding with
* @property {string} [reason] The reason for editing the guild onboarding
*/
/**
* Data for editing a guild onboarding prompt.
* @typedef {Object} GuildOnboardingPromptData
* @property {Snowflake} [id] The id of the prompt
* @property {string} title The title for the prompt
* @property {boolean} [singleSelect] Whether users are limited to selecting one option for the prompt
* @property {boolean} [required] Whether the prompt is required before a user completes the onboarding flow
* @property {boolean} [inOnboarding] Whether the prompt is present in the onboarding flow
* @property {GuildOnboardingPromptType} [type] The type of the prompt
* @property {GuildOnboardingPromptOptionData[]|Collection<Snowflake, GuildOnboardingPrompt>} options
* The options available within the prompt
*/
/**
* Data for editing a guild onboarding prompt option.
* @typedef {Object} GuildOnboardingPromptOptionData
* @property {?Snowflake} [id] The id of the option
* @property {ChannelResolvable[]|Collection<Snowflake, GuildChannel>} [channels]
* The channels a member is added to when the option is selected
* @property {RoleResolvable[]|Collection<Snowflake, Role>} [roles]
* The roles assigned to a member when the option is selected
* @property {string} title The title of the option
* @property {?string} [description] The description of the option
* @property {?(EmojiIdentifierResolvable|Emoji)} [emoji] The emoji of the option
*/
/**
* Edits the guild onboarding data for this guild.
* @param {GuildOnboardingEditOptions} options The options to provide
* @returns {Promise<GuildOnboarding>}
*/
async editOnboarding(options) {
const newData = await this.client.rest.put(Routes.guildOnboarding(this.id), {
body: {
prompts: options.prompts?.map(prompt => ({
// Currently, the prompt ids are required even for new ones (which won't be used)
id: prompt.id ?? DiscordSnowflake.generate().toString(),
title: prompt.title,
single_select: prompt.singleSelect,
required: prompt.required,
in_onboarding: prompt.inOnboarding,
type: prompt.type,
options: prompt.options.map(option => {
const emoji = resolvePartialEmoji(option.emoji);
return {
id: option.id,
channel_ids: option.channels?.map(channel => this.channels.resolveId(channel)),
role_ids: option.roles?.map(role => this.roles.resolveId(role)),
title: option.title,
description: option.description,
emoji_animated: emoji?.animated,
emoji_id: emoji?.id,
emoji_name: emoji?.name,
};
}),
})),
default_channel_ids: options.defaultChannels?.map(channel => this.channels.resolveId(channel)),
enabled: options.enabled,
mode: options.mode,
},
reason: options.reason,
});
return new GuildOnboarding(this.client, newData);
}
/**
* Welcome channel data
* @typedef {Object} WelcomeChannelData

View File

@@ -43,6 +43,12 @@ class GuildOnboarding extends Base {
* @type {boolean}
*/
this.enabled = data.enabled;
/**
* The mode of this onboarding
* @type {GuildOnboardingMode}
*/
this.mode = data.mode;
}
/**

View File

@@ -2,7 +2,7 @@
const { Collection } = require('@discordjs/collection');
const Base = require('./Base');
const { resolvePartialEmoji } = require('../util/Util');
const { Emoji } = require('./Emoji.js');
/**
* Represents the data of an option from a prompt of a guilds onboarding.
@@ -45,18 +45,11 @@ class GuildOnboardingPromptOption extends Base {
);
/**
* The data for an emoji of a guilds onboarding prompt option
* @typedef {Object} GuildOnboardingPromptOptionEmoji
* @property {?Snowflake} id The id of the emoji
* @property {string} name The name of the emoji
* @property {boolean} animated Whether the emoji is animated
* The raw emoji of the option
* @type {APIPartialEmoji}
* @private
*/
/**
* The emoji of the option
* @type {?GuildOnboardingPromptOptionEmoji}
*/
this.emoji = resolvePartialEmoji(data.emoji);
this._emoji = data.emoji;
/**
* The title of the option
@@ -79,6 +72,15 @@ class GuildOnboardingPromptOption extends Base {
get guild() {
return this.client.guilds.cache.get(this.guildId);
}
/**
* The emoji of this onboarding prompt option
* @type {?(GuildEmoji|Emoji)}
*/
get emoji() {
if (!this._emoji.id && !this._emoji.name) return null;
return this.client.emojis.resolve(this._emoji.id) ?? new Emoji(this.client, this._emoji);
}
}
exports.GuildOnboardingPromptOption = GuildOnboardingPromptOption;

View File

@@ -160,6 +160,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIModalSubmission}
*/
/**
* @external APIPartialEmoji
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIPartialEmoji}
*/
/**
* @external APIRole
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIRole}
@@ -335,6 +340,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildNSFWLevel}
*/
/**
* @external GuildOnboardingMode
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildOnboardingMode}
*/
/**
* @external GuildOnboardingPromptType
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildOnboardingPromptType}

View File

@@ -111,7 +111,7 @@ function parseEmoji(text) {
/**
* Resolves a partial emoji object from an {@link EmojiIdentifierResolvable}, without checking a Client.
* @param {Emoji|EmojiIdentifierResolvable} emoji Emoji identifier to resolve
* @returns {?(PartialEmoji|PartialEmojiOnlyId)} Suppling a snowflake yields `PartialEmojiOnlyId`.
* @returns {?(PartialEmoji|PartialEmojiOnlyId)} Supplying a snowflake yields `PartialEmojiOnlyId`.
* @private
*/
function resolvePartialEmoji(emoji) {