Skip to content

Commit

Permalink
feat(WelcomeScreen): welcome screens (#5490)
Browse files Browse the repository at this point in the history
Co-authored-by: Jan <[email protected]>
Co-authored-by: izexi <[email protected]>
Co-authored-by: SpaceEEC <[email protected]>
Co-authored-by: Vlad Frangu <[email protected]>
  • Loading branch information
5 people authored Jun 19, 2021
1 parent 807ea2d commit 44e2ee7
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/managers/ChannelManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ChannelManager extends BaseManager {
const existing = this.cache.get(data.id);
if (existing) {
if (existing._patch && cache) existing._patch(data);
if (guild) guild.channels.add(existing);
if (guild) guild.channels?.add(existing);
return existing;
}

Expand Down
78 changes: 78 additions & 0 deletions src/structures/AnonymousGuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict';

const BaseGuild = require('./BaseGuild');
const { VerificationLevels, NSFWLevels } = require('../util/Constants');

/**
* Bundles common attributes and methods between {@link Guild} and {@link InviteGuild}
* @abstract
*/
class AnonymousGuild extends BaseGuild {
constructor(client, data) {
super(client, data);
this._patch(data);
}

_patch(data) {
this.features = data.features;
/**
* The hash of the guild invite splash image
* @type {?string}
*/
this.splash = data.splash;

/**
* The hash of the guild banner
* @type {?string}
*/
this.banner = data.banner;

/**
* The description of the guild, if any
* @type {?string}
*/
this.description = data.description;

/**
* The verification level of the guild
* @type {VerificationLevel}
*/
this.verificationLevel = VerificationLevels[data.verification_level];

/**
* The vanity invite code of the guild, if any
* @type {?string}
*/
this.vanityURLCode = data.vanity_url_code;

if ('nsfw_level' in data) {
/**
* The NSFW level of this guild
* @type {NSFWLevel}
*/
this.nsfwLevel = NSFWLevels[data.nsfw_level];
}
}

/**
* The URL to this guild's banner.
* @param {ImageURLOptions} [options={}] Options for the Image URL
* @returns {?string}
*/
bannerURL({ format, size } = {}) {
if (!this.banner) return null;
return this.client.rest.cdn.Banner(this.id, this.banner, format, size);
}

/**
* The URL to this guild's invite splash image.
* @param {ImageURLOptions} [options={}] Options for the Image URL
* @returns {?string}
*/
splashURL({ format, size } = {}) {
if (!this.splash) return null;
return this.client.rest.cdn.Splash(this.id, this.splash, format, size);
}
}

module.exports = AnonymousGuild;
3 changes: 2 additions & 1 deletion src/structures/BaseGuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ const Base = require('./Base');
const SnowflakeUtil = require('../util/SnowflakeUtil');

/**
* The base class for {@link Guild} and {@link OAuth2Guild}.
* The base class for {@link Guild}, {@link OAuth2Guild} and {@link InviteGuild}.
* @extends {Base}
* @abstract
*/
class BaseGuild extends Base {
constructor(client, data) {
Expand Down
2 changes: 1 addition & 1 deletion src/structures/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class Channel extends Base {
break;
}
}
if (channel) guild.channels.cache.set(channel.id, channel);
if (channel) guild.channels?.cache.set(channel.id, channel);
}
}
return channel;
Expand Down
4 changes: 2 additions & 2 deletions src/structures/Emoji.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ class Emoji extends Base {
super(client);
/**
* Whether this emoji is animated
* @type {boolean}
* @type {?boolean}
*/
this.animated = emoji.animated;
this.animated = emoji.animated ?? null;

/**
* The name of this emoji
Expand Down
111 changes: 68 additions & 43 deletions src/structures/Guild.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use strict';

const BaseGuild = require('./BaseGuild');
const AnonymousGuild = require('./AnonymousGuild');
const GuildAuditLogs = require('./GuildAuditLogs');
const GuildPreview = require('./GuildPreview');
const GuildTemplate = require('./GuildTemplate');
const Integration = require('./Integration');
const Invite = require('./Invite');
const Webhook = require('./Webhook');
const WelcomeScreen = require('./WelcomeScreen');
const { Error, TypeError } = require('../errors');
const GuildApplicationCommandManager = require('../managers/GuildApplicationCommandManager');
const GuildBanManager = require('../managers/GuildBanManager');
Expand All @@ -24,7 +25,6 @@ const {
PartialTypes,
VerificationLevels,
ExplicitContentFilterLevels,
NSFWLevels,
Status,
MFALevels,
PremiumTiers,
Expand All @@ -37,9 +37,9 @@ const Util = require('../util/Util');
* Represents a guild (or a server) on Discord.
* <info>It's recommended to see if a guild is available before performing operations or reading data from it. You can
* check this with `guild.available`.</info>
* @extends {BaseGuild}
* @extends {AnonymousGuild}
*/
class Guild extends BaseGuild {
class Guild extends AnonymousGuild {
constructor(client, data) {
super(client, data);

Expand Down Expand Up @@ -131,18 +131,12 @@ class Guild extends BaseGuild {
* @private
*/
_patch(data) {
super._patch(data);
this.id = data.id;
this.name = data.name;
this.icon = data.icon;
this.features = data.features;
this.available = !data.unavailable;

/**
* The hash of the guild invite splash image
* @type {?string}
*/
this.splash = data.splash;

/**
* The hash of the guild discovery splash image
* @type {?string}
Expand All @@ -155,14 +149,6 @@ class Guild extends BaseGuild {
*/
this.memberCount = data.member_count || this.memberCount;

if ('nsfw_level' in data) {
/**
* The NSFW level of this guild
* @type {NSFWLevel}
*/
this.nsfwLevel = NSFWLevels[data.nsfw_level];
}

/**
* Whether the guild is "large" (has more than large_threshold members, 50 by default)
* @type {boolean}
Expand Down Expand Up @@ -247,12 +233,6 @@ class Guild extends BaseGuild {
this.widgetChannelID = data.widget_channel_id;
}

/**
* The verification level of the guild
* @type {VerificationLevel}
*/
this.verificationLevel = VerificationLevels[data.verification_level];

/**
* The explicit content filter level of the guild
* @type {ExplicitContentFilterLevel}
Expand Down Expand Up @@ -326,31 +306,13 @@ class Guild extends BaseGuild {
this.approximatePresenceCount = null;
}

/**
* The vanity invite code of the guild, if any
* @type {?string}
*/
this.vanityURLCode = data.vanity_url_code;

/**
* The use count of the vanity URL code of the guild, if any
* <info>You will need to fetch this parameter using {@link Guild#fetchVanityData} if you want to receive it</info>
* @type {?number}
*/
this.vanityURLUses = null;

/**
* The description of the guild, if any
* @type {?string}
*/
this.description = data.description;

/**
* The hash of the guild banner
* @type {?string}
*/
this.banner = data.banner;

/**
* The ID of the rules channel for the guild
* @type {?Snowflake}
Expand Down Expand Up @@ -576,6 +538,15 @@ class Guild extends BaseGuild {
);
}

/**
* Fetches the welcome screen for this guild.
* @returns {Promise<WelcomeScreen>}
*/
async fetchWelcomeScreen() {
const data = await this.client.api.guilds(this.id, 'welcome-screen').get();
return new WelcomeScreen(this, data);
}

/**
* The data for creating an integration.
* @typedef {Object} IntegrationData
Expand Down Expand Up @@ -900,6 +871,60 @@ class Guild extends BaseGuild {
.then(newData => this.client.actions.GuildUpdate.handle(newData).updated);
}

/**
* Welcome channel data
* @typedef {Object} WelcomeChannelData
* @property {string} description The description to show for this welcome channel
* @property {GuildTextChannelResolvable} channel The channel to link for this welcome channel
* @property {EmojiIdentifierResolvable} [emoji] The emoji to display for this welcome channel
*/

/**
* Welcome screen edit data
* @typedef {Object} WelcomeScreenEditData
* @property {boolean} [enabled] Whether the welcome screen is enabled
* @property {string} [description] The description for the welcome screen
* @property {WelcomeChannelData[]} [welcomeChannels] The welcome channel data for the welcome screen
*/

/**
* Updates the guild's welcome screen
* @param {WelcomeScreenEditData} data Data to edit the welcome screen with
* @returns {Promise<WelcomeScreen>}
* @example
* guild.editWelcomeScreen({
* description: 'Hello World',
* enabled: true,
* welcomeChannels: [
* {
* description: 'foobar',
* channel: '222197033908436994',
* }
* ],
* })
*/
async editWelcomeScreen(data) {
const { enabled, description, welcomeChannels } = data;
const welcome_channels = welcomeChannels?.map(welcomeChannelData => {
const emoji = this.emojis.resolve(welcomeChannelData.emoji);
return {
emoji_id: emoji?.id ?? null,
emoji_name: emoji?.name ?? welcomeChannelData.emoji,
channel_id: this.channels.resolveID(welcomeChannelData.channel),
description: welcomeChannelData.description,
};
});

const patchData = await this.client.api.guilds(this.id, 'welcome-screen').patch({
data: {
welcome_channels,
description,
enabled,
},
});
return new WelcomeScreen(this, patchData);
}

/**
* Edits the level of the explicit content filter.
* @param {ExplicitContentFilterLevel|number} explicitContentFilter The new level of the explicit content filter
Expand Down
11 changes: 8 additions & 3 deletions src/structures/Invite.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ class Invite extends Base {
}

_patch(data) {
const InviteGuild = require('./InviteGuild');
const Guild = require('./Guild');
/**
* The guild the invite is for
* @type {?Guild}
* The guild the invite is for including welcome screen data if present
* @type {?(Guild|InviteGuild)}
*/
this.guild = data.guild ? this.client.guilds.add(data.guild, false) : null;
this.guild = null;
if (data.guild) {
this.guild = data.guild instanceof Guild ? data.guild : new InviteGuild(this.client, data.guild);
}

/**
* The code for this invite
Expand Down
23 changes: 23 additions & 0 deletions src/structures/InviteGuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

const AnonymousGuild = require('./AnonymousGuild');
const WelcomeScreen = require('./WelcomeScreen');

/**
* Represents a guild received from an invite, includes welcome screen data if available.
* @extends {AnonymousGuild}
*/
class InviteGuild extends AnonymousGuild {
constructor(client, data) {
super(client, data);

/**
* The welcome screen for this invite guild
* @type {?WelcomeScreen}
*/
this.welcomeScreen =
typeof data.welcome_screen !== 'undefined' ? new WelcomeScreen(this, data.welcome_screen) : null;
}
}

module.exports = InviteGuild;
Loading

0 comments on commit 44e2ee7

Please sign in to comment.