summaryrefslogtreecommitdiff
path: root/node_modules/discord.js/src/structures
diff options
context:
space:
mode:
author8cy <[email protected]>2020-04-30 15:46:16 -0700
committer8cy <[email protected]>2020-04-30 15:46:16 -0700
commit3a4deac89054021b56ad5bd8005b2044cc085c98 (patch)
tree3dd6af8503e497e46180b6b5231674f36bdce9f2 /node_modules/discord.js/src/structures
downloaduppity-3a4deac89054021b56ad5bd8005b2044cc085c98.tar.xz
uppity-3a4deac89054021b56ad5bd8005b2044cc085c98.zip
Up, up, uppity.
Diffstat (limited to 'node_modules/discord.js/src/structures')
-rw-r--r--node_modules/discord.js/src/structures/APIMessage.js379
-rw-r--r--node_modules/discord.js/src/structures/Base.js42
-rw-r--r--node_modules/discord.js/src/structures/BaseGuildEmoji.js58
-rw-r--r--node_modules/discord.js/src/structures/CategoryChannel.js33
-rw-r--r--node_modules/discord.js/src/structures/Channel.js151
-rw-r--r--node_modules/discord.js/src/structures/ClientApplication.js156
-rw-r--r--node_modules/discord.js/src/structures/ClientPresence.js87
-rw-r--r--node_modules/discord.js/src/structures/ClientUser.js180
-rw-r--r--node_modules/discord.js/src/structures/DMChannel.js98
-rw-r--r--node_modules/discord.js/src/structures/Emoji.js104
-rw-r--r--node_modules/discord.js/src/structures/Guild.js1349
-rw-r--r--node_modules/discord.js/src/structures/GuildAuditLogs.js509
-rw-r--r--node_modules/discord.js/src/structures/GuildChannel.js609
-rw-r--r--node_modules/discord.js/src/structures/GuildEmoji.js159
-rw-r--r--node_modules/discord.js/src/structures/GuildMember.js410
-rw-r--r--node_modules/discord.js/src/structures/GuildPreview.js157
-rw-r--r--node_modules/discord.js/src/structures/GuildPreviewEmoji.js26
-rw-r--r--node_modules/discord.js/src/structures/Integration.js167
-rw-r--r--node_modules/discord.js/src/structures/Invite.js194
-rw-r--r--node_modules/discord.js/src/structures/Message.js623
-rw-r--r--node_modules/discord.js/src/structures/MessageAttachment.js98
-rw-r--r--node_modules/discord.js/src/structures/MessageCollector.js129
-rw-r--r--node_modules/discord.js/src/structures/MessageEmbed.js454
-rw-r--r--node_modules/discord.js/src/structures/MessageMentions.js221
-rw-r--r--node_modules/discord.js/src/structures/MessageReaction.js135
-rw-r--r--node_modules/discord.js/src/structures/NewsChannel.js18
-rw-r--r--node_modules/discord.js/src/structures/PartialGroupDMChannel.js46
-rw-r--r--node_modules/discord.js/src/structures/PermissionOverwrites.js189
-rw-r--r--node_modules/discord.js/src/structures/Presence.js336
-rw-r--r--node_modules/discord.js/src/structures/ReactionCollector.js190
-rw-r--r--node_modules/discord.js/src/structures/ReactionEmoji.js31
-rw-r--r--node_modules/discord.js/src/structures/Role.js403
-rw-r--r--node_modules/discord.js/src/structures/StoreChannel.js22
-rw-r--r--node_modules/discord.js/src/structures/Team.js109
-rw-r--r--node_modules/discord.js/src/structures/TeamMember.js65
-rw-r--r--node_modules/discord.js/src/structures/TextChannel.js151
-rw-r--r--node_modules/discord.js/src/structures/User.js318
-rw-r--r--node_modules/discord.js/src/structures/VoiceChannel.js151
-rw-r--r--node_modules/discord.js/src/structures/VoiceRegion.js52
-rw-r--r--node_modules/discord.js/src/structures/VoiceState.js208
-rw-r--r--node_modules/discord.js/src/structures/Webhook.js273
-rw-r--r--node_modules/discord.js/src/structures/interfaces/Collector.js281
-rw-r--r--node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js396
43 files changed, 9767 insertions, 0 deletions
diff --git a/node_modules/discord.js/src/structures/APIMessage.js b/node_modules/discord.js/src/structures/APIMessage.js
new file mode 100644
index 0000000..86dab74
--- /dev/null
+++ b/node_modules/discord.js/src/structures/APIMessage.js
@@ -0,0 +1,379 @@
+'use strict';
+
+const MessageAttachment = require('./MessageAttachment');
+const MessageEmbed = require('./MessageEmbed');
+const { RangeError } = require('../errors');
+const { browser } = require('../util/Constants');
+const DataResolver = require('../util/DataResolver');
+const MessageFlags = require('../util/MessageFlags');
+const Util = require('../util/Util');
+
+/**
+ * Represents a message to be sent to the API.
+ */
+class APIMessage {
+ /**
+ * @param {MessageTarget} target - The target for this message to be sent to
+ * @param {MessageOptions|WebhookMessageOptions} options - Options passed in from send
+ */
+ constructor(target, options) {
+ /**
+ * The target for this message to be sent to
+ * @type {MessageTarget}
+ */
+ this.target = target;
+
+ /**
+ * Options passed in from send
+ * @type {MessageOptions|WebhookMessageOptions}
+ */
+ this.options = options;
+
+ /**
+ * Data sendable to the API
+ * @type {?Object}
+ */
+ this.data = null;
+
+ /**
+ * Files sendable to the API
+ * @type {?Object[]}
+ */
+ this.files = null;
+ }
+
+ /**
+ * Whether or not the target is a webhook
+ * @type {boolean}
+ * @readonly
+ */
+ get isWebhook() {
+ const Webhook = require('./Webhook');
+ const WebhookClient = require('../client/WebhookClient');
+ return this.target instanceof Webhook || this.target instanceof WebhookClient;
+ }
+
+ /**
+ * Whether or not the target is a user
+ * @type {boolean}
+ * @readonly
+ */
+ get isUser() {
+ const User = require('./User');
+ const GuildMember = require('./GuildMember');
+ return this.target instanceof User || this.target instanceof GuildMember;
+ }
+
+ /**
+ * Whether or not the target is a message
+ * @type {boolean}
+ * @readonly
+ */
+ get isMessage() {
+ const Message = require('./Message');
+ return this.target instanceof Message;
+ }
+
+ /**
+ * Makes the content of this message.
+ * @returns {?(string|string[])}
+ */
+ makeContent() {
+ const GuildMember = require('./GuildMember');
+
+ let content;
+ if (this.options.content === null) {
+ content = '';
+ } else if (typeof this.options.content !== 'undefined') {
+ content = Util.resolveString(this.options.content);
+ }
+
+ const disableMentions =
+ typeof this.options.disableMentions === 'undefined'
+ ? this.target.client.options.disableMentions
+ : this.options.disableMentions;
+ if (disableMentions === 'all') {
+ content = Util.removeMentions(content || '');
+ } else if (disableMentions === 'everyone') {
+ content = (content || '').replace(/@([^<>@ ]*)/gmsu, (match, target) => {
+ if (target.match(/^[&!]?\d+$/)) {
+ return `@${target}`;
+ } else {
+ return `@\u200b${target}`;
+ }
+ });
+ }
+
+ const isSplit = typeof this.options.split !== 'undefined' && this.options.split !== false;
+ const isCode = typeof this.options.code !== 'undefined' && this.options.code !== false;
+ const splitOptions = isSplit ? { ...this.options.split } : undefined;
+
+ let mentionPart = '';
+ if (this.options.reply && !this.isUser && this.target.type !== 'dm') {
+ const id = this.target.client.users.resolveID(this.options.reply);
+ mentionPart = `<@${this.options.reply instanceof GuildMember && this.options.reply.nickname ? '!' : ''}${id}>, `;
+ if (isSplit) {
+ splitOptions.prepend = `${mentionPart}${splitOptions.prepend || ''}`;
+ }
+ }
+
+ if (content || mentionPart) {
+ if (isCode) {
+ const codeName = typeof this.options.code === 'string' ? this.options.code : '';
+ content = `${mentionPart}\`\`\`${codeName}\n${Util.cleanCodeBlockContent(content || '')}\n\`\`\``;
+ if (isSplit) {
+ splitOptions.prepend = `${splitOptions.prepend || ''}\`\`\`${codeName}\n`;
+ splitOptions.append = `\n\`\`\`${splitOptions.append || ''}`;
+ }
+ } else if (mentionPart) {
+ content = `${mentionPart}${content || ''}`;
+ }
+
+ if (isSplit) {
+ content = Util.splitMessage(content || '', splitOptions);
+ }
+ }
+
+ return content;
+ }
+
+ /**
+ * Resolves data.
+ * @returns {APIMessage}
+ */
+ resolveData() {
+ if (this.data) return this;
+
+ const content = this.makeContent();
+ const tts = Boolean(this.options.tts);
+
+ let nonce;
+ if (typeof this.options.nonce !== 'undefined') {
+ nonce = parseInt(this.options.nonce);
+ if (isNaN(nonce) || nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE');
+ }
+
+ const embedLikes = [];
+ if (this.isWebhook) {
+ if (this.options.embeds) {
+ embedLikes.push(...this.options.embeds);
+ }
+ } else if (this.options.embed) {
+ embedLikes.push(this.options.embed);
+ }
+ const embeds = embedLikes.map(e => new MessageEmbed(e).toJSON());
+
+ let username;
+ let avatarURL;
+ if (this.isWebhook) {
+ username = this.options.username || this.target.name;
+ if (this.options.avatarURL) avatarURL = this.options.avatarURL;
+ }
+
+ let flags;
+ if (this.isMessage) {
+ // eslint-disable-next-line eqeqeq
+ flags = this.options.flags != null ? new MessageFlags(this.options.flags).bitfield : this.target.flags.bitfield;
+ }
+
+ const allowedMentions =
+ typeof this.options.allowedMentions === 'undefined'
+ ? this.target.client.options.allowedMentions
+ : this.options.allowedMentions;
+
+ this.data = {
+ content,
+ tts,
+ nonce,
+ embed: this.options.embed === null ? null : embeds[0],
+ embeds,
+ username,
+ avatar_url: avatarURL,
+ allowed_mentions: allowedMentions,
+ flags,
+ };
+ return this;
+ }
+
+ /**
+ * Resolves files.
+ * @returns {Promise<APIMessage>}
+ */
+ async resolveFiles() {
+ if (this.files) return this;
+
+ const embedLikes = [];
+ if (this.isWebhook) {
+ if (this.options.embeds) {
+ embedLikes.push(...this.options.embeds);
+ }
+ } else if (this.options.embed) {
+ embedLikes.push(this.options.embed);
+ }
+
+ const fileLikes = [];
+ if (this.options.files) {
+ fileLikes.push(...this.options.files);
+ }
+ for (const embed of embedLikes) {
+ if (embed.files) {
+ fileLikes.push(...embed.files);
+ }
+ }
+
+ this.files = await Promise.all(fileLikes.map(f => this.constructor.resolveFile(f)));
+ return this;
+ }
+
+ /**
+ * Converts this APIMessage into an array of APIMessages for each split content
+ * @returns {APIMessage[]}
+ */
+ split() {
+ if (!this.data) this.resolveData();
+
+ if (!Array.isArray(this.data.content)) return [this];
+
+ const apiMessages = [];
+
+ for (let i = 0; i < this.data.content.length; i++) {
+ let data;
+ let opt;
+
+ if (i === this.data.content.length - 1) {
+ data = { ...this.data, content: this.data.content[i] };
+ opt = { ...this.options, content: this.data.content[i] };
+ } else {
+ data = { content: this.data.content[i], tts: this.data.tts };
+ opt = { content: this.data.content[i], tts: this.data.tts };
+ }
+
+ const apiMessage = new APIMessage(this.target, opt);
+ apiMessage.data = data;
+ apiMessages.push(apiMessage);
+ }
+
+ return apiMessages;
+ }
+
+ /**
+ * Resolves a single file into an object sendable to the API.
+ * @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file
+ * @returns {Object}
+ */
+ static async resolveFile(fileLike) {
+ let attachment;
+ let name;
+
+ const findName = thing => {
+ if (typeof thing === 'string') {
+ return Util.basename(thing);
+ }
+
+ if (thing.path) {
+ return Util.basename(thing.path);
+ }
+
+ return 'file.jpg';
+ };
+
+ const ownAttachment =
+ typeof fileLike === 'string' ||
+ fileLike instanceof (browser ? ArrayBuffer : Buffer) ||
+ typeof fileLike.pipe === 'function';
+ if (ownAttachment) {
+ attachment = fileLike;
+ name = findName(attachment);
+ } else {
+ attachment = fileLike.attachment;
+ name = fileLike.name || findName(attachment);
+ }
+
+ const resource = await DataResolver.resolveFile(attachment);
+ return { attachment, name, file: resource };
+ }
+
+ /**
+ * Partitions embeds and attachments.
+ * @param {Array<MessageEmbed|MessageAttachment>} items Items to partition
+ * @returns {Array<MessageEmbed[], MessageAttachment[]>}
+ */
+ static partitionMessageAdditions(items) {
+ const embeds = [];
+ const files = [];
+ for (const item of items) {
+ if (item instanceof MessageEmbed) {
+ embeds.push(item);
+ } else if (item instanceof MessageAttachment) {
+ files.push(item);
+ }
+ }
+
+ return [embeds, files];
+ }
+
+ /**
+ * Transforms the user-level arguments into a final options object. Passing a transformed options object alone into
+ * this method will keep it the same, allowing for the reuse of the final options object.
+ * @param {StringResolvable} [content] Content to send
+ * @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use
+ * @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto transformed options
+ * @param {boolean} [isWebhook=false] Whether or not to use WebhookMessageOptions as the result
+ * @returns {MessageOptions|WebhookMessageOptions}
+ */
+ static transformOptions(content, options, extra = {}, isWebhook = false) {
+ if (!options && typeof content === 'object' && !Array.isArray(content)) {
+ options = content;
+ content = undefined;
+ }
+
+ if (!options) {
+ options = {};
+ } else if (options instanceof MessageEmbed) {
+ return isWebhook ? { content, embeds: [options], ...extra } : { content, embed: options, ...extra };
+ } else if (options instanceof MessageAttachment) {
+ return { content, files: [options], ...extra };
+ }
+
+ if (Array.isArray(options)) {
+ const [embeds, files] = this.partitionMessageAdditions(options);
+ return isWebhook ? { content, embeds, files, ...extra } : { content, embed: embeds[0], files, ...extra };
+ } else if (Array.isArray(content)) {
+ const [embeds, files] = this.partitionMessageAdditions(content);
+ if (embeds.length || files.length) {
+ return isWebhook ? { embeds, files, ...extra } : { embed: embeds[0], files, ...extra };
+ }
+ }
+
+ return { content, ...options, ...extra };
+ }
+
+ /**
+ * Creates an `APIMessage` from user-level arguments.
+ * @param {MessageTarget} target Target to send to
+ * @param {StringResolvable} [content] Content to send
+ * @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use
+ * @param {MessageOptions|WebhookMessageOptions} [extra={}] - Extra options to add onto transformed options
+ * @returns {MessageOptions|WebhookMessageOptions}
+ */
+ static create(target, content, options, extra = {}) {
+ const Webhook = require('./Webhook');
+ const WebhookClient = require('../client/WebhookClient');
+
+ const isWebhook = target instanceof Webhook || target instanceof WebhookClient;
+ const transformed = this.transformOptions(content, options, extra, isWebhook);
+ return new this(target, transformed);
+ }
+}
+
+module.exports = APIMessage;
+
+/**
+ * A target for a message.
+ * @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient} MessageTarget
+ */
+
+/**
+ * Additional items that can be sent with a message.
+ * @typedef {MessageEmbed|MessageAttachment|Array<MessageEmbed|MessageAttachment>} MessageAdditions
+ */
diff --git a/node_modules/discord.js/src/structures/Base.js b/node_modules/discord.js/src/structures/Base.js
new file mode 100644
index 0000000..65c1fa5
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Base.js
@@ -0,0 +1,42 @@
+'use strict';
+
+const Util = require('../util/Util');
+
+/**
+ * Represents a data model that is identifiable by a Snowflake (i.e. Discord API data models).
+ */
+class Base {
+ constructor(client) {
+ /**
+ * The client that instantiated this
+ * @name Base#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ }
+
+ _clone() {
+ return Object.assign(Object.create(this), this);
+ }
+
+ _patch(data) {
+ return data;
+ }
+
+ _update(data) {
+ const clone = this._clone();
+ this._patch(data);
+ return clone;
+ }
+
+ toJSON(...props) {
+ return Util.flatten(this, ...props);
+ }
+
+ valueOf() {
+ return this.id;
+ }
+}
+
+module.exports = Base;
diff --git a/node_modules/discord.js/src/structures/BaseGuildEmoji.js b/node_modules/discord.js/src/structures/BaseGuildEmoji.js
new file mode 100644
index 0000000..d366527
--- /dev/null
+++ b/node_modules/discord.js/src/structures/BaseGuildEmoji.js
@@ -0,0 +1,58 @@
+'use strict';
+
+const Emoji = require('./Emoji');
+
+/**
+ * Parent class for {@link GuildEmoji} and {@link GuildPreviewEmoji}.
+ * @extends {Emoji}
+ */
+class BaseGuildEmoji extends Emoji {
+ constructor(client, data, guild) {
+ super(client, data);
+
+ /**
+ * The guild this emoji is a part of
+ * @type {Guild|GuildPreview}
+ */
+ this.guild = guild;
+
+ /**
+ * Array of role ids this emoji is active for
+ * @name BaseGuildEmoji#_roles
+ * @type {Snowflake[]}
+ * @private
+ */
+ Object.defineProperty(this, '_roles', { value: [], writable: true });
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ if (data.name) this.name = data.name;
+
+ /**
+ * Whether or not this emoji requires colons surrounding it
+ * @type {boolean}
+ * @name GuildEmoji#requiresColons
+ */
+ if (typeof data.require_colons !== 'undefined') this.requiresColons = data.require_colons;
+
+ /**
+ * Whether this emoji is managed by an external service
+ * @type {boolean}
+ * @name GuildEmoji#managed
+ */
+ if (typeof data.managed !== 'undefined') this.managed = data.managed;
+
+ /**
+ * Whether this emoji is available
+ * @type {boolean}
+ * @name GuildEmoji#available
+ */
+ if (typeof data.available !== 'undefined') this.available = data.available;
+
+ if (data.roles) this._roles = data.roles;
+ }
+}
+
+module.exports = BaseGuildEmoji;
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..4ac9fbb
--- /dev/null
+++ b/node_modules/discord.js/src/structures/CategoryChannel.js
@@ -0,0 +1,33 @@
+'use strict';
+
+const GuildChannel = require('./GuildChannel');
+
+/**
+ * Represents a guild category channel on Discord.
+ * @extends {GuildChannel}
+ */
+class CategoryChannel extends GuildChannel {
+ /**
+ * Channels that are a part of this category
+ * @type {?Collection<Snowflake, GuildChannel>}
+ * @readonly
+ */
+ get children() {
+ return this.guild.channels.cache.filter(c => c.parentID === this.id);
+ }
+
+ /**
+ * Sets the category parent of this channel.
+ * <warn>It is not currently possible to set the parent of a CategoryChannel.</warn>
+ * @method setParent
+ * @memberof CategoryChannel
+ * @instance
+ * @param {?GuildChannel|Snowflake} channel Parent channel
+ * @param {Object} [options={}] Options to pass
+ * @param {boolean} [options.lockPermissions=true] Lock the permissions to what the parent's permissions are
+ * @param {string} [options.reason] Reason for modifying the parent of this channel
+ * @returns {Promise<GuildChannel>}
+ */
+}
+
+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..1dc7ee0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Channel.js
@@ -0,0 +1,151 @@
+'use strict';
+
+const Base = require('./Base');
+const { ChannelTypes } = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents any channel on Discord.
+ * @extends {Base}
+ */
+class Channel extends Base {
+ constructor(client, data) {
+ super(client);
+
+ const type = Object.keys(ChannelTypes)[data.type];
+ /**
+ * The type of the channel, either:
+ * * `dm` - a 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
+ * * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel
+ * @type {string}
+ */
+ this.type = type ? type.toLowerCase() : 'unknown';
+
+ /**
+ * Whether the channel has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this._patch(data);
+ }
+
+ _patch(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 at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * 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}!`);
+ */
+ toString() {
+ return `<#${this.id}>`;
+ }
+
+ /**
+ * Deletes this channel.
+ * @returns {Promise<Channel>}
+ * @example
+ * // Delete the channel
+ * channel.delete()
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ delete() {
+ return this.client.api
+ .channels(this.id)
+ .delete()
+ .then(() => this);
+ }
+
+ /**
+ * Fetches this channel.
+ * @returns {Promise<Channel>}
+ */
+ fetch() {
+ return this.client.channels.fetch(this.id, true);
+ }
+
+ static create(client, data, guild) {
+ const Structures = require('../util/Structures');
+ let channel;
+ if (!data.guild_id && !guild) {
+ if ((data.recipients && data.type !== ChannelTypes.GROUP) || data.type === ChannelTypes.DM) {
+ const DMChannel = Structures.get('DMChannel');
+ channel = new DMChannel(client, data);
+ } else if (data.type === ChannelTypes.GROUP) {
+ const PartialGroupDMChannel = require('./PartialGroupDMChannel');
+ channel = new PartialGroupDMChannel(client, data);
+ }
+ } else {
+ guild = guild || client.guilds.cache.get(data.guild_id);
+ if (guild) {
+ switch (data.type) {
+ case ChannelTypes.TEXT: {
+ const TextChannel = Structures.get('TextChannel');
+ channel = new TextChannel(guild, data);
+ break;
+ }
+ case ChannelTypes.VOICE: {
+ const VoiceChannel = Structures.get('VoiceChannel');
+ channel = new VoiceChannel(guild, data);
+ break;
+ }
+ case ChannelTypes.CATEGORY: {
+ const CategoryChannel = Structures.get('CategoryChannel');
+ channel = new CategoryChannel(guild, data);
+ break;
+ }
+ case ChannelTypes.NEWS: {
+ const NewsChannel = Structures.get('NewsChannel');
+ channel = new NewsChannel(guild, data);
+ break;
+ }
+ case ChannelTypes.STORE: {
+ const StoreChannel = Structures.get('StoreChannel');
+ channel = new StoreChannel(guild, data);
+ break;
+ }
+ }
+ if (channel) guild.channels.cache.set(channel.id, channel);
+ }
+ }
+ return channel;
+ }
+
+ toJSON(...props) {
+ return super.toJSON({ createdTimestamp: true }, ...props);
+ }
+}
+
+module.exports = Channel;
diff --git a/node_modules/discord.js/src/structures/ClientApplication.js b/node_modules/discord.js/src/structures/ClientApplication.js
new file mode 100644
index 0000000..753d90d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientApplication.js
@@ -0,0 +1,156 @@
+'use strict';
+
+const Base = require('./Base');
+const Team = require('./Team');
+const { ClientApplicationAssetTypes, Endpoints } = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+
+const AssetTypes = Object.keys(ClientApplicationAssetTypes);
+
+/**
+ * Represents a Client OAuth2 Application.
+ * @extends {Base}
+ */
+class ClientApplication extends Base {
+ constructor(client, data) {
+ super(client);
+ this._patch(data);
+ }
+
+ _patch(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 cover image
+ * @type {?string}
+ */
+ this.cover = data.cover_image || null;
+
+ /**
+ * The app's RPC origins, if enabled
+ * @type {string[]}
+ */
+ this.rpcOrigins = data.rpc_origins || [];
+
+ /**
+ * If this app's bot requires a code grant when using the OAuth2 flow
+ * @type {?boolean}
+ */
+ this.botRequireCodeGrant = typeof data.bot_require_code_grant !== 'undefined' ? data.bot_require_code_grant : null;
+
+ /**
+ * If this app's bot is public
+ * @type {?boolean}
+ */
+ this.botPublic = typeof data.bot_public !== 'undefined' ? data.bot_public : null;
+
+ /**
+ * The owner of this OAuth application
+ * @type {?User|Team}
+ */
+ this.owner = data.team ? new Team(this.client, data.team) : data.owner ? this.client.users.add(data.owner) : 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 at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * A link to the application's icon.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string} URL to the icon
+ */
+ iconURL({ format, size } = {}) {
+ if (!this.icon) return null;
+ return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size });
+ }
+
+ /**
+ * A link to this application's cover image.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string} URL to the cover image
+ */
+ coverImage({ format, size } = {}) {
+ if (!this.cover) return null;
+ return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size });
+ }
+
+ /**
+ * Asset data.
+ * @typedef {Object} ClientAsset
+ * @property {Snowflake} id The asset ID
+ * @property {string} name The asset name
+ * @property {string} type The asset type
+ */
+
+ /**
+ * Gets the clients rich presence assets.
+ * @returns {Promise<Array<ClientAsset>>}
+ */
+ fetchAssets() {
+ return this.client.api.oauth2
+ .applications(this.id)
+ .assets.get()
+ .then(assets =>
+ assets.map(a => ({
+ id: a.id,
+ name: a.name,
+ type: AssetTypes[a.type - 1],
+ })),
+ );
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the application's name instead of the
+ * ClientApplication object.
+ * @returns {string}
+ * @example
+ * // Logs: Application name: My App
+ * console.log(`Application name: ${application}`);
+ */
+ toString() {
+ return this.name;
+ }
+
+ toJSON() {
+ return super.toJSON({ createdTimestamp: true });
+ }
+}
+
+module.exports = ClientApplication;
diff --git a/node_modules/discord.js/src/structures/ClientPresence.js b/node_modules/discord.js/src/structures/ClientPresence.js
new file mode 100644
index 0000000..a39ba00
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientPresence.js
@@ -0,0 +1,87 @@
+'use strict';
+
+const { Presence } = require('./Presence');
+const { TypeError } = require('../errors');
+const Collection = require('../util/Collection');
+const { ActivityTypes, OPCodes } = require('../util/Constants');
+
+class ClientPresence extends Presence {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} [data={}] The data for the client presence
+ */
+ constructor(client, data = {}) {
+ super(client, Object.assign(data, { status: 'online', user: { id: null } }));
+ }
+
+ async set(presence) {
+ const packet = await this._parse(presence);
+ this.patch(packet);
+ if (typeof presence.shardID === 'undefined') {
+ this.client.ws.broadcast({ op: OPCodes.STATUS_UPDATE, d: packet });
+ } else if (Array.isArray(presence.shardID)) {
+ for (const shardID of presence.shardID) {
+ this.client.ws.shards.get(shardID).send({ op: OPCodes.STATUS_UPDATE, d: packet });
+ }
+ } else {
+ this.client.ws.shards.get(presence.shardID).send({ op: OPCodes.STATUS_UPDATE, d: packet });
+ }
+ return this;
+ }
+
+ async _parse({ status, since, afk, activity }) {
+ const applicationID = activity && (activity.application ? activity.application.id || activity.application : null);
+ let assets = new Collection();
+ if (activity) {
+ if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', 'name', 'string');
+ if (!activity.type) activity.type = 0;
+ if (activity.assets && applicationID) {
+ try {
+ const a = await this.client.api.oauth2.applications(applicationID).assets.get();
+ for (const asset of a) assets.set(asset.name, asset.id);
+ } catch {} // eslint-disable-line no-empty
+ }
+ }
+
+ const packet = {
+ afk: afk != null ? afk : false, // eslint-disable-line eqeqeq
+ since: since != null ? since : null, // eslint-disable-line eqeqeq
+ status: status || this.status,
+ game: activity
+ ? {
+ type: activity.type,
+ name: activity.name,
+ url: activity.url,
+ details: activity.details || undefined,
+ state: activity.state || undefined,
+ assets: activity.assets
+ ? {
+ large_text: activity.assets.largeText || undefined,
+ small_text: activity.assets.smallText || undefined,
+ large_image: assets.get(activity.assets.largeImage) || activity.assets.largeImage,
+ small_image: assets.get(activity.assets.smallImage) || activity.assets.smallImage,
+ }
+ : undefined,
+ timestamps: activity.timestamps || undefined,
+ party: activity.party || undefined,
+ application_id: applicationID || undefined,
+ secrets: activity.secrets || undefined,
+ instance: activity.instance || undefined,
+ }
+ : null,
+ };
+
+ if ((status || afk || since) && !activity) {
+ packet.game = this.activities[0] || null;
+ }
+
+ if (packet.game) {
+ packet.game.type =
+ typeof packet.game.type === 'number' ? packet.game.type : ActivityTypes.indexOf(packet.game.type);
+ }
+
+ return packet;
+ }
+}
+
+module.exports = ClientPresence;
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..84ada54
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ClientUser.js
@@ -0,0 +1,180 @@
+'use strict';
+
+const DataResolver = require('../util/DataResolver');
+const Structures = require('../util/Structures');
+
+/**
+ * Represents the logged in client's Discord user.
+ * @extends {User}
+ */
+class ClientUser extends Structures.get('User') {
+ constructor(client, data) {
+ super(client, data);
+ this._typing = new Map();
+ }
+
+ _patch(data) {
+ super._patch(data);
+
+ if ('verified' in data) {
+ /**
+ * Whether or not this account has been verified
+ * @type {boolean}
+ */
+ this.verified = data.verified;
+ }
+
+ if ('mfa_enabled' in data) {
+ /**
+ * If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account
+ * @type {?boolean}
+ */
+ this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null;
+ } else if (typeof this.mfaEnabled === 'undefined') {
+ this.mfaEnabled = null;
+ }
+
+ if (data.token) this.client.token = data.token;
+ }
+
+ /**
+ * ClientUser's presence
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ return this.client.presence;
+ }
+
+ edit(data) {
+ return this.client.api
+ .users('@me')
+ .patch({ data })
+ .then(newData => {
+ this.client.token = newData.token;
+ const { updated } = this.client.actions.UserUpdate.handle(newData);
+ if (updated) return updated;
+ return this;
+ });
+ }
+
+ /**
+ * Sets 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
+ * @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) {
+ return this.edit({ username });
+ }
+
+ /**
+ * Sets 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);
+ */
+ async setAvatar(avatar) {
+ return this.edit({ avatar: await DataResolver.resolveImage(avatar) });
+ }
+
+ /**
+ * Data resembling a raw Discord presence.
+ * @typedef {Object} PresenceData
+ * @property {PresenceStatusData} [status] Status of the user
+ * @property {boolean} [afk] Whether the user is AFK
+ * @property {Object} [activity] Activity the user is playing
+ * @property {Object|string} [activity.application] An application object or application id
+ * @property {string} [activity.application.id] The id of the application
+ * @property {string} [activity.name] Name of the activity
+ * @property {ActivityType|number} [activity.type] Type of the activity
+ * @property {string} [activity.url] Stream url
+ * @property {?number|number[]} [shardID] Shard Id(s) to have the activity set on
+ */
+
+ /**
+ * Sets the full presence of the client user.
+ * @param {PresenceData} data Data for the presence
+ * @returns {Promise<Presence>}
+ * @example
+ * // Set the client user's presence
+ * client.user.setPresence({ activity: { name: 'with discord.js' }, status: 'idle' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setPresence(data) {
+ return this.client.presence.set(data);
+ }
+
+ /**
+ * A user's status. Must be one of:
+ * * `online`
+ * * `idle`
+ * * `invisible`
+ * * `dnd` (do not disturb)
+ * @typedef {string} PresenceStatusData
+ */
+
+ /**
+ * Sets the status of the client user.
+ * @param {PresenceStatusData} status Status to change to
+ * @param {?number|number[]} [shardID] Shard ID(s) to have the activity set on
+ * @returns {Promise<Presence>}
+ * @example
+ * // Set the client user's status
+ * client.user.setStatus('idle')
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ setStatus(status, shardID) {
+ return this.setPresence({ status, shardID });
+ }
+
+ /**
+ * Options for setting an activity
+ * @typedef ActivityOptions
+ * @type {Object}
+ * @property {string} [url] Twitch stream URL
+ * @property {ActivityType|number} [type] Type of the activity
+ * @property {?number|number[]} [shardID] Shard Id(s) to have the activity set on
+ */
+
+ /**
+ * Sets the activity the client user is playing.
+ * @param {string|ActivityOptions} [name] Activity being played, or options for setting the activity
+ * @param {ActivityOptions} [options] Options for setting the activity
+ * @returns {Promise<Presence>}
+ * @example
+ * // Set the client user's activity
+ * client.user.setActivity('discord.js', { type: 'WATCHING' })
+ * .then(presence => console.log(`Activity set to ${presence.activities[0].name}`))
+ * .catch(console.error);
+ */
+ setActivity(name, options = {}) {
+ if (!name) return this.setPresence({ activity: null, shardID: options.shardID });
+
+ const activity = Object.assign({}, options, typeof name === 'object' ? name : { name });
+ return this.setPresence({ activity, shardID: activity.shardID });
+ }
+
+ /**
+ * Sets/removes the AFK flag for the client user.
+ * @param {boolean} afk Whether or not the user is AFK
+ * @returns {Promise<Presence>}
+ */
+ setAFK(afk) {
+ return this.setPresence({ afk });
+ }
+}
+
+module.exports = ClientUser;
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..e661bc4
--- /dev/null
+++ b/node_modules/discord.js/src/structures/DMChannel.js
@@ -0,0 +1,98 @@
+'use strict';
+
+const Channel = require('./Channel');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const MessageManager = require('../managers/MessageManager');
+
+/**
+ * Represents a direct message channel between two users.
+ * @extends {Channel}
+ * @implements {TextBasedChannel}
+ */
+class DMChannel extends Channel {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the DM channel
+ */
+ constructor(client, data) {
+ super(client, data);
+ // Override the channel type so partials have a known type
+ this.type = 'dm';
+ /**
+ * A manager of the messages belonging to this channel
+ * @type {MessageManager}
+ */
+ this.messages = new MessageManager(this);
+ this._typing = new Map();
+ }
+
+ _patch(data) {
+ super._patch(data);
+
+ if (data.recipients) {
+ /**
+ * The recipient on the other end of the DM
+ * @type {User}
+ */
+ this.recipient = this.client.users.add(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;
+ }
+
+ /**
+ * Whether this DMChannel is a partial
+ * @type {boolean}
+ * @readonly
+ */
+ get partial() {
+ return typeof this.lastMessageID === 'undefined';
+ }
+
+ /**
+ * Fetch this DMChannel.
+ * @returns {Promise<DMChannel>}
+ */
+ fetch() {
+ return this.recipient.createDM();
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the recipient's mention instead of the
+ * DMChannel object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <@123456789012345678>!
+ * console.log(`Hello from ${channel}!`);
+ */
+ toString() {
+ return this.recipient.toString();
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastMessage() {}
+ get lastPinAt() {}
+ send() {}
+ startTyping() {}
+ stopTyping() {}
+ get typing() {}
+ get typingCount() {}
+ createMessageCollector() {}
+ awaitMessages() {}
+ // Doesn't work on DM channels; bulkDelete() {}
+}
+
+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..0214ea8
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Emoji.js
@@ -0,0 +1,104 @@
+'use strict';
+
+const Base = require('./Base');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents an emoji, see {@link GuildEmoji} and {@link ReactionEmoji}.
+ * @extends {Base}
+ */
+class Emoji extends Base {
+ constructor(client, emoji) {
+ super(client);
+ /**
+ * Whether this emoji is animated
+ * @type {boolean}
+ */
+ this.animated = emoji.animated;
+
+ /**
+ * The name of this emoji
+ * @type {string}
+ */
+ this.name = emoji.name;
+
+ /**
+ * The ID of this emoji
+ * @type {?Snowflake}
+ */
+ this.id = emoji.id;
+
+ /**
+ * Whether this emoji has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+ }
+
+ /**
+ * The identifier of this emoji, used for message reactions
+ * @type {string}
+ * @readonly
+ */
+ get identifier() {
+ if (this.id) return `${this.animated ? 'a:' : ''}${this.name}:${this.id}`;
+ return encodeURIComponent(this.name);
+ }
+
+ /**
+ * The URL to the emoji file if its a custom emoji
+ * @type {?string}
+ * @readonly
+ */
+ get url() {
+ if (!this.id) return null;
+ return this.client.rest.cdn.Emoji(this.id, this.animated ? 'gif' : 'png');
+ }
+
+ /**
+ * The timestamp the 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 emoji was created at, or null if unicode
+ * @type {?Date}
+ * @readonly
+ */
+ get createdAt() {
+ if (!this.id) return null;
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the text required to form a graphical emoji on Discord
+ * instead of the Emoji object.
+ * @returns {string}
+ * @example
+ * // Send a custom emoji from a guild:
+ * const emoji = guild.emojis.cache.first();
+ * msg.reply(`Hello! ${emoji}`);
+ * @example
+ * // Send the emoji used in a reaction to the channel the reaction is part of
+ * reaction.message.channel.send(`The emoji used was: ${reaction.emoji}`);
+ */
+ toString() {
+ return this.id ? `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>` : this.name;
+ }
+
+ toJSON() {
+ return super.toJSON({
+ guild: 'guildID',
+ createdTimestamp: true,
+ url: true,
+ identifier: true,
+ });
+ }
+}
+
+module.exports = Emoji;
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..e2bc8c4
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Guild.js
@@ -0,0 +1,1349 @@
+'use strict';
+
+const Base = require('./Base');
+const GuildAuditLogs = require('./GuildAuditLogs');
+const GuildPreview = require('./GuildPreview');
+const Integration = require('./Integration');
+const Invite = require('./Invite');
+const VoiceRegion = require('./VoiceRegion');
+const Webhook = require('./Webhook');
+const GuildChannelManager = require('../managers/GuildChannelManager');
+const GuildEmojiManager = require('../managers/GuildEmojiManager');
+const GuildMemberManager = require('../managers/GuildMemberManager');
+const PresenceManager = require('../managers/PresenceManager');
+const RoleManager = require('../managers/RoleManager');
+const VoiceStateManager = require('../managers/VoiceStateManager');
+const Collection = require('../util/Collection');
+const {
+ ChannelTypes,
+ DefaultMessageNotifications,
+ PartialTypes,
+ VerificationLevels,
+ ExplicitContentFilterLevels,
+} = require('../util/Constants');
+const DataResolver = require('../util/DataResolver');
+const Snowflake = require('../util/Snowflake');
+const SystemChannelFlags = require('../util/SystemChannelFlags');
+const Util = require('../util/Util');
+
+/**
+ * Represents a guild (or a server) on Discord.
+ * <info>It's recommended to see if a guild is available before performing operations or reading data from it. You can
+ * check this with `guild.available`.</info>
+ * @extends {Base}
+ */
+class Guild extends Base {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the guild
+ */
+ constructor(client, data) {
+ super(client);
+
+ /**
+ * A manager of the members belonging to this guild
+ * @type {GuildMemberManager}
+ */
+ this.members = new GuildMemberManager(this);
+
+ /**
+ * A manager of the channels belonging to this guild
+ * @type {GuildChannelManager}
+ */
+ this.channels = new GuildChannelManager(this);
+
+ /**
+ * A manager of the roles belonging to this guild
+ * @type {RoleManager}
+ */
+ this.roles = new RoleManager(this);
+
+ /**
+ * A manager of the presences belonging to this guild
+ * @type {PresenceManager}
+ */
+ this.presences = new PresenceManager(this.client);
+
+ /**
+ * A manager of the voice states of this guild
+ * @type {VoiceStateManager}
+ */
+ this.voiceStates = new VoiceStateManager(this);
+
+ /**
+ * 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._patch(data);
+ if (!data.channels) this.available = false;
+ }
+
+ /**
+ * The id of the shard this Guild belongs to.
+ * @type {number}
+ */
+ this.shardID = data.shardID;
+ }
+
+ /**
+ * The Shard this Guild belongs to.
+ * @type {WebSocketShard}
+ * @readonly
+ */
+ get shard() {
+ return this.client.ws.shards.get(this.shardID);
+ }
+
+ /**
+ * Sets up the guild.
+ * @param {*} data The raw data of the guild
+ * @private
+ */
+ _patch(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 enabled guild features, here are the possible values:
+ * * ANIMATED_ICON
+ * * BANNER
+ * * COMMERCE
+ * * DISCOVERABLE
+ * * FEATURABLE
+ * * INVITE_SPLASH
+ * * NEWS
+ * * PARTNERED
+ * * PUBLIC
+ * * PUBLIC_DISABLED
+ * * VANITY_URL
+ * * VERIFIED
+ * * VIP_REGIONS
+ * * WELCOME_SCREEN_ENABLED
+ * @typedef {string} Features
+ */
+
+ /**
+ * An array of guild features partnered guilds have enabled
+ * @type {Features[]}
+ */
+ 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 {?Snowflake}
+ */
+ 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 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;
+ }
+
+ /**
+ * 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 embed channel ID, if enabled
+ * @type {?string}
+ * @name Guild#embedChannelID
+ */
+ if (typeof data.embed_channel_id !== 'undefined') this.embedChannelID = data.embed_channel_id;
+
+ /**
+ * The verification level of the guild
+ * @type {VerificationLevel}
+ */
+ this.verificationLevel = VerificationLevels[data.verification_level];
+
+ /**
+ * The explicit content filter level of the guild
+ * @type {ExplicitContentFilterLevel}
+ */
+ this.explicitContentFilter = ExplicitContentFilterLevels[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 the guild's default message notifications
+ * @type {DefaultMessageNotifications|number}
+ */
+ this.defaultMessageNotifications =
+ DefaultMessageNotifications[data.default_message_notifications] || data.default_message_notifications;
+
+ /**
+ * The value set for the guild's system channel flags
+ * @type {Readonly<SystemChannelFlags>}
+ */
+ this.systemChannelFlags = new SystemChannelFlags(data.system_channel_flags).freeze();
+
+ /**
+ * 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 || 25000;
+
+ /**
+ * The vanity URL code of the guild, if any
+ * @type {?string}
+ */
+ this.vanityURLCode = data.vanity_url_code;
+
+ /**
+ * The description of the guild, if any
+ * @type {?string}
+ */
+ this.description = data.description;
+
+ /**
+ * The hash of the guild banner
+ * @type {?string}
+ */
+ this.banner = data.banner;
+
+ 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.channels) {
+ this.channels.cache.clear();
+ for (const rawChannel of data.channels) {
+ this.client.channels.add(rawChannel, this);
+ }
+ }
+
+ if (data.roles) {
+ this.roles.cache.clear();
+ for (const role of data.roles) this.roles.add(role);
+ }
+
+ if (data.members) {
+ this.members.cache.clear();
+ for (const guildUser of data.members) this.members.add(guildUser);
+ }
+
+ if (data.owner_id) {
+ /**
+ * The user ID of this guild's owner
+ * @type {Snowflake}
+ */
+ this.ownerID = data.owner_id;
+ }
+
+ if (data.presences) {
+ for (const presence of data.presences) {
+ this.presences.add(Object.assign(presence, { guild: this }));
+ }
+ }
+
+ if (data.voice_states) {
+ this.voiceStates.cache.clear();
+ for (const voiceState of data.voice_states) {
+ this.voiceStates.add(voiceState);
+ }
+ }
+
+ if (!this.emojis) {
+ /**
+ * A manager of the emojis belonging to this guild
+ * @type {GuildEmojiManager}
+ */
+ this.emojis = new GuildEmojiManager(this);
+ if (data.emojis) for (const emoji of data.emojis) this.emojis.add(emoji);
+ } else if (data.emojis) {
+ this.client.actions.GuildEmojisUpdate.handle({
+ guild_id: this.id,
+ emojis: data.emojis,
+ });
+ }
+ }
+
+ /**
+ * The URL to this guild's banner.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ bannerURL({ format, size } = {}) {
+ if (!this.banner) return null;
+ return this.client.rest.cdn.Banner(this.id, this.banner, format, size);
+ }
+
+ /**
+ * The timestamp the guild was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the guild was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The time the client user joined the guild
+ * @type {Date}
+ * @readonly
+ */
+ get joinedAt() {
+ return new Date(this.joinedTimestamp);
+ }
+
+ /**
+ * If this guild is partnered
+ * @type {boolean}
+ * @readonly
+ */
+ get partnered() {
+ return this.features.includes('PARTNERED');
+ }
+
+ /**
+ * If this guild is verified
+ * @type {boolean}
+ * @readonly
+ */
+ get verified() {
+ return this.features.includes('VERIFIED');
+ }
+
+ /**
+ * The URL to this guild's icon.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ iconURL({ format, size, dynamic } = {}) {
+ if (!this.icon) return null;
+ return this.client.rest.cdn.Icon(this.id, this.icon, format, size, dynamic);
+ }
+
+ /**
+ * 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.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ splashURL({ format, size } = {}) {
+ if (!this.splash) return null;
+ return this.client.rest.cdn.Splash(this.id, this.splash, format, size);
+ }
+
+ /**
+ * The owner of the guild
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get owner() {
+ return (
+ this.members.cache.get(this.ownerID) ||
+ (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER)
+ ? this.members.add({ user: { id: this.ownerID } }, true)
+ : null)
+ );
+ }
+
+ /**
+ * AFK voice channel for this guild
+ * @type {?VoiceChannel}
+ * @readonly
+ */
+ get afkChannel() {
+ return this.client.channels.cache.get(this.afkChannelID) || null;
+ }
+
+ /**
+ * System channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get systemChannel() {
+ return this.client.channels.cache.get(this.systemChannelID) || null;
+ }
+
+ /**
+ * Widget channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get widgetChannel() {
+ return this.client.channels.cache.get(this.widgetChannelID) || null;
+ }
+
+ /**
+ * Embed channel for this guild
+ * @type {?TextChannel}
+ * @readonly
+ */
+ get embedChannel() {
+ return this.client.channels.cache.get(this.embedChannelID) || null;
+ }
+
+ /**
+ * 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.cache.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.cache.get(this.publicUpdatesChannelID) || null;
+ }
+
+ /**
+ * The client user as a GuildMember of this guild
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get me() {
+ return (
+ this.members.cache.get(this.client.user.id) ||
+ (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER)
+ ? this.members.add({ user: { id: this.client.user.id } }, true)
+ : null)
+ );
+ }
+
+ /**
+ * The voice state for the client user of this guild, if any
+ * @type {?VoiceState}
+ * @readonly
+ */
+ get voice() {
+ return this.voiceStates.cache.get(this.client.user.id);
+ }
+
+ /**
+ * 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.members.resolve(user);
+ }
+
+ /**
+ * Fetches this guild.
+ * @returns {Promise<Guild>}
+ */
+ fetch() {
+ return this.client.api
+ .guilds(this.id)
+ .get()
+ .then(data => {
+ this._patch(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
+ */
+
+ /**
+ * Fetches information on a banned user from this guild.
+ * @param {UserResolvable} user The User to fetch the ban info of
+ * @returns {Promise<BanInfo>}
+ */
+ fetchBan(user) {
+ const id = this.client.users.resolveID(user);
+ if (!id) throw new Error('FETCH_BAN_RESOLVE_ID');
+ return this.client.api
+ .guilds(this.id)
+ .bans(id)
+ .get()
+ .then(ban => ({
+ reason: ban.reason,
+ user: this.client.users.add(ban.user),
+ }));
+ }
+
+ /**
+ * Fetches a collection of banned users in this guild.
+ * @returns {Promise<Collection<Snowflake, BanInfo>>}
+ */
+ fetchBans() {
+ return this.client.api
+ .guilds(this.id)
+ .bans.get()
+ .then(bans =>
+ bans.reduce((collection, ban) => {
+ collection.set(ban.user.id, {
+ reason: ban.reason,
+ user: this.client.users.add(ban.user),
+ });
+ return collection;
+ }, new Collection()),
+ );
+ }
+
+ /**
+ * 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.api
+ .guilds(this.id)
+ .integrations.get()
+ .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 the integration
+ * @param {string} reason Reason for creating the integration
+ * @returns {Promise<Guild>}
+ */
+ createIntegration(data, reason) {
+ return this.client.api
+ .guilds(this.id)
+ .integrations.post({ data, reason })
+ .then(() => this);
+ }
+
+ /**
+ * Fetches 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.api
+ .guilds(this.id)
+ .invites.get()
+ .then(inviteItems => {
+ const invites = new Collection();
+ for (const inviteItem of inviteItems) {
+ const invite = new Invite(this.client, inviteItem);
+ invites.set(invite.code, invite);
+ }
+ return invites;
+ });
+ }
+
+ /**
+ * Obtains a guild preview for this guild from Discord, only available for public guilds.
+ * @returns {Promise<GuildPreview>}
+ */
+ fetchPreview() {
+ return this.client.api
+ .guilds(this.id)
+ .preview.get()
+ .then(data => new GuildPreview(this.client, data));
+ }
+
+ /**
+ * 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('VANITY_URL'));
+ }
+ return this.client.api
+ .guilds(this.id, 'vanity-url')
+ .get()
+ .then(res => res.code);
+ }
+
+ /**
+ * Fetches 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.api
+ .guilds(this.id)
+ .webhooks.get()
+ .then(data => {
+ const hooks = new Collection();
+ for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
+ return hooks;
+ });
+ }
+
+ /**
+ * Fetches available voice regions.
+ * @returns {Promise<Collection<string, VoiceRegion>>}
+ */
+ fetchVoiceRegions() {
+ return this.client.api
+ .guilds(this.id)
+ .regions.get()
+ .then(res => {
+ const regions = new Collection();
+ for (const region of res) regions.set(region.id, new VoiceRegion(region));
+ return regions;
+ });
+ }
+
+ /**
+ * The Guild Embed object
+ * @typedef {Object} GuildEmbedData
+ * @property {boolean} enabled Whether the embed is enabled
+ * @property {?GuildChannel} 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.api
+ .guilds(this.id)
+ .embed.get()
+ .then(data => ({
+ enabled: data.enabled,
+ channel: data.channel_id ? this.channels.cache.get(data.channel_id) : null,
+ }));
+ }
+
+ /**
+ * Fetches 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 {number} [options.limit] Limit number of entries
+ * @param {UserResolvable} [options.user] Only show entries involving this user
+ * @param {AuditLogAction|number} [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 = {}) {
+ if (options.before && options.before instanceof GuildAuditLogs.Entry) options.before = options.before.id;
+ if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type];
+
+ return this.client.api
+ .guilds(this.id)
+ ['audit-logs'].get({
+ query: {
+ before: options.before,
+ limit: options.limit,
+ user_id: this.client.users.resolveID(options.user),
+ action_type: options.type,
+ },
+ })
+ .then(data => GuildAuditLogs.build(this, data));
+ }
+
+ /**
+ * 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>|RoleResolvable[]} [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.users.resolveID(user);
+ if (!user) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable'));
+ if (this.members.cache.has(user)) return Promise.resolve(this.members.cache.get(user));
+ options.access_token = options.accessToken;
+ if (options.roles) {
+ const roles = [];
+ for (let role of options.roles instanceof Collection ? options.roles.values() : options.roles) {
+ role = this.roles.resolve(role);
+ if (!role) {
+ return Promise.reject(
+ new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true),
+ );
+ }
+ roles.push(role.id);
+ }
+ options.roles = roles;
+ }
+ return this.client.api
+ .guilds(this.id)
+ .members(user)
+ .put({ data: options })
+ .then(data => this.members.add(data));
+ }
+
+ /**
+ * 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 {VerificationLevel|number} [verificationLevel] The verification level of the guild
+ * @property {ExplicitContentFilterLevel|number} [explicitContentFilter] The level of the explicit content filter
+ * @property {ChannelResolvable} [afkChannel] The AFK channel of the guild
+ * @property {ChannelResolvable} [systemChannel] The system channel of the guild
+ * @property {number} [afkTimeout] The AFK timeout of the guild
+ * @property {Base64Resolvable} [icon] The icon of the guild
+ * @property {GuildMemberResolvable} [owner] The owner of the guild
+ * @property {Base64Resolvable} [splash] The splash screen of the guild
+ * @property {Base64Resolvable} [banner] The banner of the guild
+ * @property {DefaultMessageNotifications|number} [defaultMessageNotifications] The default message notifications
+ * @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 this guild
+ * @returns {Promise<Guild>}
+ * @example
+ * // Set the guild name and region
+ * guild.edit({
+ * name: 'Discord Guild',
+ * region: 'london',
+ * })
+ * .then(updated => console.log(`New guild name ${updated} in region ${updated.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 =
+ typeof data.verificationLevel === 'number'
+ ? Number(data.verificationLevel)
+ : VerificationLevels.indexOf(data.verificationLevel);
+ }
+ if (typeof data.afkChannel !== 'undefined') {
+ _data.afk_channel_id = this.client.channels.resolveID(data.afkChannel);
+ }
+ if (typeof data.systemChannel !== 'undefined') {
+ _data.system_channel_id = this.client.channels.resolveID(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.users.resolveID(data.owner);
+ if (data.splash) _data.splash = data.splash;
+ if (data.banner) _data.banner = data.banner;
+ if (typeof data.explicitContentFilter !== 'undefined') {
+ _data.explicit_content_filter =
+ typeof data.explicitContentFilter === 'number'
+ ? data.explicitContentFilter
+ : ExplicitContentFilterLevels.indexOf(data.explicitContentFilter);
+ }
+ if (typeof data.defaultMessageNotifications !== 'undefined') {
+ _data.default_message_notifications =
+ typeof data.defaultMessageNotifications === 'string'
+ ? DefaultMessageNotifications.indexOf(data.defaultMessageNotifications)
+ : data.defaultMessageNotifications;
+ }
+ if (typeof data.systemChannelFlags !== 'undefined') {
+ _data.system_channel_flags = SystemChannelFlags.resolve(data.systemChannelFlags);
+ }
+ return this.client.api
+ .guilds(this.id)
+ .patch({ data: _data, reason })
+ .then(newData => this.client.actions.GuildUpdate.handle(newData).updated);
+ }
+
+ /**
+ * Edits the level of the explicit content filter.
+ * @param {ExplicitContentFilterLevel|number} explicitContentFilter The new level of the explicit content filter
+ * @param {string} [reason] Reason for changing the level of the guild's explicit content filter
+ * @returns {Promise<Guild>}
+ */
+ setExplicitContentFilter(explicitContentFilter, reason) {
+ return this.edit({ explicitContentFilter }, reason);
+ }
+
+ /* eslint-disable max-len */
+ /**
+ * 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);
+ }
+ /* eslint-enable max-len */
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Edits 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(updated => console.log(`Updated guild name to ${guild}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Edits 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(updated => console.log(`Updated guild region to ${updated.region}`))
+ * .catch(console.error);
+ */
+ setRegion(region, reason) {
+ return this.edit({ region }, reason);
+ }
+
+ /**
+ * Edits the verification level of the guild.
+ * @param {VerificationLevel|number} verificationLevel The new verification level of the guild
+ * @param {string} [reason] Reason for changing the guild's verification level
+ * @returns {Promise<Guild>}
+ * @example
+ * // Edit the guild verification level
+ * guild.setVerificationLevel(1)
+ * .then(updated => console.log(`Updated guild verification level to ${guild.verificationLevel}`))
+ * .catch(console.error);
+ */
+ setVerificationLevel(verificationLevel, reason) {
+ return this.edit({ verificationLevel }, reason);
+ }
+
+ /**
+ * Edits 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(updated => console.log(`Updated guild AFK channel to ${guild.afkChannel.name}`))
+ * .catch(console.error);
+ */
+ setAFKChannel(afkChannel, reason) {
+ return this.edit({ afkChannel }, reason);
+ }
+
+ /**
+ * Edits 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>}
+ * @example
+ * // Edit the guild system channel
+ * guild.setSystemChannel(channel)
+ * .then(updated => console.log(`Updated guild system channel to ${guild.systemChannel.name}`))
+ * .catch(console.error);
+ */
+ setSystemChannel(systemChannel, reason) {
+ return this.edit({ systemChannel }, reason);
+ }
+
+ /**
+ * Edits 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(updated => console.log(`Updated guild AFK timeout to ${guild.afkTimeout}`))
+ * .catch(console.error);
+ */
+ setAFKTimeout(afkTimeout, reason) {
+ return this.edit({ afkTimeout }, reason);
+ }
+
+ /**
+ * Sets 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(updated => console.log('Updated the guild icon'))
+ * .catch(console.error);
+ */
+ async setIcon(icon, reason) {
+ return this.edit({ icon: await DataResolver.resolveImage(icon), 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.cache.first())
+ * .then(updated => console.log(`Updated the guild owner to ${updated.owner.displayName}`))
+ * .catch(console.error);
+ */
+ setOwner(owner, reason) {
+ return this.edit({ owner }, reason);
+ }
+
+ /**
+ * Sets a new guild splash screen.
+ * @param {Base64Resolvable|BufferResolvable} 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(updated => console.log('Updated the guild splash'))
+ * .catch(console.error);
+ */
+ async setSplash(splash, reason) {
+ return this.edit({ splash: await DataResolver.resolveImage(splash), reason });
+ }
+
+ /**
+ * Sets a new guild banner.
+ * @param {Base64Resolvable|BufferResolvable} banner The new banner of the guild
+ * @param {string} [reason] Reason for changing the guild's banner
+ * @returns {Promise<Guild>}
+ * @example
+ * guild.setBanner('./banner.png')
+ * .then(updated => console.log('Updated the guild banner'))
+ * .catch(console.error);
+ */
+ async setBanner(banner, reason) {
+ return this.edit({ banner: await DataResolver.resolveImage(banner), reason });
+ }
+
+ /**
+ * 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.setChannelPositions([{ channel: channelID, position: newChannelIndex }])
+ * .then(guild => console.log(`Updated channel positions for ${guild}`))
+ * .catch(console.error);
+ */
+ setChannelPositions(channelPositions) {
+ const updatedChannels = channelPositions.map(r => ({
+ id: this.client.channels.resolveID(r.channel),
+ position: r.position,
+ }));
+
+ return this.client.api
+ .guilds(this.id)
+ .channels.patch({ data: updatedChannels })
+ .then(
+ () =>
+ this.client.actions.GuildChannelsPositionUpdate.handle({
+ guild_id: this.id,
+ channels: updatedChannels,
+ }).guild,
+ );
+ }
+
+ /**
+ * The data needed for updating a guild role's position
+ * @typedef {Object} GuildRolePosition
+ * @property {RoleResolveable} role The ID of the role
+ * @property {number} position The position to update
+ */
+
+ /**
+ * Batch-updates the guild's role positions
+ * @param {GuildRolePosition[]} rolePositions Role positions to update
+ * @returns {Promise<Guild>}
+ * @example
+ * guild.setRolePositions([{ role: roleID, position: updatedRoleIndex }])
+ * .then(guild => console.log(`Role permissions updated for ${guild}`))
+ * .catch(console.error);
+ */
+ setRolePositions(rolePositions) {
+ // Make sure rolePositions are prepared for API
+ rolePositions = rolePositions.map(o => ({
+ id: this.roles.resolveID(o.role),
+ position: o.position,
+ }));
+
+ // Call the API to update role positions
+ return this.client.api
+ .guilds(this.id)
+ .roles.patch({
+ data: rolePositions,
+ })
+ .then(
+ () =>
+ this.client.actions.GuildRolesPositionUpdate.handle({
+ guild_id: this.id,
+ roles: rolePositions,
+ }).guild,
+ );
+ }
+
+ /**
+ * 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.api
+ .guilds(this.id)
+ .embed.patch({
+ data: {
+ enabled: embed.enabled,
+ channel_id: this.channels.resolveID(embed.channel),
+ },
+ reason,
+ })
+ .then(() => this);
+ }
+
+ /**
+ * Leaves the guild.
+ * @returns {Promise<Guild>}
+ * @example
+ * // Leave a guild
+ * guild.leave()
+ * .then(g => console.log(`Left the guild ${g}`))
+ * .catch(console.error);
+ */
+ leave() {
+ if (this.ownerID === this.client.user.id) return Promise.reject(new Error('GUILD_OWNED'));
+ return this.client.api
+ .users('@me')
+ .guilds(this.id)
+ .delete()
+ .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild);
+ }
+
+ /**
+ * Deletes 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.api
+ .guilds(this.id)
+ .delete()
+ .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild);
+ }
+
+ /**
+ * 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 &&
+ guild instanceof this.constructor &&
+ this.id === guild.id &&
+ this.available === guild.available &&
+ this.splash === guild.splash &&
+ this.region === guild.region &&
+ this.name === guild.name &&
+ this.memberCount === guild.memberCount &&
+ this.large === guild.large &&
+ this.icon === guild.icon &&
+ this.ownerID === guild.ownerID &&
+ this.verificationLevel === guild.verificationLevel &&
+ this.embedEnabled === guild.embedEnabled &&
+ (this.features === guild.features ||
+ (this.features.length === guild.features.length &&
+ this.features.every((feat, i) => feat === guild.features[i])));
+
+ if (equal) {
+ if (this.embedChannel) {
+ if (!guild.embedChannel || this.embedChannel.id !== guild.embedChannel.id) equal = false;
+ } else if (guild.embedChannel) {
+ equal = false;
+ }
+ }
+
+ return equal;
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the guild's name instead of the Guild object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from My Guild!
+ * console.log(`Hello from ${guild}!`);
+ */
+ toString() {
+ return this.name;
+ }
+
+ toJSON() {
+ const json = super.toJSON({
+ available: false,
+ createdTimestamp: true,
+ nameAcronym: true,
+ presences: false,
+ voiceStates: false,
+ });
+ json.iconURL = this.iconURL();
+ json.splashURL = this.splashURL();
+ json.bannerURL = this.bannerURL();
+ return json;
+ }
+
+ /**
+ * Creates a collection of this guild's roles, sorted by their position and IDs.
+ * @returns {Collection<Role>}
+ * @private
+ */
+ _sortedRoles() {
+ return Util.discordSort(this.roles.cache);
+ }
+
+ /**
+ * Creates a collection of this guild's or a specific category's channels, sorted by their position and IDs.
+ * @param {GuildChannel} [channel] Category to get the channels of
+ * @returns {Collection<GuildChannel>}
+ * @private
+ */
+ _sortedChannels(channel) {
+ const category = channel.type === ChannelTypes.CATEGORY;
+ return Util.discordSort(
+ this.channels.cache.filter(
+ c =>
+ (['text', 'news', 'store'].includes(channel.type)
+ ? ['text', 'news', 'store'].includes(c.type)
+ : c.type === channel.type) &&
+ (category || c.parent === channel.parent),
+ ),
+ );
+ }
+}
+
+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..b3522b0
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildAuditLogs.js
@@ -0,0 +1,509 @@
+'use strict';
+
+const Integration = require('./Integration');
+const Webhook = require('./Webhook');
+const Collection = require('../util/Collection');
+const { PartialTypes } = require('../util/Constants');
+const Snowflake = require('../util/Snowflake');
+const Util = require('../util/Util');
+
+/**
+ * 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.users.add(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|GuildEmoji|Invite|Webhook|Integration} AuditLogEntryTarget
+ */
+
+ /**
+ * Finds the target type from the entry action.
+ * @param {AuditLogAction} target The action target
+ * @returns {AuditLogTargetType}
+ */
+ 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 Targets.UNKNOWN;
+ }
+
+ /**
+ * 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';
+ }
+
+ toJSON() {
+ return Util.flatten(this);
+ }
+}
+
+/**
+ * Audit logs entry.
+ */
+class GuildAuditLogsEntry {
+ 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 presentation
+ * @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.options.partials.includes(PartialTypes.USER)
+ ? guild.client.users.add({ id: data.user_id })
+ : guild.client.users.cache.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.cache.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.cache.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.cache.get(data.options.id) || { id: data.options.id, type: 'member' };
+ break;
+
+ case 'role':
+ this.extra = guild.roles.cache.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.target = 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.options.partials.includes(PartialTypes.USER)
+ ? guild.client.users.add({ id: data.target_id })
+ : guild.client.users.cache.get(data.target_id);
+ } else if (targetType === Targets.GUILD) {
+ this.target = guild.client.guilds.cache.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) {
+ this.target = guild.members.fetch(guild.client.user.id).then(me => {
+ if (me.permissions.has('MANAGE_GUILD')) {
+ const change = this.changes.find(c => c.key === 'code');
+ return guild.fetchInvites().then(invites => {
+ this.target = invites.find(i => i.code === (change.new || change.old));
+ });
+ } else {
+ this.target = this.changes.reduce((o, c) => {
+ o[c.key] = c.new || c.old;
+ return o;
+ }, {});
+ return this.target;
+ }
+ });
+ } 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.cache.get(data.target_id) || { id: data.target_id }
+ : guild.client.users.cache.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`].cache.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 at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ toJSON() {
+ return Util.flatten(this, { createdTimestamp: true });
+ }
+}
+
+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..2e80eca
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildChannel.js
@@ -0,0 +1,609 @@
+'use strict';
+
+const Channel = require('./Channel');
+const Invite = require('./Invite');
+const PermissionOverwrites = require('./PermissionOverwrites');
+const Role = require('./Role');
+const { Error, TypeError } = require('../errors');
+const Collection = require('../util/Collection');
+const Permissions = require('../util/Permissions');
+const Util = require('../util/Util');
+
+/**
+ * Represents a guild channel from any of the following:
+ * - {@link TextChannel}
+ * - {@link VoiceChannel}
+ * - {@link CategoryChannel}
+ * - {@link NewsChannel}
+ * - {@link StoreChannel}
+ * @extends {Channel}
+ */
+class GuildChannel extends Channel {
+ /**
+ * @param {Guild} guild The guild the guild channel is part of
+ * @param {Object} data The data for the guild channel
+ */
+ constructor(guild, data) {
+ super(guild.client, data);
+
+ /**
+ * The guild the channel is in
+ * @type {Guild}
+ */
+ this.guild = guild;
+ }
+
+ _patch(data) {
+ super._patch(data);
+
+ /**
+ * The name of the guild channel
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The raw position of the channel from discord
+ * @type {number}
+ */
+ this.rawPosition = 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 category parent of this channel
+ * @type {?CategoryChannel}
+ * @readonly
+ */
+ get parent() {
+ return this.guild.channels.cache.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.bitfield === value.deny.bitfield &&
+ testVal.allow.bitfield === value.allow.bitfield
+ );
+ });
+ }
+
+ /**
+ * The position of the channel
+ * @type {number}
+ * @readonly
+ */
+ get position() {
+ const sorted = this.guild._sortedChannels(this);
+ return sorted.array().indexOf(sorted.get(this.id));
+ }
+
+ /**
+ * Gets 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 {?Readonly<Permissions>}
+ */
+ permissionsFor(memberOrRole) {
+ const member = this.guild.members.resolve(memberOrRole);
+ if (member) return this.memberPermissions(member);
+ const role = this.guild.roles.resolve(memberOrRole);
+ if (role) return this.rolePermissions(role);
+ return null;
+ }
+
+ overwritesFor(member, verified = false, roles = null) {
+ if (!verified) member = this.guild.members.resolve(member);
+ if (!member) return [];
+
+ roles = roles || member.roles.cache;
+ 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,
+ };
+ }
+
+ /**
+ * Gets the overall set of permissions for a member in this channel, taking into account channel overwrites.
+ * @param {GuildMember} member The member to obtain the overall permissions for
+ * @returns {Readonly<Permissions>}
+ * @private
+ */
+ memberPermissions(member) {
+ if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze();
+
+ const roles = member.roles.cache;
+ 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 {Role} role The role to obtain the overall permissions for
+ * @returns {Readonly<Permissions>}
+ * @private
+ */
+ rolePermissions(role) {
+ if (role.permissions.has(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 role.permissions
+ .remove(everyoneOverwrites ? everyoneOverwrites.deny : 0)
+ .add(everyoneOverwrites ? everyoneOverwrites.allow : 0)
+ .remove(roleOverwrites ? roleOverwrites.deny : 0)
+ .add(roleOverwrites ? roleOverwrites.allow : 0)
+ .freeze();
+ }
+
+ /**
+ * Replaces the permission overwrites in this channel.
+ * @param {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} overwrites
+ * Permission overwrites the channel gets updated with
+ * @param {string} [reason] Reason for updating the channel overwrites
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * channel.overwritePermissions([
+ * {
+ * id: message.author.id,
+ * deny: ['VIEW_CHANNEL'],
+ * },
+ * ], 'Needed to change permissions');
+ */
+ overwritePermissions(overwrites, reason) {
+ if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) {
+ return Promise.reject(
+ new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true),
+ );
+ }
+ return this.edit({ permissionOverwrites: overwrites, reason }).then(() => this);
+ }
+
+ /**
+ * Updates Overwrites for a user or role in this channel. (creates if non-existent)
+ * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
+ * @param {PermissionOverwriteOptions} options The options for the update
+ * @param {string} [reason] Reason for creating/editing this overwrite
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Update or Create permission overwrites for a message author
+ * message.channel.updateOverwrite(message.author, {
+ * SEND_MESSAGES: false
+ * })
+ * .then(channel => console.log(channel.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ */
+ updateOverwrite(userOrRole, options, reason) {
+ userOrRole = this.guild.roles.resolve(userOrRole) || this.client.users.resolve(userOrRole);
+ if (!userOrRole) return Promise.reject(new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role', true));
+
+ const existing = this.permissionOverwrites.get(userOrRole.id);
+ if (existing) return existing.update(options, reason).then(() => this);
+ return this.createOverwrite(userOrRole, options, reason);
+ }
+
+ /**
+ * Overwrites the permissions for a user or role in this channel. (replaces if existent)
+ * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
+ * @param {PermissionOverwriteOptions} options The options for the update
+ * @param {string} [reason] Reason for creating/editing this overwrite
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Create or Replace permissions overwrites for a message author
+ * message.channel.createOverwrite(message.author, {
+ * SEND_MESSAGES: false
+ * })
+ * .then(channel => console.log(channel.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ */
+ createOverwrite(userOrRole, options, reason) {
+ userOrRole = this.guild.roles.resolve(userOrRole) || this.client.users.resolve(userOrRole);
+ if (!userOrRole) return Promise.reject(new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role', true));
+
+ const type = userOrRole instanceof Role ? 'role' : 'member';
+ const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options);
+
+ return this.client.api
+ .channels(this.id)
+ .permissions[userOrRole.id].put({
+ data: { id: userOrRole.id, type, allow: allow.bitfield, deny: deny.bitfield },
+ reason,
+ })
+ .then(() => this);
+ }
+
+ /**
+ * Locks in the permission overwrites from the parent channel.
+ * @returns {Promise<GuildChannel>}
+ */
+ lockPermissions() {
+ if (!this.parent) return Promise.reject(new Error('GUILD_CHANNEL_ORPHAN'));
+ const permissionOverwrites = this.parent.permissionOverwrites.map(overwrite => overwrite.toJSON());
+ return this.edit({ permissionOverwrites });
+ }
+
+ /**
+ * 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.cache.values()) {
+ if (this.permissionsFor(member).has('VIEW_CHANNEL', false)) {
+ members.set(member.id, member);
+ }
+ }
+ return members;
+ }
+
+ /**
+ * The data for a guild channel.
+ * @typedef {Object} ChannelData
+ * @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 voice channel
+ * @property {Snowflake} [parentID] The parent ID of the channel
+ * @property {boolean} [lockPermissions]
+ * Lock the permissions of the channel to what the parent's permissions are
+ * @property {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} [permissionOverwrites]
+ * Permission overwrites for the channel
+ * @property {number} [rateLimitPerUser] The ratelimit per user for the channel in seconds
+ */
+
+ /**
+ * 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);
+ */
+ async edit(data, reason) {
+ if (typeof data.position !== 'undefined') {
+ await Util.setPosition(
+ this,
+ data.position,
+ false,
+ this.guild._sortedChannels(this),
+ this.client.api.guilds(this.guild.id).channels,
+ reason,
+ ).then(updatedChannels => {
+ this.client.actions.GuildChannelsPositionUpdate.handle({
+ guild_id: this.guild.id,
+ channels: updatedChannels,
+ });
+ });
+ }
+
+ const permission_overwrites =
+ data.permissionOverwrites && data.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));
+
+ const newData = await this.client.api.channels(this.id).patch({
+ data: {
+ name: (data.name || this.name).trim(),
+ topic: data.topic,
+ nsfw: data.nsfw,
+ bitrate: data.bitrate || this.bitrate,
+ user_limit: typeof data.userLimit !== 'undefined' ? data.userLimit : this.userLimit,
+ parent_id: data.parentID,
+ lock_permissions: data.lockPermissions,
+ rate_limit_per_user: data.rateLimitPerUser,
+ permission_overwrites,
+ },
+ reason,
+ });
+
+ const clone = this._clone();
+ clone._patch(newData);
+ return clone;
+ }
+
+ /**
+ * Sets 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);
+ }
+
+ /**
+ * Sets the category parent of this channel.
+ * @param {?CategoryChannel|Snowflake} channel Parent channel
+ * @param {Object} [options={}] Options to pass
+ * @param {boolean} [options.lockPermissions=true] Lock the permissions to what the parent's permissions are
+ * @param {string} [options.reason] Reason for modifying the parent of this channel
+ * @returns {Promise<GuildChannel>}
+ * @example
+ * // Add a parent to a channel
+ * message.channel.setParent('355908108431917066', { lockPermissions: false })
+ * .then(channel => console.log(`New parent of ${message.channel.name}: ${channel.name}`))
+ * .catch(console.error);
+ */
+ setParent(channel, { lockPermissions = true, reason } = {}) {
+ return this.edit(
+ {
+ // eslint-disable-next-line no-prototype-builtins
+ parentID: channel !== null ? (channel.hasOwnProperty('id') ? channel.id : channel) : null,
+ lockPermissions,
+ },
+ reason,
+ );
+ }
+
+ /**
+ * Sets 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(newChannel => console.log(`Channel's new topic is ${newChannel.topic}`))
+ * .catch(console.error);
+ */
+ setTopic(topic, reason) {
+ return this.edit({ topic }, reason);
+ }
+
+ /**
+ * Sets a new position for the guild channel.
+ * @param {number} position The new position for the guild channel
+ * @param {Object} [options] Options for setting position
+ * @param {boolean} [options.relative=false] Change the position relative to its current value
+ * @param {string} [options.reason] Reason for changing the position
+ * @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, reason } = {}) {
+ return Util.setPosition(
+ this,
+ position,
+ relative,
+ this.guild._sortedChannels(this),
+ this.client.api.guilds(this.guild.id).channels,
+ reason,
+ ).then(updatedChannels => {
+ this.client.actions.GuildChannelsPositionUpdate.handle({
+ guild_id: this.guild.id,
+ channels: updatedChannels,
+ });
+ return this;
+ });
+ }
+
+ /**
+ * Creates an invite to this guild channel.
+ * @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} [options.reason] Reason for creating this
+ * @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({ temporary = false, maxAge = 86400, maxUses = 0, unique, reason } = {}) {
+ return this.client.api
+ .channels(this.id)
+ .invites.post({
+ data: {
+ temporary,
+ max_age: maxAge,
+ max_uses: maxUses,
+ unique,
+ },
+ reason,
+ })
+ .then(invite => new Invite(this.client, invite));
+ }
+
+ /**
+ * Fetches a collection of invites to this guild channel.
+ * Resolves with a collection mapping invites by their codes.
+ * @returns {Promise<Collection<string, Invite>>}
+ */
+ async fetchInvites() {
+ const inviteItems = await this.client.api.channels(this.id).invites.get();
+ const invites = new Collection();
+ for (const inviteItem of inviteItems) {
+ const invite = new Invite(this.client, inviteItem);
+ invites.set(invite.code, invite);
+ }
+ return invites;
+ }
+
+ /* eslint-disable max-len */
+ /**
+ * Clones this channel.
+ * @param {Object} [options] The options
+ * @param {string} [options.name=this.name] Name of the new channel
+ * @param {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} [options.permissionOverwrites=this.permissionOverwrites]
+ * Permission overwrites of the new channel
+ * @param {string} [options.type=this.type] Type of the new channel
+ * @param {string} [options.topic=this.topic] Topic of the new channel (only text)
+ * @param {boolean} [options.nsfw=this.nsfw] Whether the new channel is nsfw (only text)
+ * @param {number} [options.bitrate=this.bitrate] Bitrate of the new channel in bits (only voice)
+ * @param {number} [options.userLimit=this.userLimit] Maximum amount of users allowed in the new channel (only voice)
+ * @param {number} [options.rateLimitPerUser=ThisType.rateLimitPerUser] Ratelimit per user for the new channel (only text)
+ * @param {ChannelResolvable} [options.parent=this.parent] Parent of the new channel
+ * @param {string} [options.reason] Reason for cloning this channel
+ * @returns {Promise<GuildChannel>}
+ */
+ clone(options = {}) {
+ 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,
+ },
+ options,
+ );
+ return this.guild.channels.create(options.name, options);
+ }
+ /* eslint-enable max-len */
+
+ /**
+ * 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.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false);
+ }
+
+ /**
+ * Whether the channel is manageable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get manageable() {
+ if (this.client.user.id === this.guild.ownerID) return true;
+ if (this.type === 'voice') {
+ if (!this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false)) {
+ return false;
+ }
+ } else if (!this.viewable) {
+ return false;
+ }
+ return this.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false);
+ }
+
+ /**
+ * Whether the channel is viewable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get viewable() {
+ 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.VIEW_CHANNEL, false);
+ }
+
+ /**
+ * 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(console.log)
+ * .catch(console.error);
+ */
+ delete(reason) {
+ return this.client.api
+ .channels(this.id)
+ .delete({ reason })
+ .then(() => this);
+ }
+}
+
+module.exports = GuildChannel;
diff --git a/node_modules/discord.js/src/structures/GuildEmoji.js b/node_modules/discord.js/src/structures/GuildEmoji.js
new file mode 100644
index 0000000..d4189e9
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildEmoji.js
@@ -0,0 +1,159 @@
+'use strict';
+
+const BaseGuildEmoji = require('./BaseGuildEmoji');
+const { Error } = require('../errors');
+const GuildEmojiRoleManager = require('../managers/GuildEmojiRoleManager');
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a custom emoji.
+ * @extends {BaseGuildEmoji}
+ */
+class GuildEmoji extends BaseGuildEmoji {
+ /**
+ * @name GuildEmoji
+ * @kind constructor
+ * @memberof GuildEmoji
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the guild emoji
+ * @param {Guild} guild The guild the guild emoji is part of
+ */
+
+ /**
+ * The guild this emoji is part of
+ * @type {Guild}
+ * @name GuildEmoji#guild
+ */
+
+ _clone() {
+ const clone = super._clone();
+ clone._roles = this._roles.slice();
+ return clone;
+ }
+
+ /**
+ * Whether the emoji is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME');
+ return !this.managed && this.guild.me.hasPermission(Permissions.FLAGS.MANAGE_EMOJIS);
+ }
+
+ /**
+ * A manager for roles this emoji is active for.
+ * @type {GuildEmojiRoleManager}
+ * @readonly
+ */
+ get roles() {
+ return new GuildEmojiRoleManager(this);
+ }
+
+ /**
+ * Fetches the author for this emoji
+ * @returns {Promise<User>}
+ */
+ fetchAuthor() {
+ if (this.managed) {
+ return Promise.reject(new Error('EMOJI_MANAGED'));
+ } else {
+ if (!this.guild.me) return Promise.reject(new Error('GUILD_UNCACHED_ME'));
+ if (!this.guild.me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS)) {
+ return Promise.reject(new Error('MISSING_MANAGE_EMOJIS_PERMISSION', this.guild));
+ }
+ }
+ return this.client.api
+ .guilds(this.guild.id)
+ .emojis(this.id)
+ .get()
+ .then(emoji => this.client.users.add(emoji.user));
+ }
+
+ /**
+ * Data for editing an emoji.
+ * @typedef {Object} GuildEmojiEditData
+ * @property {string} [name] The name of the emoji
+ * @property {Collection<Snowflake, Role>|RoleResolvable[]} [roles] Roles to restrict emoji to
+ */
+
+ /**
+ * Edits the emoji.
+ * @param {GuildEmojiEditData} data The new data for the emoji
+ * @param {string} [reason] Reason for editing this emoji
+ * @returns {Promise<GuildEmoji>}
+ * @example
+ * // Edit an emoji
+ * emoji.edit({ name: 'newemoji' })
+ * .then(e => console.log(`Edited emoji ${e}`))
+ * .catch(console.error);
+ */
+ edit(data, reason) {
+ const roles = data.roles ? data.roles.map(r => r.id || r) : undefined;
+ return this.client.api
+ .guilds(this.guild.id)
+ .emojis(this.id)
+ .patch({
+ data: {
+ name: data.name,
+ roles,
+ },
+ reason,
+ })
+ .then(newData => {
+ const clone = this._clone();
+ clone._patch(newData);
+ return clone;
+ });
+ }
+
+ /**
+ * Sets the name of the emoji.
+ * @param {string} name The new name for the emoji
+ * @param {string} [reason] Reason for changing the emoji's name
+ * @returns {Promise<GuildEmoji>}
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Deletes the emoji.
+ * @param {string} [reason] Reason for deleting the emoji
+ * @returns {Promise<GuildEmoji>}
+ */
+ delete(reason) {
+ return this.client.api
+ .guilds(this.guild.id)
+ .emojis(this.id)
+ .delete({ reason })
+ .then(() => this);
+ }
+
+ /**
+ * Whether this emoji is the same as another one.
+ * @param {GuildEmoji|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 GuildEmoji) {
+ return (
+ other.id === this.id &&
+ other.name === this.name &&
+ other.managed === this.managed &&
+ other.requiresColons === this.requiresColons &&
+ other.roles.cache.size === this.roles.cache.size &&
+ other.roles.cache.every(role => this.roles.cache.has(role.id))
+ );
+ } else {
+ return (
+ other.id === this.id &&
+ other.name === this.name &&
+ other.roles.length === this.roles.cache.size &&
+ other.roles.every(role => this.roles.cache.has(role))
+ );
+ }
+ }
+}
+
+module.exports = GuildEmoji;
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..f949517
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildMember.js
@@ -0,0 +1,410 @@
+'use strict';
+
+const Base = require('./Base');
+const { Presence } = require('./Presence');
+const Role = require('./Role');
+const VoiceState = require('./VoiceState');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const { Error } = require('../errors');
+const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager');
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a member of a guild on Discord.
+ * @implements {TextBasedChannel}
+ * @extends {Base}
+ */
+class GuildMember extends Base {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the guild member
+ * @param {Guild} guild The guild the member is part of
+ */
+ constructor(client, data, guild) {
+ super(client);
+
+ /**
+ * The guild that this member is part of
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ /**
+ * The user that this guild member instance represents
+ * @type {User}
+ * @name GuildMember#user
+ */
+ if (data.user) this.user = client.users.add(data.user, true);
+
+ /**
+ * The timestamp the member joined the guild at
+ * @type {?number}
+ */
+ this.joinedTimestamp = null;
+
+ /**
+ * The ID of the last message sent by the member in their guild, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The ID of the channel for the last message sent by the member in their guild, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageChannelID = null;
+
+ /**
+ * The timestamp of when the member used their Nitro boost on the guild, if it was used
+ * @type {?number}
+ */
+ this.premiumSinceTimestamp = null;
+
+ /**
+ * Whether the member has been removed from the guild
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ this._roles = [];
+ if (data) this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The nickname of this member, if they have one
+ * @type {?string}
+ * @name GuildMember#nickname
+ */
+ if (typeof data.nick !== 'undefined') this.nickname = data.nick;
+
+ if (data.joined_at) this.joinedTimestamp = new Date(data.joined_at).getTime();
+ if (data.premium_since) this.premiumSinceTimestamp = new Date(data.premium_since).getTime();
+
+ if (data.user) this.user = this.guild.client.users.add(data.user);
+ if (data.roles) this._roles = data.roles;
+ }
+
+ _clone() {
+ const clone = super._clone();
+ clone._roles = this._roles.slice();
+ return clone;
+ }
+
+ /**
+ * Whether this GuildMember is a partial
+ * @type {boolean}
+ * @readonly
+ */
+ get partial() {
+ return !this.joinedTimestamp;
+ }
+
+ /**
+ * A manager for the roles belonging to this member
+ * @type {GuildMemberRoleManager}
+ * @readonly
+ */
+ get roles() {
+ return new GuildMemberRoleManager(this);
+ }
+
+ /**
+ * The Message object of the last message sent by the member in their guild, if one was sent
+ * @type {?Message}
+ * @readonly
+ */
+ get lastMessage() {
+ const channel = this.guild.channels.cache.get(this.lastMessageChannelID);
+ return (channel && channel.messages.cache.get(this.lastMessageID)) || null;
+ }
+
+ /**
+ * The voice state of this member
+ * @type {VoiceState}
+ * @readonly
+ */
+ get voice() {
+ return this.guild.voiceStates.cache.get(this.id) || new VoiceState(this.guild, { user_id: this.id });
+ }
+
+ /**
+ * 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 guild member
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ return (
+ this.guild.presences.cache.get(this.id) ||
+ new Presence(this.client, {
+ user: {
+ id: this.id,
+ },
+ guild: this.guild,
+ })
+ );
+ }
+
+ /**
+ * The displayed color of this member in base 10
+ * @type {number}
+ * @readonly
+ */
+ get displayColor() {
+ const role = this.roles.color;
+ return (role && role.color) || 0;
+ }
+
+ /**
+ * The displayed color of this member in hexadecimal
+ * @type {string}
+ * @readonly
+ */
+ get displayHexColor() {
+ const role = this.roles.color;
+ return (role && role.hexColor) || '#000000';
+ }
+
+ /**
+ * The ID of this member
+ * @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 {Readonly<Permissions>}
+ * @readonly
+ */
+ get permissions() {
+ if (this.user.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze();
+ return new Permissions(this.roles.cache.map(role => role.permissions)).freeze();
+ }
+
+ /**
+ * Whether the client user is above this user in the hierarchy, according to role position and guild ownership.
+ * This is a prerequisite for many moderative actions.
+ * @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;
+ if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME');
+ return this.guild.me.roles.highest.comparePositionTo(this.roles.highest) > 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 a member in a guild channel,
+ * taking into account roles and permission overwrites.
+ * @param {ChannelResolvable} channel The guild channel to use as context
+ * @returns {Readonly<Permissions>}
+ */
+ permissionsIn(channel) {
+ channel = this.guild.channels.resolve(channel);
+ if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE');
+ return channel.memberPermissions(this);
+ }
+
+ /**
+ * Checks if any of this member's roles have a permission.
+ * @param {PermissionResolvable} permission Permission(s) to check for
+ * @param {Object} [options] Options
+ * @param {boolean} [options.checkAdmin=true] Whether to allow the administrator permission to override
+ * @param {boolean} [options.checkOwner=true] Whether to allow being the guild's owner to override
+ * @returns {boolean}
+ */
+ hasPermission(permission, { checkAdmin = true, checkOwner = true } = {}) {
+ if (checkOwner && this.user.id === this.guild.ownerID) return true;
+ return this.roles.cache.some(r => r.permissions.has(permission, checkAdmin));
+ }
+
+ /**
+ * The data for editing a guild 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>}
+ */
+ async edit(data, reason) {
+ if (data.channel) {
+ data.channel = this.guild.channels.resolve(data.channel);
+ if (!data.channel || data.channel.type !== 'voice') {
+ throw new Error('GUILD_VOICE_CHANNEL_RESOLVE');
+ }
+ data.channel_id = data.channel.id;
+ data.channel = undefined;
+ } else if (data.channel === null) {
+ data.channel_id = null;
+ data.channel = undefined;
+ }
+ if (data.roles) data.roles = data.roles.map(role => (role instanceof Role ? role.id : role));
+ let endpoint = this.client.api.guilds(this.guild.id);
+ if (this.user.id === this.client.user.id) {
+ const keys = Object.keys(data);
+ if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members('@me').nick;
+ else endpoint = endpoint.members(this.id);
+ } else {
+ endpoint = endpoint.members(this.id);
+ }
+ await endpoint.patch({ data, reason });
+
+ const clone = this._clone();
+ data.user = this.user;
+ clone._patch(data);
+ return clone;
+ }
+
+ /**
+ * Sets 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>}
+ */
+ 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 member.
+ * @returns {Promise<DMChannel>}
+ */
+ deleteDM() {
+ return this.user.deleteDM();
+ }
+
+ /**
+ * Kicks this member from the guild.
+ * @param {string} [reason] Reason for kicking user
+ * @returns {Promise<GuildMember>}
+ */
+ kick(reason) {
+ return this.client.api
+ .guilds(this.guild.id)
+ .members(this.user.id)
+ .delete({ reason })
+ .then(() => this);
+ }
+
+ /**
+ * Bans this guild member.
+ * @param {Object} [options] Options for the ban
+ * @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
+ * guildMember.ban({ days: 7, reason: 'They deserved it' })
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ ban(options) {
+ return this.guild.members.ban(this, options);
+ }
+
+ /**
+ * Fetches this GuildMember.
+ * @returns {Promise<GuildMember>}
+ */
+ fetch() {
+ return this.guild.members.fetch(this.id, true);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the user's mention instead of the GuildMember object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <@123456789012345678>!
+ * console.log(`Hello from ${member}!`);
+ */
+ toString() {
+ return `<@${this.nickname ? '!' : ''}${this.user.id}>`;
+ }
+
+ toJSON() {
+ return super.toJSON({
+ guild: 'guildID',
+ user: 'userID',
+ displayName: true,
+ speaking: false,
+ lastMessage: false,
+ lastMessageID: false,
+ roles: true,
+ });
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ send() {}
+}
+
+TextBasedChannel.applyToClass(GuildMember);
+
+module.exports = GuildMember;
diff --git a/node_modules/discord.js/src/structures/GuildPreview.js b/node_modules/discord.js/src/structures/GuildPreview.js
new file mode 100644
index 0000000..681ff60
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildPreview.js
@@ -0,0 +1,157 @@
+'use strict';
+
+const Base = require('./Base');
+const GuildPreviewEmoji = require('./GuildPreviewEmoji');
+const Collection = require('../util/Collection');
+
+/**
+ * Represents the data about the guild any bot can preview, connected to the specified public guild.
+ * @extends {Base}
+ */
+class GuildPreview extends Base {
+ constructor(client, data) {
+ super(client);
+
+ if (!data) return;
+
+ this._patch(data);
+ }
+
+ /**
+ * Builds the public guild with the provided data.
+ * @param {*} data The raw data of the public guild
+ * @private
+ */
+ _patch(data) {
+ /**
+ * The id of this public guild
+ * @type {string}
+ */
+ this.id = data.id;
+
+ /**
+ * The name of this public guild
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The icon of this public guild
+ * @type {?string}
+ */
+ this.icon = data.icon;
+
+ /**
+ * The splash icon of this public guild
+ * @type {?string}
+ */
+ this.splash = data.splash;
+
+ /**
+ * The discovery splash icon of this public guild
+ * @type {?string}
+ */
+ this.discoverySplash = data.discovery_splash;
+
+ /**
+ * An array of enabled guild features
+ * @type {Features[]}
+ */
+ this.features = data.features;
+
+ /**
+ * The approximate count of members in this public guild
+ * @type {number}
+ */
+ this.approximateMemberCount = data.approximate_member_count;
+
+ /**
+ * The approximate count of online members in this public guild
+ * @type {number}
+ */
+ this.approximatePresenceCount = data.approximate_presence_count;
+
+ /**
+ * The description for this public guild
+ * @type {?string}
+ */
+ this.description = data.description;
+
+ if (!this.emojis) {
+ /**
+ * Collection of emojis belonging to this public guild
+ * @type {Collection<Snowflake, GuildPreviewEmoji>}
+ */
+ this.emojis = new Collection();
+ } else {
+ this.emojis.clear();
+ }
+ for (const emoji of data.emojis) {
+ this.emojis.set(emoji.id, new GuildPreviewEmoji(this.client, emoji, this));
+ }
+ }
+
+ /**
+ * The URL to this public guild's splash.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ splashURL({ format, size } = {}) {
+ if (!this.splash) return null;
+ return this.client.rest.cdn.Splash(this.id, this.splash, format, size);
+ }
+
+ /**
+ * The URL to this public guild's discovery splash.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ discoverySplashURL({ format, size } = {}) {
+ if (!this.discoverySplash) return null;
+ return this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size);
+ }
+
+ /**
+ * The URL to this public guild's icon.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ iconURL({ format, size, dynamic } = {}) {
+ if (!this.icon) return null;
+ return this.client.rest.cdn.Icon(this.id, this.icon, format, size, dynamic);
+ }
+
+ /**
+ * Fetches this public guild.
+ * @returns {Promise<GuildPreview>}
+ */
+ fetch() {
+ return this.client.api
+ .guilds(this.id)
+ .preview.get()
+ .then(data => {
+ this._patch(data);
+ return this;
+ });
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the guild's name instead of the Guild object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from My Guild!
+ * console.log(`Hello from ${previewGuild}!`);
+ */
+ toString() {
+ return this.name;
+ }
+
+ toJSON() {
+ const json = super.toJSON();
+ json.iconURL = this.iconURL();
+ json.splashURL = this.splashURL();
+ return json;
+ }
+}
+
+module.exports = GuildPreview;
diff --git a/node_modules/discord.js/src/structures/GuildPreviewEmoji.js b/node_modules/discord.js/src/structures/GuildPreviewEmoji.js
new file mode 100644
index 0000000..4c70903
--- /dev/null
+++ b/node_modules/discord.js/src/structures/GuildPreviewEmoji.js
@@ -0,0 +1,26 @@
+'use strict';
+
+const BaseGuildEmoji = require('./BaseGuildEmoji');
+
+/**
+ * Represents an instance of an emoji belonging to a public guild obtained through Discord's preview endpoint.
+ * @extends {BaseGuildEmoji}
+ */
+class GuildPreviewEmoji extends BaseGuildEmoji {
+ /**
+ * The public guild this emoji is part of
+ * @type {GuildPreview}
+ * @name GuildPreviewEmoji#guild
+ */
+
+ /**
+ * Set of roles this emoji is active for
+ * @type {Set<Snowflake>}
+ * @readonly
+ */
+ get roles() {
+ return new Set(this._roles);
+ }
+}
+
+module.exports = GuildPreviewEmoji;
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..49a0227
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Integration.js
@@ -0,0 +1,167 @@
+'use strict';
+
+const Base = require('./Base');
+
+/**
+ * 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 extends Base {
+ constructor(client, data, guild) {
+ super(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.cache.get(data.role_id);
+
+ /**
+ * The user for this integration
+ * @type {User}
+ */
+ this.user = this.client.users.add(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;
+ }
+
+ /**
+ * Sync this integration
+ * @returns {Promise<Integration>}
+ */
+ sync() {
+ this.syncing = true;
+ return this.client.api
+ .guilds(this.guild.id)
+ .integrations(this.id)
+ .post()
+ .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 = null;
+ }
+ if ('expireGracePeriod' in data) {
+ data.expire_grace_period = data.expireGracePeriod;
+ data.expireGracePeriod = null;
+ }
+ // The option enable_emoticons is only available for Twitch at this moment
+ return this.client.api
+ .guilds(this.guild.id)
+ .integrations(this.id)
+ .patch({ 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.api
+ .guilds(this.guild.id)
+ .integrations(this.id)
+ .delete({ reason })
+ .then(() => this);
+ }
+
+ toJSON() {
+ return super.toJSON({
+ role: 'roleID',
+ guild: 'guildID',
+ user: 'userID',
+ });
+ }
+}
+
+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..6833266
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Invite.js
@@ -0,0 +1,194 @@
+'use strict';
+
+const Base = require('./Base');
+const { Endpoints } = require('../util/Constants');
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents an invitation to a guild channel.
+ * <warn>The only guaranteed properties are `code`, `channel`, and `url`. Other properties can be missing.</warn>
+ * @extends {Base}
+ */
+class Invite extends Base {
+ constructor(client, data) {
+ super(client);
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The guild the invite is for
+ * @type {?Guild}
+ */
+ this.guild = data.guild ? this.client.guilds.add(data.guild, false) : null;
+
+ /**
+ * 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 = 'approximate_presence_count' in data ? data.approximate_presence_count : null;
+
+ /**
+ * The approximate total number of members of the guild this invite is for
+ * @type {?number}
+ */
+ this.memberCount = 'approximate_member_count' in data ? data.approximate_member_count : null;
+
+ /**
+ * Whether or not this invite is temporary
+ * @type {?boolean}
+ */
+ this.temporary = 'temporary' in data ? data.temporary : null;
+
+ /**
+ * The maximum age of the invite, in seconds, 0 if never expires
+ * @type {?number}
+ */
+ this.maxAge = 'max_age' in data ? data.max_age : null;
+
+ /**
+ * How many times this invite has been used
+ * @type {?number}
+ */
+ this.uses = 'uses' in data ? data.uses : null;
+
+ /**
+ * The maximum uses of this invite
+ * @type {?number}
+ */
+ this.maxUses = 'max_uses' in data ? data.max_uses : null;
+
+ /**
+ * The user who created this invite
+ * @type {?User}
+ */
+ this.inviter = data.inviter ? this.client.users.add(data.inviter) : null;
+
+ /**
+ * The target user for this invite
+ * @type {?User}
+ */
+ this.targetUser = data.target_user ? this.client.users.add(data.target_user) : null;
+
+ /**
+ * The type of the target user:
+ * * 1: STREAM
+ * @typedef {number} TargetUser
+ */
+
+ /**
+ * The target user type
+ * @type {?TargetUser}
+ */
+ this.targetUserType = typeof data.target_user_type === 'number' ? data.target_user_type : null;
+
+ /**
+ * The channel the invite is for
+ * @type {Channel}
+ */
+ this.channel = this.client.channels.add(data.channel, this.guild, false);
+
+ /**
+ * The timestamp the invite was created at
+ * @type {?number}
+ */
+ this.createdTimestamp = 'created_at' in data ? new Date(data.created_at).getTime() : null;
+ }
+
+ /**
+ * The time the invite was created at
+ * @type {?Date}
+ * @readonly
+ */
+ get createdAt() {
+ return this.createdTimestamp ? new Date(this.createdTimestamp) : null;
+ }
+
+ /**
+ * Whether the invite is deletable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ const guild = this.guild;
+ if (!guild || !this.client.guilds.cache.has(guild.id)) return false;
+ if (!guild.me) throw new Error('GUILD_UNCACHED_ME');
+ return (
+ this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false) ||
+ guild.me.permissions.has(Permissions.FLAGS.MANAGE_GUILD)
+ );
+ }
+
+ /**
+ * The timestamp the invite will expire at
+ * @type {?number}
+ * @readonly
+ */
+ get expiresTimestamp() {
+ return this.createdTimestamp && this.maxAge ? this.createdTimestamp + this.maxAge * 1000 : null;
+ }
+
+ /**
+ * The time the invite will expire at
+ * @type {?Date}
+ * @readonly
+ */
+ get expiresAt() {
+ const { expiresTimestamp } = this;
+ return expiresTimestamp ? new Date(expiresTimestamp) : null;
+ }
+
+ /**
+ * The URL to the invite
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return Endpoints.invite(this.client.options.http.invite, this.code);
+ }
+
+ /**
+ * Deletes this invite.
+ * @param {string} [reason] Reason for deleting this invite
+ * @returns {Promise<Invite>}
+ */
+ delete(reason) {
+ return this.client.api.invites[this.code].delete({ reason }).then(() => this);
+ }
+
+ /**
+ * 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;
+ }
+
+ toJSON() {
+ return super.toJSON({
+ url: true,
+ expiresTimestamp: true,
+ presenceCount: false,
+ memberCount: false,
+ uses: false,
+ channel: 'channelID',
+ inviter: 'inviterID',
+ guild: 'guildID',
+ });
+ }
+
+ valueOf() {
+ return this.code;
+ }
+}
+
+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..06ed12a
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Message.js
@@ -0,0 +1,623 @@
+'use strict';
+
+const APIMessage = require('./APIMessage');
+const Base = require('./Base');
+const ClientApplication = require('./ClientApplication');
+const MessageAttachment = require('./MessageAttachment');
+const Embed = require('./MessageEmbed');
+const Mentions = require('./MessageMentions');
+const ReactionCollector = require('./ReactionCollector');
+const { Error, TypeError } = require('../errors');
+const ReactionManager = require('../managers/ReactionManager');
+const Collection = require('../util/Collection');
+const { MessageTypes } = require('../util/Constants');
+const MessageFlags = require('../util/MessageFlags');
+const Permissions = require('../util/Permissions');
+const Util = require('../util/Util');
+
+/**
+ * Represents a message on Discord.
+ * @extends {Base}
+ */
+class Message extends Base {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the message
+ * @param {TextChannel|DMChannel} channel The channel the message was sent in
+ */
+ constructor(client, data, channel) {
+ super(client);
+
+ /**
+ * The channel that the message was sent in
+ * @type {TextChannel|DMChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * Whether this message has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+
+ if (data) this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The ID of the message
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of the message
+ * @type {MessageType}
+ */
+ this.type = MessageTypes[data.type];
+
+ /**
+ * The content of the message
+ * @type {string}
+ */
+ this.content = data.content;
+
+ /**
+ * The author of the message
+ * @type {?User}
+ */
+ this.author = data.author ? this.client.users.add(data.author, !data.webhook_id) : null;
+
+ /**
+ * 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(e, true));
+
+ /**
+ * A collection of attachments in the message - e.g. Pictures - mapped by their ID
+ * @type {Collection<Snowflake, MessageAttachment>}
+ */
+ this.attachments = new Collection();
+ if (data.attachments) {
+ for (const attachment of data.attachments) {
+ this.attachments.set(attachment.id, new MessageAttachment(attachment.url, attachment.filename, 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 manager of the reactions belonging to this message
+ * @type {ReactionManager}
+ */
+ this.reactions = new ReactionManager(this);
+ if (data.reactions && data.reactions.length > 0) {
+ for (const reaction of data.reactions) {
+ this.reactions.add(reaction);
+ }
+ }
+
+ /**
+ * 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;
+
+ /**
+ * Supplemental application information for group activities
+ * @type {?ClientApplication}
+ */
+ this.application = data.application ? new ClientApplication(this.client, data.application) : null;
+
+ /**
+ * Group activity
+ * @type {?MessageActivity}
+ */
+ this.activity = data.activity
+ ? {
+ partyID: data.activity.party_id,
+ type: data.activity.type,
+ }
+ : null;
+
+ /**
+ * The previous versions of the message, sorted with the most recent first
+ * @type {Message[]}
+ * @private
+ */
+ this._edits = [];
+
+ if (this.member && data.member) {
+ this.member._patch(data.member);
+ } else if (data.member && this.guild && this.author) {
+ this.guild.members.add(Object.assign(data.member, { user: this.author }));
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Whether or not this message is a partial
+ * @type {boolean}
+ * @readonly
+ */
+ get partial() {
+ return typeof this.content !== 'string' || !this.author;
+ }
+
+ /**
+ * Updates the message.
+ * @param {Object} data Raw Discord message update data
+ * @private
+ */
+ patch(data) {
+ const clone = this._clone();
+ 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(e, true));
+ 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 MessageAttachment(attachment.url, attachment.filename, attachment));
+ }
+ } else {
+ this.attachments = new Collection(this.attachments);
+ }
+
+ this.mentions = new Mentions(
+ this,
+ 'mentions' in data ? data.mentions : this.mentions.users,
+ 'mention_roles' in data ? data.mention_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();
+ }
+
+ /**
+ * 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}
+ * @readonly
+ */
+ get member() {
+ return this.guild ? this.guild.member(this.author) || null : null;
+ }
+
+ /**
+ * The time the message was sent at
+ * @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 this 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() {
+ // eslint-disable-next-line eqeqeq
+ return this.content != null ? Util.cleanContent(this.content, this) : null;
+ }
+
+ /**
+ * 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 createReactionCollector 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, false)))
+ );
+ }
+
+ /**
+ * 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, false))
+ );
+ }
+
+ /**
+ * Options that can be passed into editMessage.
+ * @typedef {Object} MessageEditOptions
+ * @property {string} [content] Content to be edited
+ * @property {Object} [embed] An embed to be added/edited
+ * @property {string|boolean} [code] Language for optional codeblock formatting to apply
+ * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
+ */
+
+ /**
+ * Edits the content of the message.
+ * @param {StringResolvable|APIMessage} [content] The new content for the message
+ * @param {MessageEditOptions|MessageEmbed} [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(`Updated the content of a message to ${msg.content}`))
+ * .catch(console.error);
+ */
+ edit(content, options) {
+ const { data } =
+ content instanceof APIMessage ? content.resolveData() : APIMessage.create(this, content, options).resolveData();
+ return this.client.api.channels[this.channel.id].messages[this.id].patch({ data }).then(d => {
+ const clone = this._clone();
+ clone._patch(d);
+ return clone;
+ });
+ }
+
+ /**
+ * Pins this message to the channel's pinned messages.
+ * @returns {Promise<Message>}
+ */
+ pin() {
+ return this.client.api
+ .channels(this.channel.id)
+ .pins(this.id)
+ .put()
+ .then(() => this);
+ }
+
+ /**
+ * Unpins this message from the channel's pinned messages.
+ * @returns {Promise<Message>}
+ */
+ unpin() {
+ return this.client.api
+ .channels(this.channel.id)
+ .pins(this.id)
+ .delete()
+ .then(() => this);
+ }
+
+ /**
+ * Adds a reaction to the message.
+ * @param {EmojiIdentifierResolvable} 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.cache.get('123456789012345678'))
+ * .then(console.log)
+ * .catch(console.error);
+ */
+ react(emoji) {
+ emoji = this.client.emojis.resolveIdentifier(emoji);
+ if (!emoji) throw new TypeError('EMOJI_TYPE');
+
+ return this.client.api
+ .channels(this.channel.id)
+ .messages(this.id)
+ .reactions(emoji, '@me')
+ .put()
+ .then(
+ () =>
+ this.client.actions.MessageReactionAdd.handle({
+ user: this.client.user,
+ channel: this.channel,
+ message: this,
+ emoji: Util.parseEmoji(emoji),
+ }).reaction,
+ );
+ }
+
+ /**
+ * Deletes the message.
+ * @param {Object} [options] Options
+ * @param {number} [options.timeout=0] How long to wait to delete the message in milliseconds
+ * @param {string} [options.reason] Reason for deleting this message, if it does not belong to the client user
+ * @returns {Promise<Message>}
+ * @example
+ * // Delete a message
+ * message.delete()
+ * .then(msg => console.log(`Deleted message from ${msg.author.username}`))
+ * .catch(console.error);
+ */
+ delete(options = {}) {
+ if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
+ const { timeout = 0, reason } = options;
+ if (timeout <= 0) {
+ return this.channel.messages.delete(this.id, reason).then(() => this);
+ } else {
+ return new Promise(resolve => {
+ this.client.setTimeout(() => {
+ resolve(this.delete({ reason }));
+ }, timeout);
+ });
+ }
+ }
+
+ /**
+ * Replies to the message.
+ * @param {StringResolvable|APIMessage} [content=''] The content for the message
+ * @param {MessageOptions|MessageAdditions} [options={}] The options to provide
+ * @returns {Promise<Message|Message[]>}
+ * @example
+ * // Reply to a message
+ * message.reply('Hey, I\'m a reply!')
+ * .then(() => console.log(`Sent a reply to ${message.author.username}`))
+ * .catch(console.error);
+ */
+ reply(content, options) {
+ return this.channel.send(
+ content instanceof APIMessage
+ ? content
+ : APIMessage.transformOptions(content, options, { reply: this.member || this.author }),
+ );
+ }
+
+ /**
+ * Fetch this message.
+ * @returns {Promise<Message>}
+ */
+ fetch() {
+ return this.channel.messages.fetch(this.id, true);
+ }
+
+ /**
+ * Fetches the webhook used to create this message.
+ * @returns {Promise<?Webhook>}
+ */
+ fetchWebhook() {
+ if (!this.webhookID) return Promise.reject(new Error('WEBHOOK_MESSAGE'));
+ 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({ 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;
+ }
+
+ toJSON() {
+ return super.toJSON({
+ channel: 'channelID',
+ author: 'authorID',
+ application: 'applicationID',
+ guild: 'guildID',
+ cleanContent: true,
+ member: false,
+ reactions: false,
+ });
+ }
+}
+
+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..f5fb723
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageAttachment.js
@@ -0,0 +1,98 @@
+'use strict';
+
+const Util = require('../util/Util');
+
+/**
+ * Represents an attachment in a message.
+ */
+class MessageAttachment {
+ /**
+ * @param {BufferResolvable|Stream} attachment The file
+ * @param {string} [name=null] The name of the file, if any
+ * @param {Object} [data] Extra data
+ */
+ constructor(attachment, name = null, data) {
+ this.attachment = attachment;
+ /**
+ * The name of this attachment
+ * @type {?string}
+ */
+ this.name = name;
+ if (data) this._patch(data);
+ }
+
+ /**
+ * Sets the file of this attachment.
+ * @param {BufferResolvable|Stream} attachment The file
+ * @param {string} [name=null] The name of the file, if any
+ * @returns {MessageAttachment} This attachment
+ */
+ setFile(attachment, name = null) {
+ this.attachment = attachment;
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the name of this attachment.
+ * @param {string} name The name of the file
+ * @returns {MessageAttachment} This attachment
+ */
+ setName(name) {
+ this.name = name;
+ return this;
+ }
+
+ _patch(data) {
+ /**
+ * The ID of this attachment
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The size of this attachment in bytes
+ * @type {number}
+ */
+ this.size = 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 or video)
+ * @type {?number}
+ */
+ this.height = typeof data.height !== 'undefined' ? data.height : null;
+
+ /**
+ * The width of this attachment (if an image or video)
+ * @type {?number}
+ */
+ this.width = typeof data.width !== 'undefined' ? data.width : null;
+ }
+
+ /**
+ * Whether or not this attachment has been marked as a spoiler
+ * @type {boolean}
+ * @readonly
+ */
+ get spoiler() {
+ return Util.basename(this.url).startsWith('SPOILER_');
+ }
+
+ toJSON() {
+ return Util.flatten(this);
+ }
+}
+
+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..f8f3d5a
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageCollector.js
@@ -0,0 +1,129 @@
+'use strict';
+
+const Collector = require('./interfaces/Collector');
+const { Events } = require('../util/Constants');
+
+/**
+ * @typedef {CollectorOptions} MessageCollectorOptions
+ * @property {number} max The maximum amount of messages to collect
+ * @property {number} maxProcessed The maximum amount of messages to process
+ */
+
+/**
+ * Collects messages on a channel.
+ * Will automatically stop if the channel (`'channelDelete'`) or guild (`'guildDelete'`) are deleted.
+ * @extends {Collector}
+ */
+class MessageCollector extends Collector {
+ /**
+ * @param {TextChannel|DMChannel} 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;
+
+ const bulkDeleteListener = messages => {
+ for (const message of messages.values()) this.handleDispose(message);
+ };
+ this._handleChannelDeletion = this._handleChannelDeletion.bind(this);
+ this._handleGuildDeletion = this._handleGuildDeletion.bind(this);
+
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() + 1);
+ this.client.on(Events.MESSAGE_CREATE, this.handleCollect);
+ this.client.on(Events.MESSAGE_DELETE, this.handleDispose);
+ this.client.on(Events.MESSAGE_BULK_DELETE, bulkDeleteListener);
+ this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion);
+ this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion);
+
+ this.once('end', () => {
+ this.client.removeListener(Events.MESSAGE_CREATE, this.handleCollect);
+ this.client.removeListener(Events.MESSAGE_DELETE, this.handleDispose);
+ this.client.removeListener(Events.MESSAGE_BULK_DELETE, bulkDeleteListener);
+ this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion);
+ this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion);
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() - 1);
+ });
+ }
+
+ /**
+ * Handles a message for possible collection.
+ * @param {Message} message The message that could be collected
+ * @returns {?Snowflake}
+ * @private
+ */
+ collect(message) {
+ /**
+ * Emitted whenever a message is collected.
+ * @event MessageCollector#collect
+ * @param {Message} message The message that was collected
+ */
+ if (message.channel.id !== this.channel.id) return null;
+ this.received++;
+ return message.id;
+ }
+
+ /**
+ * Handles a message for possible disposal.
+ * @param {Message} message The message that could be disposed of
+ * @returns {?Snowflake}
+ */
+ dispose(message) {
+ /**
+ * Emitted whenever a message is disposed of.
+ * @event MessageCollector#dispose
+ * @param {Message} message The message that was disposed of
+ */
+ return message.channel.id === this.channel.id ? message.id : null;
+ }
+
+ /**
+ * Checks after un/collection to see if the collector is done.
+ * @returns {?string}
+ * @private
+ */
+ endReason() {
+ if (this.options.max && this.collected.size >= this.options.max) return 'limit';
+ if (this.options.maxProcessed && this.received === this.options.maxProcessed) return 'processedLimit';
+ return null;
+ }
+
+ /**
+ * Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'.
+ * @private
+ * @param {GuildChannel} channel The channel that was deleted
+ * @returns {void}
+ */
+ _handleChannelDeletion(channel) {
+ if (channel.id === this.channel.id) {
+ this.stop('channelDelete');
+ }
+ }
+
+ /**
+ * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'.
+ * @private
+ * @param {Guild} guild The guild that was deleted
+ * @returns {void}
+ */
+ _handleGuildDeletion(guild) {
+ if (this.channel.guild && guild.id === this.channel.guild.id) {
+ this.stop('guildDelete');
+ }
+ }
+}
+
+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..2b993a3
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageEmbed.js
@@ -0,0 +1,454 @@
+'use strict';
+
+const { RangeError } = require('../errors');
+const Util = require('../util/Util');
+
+/**
+ * Represents an embed in a message (image/video preview, rich embed, etc.)
+ */
+class MessageEmbed {
+ /**
+ * @name MessageEmbed
+ * @kind constructor
+ * @memberof MessageEmbed
+ * @param {MessageEmbed|Object} [data={}] MessageEmbed to clone or raw embed data
+ */
+
+ constructor(data = {}, skipValidation = false) {
+ this.setup(data, skipValidation);
+ }
+
+ setup(data, skipValidation) {
+ /**
+ * The type of this embed, either:
+ * * `rich` - a rich embed
+ * * `image` - an image embed
+ * * `video` - a video embed
+ * * `gifv` - a gifv embed
+ * * `article` - an article embed
+ * * `link` - a link 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 this embed
+ * @type {?number}
+ */
+ this.color = Util.resolveColor(data.color);
+
+ /**
+ * The timestamp of this embed
+ * @type {?number}
+ */
+ this.timestamp = data.timestamp ? new Date(data.timestamp).getTime() : null;
+
+ /**
+ * @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
+ */
+
+ /**
+ * The fields of this embed
+ * @type {EmbedField[]}
+ */
+ this.fields = [];
+ if (data.fields) {
+ this.fields = skipValidation ? data.fields.map(Util.cloneObject) : this.constructor.normalizeFields(data.fields);
+ }
+
+ /**
+ * @typedef {Object} MessageEmbedThumbnail
+ * @property {string} url URL for this thumbnail
+ * @property {string} proxyURL ProxyURL for this thumbnail
+ * @property {number} height Height of this thumbnail
+ * @property {number} width Width of this thumbnail
+ */
+
+ /**
+ * The thumbnail of this embed (if there is one)
+ * @type {?MessageEmbedThumbnail}
+ */
+ this.thumbnail = data.thumbnail
+ ? {
+ url: data.thumbnail.url,
+ proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url,
+ height: data.thumbnail.height,
+ width: data.thumbnail.width,
+ }
+ : null;
+
+ /**
+ * @typedef {Object} MessageEmbedImage
+ * @property {string} url URL for this image
+ * @property {string} proxyURL ProxyURL for this image
+ * @property {number} height Height of this image
+ * @property {number} width Width of this image
+ */
+
+ /**
+ * The image of this embed, if there is one
+ * @type {?MessageEmbedImage}
+ */
+ this.image = data.image
+ ? {
+ url: data.image.url,
+ proxyURL: data.image.proxyURL || data.image.proxy_url,
+ height: data.image.height,
+ width: data.image.width,
+ }
+ : null;
+
+ /**
+ * @typedef {Object} MessageEmbedVideo
+ * @property {string} url URL of this video
+ * @property {string} proxyURL ProxyURL for this video
+ * @property {number} height Height of this video
+ * @property {number} width Width of this video
+ */
+
+ /**
+ * The video of this embed (if there is one)
+ * @type {?MessageEmbedVideo}
+ * @readonly
+ */
+ this.video = data.video
+ ? {
+ url: data.video.url,
+ proxyURL: data.video.proxyURL || data.video.proxy_url,
+ height: data.video.height,
+ width: data.video.width,
+ }
+ : null;
+
+ /**
+ * @typedef {Object} MessageEmbedAuthor
+ * @property {string} name The name of this author
+ * @property {string} url URL of this author
+ * @property {string} iconURL URL of the icon for this author
+ * @property {string} proxyIconURL Proxied URL of the icon for this author
+ */
+
+ /**
+ * The author of this embed (if there is one)
+ * @type {?MessageEmbedAuthor}
+ */
+ this.author = data.author
+ ? {
+ name: data.author.name,
+ url: data.author.url,
+ iconURL: data.author.iconURL || data.author.icon_url,
+ proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url,
+ }
+ : null;
+
+ /**
+ * @typedef {Object} MessageEmbedProvider
+ * @property {string} name The name of this provider
+ * @property {string} url URL of this provider
+ */
+
+ /**
+ * The provider of this embed (if there is one)
+ * @type {?MessageEmbedProvider}
+ */
+ this.provider = data.provider
+ ? {
+ name: data.provider.name,
+ url: data.provider.name,
+ }
+ : null;
+
+ /**
+ * @typedef {Object} MessageEmbedFooter
+ * @property {string} text The text of this footer
+ * @property {string} iconURL URL of the icon for this footer
+ * @property {string} proxyIconURL Proxied URL of the icon for this footer
+ */
+
+ /**
+ * The footer of this embed
+ * @type {?MessageEmbedFooter}
+ */
+ this.footer = data.footer
+ ? {
+ text: data.footer.text,
+ iconURL: data.footer.iconURL || data.footer.icon_url,
+ proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url,
+ }
+ : null;
+
+ /**
+ * The files of this embed
+ * @type {Array<FileOptions|string|MessageAttachment>}
+ */
+ this.files = data.files || [];
+ }
+
+ /**
+ * The date displayed on this embed
+ * @type {?Date}
+ * @readonly
+ */
+ get createdAt() {
+ return this.timestamp ? new Date(this.timestamp) : null;
+ }
+
+ /**
+ * The hexadecimal version of the embed color, with a leading hash
+ * @type {?string}
+ * @readonly
+ */
+ get hexColor() {
+ return this.color ? `#${this.color.toString(16).padStart(6, '0')}` : null;
+ }
+
+ /**
+ * The accumulated length for the embed title, description, fields 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)
+ );
+ }
+
+ /**
+ * Adds a field to the embed (max 25).
+ * @param {StringResolvable} name The name of this field
+ * @param {StringResolvable} value The value of this field
+ * @param {boolean} [inline=false] If this field will be displayed inline
+ * @returns {MessageEmbed}
+ */
+ addField(name, value, inline) {
+ return this.addFields({ name, value, inline });
+ }
+
+ /**
+ * Adds fields to the embed (max 25).
+ * @param {...EmbedFieldData|EmbedFieldData[]} fields The fields to add
+ * @returns {MessageEmbed}
+ */
+ addFields(...fields) {
+ this.fields.push(...this.constructor.normalizeFields(fields));
+ return this;
+ }
+
+ /**
+ * 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|EmbedFieldData[]} [fields] The replacing field objects
+ * @returns {MessageEmbed}
+ */
+ spliceFields(index, deleteCount, ...fields) {
+ this.fields.splice(index, deleteCount, ...this.constructor.normalizeFields(...fields));
+ 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. Multiple files can be attached.
+ * @param {Array<FileOptions|string|MessageAttachment>} files Files to attach
+ * @returns {MessageEmbed}
+ */
+ attachFiles(files) {
+ this.files = this.files.concat(files);
+ return this;
+ }
+
+ /**
+ * Sets the author of this embed.
+ * @param {StringResolvable} name The name of the author
+ * @param {string} [iconURL] The icon URL of the author
+ * @param {string} [url] The URL of the author
+ * @returns {MessageEmbed}
+ */
+ setAuthor(name, iconURL, url) {
+ this.author = { name: Util.resolveString(name), iconURL, url };
+ return this;
+ }
+
+ /**
+ * Sets the color of this embed.
+ * @param {ColorResolvable} color The color of the embed
+ * @returns {MessageEmbed}
+ */
+ setColor(color) {
+ this.color = Util.resolveColor(color);
+ return this;
+ }
+
+ /**
+ * Sets the description of this embed.
+ * @param {StringResolvable} description The description
+ * @returns {MessageEmbed}
+ */
+ setDescription(description) {
+ description = Util.resolveString(description);
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Sets the footer of this embed.
+ * @param {StringResolvable} text The text of the footer
+ * @param {string} [iconURL] The icon URL of the footer
+ * @returns {MessageEmbed}
+ */
+ setFooter(text, iconURL) {
+ text = Util.resolveString(text);
+ this.footer = { text, iconURL };
+ return this;
+ }
+
+ /**
+ * Sets the image of this embed.
+ * @param {string} url The URL of the image
+ * @returns {MessageEmbed}
+ */
+ setImage(url) {
+ this.image = { url };
+ return this;
+ }
+
+ /**
+ * Sets the thumbnail of this embed.
+ * @param {string} url The URL of the thumbnail
+ * @returns {MessageEmbed}
+ */
+ setThumbnail(url) {
+ this.thumbnail = { url };
+ return this;
+ }
+
+ /**
+ * Sets the timestamp of this embed.
+ * @param {Date|number} [timestamp=Date.now()] The timestamp or date
+ * @returns {MessageEmbed}
+ */
+ setTimestamp(timestamp = Date.now()) {
+ if (timestamp instanceof Date) timestamp = timestamp.getTime();
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * Sets the title of this embed.
+ * @param {StringResolvable} title The title
+ * @returns {MessageEmbed}
+ */
+ setTitle(title) {
+ title = Util.resolveString(title);
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * Sets the URL of this embed.
+ * @param {string} url The URL
+ * @returns {MessageEmbed}
+ */
+ setURL(url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * 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,
+ thumbnail: this.thumbnail,
+ image: this.image,
+ author: this.author
+ ? {
+ name: this.author.name,
+ url: this.author.url,
+ icon_url: this.author.iconURL,
+ }
+ : null,
+ footer: this.footer
+ ? {
+ text: this.footer.text,
+ icon_url: this.footer.iconURL,
+ }
+ : 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) throw new RangeError('EMBED_FIELD_NAME');
+ value = Util.resolveString(value);
+ if (!value) throw new RangeError('EMBED_FIELD_VALUE');
+ return { name, value, inline };
+ }
+
+ /**
+ * @typedef {Object} EmbedFieldData
+ * @property {StringResolvable} name The name of this field
+ * @property {StringResolvable} value The value of this field
+ * @property {boolean} [inline] If this field will be displayed inline
+ */
+
+ /**
+ * Normalizes field input and resolves strings.
+ * @param {...EmbedFieldData|EmbedFieldData[]} fields Fields to normalize
+ * @returns {EmbedField[]}
+ */
+ static normalizeFields(...fields) {
+ return fields
+ .flat(2)
+ .map(field =>
+ this.normalizeField(
+ field && field.name,
+ field && field.value,
+ field && typeof field.inline === 'boolean' ? field.inline : false,
+ ),
+ );
+ }
+}
+
+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..cbdd1c7
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageMentions.js
@@ -0,0 +1,221 @@
+'use strict';
+
+const Collection = require('../util/Collection');
+const { ChannelTypes } = require('../util/Constants');
+const Util = require('../util/Util');
+
+/**
+ * Keeps track of mentions in a {@link Message}.
+ */
+class MessageMentions {
+ constructor(message, users, roles, everyone, crosspostedChannels) {
+ /**
+ * The client the message is from
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: message.client });
+
+ /**
+ * The guild the message is in
+ * @type {?Guild}
+ * @readonly
+ */
+ Object.defineProperty(this, 'guild', { value: message.guild });
+
+ /**
+ * The initial message content
+ * @type {string}
+ * @readonly
+ * @private
+ */
+ Object.defineProperty(this, '_content', { value: message.content });
+
+ /**
+ * 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) {
+ if (mention.member && message.guild) {
+ message.guild.members.add(Object.assign(mention.member, { user: mention }));
+ }
+ const user = message.client.users.add(mention);
+ this.users.set(user.id, user);
+ }
+ }
+ } 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</info>
+ * @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.cache.get(mention);
+ if (role) this.roles.set(role.id, role);
+ }
+ }
+ } else {
+ this.roles = new Collection();
+ }
+
+ /**
+ * Cached members for {@link MessageMention#members}
+ * @type {?Collection<Snowflake, GuildMember>}
+ * @private
+ */
+ this._members = null;
+
+ /**
+ * Cached channels for {@link MessageMention#channels}
+ * @type {?Collection<Snowflake, GuildChannel>}
+ * @private
+ */
+ this._channels = null;
+
+ /**
+ * Crossposted channel data.
+ * @typedef {Object} CrosspostedChannel
+ * @property {string} channelID ID of the mentioned channel
+ * @property {string} guildID ID of the guild that has the channel
+ * @property {string} type Type of the channel
+ * @property {string} name The name of the channel
+ */
+
+ if (crosspostedChannels) {
+ if (crosspostedChannels instanceof Collection) {
+ /**
+ * A collection of crossposted channels
+ * <info>Order as received from the API, not as they appear in the message content</info>
+ * @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</info>
+ * @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.cache.get(matches[1]);
+ if (chan) this._channels.set(chan.id, chan);
+ }
+ return this._channels;
+ }
+
+ /**
+ * Checks if a user, guild member, role, or channel is mentioned.
+ * Takes into account user mentions, role mentions, and @everyone/@here mentions.
+ * @param {UserResolvable|GuildMember|Role|GuildChannel} data User/GuildMember/Role/Channel to check
+ * @param {Object} [options] Options
+ * @param {boolean} [options.ignoreDirect=false] - Whether to ignore direct mentions to the item
+ * @param {boolean} [options.ignoreRoles=false] - Whether to ignore role mentions to a guild member
+ * @param {boolean} [options.ignoreEveryone=false] - Whether to ignore everyone/here mentions
+ * @returns {boolean}
+ */
+ has(data, { ignoreDirect = false, ignoreRoles = false, ignoreEveryone = false } = {}) {
+ if (!ignoreEveryone && this.everyone) return true;
+ const GuildMember = require('./GuildMember');
+ if (!ignoreRoles && data instanceof GuildMember) {
+ for (const role of this.roles.values()) if (data.roles.cache.has(role.id)) return true;
+ }
+
+ if (!ignoreDirect) {
+ const id = data.id || data;
+ return this.users.has(id) || this.channels.has(id) || this.roles.has(id);
+ }
+
+ return false;
+ }
+
+ toJSON() {
+ return Util.flatten(this, {
+ members: true,
+ channels: true,
+ });
+ }
+}
+
+/**
+ * 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 = /<@!?(\d{17,19})>/g;
+
+/**
+ * Regular expression that globally matches role mentions like `<@&297577916114403338>`
+ * @type {RegExp}
+ */
+MessageMentions.ROLES_PATTERN = /<@&(\d{17,19})>/g;
+
+/**
+ * Regular expression that globally matches channel mentions like `<#222079895583457280>`
+ * @type {RegExp}
+ */
+MessageMentions.CHANNELS_PATTERN = /<#(\d{17,19})>/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..771626e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/MessageReaction.js
@@ -0,0 +1,135 @@
+'use strict';
+
+const GuildEmoji = require('./GuildEmoji');
+const ReactionEmoji = require('./ReactionEmoji');
+const ReactionUserManager = require('../managers/ReactionUserManager');
+const Util = require('../util/Util');
+
+/**
+ * Represents a reaction to a message.
+ */
+class MessageReaction {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the message reaction
+ * @param {Message} message The message the reaction refers to
+ */
+ constructor(client, data, message) {
+ /**
+ * The client that instantiated this message reaction
+ * @name MessageReaction#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ /**
+ * The message that this reaction refers to
+ * @type {Message}
+ */
+ this.message = message;
+
+ /**
+ * Whether the client has given this reaction
+ * @type {boolean}
+ */
+ this.me = data.me;
+
+ /**
+ * A manager of the users that have given this reaction
+ * @type {ReactionUserManager}
+ */
+ this.users = new ReactionUserManager(client, undefined, this);
+
+ this._emoji = new ReactionEmoji(this, data.emoji);
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The number of people that have given the same reaction
+ * @type {?number}
+ * @name MessageReaction#count
+ */
+ // eslint-disable-next-line eqeqeq
+ if (this.count == undefined) this.count = data.count;
+ }
+
+ /**
+ * Removes all users from this reaction.
+ * @returns {Promise<MessageReaction>}
+ */
+ async remove() {
+ await this.client.api
+ .channels(this.message.channel.id)
+ .messages(this.message.id)
+ .reactions(this._emoji.identifier)
+ .delete();
+ return this;
+ }
+
+ /**
+ * The emoji of this reaction, either an GuildEmoji 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 {GuildEmoji|ReactionEmoji}
+ * @readonly
+ */
+ get emoji() {
+ if (this._emoji instanceof GuildEmoji) 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.cache;
+ if (emojis.has(this._emoji.id)) {
+ const emoji = emojis.get(this._emoji.id);
+ this._emoji = emoji;
+ return emoji;
+ }
+ }
+ return this._emoji;
+ }
+
+ /**
+ * Whether or not this reaction is a partial
+ * @type {boolean}
+ * @readonly
+ */
+ get partial() {
+ return this.count === null;
+ }
+
+ /**
+ * Fetch this reaction.
+ * @returns {Promise<MessageReaction>}
+ */
+ async fetch() {
+ const message = await this.message.fetch();
+ const existing = message.reactions.cache.get(this.emoji.id || this.emoji.name);
+ // The reaction won't get set when it has been completely removed
+ this._patch(existing || { count: 0 });
+ return this;
+ }
+
+ toJSON() {
+ return Util.flatten(this, { emoji: 'emojiID', message: 'messageID' });
+ }
+
+ _add(user) {
+ if (this.partial) return;
+ this.users.cache.set(user.id, user);
+ if (!this.me || user.id !== this.message.client.user.id || this.count === 0) this.count++;
+ if (!this.me) this.me = user.id === this.message.client.user.id;
+ }
+
+ _remove(user) {
+ if (this.partial) return;
+ this.users.cache.delete(user.id);
+ if (!this.me || user.id !== this.message.client.user.id) this.count--;
+ if (user.id === this.message.client.user.id) this.me = false;
+ if (this.count <= 0 && this.users.cache.size === 0) {
+ this.message.reactions.cache.delete(this.emoji.id || this.emoji.name);
+ }
+ }
+}
+
+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..76727fc
--- /dev/null
+++ b/node_modules/discord.js/src/structures/NewsChannel.js
@@ -0,0 +1,18 @@
+'use strict';
+
+const TextChannel = require('./TextChannel');
+
+/**
+ * Represents a guild news channel on Discord.
+ * @extends {TextChannel}
+ */
+class NewsChannel extends TextChannel {
+ _patch(data) {
+ super._patch(data);
+
+ // News channels don't have a rate limit per user, remove it
+ this.rateLimitPerUser = undefined;
+ }
+}
+
+module.exports = NewsChannel;
diff --git a/node_modules/discord.js/src/structures/PartialGroupDMChannel.js b/node_modules/discord.js/src/structures/PartialGroupDMChannel.js
new file mode 100644
index 0000000..e398f23
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PartialGroupDMChannel.js
@@ -0,0 +1,46 @@
+'use strict';
+
+const Channel = require('./Channel');
+const { Error } = require('../errors');
+
+/**
+ * Represents a Partial Group DM Channel on Discord.
+ * @extends {Channel}
+ */
+class PartialGroupDMChannel extends Channel {
+ constructor(client, data) {
+ super(client, data);
+
+ /**
+ * The name of this Group DM Channel
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The hash of the channel icon
+ * @type {?string}
+ */
+ this.icon = data.icon;
+ }
+
+ /**
+ * The URL to this channel's icon.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ iconURL({ format, size } = {}) {
+ if (!this.icon) return null;
+ return this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size);
+ }
+
+ delete() {
+ return Promise.reject(new Error('DELETE_GROUP_DM_CHANNEL'));
+ }
+
+ fetch() {
+ return Promise.reject(new Error('FETCH_GROUP_DM_CHANNEL'));
+ }
+}
+
+module.exports = PartialGroupDMChannel;
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..ea7a45e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/PermissionOverwrites.js
@@ -0,0 +1,189 @@
+'use strict';
+
+const Role = require('./Role');
+const { TypeError } = require('../errors');
+const Permissions = require('../util/Permissions');
+const Util = require('../util/Util');
+
+/**
+ * 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._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The ID of this overwrite, either a user ID or a role ID
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * The type of a permission overwrite. It can be one of:
+ * * member
+ * * role
+ * @typedef {string} OverwriteType
+ */
+
+ /**
+ * The type of this overwrite
+ * @type {OverwriteType}
+ */
+ this.type = data.type;
+
+ /**
+ * The permissions that are denied for the user or role.
+ * @type {Readonly<Permissions>}
+ */
+ this.deny = new Permissions(data.deny).freeze();
+
+ /**
+ * The permissions that are allowed for the user or role.
+ * @type {Readonly<Permissions>}
+ */
+ this.allow = new Permissions(data.allow).freeze();
+ }
+
+ /**
+ * Updates this permissionOverwrites.
+ * @param {PermissionOverwriteOptions} options The options for the update
+ * @param {string} [reason] Reason for creating/editing this overwrite
+ * @returns {Promise<PermissionOverwrites>}
+ * @example
+ * // Update permission overwrites
+ * permissionOverwrites.update({
+ * SEND_MESSAGES: false
+ * })
+ * .then(channel => console.log(channel.permissionOverwrites.get(message.author.id)))
+ * .catch(console.error);
+ */
+ update(options, reason) {
+ const { allow, deny } = this.constructor.resolveOverwriteOptions(options, this);
+
+ return this.channel.client.api
+ .channels(this.channel.id)
+ .permissions[this.id].put({
+ data: { id: this.id, type: this.type, allow: allow.bitfield, deny: deny.bitfield },
+ reason,
+ })
+ .then(() => this);
+ }
+
+ /**
+ * Deletes this Permission Overwrite.
+ * @param {string} [reason] Reason for deleting this overwrite
+ * @returns {Promise<PermissionOverwrites>}
+ */
+ delete(reason) {
+ return this.channel.client.api.channels[this.channel.id].permissions[this.id].delete({ reason }).then(() => this);
+ }
+
+ toJSON() {
+ return Util.flatten(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
+ */
+
+ /**
+ * @typedef {object} ResolvedOverwriteOptions
+ * @property {Permissions} allow The allowed permissions
+ * @property {Permissions} deny The denied permissions
+ */
+
+ /**
+ * Resolves bitfield permissions overwrites from an object.
+ * @param {PermissionOverwriteOptions} options The options for the update
+ * @param {Object} initialPermissions The initial permissions
+ * @param {PermissionResolvable} initialPermissions.allow Initial allowed permissions
+ * @param {PermissionResolvable} initialPermissions.deny Initial denied permissions
+ * @returns {ResolvedOverwriteOptions}
+ */
+ static resolveOverwriteOptions(options, { allow, deny } = {}) {
+ allow = new Permissions(allow);
+ deny = new Permissions(deny);
+
+ for (const [perm, value] of Object.entries(options)) {
+ if (value === true) {
+ allow.add(Permissions.FLAGS[perm]);
+ deny.remove(Permissions.FLAGS[perm]);
+ } else if (value === false) {
+ allow.remove(Permissions.FLAGS[perm]);
+ deny.add(Permissions.FLAGS[perm]);
+ } else if (value === null) {
+ allow.remove(Permissions.FLAGS[perm]);
+ deny.remove(Permissions.FLAGS[perm]);
+ }
+ }
+
+ return { allow, deny };
+ }
+
+ /**
+ * The raw data for a permission overwrite
+ * @typedef {Object} RawOverwriteData
+ * @property {Snowflake} id The id of the overwrite
+ * @property {number} allow The permissions to allow
+ * @property {number} deny The permissions to deny
+ * @property {OverwriteType} type The type of this OverwriteData
+ */
+
+ /**
+ * Data that can be resolved into {@link RawOverwriteData}
+ * @typedef {PermissionOverwrites|OverwriteData} OverwriteResolvable
+ */
+
+ /**
+ * Data that can be used for a permission overwrite
+ * @typedef {Object} OverwriteData
+ * @property {GuildMemberResolvable|RoleResolvable} id Member or role this overwrite is for
+ * @property {PermissionResolvable} [allow] The permissions to allow
+ * @property {PermissionResolvable} [deny] The permissions to deny
+ * @property {OverwriteType} [type] The type of this OverwriteData
+ */
+
+ /**
+ * Resolves an overwrite into {@link RawOverwriteData}.
+ * @param {OverwriteResolvable} overwrite The overwrite-like data to resolve
+ * @param {Guild} guild The guild to resolve from
+ * @returns {RawOverwriteData}
+ */
+ static resolve(overwrite, guild) {
+ if (overwrite instanceof this) return overwrite.toJSON();
+ if (typeof overwrite.id === 'string' && ['role', 'member'].includes(overwrite.type)) {
+ return { ...overwrite, allow: Permissions.resolve(overwrite.allow), deny: Permissions.resolve(overwrite.deny) };
+ }
+
+ const userOrRole = guild.roles.resolve(overwrite.id) || guild.client.users.resolve(overwrite.id);
+ if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role', true);
+ const type = userOrRole instanceof Role ? 'role' : 'member';
+
+ return {
+ id: userOrRole.id,
+ type,
+ allow: Permissions.resolve(overwrite.allow),
+ deny: Permissions.resolve(overwrite.deny),
+ };
+ }
+}
+
+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..ac07a54
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Presence.js
@@ -0,0 +1,336 @@
+'use strict';
+
+const Emoji = require('./Emoji');
+const ActivityFlags = require('../util/ActivityFlags');
+const { ActivityTypes } = require('../util/Constants');
+const Util = require('../util/Util');
+
+/**
+ * Activity sent in a message.
+ * @typedef {Object} MessageActivity
+ * @property {string} [partyID] Id of the party represented in activity
+ * @property {number} [type] Type of activity sent
+ */
+
+/**
+ * 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 {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} [data={}] The data for the presence
+ */
+ constructor(client, data = {}) {
+ /**
+ * The client that instantiated this
+ * @name Presence#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ /**
+ * The user ID of this presence
+ * @type {Snowflake}
+ */
+ this.userID = data.user.id;
+
+ /**
+ * The guild of this presence
+ * @type {?Guild}
+ */
+ this.guild = data.guild || null;
+
+ this.patch(data);
+ }
+
+ /**
+ * The user of this presence
+ * @type {?User}
+ * @readonly
+ */
+ get user() {
+ return this.client.users.cache.get(this.userID) || null;
+ }
+
+ /**
+ * The member of this presence
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get member() {
+ return this.guild.members.cache.get(this.userID) || null;
+ }
+
+ patch(data) {
+ /**
+ * The status of this presence
+ * @type {PresenceStatus}
+ */
+ this.status = data.status || this.status || 'offline';
+
+ if (data.activities) {
+ /**
+ * The activities of this presence
+ * @type {Activity[]}
+ */
+ this.activities = data.activities.map(activity => new Activity(this, activity));
+ } else if (data.activity || data.game) {
+ this.activities = [new Activity(this, data.game || data.activity)];
+ } 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;
+
+ return this;
+ }
+
+ _clone() {
+ const clone = Object.assign(Object.create(this), this);
+ if (this.activities) clone.activities = this.activities.map(activity => activity._clone());
+ return clone;
+ }
+
+ /**
+ * 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)
+ );
+ }
+
+ toJSON() {
+ return Util.flatten(this);
+ }
+}
+
+/**
+ * Represents an activity that is part of a user's presence.
+ */
+class Activity {
+ constructor(presence, data) {
+ Object.defineProperty(this, 'presence', { value: presence });
+
+ /**
+ * The name of the activity being played
+ * @type {string}
+ */
+ this.name = data.name;
+
+ /**
+ * The type of the activity status
+ * @type {ActivityType}
+ */
+ this.type = ActivityTypes[data.type];
+
+ /**
+ * If the activity 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;
+
+ this.syncID = data.sync_id;
+
+ /**
+ * Flags that describe the activity
+ * @type {Readonly<ActivityFlags>}
+ */
+ this.flags = new ActivityFlags(data.flags).freeze();
+
+ /**
+ * Emoji for a custom activity
+ * @type {?Emoji}
+ */
+ this.emoji = data.emoji ? new Emoji(presence.client, data.emoji) : null;
+
+ /**
+ * Creation date of the activity
+ * @type {number}
+ */
+ this.createdTimestamp = new Date(data.created_at).getTime();
+ }
+
+ /**
+ * Whether this activity is equal to another activity.
+ * @param {Activity} activity The activity to compare with
+ * @returns {boolean}
+ */
+ equals(activity) {
+ return (
+ this === activity ||
+ (activity && this.name === activity.name && this.type === activity.type && this.url === activity.url)
+ );
+ }
+
+ /**
+ * The time the activity was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
+ * @returns {string}
+ */
+ toString() {
+ return this.name;
+ }
+
+ _clone() {
+ return Object.assign(Object.create(this), this);
+ }
+}
+
+/**
+ * Assets for a rich presence
+ */
+class RichPresenceAssets {
+ constructor(activity, assets) {
+ Object.defineProperty(this, 'activity', { value: activity });
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Gets the URL of the small image asset
+ * @param {Object} [options] Options for the image url
+ * @param {string} [options.format] Format of the image
+ * @param {number} [options.size] Size of the image
+ * @returns {?string} The small image URL
+ */
+ smallImageURL({ format, size } = {}) {
+ if (!this.smallImage) return null;
+ return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.smallImage, {
+ format,
+ size,
+ });
+ }
+
+ /**
+ * Gets the URL of the large image asset
+ * @param {Object} [options] Options for the image url
+ * @param {string} [options.format] Format of the image
+ * @param {number} [options.size] Size of the image
+ * @returns {?string} The large image URL
+ */
+ largeImageURL({ format, size } = {}) {
+ 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 this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.largeImage, {
+ format,
+ size,
+ });
+ }
+}
+
+exports.Presence = Presence;
+exports.Activity = Activity;
+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..6da9d17
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ReactionCollector.js
@@ -0,0 +1,190 @@
+'use strict';
+
+const Collector = require('./interfaces/Collector');
+const Collection = require('../util/Collection');
+const { Events } = require('../util/Constants');
+
+/**
+ * @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.
+ * Will automatically stop if the message (`'messageDelete'`),
+ * channel (`'channelDelete'`), or guild (`'guildDelete'`) are deleted.
+ * @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 upon which to collect reactions
+ * @type {Message}
+ */
+ this.message = message;
+
+ /**
+ * The users which have reacted to this message
+ * @type {Collection}
+ */
+ this.users = new Collection();
+
+ /**
+ * The total number of reactions collected
+ * @type {number}
+ */
+ this.total = 0;
+
+ this.empty = this.empty.bind(this);
+ this._handleChannelDeletion = this._handleChannelDeletion.bind(this);
+ this._handleGuildDeletion = this._handleGuildDeletion.bind(this);
+ this._handleMessageDeletion = this._handleMessageDeletion.bind(this);
+
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() + 1);
+ this.client.on(Events.MESSAGE_REACTION_ADD, this.handleCollect);
+ this.client.on(Events.MESSAGE_REACTION_REMOVE, this.handleDispose);
+ this.client.on(Events.MESSAGE_REACTION_REMOVE_ALL, this.empty);
+ this.client.on(Events.MESSAGE_DELETE, this._handleMessageDeletion);
+ this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion);
+ this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion);
+
+ this.once('end', () => {
+ this.client.removeListener(Events.MESSAGE_REACTION_ADD, this.handleCollect);
+ this.client.removeListener(Events.MESSAGE_REACTION_REMOVE, this.handleDispose);
+ this.client.removeListener(Events.MESSAGE_REACTION_REMOVE_ALL, this.empty);
+ this.client.removeListener(Events.MESSAGE_DELETE, this._handleMessageDeletion);
+ this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion);
+ this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion);
+ if (this.client.getMaxListeners() !== 0) this.client.setMaxListeners(this.client.getMaxListeners() - 1);
+ });
+
+ this.on('collect', (reaction, user) => {
+ this.total++;
+ this.users.set(user.id, user);
+ });
+
+ this.on('remove', (reaction, user) => {
+ this.total--;
+ if (!this.collected.some(r => r.users.cache.has(user.id))) this.users.delete(user.id);
+ });
+ }
+
+ /**
+ * Handles an incoming reaction for possible collection.
+ * @param {MessageReaction} reaction The reaction to possibly collect
+ * @returns {?Snowflake|string}
+ * @private
+ */
+ collect(reaction) {
+ /**
+ * Emitted whenever a reaction is collected.
+ * @event ReactionCollector#collect
+ * @param {MessageReaction} reaction The reaction that was collected
+ * @param {User} user The user that added the reaction
+ */
+ if (reaction.message.id !== this.message.id) return null;
+ return ReactionCollector.key(reaction);
+ }
+
+ /**
+ * Handles a reaction deletion for possible disposal.
+ * @param {MessageReaction} reaction The reaction to possibly dispose of
+ * @param {User} user The user that removed the reaction
+ * @returns {?Snowflake|string}
+ */
+ dispose(reaction, user) {
+ /**
+ * Emitted whenever a reaction is disposed of.
+ * @event ReactionCollector#dispose
+ * @param {MessageReaction} reaction The reaction that was disposed of
+ * @param {User} user The user that removed the reaction
+ */
+ if (reaction.message.id !== this.message.id) return null;
+
+ /**
+ * Emitted whenever a reaction is removed from a message. Will emit on all reaction removals,
+ * as opposed to {@link Collector#dispose} which will only be emitted when the entire reaction
+ * is removed.
+ * @event ReactionCollector#remove
+ * @param {MessageReaction} reaction The reaction that was removed
+ * @param {User} user The user that removed the reaction
+ */
+ if (this.collected.has(ReactionCollector.key(reaction)) && this.users.has(user.id)) {
+ this.emit('remove', reaction, user);
+ }
+ return reaction.count ? null : ReactionCollector.key(reaction);
+ }
+
+ /**
+ * Empties this reaction collector.
+ */
+ empty() {
+ this.total = 0;
+ this.collected.clear();
+ this.users.clear();
+ this.checkEnd();
+ }
+
+ endReason() {
+ 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;
+ }
+
+ /**
+ * Handles checking if the message has been deleted, and if so, stops the collector with the reason 'messageDelete'.
+ * @private
+ * @param {Message} message The message that was deleted
+ * @returns {void}
+ */
+ _handleMessageDeletion(message) {
+ if (message.id === this.message.id) {
+ this.stop('messageDelete');
+ }
+ }
+
+ /**
+ * Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'.
+ * @private
+ * @param {GuildChannel} channel The channel that was deleted
+ * @returns {void}
+ */
+ _handleChannelDeletion(channel) {
+ if (channel.id === this.message.channel.id) {
+ this.stop('channelDelete');
+ }
+ }
+
+ /**
+ * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'.
+ * @private
+ * @param {Guild} guild The guild that was deleted
+ * @returns {void}
+ */
+ _handleGuildDeletion(guild) {
+ if (this.message.guild && guild.id === this.message.guild.id) {
+ this.stop('guildDelete');
+ }
+ }
+
+ /**
+ * Gets the collector key for a reaction.
+ * @param {MessageReaction} reaction The message reaction to get the key for
+ * @returns {Snowflake|string}
+ */
+ static key(reaction) {
+ return reaction.emoji.id || reaction.emoji.name;
+ }
+}
+
+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..5c4bc13
--- /dev/null
+++ b/node_modules/discord.js/src/structures/ReactionEmoji.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const Emoji = require('./Emoji');
+const Util = require('../util/Util');
+
+/**
+ * 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.
+ * @extends {Emoji}
+ */
+class ReactionEmoji extends Emoji {
+ constructor(reaction, emoji) {
+ super(reaction.message.client, emoji);
+ /**
+ * The message reaction this emoji refers to
+ * @type {MessageReaction}
+ */
+ this.reaction = reaction;
+ }
+
+ toJSON() {
+ return Util.flatten(this, { identifier: true });
+ }
+
+ valueOf() {
+ return this.id;
+ }
+}
+
+module.exports = ReactionEmoji;
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..bba198e
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Role.js
@@ -0,0 +1,403 @@
+'use strict';
+
+const Base = require('./Base');
+const { Error, TypeError } = require('../errors');
+const Permissions = require('../util/Permissions');
+const Snowflake = require('../util/Snowflake');
+const Util = require('../util/Util');
+
+/**
+ * Represents a role on Discord.
+ * @extends {Base}
+ */
+class Role extends Base {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the role
+ * @param {Guild} guild The guild the role is part of
+ */
+ constructor(client, data, guild) {
+ super(client);
+
+ /**
+ * The guild that the role belongs to
+ * @type {Guild}
+ */
+ this.guild = guild;
+
+ if (data) this._patch(data);
+ }
+
+ _patch(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 raw position of the role from the API
+ * @type {number}
+ */
+ this.rawPosition = data.position;
+
+ /**
+ * The permissions of the role
+ * @type {Readonly<Permissions>}
+ */
+ this.permissions = new Permissions(data.permissions).freeze();
+
+ /**
+ * 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;
+
+ /**
+ * Whether the role has been deleted
+ * @type {boolean}
+ */
+ this.deleted = false;
+ }
+
+ /**
+ * The timestamp the role was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the role was created at
+ * @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() {
+ return `#${this.color.toString(16).padStart(6, '0')}`;
+ }
+
+ /**
+ * The cached guild members that have this role
+ * @type {Collection<Snowflake, GuildMember>}
+ * @readonly
+ */
+ get members() {
+ return this.guild.members.cache.filter(m => m.roles.cache.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)) return false;
+ return clientMember.roles.highest.comparePositionTo(this) > 0;
+ }
+
+ /**
+ * The position of the role in the role manager
+ * @type {number}
+ * @readonly
+ */
+ get position() {
+ const sorted = this.guild._sortedRoles();
+ return sorted.array().indexOf(sorted.get(this.id));
+ }
+
+ /**
+ * Compares this role's position to another role's.
+ * @param {RoleResolvable} 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) {
+ role = this.guild.roles.resolve(role);
+ if (!role) throw new TypeError('INVALID_TYPE', 'role', 'Role nor a Snowflake');
+ 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} [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] Reason for editing this role
+ * @returns {Promise<Role>}
+ * @example
+ * // Edit a role
+ * role.edit({ name: 'new role' })
+ * .then(updated => console.log(`Edited role ${updated.name} name to ${updated.name}`))
+ * .catch(console.error);
+ */
+ async edit(data, reason) {
+ if (typeof data.permissions !== 'undefined') data.permissions = Permissions.resolve(data.permissions);
+ else data.permissions = this.permissions.bitfield;
+ if (typeof data.position !== 'undefined') {
+ await Util.setPosition(
+ this,
+ data.position,
+ false,
+ this.guild._sortedRoles(),
+ this.client.api.guilds(this.guild.id).roles,
+ reason,
+ ).then(updatedRoles => {
+ this.client.actions.GuildRolesPositionUpdate.handle({
+ guild_id: this.guild.id,
+ roles: updatedRoles,
+ });
+ });
+ }
+ return this.client.api.guilds[this.guild.id].roles[this.id]
+ .patch({
+ data: {
+ name: data.name || this.name,
+ color: data.color !== null ? Util.resolveColor(data.color || this.color) : null,
+ hoist: typeof data.hoist !== 'undefined' ? data.hoist : this.hoist,
+ permissions: data.permissions,
+ mentionable: typeof data.mentionable !== 'undefined' ? data.mentionable : this.mentionable,
+ },
+ reason,
+ })
+ .then(role => {
+ const clone = this._clone();
+ clone._patch(role);
+ return clone;
+ });
+ }
+
+ /**
+ * Returns `channel.permissionsFor(role)`. Returns permissions for a role in a guild channel,
+ * taking into account permission overwrites.
+ * @param {ChannelResolvable} channel The guild channel to use as context
+ * @returns {Readonly<Permissions>}
+ */
+ permissionsIn(channel) {
+ channel = this.guild.channels.resolve(channel);
+ if (!channel) throw new Error('GUILD_CHANNEL_RESOLVE');
+ return channel.rolePermissions(this);
+ }
+
+ /**
+ * Sets 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 role')
+ * .then(updated => console.log(`Edited name of role ${role.name} to ${updated.name}`))
+ * .catch(console.error);
+ */
+ setName(name, reason) {
+ return this.edit({ name }, reason);
+ }
+
+ /**
+ * Sets 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 ${updated.color}`))
+ * .catch(console.error);
+ */
+ setColor(color, reason) {
+ return this.edit({ color }, reason);
+ }
+
+ /**
+ * Sets 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(r => console.log(`Role hoisted: ${r.hoist}`))
+ * .catch(console.error);
+ */
+ setHoist(hoist, reason) {
+ return this.edit({ hoist }, reason);
+ }
+
+ /**
+ * Sets 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);
+ }
+
+ /**
+ * Sets 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)
+ * .then(updated => console.log(`Role updated ${updated.name}`))
+ * .catch(console.error);
+ */
+ setMentionable(mentionable, reason) {
+ return this.edit({ mentionable }, reason);
+ }
+
+ /**
+ * Sets the position of the role.
+ * @param {number} position The position of the role
+ * @param {Object} [options] Options for setting position
+ * @param {boolean} [options.relative=false] Change the position relative to its current value
+ * @param {string} [options.reason] Reason for changing the position
+ * @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, reason } = {}) {
+ return Util.setPosition(
+ this,
+ position,
+ relative,
+ this.guild._sortedRoles(),
+ this.client.api.guilds(this.guild.id).roles,
+ reason,
+ ).then(updatedRoles => {
+ this.client.actions.GuildRolesPositionUpdate.handle({
+ guild_id: this.guild.id,
+ roles: updatedRoles,
+ });
+ return this;
+ });
+ }
+
+ /**
+ * Deletes the role.
+ * @param {string} [reason] Reason for deleting this 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.api.guilds[this.guild.id].roles[this.id].delete({ reason }).then(() => {
+ this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id });
+ return this;
+ });
+ }
+
+ /**
+ * 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.bitfield === role.permissions.bitfield &&
+ this.managed === role.managed
+ );
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the role's mention instead of the Role object.
+ * @returns {string}
+ * @example
+ * // Logs: Role: <@&123456789012345678>
+ * console.log(`Role: ${role}`);
+ */
+ toString() {
+ if (this.id === this.guild.id) return '@everyone';
+ return `<@&${this.id}>`;
+ }
+
+ toJSON() {
+ return super.toJSON({ createdTimestamp: true });
+ }
+
+ /**
+ * 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;
+ }
+}
+
+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..87cb040
--- /dev/null
+++ b/node_modules/discord.js/src/structures/StoreChannel.js
@@ -0,0 +1,22 @@
+'use strict';
+
+const GuildChannel = require('./GuildChannel');
+
+/**
+ * Represents a guild store channel on Discord.
+ * @extends {GuildChannel}
+ */
+class StoreChannel extends GuildChannel {
+ _patch(data) {
+ super._patch(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..a28c5a2
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Team.js
@@ -0,0 +1,109 @@
+'use strict';
+
+const Base = require('./Base');
+const TeamMember = require('./TeamMember');
+const Collection = require('../util/Collection');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a Client OAuth2 Application Team.
+ * @extends {Base}
+ */
+class Team extends Base {
+ constructor(client, data) {
+ super(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, memberData);
+ this.members.set(member.id, member);
+ }
+ }
+
+ /**
+ * The owner of this 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.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string} URL to the icon
+ */
+ iconURL({ format, size } = {}) {
+ if (!this.icon) return null;
+ return this.client.rest.cdn.TeamIcon(this.id, this.icon, { format, size });
+ }
+
+ /**
+ * 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;
+ }
+
+ toJSON() {
+ return super.toJSON({ createdTimestamp: true });
+ }
+}
+
+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..ba7ecd2
--- /dev/null
+++ b/node_modules/discord.js/src/structures/TeamMember.js
@@ -0,0 +1,65 @@
+'use strict';
+
+const Base = require('./Base');
+const { MembershipStates } = require('../util/Constants');
+
+/**
+ * Represents a Client OAuth2 Application Team Member.
+ * @extends {Base}
+ */
+class TeamMember extends Base {
+ constructor(team, data) {
+ super(team.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 permissions 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.users.add(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: <@123456789012345678>
+ * 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..65ec13d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/TextChannel.js
@@ -0,0 +1,151 @@
+'use strict';
+
+const GuildChannel = require('./GuildChannel');
+const Webhook = require('./Webhook');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const MessageManager = require('../managers/MessageManager');
+const Collection = require('../util/Collection');
+const DataResolver = require('../util/DataResolver');
+
+/**
+ * Represents a guild text channel on Discord.
+ * @extends {GuildChannel}
+ * @implements {TextBasedChannel}
+ */
+class TextChannel extends GuildChannel {
+ /**
+ * @param {Guild} guild The guild the text channel is part of
+ * @param {Object} data The data for the text channel
+ */
+ constructor(guild, data) {
+ super(guild, data);
+ /**
+ * A manager of the messages sent to this channel
+ * @type {MessageManager}
+ */
+ this.messages = new MessageManager(this);
+ this._typing = new Map();
+ }
+
+ _patch(data) {
+ super._patch(data);
+
+ /**
+ * The topic of the text channel
+ * @type {?string}
+ */
+ this.topic = data.topic;
+
+ /**
+ * If the guild considers this channel NSFW
+ * @type {boolean}
+ * @readonly
+ */
+ this.nsfw = 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 ratelimit per user for this channel in seconds
+ * @type {number}
+ */
+ this.rateLimitPerUser = data.rate_limit_per_user || 0;
+
+ /**
+ * 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;
+
+ if (data.messages) for (const message of data.messages) this.messages.add(message);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Fetches 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.api.channels[this.id].webhooks.get().then(data => {
+ const hooks = new Collection();
+ for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
+ return hooks;
+ });
+ }
+
+ /**
+ * Creates a webhook for the channel.
+ * @param {string} name The name of the webhook
+ * @param {Object} [options] Options for creating the webhook
+ * @param {BufferResolvable|Base64Resolvable} [options.avatar] Avatar for the webhook
+ * @param {string} [options.reason] Reason for creating the webhook
+ * @returns {Promise<Webhook>} webhook The created webhook
+ * @example
+ * // Create a webhook for the current channel
+ * channel.createWebhook('Snek', {
+ * avatar: 'https://i.imgur.com/mI8XcpG.jpg',
+ * reason: 'Needed a cool new Webhook'
+ * })
+ * .then(console.log)
+ * .catch(console.error)
+ */
+ async createWebhook(name, { avatar, reason } = {}) {
+ if (typeof avatar === 'string' && !avatar.startsWith('data:')) {
+ avatar = await DataResolver.resolveImage(avatar);
+ }
+ return this.client.api.channels[this.id].webhooks
+ .post({
+ data: {
+ name,
+ avatar,
+ },
+ reason,
+ })
+ .then(data => new Webhook(this.client, data));
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ get lastMessage() {}
+ get lastPinAt() {}
+ send() {}
+ startTyping() {}
+ stopTyping() {}
+ get typing() {}
+ get typingCount() {}
+ createMessageCollector() {}
+ awaitMessages() {}
+ bulkDelete() {}
+}
+
+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..b2a6500
--- /dev/null
+++ b/node_modules/discord.js/src/structures/User.js
@@ -0,0 +1,318 @@
+'use strict';
+
+const Base = require('./Base');
+const { Presence } = require('./Presence');
+const TextBasedChannel = require('./interfaces/TextBasedChannel');
+const { Error } = require('../errors');
+const Snowflake = require('../util/Snowflake');
+const UserFlags = require('../util/UserFlags');
+
+/**
+ * Represents a user on Discord.
+ * @implements {TextBasedChannel}
+ * @extends {Base}
+ */
+class User extends Base {
+ /**
+ * @param {Client} client The instantiating client
+ * @param {Object} data The data for the user
+ */
+ constructor(client, data) {
+ super(client);
+
+ /**
+ * The ID of the user
+ * @type {Snowflake}
+ */
+ this.id = data.id;
+
+ /**
+ * Whether or not the user is a bot
+ * @type {boolean}
+ * @name User#bot
+ */
+ this.bot = Boolean(data.bot);
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ /**
+ * The username of the user
+ * @type {?string}
+ * @name User#username
+ */
+ if (data.username) this.username = data.username;
+
+ /**
+ * A discriminator based on username for the user
+ * @type {?string}
+ * @name User#discriminator
+ */
+ if (data.discriminator) this.discriminator = data.discriminator;
+
+ /**
+ * The ID of the user's avatar
+ * @type {?string}
+ * @name User#avatar
+ */
+ if (typeof data.avatar !== 'undefined') this.avatar = data.avatar;
+
+ if (typeof data.bot !== 'undefined') this.bot = Boolean(data.bot);
+
+ /**
+ * Whether the user 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 locale of the user's client (ISO 639-1)
+ * @type {?string}
+ * @name User#locale
+ */
+ if (data.locale) this.locale = data.locale;
+
+ /**
+ * The flags for this user
+ * @type {?UserFlags}
+ */
+ if (typeof data.public_flags !== 'undefined') this.flags = new UserFlags(data.public_flags);
+
+ /**
+ * The ID of the last message sent by the user, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The ID of the channel for the last message sent by the user, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageChannelID = null;
+ }
+
+ /**
+ * Whether this User is a partial
+ * @type {boolean}
+ * @readonly
+ */
+ get partial() {
+ return typeof this.username !== 'string';
+ }
+
+ /**
+ * The timestamp the user was created at
+ * @type {number}
+ * @readonly
+ */
+ get createdTimestamp() {
+ return Snowflake.deconstruct(this.id).timestamp;
+ }
+
+ /**
+ * The time the user was created at
+ * @type {Date}
+ * @readonly
+ */
+ get createdAt() {
+ return new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The Message object of the last message sent by the user, if one was sent
+ * @type {?Message}
+ * @readonly
+ */
+ get lastMessage() {
+ const channel = this.client.channels.cache.get(this.lastMessageChannelID);
+ return (channel && channel.messages.cache.get(this.lastMessageID)) || null;
+ }
+
+ /**
+ * The presence of this user
+ * @type {Presence}
+ * @readonly
+ */
+ get presence() {
+ for (const guild of this.client.guilds.cache.values()) {
+ if (guild.presences.cache.has(this.id)) return guild.presences.cache.get(this.id);
+ }
+ return new Presence(this.client, { user: { id: this.id } });
+ }
+
+ /**
+ * A link to the user's avatar.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ avatarURL({ format, size, dynamic } = {}) {
+ if (!this.avatar) return null;
+ return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size, dynamic);
+ }
+
+ /**
+ * A link to the user's default avatar
+ * @type {string}
+ * @readonly
+ */
+ get defaultAvatarURL() {
+ return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
+ }
+
+ /**
+ * A link to the user's avatar if they have one.
+ * Otherwise a link to their default avatar will be returned.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {string}
+ */
+ displayAvatarURL(options) {
+ return this.avatarURL(options) || this.defaultAvatarURL;
+ }
+
+ /**
+ * The Discord "tag" (e.g. `hydrabolt#0001`) for this user
+ * @type {?string}
+ * @readonly
+ */
+ get tag() {
+ return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null;
+ }
+
+ /**
+ * Checks whether the user is typing in a channel.
+ * @param {ChannelResolvable} channel The channel to check in
+ * @returns {boolean}
+ */
+ typingIn(channel) {
+ channel = this.client.channels.resolve(channel);
+ return channel._typing.has(this.id);
+ }
+
+ /**
+ * Gets the time that the user started typing.
+ * @param {ChannelResolvable} channel The channel to get the time in
+ * @returns {?Date}
+ */
+ typingSinceIn(channel) {
+ channel = this.client.channels.resolve(channel);
+ return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null;
+ }
+
+ /**
+ * Gets 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.channels.resolve(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.cache.find(c => c.type === 'dm' && c.recipient.id === this.id) || null;
+ }
+
+ /**
+ * Creates a DM channel between the client and the user.
+ * @returns {Promise<DMChannel>}
+ */
+ async createDM() {
+ const { dmChannel } = this;
+ if (dmChannel && !dmChannel.partial) return dmChannel;
+ const data = await this.client.api.users(this.client.user.id).channels.post({
+ data: {
+ recipient_id: this.id,
+ },
+ });
+ return this.client.actions.ChannelCreate.handle(data).channel;
+ }
+
+ /**
+ * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
+ * @returns {Promise<DMChannel>}
+ */
+ async deleteDM() {
+ const { dmChannel } = this;
+ if (!dmChannel) throw new Error('USER_NO_DMCHANNEL');
+ const data = await this.client.api.channels(dmChannel.id).delete();
+ return this.client.actions.ChannelDelete.handle(data).channel;
+ }
+
+ /**
+ * 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;
+
+ return equal;
+ }
+
+ /**
+ * Fetches this user's flags.
+ * @returns {Promise<UserFlags>}
+ */
+ async fetchFlags() {
+ if (this.flags) return this.flags;
+ const data = await this.client.api.users(this.id).get();
+ this._patch(data);
+ return this.flags;
+ }
+
+ /**
+ * Fetches this user.
+ * @returns {Promise<User>}
+ */
+ fetch() {
+ return this.client.users.fetch(this.id, true);
+ }
+
+ /**
+ * When concatenated with a string, this automatically returns the user's mention instead of the User object.
+ * @returns {string}
+ * @example
+ * // Logs: Hello from <@123456789012345678>!
+ * console.log(`Hello from ${user}!`);
+ */
+ toString() {
+ return `<@${this.id}>`;
+ }
+
+ toJSON(...props) {
+ const json = super.toJSON(
+ {
+ createdTimestamp: true,
+ defaultAvatarURL: true,
+ tag: true,
+ lastMessage: false,
+ lastMessageID: false,
+ },
+ ...props,
+ );
+ json.avatarURL = this.avatarURL();
+ json.displayAvatarURL = this.displayAvatarURL();
+ return json;
+ }
+
+ // These are here only for documentation purposes - they are implemented by TextBasedChannel
+ /* eslint-disable no-empty-function */
+ send() {}
+}
+
+TextBasedChannel.applyToClass(User);
+
+module.exports = User;
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..6fb5ab7
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceChannel.js
@@ -0,0 +1,151 @@
+'use strict';
+
+const GuildChannel = require('./GuildChannel');
+const { Error } = require('../errors');
+const Collection = require('../util/Collection');
+const { browser } = require('../util/Constants');
+const Permissions = require('../util/Permissions');
+
+/**
+ * Represents a guild voice channel on Discord.
+ * @extends {GuildChannel}
+ */
+class VoiceChannel extends GuildChannel {
+ _patch(data) {
+ super._patch(data);
+ /**
+ * The bitrate of this voice channel
+ * @type {number}
+ */
+ this.bitrate = data.bitrate;
+
+ /**
+ * The maximum amount of users allowed in this channel - 0 means unlimited.
+ * @type {number}
+ */
+ this.userLimit = data.user_limit;
+ }
+
+ /**
+ * The members in this voice channel
+ * @type {Collection<Snowflake, GuildMember>}
+ * @name VoiceChannel#members
+ * @readonly
+ */
+ get members() {
+ const coll = new Collection();
+ for (const state of this.guild.voiceStates.cache.values()) {
+ if (state.channelID === this.id && state.member) {
+ coll.set(state.id, state.member);
+ }
+ }
+ return coll;
+ }
+
+ /**
+ * 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, false);
+ }
+
+ /**
+ * Whether the channel is editable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get editable() {
+ return this.manageable && this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false);
+ }
+
+ /**
+ * Whether the channel is joinable by the client user
+ * @type {boolean}
+ * @readonly
+ */
+ get joinable() {
+ if (browser) return false;
+ if (!this.viewable) return false;
+ if (!this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false)) return false;
+ if (this.full && !this.permissionsFor(this.client.user).has(Permissions.FLAGS.MOVE_MEMBERS, false)) 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(Permissions.FLAGS.SPEAK, false);
+ }
+
+ /**
+ * Sets the bitrate of the channel.
+ * @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(48000)
+ * .then(vc => console.log(`Set bitrate to ${vc.bitrate}bps for ${vc.name}`))
+ * .catch(console.error);
+ */
+ setBitrate(bitrate, reason) {
+ 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 (browser) return Promise.reject(new Error('VOICE_NO_BROWSER'));
+ return this.client.voice.joinChannel(this);
+ }
+
+ /**
+ * Leaves this voice channel.
+ * @example
+ * // Leave a voice channel
+ * voiceChannel.leave();
+ */
+ leave() {
+ if (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..9626195
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceRegion.js
@@ -0,0 +1,52 @@
+'use strict';
+
+const Util = require('../util/Util');
+
+/**
+ * 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;
+ }
+
+ toJSON() {
+ return Util.flatten(this);
+ }
+}
+
+module.exports = VoiceRegion;
diff --git a/node_modules/discord.js/src/structures/VoiceState.js b/node_modules/discord.js/src/structures/VoiceState.js
new file mode 100644
index 0000000..cc9bcde
--- /dev/null
+++ b/node_modules/discord.js/src/structures/VoiceState.js
@@ -0,0 +1,208 @@
+'use strict';
+
+const Base = require('./Base');
+const { Error, TypeError } = require('../errors');
+const { browser } = require('../util/Constants');
+
+/**
+ * Represents the voice state for a Guild Member.
+ */
+class VoiceState extends Base {
+ /**
+ * @param {Guild} guild The guild the voice state is part of
+ * @param {Object} data The data for the voice state
+ */
+ constructor(guild, data) {
+ super(guild.client);
+ /**
+ * The guild of this voice state
+ * @type {Guild}
+ */
+ this.guild = guild;
+ /**
+ * The ID of the member of this voice state
+ * @type {Snowflake}
+ */
+ this.id = data.user_id;
+ this._patch(data);
+ }
+
+ _patch(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-deafened
+ * @type {?boolean}
+ */
+ this.selfDeaf = data.self_deaf;
+ /**
+ * Whether this member is self-muted
+ * @type {?boolean}
+ */
+ this.selfMute = data.self_mute;
+ /**
+ * The session ID of this member's connection
+ * @type {?string}
+ */
+ this.sessionID = data.session_id;
+ /**
+ * Whether this member is streaming using "Go Live"
+ * @type {boolean}
+ */
+ this.streaming = data.self_stream || false;
+ /**
+ * The ID of the voice channel that this member is in
+ * @type {?Snowflake}
+ */
+ this.channelID = data.channel_id;
+ return this;
+ }
+
+ /**
+ * The member that this voice state belongs to
+ * @type {?GuildMember}
+ * @readonly
+ */
+ get member() {
+ return this.guild.members.cache.get(this.id) || null;
+ }
+
+ /**
+ * The channel that the member is connected to
+ * @type {?VoiceChannel}
+ * @readonly
+ */
+ get channel() {
+ return this.guild.channels.cache.get(this.channelID) || null;
+ }
+
+ /**
+ * If this is a voice state of the client user, then this will refer to the active VoiceConnection for this guild
+ * @type {?VoiceConnection}
+ * @readonly
+ */
+ get connection() {
+ if (browser || this.id !== this.client.user.id) return null;
+ return this.client.voice.connections.get(this.guild.id) || null;
+ }
+
+ /**
+ * Whether this member is either self-deafened or server-deafened
+ * @type {?boolean}
+ * @readonly
+ */
+ get deaf() {
+ return this.serverDeaf || this.selfDeaf;
+ }
+
+ /**
+ * Whether this member is either self-muted or server-muted
+ * @type {?boolean}
+ * @readonly
+ */
+ get mute() {
+ return this.serverMute || this.selfMute;
+ }
+
+ /**
+ * Whether this member is currently speaking. A boolean if the information is available (aka
+ * the bot is connected to any voice channel in the guild), otherwise this is null
+ * @type {?boolean}
+ * @readonly
+ */
+ get speaking() {
+ return this.channel && this.channel.connection ? Boolean(this.channel.connection._speaking.get(this.id)) : null;
+ }
+
+ /**
+ * Mutes/unmutes the member of this voice state.
+ * @param {boolean} mute Whether or not the member should be muted
+ * @param {string} [reason] Reason for muting or unmuting
+ * @returns {Promise<GuildMember>}
+ */
+ setMute(mute, reason) {
+ return this.member ? this.member.edit({ mute }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
+ }
+
+ /**
+ * Deafens/undeafens the member of this voice state.
+ * @param {boolean} deaf Whether or not the member should be deafened
+ * @param {string} [reason] Reason for deafening or undeafening
+ * @returns {Promise<GuildMember>}
+ */
+ setDeaf(deaf, reason) {
+ return this.member ? this.member.edit({ deaf }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
+ }
+
+ /**
+ * Kicks the member from the voice channel.
+ * @param {string} [reason] Reason for kicking member from the channel
+ * @returns {Promise<GuildMember>}
+ */
+ kick(reason) {
+ return this.setChannel(null, reason);
+ }
+
+ /**
+ * Moves the member to a different channel, or disconnects them from the one they're in.
+ * @param {ChannelResolvable|null} [channel] Channel to move the member to, or `null` if you want to disconnect them
+ * from voice. Requires the `MOVE_MEMBERS` permission.
+ * @param {string} [reason] Reason for moving member to another channel or disconnecting
+ * @returns {Promise<GuildMember>}
+ */
+ setChannel(channel, reason) {
+ return this.member
+ ? this.member.edit({ channel }, reason)
+ : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
+ }
+
+ /**
+ * Self-mutes/unmutes the bot for this voice state.
+ * @param {boolean} mute Whether or not the bot should be self-muted
+ * @returns {Promise<boolean>} true if the voice state was successfully updated, otherwise false
+ */
+ async setSelfMute(mute) {
+ if (this.id !== this.client.user.id) throw new Error('VOICE_STATE_NOT_OWN');
+ if (typeof mute !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'mute');
+ if (!this.connection) return false;
+ this.selfMute = mute;
+ await this.connection.sendVoiceStateUpdate();
+ return true;
+ }
+
+ /**
+ * Self-deafens/undeafens the bot for this voice state.
+ * @param {boolean} deaf Whether or not the bot should be self-deafened
+ * @returns {Promise<boolean>} true if the voice state was successfully updated, otherwise false
+ */
+ async setSelfDeaf(deaf) {
+ if (this.id !== this.client.user.id) return new Error('VOICE_STATE_NOT_OWN');
+ if (typeof deaf !== 'boolean') return new TypeError('VOICE_STATE_INVALID_TYPE', 'deaf');
+ if (!this.connection) return false;
+ this.selfDeaf = deaf;
+ await this.connection.sendVoiceStateUpdate();
+ return true;
+ }
+
+ toJSON() {
+ return super.toJSON({
+ id: true,
+ serverDeaf: true,
+ serverMute: true,
+ selfDeaf: true,
+ selfMute: true,
+ sessionID: true,
+ channelID: 'channel',
+ });
+ }
+}
+
+module.exports = VoiceState;
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..b9aed9d
--- /dev/null
+++ b/node_modules/discord.js/src/structures/Webhook.js
@@ -0,0 +1,273 @@
+'use strict';
+
+const APIMessage = require('./APIMessage');
+const Channel = require('./Channel');
+const { WebhookTypes } = require('../util/Constants');
+const DataResolver = require('../util/DataResolver');
+const Snowflake = require('../util/Snowflake');
+
+/**
+ * Represents a webhook.
+ */
+class Webhook {
+ constructor(client, data) {
+ /**
+ * The client that instantiated the webhook
+ * @name Webhook#client
+ * @type {Client}
+ * @readonly
+ */
+ Object.defineProperty(this, 'client', { value: client });
+ if (data) this._patch(data);
+ }
+
+ _patch(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 = 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.cache.get(data.user.id) : data.user;
+ } else {
+ this.owner = null;
+ }
+ }
+
+ /**
+ * Options that can be passed into send.
+ * @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 {Object[]} [embeds] An array of embeds for the message
+ * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
+ * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
+ * @property {DisableMentionType} [disableMentions=this.client.options.disableMentions] Whether or not all mentions or
+ * everyone/here mentions should be sanitized to prevent unexpected mentions
+ * @property {FileOptions[]|string[]} [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.
+ */
+
+ /**
+ * Sends a message with this webhook.
+ * @param {StringResolvable|APIMessage} [content=''] The content to send
+ * @param {WebhookMessageOptions|MessageAdditions} [options={}] The options to provide
+ * @returns {Promise<Message|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);
+ */
+ async send(content, options) {
+ let apiMessage;
+
+ if (content instanceof APIMessage) {
+ apiMessage = content.resolveData();
+ } else {
+ apiMessage = APIMessage.create(this, content, options).resolveData();
+ if (Array.isArray(apiMessage.data.content)) {
+ return Promise.all(apiMessage.split().map(this.send.bind(this)));
+ }
+ }
+
+ const { data, files } = await apiMessage.resolveFiles();
+ return this.client.api
+ .webhooks(this.id, this.token)
+ .post({
+ data,
+ files,
+ query: { wait: true },
+ auth: false,
+ })
+ .then(d => {
+ const channel = this.client.channels ? this.client.channels.cache.get(d.channel_id) : undefined;
+ if (!channel) return d;
+ return channel.messages.add(d, false);
+ });
+ }
+
+ /**
+ * Sends a raw slack message with this webhook.
+ * @param {Object} body The raw body to send
+ * @returns {Promise<boolean>}
+ * @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.api
+ .webhooks(this.id, this.token)
+ .slack.post({
+ query: { wait: true },
+ auth: false,
+ data: body,
+ })
+ .then(data => data.toString() === 'ok');
+ }
+
+ /**
+ * Edits the webhook.
+ * @param {Object} options Options
+ * @param {string} [options.name=this.name] New name for this webhook
+ * @param {BufferResolvable} [options.avatar] New avatar for this webhook
+ * @param {ChannelResolvable} [options.channel] New channel for this webhook
+ * @param {string} [reason] Reason for editing this webhook
+ * @returns {Promise<Webhook>}
+ */
+ async edit({ name = this.name, avatar, channel }, reason) {
+ if (avatar && typeof avatar === 'string' && !avatar.startsWith('data:')) {
+ avatar = await DataResolver.resolveImage(avatar);
+ }
+ if (channel) channel = channel instanceof Channel ? channel.id : channel;
+ const data = await this.client.api.webhooks(this.id, channel ? undefined : this.token).patch({
+ data: { name, avatar, channel_id: channel },
+ reason,
+ });
+
+ this.name = data.name;
+ this.avatar = data.avatar;
+ this.channelID = data.channel_id;
+ return this;
+ }
+
+ /**
+ * Deletes the webhook.
+ * @param {string} [reason] Reason for deleting this webhook
+ * @returns {Promise}
+ */
+ delete(reason) {
+ return this.client.api.webhooks(this.id, this.token).delete({ reason });
+ }
+ /**
+ * 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);
+ }
+
+ /**
+ * The url of this webhook
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return this.client.options.http.api + this.client.api.webhooks(this.id, this.token);
+ }
+
+ /**
+ * A link to the webhook's avatar.
+ * @param {ImageURLOptions} [options={}] Options for the Image URL
+ * @returns {?string}
+ */
+ avatarURL({ format, size } = {}) {
+ if (!this.avatar) return null;
+ return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size);
+ }
+
+ static applyToClass(structure) {
+ for (const prop of ['send', 'sendSlackMessage', 'edit', 'delete', 'createdTimestamp', 'createdAt', 'url']) {
+ Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(Webhook.prototype, prop));
+ }
+ }
+}
+
+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..21d01ca
--- /dev/null
+++ b/node_modules/discord.js/src/structures/interfaces/Collector.js
@@ -0,0 +1,281 @@
+'use strict';
+
+const EventEmitter = require('events');
+const Collection = require('../../util/Collection');
+const Util = require('../../util/Util');
+
+/**
+ * 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 in milliseconds
+ * @property {number} [idle] How long to stop the collector after inactivity in milliseconds
+ * @property {boolean} [dispose=false] Whether to dispose data when it's deleted
+ */
+
+/**
+ * Abstract class for defining a new Collector.
+ * @abstract
+ */
+class Collector extends EventEmitter {
+ constructor(client, filter, options = {}) {
+ super();
+
+ /**
+ * The client that instantiated this Collector
+ * @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;
+
+ this.handleCollect = this.handleCollect.bind(this);
+ this.handleDispose = this.handleDispose.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);
+ }
+
+ /**
+ * Call this to handle an event as a collectable element. Accepts any event data as parameters.
+ * @param {...*} args The arguments emitted by the listener
+ * @emits Collector#collect
+ */
+ handleCollect(...args) {
+ const collect = this.collect(...args);
+
+ if (collect && this.filter(...args, this.collected)) {
+ this.collected.set(collect, args[0]);
+
+ /**
+ * Emitted whenever an element is collected.
+ * @event Collector#collect
+ * @param {...*} args The arguments emitted by the listener
+ */
+ this.emit('collect', ...args);
+
+ if (this._idletimeout) {
+ this.client.clearTimeout(this._idletimeout);
+ this._idletimeout = this.client.setTimeout(() => this.stop('idle'), this.options.idle);
+ }
+ }
+ this.checkEnd();
+ }
+
+ /**
+ * Call this to remove an element from the collection. Accepts any event data as parameters.
+ * @param {...*} args The arguments emitted by the listener
+ * @emits Collector#dispose
+ */
+ handleDispose(...args) {
+ if (!this.options.dispose) return;
+
+ const dispose = this.dispose(...args);
+ if (!dispose || !this.filter(...args) || !this.collected.has(dispose)) return;
+ this.collected.delete(dispose);
+
+ /**
+ * Emitted whenever an element is disposed of.
+ * @event Collector#dispose
+ * @param {...*} args The arguments emitted by the listener
+ */
+ this.emit('dispose', ...args);
+ this.checkEnd();
+ }
+
+ /**
+ * Returns 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);
+ });
+ }
+
+ /**
+ * Stops this collector and emits 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;
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Resets the collectors timeout and idle timer.
+ * @param {Object} [options] Options
+ * @param {number} [options.time] How long to run the collector for in milliseconds
+ * @param {number} [options.idle] How long to stop the collector after inactivity in milliseconds
+ */
+ resetTimer({ time, idle } = {}) {
+ if (this._timeout) {
+ this.client.clearTimeout(this._timeout);
+ this._timeout = this.client.setTimeout(() => this.stop('time'), time || this.options.time);
+ }
+ if (this._idletimeout) {
+ this.client.clearTimeout(this._idletimeout);
+ this._idletimeout = this.client.setTimeout(() => this.stop('idle'), idle || this.options.idle);
+ }
+ }
+
+ /**
+ * Checks whether the collector should end, and if so, ends it.
+ */
+ checkEnd() {
+ const reason = this.endReason();
+ if (reason) this.stop(reason);
+ }
+
+ /**
+ * Allows collectors to be consumed with for-await-of loops
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of}
+ */
+ async *[Symbol.asyncIterator]() {
+ const queue = [];
+ const onCollect = item => queue.push(item);
+ this.on('collect', onCollect);
+
+ try {
+ while (queue.length || !this.ended) {
+ if (queue.length) {
+ yield queue.shift();
+ } else {
+ // eslint-disable-next-line no-await-in-loop
+ await new Promise(resolve => {
+ const tick = () => {
+ this.removeListener('collect', tick);
+ this.removeListener('end', tick);
+ return resolve();
+ };
+ this.on('collect', tick);
+ this.on('end', tick);
+ });
+ }
+ }
+ } finally {
+ this.removeListener('collect', onCollect);
+ }
+ }
+
+ toJSON() {
+ return Util.flatten(this);
+ }
+
+ /* eslint-disable no-empty-function, valid-jsdoc */
+ /**
+ * Handles incoming events from the `handleCollect` function. Returns null if the event should not
+ * be collected, or returns an object describing the data that should be stored.
+ * @see Collector#handleCollect
+ * @param {...*} args Any args the event listener emits
+ * @returns {?{key, value}} Data to insert into collection, if any
+ * @abstract
+ */
+ collect() {}
+
+ /**
+ * Handles incoming events from the `handleDispose`. Returns null if the event should not
+ * be disposed, or returns the key that should be removed.
+ * @see Collector#handleDispose
+ * @param {...*} args Any args the event listener emits
+ * @returns {?*} Key to remove from the collection, if any
+ * @abstract
+ */
+ dispose() {}
+
+ /**
+ * The reason this collector has ended or will end with.
+ * @returns {?string} Reason to end the collector, if any
+ * @abstract
+ */
+ endReason() {}
+ /* 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..b63c92a
--- /dev/null
+++ b/node_modules/discord.js/src/structures/interfaces/TextBasedChannel.js
@@ -0,0 +1,396 @@
+'use strict';
+
+/* eslint-disable import/order */
+const MessageCollector = require('../MessageCollector');
+const APIMessage = require('../APIMessage');
+const Snowflake = require('../../util/Snowflake');
+const Collection = require('../../util/Collection');
+const { RangeError, TypeError } = require('../../errors');
+
+/**
+ * Interface for classes that have text-channel-like features.
+ * @interface
+ */
+class TextBasedChannel {
+ constructor() {
+ /**
+ * A manager of the messages sent to this channel
+ * @type {MessageManager}
+ */
+ this.messages = new MessageManager(this);
+
+ /**
+ * The ID of the last message in the channel, if one was sent
+ * @type {?Snowflake}
+ */
+ this.lastMessageID = null;
+
+ /**
+ * The timestamp when the last pinned message was pinned, if there was one
+ * @type {?number}
+ */
+ this.lastPinTimestamp = null;
+ }
+
+ /**
+ * The Message object of the last message in the channel, if one was sent
+ * @type {?Message}
+ * @readonly
+ */
+ get lastMessage() {
+ return this.messages.cache.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;
+ }
+
+ /**
+ * 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 {string} [content=''] The content for the message
+ * @property {MessageEmbed|Object} [embed] An embed for the message
+ * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
+ * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
+ * @property {DisableMentionType} [disableMentions=this.client.options.disableMentions] Whether or not all mentions or
+ * everyone/here mentions should be sanitized to prevent unexpected mentions
+ * @property {FileOptions[]|BufferResolvable[]} [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)
+ */
+
+ /**
+ * Options provided to control parsing of mentions by Discord
+ * @typedef {Object} MessageMentionOptions
+ * @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed
+ * @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions
+ * @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions
+ */
+
+ /**
+ * Types of mentions to enable in MessageMentionOptions.
+ * - `roles`
+ * - `users`
+ * - `everyone`
+ * @typedef {string} MessageMentionTypes
+ */
+
+ /**
+ * The type of mentions to disable.
+ * - `none`
+ * - `all`
+ * - `everyone`
+ * @typedef {string} DisableMentionType
+ */
+
+ /**
+ * @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=2000] 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
+ */
+
+ /**
+ * Sends a message to this channel.
+ * @param {StringResolvable|APIMessage} [content=''] The content to send
+ * @param {MessageOptions|MessageAdditions} [options={}] The options to provide
+ * @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);
+ */
+ async send(content, options) {
+ const User = require('../User');
+ const GuildMember = require('../GuildMember');
+
+ if (this instanceof User || this instanceof GuildMember) {
+ return this.createDM().then(dm => dm.send(content, options));
+ }
+
+ let apiMessage;
+
+ if (content instanceof APIMessage) {
+ apiMessage = content.resolveData();
+ } else {
+ apiMessage = APIMessage.create(this, content, options).resolveData();
+ if (Array.isArray(apiMessage.data.content)) {
+ return Promise.all(apiMessage.split().map(this.send.bind(this)));
+ }
+ }
+
+ const { data, files } = await apiMessage.resolveFiles();
+ return this.client.api.channels[this.id].messages
+ .post({ data, files })
+ .then(d => this.client.actions.MessageCreate.handle(d).message);
+ }
+
+ /**
+ * Starts a typing indicator in the channel.
+ * @param {number} [count=1] The number of times startTyping should be considered to have been called
+ * @returns {Promise} Resolves once the bot stops typing gracefully, or rejects when an error occurs
+ * @example
+ * // Start typing in a channel, or increase the typing count by one
+ * channel.startTyping();
+ * @example
+ * // Start typing in a channel with a typing count of five, or set it to five
+ * channel.startTyping(5);
+ */
+ startTyping(count) {
+ if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT');
+ if (this.client.user._typing.has(this.id)) {
+ const entry = this.client.user._typing.get(this.id);
+ entry.count = count || entry.count + 1;
+ return entry.promise;
+ }
+
+ const entry = {};
+ entry.promise = new Promise((resolve, reject) => {
+ const endpoint = this.client.api.channels[this.id].typing;
+ Object.assign(entry, {
+ count: count || 1,
+ interval: this.client.setInterval(() => {
+ endpoint.post().catch(error => {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ reject(error);
+ });
+ }, 9000),
+ resolve,
+ });
+ endpoint.post().catch(error => {
+ this.client.clearInterval(entry.interval);
+ this.client.user._typing.delete(this.id);
+ reject(error);
+ });
+ this.client.user._typing.set(this.id, entry);
+ });
+ return entry.promise;
+ }
+
+ /**
+ * 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 regardless of typing count
+ * 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);
+ entry.resolve();
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 createMessageCollector 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.createMessageCollector(filter, options);
+ collector.once('end', (collection, reason) => {
+ if (options.errors && options.errors.includes(reason)) {
+ reject(collection);
+ } else {
+ resolve(collection);
+ }
+ });
+ });
+ }
+
+ /**
+ * Bulk deletes given messages that are newer than two weeks.
+ * @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);
+ */
+ async bulkDelete(messages, filterOld = false) {
+ if (Array.isArray(messages) || 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 new Collection();
+ if (messageIDs.length === 1) {
+ await this.client.api
+ .channels(this.id)
+ .messages(messageIDs[0])
+ .delete();
+ const message = this.client.actions.MessageDelete.getMessage(
+ {
+ message_id: messageIDs[0],
+ },
+ this,
+ );
+ return message ? new Collection([[message.id, message]]) : new Collection();
+ }
+ await this.client.api.channels[this.id].messages['bulk-delete'].post({ data: { messages: messageIDs } });
+ return messageIDs.reduce(
+ (col, id) =>
+ col.set(
+ id,
+ this.client.actions.MessageDeleteBulk.getMessage(
+ {
+ message_id: id,
+ },
+ this,
+ ),
+ ),
+ new Collection(),
+ );
+ }
+ if (!isNaN(messages)) {
+ const msgs = await this.messages.fetch({ limit: messages });
+ return this.bulkDelete(msgs, filterOld);
+ }
+ throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
+ }
+
+ static applyToClass(structure, full = false, ignore = []) {
+ const props = ['send'];
+ if (full) {
+ props.push(
+ 'lastMessage',
+ 'lastPinAt',
+ 'bulkDelete',
+ 'startTyping',
+ 'stopTyping',
+ 'typing',
+ 'typingCount',
+ 'createMessageCollector',
+ 'awaitMessages',
+ );
+ }
+ for (const prop of props) {
+ if (ignore.includes(prop)) continue;
+ Object.defineProperty(
+ structure.prototype,
+ prop,
+ Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop),
+ );
+ }
+ }
+}
+
+module.exports = TextBasedChannel;
+
+// Fixes Circular
+const MessageManager = require('../../managers/MessageManager');