From 2ee0f1cdc69dcd0c8f870bcecc3d36aef0e44ad1 Mon Sep 17 00:00:00 2001
From: Sugden <28943913+NotSugden@users.noreply.github.com>
Date: Sat, 29 Feb 2020 06:43:42 +0000
Subject: [PATCH] =?UTF-8?q?feat(GuildManager):=20Allow=20for=20more=20opti?=
=?UTF-8?q?ons=20for=20GuildManager.cre=E2=80=A6=20(#3742)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* typings: add GuildVerificationLevel and GuildExplicitContentFilter
* implement new types
* fix jsdoc on stores
* typo
* add more options for GuildStore#create
* add channels and roles
* update typings
* fix typings and use snake case for permissionOverwrites
* typings & jsdoc
* fix tslint
* remove trailing whitespace
* fix jsdoc
* fix jsdoc
* fix oopsies
* fix lint
* fix lint
* fix mr lint man
* add typedefs and support for setting channel parents
* fix tab indenation
* update jsdoc
* suggested changes
* style: fix silly format
* docs(PartialChannelData): name is not optional
* style: remove silly format
---
src/managers/GuildManager.js | 144 ++++++++++++++++++++-----
src/structures/Guild.js | 36 ++++---
src/structures/GuildChannel.js | 4 +-
src/structures/PermissionOverwrites.js | 6 +-
src/util/Constants.js | 33 ++++--
typings/index.d.ts | 40 +++++--
6 files changed, 202 insertions(+), 61 deletions(-)
diff --git a/src/managers/GuildManager.js b/src/managers/GuildManager.js
index 8a28e06b2..407118633 100644
--- a/src/managers/GuildManager.js
+++ b/src/managers/GuildManager.js
@@ -2,10 +2,17 @@
const BaseManager = require('./BaseManager');
const DataResolver = require('../util/DataResolver');
-const { Events } = require('../util/Constants');
+const {
+ Events,
+ VerificationLevels,
+ DefaultMessageNotifications,
+ ExplicitContentFilterLevels,
+} = require('../util/Constants');
const Guild = require('../structures/Guild');
const GuildChannel = require('../structures/GuildChannel');
const GuildMember = require('../structures/GuildMember');
+const Permissions = require('../util/Permissions');
+const { resolveColor } = require('../util/Util');
const GuildEmoji = require('../structures/GuildEmoji');
const Invite = require('../structures/Invite');
const Role = require('../structures/Role');
@@ -36,6 +43,45 @@ class GuildManager extends BaseManager {
* @typedef {Guild|GuildChannel|GuildMember|GuildEmoji|Role|Snowflake|Invite} GuildResolvable
*/
+ /**
+ * Partial data for a Role.
+ * @typedef {Object} PartialRoleData
+ * @property {number} id The ID for this role, used to set channel overrides,
+ * this is a placeholder and will be replaced by the API after consumption
+ * @property {string} [name] The name of the role
+ * @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number
+ * @property {boolean} [hoist] Whether or not the role should be hoisted
+ * @property {number} [position] The position of the role
+ * @property {PermissionResolvable|number} [permissions] The permissions of the role
+ * @property {boolean} [mentionable] Whether or not the role should be mentionable
+ */
+
+ /**
+ * Partial overwrite data.
+ * @typedef {Object} PartialOverwriteData
+ * @property {number|Snowflake} id The Role or User ID for this overwrite
+ * @property {string} [type] The type of this overwrite
+ * @property {PermissionResolvable} [allow] The permissions to allow
+ * @property {PermissionResolvable} [deny] The permissions to deny
+ */
+
+ /**
+ * Partial data for a Channel.
+ * @typedef {Object} PartialChannelData
+ * @property {number} [id] The ID for this channel, used to set its parent,
+ * this is a placeholder and will be replaced by the API after consumption
+ * @property {number} [parentID] The parent ID for this channel
+ * @property {string} [type] The type of the channel
+ * @property {string} name The name of the channel
+ * @property {string} [topic] The topic of the text channel
+ * @property {boolean} [nsfw] Whether the channel is NSFW
+ * @property {number} [bitrate] The bitrate of the voice channel
+ * @property {number} [userLimit] The user limit of the channel
+ * @property {PartialOverwriteData} [permissionOverwrites]
+ * Overwrites of the channel
+ * @property {number} [rateLimitPerUser] The rate limit per user of the channel in seconds
+ */
+
/**
* Resolves a GuildResolvable to a Guild object.
* @method resolve
@@ -75,37 +121,81 @@ class GuildManager extends BaseManager {
* This is only available to bots in fewer than 10 guilds.
* @param {string} name The name of the guild
* @param {Object} [options] Options for the creating
- * @param {string} [options.region] The region for the server, defaults to the closest one available
+ * @param {PartialChannelData[]} [options.channels] The channels for this guild
+ * @param {DefaultMessageNotifications} [options.defaultMessageNotifications] The default message notifications
+ * for the guild
+ * @param {ExplicitContentFilterLevel} [options.explicitContentFilter] The explicit content filter level for the guild
* @param {BufferResolvable|Base64Resolvable} [options.icon=null] The icon for the guild
+ * @param {string} [options.region] The region for the server, defaults to the closest one available
+ * @param {PartialRoleData[]} [options.roles] The roles for this guild,
+ * the first element of this array is used to change properties of the guild's everyone role.
+ * @param {VerificationLevel} [options.verificationLevel] The verification level for the guild
* @returns {Promise} The guild that was created
*/
- create(name, { region, icon = null } = {}) {
- if (!icon || (typeof icon === 'string' && icon.startsWith('data:'))) {
- return new Promise((resolve, reject) =>
- this.client.api.guilds.post({ data: { name, region, icon } })
- .then(data => {
- if (this.client.guilds.cache.has(data.id)) return resolve(this.client.guilds.cache.get(data.id));
-
- const handleGuild = guild => {
- if (guild.id === data.id) {
- this.client.removeListener(Events.GUILD_CREATE, handleGuild);
- this.client.clearTimeout(timeout);
- resolve(guild);
- }
- };
- this.client.on(Events.GUILD_CREATE, handleGuild);
-
- const timeout = this.client.setTimeout(() => {
- this.client.removeListener(Events.GUILD_CREATE, handleGuild);
- resolve(this.client.guilds.add(data));
- }, 10000);
- return undefined;
- }, reject),
- );
+ async create(name, {
+ channels = [],
+ defaultMessageNotifications,
+ explicitContentFilter,
+ icon = null,
+ region,
+ roles = [],
+ verificationLevel,
+ } = {}) {
+ icon = await DataResolver.resolveImage(icon);
+ if (typeof verificationLevel !== 'undefined' && typeof verificationLevel !== 'number') {
+ verificationLevel = VerificationLevels.indexOf(verificationLevel);
}
+ if (typeof defaultMessageNotifications !== 'undefined' && typeof defaultMessageNotifications !== 'number') {
+ defaultMessageNotifications = DefaultMessageNotifications.indexOf(defaultMessageNotifications);
+ }
+ if (typeof explicitContentFilter !== 'undefined' && typeof explicitContentFilter !== 'number') {
+ explicitContentFilter = ExplicitContentFilterLevels.indexOf(explicitContentFilter);
+ }
+ for (const channel of channels) {
+ channel.parent_id = channel.parentID;
+ delete channel.parentID;
+ if (!channel.permissionOverwrites) continue;
+ for (const overwrite of channel.permissionOverwrites) {
+ if (overwrite.allow) overwrite.allow = Permissions.resolve(overwrite.allow);
+ if (overwrite.deny) overwrite.deny = Permissions.resolve(overwrite.deny);
+ }
+ channel.permission_overwrites = channel.permissionOverwrites;
+ delete channel.permissionOverwrites;
+ }
+ for (const role of roles) {
+ if (role.color) role.color = resolveColor(role.color);
+ if (role.permissions) role.permissions = Permissions.resolve(role.permissions);
+ }
+ return new Promise((resolve, reject) =>
+ this.client.api.guilds.post({ data: {
+ name,
+ region,
+ icon,
+ verification_level: verificationLevel,
+ default_message_notifications: defaultMessageNotifications,
+ explicit_content_filter: explicitContentFilter,
+ channels,
+ roles,
+ } })
+ .then(data => {
+ if (this.client.guilds.cache.has(data.id)) return resolve(this.client.guilds.cache.get(data.id));
- return DataResolver.resolveImage(icon)
- .then(data => this.create(name, { region, icon: data || null }));
+ const handleGuild = guild => {
+ if (guild.id === data.id) {
+ this.client.removeListener(Events.GUILD_CREATE, handleGuild);
+ this.client.clearTimeout(timeout);
+ resolve(guild);
+ }
+ };
+ this.client.on(Events.GUILD_CREATE, handleGuild);
+
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener(Events.GUILD_CREATE, handleGuild);
+ resolve(this.client.guilds.add(data));
+ }, 10000);
+ return undefined;
+ }, reject),
+ );
}
}
diff --git a/src/structures/Guild.js b/src/structures/Guild.js
index 2368ae626..57431e42c 100644
--- a/src/structures/Guild.js
+++ b/src/structures/Guild.js
@@ -5,7 +5,13 @@ const Integration = require('./Integration');
const GuildAuditLogs = require('./GuildAuditLogs');
const Webhook = require('./Webhook');
const VoiceRegion = require('./VoiceRegion');
-const { ChannelTypes, DefaultMessageNotifications, PartialTypes } = require('../util/Constants');
+const {
+ ChannelTypes,
+ DefaultMessageNotifications,
+ PartialTypes,
+ VerificationLevels,
+ ExplicitContentFilterLevels,
+} = require('../util/Constants');
const Collection = require('../util/Collection');
const Util = require('../util/Util');
const DataResolver = require('../util/DataResolver');
@@ -247,15 +253,15 @@ class Guild extends Base {
/**
* The verification level of the guild
- * @type {number}
+ * @type {VerificationLevel}
*/
- this.verificationLevel = data.verification_level;
+ this.verificationLevel = VerificationLevels[data.verification_level];
/**
* The explicit content filter level of the guild
- * @type {number}
+ * @type {ExplicitContentFilterLevel}
*/
- this.explicitContentFilter = data.explicit_content_filter;
+ this.explicitContentFilter = ExplicitContentFilterLevels[data.explicit_content_filter];
/**
* The required MFA level for the guild
@@ -821,8 +827,8 @@ class Guild extends Base {
* @typedef {Object} GuildEditData
* @property {string} [name] The name of the guild
* @property {string} [region] The region of the guild
- * @property {number} [verificationLevel] The verification level of the guild
- * @property {number} [explicitContentFilter] The level of the explicit content filter
+ * @property {VerificationLevel|number} [verificationLevel] The verification level of the guild
+ * @property {ExplicitContentFilterLevel|number} [explicitContentFilter] The level of the explicit content filter
* @property {ChannelResolvable} [afkChannel] The AFK channel of the guild
* @property {ChannelResolvable} [systemChannel] The system channel of the guild
* @property {number} [afkTimeout] The AFK timeout of the guild
@@ -852,7 +858,11 @@ class Guild extends Base {
const _data = {};
if (data.name) _data.name = data.name;
if (data.region) _data.region = data.region;
- if (typeof data.verificationLevel !== 'undefined') _data.verification_level = Number(data.verificationLevel);
+ if (typeof data.verificationLevel !== 'undefined') {
+ _data.verification_level = typeof data.verificationLevel === 'number' ?
+ Number(data.verificationLevel) :
+ ExplicitContentFilterLevels.indexOf(data.verificationLevel);
+ }
if (typeof data.afkChannel !== 'undefined') {
_data.afk_channel_id = this.client.channels.resolveID(data.afkChannel);
}
@@ -865,12 +875,14 @@ class Guild extends Base {
if (data.splash) _data.splash = data.splash;
if (data.banner) _data.banner = data.banner;
if (typeof data.explicitContentFilter !== 'undefined') {
- _data.explicit_content_filter = Number(data.explicitContentFilter);
+ _data.explicit_content_filter = typeof data.explicitContentFilter === 'number' ?
+ data.explicitContentFilter :
+ ExplicitContentFilterLevels.indexOf(data.explicitContentFilter);
}
if (typeof data.defaultMessageNotifications !== 'undefined') {
_data.default_message_notifications = typeof data.defaultMessageNotifications === 'string' ?
DefaultMessageNotifications.indexOf(data.defaultMessageNotifications) :
- Number(data.defaultMessageNotifications);
+ data.defaultMessageNotifications;
}
if (typeof data.systemChannelFlags !== 'undefined') {
_data.system_channel_flags = SystemChannelFlags.resolve(data.systemChannelFlags);
@@ -881,7 +893,7 @@ class Guild extends Base {
/**
* Edits the level of the explicit content filter.
- * @param {number} explicitContentFilter The new level of the explicit content filter
+ * @param {ExplicitContentFilterLevel|number} explicitContentFilter The new level of the explicit content filter
* @param {string} [reason] Reason for changing the level of the guild's explicit content filter
* @returns {Promise}
*/
@@ -943,7 +955,7 @@ class Guild extends Base {
/**
* Edits the verification level of the guild.
- * @param {number} verificationLevel The new verification level of the guild
+ * @param {VerificationLevel|number} verificationLevel The new verification level of the guild
* @param {string} [reason] Reason for changing the guild's verification level
* @returns {Promise}
* @example
diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js
index 35a0bc2ab..d80f5b24f 100644
--- a/src/structures/GuildChannel.js
+++ b/src/structures/GuildChannel.js
@@ -211,7 +211,7 @@ class GuildChannel extends Channel {
/**
* Updates Overwrites for a user or role in this channel. (creates if non-existent)
* @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
- * @param {PermissionOverwriteOption} options The options for the update
+ * @param {PermissionOverwriteOptions} options The options for the update
* @param {string} [reason] Reason for creating/editing this overwrite
* @returns {Promise}
* @example
@@ -234,7 +234,7 @@ class GuildChannel extends Channel {
/**
* Overwrites the permissions for a user or role in this channel. (replaces if existent)
* @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
- * @param {PermissionOverwriteOption} options The options for the update
+ * @param {PermissionOverwriteOptions} options The options for the update
* @param {string} [reason] Reason for creating/editing this overwrite
* @returns {Promise}
* @example
diff --git a/src/structures/PermissionOverwrites.js b/src/structures/PermissionOverwrites.js
index cf11eaa94..59a5d93f8 100644
--- a/src/structures/PermissionOverwrites.js
+++ b/src/structures/PermissionOverwrites.js
@@ -56,7 +56,7 @@ class PermissionOverwrites {
/**
* Updates this permissionOverwrites.
- * @param {PermissionOverwriteOption} options The options for the update
+ * @param {PermissionOverwriteOptions} options The options for the update
* @param {string} [reason] Reason for creating/editing this overwrite
* @returns {Promise}
* @example
@@ -99,7 +99,7 @@ class PermissionOverwrites {
* 'ATTACH_FILES': false,
* }
* ```
- * @typedef {Object} PermissionOverwriteOption
+ * @typedef {Object} PermissionOverwriteOptions
*/
/**
@@ -110,7 +110,7 @@ class PermissionOverwrites {
/**
* Deletes this Permission Overwrite.
- * @param {PermissionOverwriteOption} options The options for the update
+ * @param {PermissionOverwriteOptions} options The options for the update
* @param {Object} initialPermissions The initial permissions
* @param {PermissionResolvable} initialPermissions.allow Initial allowed permissions
* @param {PermissionResolvable} initialPermissions.deny Initial denied permissions
diff --git a/src/util/Constants.js b/src/util/Constants.js
index b38e0b580..547ffde1d 100644
--- a/src/util/Constants.js
+++ b/src/util/Constants.js
@@ -493,21 +493,34 @@ exports.Colors = {
NOT_QUITE_BLACK: 0x23272A,
};
+/**
+ * The value set for the explicit content filter levels for a guild:
+ * * DISABLED
+ * * MEMBERS_WITHOUT_ROLES
+ * * ALL_MEMBERS
+ * @typedef {string} ExplicitContentFilterLevel
+ */
+exports.ExplicitContentFilterLevels = [
+ 'DISABLED',
+ 'MEMBERS_WITHOUT_ROLES',
+ 'ALL_MEMBERS',
+];
+
/**
* The value set for the verification levels for a guild:
- * * None
- * * Low
- * * Medium
- * * (╯°□°)╯︵ ┻━┻
- * * ┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻
+ * * NONE
+ * * LOW
+ * * MEDIUM
+ * * HIGH
+ * * VERY_HIGH
* @typedef {string} VerificationLevel
*/
exports.VerificationLevels = [
- 'None',
- 'Low',
- 'Medium',
- '(╯°□°)╯︵ ┻━┻',
- '┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻',
+ 'NONE',
+ 'LOW',
+ 'MEDIUM',
+ 'HIGH',
+ 'VERY_HIGH',
];
/**
diff --git a/typings/index.d.ts b/typings/index.d.ts
index bed7fa219..1bc54ae47 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -582,7 +582,9 @@ declare module 'discord.js' {
};
MessageTypes: MessageType[];
ActivityTypes: ActivityType[];
+ ExplicitContentFilterLevels: ExplicitContentFilterLevel[];
DefaultMessageNotifications: DefaultMessageNotifications[];
+ VerificationLevels: VerificationLevel[];
MembershipStates: 'INVITED' | 'ACCEPTED';
};
@@ -647,7 +649,7 @@ declare module 'discord.js' {
public embedChannelID: Snowflake | null;
public embedEnabled: boolean;
public emojis: GuildEmojiManager;
- public explicitContentFilter: number;
+ public explicitContentFilter: ExplicitContentFilterLevel;
public features: GuildFeatures[];
public icon: string | null;
public id: Snowflake;
@@ -681,7 +683,7 @@ declare module 'discord.js' {
public systemChannelFlags: Readonly;
public systemChannelID: Snowflake | null;
public vanityURLCode: string | null;
- public verificationLevel: number;
+ public verificationLevel: VerificationLevel;
public readonly verified: boolean;
public readonly voice: VoiceState | null;
public readonly voiceStates: VoiceStateManager;
@@ -713,7 +715,7 @@ declare module 'discord.js' {
public setChannelPositions(channelPositions: ChannelPosition[]): Promise;
public setDefaultMessageNotifications(defaultMessageNotifications: DefaultMessageNotifications | number, reason?: string): Promise;
public setEmbed(embed: GuildEmbedData, reason?: string): Promise;
- public setExplicitContentFilter(explicitContentFilter: number, reason?: string): Promise;
+ public setExplicitContentFilter(explicitContentFilter: ExplicitContentFilterLevel, reason?: string): Promise;
public setIcon(icon: Base64Resolvable | null, reason?: string): Promise;
public setName(name: string, reason?: string): Promise;
public setOwner(owner: GuildMemberResolvable, reason?: string): Promise;
@@ -722,7 +724,7 @@ declare module 'discord.js' {
public setSplash(splash: Base64Resolvable | null, reason?: string): Promise;
public setSystemChannel(systemChannel: ChannelResolvable | null, reason?: string): Promise;
public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise;
- public setVerificationLevel(verificationLevel: number, reason?: string): Promise;
+ public setVerificationLevel(verificationLevel: VerificationLevel, reason?: string): Promise;
public splashURL(options?: ImageURLOptions): string | null;
public toJSON(): object;
public toString(): string;
@@ -2179,6 +2181,8 @@ declare module 'discord.js' {
codeBlockContent?: boolean;
}
+ type ExplicitContentFilterLevel = 'DISABLED' | 'MEMBERS_WITHOUT_ROLES' | 'ALL_MEMBERS';
+
interface Extendable {
GuildEmoji: typeof GuildEmoji;
DMChannel: typeof DMChannel;
@@ -2303,8 +2307,8 @@ declare module 'discord.js' {
interface GuildEditData {
name?: string;
region?: string;
- verificationLevel?: number;
- explicitContentFilter?: number;
+ verificationLevel?: VerificationLevel;
+ explicitContentFilter?: ExplicitContentFilterLevel;
defaultMessageNotifications?: DefaultMessageNotifications | number;
afkChannel?: ChannelResolvable;
systemChannel?: ChannelResolvable;
@@ -2625,9 +2629,27 @@ declare module 'discord.js' {
};
interface PartialChannel extends Partialize {}
+
+ interface PartialChannelData {
+ id?: number;
+ name: string;
+ topic?: string;
+ type?: ChannelType;
+ parentID?: number;
+ permissionOverwrites?: {
+ id: number | Snowflake;
+ type?: OverwriteType;
+ allow?: PermissionResolvable;
+ deny?: PermissionResolvable;
+ }[];
+ }
+
interface PartialGuildMember extends Partialize {}
interface PartialMessage extends Partialize {}
- interface PartialUser extends Partialize {}
+
+ interface PartialRoleData extends RoleData {
+ id?: number;
+ }
type PartialTypes = 'USER'
| 'CHANNEL'
@@ -2635,6 +2657,8 @@ declare module 'discord.js' {
| 'MESSAGE'
| 'REACTION';
+ interface PartialUser extends Partialize {}
+
type PresenceStatus = ClientPresenceStatus | 'offline';
type PresenceStatusData = ClientPresenceStatus | 'invisible';
@@ -2720,6 +2744,8 @@ declare module 'discord.js' {
type UserResolvable = User | Snowflake | Message | GuildMember;
+ type VerificationLevel = 'NONE' | 'LOW' | 'MEDIUM' | 'HIGH' | 'VERY_HIGH';
+
type VoiceStatus = number;
interface WebhookEditData {