summaryrefslogtreecommitdiff
path: root/node_modules/discord.js/src/structures
diff options
context:
space:
mode:
author8cy <[email protected]>2020-04-03 02:37:42 -0700
committer8cy <[email protected]>2020-04-03 02:37:42 -0700
commit60867fb030bae582082340ead7dbc7efdc2f5398 (patch)
tree4c6a7356351be2e4914e15c4703172597c45656e /node_modules/discord.js/src/structures
parentcommenting (diff)
downloads5nical-60867fb030bae582082340ead7dbc7efdc2f5398.tar.xz
s5nical-60867fb030bae582082340ead7dbc7efdc2f5398.zip
2020/04/03, 02:34, v1.2.0
Diffstat (limited to 'node_modules/discord.js/src/structures')
-rw-r--r--node_modules/discord.js/src/structures/Attachment.js75
-rw-r--r--node_modules/discord.js/src/structures/CategoryChannel.js22
-rw-r--r--node_modules/discord.js/src/structures/Channel.js78
-rw-r--r--node_modules/discord.js/src/structures/ClientUser.js447
-rw-r--r--node_modules/discord.js/src/structures/ClientUserChannelOverride.js30
-rw-r--r--node_modules/discord.js/src/structures/ClientUserGuildSettings.js60
-rw-r--r--node_modules/discord.js/src/structures/ClientUserSettings.js80
-rw-r--r--node_modules/discord.js/src/structures/DMChannel.js76
-rw-r--r--node_modules/discord.js/src/structures/Emoji.js273
-rw-r--r--node_modules/discord.js/src/structures/GroupDMChannel.js246
-rw-r--r--node_modules/discord.js/src/structures/Guild.js1711
-rw-r--r--node_modules/discord.js/src/structures/GuildAuditLogs.js462
-rw-r--r--node_modules/discord.js/src/structures/GuildChannel.js601
-rw-r--r--node_modules/discord.js/src/structures/GuildMember.js636
-rw-r--r--node_modules/discord.js/src/structures/Integration.js151
-rw-r--r--node_modules/discord.js/src/structures/Invite.js164
-rw-r--r--node_modules/discord.js/src/structures/Message.js661
-rw-r--r--node_modules/discord.js/src/structures/MessageAttachment.js79
-rw-r--r--node_modules/discord.js/src/structures/MessageCollector.js97
-rw-r--r--node_modules/discord.js/src/structures/MessageEmbed.js386
-rw-r--r--node_modules/discord.js/src/structures/MessageMentions.js185
-rw-r--r--node_modules/discord.js/src/structures/MessageReaction.js107
-rw-r--r--node_modules/discord.js/src/structures/NewsChannel.js24
-rw-r--r--node_modules/discord.js/src/structures/OAuth2Application.js157
-rw-r--r--node_modules/discord.js/src/structures/PartialGuild.js51
-rw-r--r--node_modules/discord.js/src/structures/PartialGuildChannel.js44
-rw-r--r--node_modules/discord.js/src/structures/PermissionOverwrites.js69
-rw-r--r--node_modules/discord.js/src/structures/Presence.js301
-rw-r--r--node_modules/discord.js/src/structures/ReactionCollector.js89
-rw-r--r--node_modules/discord.js/src/structures/ReactionEmoji.js98
-rw-r--r--node_modules/discord.js/src/structures/RichEmbed.js331
-rw-r--r--node_modules/discord.js/src/structures/Role.js376
-rw-r--r--node_modules/discord.js/src/structures/StoreChannel.js25
-rw-r--r--node_modules/discord.js/src/structures/Team.js109
-rw-r--r--node_modules/discord.js/src/structures/TeamMember.js67
-rw-r--r--node_modules/discord.js/src/structures/TextChannel.js154
-rw-r--r--node_modules/discord.js/src/structures/User.js336
-rw-r--r--node_modules/discord.js/src/structures/UserConnection.js48
-rw-r--r--node_modules/discord.js/src/structures/UserProfile.js62
-rw-r--r--node_modules/discord.js/src/structures/VoiceChannel.js146
-rw-r--r--node_modules/discord.js/src/structures/VoiceRegion.js50
-rw-r--r--node_modules/discord.js/src/structures/Webhook.js377
-rw-r--r--node_modules/discord.js/src/structures/interfaces/Collector.js208
-rw-r--r--node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js635
-rw-r--r--node_modules/discord.js/src/structures/shared/resolvePermissions.js26
45 files changed, 10410 insertions, 0 deletions
diff --git a/node_modules/discord.js/src/structures/Attachment.js b/node_modules/discord.js/src/structures/Attachment.js
new file mode 100644
index 0000000..216b61c
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Attachment.js
@@ -0,0 +1,75 @@
+/**
+ * Represents an attachment in a message.
+ * @param {BufferResolvable|Stream} file The file
+ * @param {string} [name] The name of the file, if any
+ */
+class Attachment {
+ constructor(file, name) {
+ this.file = null;
+ if (name) this.setAttachment(file, name);
+ else this._attach(file);
+ }
+
+ /**
+ * The name of the file
+ * @type {?string}
+ * @readonly
+ */
+ get name() {
+ return this.file.name;
+ }
+
+ /**
+ * The file
+ * @type {?BufferResolvable|Stream}
+ * @readonly
+ */
+ get attachment() {
+ return this.file.attachment;
+ }
+
+ /**
+ * Set the file of this attachment.
+ * @param {BufferResolvable|Stream} file The file
+ * @param {string} name The name of the file
+ * @returns {Attachment} This attachment
+ */
+ setAttachment(file, name) {
+ this.file = { attachment: file, name };
+ return this;
+ }
+
+ /**
+ * Set the file of this attachment.
+ * @param {BufferResolvable|Stream} attachment The file
+ * @returns {Attachment} This attachment
+ */
+ setFile(attachment) {
+ this.file = { attachment };
+ return this;
+ }
+
+ /**
+ * Set the name of this attachment.
+ * @param {string} name The name of the image
+ * @returns {Attachment} This attachment
+ */
+ setName(name) {
+ this.file.name = name;
+ return this;
+ }
+
+ /**
+ * Set the file of this attachment.
+ * @param {BufferResolvable|Stream} file The file
+ * @param {string} name The name of the file
+ * @returns {void}
+ * @private
+ */
+ _attach(file, name) {
+ if (typeof file === 'string') this.file = file;
+ else this.setAttachment(file, name);
+ }
+}
+
+module.exports = Attachment;
diff --git a/node_modules/discord.js/src/structures/CategoryChannel.js b/node_modules/discord.js/src/structures/CategoryChannel.js
new file mode 100644
index 0000000..9ba263a
--- /dev/null
+++ b/node_modules/discord.js/src/structures/CategoryChannel.js
@@ -0,0 +1,22 @@
+const GuildChannel = require('./GuildChannel');
+
+/**
+ * Represents a guild category channel on Discord.
+ * @extends {GuildChannel}
+ */
+class CategoryChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'category';
+ }
+ /**
+ * The channels that are part of this category
+ * @type {?Collection<Snowflake, GuildChannel>}
+ * @readonly
+ */
+ get children() {
+ return this.guild.channels.filter(c => c.parentID === this.id);
+ }
+}
+
+module.exports = CategoryChannel;
diff --git a/node_modules/discord.js/src/structures/Channel.js b/node_modules/discord.js/src/structures/Channel.js
new file mode 100644
index 0000000..732945d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Channel.js
@@ -0,0 +1,78 @@
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents any channel on Discord.
+ */
+class Channel {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the Channel
+ * @name Channel#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The type of the channel, either:
+ * * `dm` - a DM channel
+ * * `group` - a Group DM channel
+ * * `text` - a guild text channel
+ * * `voice` - a guild voice channel
+ * * `category` - a guild category channel
+ * * `news` - a guild news channel
+ * * `store` - a guild store channel
+ * @type {string}
+ */
+ this.type = null;
+
+ /**
+ * Whether the channel has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The unique ID of the channel
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+ }
+
+ /**
+ * The timestamp the channel was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the channel was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Deletes the channel.
+ * @returns {Promise<Channel>}
+ * @example
+ * // Delete the channel
+ * channel.delete()
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ delete() {
+ return this.client.rest.methods.deleteChannel(this);
+ }
+}
+
+module.exports = Channel;
diff --git a/node_modules/discord.js/src/structures/ClientUser.js b/node_modules/discord.js/src/structures/ClientUser.js
new file mode 100644
index 0000000..4ae8c6e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUser.js
@@ -0,0 +1,447 @@
+const User = require('./User');
+const Collection = require('../util/Collection');
+const ClientUserSettings = require('./ClientUserSettings');
+const ClientUserGuildSettings = require('./ClientUserGuildSettings');
+const Constants = require('../util/Constants');
+const util = require('util');
+
+/**
+ * Represents the logged in client's Discord user.
+ * @extends {User}
+ */
+class ClientUser extends User {
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * Whether or not this account has been verified
+ * @type {boolean}
+ */
+ this.verified = data.verified;
+
+ /**
+ * The email of this account
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?string}
+ * @deprecated
+ */
+ this.email = data.email;
+ this.localPresence = {};
+ this._typing = new Map();
+
+ /**
+ * A Collection of friends for the logged in user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, User>}
+ * @deprecated
+ */
+ this.friends = new Collection();
+
+ /**
+ * A Collection of blocked users for the logged in user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, User>}
+ * @deprecated
+ */
+ this.blocked = new Collection();
+
+ /**
+ * A Collection of notes for the logged in user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<Snowflake, string>}
+ * @deprecated
+ */
+ this.notes = new Collection();
+
+ /**
+ * If the user has Discord premium (nitro)
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?boolean}
+ * @deprecated
+ */
+ this.premium = typeof data.premium === 'boolean' ? data.premium : null;
+
+ /**
+ * If the user has MFA enabled on their account
+ * @type {boolean}
+ */
+ this.mfaEnabled = data.mfa_enabled;
+
+ /**
+ * If the user has ever used a mobile device on Discord
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?boolean}
+ * @deprecated
+ */
+ this.mobile = typeof data.mobile === 'boolean' ? data.mobile : null;
+
+ /**
+ * Various settings for this user
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {?ClientUserSettings}
+ * @deprecated
+ */
+ this.settings = data.user_settings ? new ClientUserSettings(this, data.user_settings) : null;
+
+ /**
+ * All of the user's guild settings
+ * <warn>This is only filled when using a user account</warn>
+ * @type {Collection<Snowflake, ClientUserGuildSettings>}
+ * @deprecated
+ */
+ this.guildSettings = new Collection();
+ if (data.user_guild_settings) {
+ for (const settings of data.user_guild_settings) {
+ this.guildSettings.set(settings.guild_id, new ClientUserGuildSettings(settings, this.client));
+ }
+ }
+ }
+
+ edit(data) {
+ return this.client.rest.methods.updateCurrentUser(data);
+ }
+
+ /**
+ * Set the username of the logged in client.
+ * <info>Changing usernames in Discord is heavily rate limited, with only 2 requests
+ * every hour. Use this sparingly!</info>
+ * @param {string} username The new username
+ * @param {string} [password] Current password (only for user accounts)
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set username
+ * client.user.setUsername('discordjs')
+ * .then(user => console.log(`My new username is ${user.username}`))
+ * .catch(console.error);
+ */
+ setUsername(username, password) {
+ return this.client.rest.methods.updateCurrentUser({ username }, password);
+ }
+
+ /**
+ * Changes the email for the client user's account.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {string} email New email to change to
+ * @param {string} password Current password
+ * @returns {Promise<ClientUser>}
+ * @deprecated
+ * @example
+ * // Set email
+ * client.user.setEmail('[email protected]', 'some amazing password 123')
+ * .then(user => console.log(`My new email is ${user.email}`))
+ * .catch(console.error);
+ */
+ setEmail(email, password) {
+ return this.client.rest.methods.updateCurrentUser({ email }, password);
+ }
+
+ /**
+ * Changes the password for the client user's account.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {string} newPassword New password to change to
+ * @param {string} oldPassword Current password
+ * @returns {Promise<ClientUser>}
+ * @deprecated
+ * @example
+ * // Set password
+ * client.user.setPassword('some new amazing password 456', 'some amazing password 123')
+ * .then(user => console.log('New password set!'))
+ * .catch(console.error);
+ */
+ setPassword(newPassword, oldPassword) {
+ return this.client.rest.methods.updateCurrentUser({ password: newPassword }, oldPassword);
+ }
+
+ /**
+ * Set the avatar of the logged in client.
+ * @param {BufferResolvable|Base64Resolvable} avatar The new avatar
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set avatar
+ * client.user.setAvatar('./avatar.png')
+ * .then(user => console.log(`New avatar set!`))
+ * .catch(console.error);
+ */
+ setAvatar(avatar) {
+ return this.client.resolver.resolveImage(avatar).then(data =>
+ this.client.rest.methods.updateCurrentUser({ avatar: data })
+ );
+ }
+
+ /**
+ * Data resembling a raw Discord presence.
+ * @typedef {Object} PresenceData
+ * @property {PresenceStatus} [status] Status of the user
+ * @property {boolean} [afk] Whether the user is AFK
+ * @property {Object} [game] Game the user is playing
+ * @property {string} [game.name] Name of the game
+ * @property {string} [game.url] Twitch stream URL
+ * @property {?ActivityType|number} [game.type] Type of the activity
+ */
+
+ /**
+ * Sets the full presence of the client user.
+ * @param {PresenceData} data Data for the presence
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set the client user's presence
+ * client.user.setPresence({ game: { name: 'with discord.js' }, status: 'idle' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setPresence(data) {
+ // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}}
+ return new Promise(resolve => {
+ let status = this.localPresence.status || this.presence.status;
+ let game = this.localPresence.game;
+ let afk = this.localPresence.afk || this.presence.afk;
+
+ if (!game && this.presence.game) {
+ game = {
+ name: this.presence.game.name,
+ type: this.presence.game.type,
+ url: this.presence.game.url,
+ };
+ }
+
+ if (data.status) {
+ if (typeof data.status !== 'string') throw new TypeError('Status must be a string');
+ if (this.bot) {
+ status = data.status;
+ } else {
+ this.settings.update(Constants.UserSettingsMap.status, data.status);
+ status = 'invisible';
+ }
+ }
+
+ if (data.game) {
+ game = data.game;
+ game.type = game.url && typeof game.type === 'undefined' ? 1 : game.type || 0;
+ if (typeof game.type === 'string') {
+ game.type = Constants.ActivityTypes.indexOf(game.type.toUpperCase());
+ }
+ } else if (typeof data.game !== 'undefined') {
+ game = null;
+ }
+
+ if (typeof data.afk !== 'undefined') afk = data.afk;
+ afk = Boolean(afk);
+
+ this.localPresence = { status, game, afk };
+ this.localPresence.since = 0;
+ this.localPresence.game = this.localPresence.game || null;
+
+ this.client.ws.send({
+ op: 3,
+ d: this.localPresence,
+ });
+
+ this.client._setPresence(this.id, this.localPresence);
+
+ resolve(this);
+ });
+ }
+
+ /**
+ * A user's status. Must be one of:
+ * * `online`
+ * * `idle`
+ * * `invisible`
+ * * `dnd` (do not disturb)
+ * @typedef {string} PresenceStatus
+ */
+
+ /**
+ * Sets the status of the client user.
+ * @param {PresenceStatus} status Status to change to
+ * @returns {Promise<ClientUser>}
+ * @example
+ * // Set the client user's status
+ * client.user.setStatus('idle')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setStatus(status) {
+ return this.setPresence({ status });
+ }
+
+ /**
+ * Sets the game the client user is playing.
+ * @param {?string} game Game being played
+ * @param {?string} [streamingURL] Twitch stream URL
+ * @returns {Promise<ClientUser>}
+ * @deprecated
+ */
+ setGame(game, streamingURL) {
+ if (!game) return this.setPresence({ game: null });
+ return this.setPresence({
+ game: {
+ name: game,
+ url: streamingURL,
+ },
+ });
+ }
+
+ /**
+ * Sets the activity the client user is playing.
+ * @param {?string} name Activity being played
+ * @param {Object} [options] Options for setting the activity
+ * @param {string} [options.url] Twitch stream URL
+ * @param {ActivityType|number} [options.type] Type of the activity
+ * @returns {Promise<Presence>}
+ * @example
+ * client.user.setActivity('YouTube', { type: 'WATCHING' })
+ * .then(presence => console.log(`Activity set to ${presence.game ? presence.game.name : 'none'}`))
+ * .catch(console.error);
+ */
+ setActivity(name, { url, type } = {}) {
+ if (!name) return this.setPresence({ game: null });
+ return this.setPresence({
+ game: { name, type, url },
+ }).then(clientUser => clientUser.presence);
+ }
+
+ /**
+ * Sets/removes the AFK flag for the client user.
+ * @param {boolean} afk Whether or not the user is AFK
+ * @returns {Promise<ClientUser>}
+ */
+ setAFK(afk) {
+ return this.setPresence({ afk });
+ }
+
+ /**
+ * Fetches messages that mentioned the client's user.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {Object} [options] Options for the fetch
+ * @param {number} [options.limit=25] Maximum number of mentions to retrieve
+ * @param {boolean} [options.roles=true] Whether to include role mentions
+ * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions
+ * @param {GuildResolvable} [options.guild] Limit the search to a specific guild
+ * @returns {Promise<Message[]>}
+ * @deprecated
+ * @example
+ * // Fetch mentions
+ * client.user.fetchMentions()
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Fetch mentions from a guild
+ * client.user.fetchMentions({ guild: '222078108977594368' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchMentions(options = {}) {
+ return this.client.rest.methods.fetchMentions(options);
+ }
+
+ /**
+ * Send a friend request.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {UserResolvable} user The user to send the friend request to
+ * @returns {Promise<User>} The user the friend request was sent to
+ * @deprecated
+ */
+ addFriend(user) {
+ user = this.client.resolver.resolveUser(user);
+ return this.client.rest.methods.addFriend(user);
+ }
+
+ /**
+ * Remove a friend.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {UserResolvable} user The user to remove from your friends
+ * @returns {Promise<User>} The user that was removed
+ * @deprecated
+ */
+ removeFriend(user) {
+ user = this.client.resolver.resolveUser(user);
+ return this.client.rest.methods.removeFriend(user);
+ }
+
+ /**
+ * Creates a guild.
+ * <warn>This is only available to bots in less than 10 guilds and user accounts.</warn>
+ * @param {string} name The name of the guild
+ * @param {string} [region] The region for the server
+ * @param {BufferResolvable|Base64Resolvable} [icon=null] The icon for the guild
+ * @returns {Promise<Guild>} The guild that was created
+ */
+ createGuild(name, region, icon = null) {
+ if (typeof icon === 'string' && icon.startsWith('data:')) {
+ return this.client.rest.methods.createGuild({ name, icon, region });
+ } else {
+ return this.client.resolver.resolveImage(icon).then(data =>
+ this.client.rest.methods.createGuild({ name, icon: data, region })
+ );
+ }
+ }
+
+ /**
+ * An object containing either a user or access token, and an optional nickname.
+ * @typedef {Object} GroupDMRecipientOptions
+ * @property {UserResolvable|Snowflake} [user] User to add to the Group DM
+ * (only available if a user is creating the DM)
+ * @property {string} [accessToken] Access token to use to add a user to the Group DM
+ * (only available if a bot is creating the DM)
+ * @property {string} [nick] Permanent nickname (only available if a bot is creating the DM)
+ */
+
+ /**
+ * Creates a Group DM.
+ * @param {GroupDMRecipientOptions[]} recipients The recipients
+ * @returns {Promise<GroupDMChannel>}
+ * @example
+ * // Create a Group DM with a token provided from OAuth
+ * client.user.createGroupDM([{
+ * user: '66564597481480192',
+ * accessToken: token
+ * }])
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ createGroupDM(recipients) {
+ return this.client.rest.methods.createGroupDM({
+ recipients: recipients.map(u => this.client.resolver.resolveUserID(u.user)),
+ accessTokens: recipients.map(u => u.accessToken),
+ nicks: recipients.reduce((o, r) => {
+ if (r.nick) o[r.user ? r.user.id : r.id] = r.nick;
+ return o;
+ }, {}),
+ });
+ }
+
+ /**
+ * Accepts an invite to join a guild.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {Invite|string} invite Invite or code to accept
+ * @returns {Promise<Guild>} Joined guild
+ * @deprecated
+ */
+ acceptInvite(invite) {
+ return this.client.rest.methods.acceptInvite(invite);
+ }
+}
+
+ClientUser.prototype.acceptInvite =
+ util.deprecate(ClientUser.prototype.acceptInvite, 'ClientUser#acceptInvite: userbot methods will be removed');
+
+ClientUser.prototype.setGame =
+ util.deprecate(ClientUser.prototype.setGame, 'ClientUser#setGame: use ClientUser#setActivity instead');
+
+ClientUser.prototype.addFriend =
+ util.deprecate(ClientUser.prototype.addFriend, 'ClientUser#addFriend: userbot methods will be removed');
+
+ClientUser.prototype.removeFriend =
+ util.deprecate(ClientUser.prototype.removeFriend, 'ClientUser#removeFriend: userbot methods will be removed');
+
+ClientUser.prototype.setPassword =
+ util.deprecate(ClientUser.prototype.setPassword, 'ClientUser#setPassword: userbot methods will be removed');
+
+ClientUser.prototype.setEmail =
+ util.deprecate(ClientUser.prototype.setEmail, 'ClientUser#setEmail: userbot methods will be removed');
+
+ClientUser.prototype.fetchMentions =
+ util.deprecate(ClientUser.prototype.fetchMentions, 'ClientUser#fetchMentions: userbot methods will be removed');
+
+module.exports = ClientUser;
diff --git a/node_modules/discord.js/src/structures/ClientUserChannelOverride.js b/node_modules/discord.js/src/structures/ClientUserChannelOverride.js
new file mode 100644
index 0000000..93efa45
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUserChannelOverride.js
@@ -0,0 +1,30 @@
+const Constants = require('../util/Constants');
+
+/**
+ * A wrapper around the ClientUser's channel overrides.
+ */
+class ClientUserChannelOverride {
+ constructor(data) {
+ this.patch(data);
+ }
+
+ /**
+ * Patch the data contained in this class with new partial data.
+ * @param {Object} data Data to patch this with
+ * @returns {void}
+ * @private
+ */
+ patch(data) {
+ for (const key of Object.keys(Constants.UserChannelOverrideMap)) {
+ const value = Constants.UserChannelOverrideMap[key];
+ if (!data.hasOwnProperty(key)) continue;
+ if (typeof value === 'function') {
+ this[value.name] = value(data[key]);
+ } else {
+ this[value] = data[key];
+ }
+ }
+ }
+}
+
+module.exports = ClientUserChannelOverride;
diff --git a/node_modules/discord.js/src/structures/ClientUserGuildSettings.js b/node_modules/discord.js/src/structures/ClientUserGuildSettings.js
new file mode 100644
index 0000000..5a28747
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUserGuildSettings.js
@@ -0,0 +1,60 @@
+const Constants = require('../util/Constants');
+const Collection = require('../util/Collection');
+const ClientUserChannelOverride = require('./ClientUserChannelOverride');
+
+/**
+ * A wrapper around the ClientUser's guild settings.
+ */
+class ClientUserGuildSettings {
+ constructor(data, client) {
+ /**
+ * The client that created the instance of the ClientUserGuildSettings
+ * @name ClientUserGuildSettings#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ /**
+ * The ID of the guild this settings are for
+ * @type {Snowflake}
+ */
+ this.guildID = data.guild_id;
+ this.channelOverrides = new Collection();
+ this.patch(data);
+ }
+
+ /**
+ * Patch the data contained in this class with new partial data.
+ * @param {Object} data Data to patch this with
+ * @returns {void}
+ * @private
+ */
+ patch(data) {
+ for (const key of Object.keys(Constants.UserGuildSettingsMap)) {
+ const value = Constants.UserGuildSettingsMap[key];
+ if (!data.hasOwnProperty(key)) continue;
+ if (key === 'channel_overrides') {
+ for (const channel of data[key]) {
+ this.channelOverrides.set(channel.channel_id,
+ new ClientUserChannelOverride(channel));
+ }
+ } else if (typeof value === 'function') {
+ this[value.name] = value(data[key]);
+ } else {
+ this[value] = data[key];
+ }
+ }
+ }
+
+ /**
+ * Update a specific property of the guild settings.
+ * @param {string} name Name of property
+ * @param {value} value Value to patch
+ * @returns {Promise<Object>}
+ */
+ update(name, value) {
+ return this.client.rest.methods.patchClientUserGuildSettings(this.guildID, { [name]: value });
+ }
+}
+
+module.exports = ClientUserGuildSettings;
diff --git a/node_modules/discord.js/src/structures/ClientUserSettings.js b/node_modules/discord.js/src/structures/ClientUserSettings.js
new file mode 100644
index 0000000..6b18c03
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUserSettings.js
@@ -0,0 +1,80 @@
+const Constants = require('../util/Constants');
+const Util = require('../util/Util');
+
+/**
+ * A wrapper around the ClientUser's settings.
+ */
+class ClientUserSettings {
+ constructor(user, data) {
+ this.user = user;
+ this.patch(data);
+ }
+
+ /**
+ * Patch the data contained in this class with new partial data.
+ * @param {Object} data Data to patch this with
+ * @returns {void}
+ * @private
+ */
+ patch(data) {
+ for (const key of Object.keys(Constants.UserSettingsMap)) {
+ const value = Constants.UserSettingsMap[key];
+ if (!data.hasOwnProperty(key)) continue;
+ if (typeof value === 'function') {
+ this[value.name] = value(data[key]);
+ } else {
+ this[value] = data[key];
+ }
+ }
+ }
+
+ /**
+ * Update a specific property of of user settings.
+ * @param {string} name Name of property
+ * @param {*} value Value to patch
+ * @returns {Promise<Object>}
+ */
+ update(name, value) {
+ return this.user.client.rest.methods.patchUserSettings({ [name]: value });
+ }
+
+ /**
+ * Sets the position at which this guild will appear in the Discord client.
+ * @param {Guild} guild The guild to move
+ * @param {number} position Absolute or relative position
+ * @param {boolean} [relative=false] Whether to position relatively or absolutely
+ * @returns {Promise<Guild>}
+ */
+ setGuildPosition(guild, position, relative) {
+ const temp = Object.assign([], this.guildPositions);
+ Util.moveElementInArray(temp, guild.id, position, relative);
+ return this.update('guild_positions', temp).then(() => guild);
+ }
+
+ /**
+ * Add a guild to the list of restricted guilds.
+ * @param {Guild} guild The guild to add
+ * @returns {Promise<Guild>}
+ */
+ addRestrictedGuild(guild) {
+ const temp = Object.assign([], this.restrictedGuilds);
+ if (temp.includes(guild.id)) return Promise.reject(new Error('Guild is already restricted'));
+ temp.push(guild.id);
+ return this.update('restricted_guilds', temp).then(() => guild);
+ }
+
+ /**
+ * Remove a guild from the list of restricted guilds.
+ * @param {Guild} guild The guild to remove
+ * @returns {Promise<Guild>}
+ */
+ removeRestrictedGuild(guild) {
+ const temp = Object.assign([], this.restrictedGuilds);
+ const index = temp.indexOf(guild.id);
+ if (index < 0) return Promise.reject(new Error('Guild is not restricted'));
+ temp.splice(index, 1);
+ return this.update('restricted_guilds', temp).then(() => guild);
+ }
+}
+
+module.exports = ClientUserSettings;
diff --git a/node_modules/discord.js/src/structures/DMChannel.js b/node_modules/discord.js/src/structures/DMChannel.js
new file mode 100644
index 0000000..0a6a3d9
--- /dev/null
+++ b/node_modules/discord.js/src/structures/DMChannel.js
@@ -0,0 +1,76 @@
+const Channel = require('./Channel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Collection = require('../util/Collection');
+
+/**
+ * Represents a direct message channel between two users.
+ * @extends {Channel}
+ * @implements {TextBasedChannel}
+ */
+class DMChannel extends Channel {
+ constructor(client, data) {
+ super(client, data);
+ this.type = 'dm';
+ this.messages = new Collection();
+ this._typing = new Map();
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The recipient on the other end of the DM
+ * @type {User}
+ */
+ this.recipient = this.client.dataManager.newUser(data.recipients[0]);
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = data.last_message_id;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the recipient's mention instead of the
+ * DM channel object.
+ * @returns {string}
+ */
+ toString() {
+ return this.recipient.toString();
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastPinAt() {}
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendFiles() {}
+ sendCode() {}
+ fetchMessage() {}
+ fetchMessages() {}
+ fetchPinnedMessages() {}
+ search() {}
+ startTyping() {}
+ stopTyping() {}
+ get typing() {}
+ get typingCount() {}
+ createCollector() {}
+ createMessageCollector() {}
+ awaitMessages() {}
+ // Doesn't work on DM channels; bulkDelete() {}
+ acknowledge() {}
+ _cacheMessage() {}
+}
+
+TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']);
+
+module.exports = DMChannel;
diff --git a/node_modules/discord.js/src/structures/Emoji.js b/node_modules/discord.js/src/structures/Emoji.js
new file mode 100644
index 0000000..f514b81
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Emoji.js
@@ -0,0 +1,273 @@
+const Constants = require('../util/Constants');
+const Collection = require('../util/Collection');
+const Permissions = require('../util/Permissions');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a custom emoji.
+ */
+class Emoji {
+ constructor(guild, data) {
+ /**
+ * The client that instantiated this object
+ * @name Emoji#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: guild.client });
+
+ /**
+ * The guild this emoji is part of
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * Whether this emoji has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the emoji
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the emoji
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * Whether or not this emoji requires colons surrounding it
+ * @type {boolean}
+ */
+ this.requiresColons = data.require_colons;
+
+ /**
+ * Whether this emoji is managed by an external service
+ * @type {boolean}
+ */
+ this.managed = data.managed;
+
+ /**
+ * Whether this emoji is animated
+ * @type {boolean}
+ */
+ this.animated = data.animated;
+
+ /**
+ * Whether this emoji is available
+ * @type {boolean}
+ * @name Emoji#available
+ */
+ if (typeof data.available !== 'undefined') this.available = data.available;
+
+ this._roles = data.roles;
+ }
+
+ /**
+ * The timestamp the emoji was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the emoji was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Whether the emoji is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return !this.managed && this.guild.me.hasPermission(Permissions.FLAGS.MANAGE_EMOJIS);
+ }
+
+ /**
+ * A collection of roles this emoji is active for (empty if all), mapped by role ID
+ * @type {Collection<Snowflake, Role>}
+ * @readonly
+ */
+ get roles() {
+ const roles = new Collection();
+ for (const role of this._roles) {
+ if (this.guild.roles.has(role)) roles.set(role, this.guild.roles.get(role));
+ }
+ return roles;
+ }
+
+ /**
+ * The URL to the emoji file
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).Emoji(this.id, this.animated ? 'gif' : 'png');
+ }
+
+ /**
+ * The identifier of this emoji, used for message reactions
+ * @type {string}
+ * @readonly
+ */
+ get identifier() {
+ if (this.id) return `${this.name}:${this.id}`;
+ return encodeURIComponent(this.name);
+ }
+
+ /**
+ * Data for editing an emoji.
+ * @typedef {Object} EmojiEditData
+ * @property {string} [name] The name of the emoji
+ * @property {Collection<Snowflake, Role>|Array<Snowflake|Role>} [roles] Roles to restrict emoji to
+ */
+
+ /**
+ * Edits the emoji.
+ * @param {EmojiEditData} data The new data for the emoji
+ * @param {string} [reason] Reason for editing this emoji
+ * @returns {Promise<Emoji>}
+ * @example
+ * // Edit an emoji
+ * emoji.edit({name: 'newemoji'})
+ * .then(e => console.log(`Edited emoji ${e}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateEmoji(this, data, reason);
+ }
+
+ /**
+ * Set the name of the emoji.
+ * @param {string} name The new name for the emoji
+ * @param {string} [reason] The reason for changing the emoji's name
+ * @returns {Promise<Emoji>}
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Fetches the author for this emoji
+ * @returns {Promise<User>}
+ */
+ fetchAuthor() {
+ if (this.managed) return Promise.reject(new Error('Emoji is managed and has no Author.'));
+ if (!this.guild.me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS)) {
+ return Promise.reject(
+ new Error(`Client must have Manage Emoji permission in guild ${this.guild} to see emoji authors.`)
+ );
+ }
+ return this.client.rest.makeRequest('get', Constants.Endpoints.Guild(this.guild).Emoji(this.id), true)
+ .then(emoji => this.client.dataManager.newUser(emoji.user));
+ }
+
+ /**
+ * Add a role to the list of roles that can use this emoji.
+ * @param {Role} role The role to add
+ * @returns {Promise<Emoji>}
+ */
+ addRestrictedRole(role) {
+ return this.addRestrictedRoles([role]);
+ }
+
+ /**
+ * Add multiple roles to the list of roles that can use this emoji.
+ * @param {Role[]} roles Roles to add
+ * @returns {Promise<Emoji>}
+ */
+ addRestrictedRoles(roles) {
+ const newRoles = new Collection(this.roles);
+ for (const role of roles) {
+ if (this.guild.roles.has(role.id)) newRoles.set(role.id, role);
+ }
+ return this.edit({ roles: newRoles });
+ }
+
+ /**
+ * Remove a role from the list of roles that can use this emoji.
+ * @param {Role} role The role to remove
+ * @returns {Promise<Emoji>}
+ */
+ removeRestrictedRole(role) {
+ return this.removeRestrictedRoles([role]);
+ }
+
+ /**
+ * Remove multiple roles from the list of roles that can use this emoji.
+ * @param {Role[]} roles Roles to remove
+ * @returns {Promise<Emoji>}
+ */
+ removeRestrictedRoles(roles) {
+ const newRoles = new Collection(this.roles);
+ for (const role of roles) {
+ if (newRoles.has(role.id)) newRoles.delete(role.id);
+ }
+ return this.edit({ roles: newRoles });
+ }
+
+
+ /**
+ * Deletes the emoji.
+ * @param {string} [reason] Reason for deleting the emoji
+ * @returns {Promise<Emoji>}
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteEmoji(this, reason);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the emoji mention rather than the object.
+ * @returns {string}
+ * @example
+ * // Send an emoji:
+ * const emoji = guild.emojis.first();
+ * msg.reply(`Hello! ${emoji}`);
+ */
+ toString() {
+ if (!this.id || !this.requiresColons) {
+ return this.name;
+ }
+
+ return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;
+ }
+
+ /**
+ * Whether this emoji is the same as another one.
+ * @param {Emoji|Object} other The emoji to compare it to
+ * @returns {boolean} Whether the emoji is equal to the given emoji or not
+ */
+ equals(other) {
+ if (other instanceof Emoji) {
+ return (
+ other.id === this.id &&
+ other.name === this.name &&
+ other.managed === this.managed &&
+ other.requiresColons === this.requiresColons
+ );
+ } else {
+ return (
+ other.id === this.id &&
+ other.name === this.name
+ );
+ }
+ }
+}
+
+module.exports = Emoji;
diff --git a/node_modules/discord.js/src/structures/GroupDMChannel.js b/node_modules/discord.js/src/structures/GroupDMChannel.js
new file mode 100644
index 0000000..0febf6c
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GroupDMChannel.js
@@ -0,0 +1,246 @@
+const Channel = require('./Channel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Collection = require('../util/Collection');
+const Constants = require('../util/Constants');
+
+/*
+{ type: 3,
+ recipients:
+ [ { username: 'Charlie',
+ id: '123',
+ discriminator: '6631',
+ avatar: '123' },
+ { username: 'Ben',
+ id: '123',
+ discriminator: '2055',
+ avatar: '123' },
+ { username: 'Adam',
+ id: '123',
+ discriminator: '2406',
+ avatar: '123' } ],
+ owner_id: '123',
+ name: null,
+ last_message_id: '123',
+ id: '123',
+ icon: null }
+*/
+
+/**
+ * Represents a Group DM on Discord.
+ * @extends {Channel}
+ * @implements {TextBasedChannel}
+ */
+class GroupDMChannel extends Channel {
+ constructor(client, data) {
+ super(client, data);
+ this.type = 'group';
+ this.messages = new Collection();
+ this._typing = new Map();
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The name of this Group DM, can be null if one isn't set
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * A hash of this Group DM icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The user ID of this Group DM's owner
+ * @type {string}
+ */
+ this.ownerID = data.owner_id;
+
+ /**
+ * If the DM is managed by an application
+ * @type {boolean}
+ */
+ this.managed = data.managed;
+
+ /**
+ * Application ID of the application that made this Group DM, if applicable
+ * @type {?string}
+ */
+ this.applicationID = data.application_id;
+
+ if (data.nicks) {
+ /**
+ * Nicknames for group members
+ * @type {?Collection<Snowflake, string>}
+ */
+ this.nicks = new Collection(data.nicks.map(n => [n.id, n.nick]));
+ }
+
+ if (!this.recipients) {
+ /**
+ * A collection of the recipients of this DM, mapped by their ID
+ * @type {Collection<Snowflake, User>}
+ */
+ this.recipients = new Collection();
+ }
+
+ if (data.recipients) {
+ for (const recipient of data.recipients) {
+ const user = this.client.dataManager.newUser(recipient);
+ this.recipients.set(user.id, user);
+ }
+ }
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = data.last_message_id;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+ }
+
+ /**
+ * The owner of this Group DM
+ * @type {User}
+ * @readonly
+ */
+ get owner() {
+ return this.client.users.get(this.ownerID);
+ }
+
+ /**
+ * The URL to this guild's icon
+ * @type {?string}
+ * @readonly
+ */
+ get iconURL() {
+ if (!this.icon) return null;
+ return Constants.Endpoints.Channel(this).Icon(this.client.options.http.cdn, this.icon);
+ }
+
+ edit(data) {
+ const _data = {};
+ if (data.name) _data.name = data.name;
+ if (typeof data.icon !== 'undefined') _data.icon = data.icon;
+ return this.client.rest.methods.updateGroupDMChannel(this, _data);
+ }
+
+ /**
+ * Whether this channel equals another channel. It compares all properties, so for most operations
+ * it is advisable to just compare `channel.id === channel2.id` as it is much faster and is often
+ * what most users need.
+ * @param {GroupDMChannel} channel Channel to compare with
+ * @returns {boolean}
+ */
+ equals(channel) {
+ const equal = channel &&
+ this.id === channel.id &&
+ this.name === channel.name &&
+ this.icon === channel.icon &&
+ this.ownerID === channel.ownerID;
+
+ if (equal) {
+ return this.recipients.equals(channel.recipients);
+ }
+
+ return equal;
+ }
+
+ /**
+ * Add a user to the DM
+ * @param {UserResolvable|string} accessTokenOrID Access token or user resolvable
+ * @param {string} [nick] Permanent nickname to give the user (only available if a bot is creating the DM)
+ * @returns {Promise<GroupDMChannel>}
+ */
+
+ addUser(accessTokenOrID, nick) {
+ return this.client.rest.methods.addUserToGroupDM(this, {
+ nick,
+ id: this.client.resolver.resolveUserID(accessTokenOrID),
+ accessToken: accessTokenOrID,
+ });
+ }
+
+ /**
+ * Set a new GroupDMChannel icon.
+ * @param {Base64Resolvable|BufferResolvable} icon The new icon of the group dm
+ * @returns {Promise<GroupDMChannel>}
+ * @example
+ * // Edit the group dm icon
+ * channel.setIcon('./icon.png')
+ * .then(updated => console.log('Updated the channel icon'))
+ * .catch(console.error);
+ */
+ setIcon(icon) {
+ return this.client.resolver.resolveImage(icon).then(data => this.edit({ icon: data }));
+ }
+
+ /**
+ * Sets a new name for this Group DM.
+ * @param {string} name New name for this Group DM
+ * @returns {Promise<GroupDMChannel>}
+ */
+ setName(name) {
+ return this.edit({ name });
+ }
+
+ /**
+ * Removes a user from this Group DM.
+ * @param {UserResolvable} user User to remove
+ * @returns {Promise<GroupDMChannel>}
+ */
+ removeUser(user) {
+ const id = this.client.resolver.resolveUserID(user);
+ return this.client.rest.methods.removeUserFromGroupDM(this, id);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the channel's name instead of the Channel object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from My Group DM!
+ * console.log(`Hello from ${channel}!`);
+ * @example
+ * // Logs: Hello from My Group DM!
+ * console.log(`Hello from ' + channel + '!');
+ */
+ toString() {
+ return this.name;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastPinAt() {}
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendFiles() {}
+ sendCode() {}
+ fetchMessage() {}
+ fetchMessages() {}
+ fetchPinnedMessages() {}
+ search() {}
+ startTyping() {}
+ stopTyping() {}
+ get typing() {}
+ get typingCount() {}
+ createCollector() {}
+ createMessageCollector() {}
+ awaitMessages() {}
+ // Doesn't work on Group DMs; bulkDelete() {}
+ acknowledge() {}
+ _cacheMessage() {}
+}
+
+TextBasedChannel.applyToClass(GroupDMChannel, true, ['bulkDelete']);
+
+module.exports = GroupDMChannel;
diff --git a/node_modules/discord.js/src/structures/Guild.js b/node_modules/discord.js/src/structures/Guild.js
new file mode 100644
index 0000000..69acfe1
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Guild.js
@@ -0,0 +1,1711 @@
+const util = require('util');
+const Long = require('long');
+const User = require('./User');
+const Role = require('./Role');
+const Emoji = require('./Emoji');
+const Presence = require('./Presence').Presence;
+const GuildMember = require('./GuildMember');
+const Integration = require('./Integration');
+const Constants = require('../util/Constants');
+const Collection = require('../util/Collection');
+const Util = require('../util/Util');
+const Snowflake = require('../util/Snowflake');
+const SystemChannelFlags = require('../util/SystemChannelFlags');
+
+/**
+ * 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>
+ */
+class Guild {
+ constructor(client, data) {
+ /**
+ * The client that created the instance of the guild
+ * @name Guild#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * A collection of members that are in this guild. The key is the member's ID, the value is the member
+ * @type {Collection<Snowflake, GuildMember>}
+ */
+ this.members = new Collection();
+
+ /**
+ * A collection of channels that are in this guild. The key is the channel's ID, the value is the channel
+ * @type {Collection<Snowflake, GuildChannel>}
+ */
+ this.channels = new Collection();
+
+ /**
+ * A collection of roles that are in this guild. The key is the role's ID, the value is the role
+ * @type {Collection<Snowflake, Role>}
+ */
+ this.roles = new Collection();
+
+ /**
+ * A collection of presences in this guild
+ * @type {Collection<Snowflake, Presence>}
+ */
+ this.presences = new Collection();
+
+ /**
+ * Whether the bot has been removed from the guild
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (!data) return;
+ if (data.unavailable) {
+ /**
+ * Whether the guild is available to access. If it is not available, it indicates a server outage
+ * @type {boolean}
+ */
+ this.available = false;
+
+ /**
+ * The Unique ID of the guild, useful for comparisons
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+ } else {
+ this.setup(data);
+ if (!data.channels) this.available = false;
+ }
+ }
+
+ /* eslint-disable complexity */
+ /**
+ * Sets up the guild.
+ * @param {*} data The raw data of the guild
+ * @private
+ */
+ setup(data) {
+ /**
+ * The name of the guild
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The hash of the guild icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The hash of the guild splash image (VIP only)
+ * @type {?string}
+ */
+ this.splash = data.splash;
+
+ /**
+ * The region the guild is located in
+ * @type {string}
+ */
+ this.region = data.region;
+
+ /**
+ * The full amount of members in this guild
+ * @type {number}
+ */
+ this.memberCount = data.member_count || this.memberCount;
+
+ /**
+ * Whether the guild is "large" (has more than 250 members)
+ * @type {boolean}
+ */
+ this.large = Boolean('large' in data ? data.large : this.large);
+
+ /**
+ * An array of guild features
+ * @type {Object[]}
+ */
+ this.features = data.features;
+
+ /**
+ * The ID of the application that created this guild (if applicable)
+ * @type {?Snowflake}
+ */
+ this.applicationID = data.application_id;
+
+ /**
+ * The time in seconds before a user is counted as "away from keyboard"
+ * @type {?number}
+ */
+ this.afkTimeout = data.afk_timeout;
+
+ /**
+ * The ID of the voice channel where AFK members are moved
+ * @type {?string}
+ */
+ this.afkChannelID = data.afk_channel_id;
+
+ /**
+ * The ID of the system channel
+ * @type {?Snowflake}
+ */
+ this.systemChannelID = data.system_channel_id;
+
+ /**
+ * Whether embedded images are enabled on this guild
+ * @type {boolean}
+ */
+ this.embedEnabled = data.embed_enabled;
+
+ /**
+ * The verification level of the guild
+ * @type {number}
+ */
+ this.verificationLevel = data.verification_level;
+
+ /**
+ * The explicit content filter level of the guild
+ * @type {number}
+ */
+ this.explicitContentFilter = data.explicit_content_filter;
+
+ /**
+ * The required MFA level for the guild
+ * @type {number}
+ */
+ this.mfaLevel = data.mfa_level;
+
+ /**
+ * The timestamp the client user joined the guild at
+ * @type {number}
+ */
+ this.joinedTimestamp = data.joined_at ? new Date(data.joined_at).getTime() : this.joinedTimestamp;
+
+ /**
+ * The value set for a guild's default message notifications
+ * @type {DefaultMessageNotifications|number}
+ */
+ this.defaultMessageNotifications = Constants.DefaultMessageNotifications[data.default_message_notifications] ||
+ data.default_message_notifications;
+
+ /**
+ * The value for the guild's system channel flags
+ * @type {Readonly<SystemChannelFlags>}
+ */
+ this.systemChannelFlags = new SystemChannelFlags(data.system_channel_flags).freeze();
+
+ /**
+ * The type of premium tier:
+ * * 0: NONE
+ * * 1: TIER_1
+ * * 2: TIER_2
+ * * 3: TIER_3
+ * @typedef {number} PremiumTier
+ */
+
+ /**
+ * The premium tier on this guild
+ * @type {PremiumTier}
+ */
+ this.premiumTier = data.premium_tier;
+
+ /**
+ * The total number of users currently boosting this server
+ * @type {?number}
+ * @name Guild#premiumSubscriptionCount
+ */
+ if (typeof data.premium_subscription_count !== 'undefined') {
+ this.premiumSubscriptionCount = data.premium_subscription_count;
+ }
+
+ /**
+ * 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 embed channel ID, if enabled
+ * @type {?string}
+ * @name Guild#embedChannelID
+ */
+ if (typeof data.embed_channel_id !== 'undefined') this.embedChannelID = data.embed_channel_id;
+
+ /**
+ * The maximum amount of members the guild can have
+ * <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
+ * @type {?number}
+ * @name Guild#maximumMembers
+ */
+ if (typeof data.max_members !== 'undefined') this.maximumMembers = data.max_members || 250000;
+
+ /**
+ * The maximum amount of presences the guild can have
+ * <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
+ * @type {?number}
+ * @name Guild#maximumPresences
+ */
+ if (typeof data.max_presences !== 'undefined') this.maximumPresences = data.max_presences || 5000;
+
+ /**
+ * Whether widget images are enabled on this guild
+ * @type {?boolean}
+ * @name Guild#widgetEnabled
+ */
+ if (typeof data.widget_enabled !== 'undefined') this.widgetEnabled = data.widget_enabled;
+
+ /**
+ * The widget channel ID, if enabled
+ * @type {?string}
+ * @name Guild#widgetChannelID
+ */
+ if (typeof data.widget_channel_id !== 'undefined') this.widgetChannelID = data.widget_channel_id;
+
+ /**
+ * The vanity URL code of the guild, if any
+ * @type {?string}
+ */
+ this.vanityURLCode = data.vanity_url_code;
+
+ this.id = data.id;
+ this.available = !data.unavailable;
+ this.features = data.features || this.features || [];
+
+ /**
+ * The ID of the rules channel for the guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?Snowflake}
+ */
+ this.rulesChannelID = data.rules_channel_id;
+
+ /**
+ * The ID of the public updates channel for the guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?Snowflake}
+ */
+ this.publicUpdatesChannelID = data.public_updates_channel_id;
+
+ if (data.members) {
+ this.members.clear();
+ for (const guildUser of data.members) this._addMember(guildUser, false);
+ }
+
+ if (data.owner_id) {
+ /**
+ * The user ID of this guild's owner
+ * @type {Snowflake}
+ */
+ this.ownerID = data.owner_id;
+ }
+
+ if (data.channels) {
+ this.channels.clear();
+ for (const channel of data.channels) this.client.dataManager.newChannel(channel, this);
+ }
+
+ if (data.roles) {
+ this.roles.clear();
+ for (const role of data.roles) {
+ const newRole = new Role(this, role);
+ this.roles.set(newRole.id, newRole);
+ }
+ }
+
+ if (data.presences) {
+ for (const presence of data.presences) {
+ this._setPresence(presence.user.id, presence);
+ }
+ }
+
+ this._rawVoiceStates = new Collection();
+ if (data.voice_states) {
+ for (const voiceState of data.voice_states) {
+ this._rawVoiceStates.set(voiceState.user_id, voiceState);
+ const member = this.members.get(voiceState.user_id);
+ const voiceChannel = this.channels.get(voiceState.channel_id);
+ if (member && voiceChannel) {
+ member.serverMute = voiceState.mute;
+ member.serverDeaf = voiceState.deaf;
+ member.selfMute = voiceState.self_mute;
+ member.selfDeaf = voiceState.self_deaf;
+ member.selfStream = voiceState.self_stream || false;
+ member.voiceSessionID = voiceState.session_id;
+ member.voiceChannelID = voiceState.channel_id;
+ voiceChannel.members.set(member.user.id, member);
+ }
+ }
+ }
+
+ if (!this.emojis) {
+ /**
+ * A collection of emojis that are in this guild
+ * The key is the emoji's ID, the value is the emoji
+ * @type {Collection<Snowflake, Emoji>}
+ */
+ this.emojis = new Collection();
+ for (const emoji of data.emojis) this.emojis.set(emoji.id, new Emoji(this, emoji));
+ } else {
+ this.client.actions.GuildEmojisUpdate.handle({
+ guild_id: this.id,
+ emojis: data.emojis,
+ });
+ }
+ }
+
+ /**
+ * The timestamp the guild was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the guild was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Embed channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get embedChannel() {
+ return this.channels.get(this.embedChannelID) || null;
+ }
+
+ /**
+ * Widget channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get widgetChannel() {
+ return this.channels.get(this.widgetChannelID) || null;
+ }
+
+ /**
+ * The time the client user joined the guild
+ * @type {Date}
+ * @readonly
+ */
+ get joinedAt() {
+ return new Date(this.joinedTimestamp);
+ }
+
+ /**
+ * If this guild is verified
+ * @type {boolean}
+ * @readonly
+ */
+ get verified() {
+ return this.features.includes('VERIFIED');
+ }
+
+ /**
+ * The URL to this guild's icon
+ * @type {?string}
+ * @readonly
+ */
+ get iconURL() {
+ if (!this.icon) return null;
+ return Constants.Endpoints.Guild(this).Icon(this.client.options.http.cdn, this.icon);
+ }
+
+ /**
+ * The URL to this guild's banner.
+ * @type {?string}
+ * @readonly
+ */
+ get bannerURL() {
+ if (!this.banner) return null;
+ return Constants.Endpoints.Guild(this).Banner(this.client.options.http.cdn, this.banner);
+ }
+
+ /**
+ * The acronym that shows up in place of a guild icon.
+ * @type {string}
+ * @readonly
+ */
+ get nameAcronym() {
+ return this.name.replace(/\w+/g, name => name[0]).replace(/\s/g, '');
+ }
+
+ /**
+ * The URL to this guild's splash
+ * @type {?string}
+ * @readonly
+ */
+ get splashURL() {
+ if (!this.splash) return null;
+ return Constants.Endpoints.Guild(this).Splash(this.client.options.http.cdn, this.splash);
+ }
+
+ /**
+ * The owner of the guild
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get owner() {
+ return this.members.get(this.ownerID);
+ }
+
+ /**
+ * AFK voice channel for this guild
+ * @type {?VoiceChannel}
+ * @readonly
+ */
+ get afkChannel() {
+ return this.client.channels.get(this.afkChannelID) || null;
+ }
+
+ /**
+ * System channel for this guild
+ * @type {?GuildChannel}
+ * @readonly
+ */
+ get systemChannel() {
+ return this.client.channels.get(this.systemChannelID) || null;
+ }
+
+ /**
+ * If the client is connected to any voice channel in this guild, this will be the relevant VoiceConnection
+ * @type {?VoiceConnection}
+ * @readonly
+ */
+ get voiceConnection() {
+ if (this.client.browser) return null;
+ return this.client.voice.connections.get(this.id) || null;
+ }
+
+ /**
+ * The position of this guild
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?number}
+ * @readonly
+ * @deprecated
+ */
+ get position() {
+ if (this.client.user.bot) return null;
+ if (!this.client.user.settings.guildPositions) return null;
+ return this.client.user.settings.guildPositions.indexOf(this.id);
+ }
+
+ /**
+ * Whether the guild is muted
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get muted() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).muted;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ /**
+ * The type of message that should notify you
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?MessageNotificationType}
+ * @readonly
+ * @deprecated
+ */
+ get messageNotifications() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).messageNotifications;
+ } catch (err) {
+ return null;
+ }
+ }
+
+ /**
+ * Whether to receive mobile push notifications
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get mobilePush() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).mobilePush;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ /**
+ * Whether to suppress everyone messages
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get suppressEveryone() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.id).suppressEveryone;
+ } catch (err) {
+ return null;
+ }
+ }
+
+ /**
+ * The `@everyone` role of the guild
+ * @type {Role}
+ * @readonly
+ */
+ get defaultRole() {
+ return this.roles.get(this.id);
+ }
+
+ /**
+ * Rules channel for this guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get rulesChannel() {
+ return this.client.channels.get(this.rulesChannelID) || null;
+ }
+
+ /**
+ * Public updates channel for this guild
+ * <info>This is only available on guilds with the `PUBLIC` feature</info>
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get publicUpdatesChannel() {
+ return this.client.channels.get(this.publicUpdatesChannelID) || null;
+ }
+
+ /**
+ * The client user as a GuildMember of this guild
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get me() {
+ return this.members.get(this.client.user.id);
+ }
+
+ /**
+ * Fetches a collection of roles in the current guild sorted by position
+ * @type {Collection<Snowflake, Role>}
+ * @readonly
+ * @private
+ */
+ get _sortedRoles() {
+ return this._sortPositionWithID(this.roles);
+ }
+
+ /**
+ * Returns the GuildMember form of a User object, if the user is present in the guild.
+ * @param {UserResolvable} user The user that you want to obtain the GuildMember of
+ * @returns {?GuildMember}
+ * @example
+ * // Get the guild member of a user
+ * const member = guild.member(message.author);
+ */
+ member(user) {
+ return this.client.resolver.resolveGuildMember(this, user);
+ }
+
+ /**
+ * Fetches this guild.
+ * @returns {Promise<Guild>}
+ */
+ fetch() {
+ return this.client.rest.methods.getGuild(this).then(data => {
+ this.setup(data);
+
+ return this;
+ });
+ }
+
+ /**
+ * An object containing information about a guild member's ban.
+ * @typedef {Object} BanInfo
+ * @property {User} user User that was banned
+ * @property {?string} reason Reason the user was banned
+ */
+
+ /**
+ * Fetch a ban for a user.
+ * @returns {Promise<BanInfo>}
+ * @param {UserResolvable} user The user to fetch the ban for
+ * @example
+ * // Get ban
+ * guild.fetchBan(message.author)
+ * .then(({ user, reason }) => console.log(`${user.tag} was banned for the reason: ${reason}.`))
+ * .catch(console.error);
+ */
+ fetchBan(user) {
+ return this.client.rest.methods.getGuildBan(this, user);
+ }
+
+ /**
+ * Fetch a collection of banned users in this guild.
+ * @returns {Promise<Collection<Snowflake, User|BanInfo>>}
+ * @param {boolean} [withReasons=false] Whether or not to include the ban reason(s)
+ * @example
+ * // Fetch bans in guild
+ * guild.fetchBans()
+ * .then(bans => console.log(`This guild has ${bans.size} bans`))
+ * .catch(console.error);
+ */
+ fetchBans(withReasons = false) {
+ if (withReasons) return this.client.rest.methods.getGuildBans(this);
+ return this.client.rest.methods.getGuildBans(this)
+ .then(bans => {
+ const users = new Collection();
+ for (const ban of bans.values()) users.set(ban.user.id, ban.user);
+ return users;
+ });
+ }
+
+ /**
+ * Fetches a collection of integrations to this guild.
+ * Resolves with a collection mapping integrations by their ids.
+ * @returns {Promise<Collection<string, Integration>>}
+ * @example
+ * // Fetch integrations
+ * guild.fetchIntegrations()
+ * .then(integrations => console.log(`Fetched ${integrations.size} integrations`))
+ * .catch(console.error);
+ */
+ fetchIntegrations() {
+ return this.client.rest.methods.getIntegrations(this).then(data =>
+ data.reduce((collection, integration) =>
+ collection.set(integration.id, new Integration(this.client, integration, this)),
+ new Collection())
+ );
+ }
+
+ /**
+ * The data for creating an integration.
+ * @typedef {Object} IntegrationData
+ * @property {string} id The integration id
+ * @property {string} type The integration type
+ */
+
+ /**
+ * Creates an integration by attaching an integration object
+ * @param {IntegrationData} data The data for thes integration
+ * @param {string} reason Reason for creating the integration
+ * @returns {Promise<Guild>}
+ */
+ createIntegration(data, reason) {
+ return this.client.rest.methods.createIntegration(this, data, reason)
+ .then(() => this);
+ }
+
+ /**
+ * Fetch a collection of invites to this guild.
+ * Resolves with a collection mapping invites by their codes.
+ * @returns {Promise<Collection<string, Invite>>}
+ * @example
+ * // Fetch invites
+ * guild.fetchInvites()
+ * .then(invites => console.log(`Fetched ${invites.size} invites`))
+ * .catch(console.error);
+ * @example
+ * // Fetch invite creator by their id
+ * guild.fetchInvites()
+ * .then(invites => console.log(invites.find(invite => invite.inviter.id === '84484653687267328')))
+ * .catch(console.error);
+ */
+ fetchInvites() {
+ return this.client.rest.methods.getGuildInvites(this);
+ }
+
+ /**
+ * Fetches the vanity url invite code to this guild.
+ * Resolves with a string matching the vanity url invite code, not the full url.
+ * @returns {Promise<string>}
+ * @example
+ * // Fetch invites
+ * guild.fetchVanityCode()
+ * .then(code => {
+ * console.log(`Vanity URL: https://discord.gg/${code}`);
+ * })
+ * .catch(console.error);
+ */
+ fetchVanityCode() {
+ if (!this.features.includes('VANITY_URL')) {
+ return Promise.reject(new Error('This guild does not have the VANITY_URL feature enabled.'));
+ }
+ return this.client.rest.methods.getGuildVanityCode(this);
+ }
+
+
+ /**
+ * Fetch all webhooks for the guild.
+ * @returns {Promise<Collection<Snowflake, Webhook>>}
+ * @example
+ * // Fetch webhooks
+ * guild.fetchWebhooks()
+ * .then(webhooks => console.log(`Fetched ${webhooks.size} webhooks`))
+ * .catch(console.error);
+ */
+ fetchWebhooks() {
+ return this.client.rest.methods.getGuildWebhooks(this);
+ }
+
+ /**
+ * Fetch available voice regions.
+ * @returns {Promise<Collection<string, VoiceRegion>>}
+ * @example
+ * // Fetch voice regions
+ * guild.fetchVoiceRegions()
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchVoiceRegions() {
+ return this.client.rest.methods.fetchVoiceRegions(this.id);
+ }
+
+ /**
+ * The Guild Embed object
+ * @typedef {Object} GuildEmbedData
+ * @property {boolean} enabled Whether the embed is enabled
+ * @property {?ChannelResolvable} channel The embed channel
+ */
+
+ /**
+ * Fetches the guild embed.
+ * @returns {Promise<GuildEmbedData>}
+ * @example
+ * // Fetches the guild embed
+ * guild.fetchEmbed()
+ * .then(embed => console.log(`The embed is ${embed.enabled ? 'enabled' : 'disabled'}`))
+ * .catch(console.error);
+ */
+ fetchEmbed() {
+ return this.client.rest.methods.fetchEmbed(this.id);
+ }
+
+ /**
+ * Fetch audit logs for this guild.
+ * @param {Object} [options={}] Options for fetching audit logs
+ * @param {Snowflake|GuildAuditLogsEntry} [options.before] Limit to entries from before specified entry
+ * @param {Snowflake|GuildAuditLogsEntry} [options.after] Limit to entries from after specified entry
+ * @param {number} [options.limit] Limit number of entries
+ * @param {UserResolvable} [options.user] Only show entries involving this user
+ * @param {AuditLogAction} [options.type] Only show entries involving this action type
+ * @returns {Promise<GuildAuditLogs>}
+ * @example
+ * // Output audit log entries
+ * guild.fetchAuditLogs()
+ * .then(audit => console.log(audit.entries.first()))
+ * .catch(console.error);
+ */
+ fetchAuditLogs(options) {
+ return this.client.rest.methods.getGuildAuditLogs(this, options);
+ }
+
+ /**
+ * Adds a user to the guild using OAuth2. Requires the `CREATE_INSTANT_INVITE` permission.
+ * @param {UserResolvable} user User to add to the guild
+ * @param {Object} options Options for the addition
+ * @param {string} options.accessToken An OAuth2 access token for the user with the `guilds.join` scope granted to the
+ * bot's application
+ * @param {string} [options.nick] Nickname to give the member (requires `MANAGE_NICKNAMES`)
+ * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} [options.roles] Roles to add to the member
+ * (requires `MANAGE_ROLES`)
+ * @param {boolean} [options.mute] Whether the member should be muted (requires `MUTE_MEMBERS`)
+ * @param {boolean} [options.deaf] Whether the member should be deafened (requires `DEAFEN_MEMBERS`)
+ * @returns {Promise<GuildMember>}
+ */
+ addMember(user, options) {
+ user = this.client.resolver.resolveUserID(user);
+ if (this.members.has(user)) return Promise.resolve(this.members.get(user));
+ return this.client.rest.methods.putGuildMember(this, user, options);
+ }
+
+ /**
+ * Fetch a single guild member from a user.
+ * @param {UserResolvable} user The user to fetch the member for
+ * @param {boolean} [cache=true] Insert the member into the members cache
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Fetch a guild member
+ * guild.fetchMember(message.author)
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchMember(user, cache = true) {
+ const userID = this.client.resolver.resolveUserID(user);
+ if (!userID) return Promise.reject(new Error('Invalid id provided.'));
+ const member = this.members.get(userID);
+ if (member && member.joinedTimestamp) return Promise.resolve(member);
+ return this.client.rest.methods.getGuildMember(this, userID, cache);
+ }
+
+ /**
+ * Fetches all the members in the guild, even if they are offline. If the guild has less than 250 members,
+ * this should not be necessary.
+ * @param {string} [query=''] Limit fetch to members with similar usernames
+ * @param {number} [limit=0] Maximum number of members to request
+ * @returns {Promise<Guild>}
+ * @example
+ * // Fetch guild members
+ * guild.fetchMembers()
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Fetches a maximum of 1 member with the given query
+ * guild.fetchMembers('hydrabolt', 1)
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ fetchMembers(query = '', limit = 0) {
+ return new Promise((resolve, reject) => {
+ if (this.memberCount === this.members.size) {
+ resolve(this);
+ return;
+ }
+ this.client.ws.send({
+ op: Constants.OPCodes.REQUEST_GUILD_MEMBERS,
+ d: {
+ guild_id: this.id,
+ query,
+ limit,
+ },
+ });
+ const handler = (members, guild) => {
+ if (guild.id !== this.id) return;
+ if (this.memberCount === this.members.size || members.length < 1000) {
+ this.client.removeListener(Constants.Events.GUILD_MEMBERS_CHUNK, handler);
+ resolve(this);
+ }
+ };
+ this.client.on(Constants.Events.GUILD_MEMBERS_CHUNK, handler);
+ this.client.setTimeout(() => reject(new Error('Members didn\'t arrive in time.')), 120 * 1000);
+ });
+ }
+
+ /**
+ * Performs a search within the entire guild.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {MessageSearchOptions} [options={}] Options to pass to the search
+ * @returns {Promise<MessageSearchResult>}
+ * @deprecated
+ * @example
+ * guild.search({
+ * content: 'discord.js',
+ * before: '2016-11-17'
+ * })
+ * .then(res => {
+ * const hit = res.messages[0].find(m => m.hit).content;
+ * console.log(`I found: **${hit}**, total results: ${res.totalResults}`);
+ * })
+ * .catch(console.error);
+ */
+ search(options = {}) {
+ return this.client.rest.methods.search(this, options);
+ }
+
+ /**
+ * The data for editing a guild.
+ * @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 {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
+ * @property {Base64Resolvable} [icon] The icon of the guild
+ * @property {Base64Resolvable} [banner] The banner of the guild
+ * @property {GuildMemberResolvable} [owner] The owner of the guild
+ * @property {Base64Resolvable} [splash] The splash screen of the guild
+ * @property {SystemChannelFlagsResolvable} [systemChannelFlags] The system channel flags of the guild
+ */
+
+ /**
+ * Updates the guild with new information - e.g. a new name.
+ * @param {GuildEditData} data The data to update the guild with
+ * @param {string} [reason] Reason for editing the guild
+ * @returns {Promise<Guild>}
+ * @example
+ * // Set the guild name and region
+ * guild.edit({
+ * name: 'Discord Guild',
+ * region: 'london',
+ * })
+ * .then(g => console.log(`Changed guild name to ${g} and region to ${g.region}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ 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.afkChannel !== 'undefined') {
+ _data.afk_channel_id = this.client.resolver.resolveChannelID(data.afkChannel);
+ }
+ if (typeof data.systemChannel !== 'undefined') {
+ _data.system_channel_id = this.client.resolver.resolveChannelID(data.systemChannel);
+ }
+ if (data.afkTimeout) _data.afk_timeout = Number(data.afkTimeout);
+ if (typeof data.icon !== 'undefined') _data.icon = data.icon;
+ if (data.owner) _data.owner_id = this.client.resolver.resolveUser(data.owner).id;
+ if (typeof data.splash !== 'undefined') _data.splash = data.splash;
+ if (typeof data.banner !== 'undefined') _data.banner = data.banner;
+ if (typeof data.explicitContentFilter !== 'undefined') {
+ _data.explicit_content_filter = Number(data.explicitContentFilter);
+ }
+ if (typeof data.defaultMessageNotifications !== 'undefined') {
+ _data.default_message_notifications = typeof data.defaultMessageNotifications === 'string' ?
+ Constants.DefaultMessageNotifications.indexOf(data.defaultMessageNotifications) :
+ Number(data.defaultMessageNotifications);
+ }
+ if (typeof data.systemChannelFlags !== 'undefined') {
+ _data.system_channel_flags = SystemChannelFlags.resolve(data.systemChannelFlags);
+ }
+ return this.client.rest.methods.updateGuild(this, _data, reason);
+ }
+
+ /**
+ * Sets a new guild banner.
+ * @param {BufferResolvable|Base64Resolvable} banner The new banner of the guild
+ * @param {string} [reason] Reason for changing the guild's banner
+ * @returns {Guild}
+ */
+ setBanner(banner, reason) {
+ return this.client.resolver.resolveImage(banner).then(data => this.edit({ banner: data }, reason));
+ }
+
+ /**
+ * Edit the level of the explicit content filter.
+ * @param {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<Guild>}
+ */
+ setExplicitContentFilter(explicitContentFilter, reason) {
+ return this.edit({ explicitContentFilter }, reason);
+ }
+
+ /**
+ * Edits the setting of the default message notifications of the guild.
+ * @param {DefaultMessageNotifications|number} defaultMessageNotifications
+ * The new setting for the default message notifications
+ * @param {string} [reason] Reason for changing the setting of the default message notifications
+ * @returns {Promise<Guild>}
+ */
+ setDefaultMessageNotifications(defaultMessageNotifications, reason) {
+ return this.edit({ defaultMessageNotifications }, reason);
+ }
+
+ /**
+ * Edits the flags of the default message notifications of the guild.
+ * @param {SystemChannelFlagsResolvable} systemChannelFlags The new flags for the default message notifications
+ * @param {string} [reason] Reason for changing the flags of the default message notifications
+ * @returns {Promise<Guild>}
+ */
+ setSystemChannelFlags(systemChannelFlags, reason) {
+ return this.edit({ systemChannelFlags }, reason);
+ }
+
+ /**
+ * Edit the name of the guild.
+ * @param {string} name The new name of the guild
+ * @param {string} [reason] Reason for changing the guild's name
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild name
+ * guild.setName('Discord Guild')
+ * .then(g => console.log(`Updated guild name to ${g}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Edit the region of the guild.
+ * @param {string} region The new region of the guild
+ * @param {string} [reason] Reason for changing the guild's region
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild region
+ * guild.setRegion('london')
+ * .then(g => console.log(`Updated guild region to ${g.region}`))
+ * .catch(console.error);
+ */
+ setRegion(region, reason) {
+ return this.edit({ region }, reason);
+ }
+
+ /**
+ * Edit the verification level of the guild.
+ * @param {number} verificationLevel The new verification level of the guild
+ * @param {string} [reason] Reason for changing the guild's verification level
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild verification level
+ * guild.setVerificationLevel(1)
+ * .then(g => console.log(`Updated guild verification level to ${g.verificationLevel}`))
+ * .catch(console.error);
+ */
+ setVerificationLevel(verificationLevel, reason) {
+ return this.edit({ verificationLevel }, reason);
+ }
+
+ /**
+ * Edit the AFK channel of the guild.
+ * @param {ChannelResolvable} afkChannel The new AFK channel
+ * @param {string} [reason] Reason for changing the guild's AFK channel
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild AFK channel
+ * guild.setAFKChannel(channel)
+ * .then(g => console.log(`Updated guild AFK channel to ${g.afkChannel.name}`))
+ * .catch(console.error);
+ */
+ setAFKChannel(afkChannel, reason) {
+ return this.edit({ afkChannel }, reason);
+ }
+
+ /**
+ * Edit the system channel of the guild.
+ * @param {ChannelResolvable} systemChannel The new system channel
+ * @param {string} [reason] Reason for changing the guild's system channel
+ * @returns {Promise<Guild>}
+ */
+ setSystemChannel(systemChannel, reason) {
+ return this.edit({ systemChannel }, reason);
+ }
+
+ /**
+ * Edit the AFK timeout of the guild.
+ * @param {number} afkTimeout The time in seconds that a user must be idle to be considered AFK
+ * @param {string} [reason] Reason for changing the guild's AFK timeout
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild AFK channel
+ * guild.setAFKTimeout(60)
+ * .then(g => console.log(`Updated guild AFK timeout to ${g.afkTimeout}`))
+ * .catch(console.error);
+ */
+ setAFKTimeout(afkTimeout, reason) {
+ return this.edit({ afkTimeout }, reason);
+ }
+
+ /**
+ * Set a new guild icon.
+ * @param {Base64Resolvable|BufferResolvable} icon The new icon of the guild
+ * @param {string} [reason] Reason for changing the guild's icon
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild icon
+ * guild.setIcon('./icon.png')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setIcon(icon, reason) {
+ return this.client.resolver.resolveImage(icon).then(data => this.edit({ icon: data, reason }));
+ }
+
+ /**
+ * Sets a new owner of the guild.
+ * @param {GuildMemberResolvable} owner The new owner of the guild
+ * @param {string} [reason] Reason for setting the new owner
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild owner
+ * guild.setOwner(guild.members.first())
+ * .then(g => console.log(`Updated the guild owner to ${g.owner.displayName}`))
+ * .catch(console.error);
+ */
+ setOwner(owner, reason) {
+ return this.edit({ owner }, reason);
+ }
+
+ /**
+ * Set a new guild splash screen.
+ * @param {BufferResolvable|Base64Resolvable} splash The new splash screen of the guild
+ * @param {string} [reason] Reason for changing the guild's splash screen
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild splash
+ * guild.setSplash('./splash.png')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setSplash(splash) {
+ return this.client.resolver.resolveImage(splash).then(data => this.edit({ splash: data }));
+ }
+
+ /**
+ * Sets the position of the guild in the guild listing.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {number} position Absolute or relative position
+ * @param {boolean} [relative=false] Whether to position relatively or absolutely
+ * @returns {Promise<Guild>}
+ * @deprecated
+ */
+ setPosition(position, relative) {
+ if (this.client.user.bot) {
+ return Promise.reject(new Error('Setting guild position is only available for user accounts'));
+ }
+ return this.client.user.settings.setGuildPosition(this, position, relative);
+ }
+
+ /**
+ * Marks all messages in this guild as read.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<Guild>}
+ * @deprecated
+ */
+ acknowledge() {
+ return this.client.rest.methods.ackGuild(this);
+ }
+
+ /**
+ * Allow direct messages from guild members.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {boolean} allow Whether to allow direct messages
+ * @returns {Promise<Guild>}
+ * @deprecated
+ */
+ allowDMs(allow) {
+ const settings = this.client.user.settings;
+ if (allow) return settings.removeRestrictedGuild(this);
+ else return settings.addRestrictedGuild(this);
+ }
+
+ /**
+ * Bans a user from the guild.
+ * @param {UserResolvable} user The user to ban
+ * @param {Object|number|string} [options] Ban options. If a number, the number of days to delete messages for, if a
+ * string, the ban reason. Supplying an object allows you to do both.
+ * @param {number} [options.days=0] Number of days of messages to delete
+ * @param {string} [options.reason] Reason for banning
+ * @returns {Promise<GuildMember|User|string>} Result object will be resolved as specifically as possible.
+ * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot
+ * be resolved, the user ID will be the result.
+ * @example
+ * // Ban a user by ID
+ * guild.ban('some user ID')
+ * .then(user => console.log(`Banned ${user.username || user.id || user} from ${guild}`))
+ * .catch(console.error);
+ * @example
+ * // Ban a user by object with reason and days
+ * guild.ban(user, { days: 7, reason: 'He needed to go' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ ban(user, options = {}) {
+ if (typeof options === 'number') {
+ options = { reason: null, 'delete-message-days': options };
+ } else if (typeof options === 'string') {
+ options = { reason: options, 'delete-message-days': 0 };
+ }
+ if (options.days) options['delete-message-days'] = options.days;
+ return this.client.rest.methods.banGuildMember(this, user, options);
+ }
+
+ /**
+ * Unbans a user from the guild.
+ * @param {UserResolvable} user The user to unban
+ * @param {string} [reason] Reason for unbanning the user
+ * @returns {Promise<User>}
+ * @example
+ * // Unban a user by ID (or with a user/guild member object)
+ * guild.unban('some user ID')
+ * .then(user => console.log(`Unbanned ${user.username} from ${guild}`))
+ * .catch(console.error);
+ */
+ unban(user, reason) {
+ return this.client.rest.methods.unbanGuildMember(this, user, reason);
+ }
+
+ /**
+ * Prunes members from the guild based on how long they have been inactive.
+ * @param {number} days Number of days of inactivity required to kick
+ * @param {boolean} [dry=false] If true, will return number of users that will be kicked, without actually doing it
+ * @param {string} [reason] Reason for this prune
+ * @returns {Promise<number>} The number of members that were/will be kicked
+ * @example
+ * // See how many members will be pruned
+ * guild.pruneMembers(12, true)
+ * .then(pruned => console.log(`This will prune ${pruned} people!`))
+ * .catch(console.error);
+ * @example
+ * // Actually prune the members
+ * guild.pruneMembers(12)
+ * .then(pruned => console.log(`I just pruned ${pruned} people!`))
+ * .catch(console.error);
+ */
+ pruneMembers(days, dry = false, reason) {
+ if (typeof days !== 'number') throw new TypeError('Days must be a number.');
+ return this.client.rest.methods.pruneGuildMembers(this, days, dry, reason);
+ }
+
+ /**
+ * Syncs this guild (already done automatically every 30 seconds).
+ * <warn>This is only available when using a user account.</warn>
+ * @deprecated
+ */
+ sync() {
+ if (!this.client.user.bot) this.client.syncGuilds([this]);
+ }
+
+ /**
+ * Overwrites to use when creating a channel or replacing overwrites
+ * @typedef {Object} ChannelCreationOverwrites
+ * @property {PermissionResolvable} [allow] The permissions to allow
+ * @property {PermissionResolvable} [allowed] The permissions to allow
+ * **(deprecated)**
+ * @property {PermissionResolvable} [deny] The permissions to deny
+ * @property {PermissionResolvable} [denied] The permissions to deny
+ * **(deprecated)**
+ * @property {GuildMemberResolvable|RoleResolvable} id Member or role this overwrite is for
+ */
+
+ /**
+ * Creates a new channel in the guild.
+ * @param {string} name The name of the new channel
+ * @param {string|ChannelData} [typeOrOptions='text']
+ * The type of the new channel, one of `text`, `voice`, `category`, `news`, or `store`. **(deprecated, use options)**
+ * Alternatively options for the new channel, overriding the following parameters.
+ * @param {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [permissionOverwrites]
+ * Permission overwrites **(deprecated, use options)**
+ * @param {string} [reason] Reason for creating this channel **(deprecated, use options)**
+ * @returns {Promise<CategoryChannel|TextChannel|VoiceChannel>}
+ * @example
+ * // Create a new text channel
+ * guild.createChannel('new-general', { type: 'text' })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Create a new category channel with permission overwrites
+ * guild.createChannel('new-category', {
+ * type: 'category',
+ * permissionOverwrites: [{
+ * id: guild.id,
+ * deny: ['MANAGE_MESSAGES'],
+ * allow: ['SEND_MESSAGES']
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ createChannel(name, typeOrOptions, permissionOverwrites, reason) {
+ if (!typeOrOptions || (typeof typeOrOptions === 'string')) {
+ if (typeOrOptions) {
+ process.emitWarning(
+ 'Guild#createChannel: Create channels with an options object instead of separate parameters',
+ 'DeprecationWarning'
+ );
+ }
+ typeOrOptions = {
+ type: typeOrOptions,
+ permissionOverwrites,
+ reason,
+ };
+ }
+ return this.client.rest.methods.createChannel(this, name, typeOrOptions);
+ }
+
+ /**
+ * The data needed for updating a channel's position.
+ * @typedef {Object} ChannelPosition
+ * @property {ChannelResolvable} channel Channel to update
+ * @property {number} position New position for the channel
+ */
+
+ /**
+ * Batch-updates the guild's channels' positions.
+ * @param {ChannelPosition[]} channelPositions Channel positions to update
+ * @returns {Promise<Guild>}
+ * @example
+ * guild.updateChannels([{ channel: channelID, position: newChannelIndex }])
+ * .then(g => console.log(`Updated channel positions for ${g}`))
+ * .catch(console.error);
+ */
+ setChannelPositions(channelPositions) {
+ channelPositions = channelPositions.map(({ channel, position }) => ({ id: channel.id || channel, position }));
+ return this.client.rest.methods.setChannelPositions(this.id, channelPositions);
+ }
+
+ /**
+ * The data needed for updating a role's position.
+ * @typedef {Object} RolePosition
+ * @property {RoleResolvable} role Role to update
+ * @property {number} position New position for the role
+ */
+
+ /**
+ * Batch-updates the guild's role's positions.
+ * @param {RolePosition[]} rolePositions Role positions to update
+ * @returns {Promise<Guild>}
+ */
+ setRolePositions(rolePositions) {
+ rolePositions = rolePositions.map(({ role, position }) => ({ id: role.id || role, position }));
+ return this.client.rest.methods.setRolePositions(this.id, rolePositions);
+ }
+
+ /**
+ * Edits the guild's embed.
+ * @param {GuildEmbedData} embed The embed for the guild
+ * @param {string} [reason] Reason for changing the guild's embed
+ * @returns {Promise<Guild>}
+ */
+ setEmbed(embed, reason) {
+ return this.client.rest.methods.updateEmbed(this.id, embed, reason)
+ .then(() => this);
+ }
+
+ /**
+ * Creates a new role in the guild with given information.
+ * @param {RoleData} [data] The data to update the role with
+ * @param {string} [reason] Reason for creating this role
+ * @returns {Promise<Role>}
+ * @example
+ * // Create a new role
+ * guild.createRole()
+ * .then(role => console.log(`Created new role with name ${role.name}`))
+ * .catch(console.error);
+ * @example
+ * // Create a new role with data
+ * guild.createRole({
+ * name: 'Super Cool People',
+ * color: 'BLUE',
+ * })
+ * .then(role => console.log(`Created new role with name ${role.name} and color ${role.color}`))
+ * .catch(console.error)
+ */
+ createRole(data = {}, reason) {
+ return this.client.rest.methods.createGuildRole(this, data, reason);
+ }
+
+ /**
+ * Creates a new custom emoji in the guild.
+ * @param {BufferResolvable|Base64Resolvable} attachment The image for the emoji
+ * @param {string} name The name for the emoji
+ * @param {Collection<Snowflake, Role>|Role[]} [roles] Roles to limit the emoji to
+ * @param {string} [reason] Reason for creating the emoji
+ * @returns {Promise<Emoji>} The created emoji
+ * @example
+ * // Create a new emoji from a url
+ * guild.createEmoji('https://i.imgur.com/w3duR07.png', 'rip')
+ * .then(emoji => console.log(`Created new emoji with name ${emoji.name}`))
+ * .catch(console.error);
+ * @example
+ * // Create a new emoji from a file on your computer
+ * guild.createEmoji('./memes/banana.png', 'banana')
+ * .then(emoji => console.log(`Created new emoji with name ${emoji.name}`))
+ * .catch(console.error);
+ */
+ createEmoji(attachment, name, roles, reason) {
+ if (typeof attachment === 'string' && attachment.startsWith('data:')) {
+ return this.client.rest.methods.createEmoji(this, attachment, name, roles, reason);
+ } else {
+ return this.client.resolver.resolveImage(attachment).then(data =>
+ this.client.rest.methods.createEmoji(this, data, name, roles, reason)
+ );
+ }
+ }
+
+ /**
+ * Delete an emoji.
+ * @param {Emoji|string} emoji The emoji to delete
+ * @param {string} [reason] Reason for deleting the emoji
+ * @returns {Promise}
+ * @deprecated
+ */
+ deleteEmoji(emoji, reason) {
+ if (typeof emoji === 'string') emoji = this.emojis.get(emoji);
+ if (!(emoji instanceof Emoji)) throw new TypeError('Emoji must be either an instance of Emoji or an ID');
+ return emoji.delete(reason);
+ }
+
+ /**
+ * Causes the client to leave the guild.
+ * @returns {Promise<Guild>}
+ * @example
+ * // Leave a guild
+ * guild.leave()
+ * .then(g => console.log(`Left the guild ${g}`))
+ * .catch(console.error);
+ */
+ leave() {
+ return this.client.rest.methods.leaveGuild(this);
+ }
+
+ /**
+ * Causes the client to delete the guild.
+ * @returns {Promise<Guild>}
+ * @example
+ * // Delete a guild
+ * guild.delete()
+ * .then(g => console.log(`Deleted the guild ${g}`))
+ * .catch(console.error);
+ */
+ delete() {
+ return this.client.rest.methods.deleteGuild(this);
+ }
+
+ /**
+ * Whether this guild equals another guild. It compares all properties, so for most operations
+ * it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often
+ * what most users need.
+ * @param {Guild} guild The guild to compare with
+ * @returns {boolean}
+ */
+ equals(guild) {
+ let equal =
+ guild &&
+ this.id === guild.id &&
+ this.available === !guild.unavailable &&
+ this.splash === guild.splash &&
+ this.region === guild.region &&
+ this.name === guild.name &&
+ this.memberCount === guild.member_count &&
+ this.large === guild.large &&
+ this.icon === guild.icon &&
+ Util.arraysEqual(this.features, guild.features) &&
+ this.ownerID === guild.owner_id &&
+ this.verificationLevel === guild.verification_level &&
+ this.embedEnabled === guild.embed_enabled;
+
+ if (equal) {
+ if (this.embedChannel) {
+ if (this.embedChannel.id !== guild.embed_channel_id) equal = false;
+ } else if (guild.embed_channel_id) {
+ equal = false;
+ }
+ }
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the guild's name instead of the guild object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from My Guild!
+ * console.log(`Hello from ${guild}!`);
+ * @example
+ * // Logs: Hello from My Guild!
+ * console.log('Hello from ' + guild + '!');
+ */
+ toString() {
+ return this.name;
+ }
+
+ _addMember(guildUser, emitEvent = true) {
+ const existing = this.members.has(guildUser.user.id);
+ if (!(guildUser.user instanceof User)) guildUser.user = this.client.dataManager.newUser(guildUser.user);
+
+ guildUser.joined_at = guildUser.joined_at || 0;
+ const member = new GuildMember(this, guildUser);
+ this.members.set(member.id, member);
+
+ if (this._rawVoiceStates && this._rawVoiceStates.has(member.user.id)) {
+ const voiceState = this._rawVoiceStates.get(member.user.id);
+ member.serverMute = voiceState.mute;
+ member.serverDeaf = voiceState.deaf;
+ member.selfMute = voiceState.self_mute;
+ member.selfDeaf = voiceState.self_deaf;
+ member.selfStream = voiceState.self_stream || false;
+ member.voiceSessionID = voiceState.session_id;
+ member.voiceChannelID = voiceState.channel_id;
+ if (this.client.channels.has(voiceState.channel_id)) {
+ this.client.channels.get(voiceState.channel_id).members.set(member.user.id, member);
+ } else {
+ this.client.emit('warn', `Member ${member.id} added in guild ${this.id} with an uncached voice channel`);
+ }
+ }
+
+ /**
+ * Emitted whenever a user joins a guild.
+ * @event Client#guildMemberAdd
+ * @param {GuildMember} member The member that has joined a guild
+ */
+ if (this.client.ws.connection.status === Constants.Status.READY && emitEvent && !existing) {
+ this.client.emit(Constants.Events.GUILD_MEMBER_ADD, member);
+ }
+
+ return member;
+ }
+
+ _updateMember(member, data) {
+ const oldMember = Util.cloneObject(member);
+
+ if (data.premium_since) member.premiumSinceTimestamp = new Date(data.premium_since).getTime();
+ if (data.roles) member._roles = data.roles;
+ if (typeof data.nick !== 'undefined') member.nickname = data.nick;
+
+ const notSame = member.nickname !== oldMember.nickname ||
+ member.premiumSinceTimestamp !== oldMember.premiumSinceTimestamp ||
+ !Util.arraysEqual(member._roles, oldMember._roles);
+
+ if (this.client.ws.connection.status === Constants.Status.READY && notSame) {
+ /**
+ * Emitted whenever a guild member changes - i.e. new role, removed role, nickname.
+ * @event Client#guildMemberUpdate
+ * @param {GuildMember} oldMember The member before the update
+ * @param {GuildMember} newMember The member after the update
+ */
+ this.client.emit(Constants.Events.GUILD_MEMBER_UPDATE, oldMember, member);
+ }
+
+ return {
+ old: oldMember,
+ mem: member,
+ };
+ }
+
+ _removeMember(guildMember) {
+ if (guildMember.voiceChannel) guildMember.voiceChannel.members.delete(guildMember.id);
+ this.members.delete(guildMember.id);
+ }
+
+ _memberSpeakUpdate(user, speaking) {
+ const member = this.members.get(user);
+ if (member && member.speaking !== speaking) {
+ member.speaking = speaking;
+ /**
+ * Emitted once a guild member starts/stops speaking.
+ * @event Client#guildMemberSpeaking
+ * @param {GuildMember} member The member that started/stopped speaking
+ * @param {boolean} speaking Whether or not the member is speaking
+ */
+ this.client.emit(Constants.Events.GUILD_MEMBER_SPEAKING, member, speaking);
+ }
+ }
+
+ _setPresence(id, presence) {
+ if (this.presences.get(id)) {
+ this.presences.get(id).update(presence);
+ return;
+ }
+ this.presences.set(id, new Presence(presence, this.client));
+ }
+
+ /**
+ * Set the position of a role in this guild.
+ * @param {string|Role} role The role to edit, can be a role object or a role ID
+ * @param {number} position The new position of the role
+ * @param {boolean} [relative=false] Position Moves the role relative to its current position
+ * @returns {Promise<Guild>}
+ */
+ setRolePosition(role, position, relative = false) {
+ if (typeof role === 'string') {
+ role = this.roles.get(role);
+ if (!role) return Promise.reject(new Error('Supplied role is not a role or snowflake.'));
+ }
+
+ position = Number(position);
+ if (isNaN(position)) return Promise.reject(new Error('Supplied position is not a number.'));
+
+ let updatedRoles = this._sortedRoles.array();
+
+ Util.moveElementInArray(updatedRoles, role, position, relative);
+
+ updatedRoles = updatedRoles.map((r, i) => ({ id: r.id, position: i }));
+ return this.client.rest.methods.setRolePositions(this.id, updatedRoles);
+ }
+
+ /**
+ * Set the position of a channel in this guild.
+ * @param {string|GuildChannel} channel The channel to edit, can be a channel object or a channel ID
+ * @param {number} position The new position of the channel
+ * @param {boolean} [relative=false] Position Moves the channel relative to its current position
+ * @returns {Promise<Guild>}
+ */
+ setChannelPosition(channel, position, relative = false) {
+ if (typeof channel === 'string') {
+ channel = this.channels.get(channel);
+ if (!channel) return Promise.reject(new Error('Supplied channel is not a channel or snowflake.'));
+ }
+
+ position = Number(position);
+ if (isNaN(position)) return Promise.reject(new Error('Supplied position is not a number.'));
+
+ let updatedChannels = this._sortedChannels(channel.type).array();
+
+ Util.moveElementInArray(updatedChannels, channel, position, relative);
+
+ updatedChannels = updatedChannels.map((c, i) => ({ id: c.id, position: i }));
+ return this.client.rest.methods.setChannelPositions(this.id, updatedChannels);
+ }
+
+ /**
+ * Fetches a collection of channels in the current guild sorted by position.
+ * @param {string} type The channel type
+ * @returns {Collection<Snowflake, GuildChannel>}
+ * @private
+ */
+ _sortedChannels(type) {
+ return this._sortPositionWithID(this.channels.filter(c => {
+ if (type === 'voice' && c.type === 'voice') return true;
+ else if (type !== 'voice' && c.type !== 'voice') return true;
+ else return type === c.type;
+ }));
+ }
+
+ /**
+ * Sorts a collection by object position or ID if the positions are equivalent.
+ * Intended to be identical to Discord's sorting method.
+ * @param {Collection} collection The collection to sort
+ * @returns {Collection}
+ * @private
+ */
+ _sortPositionWithID(collection) {
+ return collection.sort((a, b) =>
+ a.position !== b.position ?
+ a.position - b.position :
+ Long.fromString(b.id).sub(Long.fromString(a.id)).toNumber()
+ );
+ }
+}
+
+/**
+ * The `#general` TextChannel of the guild
+ * @name Guild#defaultChannel
+ * @type {TextChannel}
+ * @readonly
+ * @deprecated
+ */
+Object.defineProperty(Guild.prototype, 'defaultChannel', {
+ get: util.deprecate(function defaultChannel() {
+ return this.channels.get(this.id);
+ }, 'Guild#defaultChannel: This property is obsolete, will be removed in v12.0.0, and may not function as expected.'),
+});
+
+Guild.prototype.allowDMs =
+ util.deprecate(Guild.prototype.allowDMs, 'Guild#allowDMs: userbot methods will be removed');
+
+Guild.prototype.acknowledge =
+ util.deprecate(Guild.prototype.acknowledge, 'Guild#acknowledge: userbot methods will be removed');
+
+Guild.prototype.setPosition =
+ util.deprecate(Guild.prototype.setPosition, 'Guild#setPosition: userbot methods will be removed');
+
+Guild.prototype.search =
+ util.deprecate(Guild.prototype.search, 'Guild#search: userbot methods will be removed');
+
+Guild.prototype.sync =
+ util.deprecate(Guild.prototype.sync, 'Guild#sync:, userbot methods will be removed');
+
+Guild.prototype.deleteEmoji =
+ util.deprecate(Guild.prototype.deleteEmoji, 'Guild#deleteEmoji: use Emoji#delete instead');
+
+module.exports = Guild;
diff --git a/node_modules/discord.js/src/structures/GuildAuditLogs.js b/node_modules/discord.js/src/structures/GuildAuditLogs.js
new file mode 100644
index 0000000..5c98594
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildAuditLogs.js
@@ -0,0 +1,462 @@
+const Collection = require('../util/Collection');
+const Snowflake = require('../util/Snowflake');
+const Webhook = require('./Webhook');
+const Integration = require('./Integration');
+const Invite = require('./Invite');
+
+/**
+ * The target type of an entry, e.g. `GUILD`. Here are the available types:
+ * * GUILD
+ * * CHANNEL
+ * * USER
+ * * ROLE
+ * * INVITE
+ * * WEBHOOK
+ * * EMOJI
+ * * MESSAGE
+ * * INTEGRATION
+ * @typedef {string} AuditLogTargetType
+ */
+
+/**
+ * Key mirror of all available audit log targets.
+ * @name GuildAuditLogs.Targets
+ * @type {AuditLogTargetType}
+ */
+const Targets = {
+ ALL: 'ALL',
+ GUILD: 'GUILD',
+ CHANNEL: 'CHANNEL',
+ USER: 'USER',
+ ROLE: 'ROLE',
+ INVITE: 'INVITE',
+ WEBHOOK: 'WEBHOOK',
+ EMOJI: 'EMOJI',
+ MESSAGE: 'MESSAGE',
+ INTEGRATION: 'INTEGRATION',
+ UNKNOWN: 'UNKNOWN',
+};
+
+/**
+ * The action of an entry. Here are the available actions:
+ * * ALL: null
+ * * GUILD_UPDATE: 1
+ * * CHANNEL_CREATE: 10
+ * * CHANNEL_UPDATE: 11
+ * * CHANNEL_DELETE: 12
+ * * CHANNEL_OVERWRITE_CREATE: 13
+ * * CHANNEL_OVERWRITE_UPDATE: 14
+ * * CHANNEL_OVERWRITE_DELETE: 15
+ * * MEMBER_KICK: 20
+ * * MEMBER_PRUNE: 21
+ * * MEMBER_BAN_ADD: 22
+ * * MEMBER_BAN_REMOVE: 23
+ * * MEMBER_UPDATE: 24
+ * * MEMBER_ROLE_UPDATE: 25
+ * * MEMBER_MOVE: 26
+ * * MEMBER_DISCONNECT: 27
+ * * BOT_ADD: 28,
+ * * ROLE_CREATE: 30
+ * * ROLE_UPDATE: 31
+ * * ROLE_DELETE: 32
+ * * INVITE_CREATE: 40
+ * * INVITE_UPDATE: 41
+ * * INVITE_DELETE: 42
+ * * WEBHOOK_CREATE: 50
+ * * WEBHOOK_UPDATE: 51
+ * * WEBHOOK_DELETE: 52
+ * * EMOJI_CREATE: 60
+ * * EMOJI_UPDATE: 61
+ * * EMOJI_DELETE: 62
+ * * MESSAGE_DELETE: 72
+ * * MESSAGE_BULK_DELETE: 73
+ * * MESSAGE_PIN: 74
+ * * MESSAGE_UNPIN: 75
+ * * INTEGRATION_CREATE: 80
+ * * INTEGRATION_UPDATE: 81
+ * * INTEGRATION_DELETE: 82
+ * @typedef {?number|string} AuditLogAction
+ */
+
+/**
+ * All available actions keyed under their names to their numeric values.
+ * @name GuildAuditLogs.Actions
+ * @type {AuditLogAction}
+ */
+const Actions = {
+ ALL: null,
+ GUILD_UPDATE: 1,
+ CHANNEL_CREATE: 10,
+ CHANNEL_UPDATE: 11,
+ CHANNEL_DELETE: 12,
+ CHANNEL_OVERWRITE_CREATE: 13,
+ CHANNEL_OVERWRITE_UPDATE: 14,
+ CHANNEL_OVERWRITE_DELETE: 15,
+ MEMBER_KICK: 20,
+ MEMBER_PRUNE: 21,
+ MEMBER_BAN_ADD: 22,
+ MEMBER_BAN_REMOVE: 23,
+ MEMBER_UPDATE: 24,
+ MEMBER_ROLE_UPDATE: 25,
+ MEMBER_MOVE: 26,
+ MEMBER_DISCONNECT: 27,
+ BOT_ADD: 28,
+ ROLE_CREATE: 30,
+ ROLE_UPDATE: 31,
+ ROLE_DELETE: 32,
+ INVITE_CREATE: 40,
+ INVITE_UPDATE: 41,
+ INVITE_DELETE: 42,
+ WEBHOOK_CREATE: 50,
+ WEBHOOK_UPDATE: 51,
+ WEBHOOK_DELETE: 52,
+ EMOJI_CREATE: 60,
+ EMOJI_UPDATE: 61,
+ EMOJI_DELETE: 62,
+ MESSAGE_DELETE: 72,
+ MESSAGE_BULK_DELETE: 73,
+ MESSAGE_PIN: 74,
+ MESSAGE_UNPIN: 75,
+ INTEGRATION_CREATE: 80,
+ INTEGRATION_UPDATE: 81,
+ INTEGRATION_DELETE: 82,
+};
+
+
+/**
+ * Audit logs entries are held in this class.
+ */
+class GuildAuditLogs {
+ constructor(guild, data) {
+ if (data.users) for (const user of data.users) guild.client.dataManager.newUser(user);
+
+ /**
+ * Cached webhooks
+ * @type {Collection<Snowflake, Webhook>}
+ * @private
+ */
+ this.webhooks = new Collection();
+ if (data.webhooks) {
+ for (const hook of data.webhooks) {
+ this.webhooks.set(hook.id, new Webhook(guild.client, hook));
+ }
+ }
+
+ /**
+ * Cached integrations
+ * @type {Collection<Snowflake, Integration>}
+ * @private
+ */
+ this.integrations = new Collection();
+ if (data.integrations) {
+ for (const integration of data.integrations) {
+ this.integrations.set(integration.id, new Integration(guild.client, integration, guild));
+ }
+ }
+
+ /**
+ * The entries for this guild's audit logs
+ * @type {Collection<Snowflake, GuildAuditLogsEntry>}
+ */
+ this.entries = new Collection();
+ for (const item of data.audit_log_entries) {
+ const entry = new GuildAuditLogsEntry(this, guild, item);
+ this.entries.set(entry.id, entry);
+ }
+ }
+
+ /**
+ * Handles possible promises for entry targets.
+ * @returns {Promise<GuildAuditLogs>}
+ */
+ static build(...args) {
+ const logs = new GuildAuditLogs(...args);
+ return Promise.all(logs.entries.map(e => e.target)).then(() => logs);
+ }
+
+ /**
+ * The target of an entry. It can be one of:
+ * * A guild
+ * * A user
+ * * A role
+ * * An emoji
+ * * An invite
+ * * A webhook
+ * * An integration
+ * * An object with an id key if target was deleted
+ * * An object where the keys represent either the new value or the old value
+ * @typedef {?Object|Guild|User|Role|Emoji|Invite|Webhook|Integration} AuditLogEntryTarget
+ */
+
+ /**
+ * Find target type from entry action.
+ * @param {number} target The action target
+ * @returns {?string}
+ */
+ static targetType(target) {
+ if (target < 10) return Targets.GUILD;
+ if (target < 20) return Targets.CHANNEL;
+ if (target < 30) return Targets.USER;
+ if (target < 40) return Targets.ROLE;
+ if (target < 50) return Targets.INVITE;
+ if (target < 60) return Targets.WEBHOOK;
+ if (target < 70) return Targets.EMOJI;
+ if (target < 80) return Targets.MESSAGE;
+ if (target < 90) return Targets.INTEGRATION;
+ return null;
+ }
+
+ /**
+ * The action type of an entry, e.g. `CREATE`. Here are the available types:
+ * * CREATE
+ * * DELETE
+ * * UPDATE
+ * * ALL
+ * @typedef {string} AuditLogActionType
+ */
+
+
+ /**
+ * Finds the action type from the entry action.
+ * @param {AuditLogAction} action The action target
+ * @returns {AuditLogActionType}
+ */
+ static actionType(action) {
+ if ([
+ Actions.CHANNEL_CREATE,
+ Actions.CHANNEL_OVERWRITE_CREATE,
+ Actions.MEMBER_BAN_REMOVE,
+ Actions.BOT_ADD,
+ Actions.ROLE_CREATE,
+ Actions.INVITE_CREATE,
+ Actions.WEBHOOK_CREATE,
+ Actions.EMOJI_CREATE,
+ Actions.MESSAGE_PIN,
+ Actions.INTEGRATION_CREATE,
+ ].includes(action)) return 'CREATE';
+
+ if ([
+ Actions.CHANNEL_DELETE,
+ Actions.CHANNEL_OVERWRITE_DELETE,
+ Actions.MEMBER_KICK,
+ Actions.MEMBER_PRUNE,
+ Actions.MEMBER_BAN_ADD,
+ Actions.MEMBER_DISCONNECT,
+ Actions.ROLE_DELETE,
+ Actions.INVITE_DELETE,
+ Actions.WEBHOOK_DELETE,
+ Actions.EMOJI_DELETE,
+ Actions.MESSAGE_DELETE,
+ Actions.MESSAGE_BULK_DELETE,
+ Actions.MESSAGE_UNPIN,
+ Actions.INTEGRATION_DELETE,
+ ].includes(action)) return 'DELETE';
+
+ if ([
+ Actions.GUILD_UPDATE,
+ Actions.CHANNEL_UPDATE,
+ Actions.CHANNEL_OVERWRITE_UPDATE,
+ Actions.MEMBER_UPDATE,
+ Actions.MEMBER_ROLE_UPDATE,
+ Actions.MEMBER_MOVE,
+ Actions.ROLE_UPDATE,
+ Actions.INVITE_UPDATE,
+ Actions.WEBHOOK_UPDATE,
+ Actions.EMOJI_UPDATE,
+ Actions.INTEGRATION_UPDATE,
+ ].includes(action)) return 'UPDATE';
+
+ return 'ALL';
+ }
+}
+
+/**
+ * Audit logs entry.
+ */
+class GuildAuditLogsEntry {
+ // eslint-disable-next-line complexity
+ constructor(logs, guild, data) {
+ const targetType = GuildAuditLogs.targetType(data.action_type);
+ /**
+ * The target type of this entry
+ * @type {AuditLogTargetType}
+ */
+ this.targetType = targetType;
+
+ /**
+ * The action type of this entry
+ * @type {AuditLogActionType}
+ */
+ this.actionType = GuildAuditLogs.actionType(data.action_type);
+
+ /**
+ * Specific action type of this entry in its string representation
+ * @type {AuditLogAction}
+ */
+ this.action = Object.keys(Actions).find(k => Actions[k] === data.action_type);
+
+ /**
+ * The reason of this entry
+ * @type {?string}
+ */
+ this.reason = data.reason || null;
+
+ /**
+ * The user that executed this entry
+ * @type {User}
+ */
+ this.executor = guild.client.users.get(data.user_id);
+
+ /**
+ * An entry in the audit log representing a specific change.
+ * @typedef {object} AuditLogChange
+ * @property {string} key The property that was changed, e.g. `nick` for nickname changes
+ * @property {*} [old] The old value of the change, e.g. for nicknames, the old nickname
+ * @property {*} [new] The new value of the change, e.g. for nicknames, the new nickname
+ */
+
+ /**
+ * Specific property changes
+ * @type {AuditLogChange[]}
+ */
+ this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null;
+
+ /**
+ * The ID of this entry
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * Any extra data from the entry
+ * @type {?Object|Role|GuildMember}
+ */
+ this.extra = null;
+ switch (data.action_type) {
+ case Actions.MEMBER_PRUNE:
+ this.extra = {
+ removed: Number(data.options.members_removed),
+ days: Number(data.options.delete_member_days),
+ };
+ break;
+
+ case Actions.MEMBER_MOVE:
+ case Actions.MESSAGE_DELETE:
+ case Actions.MESSAGE_BULK_DELETE:
+ this.extra = {
+ channel: guild.channels.get(data.options.channel_id) || { id: data.options.channel_id },
+ count: Number(data.options.count),
+ };
+ break;
+
+ case Actions.MESSAGE_PIN:
+ case Actions.MESSAGE_UNPIN:
+ this.extra = {
+ channel: guild.client.channels.get(data.options.channel_id) || { id: data.options.channel_id },
+ messageID: data.options.message_id,
+ };
+ break;
+
+ case Actions.MEMBER_DISCONNECT:
+ this.extra = {
+ count: Number(data.options.count),
+ };
+ break;
+
+ case Actions.CHANNEL_OVERWRITE_CREATE:
+ case Actions.CHANNEL_OVERWRITE_UPDATE:
+ case Actions.CHANNEL_OVERWRITE_DELETE:
+ switch (data.options.type) {
+ case 'member':
+ this.extra = guild.members.get(data.options.id) ||
+ { id: data.options.id, type: 'member' };
+ break;
+ case 'role':
+ this.extra = guild.roles.get(data.options.id) ||
+ { id: data.options.id, name: data.options.role_name, type: 'role' };
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /**
+ * The target of this entry
+ * @type {AuditLogEntryTarget}
+ */
+ this.target = null;
+ if (targetType === Targets.UNKNOWN) {
+ this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {});
+ this.target.id = data.target_id;
+ // MEMBER_DISCONNECT and similar types do not provide a target_id.
+ } else if (targetType === Targets.USER && data.target_id) {
+ this.target = guild.client.users.get(data.target_id);
+ } else if (targetType === Targets.GUILD) {
+ this.target = guild.client.guilds.get(data.target_id);
+ } else if (targetType === Targets.WEBHOOK) {
+ this.target = logs.webhooks.get(data.target_id) ||
+ new Webhook(guild.client,
+ this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {
+ id: data.target_id,
+ guild_id: guild.id,
+ }));
+ } else if (targetType === Targets.INVITE) {
+ const changes = this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {
+ id: data.target_id,
+ guild,
+ });
+ changes.channel = { id: changes.channel_id };
+ this.target = new Invite(guild.client, changes);
+ } else if (targetType === Targets.MESSAGE) {
+ // Discord sends a channel id for the MESSAGE_BULK_DELETE action type.
+ this.target = data.action_type === Actions.MESSAGE_BULK_DELETE ?
+ guild.channels.get(data.target_id) || { id: data.target_id } :
+ guild.client.users.get(data.target_id);
+ } else if (targetType === Targets.INTEGRATION) {
+ this.target = logs.integrations.get(data.target_id) ||
+ new Integration(guild.client, this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, { id: data.target_id }), guild);
+ } else if (data.target_id) {
+ this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id) || { id: data.target_id };
+ }
+ }
+
+ /**
+ * The timestamp this entry was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time this entry was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+}
+
+GuildAuditLogs.Actions = Actions;
+GuildAuditLogs.Targets = Targets;
+GuildAuditLogs.Entry = GuildAuditLogsEntry;
+
+module.exports = GuildAuditLogs;
diff --git a/node_modules/discord.js/src/structures/GuildChannel.js b/node_modules/discord.js/src/structures/GuildChannel.js
new file mode 100644
index 0000000..0dbc0e3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildChannel.js
@@ -0,0 +1,601 @@
+const Channel = require('./Channel');
+const Role = require('./Role');
+const PermissionOverwrites = require('./PermissionOverwrites');
+const Permissions = require('../util/Permissions');
+const Collection = require('../util/Collection');
+const Constants = require('../util/Constants');
+const Invite = require('./Invite');
+const Util = require('../util/Util');
+
+/**
+ * Represents a guild channel (i.e. text channels and voice channels).
+ * @extends {Channel}
+ */
+class GuildChannel extends Channel {
+ constructor(guild, data) {
+ super(guild.client, data);
+
+ /**
+ * The guild the channel is in
+ * @type {Guild}
+ */
+ this.guild = guild;
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The name of the guild channel
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The position of the channel in the list
+ * @type {number}
+ */
+ this.position = data.position;
+
+ /**
+ * The ID of the category parent of this channel
+ * @type {?Snowflake}
+ */
+ this.parentID = data.parent_id;
+
+ /**
+ * A map of permission overwrites in this channel for roles and users
+ * @type {Collection<Snowflake, PermissionOverwrites>}
+ */
+ this.permissionOverwrites = new Collection();
+ if (data.permission_overwrites) {
+ for (const overwrite of data.permission_overwrites) {
+ this.permissionOverwrites.set(overwrite.id, new PermissionOverwrites(this, overwrite));
+ }
+ }
+ }
+
+ /**
+ * The position of the channel
+ * @type {number}
+ * @readonly
+ */
+ get calculatedPosition() {
+ const sorted = this.guild._sortedChannels(this.type);
+ return sorted.array().indexOf(sorted.get(this.id));
+ }
+
+ /**
+ * The category parent of this channel
+ * @type {?CategoryChannel}
+ * @readonly
+ */
+ get parent() {
+ return this.guild.channels.get(this.parentID) || null;
+ }
+
+ /**
+ * If the permissionOverwrites match the parent channel, null if no parent
+ * @type {?boolean}
+ * @readonly
+ */
+ get permissionsLocked() {
+ if (!this.parent) return null;
+ if (this.permissionOverwrites.size !== this.parent.permissionOverwrites.size) return false;
+ return this.permissionOverwrites.every((value, key) => {
+ const testVal = this.parent.permissionOverwrites.get(key);
+ return testVal !== undefined &&
+ testVal.deny === value.deny &&
+ testVal.allow === value.allow;
+ });
+ }
+
+ /**
+ * Gets the overall set of permissions for a user in this channel, taking into account channel overwrites.
+ * @param {GuildMemberResolvable} member The user that you want to obtain the overall permissions for
+ * @returns {?Permissions}
+ */
+ memberPermissions(member) {
+ member = this.client.resolver.resolveGuildMember(this.guild, member);
+ if (!member) return null;
+
+ if (member.id === this.guild.ownerID) return new Permissions(member, Permissions.ALL);
+
+ const roles = member.roles;
+ const permissions = new Permissions(roles.map(role => role.permissions));
+
+ if (permissions.has(Permissions.FLAGS.ADMINISTRATOR)) return new Permissions(Permissions.ALL).freeze();
+
+ const overwrites = this.overwritesFor(member, true, roles);
+
+ return permissions
+ .remove(overwrites.everyone ? overwrites.everyone.deny : 0)
+ .add(overwrites.everyone ? overwrites.everyone.allow : 0)
+ .remove(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.deny) : 0)
+ .add(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.allow) : 0)
+ .remove(overwrites.member ? overwrites.member.deny : 0)
+ .add(overwrites.member ? overwrites.member.allow : 0)
+ .freeze();
+ }
+
+ /**
+ * Gets the overall set of permissions for a role in this channel, taking into account channel overwrites.
+ * @param {RoleResolvable} role The role that you want to obtain the overall permissions for
+ * @returns {?Permissions}
+ */
+ rolePermissions(role) {
+ if (role.permissions & Permissions.FLAGS.ADMINISTRATOR) return new Permissions(Permissions.ALL).freeze();
+
+ const everyoneOverwrites = this.permissionOverwrites.get(this.guild.id);
+ const roleOverwrites = this.permissionOverwrites.get(role.id);
+
+ return new Permissions(role.permissions)
+ .remove(everyoneOverwrites ? everyoneOverwrites.deny : 0)
+ .add(everyoneOverwrites ? everyoneOverwrites.allow : 0)
+ .remove(roleOverwrites ? roleOverwrites.deny : 0)
+ .add(roleOverwrites ? roleOverwrites.allow : 0)
+ .freeze();
+ }
+
+ /**
+ * Get the overall set of permissions for a member or role in this channel, taking into account channel overwrites.
+ * @param {GuildMemberResolvable|RoleResolvable} memberOrRole The member or role to obtain the overall permissions for
+ * @returns {?Permissions}
+ */
+ permissionsFor(memberOrRole) {
+ const member = this.guild.member(memberOrRole);
+ if (member) return this.memberPermissions(member);
+ const role = this.client.resolver.resolveRole(this.guild, memberOrRole);
+ if (role) return this.rolePermissions(role);
+ return null;
+ }
+
+ overwritesFor(member, verified = false, roles = null) {
+ if (!verified) member = this.client.resolver.resolveGuildMember(this.guild, member);
+ if (!member) return [];
+
+ roles = roles || member.roles;
+ const roleOverwrites = [];
+ let memberOverwrites;
+ let everyoneOverwrites;
+
+ for (const overwrite of this.permissionOverwrites.values()) {
+ if (overwrite.id === this.guild.id) {
+ everyoneOverwrites = overwrite;
+ } else if (roles.has(overwrite.id)) {
+ roleOverwrites.push(overwrite);
+ } else if (overwrite.id === member.id) {
+ memberOverwrites = overwrite;
+ }
+ }
+
+ return {
+ everyone: everyoneOverwrites,
+ roles: roleOverwrites,
+ member: memberOverwrites,
+ };
+ }
+
+ /**
+ * Replaces the permission overwrites for a channel
+ * @param {Object} [options] Options
+ * @param {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [options.overwrites]
+ * Permission overwrites
+ * @param {string} [options.reason] Reason for updating the channel overwrites
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * channel.replacePermissionOverwrites({
+ * overwrites: [
+ * {
+ * id: message.author.id,
+ * denied: ['VIEW_CHANNEL'],
+ * },
+ * ],
+ * reason: 'Needed to change permissions'
+ * });
+ */
+ replacePermissionOverwrites({ overwrites, reason } = {}) {
+ return this.edit({ permissionOverwrites: overwrites, reason })
+ .then(() => this);
+ }
+
+ /**
+ * An object mapping permission flags to `true` (enabled), `null` (unset) or `false` (disabled).
+ * ```js
+ * {
+ * 'SEND_MESSAGES': true,
+ * 'EMBED_LINKS': null,
+ * 'ATTACH_FILES': false,
+ * }
+ * ```
+ * @typedef {Object} PermissionOverwriteOptions
+ */
+
+ /**
+ * Overwrites the permissions for a user or role in this channel.
+ * @param {Role|Snowflake|UserResolvable} userOrRole The user or role to update
+ * @param {PermissionOverwriteOptions} options The configuration for the update
+ * @param {string} [reason] Reason for creating/editing this overwrite
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Overwrite permissions for a message author
+ * message.channel.overwritePermissions(message.author, {
+ * SEND_MESSAGES: false
+ * })
+ * .then(updated => console.log(updated.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ * @example
+ * // Overwite permissions for a message author and reset some
+ * message.channel.overwritePermissions(message.author, {
+ * VIEW_CHANNEL: false,
+ * SEND_MESSAGES: null
+ * })
+ * .then(updated => console.log(updated.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ */
+ overwritePermissions(userOrRole, options, reason) {
+ const payload = {
+ allow: 0,
+ deny: 0,
+ };
+
+ if (userOrRole instanceof Role) {
+ payload.type = 'role';
+ } else if (this.guild.roles.has(userOrRole)) {
+ userOrRole = this.guild.roles.get(userOrRole);
+ payload.type = 'role';
+ } else {
+ userOrRole = this.client.resolver.resolveUser(userOrRole);
+ payload.type = 'member';
+ if (!userOrRole) return Promise.reject(new TypeError('Supplied parameter was neither a User nor a Role.'));
+ }
+
+ payload.id = userOrRole.id;
+
+ const prevOverwrite = this.permissionOverwrites.get(userOrRole.id);
+
+ if (prevOverwrite) {
+ payload.allow = prevOverwrite.allow;
+ payload.deny = prevOverwrite.deny;
+ }
+
+ for (const perm of Object.keys(options)) {
+ if (options[perm] === true) {
+ payload.allow |= Permissions.FLAGS[perm] || 0;
+ payload.deny &= ~(Permissions.FLAGS[perm] || 0);
+ } else if (options[perm] === false) {
+ payload.allow &= ~(Permissions.FLAGS[perm] || 0);
+ payload.deny |= Permissions.FLAGS[perm] || 0;
+ } else if (options[perm] === null) {
+ payload.allow &= ~(Permissions.FLAGS[perm] || 0);
+ payload.deny &= ~(Permissions.FLAGS[perm] || 0);
+ }
+ }
+
+ return this.client.rest.methods.setChannelOverwrite(this, payload, reason).then(() => this);
+ }
+
+ /**
+ * Locks in the permission overwrites from the parent channel.
+ * @returns {Promise<GuildChannel>}
+ */
+ lockPermissions() {
+ if (!this.parent) return Promise.reject(new TypeError('Could not find a parent to this guild channel.'));
+ const permissionOverwrites = this.parent.permissionOverwrites.map(overwrite => ({
+ deny: overwrite.deny,
+ allow: overwrite.allow,
+ id: overwrite.id,
+ type: overwrite.type,
+ }));
+ return this.edit({ permissionOverwrites });
+ }
+
+ /**
+ * The data for a guild channel.
+ * @typedef {Object} ChannelData
+ * @property {string} [type] The type of the channel (Only when creating)
+ * @property {string} [name] The name of the channel
+ * @property {number} [position] The position 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 {CategoryChannel|Snowflake} [parent] The parent or parent ID of the channel
+ * @property {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [permissionOverwrites]
+ * Overwrites of the channel
+ * @property {number} [rateLimitPerUser] The rate limit per user of the channel in seconds
+ * @property {string} [reason] Reason for creating the channel (Only when creating)
+ */
+
+ /**
+ * Edits the channel.
+ * @param {ChannelData} data The new data for the channel
+ * @param {string} [reason] Reason for editing this channel
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Edit a channel
+ * channel.edit({ name: 'new-channel' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateChannel(this, data, reason).then(() => this);
+ }
+
+ /**
+ * Set a new name for the guild channel.
+ * @param {string} name The new name for the guild channel
+ * @param {string} [reason] Reason for changing the guild channel's name
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Set a new channel name
+ * channel.setName('not_general')
+ * .then(newChannel => console.log(`Channel's new name is ${newChannel.name}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Set a new position for the guild channel.
+ * @param {number} position The new position for the guild channel
+ * @param {boolean} [relative=false] Move the position relative to its current value
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Set a new channel position
+ * channel.setPosition(2)
+ * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`))
+ * .catch(console.error);
+ */
+ setPosition(position, relative) {
+ return this.guild.setChannelPosition(this, position, relative).then(() => this);
+ }
+
+ /**
+ * Set a new parent for the guild channel.
+ * @param {CategoryChannel|SnowFlake} parent The new parent for the guild channel
+ * @param {string} [reason] Reason for changing the guild channel's parent
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Sets the parent of a channel
+ * channel.setParent('174674066072928256')
+ * .then(updated => console.log(`Set the category of ${updated.name} to ${updated.parent.name}`))
+ * .catch(console.error);
+ */
+ setParent(parent, reason) {
+ parent = this.client.resolver.resolveChannelID(parent);
+ return this.edit({ parent }, reason);
+ }
+
+ /**
+ * Set a new topic for the guild channel.
+ * @param {string} topic The new topic for the guild channel
+ * @param {string} [reason] Reason for changing the guild channel's topic
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Set a new channel topic
+ * channel.setTopic('Needs more rate limiting')
+ * .then(updated => console.log(`Channel's new topic is ${updated.topic}`))
+ * .catch(console.error);
+ */
+ setTopic(topic, reason) {
+ return this.edit({ topic }, reason);
+ }
+
+ /**
+ * Create an invite to this guild channel.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {Object} [options={}] Options for the invite
+ * @param {boolean} [options.temporary=false] Whether members that joined via the invite should be automatically
+ * kicked after 24 hours if they have not yet received a role
+ * @param {number} [options.maxAge=86400] How long the invite should last (in seconds, 0 for forever)
+ * @param {number} [options.maxUses=0] Maximum number of uses
+ * @param {boolean} [options.unique=false] Create a unique invite, or use an existing one with similar settings
+ * @param {string} [reason] Reason for creating the invite
+ * @returns {Promise<Invite>}
+ * @example
+ * // Create an invite to a channel
+ * channel.createInvite()
+ * .then(invite => console.log(`Created an invite with a code of ${invite.code}`))
+ * .catch(console.error);
+ */
+ createInvite(options = {}, reason) {
+ return this.client.rest.methods.createChannelInvite(this, options, reason);
+ }
+
+ /* eslint-disable max-len */
+ /**
+ * Options to clone a guild channel.
+ * @typedef {Object} GuildChannelCloneOptions
+ * @property {string} [name=this.name] Name of the new channel
+ * @property {ChannelCreationOverwrites[]|Collection<Snowflake, PermissionOverwrites>} [permissionOverwrites=this.permissionOverwrites]
+ * Permission overwrites of the new channel
+ * @property {string} [type=this.type] Type of the new channel
+ * @property {string} [topic=this.topic] Topic of the new channel (only text)
+ * @property {boolean} [nsfw=this.nsfw] Whether the new channel is nsfw (only text)
+ * @property {number} [bitrate=this.bitrate] Bitrate of the new channel in bits (only voice)
+ * @property {number} [userLimit=this.userLimit] Maximum amount of users allowed in the new channel (only voice)
+ * @property {number} [rateLimitPerUser=ThisType.rateLimitPerUser] Ratelimit per user for the new channel (only text)
+ * @property {ChannelResolvable} [parent=this.parent] Parent of the new channel
+ * @property {string} [reason] Reason for cloning this channel
+ */
+ /* eslint-enable max-len */
+
+ /**
+ * Clone this channel.
+ * @param {string|GuildChannelCloneOptions} [nameOrOptions={}] Name for the new channel.
+ * **(deprecated, use options)**
+ * Alternatively options for cloning the channel
+ * @param {boolean} [withPermissions=true] Whether to clone the channel with this channel's permission overwrites
+ * **(deprecated, use options)**
+ * @param {boolean} [withTopic=true] Whether to clone the channel with this channel's topic
+ * **(deprecated, use options)**
+ * @param {string} [reason] Reason for cloning this channel **(deprecated, user options)**
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Clone a channel
+ * channel.clone({ topic: null, reason: 'Needed a clone' })
+ * .then(clone => console.log(`Cloned ${channel.name} to make a channel called ${clone.name}`))
+ * .catch(console.error);
+ */
+ clone(nameOrOptions = {}, withPermissions = true, withTopic = true, reason) {
+ // If more than one parameter was specified or the first is a string,
+ // convert them to a compatible options object and issue a warning
+ if (arguments.length > 1 || typeof nameOrOptions === 'string') {
+ process.emitWarning(
+ 'GuildChannel#clone: Clone channels using an options object instead of separate parameters.',
+ 'Deprecation Warning'
+ );
+
+ nameOrOptions = {
+ name: nameOrOptions,
+ permissionOverwrites: withPermissions ? this.permissionOverwrites : null,
+ topic: withTopic ? this.topic : null,
+ reason: reason || null,
+ };
+ }
+
+ Util.mergeDefault({
+ name: this.name,
+ permissionOverwrites: this.permissionOverwrites,
+ topic: this.topic,
+ type: this.type,
+ nsfw: this.nsfw,
+ parent: this.parent,
+ bitrate: this.bitrate,
+ userLimit: this.userLimit,
+ rateLimitPerUser: this.rateLimitPerUser,
+ reason: null,
+ }, nameOrOptions);
+
+ return this.guild.createChannel(nameOrOptions.name, nameOrOptions);
+ }
+
+ /**
+ * Fetches a collection of invites to this guild channel.
+ * Resolves with a collection mapping invites by their codes.
+ * @returns {Promise<Collection<string, Invite>>}
+ */
+ fetchInvites() {
+ return this.client.rest.makeRequest('get', Constants.Endpoints.Channel(this.id).invites, true)
+ .then(data => {
+ const invites = new Collection();
+ for (let invite of data) {
+ invite = new Invite(this.client, invite);
+ invites.set(invite.code, invite);
+ }
+
+ return invites;
+ });
+ }
+
+ /**
+ * Deletes this channel.
+ * @param {string} [reason] Reason for deleting this channel
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Delete the channel
+ * channel.delete('Making room for new channels')
+ * .then(deleted => console.log(`Deleted ${deleted.name} to make room for new channels`))
+ * .catch(console.error);
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteChannel(this, reason);
+ }
+
+ /**
+ * Checks if this channel has the same type, topic, position, name, overwrites and ID as another channel.
+ * In most cases, a simple `channel.id === channel2.id` will do, and is much faster too.
+ * @param {GuildChannel} channel Channel to compare with
+ * @returns {boolean}
+ */
+ equals(channel) {
+ let equal = channel &&
+ this.id === channel.id &&
+ this.type === channel.type &&
+ this.topic === channel.topic &&
+ this.position === channel.position &&
+ this.name === channel.name;
+
+ if (equal) {
+ if (this.permissionOverwrites && channel.permissionOverwrites) {
+ equal = this.permissionOverwrites.equals(channel.permissionOverwrites);
+ } else {
+ equal = !this.permissionOverwrites && !channel.permissionOverwrites;
+ }
+ }
+
+ return equal;
+ }
+
+ /**
+ * Whether the channel is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return this.id !== this.guild.id &&
+ this.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS);
+ }
+
+ /**
+ * Whether the channel is manageable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get manageable() {
+ if (this.client.user.id === this.guild.ownerID) return true;
+ const permissions = this.permissionsFor(this.client.user);
+ if (!permissions) return false;
+ return permissions.has([Permissions.FLAGS.MANAGE_CHANNELS, Permissions.FLAGS.VIEW_CHANNEL]);
+ }
+
+ /**
+ * Whether the channel is muted
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?boolean}
+ * @readonly
+ * @deprecated
+ */
+ get muted() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.guild.id).channelOverrides.get(this.id).muted;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ /**
+ * The type of message that should notify you
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?MessageNotificationType}
+ * @readonly
+ * @deprecated
+ */
+ get messageNotifications() {
+ if (this.client.user.bot) return null;
+ try {
+ return this.client.user.guildSettings.get(this.guild.id).channelOverrides.get(this.id).messageNotifications;
+ } catch (err) {
+ return Constants.MessageNotificationTypes[3];
+ }
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the channel's mention instead of the Channel object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <#123456789012345678>
+ * console.log(`Hello from ${channel}`);
+ * @example
+ * // Logs: Hello from <#123456789012345678>
+ * console.log('Hello from ' + channel);
+ */
+ toString() {
+ return `<#${this.id}>`;
+ }
+}
+
+module.exports = GuildChannel;
diff --git a/node_modules/discord.js/src/structures/GuildMember.js b/node_modules/discord.js/src/structures/GuildMember.js
new file mode 100644
index 0000000..45b75b3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildMember.js
@@ -0,0 +1,636 @@
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Role = require('./Role');
+const Permissions = require('../util/Permissions');
+const Collection = require('../util/Collection');
+const { Presence } = require('./Presence');
+const util = require('util');
+
+/**
+ * Represents a member of a guild on Discord.
+ * @implements {TextBasedChannel}
+ */
+class GuildMember {
+ constructor(guild, data) {
+ /**
+ * The client that instantiated this GuildMember
+ * @name GuildMember#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: guild.client });
+
+ /**
+ * The guild that this member is part of
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * The user that this member instance Represents
+ * @type {User}
+ */
+ this.user = {};
+
+ /**
+ * The timestamp this member joined the guild at
+ * @type {number}
+ */
+ this.joinedTimestamp = null;
+
+ /**
+ * The timestamp of when the member used their Nitro boost on the guild, if it was used
+ * @type {?number}
+ */
+ this.premiumSinceTimestamp = null;
+
+ this._roles = [];
+ if (data) this.setup(data);
+
+ /**
+ * The ID of the last message sent by this member in their guild, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The Message object of the last message sent by this member in their guild, if one was sent
+ * @type {?Message}
+ */
+ this.lastMessage = null;
+
+ /**
+ * Whether the member has been removed from the guild
+ * @type {boolean}
+ */
+ this.deleted = false;
+ }
+
+ setup(data) {
+ /**
+ * Whether this member is deafened server-wide
+ * @type {boolean}
+ */
+ this.serverDeaf = data.deaf;
+
+ /**
+ * Whether this member is muted server-wide
+ * @type {boolean}
+ */
+ this.serverMute = data.mute;
+
+ /**
+ * Whether this member is self-muted
+ * @type {boolean}
+ */
+ this.selfMute = data.self_mute;
+
+ /**
+ * Whether this member is self-deafened
+ * @type {boolean}
+ */
+ this.selfDeaf = data.self_deaf;
+
+ /**
+ * Whether this member is streaming using "Go Live"
+ * @type {boolean}
+ */
+ this.selfStream = data.self_stream || false;
+
+ /**
+ * The voice session ID of this member, if any
+ * @type {?Snowflake}
+ */
+ this.voiceSessionID = data.session_id;
+
+ /**
+ * The voice channel ID of this member, if any
+ * @type {?Snowflake}
+ */
+ this.voiceChannelID = data.channel_id;
+
+ /**
+ * Whether this member is speaking and the client is in the same channel
+ * @type {boolean}
+ */
+ this.speaking = false;
+
+ /**
+ * The nickname of this member, if they have one
+ * @type {?string}
+ */
+ this.nickname = data.nick || null;
+
+ if (data.joined_at) this.joinedTimestamp = new Date(data.joined_at).getTime();
+ if (data.premium_since) this.premiumSinceTimestamp = new Date(data.premium_since).getTime();
+
+ this.user = data.user;
+ this._roles = data.roles;
+ }
+
+ /**
+ * The time this member joined the guild
+ * @type {?Date}
+ * @readonly
+ */
+ get joinedAt() {
+ return this.joinedTimestamp ? new Date(this.joinedTimestamp) : null;
+ }
+
+ /**
+ * The time of when the member used their Nitro boost on the guild, if it was used
+ * @type {?Date}
+ * @readonly
+ */
+ get premiumSince() {
+ return this.premiumSinceTimestamp ? new Date(this.premiumSinceTimestamp) : null;
+ }
+
+ /**
+ * The presence of this member
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ return this.frozenPresence || this.guild.presences.get(this.id) || new Presence(undefined, this.client);
+ }
+
+ /**
+ * A list of roles that are applied to this member, mapped by the role ID
+ * @type {Collection<Snowflake, Role>}
+ * @readonly
+ */
+ get roles() {
+ const list = new Collection();
+ const everyoneRole = this.guild.roles.get(this.guild.id);
+
+ if (everyoneRole) list.set(everyoneRole.id, everyoneRole);
+
+ for (const roleID of this._roles) {
+ const role = this.guild.roles.get(roleID);
+ if (role) list.set(role.id, role);
+ }
+
+ return list;
+ }
+
+ /**
+ * The role of this member with the highest position
+ * @type {Role}
+ * @readonly
+ */
+ get highestRole() {
+ return this.roles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
+ }
+
+ /**
+ * The role of this member used to set their color
+ * @type {?Role}
+ * @readonly
+ */
+ get colorRole() {
+ const coloredRoles = this.roles.filter(role => role.color);
+ if (!coloredRoles.size) return null;
+ return coloredRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
+ }
+
+ /**
+ * The displayed color of this member in base 10
+ * @type {number}
+ * @readonly
+ */
+ get displayColor() {
+ const role = this.colorRole;
+ return (role && role.color) || 0;
+ }
+
+ /**
+ * The displayed color of this member in hexadecimal
+ * @type {string}
+ * @readonly
+ */
+ get displayHexColor() {
+ const role = this.colorRole;
+ return (role && role.hexColor) || '#000000';
+ }
+
+ /**
+ * The role of this member used to hoist them in a separate category in the users list
+ * @type {?Role}
+ * @readonly
+ */
+ get hoistRole() {
+ const hoistedRoles = this.roles.filter(role => role.hoist);
+ if (!hoistedRoles.size) return null;
+ return hoistedRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
+ }
+
+ /**
+ * Whether this member is muted in any way
+ * @type {boolean}
+ * @readonly
+ */
+ get mute() {
+ return this.selfMute || this.serverMute;
+ }
+
+ /**
+ * Whether this member is deafened in any way
+ * @type {boolean}
+ * @readonly
+ */
+ get deaf() {
+ return this.selfDeaf || this.serverDeaf;
+ }
+
+ /**
+ * The voice channel this member is in, if any
+ * @type {?VoiceChannel}
+ * @readonly
+ */
+ get voiceChannel() {
+ return this.guild.channels.get(this.voiceChannelID);
+ }
+
+ /**
+ * The ID of this user
+ * @type {Snowflake}
+ * @readonly
+ */
+ get id() {
+ return this.user.id;
+ }
+
+ /**
+ * The nickname of this member, or their username if they don't have one
+ * @type {string}
+ * @readonly
+ */
+ get displayName() {
+ return this.nickname || this.user.username;
+ }
+
+ /**
+ * The overall set of permissions for this member, taking only roles into account
+ * @type {Permissions}
+ * @readonly
+ */
+ get permissions() {
+ if (this.user.id === this.guild.ownerID) return new Permissions(this, Permissions.ALL);
+
+ let permissions = 0;
+ const roles = this.roles;
+ for (const role of roles.values()) permissions |= role.permissions;
+
+ return new Permissions(this, permissions);
+ }
+
+ /**
+ * Whether this member is manageable in terms of role hierarchy by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get manageable() {
+ if (this.user.id === this.guild.ownerID) return false;
+ if (this.user.id === this.client.user.id) return false;
+ if (this.client.user.id === this.guild.ownerID) return true;
+ return this.guild.me.highestRole.comparePositionTo(this.highestRole) > 0;
+ }
+
+ /**
+ * Whether this member is kickable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get kickable() {
+ return this.manageable && this.guild.me.permissions.has(Permissions.FLAGS.KICK_MEMBERS);
+ }
+
+ /**
+ * Whether this member is bannable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get bannable() {
+ return this.manageable && this.guild.me.permissions.has(Permissions.FLAGS.BAN_MEMBERS);
+ }
+
+ /**
+ * Returns `channel.permissionsFor(guildMember)`. Returns permissions for this member in a guild channel,
+ * taking into account roles and permission overwrites.
+ * @param {ChannelResolvable} channel The guild channel to use as context
+ * @returns {?Permissions}
+ */
+ permissionsIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ if (!channel || !channel.guild) throw new Error('Could not resolve channel to a guild channel.');
+ return channel.permissionsFor(this);
+ }
+
+ /**
+ * Checks if any of this member's roles have a permission.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
+ * **(deprecated)**
+ * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override
+ * (takes priority over `explicit`)
+ * @param {boolean} [checkOwner] Whether to allow being the guild's owner to override
+ * (takes priority over `explicit`)
+ * @returns {boolean}
+ */
+ hasPermission(permission, explicit = false, checkAdmin, checkOwner) {
+ if (typeof checkAdmin === 'undefined') checkAdmin = !explicit;
+ if (typeof checkOwner === 'undefined') checkOwner = !explicit;
+ if (checkOwner && this.user.id === this.guild.ownerID) return true;
+ return this.roles.some(r => r.hasPermission(permission, undefined, checkAdmin));
+ }
+
+ /**
+ * Checks whether the roles of this member allows them to perform specific actions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
+ * @returns {boolean}
+ * @deprecated
+ */
+ hasPermissions(permissions, explicit = false) {
+ if (!explicit && this.user.id === this.guild.ownerID) return true;
+ return this.hasPermission(permissions, explicit);
+ }
+
+ /**
+ * Checks whether the roles of this member allows them to perform specific actions, and lists any missing permissions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
+ * @returns {PermissionResolvable}
+ */
+ missingPermissions(permissions, explicit = false) {
+ if (!(permissions instanceof Array)) permissions = [permissions];
+ return this.permissions.missing(permissions, explicit);
+ }
+
+ /**
+ * The data for editing this member.
+ * @typedef {Object} GuildMemberEditData
+ * @property {string} [nick] The nickname to set for the member
+ * @property {Collection<Snowflake, Role>|RoleResolvable[]} [roles] The roles or role IDs to apply
+ * @property {boolean} [mute] Whether or not the member should be muted
+ * @property {boolean} [deaf] Whether or not the member should be deafened
+ * @property {ChannelResolvable|null} [channel] Channel to move member to (if they are connected to voice), or `null`
+ * if you want to kick them from voice
+ */
+
+ /**
+ * Edits this member.
+ * @param {GuildMemberEditData} data The data to edit the member with
+ * @param {string} [reason] Reason for editing this user
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Set a member's nickname and clear their roles
+ * message.member.edit({
+ * nick: 'Cool Name',
+ * roles: []
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateGuildMember(this, data, reason);
+ }
+
+ /**
+ * Mute/unmute this member.
+ * @param {boolean} mute Whether or not the member should be muted
+ * @param {string} [reason] Reason for muting or unmuting
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Mute a member with a reason
+ * message.member.setMute(true, 'It needed to be done')
+ * .then(() => console.log(`Muted ${message.member.displayName}`)))
+ * .catch(console.error);
+ */
+ setMute(mute, reason) {
+ return this.edit({ mute }, reason);
+ }
+
+ /**
+ * Deafen/undeafen this member.
+ * @param {boolean} deaf Whether or not the member should be deafened
+ * @param {string} [reason] Reason for deafening or undeafening
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Deafen a member
+ * message.member.setDeaf(true)
+ * .then(() => console.log(`Deafened ${message.member.displayName}`))
+ * .catch(console.error);
+ */
+ setDeaf(deaf, reason) {
+ return this.edit({ deaf }, reason);
+ }
+
+ /**
+ * Moves this member to the given channel.
+ * @param {ChannelResolvable|null} channel Channel to move the member to, or `null` if you want to kick them from
+ * voice
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Moves a member to a voice channel
+ * member.setVoiceChannel('174674066072928256')
+ * .then(() => console.log(`Moved ${member.displayName}`))
+ * .catch(console.error);
+ */
+ setVoiceChannel(channel) {
+ return this.edit({ channel });
+ }
+
+ /**
+ * Sets the roles applied to this member.
+ * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to apply
+ * @param {string} [reason] Reason for applying the roles
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Set the member's roles to a single role
+ * guildMember.setRoles(['391156570408615936'])
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Remove all of the member's roles
+ * guildMember.setRoles([])
+ * .then(member => console.log(`${member.displayName} now has ${member.roles.size} roles`))
+ * .catch(console.error);
+ */
+ setRoles(roles, reason) {
+ return this.edit({ roles }, reason);
+ }
+
+ /**
+ * Adds a single role to this member.
+ * @param {RoleResolvable} role The role or ID of the role to add
+ * @param {string} [reason] Reason for adding the role
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Give a role to a member
+ * message.member.addRole('193654001089118208')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ addRole(role, reason) {
+ if (!(role instanceof Role)) role = this.guild.roles.get(role);
+ if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.'));
+ return this.client.rest.methods.addMemberRole(this, role, reason);
+ }
+
+ /**
+ * Adds multiple roles to this member.
+ * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to add
+ * @param {string} [reason] Reason for adding the roles
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Gives a member a few roles
+ * message.member.addRoles(['193654001089118208', '369308579892690945'])
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ addRoles(roles, reason) {
+ let allRoles;
+ if (roles instanceof Collection) {
+ allRoles = this._roles.slice();
+ for (const role of roles.values()) allRoles.push(role.id);
+ } else {
+ allRoles = this._roles.concat(roles);
+ }
+ return this.edit({ roles: allRoles }, reason);
+ }
+
+ /**
+ * Removes a single role from this member.
+ * @param {RoleResolvable} role The role or ID of the role to remove
+ * @param {string} [reason] Reason for removing the role
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Remove a role from a member
+ * message.member.removeRole('193654001089118208')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ removeRole(role, reason) {
+ if (!(role instanceof Role)) role = this.guild.roles.get(role);
+ if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.'));
+ return this.client.rest.methods.removeMemberRole(this, role, reason);
+ }
+
+ /**
+ * Removes multiple roles from this member.
+ * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to remove
+ * @param {string} [reason] Reason for removing the roles
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Removes a few roles from the member
+ * message.member.removeRoles(['193654001089118208', '369308579892690945'])
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ removeRoles(roles, reason) {
+ const allRoles = this._roles.slice();
+ if (roles instanceof Collection) {
+ for (const role of roles.values()) {
+ const index = allRoles.indexOf(role.id);
+ if (index >= 0) allRoles.splice(index, 1);
+ }
+ } else {
+ for (const role of roles) {
+ const index = allRoles.indexOf(role instanceof Role ? role.id : role);
+ if (index >= 0) allRoles.splice(index, 1);
+ }
+ }
+ return this.edit({ roles: allRoles }, reason);
+ }
+
+ /**
+ * Set the nickname for this member.
+ * @param {string} nick The nickname for the guild member
+ * @param {string} [reason] Reason for setting the nickname
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Update the member's nickname
+ * message.member.setNickname('Cool Name')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setNickname(nick, reason) {
+ return this.edit({ nick }, reason);
+ }
+
+ /**
+ * Creates a DM channel between the client and this member.
+ * @returns {Promise<DMChannel>}
+ */
+ createDM() {
+ return this.user.createDM();
+ }
+
+ /**
+ * Deletes any DMs with this guild member.
+ * @returns {Promise<DMChannel>}
+ */
+ deleteDM() {
+ return this.user.deleteDM();
+ }
+
+ /**
+ * Kick this member from the guild.
+ * @param {string} [reason] Reason for kicking user
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Kick a member
+ * member.kick()
+ * .then(() => console.log(`Kicked ${member.displayName}`))
+ * .catch(console.error);
+ */
+ kick(reason) {
+ return this.client.rest.methods.kickGuildMember(this.guild, this, reason);
+ }
+
+ /**
+ * Ban this member.
+ * @param {Object|number|string} [options] Ban options. If a number, the number of days to delete messages for, if a
+ * string, the ban reason. Supplying an object allows you to do both.
+ * @param {number} [options.days=0] Number of days of messages to delete
+ * @param {string} [options.reason] Reason for banning
+ * @returns {Promise<GuildMember>}
+ * @example
+ * // Ban a guild member
+ * member.ban(7)
+ * .then(() => console.log(`Banned ${member.displayName}`))
+ * .catch(console.error);
+ */
+ ban(options) {
+ return this.guild.ban(this, options);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the user's mention instead of the Member object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <@123456789>!
+ * console.log(`Hello from ${member}!`);
+ */
+ toString() {
+ return `<@${this.nickname ? '!' : ''}${this.user.id}>`;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendCode() {}
+}
+
+TextBasedChannel.applyToClass(GuildMember);
+
+GuildMember.prototype.hasPermissions = util.deprecate(GuildMember.prototype.hasPermissions,
+ 'GuildMember#hasPermissions is deprecated - use GuildMember#hasPermission, it now takes an array');
+GuildMember.prototype.missingPermissions = util.deprecate(GuildMember.prototype.missingPermissions,
+ 'GuildMember#missingPermissions is deprecated - use GuildMember#permissions.missing, it now takes an array');
+
+module.exports = GuildMember;
diff --git a/node_modules/discord.js/src/structures/Integration.js b/node_modules/discord.js/src/structures/Integration.js
new file mode 100644
index 0000000..96af017
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Integration.js
@@ -0,0 +1,151 @@
+/**
+ * The information account for an integration
+ * @typedef {Object} IntegrationAccount
+ * @property {string} id The id of the account
+ * @property {string} name The name of the account
+ */
+
+/**
+ * Represents a guild integration.
+ */
+class Integration {
+ constructor(client, data, guild) {
+ /**
+ * The client that created this integration
+ * @name Integration#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The guild this integration belongs to
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * The integration id
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The integration name
+ * @type {string}
+ */
+ this.name = data.name;
+ /**
+ * The integration type (twitch, youtube, etc)
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * Whether this integration is enabled
+ * @type {boolean}
+ */
+ this.enabled = data.enabled;
+
+ /**
+ * Whether this integration is syncing
+ * @type {boolean}
+ */
+ this.syncing = data.syncing;
+
+ /**
+ * The role that this integration uses for subscribers
+ * @type {Role}
+ */
+ this.role = this.guild.roles.get(data.role_id);
+
+ /**
+ * The user for this integration
+ * @type {User}
+ */
+ this.user = this.client.dataManager.newUser(data.user);
+
+ /**
+ * The account integration information
+ * @type {IntegrationAccount}
+ */
+ this.account = data.account;
+
+ /**
+ * The last time this integration was last synced
+ * @type {number}
+ */
+ this.syncedAt = data.synced_at;
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The behavior of expiring subscribers
+ * @type {number}
+ */
+ this.expireBehavior = data.expire_behavior;
+
+ /**
+ * The grace period before expiring subscribers
+ * @type {number}
+ */
+ this.expireGracePeriod = data.expire_grace_period;
+ }
+
+ /**
+ * Syncs this integration
+ * @returns {Promise<Integration>}
+ */
+ sync() {
+ this.syncing = true;
+ return this.client.rest.methods.syncIntegration(this)
+ .then(() => {
+ this.syncing = false;
+ this.syncedAt = Date.now();
+ return this;
+ });
+ }
+
+ /**
+ * The data for editing an integration.
+ * @typedef {Object} IntegrationEditData
+ * @property {number} [expireBehavior] The new behaviour of expiring subscribers
+ * @property {number} [expireGracePeriod] The new grace period before expiring subscribers
+ */
+
+ /**
+ * Edits this integration.
+ * @param {IntegrationEditData} data The data to edit this integration with
+ * @param {string} reason Reason for editing this integration
+ * @returns {Promise<Integration>}
+ */
+ edit(data, reason) {
+ if ('expireBehavior' in data) {
+ data.expire_behavior = data.expireBehavior;
+ data.expireBehavior = undefined;
+ }
+ if ('expireGracePeriod' in data) {
+ data.expire_grace_period = data.expireGracePeriod;
+ data.expireGracePeriod = undefined;
+ }
+ // The option enable_emoticons is only available for Twitch at this moment
+ return this.client.rest.methods.editIntegration(this, data, reason)
+ .then(() => {
+ this._patch(data);
+ return this;
+ });
+ }
+
+ /**
+ * Deletes this integration.
+ * @returns {Promise<Integration>}
+ * @param {string} [reason] Reason for deleting this integration
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteIntegration(this, reason)
+ .then(() => this);
+ }
+}
+
+module.exports = Integration;
diff --git a/node_modules/discord.js/src/structures/Invite.js b/node_modules/discord.js/src/structures/Invite.js
new file mode 100644
index 0000000..1d84cf5
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Invite.js
@@ -0,0 +1,164 @@
+const PartialGuild = require('./PartialGuild');
+const PartialGuildChannel = require('./PartialGuildChannel');
+const Constants = require('../util/Constants');
+
+/**
+ * Represents an invitation to a guild channel.
+ * <warn>The only guaranteed properties are `code`, `url`, `guild`, and `channel`.
+ * Other properties can be missing.</warn>
+ */
+class Invite {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the invite
+ * @name Invite#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The guild the invite is for. If this guild is already known, this will be a guild object. If the guild is
+ * unknown, this will be a PartialGuild object
+ * @type {Guild|PartialGuild}
+ */
+ this.guild = this.client.guilds.get(data.guild.id) || new PartialGuild(this.client, data.guild);
+
+ /**
+ * The code for this invite
+ * @type {string}
+ */
+ this.code = data.code;
+
+ /**
+ * The approximate number of online members of the guild this invite is for
+ * @type {number}
+ */
+ this.presenceCount = data.approximate_presence_count;
+
+ /**
+ * The approximate total number of members of the guild this invite is for
+ * @type {number}
+ */
+ this.memberCount = data.approximate_member_count;
+
+ /**
+ * The number of text channels the guild this invite goes to has
+ * @type {number}
+ */
+ this.textChannelCount = data.guild.text_channel_count;
+
+ /**
+ * The number of voice channels the guild this invite goes to has
+ * @type {number}
+ */
+ this.voiceChannelCount = data.guild.voice_channel_count;
+
+ /**
+ * Whether or not this invite is temporary
+ * @type {boolean}
+ */
+ this.temporary = data.temporary;
+
+ /**
+ * The maximum age of the invite, in seconds
+ * @type {?number}
+ */
+ this.maxAge = data.max_age;
+
+ /**
+ * How many times this invite has been used
+ * @type {number}
+ */
+ this.uses = data.uses;
+
+ /**
+ * The maximum uses of this invite
+ * @type {number}
+ */
+ this.maxUses = data.max_uses;
+
+ if (data.inviter) {
+ /**
+ * The user who created this invite
+ * @type {?User}
+ */
+ this.inviter = this.client.dataManager.newUser(data.inviter);
+ }
+
+ /**
+ * The channel the invite is for. If this channel is already known, this will be a GuildChannel object.
+ * If the channel is unknown, this will be a PartialGuildChannel object.
+ * @type {GuildChannel|PartialGuildChannel}
+ */
+ this.channel = this.client.channels.get(data.channel.id) || new PartialGuildChannel(this.client, data.channel);
+
+ /**
+ * The timestamp the invite was created at
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.created_at).getTime();
+ }
+
+ /**
+ * The time the invite was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The timestamp the invite will expire at
+ * @type {number}
+ * @readonly
+ */
+ get expiresTimestamp() {
+ return this.createdTimestamp + (this.maxAge * 1000);
+ }
+
+ /**
+ * The time the invite will expire
+ * @type {Date}
+ * @readonly
+ */
+ get expiresAt() {
+ return new Date(this.expiresTimestamp);
+ }
+
+ /**
+ * The URL to the invite
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return Constants.Endpoints.inviteLink(this.code);
+ }
+
+ /**
+ * Deletes this invite.
+ * @param {string} [reason] Reason for deleting this invite
+ * @returns {Promise<Invite>}
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteInvite(this, reason);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the invite's URL instead of the object.
+ * @returns {string}
+ * @example
+ * // Logs: Invite: https://discord.gg/A1b2C3
+ * console.log(`Invite: ${invite}`);
+ */
+ toString() {
+ return this.url;
+ }
+}
+
+module.exports = Invite;
diff --git a/node_modules/discord.js/src/structures/Message.js b/node_modules/discord.js/src/structures/Message.js
new file mode 100644
index 0000000..c71844f
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Message.js
@@ -0,0 +1,661 @@
+const Mentions = require('./MessageMentions');
+const Attachment = require('./MessageAttachment');
+const Embed = require('./MessageEmbed');
+const RichEmbed = require('./RichEmbed');
+const MessageReaction = require('./MessageReaction');
+const ReactionCollector = require('./ReactionCollector');
+const Util = require('../util/Util');
+const Collection = require('../util/Collection');
+const Constants = require('../util/Constants');
+const Permissions = require('../util/Permissions');
+const MessageFlags = require('../util/MessageFlags');
+let GuildMember;
+
+/**
+ * Represents a message on Discord.
+ */
+class Message {
+ constructor(channel, data, client) {
+ /**
+ * The client that instantiated the Message
+ * @name Message#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The channel that the message was sent in
+ * @type {TextChannel|DMChannel|GroupDMChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * Whether this message has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) { // eslint-disable-line complexity
+ /**
+ * The ID of the message
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of the message
+ * @type {MessageType}
+ */
+ this.type = Constants.MessageTypes[data.type];
+
+ /**
+ * The content of the message
+ * @type {string}
+ */
+ this.content = data.content;
+
+ /**
+ * The author of the message
+ * @type {User}
+ */
+ this.author = this.client.dataManager.newUser(data.author, !data.webhook_id);
+
+ /**
+ * Whether or not this message is pinned
+ * @type {boolean}
+ */
+ this.pinned = data.pinned;
+
+ /**
+ * Whether or not the message was Text-To-Speech
+ * @type {boolean}
+ */
+ this.tts = data.tts;
+
+ /**
+ * A random number or string used for checking message delivery
+ * <warn>This is only received after the message was sent successfully, and
+ * lost if re-fetched</warn>
+ * @type {?string}
+ */
+ this.nonce = data.nonce;
+
+ /**
+ * Whether or not this message was sent by Discord, not actually a user (e.g. pin notifications)
+ * @type {boolean}
+ */
+ this.system = data.type !== 0;
+
+ /**
+ * A list of embeds in the message - e.g. YouTube Player
+ * @type {MessageEmbed[]}
+ */
+ this.embeds = data.embeds.map(e => new Embed(this, e));
+
+ /**
+ * A collection of attachments in the message - e.g. Pictures - mapped by their ID
+ * @type {Collection<Snowflake, MessageAttachment>}
+ */
+ this.attachments = new Collection();
+ for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment));
+
+ /**
+ * The timestamp the message was sent at
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.timestamp).getTime();
+
+ /**
+ * The timestamp the message was last edited at (if applicable)
+ * @type {?number}
+ */
+ this.editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null;
+
+ /**
+ * A collection of reactions to this message, mapped by the reaction ID
+ * @type {Collection<Snowflake, MessageReaction>}
+ */
+ this.reactions = new Collection();
+ if (data.reactions && data.reactions.length > 0) {
+ for (const reaction of data.reactions) {
+ const id = reaction.emoji.id ? `${reaction.emoji.name}:${reaction.emoji.id}` : reaction.emoji.name;
+ this.reactions.set(id, new MessageReaction(this, reaction.emoji, reaction.count, reaction.me));
+ }
+ }
+
+ /**
+ * All valid mentions that the message contains
+ * @type {MessageMentions}
+ */
+ this.mentions = new Mentions(this, data.mentions, data.mention_roles, data.mention_everyone, data.mention_channels);
+
+ /**
+ * ID of the webhook that sent the message, if applicable
+ * @type {?Snowflake}
+ */
+ this.webhookID = data.webhook_id || null;
+
+ /**
+ * Whether this message is a hit in a search
+ * @type {?boolean}
+ */
+ this.hit = typeof data.hit === 'boolean' ? data.hit : null;
+
+ /**
+ * Flags that are applied to the message
+ * @type {Readonly<MessageFlags>}
+ */
+ this.flags = new MessageFlags(data.flags).freeze();
+
+ /**
+ * Reference data sent in a crossposted message.
+ * @typedef {Object} MessageReference
+ * @property {string} channelID ID of the channel the message was crossposted from
+ * @property {?string} guildID ID of the guild the message was crossposted from
+ * @property {?string} messageID ID of the message that was crossposted
+ */
+
+ /**
+ * Message reference data
+ * @type {?MessageReference}
+ */
+ this.reference = data.message_reference ? {
+ channelID: data.message_reference.channel_id,
+ guildID: data.message_reference.guild_id,
+ messageID: data.message_reference.message_id,
+ } : null;
+
+ /**
+ * The previous versions of the message, sorted with the most recent first
+ * @type {Message[]}
+ * @private
+ */
+ this._edits = [];
+
+ if (data.member && this.guild && this.author && !this.guild.members.has(this.author.id)) {
+ this.guild._addMember(Object.assign(data.member, { user: this.author }), false);
+ }
+
+ /**
+ * Represents the author of the message as a guild member
+ * Only available if the message comes from a guild where the author is still a member
+ * @type {?GuildMember}
+ */
+ this.member = this.guild ? this.guild.member(this.author) || null : null;
+ }
+
+ /**
+ * Updates the message.
+ * @param {Object} data Raw Discord message update data
+ * @private
+ */
+ patch(data) {
+ const clone = Util.cloneObject(this);
+ this._edits.unshift(clone);
+
+ if ('edited_timestamp' in data) this.editedTimestamp = new Date(data.edited_timestamp).getTime();
+ if ('content' in data) this.content = data.content;
+ if ('pinned' in data) this.pinned = data.pinned;
+ if ('tts' in data) this.tts = data.tts;
+ if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(this, e));
+ else this.embeds = this.embeds.slice();
+
+ if ('attachments' in data) {
+ this.attachments = new Collection();
+ for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment));
+ } else {
+ this.attachments = new Collection(this.attachments);
+ }
+
+ this.mentions = new Mentions(
+ this,
+ 'mentions' in data ? data.mentions : this.mentions.users,
+ 'mentions_roles' in data ? data.mentions_roles : this.mentions.roles,
+ 'mention_everyone' in data ? data.mention_everyone : this.mentions.everyone,
+ 'mention_channels' in data ? data.mention_channels : this.mentions.crosspostedChannels
+ );
+
+ this.flags = new MessageFlags('flags' in data ? data.flags : 0).freeze();
+ }
+
+ /**
+ * The time the message was sent
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The time the message was last edited at (if applicable)
+ * @type {?Date}
+ * @readonly
+ */
+ get editedAt() {
+ return this.editedTimestamp ? new Date(this.editedTimestamp) : null;
+ }
+
+ /**
+ * The guild the message was sent in (if in a guild channel)
+ * @type {?Guild}
+ * @readonly
+ */
+ get guild() {
+ return this.channel.guild || null;
+ }
+
+ /**
+ * The url to jump to the message
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return `https://discordapp.com/channels/${this.guild ? this.guild.id : '@me'}/${this.channel.id}/${this.id}`;
+ }
+
+ /**
+ * The message contents with all mentions replaced by the equivalent text.
+ * If mentions cannot be resolved to a name, the relevant mention in the message content will not be converted.
+ * @type {string}
+ * @readonly
+ */
+ get cleanContent() {
+ return this.content
+ .replace(/@(everyone|here)/g, '@\u200b$1')
+ .replace(/<@!?[0-9]+>/g, input => {
+ const id = input.replace(/<|!|>|@/g, '');
+ if (this.channel.type === 'dm' || this.channel.type === 'group') {
+ return this.client.users.has(id) ? `@${this.client.users.get(id).username}` : input;
+ }
+
+ const member = this.channel.guild.members.get(id);
+ if (member) {
+ if (member.nickname) return `@${member.nickname}`;
+ return `@${member.user.username}`;
+ } else {
+ const user = this.client.users.get(id);
+ if (user) return `@${user.username}`;
+ return input;
+ }
+ })
+ .replace(/<#[0-9]+>/g, input => {
+ const channel = this.client.channels.get(input.replace(/<|#|>/g, ''));
+ if (channel) return `#${channel.name}`;
+ return input;
+ })
+ .replace(/<@&[0-9]+>/g, input => {
+ if (this.channel.type === 'dm' || this.channel.type === 'group') return input;
+ const role = this.guild.roles.get(input.replace(/<|@|>|&/g, ''));
+ if (role) return `@${role.name}`;
+ return input;
+ });
+ }
+
+ /**
+ * Creates a reaction collector.
+ * @param {CollectorFilter} filter The filter to apply
+ * @param {ReactionCollectorOptions} [options={}] Options to send to the collector
+ * @returns {ReactionCollector}
+ * @example
+ * // Create a reaction collector
+ * const filter = (reaction, user) => reaction.emoji.name === '👌' && user.id === 'someID'
+ * const collector = message.createReactionCollector(filter, { time: 15000 });
+ * collector.on('collect', r => console.log(`Collected ${r.emoji.name}`));
+ * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
+ */
+ createReactionCollector(filter, options = {}) {
+ return new ReactionCollector(this, filter, options);
+ }
+
+ /**
+ * An object containing the same properties as CollectorOptions, but a few more:
+ * @typedef {ReactionCollectorOptions} AwaitReactionsOptions
+ * @property {string[]} [errors] Stop/end reasons that cause the promise to reject
+ */
+
+ /**
+ * Similar to createMessageCollector but in promise form.
+ * Resolves with a collection of reactions that pass the specified filter.
+ * @param {CollectorFilter} filter The filter function to use
+ * @param {AwaitReactionsOptions} [options={}] Optional options to pass to the internal collector
+ * @returns {Promise<Collection<string, MessageReaction>>}
+ * @example
+ * // Create a reaction collector
+ * const filter = (reaction, user) => reaction.emoji.name === '👌' && user.id === 'someID'
+ * message.awaitReactions(filter, { time: 15000 })
+ * .then(collected => console.log(`Collected ${collected.size} reactions`))
+ * .catch(console.error);
+ */
+ awaitReactions(filter, options = {}) {
+ return new Promise((resolve, reject) => {
+ const collector = this.createReactionCollector(filter, options);
+ collector.once('end', (reactions, reason) => {
+ if (options.errors && options.errors.includes(reason)) reject(reactions);
+ else resolve(reactions);
+ });
+ });
+ }
+
+ /**
+ * An array of cached versions of the message, including the current version
+ * Sorted from latest (first) to oldest (last)
+ * @type {Message[]}
+ * @readonly
+ */
+ get edits() {
+ const copy = this._edits.slice();
+ copy.unshift(this);
+ return copy;
+ }
+
+ /**
+ * Whether the message is editable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get editable() {
+ return this.author.id === this.client.user.id;
+ }
+
+ /**
+ * Whether the message is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return !this.deleted && (this.author.id === this.client.user.id || (this.guild &&
+ this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES)
+ ));
+ }
+
+ /**
+ * Whether the message is pinnable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get pinnable() {
+ return this.type === 'DEFAULT' && (!this.guild ||
+ this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES));
+ }
+
+ /**
+ * Whether or not a user, channel or role is mentioned in this message.
+ * @param {GuildChannel|User|Role|string} data Either a guild channel, user or a role object, or a string representing
+ * the ID of any of these
+ * @returns {boolean}
+ */
+ isMentioned(data) {
+ data = data && data.id ? data.id : data;
+ return this.mentions.users.has(data) || this.mentions.channels.has(data) || this.mentions.roles.has(data);
+ }
+
+ /**
+ * Whether or not a guild member is mentioned in this message. Takes into account
+ * user mentions, role mentions, and @everyone/@here mentions.
+ * @param {GuildMember|User} member The member/user to check for a mention of
+ * @returns {boolean}
+ */
+ isMemberMentioned(member) {
+ // Lazy-loading is used here to get around a circular dependency that breaks things
+ if (!GuildMember) GuildMember = require('./GuildMember');
+ if (this.mentions.everyone) return true;
+ if (this.mentions.users.has(member.id)) return true;
+ if (member instanceof GuildMember && member.roles.some(r => this.mentions.roles.has(r.id))) return true;
+ return false;
+ }
+
+ /**
+ * Options that can be passed into editMessage.
+ * @typedef {Object} MessageEditOptions
+ * @property {Object} [embed] An embed to be added/edited
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {MessageFlagsResolvable} [flags] Message flags to apply
+ */
+
+ /**
+ * Edit the content of the message.
+ * @param {StringResolvable} [content] The new content for the message
+ * @param {MessageEditOptions|RichEmbed} [options] The options to provide
+ * @returns {Promise<Message>}
+ * @example
+ * // Update the content of a message
+ * message.edit('This is my new content!')
+ * .then(msg => console.log(`New message content: ${msg}`))
+ * .catch(console.error);
+ */
+ edit(content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+ if (options instanceof RichEmbed) options = { embed: options };
+ return this.client.rest.methods.updateMessage(this, content, options);
+ }
+
+ /**
+ * Edit the content of the message, with a code block.
+ * @param {string} lang The language for the code block
+ * @param {StringResolvable} content The new content for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ editCode(lang, content) {
+ content = Util.escapeMarkdown(this.client.resolver.resolveString(content), true);
+ return this.edit(`\`\`\`${lang || ''}\n${content}\n\`\`\``);
+ }
+
+ /**
+ * Pins this message to the channel's pinned messages.
+ * @returns {Promise<Message>}
+ */
+ pin() {
+ return this.client.rest.methods.pinMessage(this);
+ }
+
+ /**
+ * Unpins this message from the channel's pinned messages.
+ * @returns {Promise<Message>}
+ */
+ unpin() {
+ return this.client.rest.methods.unpinMessage(this);
+ }
+
+ /**
+ * Add a reaction to the message.
+ * @param {string|Emoji|ReactionEmoji} emoji The emoji to react with
+ * @returns {Promise<MessageReaction>}
+ * @example
+ * // React to a message with a unicode emoji
+ * message.react('🤔')
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // React to a message with a custom emoji
+ * message.react(message.guild.emojis.get('123456789012345678'))
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ react(emoji) {
+ emoji = this.client.resolver.resolveEmojiIdentifier(emoji);
+ if (!emoji) throw new TypeError('Emoji must be a string or Emoji/ReactionEmoji');
+
+ return this.client.rest.methods.addMessageReaction(this, emoji);
+ }
+
+ /**
+ * Remove all reactions from a message.
+ * @returns {Promise<Message>}
+ */
+ clearReactions() {
+ return this.client.rest.methods.removeMessageReactions(this);
+ }
+
+ /**
+ * Deletes the message.
+ * @param {number} [timeout=0] How long to wait to delete the message in milliseconds
+ * @returns {Promise<Message>}
+ * @example
+ * // Delete a message
+ * message.delete()
+ * .then(msg => console.log(`Deleted message from ${msg.author.username}`))
+ * .catch(console.error);
+ */
+ delete(timeout = 0) {
+ if (timeout <= 0) {
+ return this.client.rest.methods.deleteMessage(this);
+ } else {
+ return new Promise(resolve => {
+ this.client.setTimeout(() => {
+ resolve(this.delete());
+ }, timeout);
+ });
+ }
+ }
+
+ /**
+ * Reply to the message.
+ * @param {StringResolvable} [content] The content for the message
+ * @param {MessageOptions} [options] The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @example
+ * // Reply to a message
+ * message.reply('Hey, I\'m a reply!')
+ * .then(sent => console.log(`Sent a reply to ${sent.author.username}`))
+ * .catch(console.error);
+ */
+ reply(content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+ return this.channel.send(content, Object.assign(options, { reply: this.member || this.author }));
+ }
+
+ /**
+ * Marks the message as read.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ acknowledge() {
+ return this.client.rest.methods.ackMessage(this);
+ }
+
+ /**
+ * Fetches the webhook used to create this message.
+ * @returns {Promise<?Webhook>}
+ */
+ fetchWebhook() {
+ if (!this.webhookID) return Promise.reject(new Error('The message was not sent by a webhook.'));
+ return this.client.fetchWebhook(this.webhookID);
+ }
+
+ /**
+ * Suppresses or unsuppresses embeds on a message
+ * @param {boolean} [suppress=true] If the embeds should be suppressed or not
+ * @returns {Promise<Message>}
+ */
+ suppressEmbeds(suppress = true) {
+ const flags = new MessageFlags(this.flags.bitfield);
+
+ if (suppress) {
+ flags.add(MessageFlags.FLAGS.SUPPRESS_EMBEDS);
+ } else {
+ flags.remove(MessageFlags.FLAGS.SUPPRESS_EMBEDS);
+ }
+
+ return this.edit(undefined, { flags });
+ }
+
+ /**
+ * Used mainly internally. Whether two messages are identical in properties. If you want to compare messages
+ * without checking all the properties, use `message.id === message2.id`, which is much more efficient. This
+ * method allows you to see if there are differences in content, embeds, attachments, nonce and tts properties.
+ * @param {Message} message The message to compare it to
+ * @param {Object} rawData Raw data passed through the WebSocket about this message
+ * @returns {boolean}
+ */
+ equals(message, rawData) {
+ if (!message) return false;
+ const embedUpdate = !message.author && !message.attachments;
+ if (embedUpdate) return this.id === message.id && this.embeds.length === message.embeds.length;
+
+ let equal = this.id === message.id &&
+ this.author.id === message.author.id &&
+ this.content === message.content &&
+ this.tts === message.tts &&
+ this.nonce === message.nonce &&
+ this.embeds.length === message.embeds.length &&
+ this.attachments.length === message.attachments.length;
+
+ if (equal && rawData) {
+ equal = this.mentions.everyone === message.mentions.everyone &&
+ this.createdTimestamp === new Date(rawData.timestamp).getTime() &&
+ this.editedTimestamp === new Date(rawData.edited_timestamp).getTime();
+ }
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the message's content instead of the object.
+ * @returns {string}
+ * @example
+ * // Logs: Message: This is a message!
+ * console.log(`Message: ${message}`);
+ */
+ toString() {
+ return this.content;
+ }
+
+ _addReaction(emoji, user) {
+ const emojiID = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name;
+ let reaction;
+ if (this.reactions.has(emojiID)) {
+ reaction = this.reactions.get(emojiID);
+ if (!reaction.me) reaction.me = user.id === this.client.user.id;
+ } else {
+ reaction = new MessageReaction(this, emoji, 0, user.id === this.client.user.id);
+ this.reactions.set(emojiID, reaction);
+ }
+ if (!reaction.users.has(user.id)) {
+ reaction.users.set(user.id, user);
+ reaction.count++;
+ }
+ return reaction;
+ }
+
+ _removeReaction(emoji, user) {
+ const emojiID = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name;
+ if (this.reactions.has(emojiID)) {
+ const reaction = this.reactions.get(emojiID);
+ if (!user) {
+ this.reactions.delete(emojiID);
+ return reaction;
+ }
+ if (reaction.users.has(user.id)) {
+ reaction.users.delete(user.id);
+ reaction.count--;
+ if (user.id === this.client.user.id) reaction.me = false;
+ if (reaction.count <= 0) this.reactions.delete(emojiID);
+ return reaction;
+ }
+ }
+ return null;
+ }
+
+ _clearReactions() {
+ this.reactions.clear();
+ }
+}
+
+module.exports = Message;
diff --git a/node_modules/discord.js/src/structures/MessageAttachment.js b/node_modules/discord.js/src/structures/MessageAttachment.js
new file mode 100644
index 0000000..6db15c8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageAttachment.js
@@ -0,0 +1,79 @@
+const { basename } = require('path');
+
+/**
+ * Represents an attachment in a message.
+ */
+class MessageAttachment {
+ constructor(message, data) {
+ /**
+ * The client that instantiated this MessageAttachment
+ * @name MessageAttachment#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: message.client });
+
+ /**
+ * The message this attachment is part of
+ * @type {Message}
+ */
+ this.message = message;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this attachment
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The file name of this attachment
+ * @type {string}
+ */
+ this.filename = data.filename;
+
+ /**
+ * The size of this attachment in bytes
+ * @type {number}
+ */
+ this.filesize = data.size;
+
+ /**
+ * The URL to this attachment
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The Proxy URL to this attachment
+ * @type {string}
+ */
+ this.proxyURL = data.proxy_url;
+
+ /**
+ * The height of this attachment (if an image)
+ * @type {?number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of this attachment (if an image)
+ * @type {?number}
+ */
+ this.width = data.width;
+ }
+
+ /**
+ * Whether or not this attachment has been marked as a spoiler
+ * @type {boolean}
+ * @readonly
+ */
+ get spoiler() {
+ return basename(this.url).startsWith('SPOILER_');
+ }
+}
+
+module.exports = MessageAttachment;
diff --git a/node_modules/discord.js/src/structures/MessageCollector.js b/node_modules/discord.js/src/structures/MessageCollector.js
new file mode 100644
index 0000000..acd82be
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageCollector.js
@@ -0,0 +1,97 @@
+const Collector = require('./interfaces/Collector');
+const util = require('util');
+
+/**
+ * @typedef {CollectorOptions} MessageCollectorOptions
+ * @property {number} max The maximum amount of messages to process
+ * @property {number} maxMatches The maximum amount of messages to collect
+ */
+
+/**
+ * Collects messages on a channel.
+ * @extends {Collector}
+ */
+class MessageCollector extends Collector {
+ /**
+ * @param {TextChannel|DMChannel|GroupDMChannel} channel The channel
+ * @param {CollectorFilter} filter The filter to be applied to this collector
+ * @param {MessageCollectorOptions} options The options to be applied to this collector
+ * @emits MessageCollector#message
+ */
+ constructor(channel, filter, options = {}) {
+ super(channel.client, filter, options);
+
+ /**
+ * The channel
+ * @type {TextBasedChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * Total number of messages that were received in the channel during message collection
+ * @type {number}
+ */
+ this.received = 0;
+
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() + 1);
+ this.client.on('message', this.listener);
+
+ this._reEmitter = message => {
+ /**
+ * Emitted when the collector receives a message.
+ * @event MessageCollector#message
+ * @param {Message} message The message
+ * @deprecated
+ */
+ this.emit('message', message);
+ };
+ this.on('collect', this._reEmitter);
+ }
+
+ // Remove in v12
+ on(eventName, listener) {
+ if (eventName === 'message') {
+ listener = util.deprecate(listener, 'MessageCollector will soon no longer emit "message", use "collect" instead');
+ }
+ super.on(eventName, listener);
+ }
+
+ /**
+ * Handle an incoming message for possible collection.
+ * @param {Message} message The message that could be collected
+ * @returns {?{key: Snowflake, value: Message}}
+ * @private
+ */
+ handle(message) {
+ if (message.channel.id !== this.channel.id) return null;
+ this.received++;
+ return {
+ key: message.id,
+ value: message,
+ };
+ }
+
+ /**
+ * Check after collection to see if the collector is done.
+ * @returns {?string} Reason to end the collector, if any
+ * @private
+ */
+ postCheck() {
+ // Consider changing the end reasons for v12
+ if (this.options.maxMatches && this.collected.size >= this.options.maxMatches) return 'matchesLimit';
+ if (this.options.max && this.received >= this.options.max) return 'limit';
+ return null;
+ }
+
+ /**
+ * Removes event listeners.
+ * @private
+ */
+ cleanup() {
+ this.removeListener('collect', this._reEmitter);
+ this.client.removeListener('message', this.listener);
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() - 1);
+ }
+}
+
+module.exports = MessageCollector;
diff --git a/node_modules/discord.js/src/structures/MessageEmbed.js b/node_modules/discord.js/src/structures/MessageEmbed.js
new file mode 100644
index 0000000..2a87679
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageEmbed.js
@@ -0,0 +1,386 @@
+/**
+ * Represents an embed in a message (image/video preview, rich embed, etc.)
+ * <info>This class is only used for *received* embeds. If you wish to send one, use the {@link RichEmbed} class.</info>
+ */
+class MessageEmbed {
+ constructor(message, data) {
+ /**
+ * The client that instantiated this embed
+ * @name MessageEmbed#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: message.client });
+
+ /**
+ * The message this embed is part of
+ * @type {Message}
+ */
+ this.message = message;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The type of this embed
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * The title of this embed
+ * @type {?string}
+ */
+ this.title = data.title;
+
+ /**
+ * The description of this embed
+ * @type {?string}
+ */
+ this.description = data.description;
+
+ /**
+ * The URL of this embed
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The color of the embed
+ * @type {number}
+ */
+ this.color = data.color;
+
+ /**
+ * The fields of this embed
+ * @type {MessageEmbedField[]}
+ */
+ this.fields = [];
+ if (data.fields) for (const field of data.fields) this.fields.push(new MessageEmbedField(this, field));
+
+ /**
+ * The timestamp of this embed
+ * @type {number}
+ */
+ this.timestamp = data.timestamp;
+
+ /**
+ * The thumbnail of this embed
+ * @type {?MessageEmbedThumbnail}
+ */
+ this.thumbnail = data.thumbnail ? new MessageEmbedThumbnail(this, data.thumbnail) : null;
+
+ /**
+ * The image of this embed
+ * @type {?MessageEmbedImage}
+ */
+ this.image = data.image ? new MessageEmbedImage(this, data.image) : null;
+
+ /**
+ * The video of this embed
+ * @type {?MessageEmbedVideo}
+ */
+ this.video = data.video ? new MessageEmbedVideo(this, data.video) : null;
+
+ /**
+ * The author of this embed
+ * @type {?MessageEmbedAuthor}
+ */
+ this.author = data.author ? new MessageEmbedAuthor(this, data.author) : null;
+
+ /**
+ * The provider of this embed
+ * @type {?MessageEmbedProvider}
+ */
+ this.provider = data.provider ? new MessageEmbedProvider(this, data.provider) : null;
+
+ /**
+ * The footer of this embed
+ * @type {?MessageEmbedFooter}
+ */
+ this.footer = data.footer ? new MessageEmbedFooter(this, data.footer) : null;
+ }
+
+ /**
+ * The date this embed was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The hexadecimal version of the embed color, with a leading hash
+ * @type {?string}
+ * @readonly
+ */
+ get hexColor() {
+ if (!this.color) return null;
+ let col = this.color.toString(16);
+ while (col.length < 6) col = `0${col}`;
+ return `#${col}`;
+ }
+}
+
+/**
+ * Represents a thumbnail for a message embed.
+ */
+class MessageEmbedThumbnail {
+ constructor(embed, data) {
+ /**
+ * The embed this thumbnail is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The URL for this thumbnail
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The Proxy URL for this thumbnail
+ * @type {string}
+ */
+ this.proxyURL = data.proxy_url;
+
+ /**
+ * The height of the thumbnail
+ * @type {number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of the thumbnail
+ * @type {number}
+ */
+ this.width = data.width;
+ }
+}
+
+/**
+ * Represents an image for a message embed.
+ */
+class MessageEmbedImage {
+ constructor(embed, data) {
+ /**
+ * The embed this image is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The URL for this image
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The Proxy URL for this image
+ * @type {string}
+ */
+ this.proxyURL = data.proxy_url;
+
+ /**
+ * The height of the image
+ * @type {number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of the image
+ * @type {number}
+ */
+ this.width = data.width;
+ }
+}
+
+/**
+ * Represents a video for a message embed.
+ */
+class MessageEmbedVideo {
+ constructor(embed, data) {
+ /**
+ * The embed this video is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The source URL for this video
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The height of the video
+ * @type {number}
+ */
+ this.height = data.height;
+
+ /**
+ * The width of the video
+ * @type {number}
+ */
+ this.width = data.width;
+ }
+}
+
+/**
+ * Represents a provider for a message embed.
+ */
+class MessageEmbedProvider {
+ constructor(embed, data) {
+ /**
+ * The embed this provider is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The name of this provider
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The URL of this provider
+ * @type {string}
+ */
+ this.url = data.url;
+ }
+}
+
+/**
+ * Represents an author for a message embed.
+ */
+class MessageEmbedAuthor {
+ constructor(embed, data) {
+ /**
+ * The embed this author is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The name of this author
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The URL of this author
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * The icon URL of this author
+ * @type {string}
+ */
+ this.iconURL = data.icon_url;
+ }
+}
+
+/**
+ * Represents a field for a message embed.
+ */
+class MessageEmbedField {
+ constructor(embed, data) {
+ /**
+ * The embed this footer is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The name of this field
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The value of this field
+ * @type {string}
+ */
+ this.value = data.value;
+
+ /**
+ * If this field is displayed inline
+ * @type {boolean}
+ */
+ this.inline = data.inline;
+ }
+}
+
+/**
+ * Represents the footer of a message embed.
+ */
+class MessageEmbedFooter {
+ constructor(embed, data) {
+ /**
+ * The embed this footer is part of
+ * @type {MessageEmbed}
+ */
+ this.embed = embed;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The text in this footer
+ * @type {string}
+ */
+ this.text = data.text;
+
+ /**
+ * The icon URL of this footer
+ * @type {string}
+ */
+ this.iconURL = data.icon_url;
+
+ /**
+ * The proxy icon URL of this footer
+ * @type {string}
+ */
+ this.proxyIconUrl = data.proxy_icon_url;
+ }
+}
+
+MessageEmbed.Thumbnail = MessageEmbedThumbnail;
+MessageEmbed.Image = MessageEmbedImage;
+MessageEmbed.Video = MessageEmbedVideo;
+MessageEmbed.Provider = MessageEmbedProvider;
+MessageEmbed.Author = MessageEmbedAuthor;
+MessageEmbed.Field = MessageEmbedField;
+MessageEmbed.Footer = MessageEmbedFooter;
+
+module.exports = MessageEmbed;
diff --git a/node_modules/discord.js/src/structures/MessageMentions.js b/node_modules/discord.js/src/structures/MessageMentions.js
new file mode 100644
index 0000000..47aff55
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageMentions.js
@@ -0,0 +1,185 @@
+const Collection = require('../util/Collection');
+const { ChannelTypes } = require('../util/Constants');
+
+/**
+ * Keeps track of mentions in a {@link Message}.
+ */
+class MessageMentions {
+ constructor(message, users, roles, everyone, crosspostedChannels) {
+ /**
+ * Whether `@everyone` or `@here` were mentioned
+ * @type {boolean}
+ */
+ this.everyone = Boolean(everyone);
+
+ if (users) {
+ if (users instanceof Collection) {
+ /**
+ * Any users that were mentioned
+ * <info>Order as received from the API, not as they appear in the message content</info>
+ * @type {Collection<Snowflake, User>}
+ */
+ this.users = new Collection(users);
+ } else {
+ this.users = new Collection();
+ for (const mention of users) {
+ let user = message.client.users.get(mention.id);
+ if (!user) user = message.client.dataManager.newUser(mention);
+ this.users.set(user.id, user);
+ if (mention.member && message.guild && !message.guild.members.has(mention.id)) {
+ message.guild._addMember(Object.assign(mention.member, { user }), false);
+ }
+ }
+ }
+ } else {
+ this.users = new Collection();
+ }
+
+ if (roles) {
+ if (roles instanceof Collection) {
+ /**
+ * Any roles that were mentioned
+ * <info>Order as received from the API, not as they appear in the message content</
+ * @type {Collection<Snowflake, Role>}
+ */
+ this.roles = new Collection(roles);
+ } else {
+ this.roles = new Collection();
+ for (const mention of roles) {
+ const role = message.channel.guild.roles.get(mention);
+ if (role) this.roles.set(role.id, role);
+ }
+ }
+ } else {
+ this.roles = new Collection();
+ }
+
+ /**
+ * Content of the message
+ * @type {Message}
+ * @private
+ */
+ this._content = message.content;
+
+ /**
+ * The client the message is from
+ * @type {Client}
+ * @private
+ */
+ this._client = message.client;
+
+ /**
+ * The guild the message is in
+ * @type {?Guild}
+ * @private
+ */
+ this._guild = message.channel.guild;
+
+ /**
+ * Cached members for {@MessageMention#members}
+ * @type {?Collection<Snowflake, GuildMember>}
+ * @private
+ */
+ this._members = null;
+
+ /**
+ * Cached channels for {@MessageMention#channels}
+ * @type {?Collection<Snowflake, GuildChannel>}
+ * @private
+ */
+ this._channels = null;
+
+ /**
+ * Crossposted channel data.
+ * @typedef {Object} CrosspostedChannel
+ * @property {Snowflake} channelID ID of the mentioned channel
+ * @property {Snowflake} guildID ID of the guild that has the channel
+ * @property {string} type Type of the channel
+ * @property {string} name Name of the channel
+ */
+
+ if (crosspostedChannels) {
+ if (crosspostedChannels instanceof Collection) {
+ /**
+ * A collection of crossposted channels
+ * @type {Collection<Snowflake, CrosspostedChannel>}
+ */
+ this.crosspostedChannels = new Collection(crosspostedChannels);
+ } else {
+ this.crosspostedChannels = new Collection();
+ const channelTypes = Object.keys(ChannelTypes);
+ for (const d of crosspostedChannels) {
+ const type = channelTypes[d.type];
+ this.crosspostedChannels.set(d.id, {
+ channelID: d.id,
+ guildID: d.guild_id,
+ type: type ? type.toLowerCase() : 'unknown',
+ name: d.name,
+ });
+ }
+ }
+ } else {
+ this.crosspostedChannels = new Collection();
+ }
+ }
+
+ /**
+ * Any members that were mentioned (only in {@link TextChannel}s)
+ * <info>Order as received from the API, not as they appear in the message content</
+ * @type {?Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ if (this._members) return this._members;
+ if (!this._guild) return null;
+ this._members = new Collection();
+ this.users.forEach(user => {
+ const member = this._guild.member(user);
+ if (member) this._members.set(member.user.id, member);
+ });
+ return this._members;
+ }
+
+ /**
+ * Any channels that were mentioned
+ * <info>Order as they appear first in the message content</info>
+ * @type {Collection<Snowflake, GuildChannel>}
+ * @readonly
+ */
+ get channels() {
+ if (this._channels) return this._channels;
+ this._channels = new Collection();
+ let matches;
+ while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) {
+ const chan = this._client.channels.get(matches[1]);
+ if (chan) this._channels.set(chan.id, chan);
+ }
+ return this._channels;
+ }
+}
+
+/**
+ * Regular expression that globally matches `@everyone` and `@here`
+ * @type {RegExp}
+ */
+MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g;
+
+/**
+ * Regular expression that globally matches user mentions like `<@81440962496172032>`
+ * @type {RegExp}
+ */
+MessageMentions.USERS_PATTERN = /<@!?[0-9]+>/g;
+
+/**
+ * Regular expression that globally matches role mentions like `<@&297577916114403338>`
+ * @type {RegExp}
+ */
+MessageMentions.ROLES_PATTERN = /<@&[0-9]+>/g;
+
+/**
+ * Regular expression that globally matches channel mentions like `<#222079895583457280>`
+ * @type {RegExp}
+ */
+MessageMentions.CHANNELS_PATTERN = /<#([0-9]+)>/g;
+
+module.exports = MessageMentions;
diff --git a/node_modules/discord.js/src/structures/MessageReaction.js b/node_modules/discord.js/src/structures/MessageReaction.js
new file mode 100644
index 0000000..9109e18
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageReaction.js
@@ -0,0 +1,107 @@
+const Collection = require('../util/Collection');
+const Emoji = require('./Emoji');
+const ReactionEmoji = require('./ReactionEmoji');
+
+/**
+ * Represents a reaction to a message.
+ */
+class MessageReaction {
+ constructor(message, emoji, count, me) {
+ /**
+ * The message that this reaction refers to
+ * @type {Message}
+ */
+ this.message = message;
+
+ /**
+ * Whether the client has given this reaction
+ * @type {boolean}
+ */
+ this.me = me;
+
+ /**
+ * The number of people that have given the same reaction
+ * @type {number}
+ */
+ this.count = count || 0;
+
+ /**
+ * The users that have given this reaction, mapped by their ID
+ * @type {Collection<Snowflake, User>}
+ */
+ this.users = new Collection();
+
+ this._emoji = new ReactionEmoji(this, emoji);
+ }
+
+ /**
+ * The emoji of this reaction, either an Emoji object for known custom emojis, or a ReactionEmoji
+ * object which has fewer properties. Whatever the prototype of the emoji, it will still have
+ * `name`, `id`, `identifier` and `toString()`
+ * @type {Emoji|ReactionEmoji}
+ * @readonly
+ */
+ get emoji() {
+ if (this._emoji instanceof Emoji) return this._emoji;
+ // Check to see if the emoji has become known to the client
+ if (this._emoji.id) {
+ const emojis = this.message.client.emojis;
+ if (emojis.has(this._emoji.id)) {
+ const emoji = emojis.get(this._emoji.id);
+ this._emoji = emoji;
+ return emoji;
+ }
+ }
+ return this._emoji;
+ }
+
+ /**
+ * Removes a user from this reaction.
+ * @param {UserResolvable} [user=this.message.client.user] The user to remove the reaction of
+ * @returns {Promise<MessageReaction>}
+ */
+ remove(user = this.message.client.user) {
+ const message = this.message;
+ const userID = this.message.client.resolver.resolveUserID(user);
+ if (!userID) return Promise.reject(new Error('Couldn\'t resolve the user ID to remove from the reaction.'));
+ return message.client.rest.methods.removeMessageReaction(
+ message, this.emoji.identifier, userID
+ );
+ }
+
+ /**
+ * Removes this reaction from the message
+ * @returns {Promise<MessageReaction>}
+ */
+ removeAll() {
+ const message = this.message;
+ return message.client.rest.methods.removeMessageReactionEmoji(
+ message, this.emoji.identifier
+ );
+ }
+
+ /**
+ * Fetch all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs.
+ * @param {number} [limit=100] The maximum amount of users to fetch, defaults to 100
+ * @param {Object} [options] Options to fetch users
+ * @param {Snowflake} [options.before] Limit fetching users to those with an id lower than the supplied id
+ * @param {Snowflake} [options.after] Limit fetching users to those with an id greater than the supplied id
+ * @returns {Promise<Collection<Snowflake, User>>}
+ */
+ fetchUsers(limit = 100, { after, before } = {}) {
+ const message = this.message;
+ return message.client.rest.methods.getMessageReactionUsers(
+ message, this.emoji.identifier, { after, before, limit }
+ ).then(data => {
+ const users = new Collection();
+ for (const rawUser of data) {
+ const user = this.message.client.dataManager.newUser(rawUser);
+ this.users.set(user.id, user);
+ users.set(user.id, user);
+ }
+ return users;
+ });
+ }
+}
+
+module.exports = MessageReaction;
diff --git a/node_modules/discord.js/src/structures/NewsChannel.js b/node_modules/discord.js/src/structures/NewsChannel.js
new file mode 100644
index 0000000..bcbfbf3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/NewsChannel.js
@@ -0,0 +1,24 @@
+const TextChannel = require('./TextChannel');
+
+/**
+ * Represents a guild news channel on Discord.
+ * @extends {TextChannel}
+ */
+class NewsChannel extends TextChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'news';
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The ratelimit per user for this channel (always 0)
+ * @type {number}
+ */
+ this.rateLimitPerUser = 0;
+ }
+}
+
+module.exports = NewsChannel;
diff --git a/node_modules/discord.js/src/structures/OAuth2Application.js b/node_modules/discord.js/src/structures/OAuth2Application.js
new file mode 100644
index 0000000..710aae8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/OAuth2Application.js
@@ -0,0 +1,157 @@
+const Snowflake = require('../util/Snowflake');
+const Team = require('./Team');
+const util = require('util');
+
+/**
+ * Represents an OAuth2 Application.
+ */
+class OAuth2Application {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the application
+ * @name OAuth2Application#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(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 app's icon URL
+ * @type {string}
+ */
+ this.iconURL = `https://cdn.discordapp.com/app-icons/${this.id}/${this.icon}.jpg`;
+
+ /**
+ * The app's RPC origins
+ * @type {?string[]}
+ */
+ this.rpcOrigins = data.rpc_origins;
+
+ /**
+ * The app's redirect URIs
+ * @type {string[]}
+ */
+ this.redirectURIs = data.redirect_uris;
+
+ /**
+ * If this app's bot requires a code grant when using the OAuth2 flow
+ * @type {boolean}
+ */
+ this.botRequireCodeGrant = data.bot_require_code_grant;
+
+ /**
+ * If this app's bot is public
+ * @type {boolean}
+ */
+ this.botPublic = data.bot_public;
+
+ /**
+ * If this app can use rpc
+ * @type {boolean}
+ */
+ this.rpcApplicationState = data.rpc_application_state;
+
+ /**
+ * Object containing basic info about this app's bot
+ * @type {Object}
+ */
+ this.bot = data.bot;
+
+ /**
+ * The flags for the app
+ * @type {number}
+ */
+ this.flags = data.flags;
+
+ /**
+ * OAuth2 secret for the application
+ * @type {boolean}
+ */
+ this.secret = data.secret;
+
+ if (data.owner) {
+ /**
+ * The owner of this OAuth application
+ * @type {?User}
+ */
+ this.owner = this.client.dataManager.newUser(data.owner);
+ }
+
+ /**
+ * The owning team of this OAuth application
+ * <info>In v12.0.0 this property moves to `Team#owner`.</info>
+ * @type {?Team}
+ * @deprecated
+ */
+ this.team = data.team ? new Team(this.client, data.team) : 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
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Reset the app's secret and bot token.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {OAuth2Application}
+ * @deprecated
+ */
+ reset() {
+ return this.client.rest.methods.resetApplication(this.id);
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the app name rather than the app object.
+ * @returns {string}
+ */
+ toString() {
+ return this.name;
+ }
+}
+
+OAuth2Application.prototype.reset =
+ util.deprecate(OAuth2Application.prototype.reset, 'OAuth2Application#reset: userbot methods will be removed');
+
+module.exports = OAuth2Application;
diff --git a/node_modules/discord.js/src/structures/PartialGuild.js b/node_modules/discord.js/src/structures/PartialGuild.js
new file mode 100644
index 0000000..3eb64f2
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PartialGuild.js
@@ -0,0 +1,51 @@
+/*
+{ splash: null,
+ id: '123123123',
+ icon: '123123123',
+ name: 'name' }
+*/
+
+/**
+ * Represents a guild that the client only has limited information for - e.g. from invites.
+ */
+class PartialGuild {
+ constructor(client, data) {
+ /**
+ * The client that instantiated this PartialGuild
+ * @name PartialGuild#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this guild
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of this guild
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The hash of this guild's icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The hash of the guild splash image (VIP only)
+ * @type {?string}
+ */
+ this.splash = data.splash;
+ }
+}
+
+module.exports = PartialGuild;
diff --git a/node_modules/discord.js/src/structures/PartialGuildChannel.js b/node_modules/discord.js/src/structures/PartialGuildChannel.js
new file mode 100644
index 0000000..c30c054
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PartialGuildChannel.js
@@ -0,0 +1,44 @@
+const Constants = require('../util/Constants');
+
+/*
+{ type: 0, id: '123123', name: 'heavy-testing' } }
+*/
+
+/**
+ * Represents a guild channel that the client only has limited information for - e.g. from invites.
+ */
+class PartialGuildChannel {
+ constructor(client, data) {
+ /**
+ * The client that instantiated this PartialGuildChannel
+ * @name PartialGuildChannel#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this guild channel
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of this guild channel
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The type of this guild channel - `text` or `voice`
+ * @type {string}
+ */
+ this.type = Constants.ChannelTypes.TEXT === data.type ? 'text' : 'voice';
+ }
+}
+
+module.exports = PartialGuildChannel;
diff --git a/node_modules/discord.js/src/structures/PermissionOverwrites.js b/node_modules/discord.js/src/structures/PermissionOverwrites.js
new file mode 100644
index 0000000..ebb78fb
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PermissionOverwrites.js
@@ -0,0 +1,69 @@
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a permission overwrite for a role or member in a guild channel.
+ */
+class PermissionOverwrites {
+ constructor(guildChannel, data) {
+ /**
+ * The GuildChannel this overwrite is for
+ * @name PermissionOverwrites#channel
+ * @type {GuildChannel}
+ * @readonly
+ */
+ Object.defineProperty(this, 'channel', { value: guildChannel });
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of this overwrite, either a user ID or a role ID
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of this overwrite
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * The permissions that are denied for the user or role as a bitfield.
+ * @type {number}
+ */
+ this.deny = data.deny;
+
+ /**
+ * The permissions that are allowed for the user or role as a bitfield.
+ * @type {number}
+ */
+ this.allow = data.allow;
+
+ /**
+ * The permissions that are denied for the user or role.
+ * @type {Permissions}
+ * @deprecated
+ */
+ this.denied = new Permissions(data.deny).freeze();
+
+ /**
+ * The permissions that are allowed for the user or role.
+ * @type {Permissions}
+ * @deprecated
+ */
+ this.allowed = new Permissions(data.allow).freeze();
+ }
+
+ /**
+ * Delete this Permission Overwrite.
+ * @param {string} [reason] Reason for deleting this overwrite
+ * @returns {Promise<PermissionOverwrites>}
+ */
+ delete(reason) {
+ return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason);
+ }
+}
+
+module.exports = PermissionOverwrites;
diff --git a/node_modules/discord.js/src/structures/Presence.js b/node_modules/discord.js/src/structures/Presence.js
new file mode 100644
index 0000000..18cc3cc
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Presence.js
@@ -0,0 +1,301 @@
+const { ActivityFlags, Endpoints } = require('../util/Constants');
+const ReactionEmoji = require('./ReactionEmoji');
+
+/**
+ * The status of this presence:
+ * * **`online`** - user is online
+ * * **`idle`** - user is AFK
+ * * **`offline`** - user is offline or invisible
+ * * **`dnd`** - user is in Do Not Disturb
+ * @typedef {string} PresenceStatus
+ */
+
+/**
+ * The status of this presence:
+ * * **`online`** - user is online
+ * * **`idle`** - user is AFK
+ * * **`dnd`** - user is in Do Not Disturb
+ * @typedef {string} ClientPresenceStatus
+ */
+
+/**
+ * Represents a user's presence.
+ */
+class Presence {
+ constructor(data = {}, client) {
+ /**
+ * The client that instantiated this
+ * @name Presence#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this.update(data);
+ }
+
+ update(data) {
+ /**
+ * The status of this presence:
+ * @type {PresenceStatus}
+ */
+ this.status = data.status || this.status || 'offline';
+
+ /**
+ * The game that the user is playing
+ * @type {?Game}
+ * @deprecated
+ */
+ this.game = data.game ? new Game(data.game, this) : null;
+
+ if (data.activities) {
+ /**
+ * The activities of this presence
+ * @type {Game[]}
+ */
+ this.activities = data.activities.map(activity => new Game(activity, this));
+ } else if (data.activity || data.game) {
+ this.activities = [new Game(data.activity || data.game, this)];
+ } else {
+ this.activities = [];
+ }
+
+ /**
+ * The devices this presence is on
+ * @type {?Object}
+ * @property {?ClientPresenceStatus} web The current presence in the web application
+ * @property {?ClientPresenceStatus} mobile The current presence in the mobile application
+ * @property {?ClientPresenceStatus} desktop The current presence in the desktop application
+ */
+ this.clientStatus = data.client_status || null;
+ }
+
+ /**
+ * Whether this presence is equal to another
+ * @param {Presence} presence The presence to compare with
+ * @returns {boolean}
+ */
+ equals(presence) {
+ return this === presence || (
+ presence &&
+ this.status === presence.status &&
+ this.activities.length === presence.activities.length &&
+ this.activities.every((activity, index) => activity.equals(presence.activities[index])) &&
+ this.clientStatus.web === presence.clientStatus.web &&
+ this.clientStatus.mobile === presence.clientStatus.mobile &&
+ this.clientStatus.desktop === presence.clientStatus.desktop
+ );
+ }
+}
+
+/**
+ * Represents a game that is part of a user's presence.
+ */
+class Game {
+ constructor(data, presence) {
+ Object.defineProperty(this, 'presence', { value: presence });
+
+ /**
+ * The name of the game being played
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The type of the game status, its possible values:
+ * - 0: Playing
+ * - 1: Streaming
+ * - 2: Listening
+ * - 3: Watching
+ * @type {number}
+ */
+ this.type = data.type;
+
+ /**
+ * If the game is being streamed, a link to the stream
+ * @type {?string}
+ */
+ this.url = data.url || null;
+
+ /**
+ * Details about the activity
+ * @type {?string}
+ */
+ this.details = data.details || null;
+
+ /**
+ * State of the activity
+ * @type {?string}
+ */
+ this.state = data.state || null;
+
+ /**
+ * Application ID associated with this activity
+ * @type {?Snowflake}
+ */
+ this.applicationID = data.application_id || null;
+
+ /**
+ * Timestamps for the activity
+ * @type {?Object}
+ * @prop {?Date} start When the activity started
+ * @prop {?Date} end When the activity will end
+ */
+ this.timestamps = data.timestamps ? {
+ start: data.timestamps.start ? new Date(Number(data.timestamps.start)) : null,
+ end: data.timestamps.end ? new Date(Number(data.timestamps.end)) : null,
+ } : null;
+
+ /**
+ * Party of the activity
+ * @type {?Object}
+ * @prop {?string} id ID of the party
+ * @prop {number[]} size Size of the party as `[current, max]`
+ */
+ this.party = data.party || null;
+
+ /**
+ * Assets for rich presence
+ * @type {?RichPresenceAssets}
+ */
+ this.assets = data.assets ? new RichPresenceAssets(this, data.assets) : null;
+
+ if (data.emoji) {
+ /**
+ * Emoji for a custom activity
+ * <warn>There is no `reaction` property for this emoji.</warn>
+ * @type {?ReactionEmoji}
+ */
+ this.emoji = new ReactionEmoji({ message: { client: this.presence.client } }, data.emoji);
+ this.emoji.reaction = null;
+ } else {
+ this.emoji = null;
+ }
+
+
+ /**
+ * Creation date of the activity
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.created_at).getTime();
+
+ this.syncID = data.sync_id;
+ this._flags = data.flags;
+ }
+
+ /**
+ * The time the activity was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * Flags that describe the activity
+ * @type {ActivityFlags[]}
+ */
+ get flags() {
+ const flags = [];
+ for (const [name, flag] of Object.entries(ActivityFlags)) {
+ if ((this._flags & flag) === flag) flags.push(name);
+ }
+ return flags;
+ }
+
+ /**
+ * Whether or not the game is being streamed
+ * @type {boolean}
+ * @readonly
+ */
+ get streaming() {
+ return this.type === 1;
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the game's name instead of the Game object.
+ * @returns {string}
+ */
+ toString() {
+ return this.name;
+ }
+
+ /**
+ * Whether this game is equal to another game
+ * @param {Game} game The game to compare with
+ * @returns {boolean}
+ */
+ equals(game) {
+ return this === game || (
+ game &&
+ this.name === game.name &&
+ this.type === game.type &&
+ this.url === game.url
+ );
+ }
+}
+
+/**
+ * Assets for a rich presence
+ */
+class RichPresenceAssets {
+ constructor(game, assets) {
+ Object.defineProperty(this, 'game', { value: game });
+
+ /**
+ * Hover text for the large image
+ * @type {?string}
+ */
+ this.largeText = assets.large_text || null;
+
+ /**
+ * Hover text for the small image
+ * @type {?string}
+ */
+ this.smallText = assets.small_text || null;
+
+ /**
+ * ID of the large image asset
+ * @type {?Snowflake}
+ */
+ this.largeImage = assets.large_image || null;
+
+ /**
+ * ID of the small image asset
+ * @type {?Snowflake}
+ */
+ this.smallImage = assets.small_image || null;
+ }
+
+ /**
+ * The URL of the small image asset
+ * @type {?string}
+ * @readonly
+ */
+ get smallImageURL() {
+ if (!this.smallImage) return null;
+ return Endpoints.CDN(this.game.presence.client.options.http.cdn)
+ .AppAsset(this.game.applicationID, this.smallImage);
+ }
+
+ /**
+ * The URL of the large image asset
+ * @type {?string}
+ * @readonly
+ */
+ get largeImageURL() {
+ if (!this.largeImage) return null;
+ if (/^spotify:/.test(this.largeImage)) {
+ return `https://i.scdn.co/image/${this.largeImage.slice(8)}`;
+ } else if (/^twitch:/.test(this.largeImage)) {
+ return `https://static-cdn.jtvnw.net/previews-ttv/live_user_${this.largeImage.slice(7)}.png`;
+ }
+ return Endpoints.CDN(this.game.presence.client.options.http.cdn)
+ .AppAsset(this.game.applicationID, this.largeImage);
+ }
+}
+
+exports.Presence = Presence;
+exports.Game = Game;
+exports.RichPresenceAssets = RichPresenceAssets;
diff --git a/node_modules/discord.js/src/structures/ReactionCollector.js b/node_modules/discord.js/src/structures/ReactionCollector.js
new file mode 100644
index 0000000..2b3235e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ReactionCollector.js
@@ -0,0 +1,89 @@
+const Collector = require('./interfaces/Collector');
+const Collection = require('../util/Collection');
+
+/**
+ * @typedef {CollectorOptions} ReactionCollectorOptions
+ * @property {number} max The maximum total amount of reactions to collect
+ * @property {number} maxEmojis The maximum number of emojis to collect
+ * @property {number} maxUsers The maximum number of users to react
+ */
+
+/**
+ * Collects reactions on messages.
+ * @extends {Collector}
+ */
+class ReactionCollector extends Collector {
+ /**
+ * @param {Message} message The message upon which to collect reactions
+ * @param {CollectorFilter} filter The filter to apply to this collector
+ * @param {ReactionCollectorOptions} [options={}] The options to apply to this collector
+ */
+ constructor(message, filter, options = {}) {
+ super(message.client, filter, options);
+
+ /**
+ * The message
+ * @type {Message}
+ */
+ this.message = message;
+
+ /**
+ * The users which have reacted
+ * @type {Collection}
+ */
+ this.users = new Collection();
+
+ /**
+ * The total number of reactions collected
+ * @type {number}
+ */
+ this.total = 0;
+
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() + 1);
+ this.client.on('messageReactionAdd', this.listener);
+
+ this.on('fullCollect', (reaction, user) => {
+ this.users.set(user.id, user);
+ this.total++;
+ });
+ }
+
+ /**
+ * Handle an incoming reaction for possible collection.
+ * @param {MessageReaction} reaction The reaction to possibly collect
+ * @returns {?{key: Snowflake, value: MessageReaction}}
+ * @private
+ */
+ handle(reaction) {
+ if (reaction.message.id !== this.message.id) return null;
+ return {
+ key: reaction.emoji.id || reaction.emoji.name,
+ value: reaction,
+ };
+ }
+
+ /**
+ * Check after collection to see if the collector is done.
+ * @param {MessageReaction} reaction The reaction that was collected
+ * @param {User} user The user that reacted
+ * @returns {?string} Reason to end the collector, if any
+ * @private
+ */
+ postCheck() {
+ if (this.options.max && this.total >= this.options.max) return 'limit';
+ if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit';
+ if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit';
+ return null;
+ }
+
+ /**
+ * Remove event listeners.
+ * @private
+ */
+ cleanup() {
+ this.client.removeListener('messageReactionAdd', this.listener);
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() - 1);
+ }
+}
+
+module.exports = ReactionCollector;
diff --git a/node_modules/discord.js/src/structures/ReactionEmoji.js b/node_modules/discord.js/src/structures/ReactionEmoji.js
new file mode 100644
index 0000000..9f7597e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ReactionEmoji.js
@@ -0,0 +1,98 @@
+const Constants = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a limited emoji set used for both custom and unicode emojis. Custom emojis
+ * will use this class opposed to the Emoji class when the client doesn't know enough
+ * information about them.
+ */
+class ReactionEmoji {
+ constructor(reaction, emoji) {
+ /**
+ * The client that instantiated this object
+ * @name ReactionEmoji#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: reaction.message.client });
+
+ /**
+ * The message reaction this emoji refers to
+ * @type {MessageReaction}
+ */
+ this.reaction = reaction;
+
+ /**
+ * The name of this reaction emoji
+ * @type {string}
+ */
+ this.name = emoji.name;
+
+ /**
+ * The ID of this reaction emoji
+ * @type {?Snowflake}
+ */
+ this.id = emoji.id;
+
+ /**
+ * Whether this reaction emoji is animated
+ * @type {boolean}
+ */
+ this.animated = emoji.animated || false;
+ }
+
+ /**
+ * The timestamp the reaction emoji was created at, or null if unicode
+ * @type {?number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ if (!this.id) return null;
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the reaction emoji was created, or null if unicode
+ * @type {?Date}
+ * @readonly
+ */
+ get createdAt() {
+ if (!this.id) return null;
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The URL to the reaction emoji file, or null if unicode
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ if (!this.id) return null;
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).Emoji(this.id, this.animated ? 'gif' : 'png');
+ }
+
+ /**
+ * The identifier of this emoji, used for message reactions
+ * @type {string}
+ * @readonly
+ */
+ get identifier() {
+ if (this.id) return `${this.name}:${this.id}`;
+ return encodeURIComponent(this.name);
+ }
+
+ /**
+ * Creates the text required to form a graphical emoji on Discord.
+ * @example
+ * // Send the emoji used in a reaction to the channel the reaction is part of
+ * reaction.message.channel.send(`The emoji used is ${reaction.emoji}`);
+ * @returns {string}
+ */
+ toString() {
+ if (!this.id) return this.name;
+
+ return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;
+ }
+}
+
+module.exports = ReactionEmoji;
diff --git a/node_modules/discord.js/src/structures/RichEmbed.js b/node_modules/discord.js/src/structures/RichEmbed.js
new file mode 100644
index 0000000..5ccb22d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/RichEmbed.js
@@ -0,0 +1,331 @@
+const Attachment = require('./Attachment');
+const MessageEmbed = require('./MessageEmbed');
+const util = require('../util/Util');
+let ClientDataResolver;
+
+/**
+ * A rich embed to be sent with a message with a fluent interface for creation.
+ * @param {Object} [data] Data to set in the rich embed
+ */
+class RichEmbed {
+ constructor(data = {}) {
+ /**
+ * Title for this Embed
+ * @type {string}
+ */
+ this.title = data.title;
+
+ /**
+ * Description for this Embed
+ * @type {string}
+ */
+ this.description = data.description;
+
+ /**
+ * URL for this Embed
+ * @type {string}
+ */
+ this.url = data.url;
+
+ /**
+ * Color for this Embed
+ * @type {number}
+ */
+ this.color = data.color;
+
+ /**
+ * Author for this Embed
+ * @type {Object}
+ */
+ this.author = data.author;
+
+ /**
+ * Timestamp for this Embed
+ * @type {Date}
+ */
+ this.timestamp = data.timestamp;
+
+ /**
+ * Fields for this Embed
+ * @type {Object[]}
+ */
+ this.fields = data.fields || [];
+
+ /**
+ * Thumbnail for this Embed
+ * @type {Object}
+ */
+ this.thumbnail = data.thumbnail;
+
+ /**
+ * Image for this Embed
+ * @type {Object}
+ */
+ this.image = data.image;
+
+ /**
+ * Footer for this Embed
+ * @type {Object}
+ */
+ this.footer = data.footer;
+
+ /**
+ * File to upload alongside this Embed
+ * @type {FileOptions|string|Attachment}
+ */
+ this.file = data.file;
+
+ /**
+ * The files to upload alongside this Embed
+ * @type {Array<FileOptions|string|Attachment>}
+ */
+ this.files = [];
+ }
+
+ /**
+ * Sets the title of this embed.
+ * @param {StringResolvable} title The title
+ * @returns {RichEmbed} This embed
+ */
+ setTitle(title) {
+ title = util.resolveString(title);
+ if (title.length > 256) throw new RangeError('RichEmbed titles may not exceed 256 characters.');
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * Sets the description of this embed.
+ * @param {StringResolvable} description The description
+ * @returns {RichEmbed} This embed
+ */
+ setDescription(description) {
+ description = util.resolveString(description);
+ if (description.length > 2048) throw new RangeError('RichEmbed descriptions may not exceed 2048 characters.');
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Sets the URL of this embed.
+ * @param {string} url The URL
+ * @returns {RichEmbed} This embed
+ */
+ setURL(url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Sets the color of this embed.
+ * @param {ColorResolvable} color The color of the embed
+ * @returns {RichEmbed} This embed
+ */
+ setColor(color) {
+ if (!ClientDataResolver) ClientDataResolver = require('../client/ClientDataResolver');
+ this.color = ClientDataResolver.resolveColor(color);
+ return this;
+ }
+
+ /**
+ * Sets the author of this embed.
+ * @param {StringResolvable} name The name of the author
+ * @param {string} [icon] The icon URL of the author
+ * @param {string} [url] The URL of the author
+ * @returns {RichEmbed} This embed
+ */
+ setAuthor(name, icon, url) {
+ this.author = { name: util.resolveString(name), icon_url: icon, url };
+ return this;
+ }
+
+ /**
+ * Sets the timestamp of this embed.
+ * @param {Date|number} [timestamp=Date.now()] The timestamp or date
+ * @returns {RichEmbed} This embed
+ */
+ setTimestamp(timestamp = Date.now()) {
+ if (timestamp instanceof Date) timestamp = timestamp.getTime();
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * Adds a field to the embed (max 25).
+ * @param {StringResolvable} name The name of the field
+ * @param {StringResolvable} value The value of the field
+ * @param {boolean} [inline=false] Set the field to display inline
+ * @returns {RichEmbed} This embed
+ */
+ addField(name, value, inline = false) {
+ if (this.fields.length >= 25) throw new RangeError('RichEmbeds may not exceed 25 fields.');
+ this.fields.push(this.constructor.normalizeField(name, value, inline));
+ return this;
+ }
+
+ /**
+ * Convenience function for `<RichEmbed>.addField('\u200B', '\u200B', inline)`.
+ * @param {boolean} [inline=false] Set the field to display inline
+ * @returns {RichEmbed} This embed
+ */
+ addBlankField(inline = false) {
+ return this.addField('\u200B', '\u200B', inline);
+ }
+
+ /**
+ * @typedef {Object} EmbedField
+ * @property {string} name The name of this field
+ * @property {string} value The value of this field
+ * @property {boolean} inline If this field will be displayed inline
+ */
+
+ /**
+ * @typedef {Object} EmbedFieldData
+ * @property {StringResolvable} name The name of this field
+ * @property {StringResolvable} value The value of this field
+ * @property {boolean} [inline=false] If this field will be displayed inline
+ */
+
+ /**
+ * Removes, replaces, and inserts fields in the embed (max 25).
+ * @param {number} index The index to start at
+ * @param {number} deleteCount The number of fields to remove
+ * @param {...EmbedFieldData} [fields] The replacing field objects
+ * @returns {RichEmbed}
+ */
+ spliceFields(index, deleteCount, ...fields) {
+ if (fields) {
+ const mapper = ({ name, value, inline }) => this.constructor.normalizeField(name, value, inline);
+ this.fields.splice(index, deleteCount, ...fields.map(mapper));
+ } else {
+ this.fields.splice(index, deleteCount);
+ }
+ return this;
+ }
+
+ /**
+ * Set the thumbnail of this embed.
+ * @param {string} url The URL of the thumbnail
+ * @returns {RichEmbed} This embed
+ */
+ setThumbnail(url) {
+ this.thumbnail = { url };
+ return this;
+ }
+
+ /**
+ * Set the image of this embed.
+ * @param {string} url The URL of the image
+ * @returns {RichEmbed} This embed
+ */
+ setImage(url) {
+ this.image = { url };
+ return this;
+ }
+
+ /**
+ * Sets the footer of this embed.
+ * @param {StringResolvable} text The text of the footer
+ * @param {string} [icon] The icon URL of the footer
+ * @returns {RichEmbed} This embed
+ */
+ setFooter(text, icon) {
+ text = util.resolveString(text);
+ if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.');
+ this.footer = { text, icon_url: icon };
+ return this;
+ }
+
+ /**
+ * Sets the file to upload alongside the embed. This file can be accessed via `attachment://fileName.extension` when
+ * setting an embed image or author/footer icons. Only one file may be attached.
+ * @param {FileOptions|string|Attachment} file Local path or URL to the file to attach,
+ * or valid FileOptions for a file to attach
+ * @returns {RichEmbed} This embed
+ */
+ attachFile(file) {
+ if (this.file) throw new RangeError('You may not upload more than one file at once.');
+ if (file instanceof Attachment) file = file.file;
+ this.file = file;
+ return this;
+ }
+
+ /**
+ * Sets the files to upload alongside the embed. A file can be accessed via `attachment://fileName.extension` when
+ * setting an embed image or author/footer icons. Multiple files can be attached.
+ * @param {Array<FileOptions|string|Attachment>} files Files to attach
+ * @returns {RichEmbed}
+ */
+ attachFiles(files) {
+ files = files.map(file => file instanceof Attachment ? file.file : file);
+ this.files = this.files.concat(files);
+ return this;
+ }
+
+ /**
+ * The accumulated length for the embed title, description, fields, author and footer text
+ * @type {number}
+ * @readonly
+ */
+ get length() {
+ return (
+ (this.title ? this.title.length : 0) +
+ (this.description ? this.description.length : 0) +
+ (this.fields.length >= 1 ? this.fields.reduce((prev, curr) =>
+ prev + curr.name.length + curr.value.length, 0) : 0) +
+ (this.footer ? this.footer.text.length : 0) +
+ (this.author ? this.author.name.length : 0));
+ }
+
+ /**
+ * Transforms the embed to a plain object.
+ * @returns {Object} The raw data of this embed
+ */
+ toJSON() {
+ return {
+ title: this.title,
+ type: 'rich',
+ description: this.description,
+ url: this.url,
+ timestamp: this.timestamp ? new Date(this.timestamp) : null,
+ color: this.color,
+ fields: this.fields ?
+ this.fields.map(field => ({ name: field.name, value: field.value, inline: field.inline })) :
+ null,
+ thumbnail: this.thumbnail ? {
+ url: this.thumbnail.url,
+ } : null,
+ image: this.image ? {
+ url: this.image.url,
+ } : null,
+ author: this.author ? {
+ name: this.author.name,
+ url: this.author.url,
+ icon_url: this.author instanceof MessageEmbed.Author ? this.author.iconURL : this.author.icon_url,
+ } : null,
+ footer: this.footer ? {
+ text: this.footer.text,
+ icon_url: this.footer instanceof MessageEmbed.Footer ? this.footer.iconURL : this.footer.icon_url,
+ } : null,
+ };
+ }
+
+ /**
+ * Normalizes field input and resolves strings.
+ * @param {StringResolvable} name The name of the field
+ * @param {StringResolvable} value The value of the field
+ * @param {boolean} [inline=false] Set the field to display inline
+ * @returns {EmbedField}
+ */
+ static normalizeField(name, value, inline = false) {
+ name = util.resolveString(name);
+ if (name.length > 256) throw new RangeError('RichEmbed field names may not exceed 256 characters.');
+ if (!/\S/.test(name)) throw new RangeError('RichEmbed field names may not be empty.');
+ value = util.resolveString(value);
+ if (value.length > 1024) throw new RangeError('RichEmbed field values may not exceed 1024 characters.');
+ if (!/\S/.test(value)) throw new RangeError('RichEmbed field values may not be empty.');
+ return { name, value, inline };
+ }
+}
+
+module.exports = RichEmbed;
diff --git a/node_modules/discord.js/src/structures/Role.js b/node_modules/discord.js/src/structures/Role.js
new file mode 100644
index 0000000..056cf98
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Role.js
@@ -0,0 +1,376 @@
+const Snowflake = require('../util/Snowflake');
+const Permissions = require('../util/Permissions');
+const util = require('util');
+
+/**
+ * Represents a role on Discord.
+ */
+class Role {
+ constructor(guild, data) {
+ /**
+ * The client that instantiated the role
+ * @name Role#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: guild.client });
+
+ /**
+ * The guild that the role belongs to
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * Whether the role has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the role (unique to the guild it is part of)
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the role
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The base 10 color of the role
+ * @type {number}
+ */
+ this.color = data.color;
+
+ /**
+ * If true, users that are part of this role will appear in a separate category in the users list
+ * @type {boolean}
+ */
+ this.hoist = data.hoist;
+
+ /**
+ * The position of the role from the API
+ * @type {number}
+ */
+ this.position = data.position;
+
+ /**
+ * The permissions bitfield of the role
+ * @type {number}
+ */
+ this.permissions = data.permissions;
+
+ /**
+ * Whether or not the role is managed by an external service
+ * @type {boolean}
+ */
+ this.managed = data.managed;
+
+ /**
+ * Whether or not the role can be mentioned by anyone
+ * @type {boolean}
+ */
+ this.mentionable = data.mentionable;
+ }
+
+ /**
+ * The timestamp the role was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the role was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The hexadecimal version of the role color, with a leading hashtag
+ * @type {string}
+ * @readonly
+ */
+ get hexColor() {
+ let col = this.color.toString(16);
+ while (col.length < 6) col = `0${col}`;
+ return `#${col}`;
+ }
+
+ /**
+ * The cached guild members that have this role
+ * @type {Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ return this.guild.members.filter(m => m.roles.has(this.id));
+ }
+
+ /**
+ * Whether the role is editable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get editable() {
+ if (this.managed) return false;
+ const clientMember = this.guild.member(this.client.user);
+ if (!clientMember.permissions.has(Permissions.FLAGS.MANAGE_ROLES_OR_PERMISSIONS)) return false;
+ return clientMember.highestRole.comparePositionTo(this) > 0;
+ }
+
+ /**
+ * The position of the role in the role manager
+ * @type {number}
+ * @readonly
+ */
+ get calculatedPosition() {
+ const sorted = this.guild._sortedRoles;
+ return sorted.array().indexOf(sorted.get(this.id));
+ }
+
+ /**
+ * Get an object mapping permission names to whether or not the role enables that permission.
+ * @returns {Object<string, boolean>}
+ * @example
+ * // Print the serialized role permissions
+ * console.log(role.serialize());
+ */
+ serialize() {
+ return new Permissions(this.permissions).serialize();
+ }
+
+ /**
+ * Checks if the role has a permission.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
+ * **(deprecated)**
+ * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override
+ * (takes priority over `explicit`)
+ * @returns {boolean}
+ * @example
+ * // See if a role can ban a member
+ * if (role.hasPermission('BAN_MEMBERS')) {
+ * console.log('This role can ban members');
+ * } else {
+ * console.log('This role can\'t ban members');
+ * }
+ */
+ hasPermission(permission, explicit = false, checkAdmin) {
+ return new Permissions(this.permissions).has(
+ permission, typeof checkAdmin !== 'undefined' ? checkAdmin : !explicit
+ );
+ }
+
+ /**
+ * Checks if the role has all specified permissions.
+ * @param {PermissionResolvable} permissions The permissions to check for
+ * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permissions
+ * @returns {boolean}
+ * @deprecated
+ */
+ hasPermissions(permissions, explicit = false) {
+ return new Permissions(this.permissions).has(permissions, !explicit);
+ }
+
+ /**
+ * Compares this role's position to another role's.
+ * @param {Role} role Role to compare to this one
+ * @returns {number} Negative number if this role's position is lower (other role's is higher),
+ * positive number if this one is higher (other's is lower), 0 if equal
+ */
+ comparePositionTo(role) {
+ return this.constructor.comparePositions(this, role);
+ }
+
+ /**
+ * The data for a role.
+ * @typedef {Object} RoleData
+ * @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
+ */
+
+ /**
+ * Edits the role.
+ * @param {RoleData} data The new data for the role
+ * @param {string} [reason] The reason for editing this role
+ * @returns {Promise<Role>}
+ * @example
+ * // Edit name of a role
+ * role.edit({ name: 'New Name' })
+ * .then(updated => console.log(`Edited role name from ${role.name} to ${updated.name}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ return this.client.rest.methods.updateGuildRole(this, data, reason);
+ }
+
+ /**
+ * Set a new name for the role.
+ * @param {string} name The new name of the role
+ * @param {string} [reason] Reason for changing the role's name
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the name of the role
+ * role.setName('New Name')
+ * .then(updated => console.log(`Edited role name from ${role.name} to ${updated.name}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Set a new color for the role.
+ * @param {ColorResolvable} color The color of the role
+ * @param {string} [reason] Reason for changing the role's color
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the color of a role
+ * role.setColor('#FF0000')
+ * .then(updated => console.log(`Set color of role to ${role.color}`))
+ * .catch(console.error);
+ */
+ setColor(color, reason) {
+ return this.edit({ color }, reason);
+ }
+
+ /**
+ * Set whether or not the role should be hoisted.
+ * @param {boolean} hoist Whether or not to hoist the role
+ * @param {string} [reason] Reason for setting whether or not the role should be hoisted
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the hoist of the role
+ * role.setHoist(true)
+ * .then(updated => console.log(`Role hoisted: ${updated.hoist}`))
+ * .catch(console.error);
+ */
+ setHoist(hoist, reason) {
+ return this.edit({ hoist }, reason);
+ }
+
+ /**
+ * Set the position of the role.
+ * @param {number} position The position of the role
+ * @param {boolean} [relative=false] Move the position relative to its current value
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the position of the role
+ * role.setPosition(1)
+ * .then(updated => console.log(`Role position: ${updated.position}`))
+ * .catch(console.error);
+ */
+ setPosition(position, relative) {
+ return this.guild.setRolePosition(this, position, relative).then(() => this);
+ }
+
+ /**
+ * Set the permissions of the role.
+ * @param {PermissionResolvable} permissions The permissions of the role
+ * @param {string} [reason] Reason for changing the role's permissions
+ * @returns {Promise<Role>}
+ * @example
+ * // Set the permissions of the role
+ * role.setPermissions(['KICK_MEMBERS', 'BAN_MEMBERS'])
+ * .then(updated => console.log(`Updated permissions to ${updated.permissions.bitfield}`))
+ * .catch(console.error);
+ * @example
+ * // Remove all permissions from a role
+ * role.setPermissions(0)
+ * .then(updated => console.log(`Updated permissions to ${updated.permissions.bitfield}`))
+ * .catch(console.error);
+ */
+ setPermissions(permissions, reason) {
+ return this.edit({ permissions }, reason);
+ }
+
+ /**
+ * Set whether this role is mentionable.
+ * @param {boolean} mentionable Whether this role should be mentionable
+ * @param {string} [reason] Reason for setting whether or not this role should be mentionable
+ * @returns {Promise<Role>}
+ * @example
+ * // Make the role mentionable
+ * role.setMentionable(true, 'Role needs to be pinged')
+ * .then(updated => console.log(`Role mentionable: ${updated.mentionable}`))
+ * .catch(console.error);
+ */
+ setMentionable(mentionable, reason) {
+ return this.edit({ mentionable }, reason);
+ }
+
+ /**
+ * Deletes the role.
+ * @param {string} [reason] Reason for deleting the role
+ * @returns {Promise<Role>}
+ * @example
+ * // Delete a role
+ * role.delete('The role needed to go')
+ * .then(deleted => console.log(`Deleted role ${deleted.name}`))
+ * .catch(console.error);
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteGuildRole(this, reason);
+ }
+
+ /**
+ * Whether this role equals another role. It compares all properties, so for most operations
+ * it is advisable to just compare `role.id === role2.id` as it is much faster and is often
+ * what most users need.
+ * @param {Role} role Role to compare with
+ * @returns {boolean}
+ */
+ equals(role) {
+ return role &&
+ this.id === role.id &&
+ this.name === role.name &&
+ this.color === role.color &&
+ this.hoist === role.hoist &&
+ this.position === role.position &&
+ this.permissions === role.permissions &&
+ this.managed === role.managed;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the role mention rather than the Role object.
+ * @returns {string}
+ */
+ toString() {
+ if (this.id === this.guild.id) return '@everyone';
+ return `<@&${this.id}>`;
+ }
+
+ /**
+ * Compares the positions of two roles.
+ * @param {Role} role1 First role to compare
+ * @param {Role} role2 Second role to compare
+ * @returns {number} Negative number if the first role's position is lower (second role's is higher),
+ * positive number if the first's is higher (second's is lower), 0 if equal
+ */
+ static comparePositions(role1, role2) {
+ if (role1.position === role2.position) return role2.id - role1.id;
+ return role1.position - role2.position;
+ }
+}
+
+Role.prototype.hasPermissions = util
+ .deprecate(Role.prototype.hasPermissions,
+ 'Role#hasPermissions is deprecated - use Role#hasPermission instead, it now takes an array');
+
+module.exports = Role;
diff --git a/node_modules/discord.js/src/structures/StoreChannel.js b/node_modules/discord.js/src/structures/StoreChannel.js
new file mode 100644
index 0000000..8985c0f
--- /dev/null
+++ b/node_modules/discord.js/src/structures/StoreChannel.js
@@ -0,0 +1,25 @@
+const GuildChannel = require('./GuildChannel');
+
+/**
+ * Represents a guild store channel on Discord.
+ * @extends {GuildChannel}
+ */
+class StoreChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'store';
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * If the guild considers this channel NSFW
+ * @type {boolean}
+ * @readonly
+ */
+ this.nsfw = data.nsfw;
+ }
+}
+
+module.exports = StoreChannel;
diff --git a/node_modules/discord.js/src/structures/Team.js b/node_modules/discord.js/src/structures/Team.js
new file mode 100644
index 0000000..e0dc0e0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Team.js
@@ -0,0 +1,109 @@
+const Snowflake = require('../util/Snowflake');
+const Collection = require('../util/Collection');
+const TeamMember = require('./TeamMember');
+const Constants = require('../util/Constants');
+
+/**
+ * Represents a Client OAuth2 Application Team.
+ */
+class Team {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the team
+ * @name Team#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The ID of the Team
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of the Team
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The Team's icon hash
+ * @type {?string}
+ */
+ this.icon = data.icon || null;
+
+ /**
+ * The Team's owner id
+ * @type {?string}
+ */
+ this.ownerID = data.owner_user_id || null;
+
+ /**
+ * The Team's members
+ * @type {Collection<Snowflake, TeamMember>}
+ */
+ this.members = new Collection();
+
+ for (const memberData of data.members) {
+ const member = new TeamMember(this.client, this, memberData);
+ this.members.set(member.id, member);
+ }
+ }
+
+ /**
+ * The owner of the team
+ * @type {?TeamMember}
+ * @readonly
+ */
+ get owner() {
+ return this.members.get(this.ownerID) || null;
+ }
+
+ /**
+ * The timestamp the team was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the team was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * A link to the teams's icon.
+ * @type {?string}
+ * @readonly
+ */
+ get iconURL() {
+ if (!this.icon) return null;
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).TeamIcon(this.id, this.icon);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the Team's name instead of the
+ * Team object.
+ * @returns {string}
+ * @example
+ * // Logs: Team name: My Team
+ * console.log(`Team name: ${team}`);
+ */
+ toString() {
+ return this.name;
+ }
+}
+
+module.exports = Team;
diff --git a/node_modules/discord.js/src/structures/TeamMember.js b/node_modules/discord.js/src/structures/TeamMember.js
new file mode 100644
index 0000000..5dbc2ce
--- /dev/null
+++ b/node_modules/discord.js/src/structures/TeamMember.js
@@ -0,0 +1,67 @@
+const { MembershipStates } = require('../util/Constants');
+
+/**
+ * Represents a Client OAuth2 Application Team Member.
+ */
+class TeamMember {
+ constructor(client, team, data) {
+ /**
+ * The client that instantiated the Team Member
+ * @name TeamMember#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The Team this member is part of
+ * @type {Team}
+ */
+ this.team = team;
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The permissions this Team Member has with regard to the team
+ * @type {string[]}
+ */
+ this.permissions = data.permissions;
+
+ /**
+ * The membership state this Team Member has with regard to the team
+ * @type {MembershipStates}
+ */
+ this.membershipState = MembershipStates[data.membership_state];
+
+ /**
+ * The user for this Team Member
+ * @type {User}
+ */
+ this.user = this.client.dataManager.newUser(data.user);
+ }
+
+ /**
+ * The ID of the Team Member
+ * @type {Snowflake}
+ * @readonly
+ */
+ get id() {
+ return this.user.id;
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the team members's mention instead of the
+ * TeamMember object.
+ * @returns {string}
+ * @example
+ * // Logs: Team Member's mention: <@123456789>
+ * console.log(`Team Member's mention: ${teamMember}`);
+ */
+ toString() {
+ return this.user.toString();
+ }
+}
+
+module.exports = TeamMember;
diff --git a/node_modules/discord.js/src/structures/TextChannel.js b/node_modules/discord.js/src/structures/TextChannel.js
new file mode 100644
index 0000000..d16dac8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/TextChannel.js
@@ -0,0 +1,154 @@
+const GuildChannel = require('./GuildChannel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Collection = require('../util/Collection');
+
+/**
+ * Represents a guild text channel on Discord.
+ * @extends {GuildChannel}
+ * @implements {TextBasedChannel}
+ */
+class TextChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+ this.type = 'text';
+ /**
+ * A collection containing the messages sent to this channel
+ * @type {Collection<Snowflake, Message>}
+ */
+ this.messages = new Collection();
+ this._typing = new Map();
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The topic of the text channel
+ * @type {?string}
+ */
+ this.topic = data.topic;
+
+ /**
+ * If the Discord considers this channel NSFW
+ * @type {boolean}
+ * @readonly
+ */
+ this.nsfw = Boolean(data.nsfw);
+
+ /**
+ * The ID of the last message sent in this channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = data.last_message_id;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+
+ /**
+ * The ratelimit per user for this channel in seconds
+ * @type {number}
+ */
+ this.rateLimitPerUser = data.rate_limit_per_user || 0;
+ }
+
+ /**
+ * A collection of members that can see this channel, mapped by their ID
+ * @type {Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ const members = new Collection();
+ for (const member of this.guild.members.values()) {
+ if (this.permissionsFor(member).has('READ_MESSAGES')) {
+ members.set(member.id, member);
+ }
+ }
+ return members;
+ }
+
+ /**
+ * Fetch all webhooks for the channel.
+ * @returns {Promise<Collection<Snowflake, Webhook>>}
+ * @example
+ * // Fetch webhooks
+ * channel.fetchWebhooks()
+ * .then(hooks => console.log(`This channel has ${hooks.size} hooks`))
+ * .catch(console.error);
+ */
+ fetchWebhooks() {
+ return this.client.rest.methods.getChannelWebhooks(this);
+ }
+
+ /**
+ * Sets whether this channel is flagged as NSFW.
+ * @param {boolean} nsfw Whether the channel should be considered NSFW
+ * @param {string} [reason] Reason for changing the channel's NSFW flag
+ * @returns {Promise<TextChannel>}
+ */
+ setNSFW(nsfw, reason) {
+ return this.edit({ nsfw }, reason);
+ }
+
+ /**
+ * Create a webhook for the channel.
+ * @param {string} name The name of the webhook
+ * @param {BufferResolvable|Base64Resolvable} [avatar] The avatar for the webhook
+ * @param {string} [reason] Reason for creating this webhook
+ * @returns {Promise<Webhook>} webhook The created webhook
+ * @example
+ * channel.createWebhook('Snek', 'https://i.imgur.com/mI8XcpG.jpg')
+ * .then(webhook => console.log(`Created webhook ${webhook}`))
+ * .catch(console.error)
+ */
+ createWebhook(name, avatar, reason) {
+ if (typeof avatar === 'string' && avatar.startsWith('data:')) {
+ return this.client.rest.methods.createWebhook(this, name, avatar, reason);
+ } else {
+ return this.client.resolver.resolveImage(avatar).then(data =>
+ this.client.rest.methods.createWebhook(this, name, data, reason)
+ );
+ }
+ }
+
+ /**
+ * Sets the rate limit per user for this channel.
+ * @param {number} rateLimitPerUser The new ratelimit in seconds
+ * @param {string} [reason] Reason for changing the channel's ratelimits
+ * @returns {Promise<TextChannel>}
+ */
+ setRateLimitPerUser(rateLimitPerUser, reason) {
+ return this.edit({ rateLimitPerUser }, reason);
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastMessage() {}
+ get lastPinAt() {}
+ send() { }
+ sendMessage() { }
+ sendEmbed() { }
+ sendFile() { }
+ sendFiles() { }
+ sendCode() { }
+ fetchMessage() { }
+ fetchMessages() { }
+ fetchPinnedMessages() { }
+ search() { }
+ startTyping() { }
+ stopTyping() { }
+ get typing() { }
+ get typingCount() { }
+ createCollector() { }
+ createMessageCollector() { }
+ awaitMessages() { }
+ bulkDelete() { }
+ acknowledge() { }
+ _cacheMessage() { }
+}
+
+TextBasedChannel.applyToClass(TextChannel, true);
+
+module.exports = TextChannel;
diff --git a/node_modules/discord.js/src/structures/User.js b/node_modules/discord.js/src/structures/User.js
new file mode 100644
index 0000000..86c2bc0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/User.js
@@ -0,0 +1,336 @@
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const Constants = require('../util/Constants');
+const Presence = require('./Presence').Presence;
+const Snowflake = require('../util/Snowflake');
+const util = require('util');
+
+/**
+ * Represents a user on Discord.
+ * @implements {TextBasedChannel}
+ */
+class User {
+ constructor(client, data) {
+ /**
+ * The client that created the instance of the user
+ * @name User#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ if (data) this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The ID of the user
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The username of the user
+ * @type {string}
+ */
+ this.username = data.username;
+
+ /**
+ * A discriminator based on username for the user
+ * @type {string}
+ */
+ this.discriminator = data.discriminator;
+
+ /**
+ * The ID of the user's avatar
+ * @type {string}
+ */
+ this.avatar = data.avatar;
+
+ /**
+ * Whether or not the user is a bot
+ * @type {boolean}
+ */
+ this.bot = Boolean(data.bot);
+
+ /**
+ * Whether this is an Official Discord System user (part of the urgent message system)
+ * @type {?boolean}
+ * @name User#system
+ */
+ if (typeof data.system !== 'undefined') this.system = Boolean(data.system);
+
+ /**
+ * The ID of the last message sent by the user, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The Message object of the last message sent by the user, if one was sent
+ * @type {?Message}
+ */
+ this.lastMessage = null;
+ }
+
+ patch(data) {
+ for (const prop of ['id', 'username', 'discriminator', 'avatar', 'bot']) {
+ if (typeof data[prop] !== 'undefined') this[prop] = data[prop];
+ }
+ if (data.token) this.client.token = data.token;
+ }
+
+ /**
+ * The timestamp the user was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the user was created
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The presence of this user
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ if (this.client.presences.has(this.id)) return this.client.presences.get(this.id);
+ for (const guild of this.client.guilds.values()) {
+ if (guild.presences.has(this.id)) return guild.presences.get(this.id);
+ }
+ return new Presence(undefined, this.client);
+ }
+
+ /**
+ * A link to the user's avatar
+ * @type {?string}
+ * @readonly
+ */
+ get avatarURL() {
+ if (!this.avatar) return null;
+ return Constants.Endpoints.User(this).Avatar(this.client.options.http.cdn, this.avatar);
+ }
+
+ /**
+ * A link to the user's default avatar
+ * @type {string}
+ * @readonly
+ */
+ get defaultAvatarURL() {
+ const avatars = Object.keys(Constants.DefaultAvatars);
+ const avatar = avatars[this.discriminator % avatars.length];
+ return Constants.Endpoints.CDN(this.client.options.http.host).Asset(`${Constants.DefaultAvatars[avatar]}.png`);
+ }
+
+ /**
+ * A link to the user's avatar if they have one. Otherwise a link to their default avatar will be returned
+ * @type {string}
+ * @readonly
+ */
+ get displayAvatarURL() {
+ return this.avatarURL || this.defaultAvatarURL;
+ }
+
+ /**
+ * The Discord "tag" (e.g. `hydrabolt#0001`) for this user
+ * @type {string}
+ * @readonly
+ */
+ get tag() {
+ return `${this.username}#${this.discriminator}`;
+ }
+
+ /**
+ * The note that is set for the user
+ * <warn>This is only available when using a user account.</warn>
+ * @type {?string}
+ * @readonly
+ * @deprecated
+ */
+ get note() {
+ return this.client.user.notes.get(this.id) || null;
+ }
+
+ /**
+ * Check whether the user is typing in a channel.
+ * @param {ChannelResolvable} channel The channel to check in
+ * @returns {boolean}
+ */
+ typingIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ return channel._typing.has(this.id);
+ }
+
+ /**
+ * Get the time that the user started typing.
+ * @param {ChannelResolvable} channel The channel to get the time in
+ * @returns {?Date}
+ */
+ typingSinceIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null;
+ }
+
+ /**
+ * Get the amount of time the user has been typing in a channel for (in milliseconds), or -1 if they're not typing.
+ * @param {ChannelResolvable} channel The channel to get the time in
+ * @returns {number}
+ */
+ typingDurationIn(channel) {
+ channel = this.client.resolver.resolveChannel(channel);
+ return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1;
+ }
+
+ /**
+ * The DM between the client's user and this user
+ * @type {?DMChannel}
+ * @readonly
+ */
+ get dmChannel() {
+ return this.client.channels.find(c => c.type === 'dm' && c.recipient.id === this.id);
+ }
+
+ /**
+ * Creates a DM channel between the client and the user.
+ * @returns {Promise<DMChannel>}
+ */
+ createDM() {
+ return this.client.rest.methods.createDM(this);
+ }
+
+ /**
+ * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
+ * @returns {Promise<DMChannel>}
+ */
+ deleteDM() {
+ return this.client.rest.methods.deleteChannel(this);
+ }
+
+ /**
+ * Sends a friend request to the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ addFriend() {
+ return this.client.rest.methods.addFriend(this);
+ }
+
+ /**
+ * Removes the user from your friends.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ removeFriend() {
+ return this.client.rest.methods.removeFriend(this);
+ }
+
+ /**
+ * Blocks the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ block() {
+ return this.client.rest.methods.blockUser(this);
+ }
+
+ /**
+ * Unblocks the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ unblock() {
+ return this.client.rest.methods.unblockUser(this);
+ }
+
+ /**
+ * Get the profile of the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<UserProfile>}
+ * @deprecated
+ */
+ fetchProfile() {
+ return this.client.rest.methods.fetchUserProfile(this);
+ }
+
+ /**
+ * Sets a note for the user.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {string} note The note to set for the user
+ * @returns {Promise<User>}
+ * @deprecated
+ */
+ setNote(note) {
+ return this.client.rest.methods.setNote(this, note);
+ }
+
+ /**
+ * Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags.
+ * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
+ * @param {User} user User to compare with
+ * @returns {boolean}
+ */
+ equals(user) {
+ let equal = user &&
+ this.id === user.id &&
+ this.username === user.username &&
+ this.discriminator === user.discriminator &&
+ this.avatar === user.avatar &&
+ this.bot === Boolean(user.bot);
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically concatenates the user's mention instead of the User object.
+ * @returns {string}
+ * @example
+ * // logs: Hello from <@123456789>!
+ * console.log(`Hello from ${user}!`);
+ */
+ toString() {
+ return `<@${this.id}>`;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ send() {}
+ sendMessage() {}
+ sendEmbed() {}
+ sendFile() {}
+ sendCode() {}
+}
+
+TextBasedChannel.applyToClass(User);
+
+User.prototype.block =
+ util.deprecate(User.prototype.block, 'User#block: userbot methods will be removed');
+
+User.prototype.unblock =
+ util.deprecate(User.prototype.unblock, 'User#unblock: userbot methods will be removed');
+
+User.prototype.addFriend =
+ util.deprecate(User.prototype.addFriend, 'User#addFriend: userbot methods will be removed');
+
+User.prototype.removeFriend =
+ util.deprecate(User.prototype.removeFriend, 'User#removeFriend: userbot methods will be removed');
+
+User.prototype.setNote =
+ util.deprecate(User.prototype.setNote, 'User#setNote, userbot methods will be removed');
+
+User.prototype.fetchProfile =
+ util.deprecate(User.prototype.fetchProfile, 'User#fetchProfile: userbot methods will be removed');
+
+module.exports = User;
diff --git a/node_modules/discord.js/src/structures/UserConnection.js b/node_modules/discord.js/src/structures/UserConnection.js
new file mode 100644
index 0000000..ea6c65f
--- /dev/null
+++ b/node_modules/discord.js/src/structures/UserConnection.js
@@ -0,0 +1,48 @@
+/**
+ * Represents a user connection (or "platform identity").
+ */
+class UserConnection {
+ constructor(user, data) {
+ /**
+ * The user that owns the connection
+ * @type {User}
+ */
+ this.user = user;
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * The type of the connection
+ * @type {string}
+ */
+ this.type = data.type;
+
+ /**
+ * The username of the connection account
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The id of the connection account
+ * @type {string}
+ */
+ this.id = data.id;
+
+ /**
+ * Whether the connection is revoked
+ * @type {boolean}
+ */
+ this.revoked = data.revoked;
+
+ /**
+ * Partial server integrations (not yet implemented)
+ * @type {Object[]}
+ */
+ this.integrations = data.integrations;
+ }
+}
+
+module.exports = UserConnection;
diff --git a/node_modules/discord.js/src/structures/UserProfile.js b/node_modules/discord.js/src/structures/UserProfile.js
new file mode 100644
index 0000000..a27d4d0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/UserProfile.js
@@ -0,0 +1,62 @@
+const Collection = require('../util/Collection');
+const UserConnection = require('./UserConnection');
+
+/**
+ * Represents a user's profile on Discord.
+ */
+class UserProfile {
+ constructor(user, data) {
+ /**
+ * The owner of the profile
+ * @type {User}
+ */
+ this.user = user;
+
+ /**
+ * The client that created the instance of the UserProfile
+ * @name UserProfile#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: user.client });
+
+ /**
+ * The guilds that the client user and the user share
+ * @type {Collection<Snowflake, Guild>}
+ */
+ this.mutualGuilds = new Collection();
+
+ /**
+ * The user's connections
+ * @type {Collection<Snowflake, UserConnection>}
+ */
+ this.connections = new Collection();
+
+ this.setup(data);
+ }
+
+ setup(data) {
+ /**
+ * If the user has Discord Premium
+ * @type {boolean}
+ */
+ this.premium = data.premium;
+
+ /**
+ * The date since which the user has had Discord Premium
+ * @type {?Date}
+ */
+ this.premiumSince = data.premium_since ? new Date(data.premium_since) : null;
+
+ for (const guild of data.mutual_guilds) {
+ if (this.client.guilds.has(guild.id)) {
+ this.mutualGuilds.set(guild.id, this.client.guilds.get(guild.id));
+ }
+ }
+ for (const connection of data.connected_accounts) {
+ this.connections.set(connection.id, new UserConnection(this.user, connection));
+ }
+ }
+}
+
+module.exports = UserProfile;
diff --git a/node_modules/discord.js/src/structures/VoiceChannel.js b/node_modules/discord.js/src/structures/VoiceChannel.js
new file mode 100644
index 0000000..a89dafa
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceChannel.js
@@ -0,0 +1,146 @@
+const GuildChannel = require('./GuildChannel');
+const Collection = require('../util/Collection');
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a guild voice channel on Discord.
+ * @extends {GuildChannel}
+ */
+class VoiceChannel extends GuildChannel {
+ constructor(guild, data) {
+ super(guild, data);
+
+ /**
+ * The members in this voice channel
+ * @type {Collection<Snowflake, GuildMember>}
+ */
+ this.members = new Collection();
+
+ this.type = 'voice';
+ }
+
+ setup(data) {
+ super.setup(data);
+
+ /**
+ * The bitrate of this voice channel
+ * @type {number}
+ */
+ this.bitrate = data.bitrate * 0.001;
+
+ /**
+ * The maximum amount of users allowed in this channel - 0 means unlimited.
+ * @type {number}
+ */
+ this.userLimit = data.user_limit;
+ }
+
+ /**
+ * The voice connection for this voice channel, if the client is connected
+ * @type {?VoiceConnection}
+ * @readonly
+ */
+ get connection() {
+ const connection = this.guild.voiceConnection;
+ if (connection && connection.channel.id === this.id) return connection;
+ return null;
+ }
+
+ /**
+ * Checks if the voice channel is full
+ * @type {boolean}
+ * @readonly
+ */
+ get full() {
+ return this.userLimit > 0 && this.members.size >= this.userLimit;
+ }
+
+ /**
+ * Whether the channel is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ return super.deletable && this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT);
+ }
+
+ /**
+ * Checks if the client has permission join the voice channel
+ * @type {boolean}
+ * @readonly
+ */
+ get joinable() {
+ if (this.client.browser) return false;
+ if (!this.permissionsFor(this.client.user).has('CONNECT')) return false;
+ if (this.full && !this.permissionsFor(this.client.user).has('MOVE_MEMBERS')) return false;
+ return true;
+ }
+
+ /**
+ * Checks if the client has permission to send audio to the voice channel
+ * @type {boolean}
+ * @readonly
+ */
+ get speakable() {
+ return this.permissionsFor(this.client.user).has('SPEAK');
+ }
+
+ /**
+ * Sets the bitrate of the channel (in kbps).
+ * @param {number} bitrate The new bitrate
+ * @param {string} [reason] Reason for changing the channel's bitrate
+ * @returns {Promise<VoiceChannel>}
+ * @example
+ * // Set the bitrate of a voice channel
+ * voiceChannel.setBitrate(48)
+ * .then(vc => console.log(`Set bitrate to ${vc.bitrate}kbps for ${vc.name}`))
+ * .catch(console.error);
+ */
+ setBitrate(bitrate, reason) {
+ bitrate *= 1000;
+ return this.edit({ bitrate }, reason);
+ }
+
+ /**
+ * Sets the user limit of the channel.
+ * @param {number} userLimit The new user limit
+ * @param {string} [reason] Reason for changing the user limit
+ * @returns {Promise<VoiceChannel>}
+ * @example
+ * // Set the user limit of a voice channel
+ * voiceChannel.setUserLimit(42)
+ * .then(vc => console.log(`Set user limit to ${vc.userLimit} for ${vc.name}`))
+ * .catch(console.error);
+ */
+ setUserLimit(userLimit, reason) {
+ return this.edit({ userLimit }, reason);
+ }
+
+ /**
+ * Attempts to join this voice channel.
+ * @returns {Promise<VoiceConnection>}
+ * @example
+ * // Join a voice channel
+ * voiceChannel.join()
+ * .then(connection => console.log('Connected!'))
+ * .catch(console.error);
+ */
+ join() {
+ if (this.client.browser) return Promise.reject(new Error('Voice connections are not available in browsers.'));
+ return this.client.voice.joinChannel(this);
+ }
+
+ /**
+ * Leaves this voice channel.
+ * @example
+ * // Leave a voice channel
+ * voiceChannel.leave();
+ */
+ leave() {
+ if (this.client.browser) return;
+ const connection = this.client.voice.connections.get(this.guild.id);
+ if (connection && connection.channel.id === this.id) connection.disconnect();
+ }
+}
+
+module.exports = VoiceChannel;
diff --git a/node_modules/discord.js/src/structures/VoiceRegion.js b/node_modules/discord.js/src/structures/VoiceRegion.js
new file mode 100644
index 0000000..dc6b461
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceRegion.js
@@ -0,0 +1,50 @@
+/**
+ * Represents a Discord voice region for guilds.
+ */
+class VoiceRegion {
+ constructor(data) {
+ /**
+ * The ID of the region
+ * @type {string}
+ */
+ this.id = data.id;
+
+ /**
+ * Name of the region
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * Whether the region is VIP-only
+ * @type {boolean}
+ */
+ this.vip = data.vip;
+
+ /**
+ * Whether the region is deprecated
+ * @type {boolean}
+ */
+ this.deprecated = data.deprecated;
+
+ /**
+ * Whether the region is optimal
+ * @type {boolean}
+ */
+ this.optimal = data.optimal;
+
+ /**
+ * Whether the region is custom
+ * @type {boolean}
+ */
+ this.custom = data.custom;
+
+ /**
+ * A sample hostname for what a connection might look like
+ * @type {string}
+ */
+ this.sampleHostname = data.sample_hostname;
+ }
+}
+
+module.exports = VoiceRegion;
diff --git a/node_modules/discord.js/src/structures/Webhook.js b/node_modules/discord.js/src/structures/Webhook.js
new file mode 100644
index 0000000..5d82b0c
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Webhook.js
@@ -0,0 +1,377 @@
+const EventEmitter = require('events');
+const path = require('path');
+const Util = require('../util/Util');
+const Attachment = require('./Attachment');
+const RichEmbed = require('./RichEmbed');
+const Constants = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a webhook.
+ */
+class Webhook extends EventEmitter {
+ constructor(client, dataOrID, token) {
+ super();
+ if (client) {
+ /**
+ * The client that instantiated the webhook
+ * @name Webhook#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ if (dataOrID) this.setup(dataOrID);
+ } else {
+ this.id = dataOrID;
+ this.token = token;
+ Object.defineProperty(this, 'client', { value: this });
+ }
+ }
+
+ setup(data) {
+ /**
+ * The name of the webhook
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The token for the webhook
+ * @name Webhook#token
+ * @type {?string}
+ */
+ Object.defineProperty(this, 'token', { value: data.token || null, writable: true, configurable: true });
+
+ /**
+ * The avatar for the webhook
+ * @type {?string}
+ */
+ this.avatar = data.avatar;
+
+ /**
+ * The ID of the webhook
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of the webhook
+ * @type {WebhookTypes}
+ */
+ this.type = Constants.WebhookTypes[data.type];
+
+ /**
+ * The guild the webhook belongs to
+ * @type {Snowflake}
+ */
+ this.guildID = data.guild_id;
+
+ /**
+ * The channel the webhook belongs to
+ * @type {Snowflake}
+ */
+ this.channelID = data.channel_id;
+
+ if (data.user) {
+ /**
+ * The owner of the webhook
+ * @type {?User|Object}
+ */
+ this.owner = this.client.users ? this.client.users.get(data.user.id) : data.user;
+ } else {
+ this.owner = null;
+ }
+ }
+
+ /**
+ * The timestamp the webhook was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the webhook was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * A link to the webhook user's avatar
+ * @type {?stirng}
+ * @readonly
+ */
+ get avatarURL() {
+ if (!this.avatar) return null;
+ return Constants.Endpoints.CDN(this.client.options.http.cdn).Avatar(this.id, this.avatar);
+ }
+
+ /**
+ * The url of this webhook
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ const API = `${this.client.options.http.host}/api/v${this.client.options.http.version}`;
+ return API + Constants.Endpoints.Webhook(this.id, this.token);
+ }
+
+ /**
+ * Options that can be passed into send, sendMessage, sendFile, sendEmbed, and sendCode.
+ * @typedef {Object} WebhookMessageOptions
+ * @property {string} [username=this.name] Username override for the message
+ * @property {string} [avatarURL] Avatar URL override for the message
+ * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
+ * @property {string} [nonce=''] The nonce for the message
+ * @property {Array<RichEmbed|Object>} [embeds] An array of embeds for the message
+ * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
+ * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here
+ * should be replaced with plain-text
+ * @property {FileOptions|BufferResolvable|Attachment} [file] A file to send with the message **(deprecated)**
+ * @property {FileOptions[]|BufferResolvable[]|Attachment[]} [files] Files to send with the message
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
+ * it exceeds the character limit. If an object is provided, these are the options for splitting the message.
+ */
+
+ /**
+ * Send a message with this webhook.
+ * @param {StringResolvable} content The content to send
+ * @param {WebhookMessageOptions|Attachment|RichEmbed} [options] The options to provide,
+ * can also be just a RichEmbed or Attachment
+ * @returns {Promise<Message|Message[]|Object|Object[]>}
+ * @example
+ * // Send a basic message
+ * webhook.send('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ * @example
+ * // Send a remote file
+ * webhook.send({
+ * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send a local file
+ * webhook.send({
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send an embed with a local image inside
+ * webhook.send('This is an embed', {
+ * embeds: [{
+ * thumbnail: {
+ * url: 'attachment://file.jpg'
+ * }
+ * }],
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ send(content, options) { // eslint-disable-line complexity
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+
+ if (options instanceof Attachment) options = { files: [options] };
+ if (options instanceof RichEmbed) options = { embeds: [options] };
+
+ if (content) {
+ content = this.client.resolver.resolveString(content);
+ let { split, code, disableEveryone } = options;
+ if (split && typeof split !== 'object') split = {};
+ if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
+ content = Util.escapeMarkdown(content, true);
+ content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
+ if (split) {
+ split.prepend = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n`;
+ split.append = '\n```';
+ }
+ }
+ if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) {
+ content = content.replace(/@(everyone|here)/g, '@\u200b$1');
+ }
+
+ if (split) content = Util.splitMessage(content, split);
+ }
+
+ if (options.file) {
+ if (options.files) options.files.push(options.file);
+ else options.files = [options.file];
+ }
+
+ if (options.embeds) {
+ const files = [];
+ for (const embed of options.embeds) {
+ if (embed.file) files.push(embed.file);
+ }
+ if (options.files) options.files.push(...files);
+ else options.files = files;
+ }
+
+ if (options.embeds) options.embeds = options.embeds.map(e => new RichEmbed(e).toJSON());
+
+ if (options.files) {
+ for (let i = 0; i < options.files.length; i++) {
+ let file = options.files[i];
+ if (typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file };
+ if (!file.name) {
+ if (typeof file.attachment === 'string') {
+ file.name = path.basename(file.attachment);
+ } else if (file.attachment && file.attachment.path) {
+ file.name = path.basename(file.attachment.path);
+ } else if (file instanceof Attachment) {
+ file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' };
+ } else {
+ file.name = 'file.jpg';
+ }
+ } else if (file instanceof Attachment) {
+ file = file.file;
+ }
+ options.files[i] = file;
+ }
+
+ return Promise.all(options.files.map(file =>
+ this.client.resolver.resolveFile(file.attachment).then(resource => {
+ file.file = resource;
+ return file;
+ })
+ )).then(files => this.client.rest.methods.sendWebhookMessage(this, content, options, files));
+ }
+
+ return this.client.rest.methods.sendWebhookMessage(this, content, options);
+ }
+
+ /**
+ * Send a message with this webhook
+ * @param {StringResolvable} content The content to send
+ * @param {WebhookMessageOptions} [options={}] The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ * @example
+ * // Send a message
+ * webhook.sendMessage('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ */
+ sendMessage(content, options = {}) {
+ return this.send(content, options);
+ }
+
+ /**
+ * Send a file with this webhook.
+ * @param {BufferResolvable} attachment The file to send
+ * @param {string} [name='file.jpg'] The name and extension of the file
+ * @param {StringResolvable} [content] Text message to send with the attachment
+ * @param {WebhookMessageOptions} [options] The options to provide
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendFile(attachment, name, content, options = {}) {
+ return this.send(content, Object.assign(options, { file: { attachment, name } }));
+ }
+
+ /**
+ * Send a code block with this webhook.
+ * @param {string} lang Language for the code block
+ * @param {StringResolvable} content Content of the code block
+ * @param {WebhookMessageOptions} options The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ */
+ sendCode(lang, content, options = {}) {
+ return this.send(content, Object.assign(options, { code: lang }));
+ }
+
+ /**
+ * Send a raw slack message with this webhook.
+ * @param {Object} body The raw body to send
+ * @returns {Promise}
+ * @example
+ * // Send a slack message
+ * webhook.sendSlackMessage({
+ * 'username': 'Wumpus',
+ * 'attachments': [{
+ * 'pretext': 'this looks pretty cool',
+ * 'color': '#F0F',
+ * 'footer_icon': 'http://snek.s3.amazonaws.com/topSnek.png',
+ * 'footer': 'Powered by sneks',
+ * 'ts': Date.now() / 1000
+ * }]
+ * }).catch(console.error);
+ */
+ sendSlackMessage(body) {
+ return this.client.rest.methods.sendSlackWebhookMessage(this, body);
+ }
+
+ /**
+ * Options provided to edit a webhook.
+ * @property {string} [name] The new name for the webhook
+ * @property {BufferResolvable} [avatar] The new avatar for the webhook
+ * @property {ChannelResolvable} [channel] The new channel for the webhook
+ * @typedef {Object} WebhookEditOptions
+ */
+
+ /**
+ * Edit the webhook.
+ * @param {string|WebhookEditOptions} nameOrOptions The new name for the webhook **(deprecated, use options)**
+ * Alternatively options for the webhook, overriding the avatar parameter.
+ * @param {BufferResolvable|string} [avatarOrReason] The new avatar for the webhook **(deprecated, use options)**
+ * Alternatively a reason to edit, if using options as first parameter.
+ * @returns {Promise<Webhook>}
+ */
+ edit(nameOrOptions = this.name, avatarOrReason) {
+ if (typeof nameOrOptions !== 'object') {
+ process.emitWarning('Webhook#edit: Use options object instead of separate parameters.');
+ nameOrOptions = {
+ name: nameOrOptions,
+ avatar: avatarOrReason,
+ };
+ // Parameter was an avatar here; Clear the now reason parameter
+ avatarOrReason = undefined;
+ }
+
+ if (nameOrOptions.channel) {
+ nameOrOptions.channel_id = this.client.resolver.resolveChannelID(nameOrOptions.channel);
+ nameOrOptions.channel = undefined;
+ }
+
+ if (nameOrOptions.avatar) {
+ return this.client.resolver.resolveImage(nameOrOptions.avatar).then(data => {
+ nameOrOptions.avatar = data;
+ return this.client.rest.methods.editWebhook(this, nameOrOptions, avatarOrReason);
+ });
+ }
+
+ return this.client.rest.methods.editWebhook(this, nameOrOptions, avatarOrReason);
+ }
+
+ /**
+ * Delete the webhook.
+ * @param {string} [reason] Reason for deleting the webhook
+ * @returns {Promise}
+ */
+ delete(reason) {
+ return this.client.rest.methods.deleteWebhook(this, reason);
+ }
+}
+
+module.exports = Webhook;
diff --git a/node_modules/discord.js/src/structures/interfaces/Collector.js b/node_modules/discord.js/src/structures/interfaces/Collector.js
new file mode 100644
index 0000000..6767b42
--- /dev/null
+++ b/node_modules/discord.js/src/structures/interfaces/Collector.js
@@ -0,0 +1,208 @@
+const Collection = require('../../util/Collection');
+const EventEmitter = require('events').EventEmitter;
+
+/**
+ * Filter to be applied to the collector.
+ * @typedef {Function} CollectorFilter
+ * @param {...*} args Any arguments received by the listener
+ * @param {Collection} collection The items collected by this collector
+ * @returns {boolean}
+ */
+
+/**
+ * Options to be applied to the collector.
+ * @typedef {Object} CollectorOptions
+ * @property {number} [time] How long to run the collector for
+ * @property {number} [idle] How long to stop the collector after inactivity in milliseconds
+ */
+
+/**
+ * Abstract class for defining a new Collector.
+ * @abstract
+ */
+class Collector extends EventEmitter {
+ constructor(client, filter, options = {}) {
+ super();
+
+ /**
+ * The client
+ * @name Collector#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+
+ /**
+ * The filter applied to this collector
+ * @type {CollectorFilter}
+ */
+ this.filter = filter;
+
+ /**
+ * The options of this collector
+ * @type {CollectorOptions}
+ */
+ this.options = options;
+
+ /**
+ * The items collected by this collector
+ * @type {Collection}
+ */
+ this.collected = new Collection();
+
+ /**
+ * Whether this collector has finished collecting
+ * @type {boolean}
+ */
+ this.ended = false;
+
+ /**
+ * Timeout for cleanup
+ * @type {?Timeout}
+ * @private
+ */
+ this._timeout = null;
+
+ /**
+ * Timeout for cleanup due to inactivity
+ * @type {?Timeout}
+ * @private
+ */
+ this._idletimeout = null;
+
+ /**
+ * Call this to handle an event as a collectable element
+ * Accepts any event data as parameters
+ * @type {Function}
+ * @private
+ */
+ this.listener = this._handle.bind(this);
+ if (options.time) this._timeout = this.client.setTimeout(() => this.stop('time'), options.time);
+ if (options.idle) this._idletimeout = this.client.setTimeout(() => this.stop('idle'), options.idle);
+ }
+
+ /**
+ * @param {...*} args The arguments emitted by the listener
+ * @emits Collector#collect
+ * @private
+ */
+ _handle(...args) {
+ const collect = this.handle(...args);
+ if (collect && this.filter(...args, this.collected)) {
+ this.collected.set(collect.key, collect.value);
+
+ /**
+ * Emitted whenever an element is collected.
+ * @event Collector#collect
+ * @param {*} element The element that got collected
+ * @param {Collector} collector The collector
+ */
+ this.emit('collect', collect.value, this);
+
+ /**
+ * Emitted whenever an element is collected.
+ * @event Collector#fullCollect
+ * @param {...*} args The arguments emitted by the listener
+ * @private
+ */
+ this.emit('fullCollect', ...args, this);
+
+ if (this._idletimeout) {
+ this.client.clearTimeout(this._idletimeout);
+ this._idletimeout = this.client.setTimeout(() => this.stop('idle'), this.options.idle);
+ }
+ }
+
+ const post = this.postCheck(...args);
+ if (post) this.stop(post);
+ }
+
+ /**
+ * Return a promise that resolves with the next collected element;
+ * rejects with collected elements if the collector finishes without receiving a next element
+ * @type {Promise}
+ * @readonly
+ */
+ get next() {
+ return new Promise((resolve, reject) => {
+ if (this.ended) {
+ reject(this.collected);
+ return;
+ }
+
+ const cleanup = () => {
+ this.removeListener('collect', onCollect);
+ this.removeListener('end', onEnd);
+ };
+
+ const onCollect = item => {
+ cleanup();
+ resolve(item);
+ };
+
+ const onEnd = () => {
+ cleanup();
+ reject(this.collected); // eslint-disable-line prefer-promise-reject-errors
+ };
+
+ this.on('collect', onCollect);
+ this.on('end', onEnd);
+ });
+ }
+
+ /**
+ * Stop this collector and emit the `end` event.
+ * @param {string} [reason='user'] The reason this collector is ending
+ * @emits Collector#end
+ */
+ stop(reason = 'user') {
+ if (this.ended) return;
+
+ if (this._timeout) {
+ this.client.clearTimeout(this._timeout);
+ this._timeout = null;
+ }
+ if (this._idletimeout) {
+ this.client.clearTimeout(this._idletimeout);
+ this._idletimeout = null;
+ }
+ this.ended = true;
+ this.cleanup();
+
+ /**
+ * Emitted when the collector is finished collecting.
+ * @event Collector#end
+ * @param {Collection} collected The elements collected by the collector
+ * @param {string} reason The reason the collector ended
+ */
+ this.emit('end', this.collected, reason);
+ }
+
+ /* eslint-disable no-empty-function, valid-jsdoc */
+ /**
+ * Handles incoming events from the `listener` function. Returns null if the event should not be collected,
+ * or returns an object describing the data that should be stored.
+ * @see Collector#listener
+ * @param {...*} args Any args the event listener emits
+ * @returns {?{key: string, value}} Data to insert into collection, if any
+ * @abstract
+ */
+ handle() {}
+
+ /**
+ * This method runs after collection to see if the collector should finish.
+ * @param {...*} args Any args the event listener emits
+ * @returns {?string} Reason to end the collector, if any
+ * @abstract
+ */
+ postCheck() {}
+
+ /**
+ * Called when the collector is ending.
+ * @abstract
+ */
+ cleanup() {}
+ /* eslint-enable no-empty-function, valid-jsdoc */
+}
+
+module.exports = Collector;
diff --git a/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js b/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js
new file mode 100644
index 0000000..ceaa73b
--- /dev/null
+++ b/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js
@@ -0,0 +1,635 @@
+const path = require('path');
+const Message = require('../Message');
+const MessageCollector = require('../MessageCollector');
+const Collection = require('../../util/Collection');
+const Attachment = require('../../structures/Attachment');
+const RichEmbed = require('../../structures/RichEmbed');
+const Snowflake = require('../../util/Snowflake');
+const util = require('util');
+
+/**
+ * Interface for classes that have text-channel-like features.
+ * @interface
+ */
+class TextBasedChannel {
+ constructor() {
+ /**
+ * A collection containing the messages sent to this channel
+ * @type {Collection<Snowflake, Message>}
+ */
+ this.messages = new Collection();
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The Message object of the last message in the channel, if one was sent
+ * @type {?Message}
+ */
+ this.lastMessage = null;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = null;
+ }
+
+ /**
+ * Options provided when sending or editing a message.
+ * @typedef {Object} MessageOptions
+ * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
+ * @property {string} [nonce=''] The nonce for the message
+ * @property {RichEmbed|Object} [embed] An embed for the message
+ * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
+ * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here
+ * should be replaced with plain-text
+ * @property {FileOptions|BufferResolvable|Attachment} [file] A file to send with the message **(deprecated)**
+ * @property {FileOptions[]|BufferResolvable[]|Attachment[]} [files] Files to send with the message
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
+ * it exceeds the character limit. If an object is provided, these are the options for splitting the message
+ * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs)
+ */
+
+ /**
+ * @typedef {Object} FileOptions
+ * @property {BufferResolvable} attachment File to attach
+ * @property {string} [name='file.jpg'] Filename of the attachment
+ */
+
+ /**
+ * Options for splitting a message.
+ * @typedef {Object} SplitOptions
+ * @property {number} [maxLength=1950] Maximum character length per message piece
+ * @property {string} [char='\n'] Character to split the message with
+ * @property {string} [prepend=''] Text to prepend to every piece except the first
+ * @property {string} [append=''] Text to append to every piece except the last
+ */
+
+ /**
+ * Send a message to this channel.
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions|Attachment|RichEmbed} [options] Options for the message,
+ * can also be just a RichEmbed or Attachment
+ * @returns {Promise<Message|Message[]>}
+ * @example
+ * // Send a basic message
+ * channel.send('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ * @example
+ * // Send a remote file
+ * channel.send({
+ * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send a local file
+ * channel.send({
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ * @example
+ * // Send an embed with a local image inside
+ * channel.send('This is an embed', {
+ * embed: {
+ * thumbnail: {
+ * url: 'attachment://file.jpg'
+ * }
+ * },
+ * files: [{
+ * attachment: 'entire/path/to/file.jpg',
+ * name: 'file.jpg'
+ * }]
+ * })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ // eslint-disable-next-line complexity
+ send(content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+
+ const { reply } = options;
+ if (options instanceof Attachment) options = { files: [options.file] };
+ if (options instanceof RichEmbed) {
+ if (options.reply) options.reply = undefined;
+ options = { embed: options };
+ }
+ options.reply = reply;
+
+ if (options.embed) {
+ if (options.embed.file) {
+ if (options.files) options.files.push(options.embed.file);
+ else options.files = [options.embed.file];
+ }
+ if (options.embed.files) {
+ if (options.files) options.files = options.files.concat(options.embed.files);
+ else options.files = options.embed.files;
+ }
+ }
+
+ if (options.file) {
+ if (options.files) options.files.push(options.file);
+ else options.files = [options.file];
+ }
+
+ if (options.embed) options.embed = new RichEmbed(options.embed).toJSON();
+
+ if (options.files) {
+ for (let i = 0; i < options.files.length; i++) {
+ let file = options.files[i];
+ if (!file || typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file };
+ if (!file.name) {
+ if (typeof file.attachment === 'string') {
+ file.name = path.basename(file.attachment);
+ } else if (file.attachment && file.attachment.path) {
+ file.name = path.basename(file.attachment.path);
+ } else if (file instanceof Attachment) {
+ file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' };
+ } else {
+ file.name = 'file.jpg';
+ }
+ } else if (file instanceof Attachment) {
+ file = file.file;
+ }
+ options.files[i] = file;
+ }
+
+ return Promise.all(options.files.map(file =>
+ this.client.resolver.resolveFile(file.attachment).then(resource => {
+ file.file = resource;
+ return file;
+ })
+ )).then(files => this.client.rest.methods.sendMessage(this, content, options, files));
+ }
+
+ return this.client.rest.methods.sendMessage(this, content, options);
+ }
+
+ /**
+ * Gets a single message from this channel, regardless of it being cached or not.
+ * @param {Snowflake} messageID ID of the message to get
+ * @returns {Promise<Message>}
+ * @example
+ * // Get message
+ * channel.fetchMessage('99539446449315840')
+ * .then(message => console.log(message.content))
+ * .catch(console.error);
+ */
+ fetchMessage(messageID) {
+ if (!this.client.user.bot) {
+ return this.fetchMessages({ limit: 1, around: messageID }).then(messages => {
+ const msg = messages.get(messageID);
+ if (!msg) throw new Error('Message not found.');
+ return msg;
+ });
+ }
+ return this.client.rest.methods.getChannelMessage(this, messageID).then(data => {
+ const msg = data instanceof Message ? data : new Message(this, data, this.client);
+ this._cacheMessage(msg);
+ return msg;
+ });
+ }
+
+ /**
+ * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
+ * `after` are mutually exclusive. All the parameters are optional.
+ * @typedef {Object} ChannelLogsQueryOptions
+ * @property {number} [limit=50] Number of messages to acquire
+ * @property {Snowflake} [before] ID of a message to get the messages that were posted before it
+ * @property {Snowflake} [after] ID of a message to get the messages that were posted after it
+ * @property {Snowflake} [around] ID of a message to get the messages that were posted around it
+ */
+
+ /**
+ * Gets the past messages sent in this channel. Resolves with a collection mapping message ID's to Message objects.
+ * <info>The returned Collection does not contain reaction users of the messages if they were not cached.
+ * Those need to be fetched separately in such a case.</info>
+ * @param {ChannelLogsQueryOptions} [options={}] Query parameters to pass in
+ * @returns {Promise<Collection<Snowflake, Message>>}
+ * @example
+ * // Get messages
+ * channel.fetchMessages({ limit: 10 })
+ * .then(messages => console.log(`Received ${messages.size} messages`))
+ * .catch(console.error);
+ * @example
+ * // Get messages and filter by user ID
+ * channel.fetchMessages()
+ * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`))
+ * .catch(console.error);
+ */
+ fetchMessages(options = {}) {
+ return this.client.rest.methods.getChannelMessages(this, options).then(data => {
+ const messages = new Collection();
+ for (const message of data) {
+ const msg = new Message(this, message, this.client);
+ messages.set(message.id, msg);
+ this._cacheMessage(msg);
+ }
+ return messages;
+ });
+ }
+
+ /**
+ * Fetches the pinned messages of this channel and returns a collection of them.
+ * <info>The returned Collection does not contain any reaction data of the messages.
+ * Those need to be fetched separately.</info>
+ * @returns {Promise<Collection<Snowflake, Message>>}
+ * @example
+ * // Get pinned messages
+ * channel.fetchPinnedMessages()
+ * .then(messages => console.log(`Received ${messages.size} messages`))
+ * .catch(console.error);
+ */
+ fetchPinnedMessages() {
+ return this.client.rest.methods.getChannelPinnedMessages(this).then(data => {
+ const messages = new Collection();
+ for (const message of data) {
+ const msg = new Message(this, message, this.client);
+ messages.set(message.id, msg);
+ this._cacheMessage(msg);
+ }
+ return messages;
+ });
+ }
+
+ /**
+ * @typedef {Object} MessageSearchOptions
+ * @property {string} [content] Message content
+ * @property {Snowflake} [maxID] Maximum ID for the filter
+ * @property {Snowflake} [minID] Minimum ID for the filter
+ * @property {string} [has] One of `link`, `embed`, `file`, `video`, `image`, or `sound`,
+ * or add `-` to negate (e.g. `-file`)
+ * @property {ChannelResolvable} [channel] Channel to limit search to (only for guild search endpoint)
+ * @property {UserResolvable} [author] Author to limit search
+ * @property {string} [authorType] One of `user`, `bot`, `webhook`, or add `-` to negate (e.g. `-webhook`)
+ * @property {string} [sortBy='recent'] `recent` or `relevant`
+ * @property {string} [sortOrder='desc'] `asc` or `desc`
+ * @property {number} [contextSize=2] How many messages to get around the matched message (0 to 2)
+ * @property {number} [limit=25] Maximum number of results to get (1 to 25)
+ * @property {number} [offset=0] Offset the "pages" of results (since you can only see 25 at a time)
+ * @property {UserResolvable} [mentions] Mentioned user filter
+ * @property {boolean} [mentionsEveryone] If everyone is mentioned
+ * @property {string} [linkHostname] Filter links by hostname
+ * @property {string} [embedProvider] The name of an embed provider
+ * @property {string} [embedType] one of `image`, `video`, `url`, `rich`
+ * @property {string} [attachmentFilename] The name of an attachment
+ * @property {string} [attachmentExtension] The extension of an attachment
+ * @property {Date} [before] Date to find messages before
+ * @property {Date} [after] Date to find messages before
+ * @property {Date} [during] Date to find messages during (range of date to date + 24 hours)
+ * @property {boolean} [nsfw=false] Include results from NSFW channels
+ */
+
+ /**
+ * @typedef {Object} MessageSearchResult
+ * @property {number} totalResults Total result count
+ * @property {Message[][]} messages Array of message results
+ * The message which has triggered the result will have the `hit` property set to `true`
+ */
+
+ /**
+ * Performs a search within the channel.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {MessageSearchOptions} [options={}] Options to pass to the search
+ * @returns {Promise<MessageSearchResult>}
+ * @deprecated
+ * @example
+ * channel.search({
+ * content: 'discord.js',
+ * before: '2016-11-17'
+ * }).then(res => {
+ * const hit = res.messages[0].find(m => m.hit).content;
+ * console.log(`I found: **${hit}**, total results: ${res.totalResults}`);
+ * }).catch(console.error);
+ */
+ search(options = {}) {
+ return this.client.rest.methods.search(this, options);
+ }
+
+ /**
+ * Starts a typing indicator in the channel.
+ * @param {number} [count] The number of times startTyping should be considered to have been called
+ * @example
+ * // Start typing in a channel
+ * channel.startTyping();
+ */
+ startTyping(count) {
+ if (typeof count !== 'undefined' && count < 1) throw new RangeError('Count must be at least 1.');
+ if (this.client.user._typing.has(this.id)) {
+ const entry = this.client.user._typing.get(this.id);
+ entry.count = count || entry.count + 1;
+ return;
+ }
+
+ const entry = {
+ count: count || 1,
+ interval: this.client.setInterval(() => {
+ this.client.rest.methods.sendTyping(this.id).catch(() => {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ });
+ }, 9000),
+ };
+ this.client.rest.methods.sendTyping(this.id).catch(() => {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ });
+ this.client.user._typing.set(this.id, entry);
+ }
+
+ /**
+ * Stops the typing indicator in the channel.
+ * The indicator will only stop if this is called as many times as startTyping().
+ * <info>It can take a few seconds for the client user to stop typing.</info>
+ * @param {boolean} [force=false] Whether or not to reset the call count and force the indicator to stop
+ * @example
+ * // Reduce the typing count by one and stop typing if it reached 0
+ * channel.stopTyping();
+ * @example
+ * // Force typing to fully stop in a channel
+ * channel.stopTyping(true);
+ */
+ stopTyping(force = false) {
+ if (this.client.user._typing.has(this.id)) {
+ const entry = this.client.user._typing.get(this.id);
+ entry.count--;
+ if (entry.count <= 0 || force) {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ }
+ }
+ }
+
+ /**
+ * Whether or not the typing indicator is being shown in the channel
+ * @type {boolean}
+ * @readonly
+ */
+ get typing() {
+ return this.client.user._typing.has(this.id);
+ }
+
+ /**
+ * Number of times `startTyping` has been called
+ * @type {number}
+ * @readonly
+ */
+ get typingCount() {
+ if (this.client.user._typing.has(this.id)) return this.client.user._typing.get(this.id).count;
+ return 0;
+ }
+
+ /**
+ * The Message object of the last message in the channel, if one was sent
+ * @type {?Message}
+ * @readonly
+ */
+ get lastMessage() {
+ return this.messages.get(this.lastMessageID) || null;
+ }
+
+ /**
+ * The date when the last pinned message was pinned, if there was one
+ * @type {?Date}
+ * @readonly
+ */
+ get lastPinAt() {
+ return this.lastPinTimestamp ? new Date(this.lastPinTimestamp) : null;
+ }
+
+ /**
+ * Creates a Message Collector
+ * @param {CollectorFilter} filter The filter to create the collector with
+ * @param {MessageCollectorOptions} [options={}] The options to pass to the collector
+ * @returns {MessageCollector}
+ * @deprecated
+ */
+ createCollector(filter, options) {
+ return this.createMessageCollector(filter, options);
+ }
+
+ /**
+ * Creates a Message Collector.
+ * @param {CollectorFilter} filter The filter to create the collector with
+ * @param {MessageCollectorOptions} [options={}] The options to pass to the collector
+ * @returns {MessageCollector}
+ * @example
+ * // Create a message collector
+ * const filter = m => m.content.includes('discord');
+ * const collector = channel.createMessageCollector(filter, { time: 15000 });
+ * collector.on('collect', m => console.log(`Collected ${m.content}`));
+ * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
+ */
+ createMessageCollector(filter, options = {}) {
+ return new MessageCollector(this, filter, options);
+ }
+
+ /**
+ * An object containing the same properties as CollectorOptions, but a few more:
+ * @typedef {MessageCollectorOptions} AwaitMessagesOptions
+ * @property {string[]} [errors] Stop/end reasons that cause the promise to reject
+ */
+
+ /**
+ * Similar to createCollector but in promise form. Resolves with a collection of messages that pass the specified
+ * filter.
+ * @param {CollectorFilter} filter The filter function to use
+ * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector
+ * @returns {Promise<Collection<Snowflake, Message>>}
+ * @example
+ * // Await !vote messages
+ * const filter = m => m.content.startsWith('!vote');
+ * // Errors: ['time'] treats ending because of the time limit as an error
+ * channel.awaitMessages(filter, { max: 4, time: 60000, errors: ['time'] })
+ * .then(collected => console.log(collected.size))
+ * .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`));
+ */
+ awaitMessages(filter, options = {}) {
+ return new Promise((resolve, reject) => {
+ const collector = this.createCollector(filter, options);
+ collector.once('end', (collection, reason) => {
+ if (options.errors && options.errors.includes(reason)) {
+ reject(collection);
+ } else {
+ resolve(collection);
+ }
+ });
+ });
+ }
+
+ /**
+ * Bulk delete given messages that are newer than two weeks.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {Collection<Snowflake, Message>|Message[]|Snowflake[]|number} messages
+ * Messages or number of messages to delete
+ * @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically
+ * @returns {Promise<Collection<Snowflake, Message>>} Deleted messages
+ * @example
+ * // Bulk delete messages
+ * channel.bulkDelete(5)
+ * .then(messages => console.log(`Bulk deleted ${messages.size} messages`))
+ * .catch(console.error);
+ */
+ bulkDelete(messages, filterOld = false) {
+ if (messages instanceof Array || messages instanceof Collection) {
+ let messageIDs = messages instanceof Collection ? messages.keyArray() : messages.map(m => m.id || m);
+ if (filterOld) {
+ messageIDs = messageIDs.filter(id => Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000);
+ }
+ if (messageIDs.length === 0) return Promise.resolve(new Collection());
+ if (messageIDs.length === 1) {
+ return this.fetchMessage(messageIDs[0]).then(m => m.delete()).then(m => new Collection([[m.id, m]]));
+ }
+ return this.client.rest.methods.bulkDeleteMessages(this, messageIDs);
+ }
+ if (!isNaN(messages)) return this.fetchMessages({ limit: messages }).then(msgs => this.bulkDelete(msgs, filterOld));
+ throw new TypeError('The messages must be an Array, Collection, or number.');
+ }
+
+ /**
+ * Marks all messages in this channel as read.
+ * <warn>This is only available when using a user account.</warn>
+ * @returns {Promise<TextChannel|GroupDMChannel|DMChannel>}
+ * @deprecated
+ */
+ acknowledge() {
+ if (!this.lastMessageID) return Promise.resolve(this);
+ return this.client.rest.methods.ackTextChannel(this);
+ }
+
+ _cacheMessage(message) {
+ const maxSize = this.client.options.messageCacheMaxSize;
+ if (maxSize === 0) return null;
+ if (this.messages.size >= maxSize && maxSize > 0) this.messages.delete(this.messages.firstKey());
+ this.messages.set(message.id, message);
+ return message;
+ }
+}
+
+/** @lends TextBasedChannel.prototype */
+const Deprecated = {
+ /**
+ * Send a message to this channel.
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions} [options={}] Options for the message
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ * @example
+ * // Send a message
+ * channel.sendMessage('hello!')
+ * .then(message => console.log(`Sent message: ${message.content}`))
+ * .catch(console.error);
+ */
+ sendMessage(content, options) {
+ return this.send(content, options);
+ },
+
+ /**
+ * Send an embed to this channel.
+ * @param {RichEmbed|Object} embed Embed for the message
+ * @param {string} [content] Text for the message
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendEmbed(embed, content, options) {
+ if (!options && typeof content === 'object' && !(content instanceof Array)) {
+ options = content;
+ content = '';
+ } else if (!options) {
+ options = {};
+ }
+ return this.send(content, Object.assign(options, { embed }));
+ },
+
+ /**
+ * Send files to this channel.
+ * @param {FileOptions[]|string[]} files Files to send with the message
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendFiles(files, content, options = {}) {
+ return this.send(content, Object.assign(options, { files }));
+ },
+
+ /**
+ * Send a file to this channel.
+ * @param {BufferResolvable} attachment File to send
+ * @param {string} [name='file.jpg'] Name and extension of the file
+ * @param {StringResolvable} [content] Text for the message
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message>}
+ * @deprecated
+ */
+ sendFile(attachment, name, content, options = {}) {
+ return this.send({ files: [{ attachment, name }], content, options });
+ },
+
+ /**
+ * Send a code block to this channel.
+ * @param {string} lang Language for the code block
+ * @param {StringResolvable} content Content of the code block
+ * @param {MessageOptions} [options] Options for the message
+ * @returns {Promise<Message|Message[]>}
+ * @deprecated
+ */
+ sendCode(lang, content, options = {}) {
+ return this.send(content, Object.assign(options, { code: lang }));
+ },
+};
+
+for (const key of Object.keys(Deprecated)) {
+ TextBasedChannel.prototype[key] = util.deprecate(Deprecated[key], `TextChannel#${key}: use TextChannel#send instead`);
+}
+
+exports.applyToClass = (structure, full = false, ignore = []) => {
+ const props = ['send', 'sendMessage', 'sendEmbed', 'sendFile', 'sendFiles', 'sendCode'];
+ if (full) {
+ props.push(
+ '_cacheMessage',
+ 'acknowledge',
+ 'fetchMessages',
+ 'fetchMessage',
+ 'search',
+ 'lastMessage',
+ 'lastPinAt',
+ 'bulkDelete',
+ 'startTyping',
+ 'stopTyping',
+ 'typing',
+ 'typingCount',
+ 'fetchPinnedMessages',
+ 'createCollector',
+ 'createMessageCollector',
+ 'awaitMessages'
+ );
+ }
+ for (const prop of props) {
+ if (ignore.includes(prop)) continue;
+ Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop));
+ }
+};
+
+TextBasedChannel.prototype.acknowledge = util.deprecate(
+ TextBasedChannel.prototype.acknowledge, 'TextBasedChannel#acknowledge: userbot methods will be removed'
+);
+
+TextBasedChannel.prototype.search =
+ util.deprecate(TextBasedChannel.prototype.search, 'TextBasedChannel#search: userbot methods will be removed');
diff --git a/node_modules/discord.js/src/structures/shared/resolvePermissions.js b/node_modules/discord.js/src/structures/shared/resolvePermissions.js
new file mode 100644
index 0000000..c266d46
--- /dev/null
+++ b/node_modules/discord.js/src/structures/shared/resolvePermissions.js
@@ -0,0 +1,26 @@
+const Permissions = require('../../util/Permissions');
+const Collection = require('../../util/Collection');
+
+module.exports = function resolvePermissions(overwrites, guild) {
+ if (overwrites instanceof Collection || overwrites instanceof Array) {
+ overwrites = overwrites.map(overwrite => {
+ const role = this.client.resolver.resolveRole(guild, overwrite.id);
+ if (role) {
+ overwrite.id = role.id;
+ overwrite.type = 'role';
+ } else {
+ overwrite.id = this.client.resolver.resolveUserID(overwrite.id);
+ overwrite.type = 'member';
+ }
+
+ return {
+ allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0),
+ deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0),
+ type: overwrite.type,
+ id: overwrite.id,
+ };
+ });
+ }
+
+ return overwrites;
+};