aboutsummaryrefslogtreecommitdiff
path: root/node_modules/discord.js/src/client
diff options
context:
space:
mode:
authorAlee14 <alee14498@gmail.com>2017-03-26 15:18:10 -0400
committerAlee14 <alee14498@gmail.com>2017-03-26 15:18:10 -0400
commit29433e2f7dbd0e4a73d3c78ffe1005b922fb5982 (patch)
treeaa0ad3fe59468cbe452ee597e914839b68c01436 /node_modules/discord.js/src/client
parent878fefb4c4e1f12b804ae5c0def433fa873f4c8b (diff)
downloadAleeBot-29433e2f7dbd0e4a73d3c78ffe1005b922fb5982.tar.gz
AleeBot-29433e2f7dbd0e4a73d3c78ffe1005b922fb5982.tar.bz2
AleeBot-29433e2f7dbd0e4a73d3c78ffe1005b922fb5982.zip
Don't mind me i'm adding the discord.js files
Diffstat (limited to 'node_modules/discord.js/src/client')
-rw-r--r--node_modules/discord.js/src/client/Client.js478
-rw-r--r--node_modules/discord.js/src/client/ClientDataManager.js129
-rw-r--r--node_modules/discord.js/src/client/ClientDataResolver.js309
-rw-r--r--node_modules/discord.js/src/client/ClientManager.js67
-rw-r--r--node_modules/discord.js/src/client/WebhookClient.js46
-rw-r--r--node_modules/discord.js/src/client/actions/Action.js23
-rw-r--r--node_modules/discord.js/src/client/actions/ActionsManager.js38
-rw-r--r--node_modules/discord.js/src/client/actions/ChannelCreate.js13
-rw-r--r--node_modules/discord.js/src/client/actions/ChannelDelete.js31
-rw-r--r--node_modules/discord.js/src/client/actions/ChannelUpdate.js34
-rw-r--r--node_modules/discord.js/src/client/actions/GuildBanRemove.js13
-rw-r--r--node_modules/discord.js/src/client/actions/GuildDelete.js51
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojiCreate.js18
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojiDelete.js18
-rw-r--r--node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js15
-rw-r--r--node_modules/discord.js/src/client/actions/GuildMemberGet.js12
-rw-r--r--node_modules/discord.js/src/client/actions/GuildMemberRemove.js49
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRoleCreate.js32
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRoleDelete.js46
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRoleUpdate.js41
-rw-r--r--node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js23
-rw-r--r--node_modules/discord.js/src/client/actions/GuildSync.js27
-rw-r--r--node_modules/discord.js/src/client/actions/GuildUpdate.js34
-rw-r--r--node_modules/discord.js/src/client/actions/MessageCreate.js40
-rw-r--r--node_modules/discord.js/src/client/actions/MessageDelete.js40
-rw-r--r--node_modules/discord.js/src/client/actions/MessageDeleteBulk.js24
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionAdd.js43
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionRemove.js43
-rw-r--r--node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js25
-rw-r--r--node_modules/discord.js/src/client/actions/MessageUpdate.js43
-rw-r--r--node_modules/discord.js/src/client/actions/UserGet.js13
-rw-r--r--node_modules/discord.js/src/client/actions/UserNoteUpdate.js30
-rw-r--r--node_modules/discord.js/src/client/actions/UserUpdate.js33
-rw-r--r--node_modules/discord.js/src/client/rest/APIRequest.js49
-rw-r--r--node_modules/discord.js/src/client/rest/RESTManager.js51
-rw-r--r--node_modules/discord.js/src/client/rest/RESTMethods.js653
-rw-r--r--node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js70
-rw-r--r--node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js51
-rw-r--r--node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js104
-rw-r--r--node_modules/discord.js/src/client/rest/UserAgentManager.js22
-rw-r--r--node_modules/discord.js/src/client/voice/ClientVoiceManager.js245
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceConnection.js276
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceUDPClient.js145
-rw-r--r--node_modules/discord.js/src/client/voice/VoiceWebSocket.js249
-rw-r--r--node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js307
-rw-r--r--node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js15
-rw-r--r--node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js27
-rw-r--r--node_modules/discord.js/src/client/voice/opus/OpusEngineList.js24
-rw-r--r--node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js27
-rw-r--r--node_modules/discord.js/src/client/voice/pcm/ConverterEngine.js14
-rw-r--r--node_modules/discord.js/src/client/voice/pcm/ConverterEngineList.js1
-rw-r--r--node_modules/discord.js/src/client/voice/pcm/FfmpegConverterEngine.js86
-rw-r--r--node_modules/discord.js/src/client/voice/player/AudioPlayer.js80
-rw-r--r--node_modules/discord.js/src/client/voice/player/BasePlayer.js121
-rw-r--r--node_modules/discord.js/src/client/voice/player/DefaultPlayer.js19
-rw-r--r--node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js19
-rw-r--r--node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js154
-rw-r--r--node_modules/discord.js/src/client/voice/util/SecretKey.js16
-rw-r--r--node_modules/discord.js/src/client/websocket/WebSocketManager.js373
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js125
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js17
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js20
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js31
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js23
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js20
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js22
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js40
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js17
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js13
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js18
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js28
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js17
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js72
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js69
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js68
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js12
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js11
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js19
-rw-r--r--node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js49
95 files changed, 5986 insertions, 0 deletions
diff --git a/node_modules/discord.js/src/client/Client.js b/node_modules/discord.js/src/client/Client.js
new file mode 100644
index 0000000..11cd5f4
--- /dev/null
+++ b/node_modules/discord.js/src/client/Client.js
@@ -0,0 +1,478 @@
+const EventEmitter = require('events').EventEmitter;
+const mergeDefault = require('../util/MergeDefault');
+const Constants = require('../util/Constants');
+const RESTManager = require('./rest/RESTManager');
+const ClientDataManager = require('./ClientDataManager');
+const ClientManager = require('./ClientManager');
+const ClientDataResolver = require('./ClientDataResolver');
+const ClientVoiceManager = require('./voice/ClientVoiceManager');
+const WebSocketManager = require('./websocket/WebSocketManager');
+const ActionsManager = require('./actions/ActionsManager');
+const Collection = require('../util/Collection');
+const Presence = require('../structures/Presence').Presence;
+const ShardClientUtil = require('../sharding/ShardClientUtil');
+
+/**
+ * The starting point for making a Discord Bot.
+ * @extends {EventEmitter}
+ */
+class Client extends EventEmitter {
+ /**
+ * @param {ClientOptions} [options] Options for the client
+ */
+ constructor(options = {}) {
+ super();
+
+ // Obtain shard details from environment
+ if (!options.shardId && 'SHARD_ID' in process.env) options.shardId = Number(process.env.SHARD_ID);
+ if (!options.shardCount && 'SHARD_COUNT' in process.env) options.shardCount = Number(process.env.SHARD_COUNT);
+
+ /**
+ * The options the client was instantiated with
+ * @type {ClientOptions}
+ */
+ this.options = mergeDefault(Constants.DefaultOptions, options);
+ this._validateOptions();
+
+ /**
+ * The REST manager of the client
+ * @type {RESTManager}
+ * @private
+ */
+ this.rest = new RESTManager(this);
+
+ /**
+ * The data manager of the Client
+ * @type {ClientDataManager}
+ * @private
+ */
+ this.dataManager = new ClientDataManager(this);
+
+ /**
+ * The manager of the Client
+ * @type {ClientManager}
+ * @private
+ */
+ this.manager = new ClientManager(this);
+
+ /**
+ * The WebSocket Manager of the Client
+ * @type {WebSocketManager}
+ * @private
+ */
+ this.ws = new WebSocketManager(this);
+
+ /**
+ * The Data Resolver of the Client
+ * @type {ClientDataResolver}
+ * @private
+ */
+ this.resolver = new ClientDataResolver(this);
+
+ /**
+ * The Action Manager of the Client
+ * @type {ActionsManager}
+ * @private
+ */
+ this.actions = new ActionsManager(this);
+
+ /**
+ * The Voice Manager of the Client (`null` in browsers)
+ * @type {?ClientVoiceManager}
+ * @private
+ */
+ this.voice = !this.browser ? new ClientVoiceManager(this) : null;
+
+ /**
+ * The shard helpers for the client (only if the process was spawned as a child, such as from a ShardingManager)
+ * @type {?ShardClientUtil}
+ */
+ this.shard = process.send ? ShardClientUtil.singleton(this) : null;
+
+ /**
+ * A collection of the Client's stored users
+ * @type {Collection<string, User>}
+ */
+ this.users = new Collection();
+
+ /**
+ * A collection of the Client's stored guilds
+ * @type {Collection<string, Guild>}
+ */
+ this.guilds = new Collection();
+
+ /**
+ * A collection of the Client's stored channels
+ * @type {Collection<string, Channel>}
+ */
+ this.channels = new Collection();
+
+ /**
+ * A collection of presences for friends of the logged in user.
+ * <warn>This is only filled when using a user account.</warn>
+ * @type {Collection<string, Presence>}
+ */
+ this.presences = new Collection();
+
+ if (!this.token && 'CLIENT_TOKEN' in process.env) {
+ /**
+ * The authorization token for the logged in user/bot.
+ * @type {?string}
+ */
+ this.token = process.env.CLIENT_TOKEN;
+ } else {
+ this.token = null;
+ }
+
+ /**
+ * The ClientUser representing the logged in Client
+ * @type {?ClientUser}
+ */
+ this.user = null;
+
+ /**
+ * The date at which the Client was regarded as being in the `READY` state.
+ * @type {?Date}
+ */
+ this.readyAt = null;
+
+ /**
+ * The previous heartbeat pings of the websocket (most recent first, limited to three elements)
+ * @type {number[]}
+ */
+ this.pings = [];
+
+ this._pingTimestamp = 0;
+ this._timeouts = new Set();
+ this._intervals = new Set();
+
+ if (this.options.messageSweepInterval > 0) {
+ this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000);
+ }
+ }
+
+ /**
+ * The status for the logged in Client.
+ * @type {?number}
+ * @readonly
+ */
+ get status() {
+ return this.ws.status;
+ }
+
+ /**
+ * The uptime for the logged in Client.
+ * @type {?number}
+ * @readonly
+ */
+ get uptime() {
+ return this.readyAt ? Date.now() - this.readyAt : null;
+ }
+
+ /**
+ * The average heartbeat ping of the websocket
+ * @type {number}
+ * @readonly
+ */
+ get ping() {
+ return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length;
+ }
+
+ /**
+ * Returns a collection, mapping guild ID to voice connections.
+ * @type {Collection<string, VoiceConnection>}
+ * @readonly
+ */
+ get voiceConnections() {
+ if (this.browser) return new Collection();
+ return this.voice.connections;
+ }
+
+ /**
+ * The emojis that the client can use. Mapped by emoji ID.
+ * @type {Collection<string, Emoji>}
+ * @readonly
+ */
+ get emojis() {
+ const emojis = new Collection();
+ for (const guild of this.guilds.values()) {
+ for (const emoji of guild.emojis.values()) emojis.set(emoji.id, emoji);
+ }
+ return emojis;
+ }
+
+ /**
+ * The timestamp that the client was last ready at
+ * @type {?number}
+ * @readonly
+ */
+ get readyTimestamp() {
+ return this.readyAt ? this.readyAt.getTime() : null;
+ }
+
+ /**
+ * Whether the client is in a browser environment
+ * @type {boolean}
+ * @readonly
+ */
+ get browser() {
+ return typeof window !== 'undefined';
+ }
+
+ /**
+ * Logs the client in. If successful, resolves with the account's token. <warn>If you're making a bot, it's
+ * much better to use a bot account rather than a user account.
+ * Bot accounts have higher rate limits and have access to some features user accounts don't have. User bots
+ * that are making a lot of API requests can even be banned.</warn>
+ * @param {string} token The token used for the account.
+ * @returns {Promise<string>}
+ * @example
+ * // log the client in using a token
+ * const token = 'my token';
+ * client.login(token);
+ * @example
+ * // log the client in using email and password
+ * const email = 'user@email.com';
+ * const password = 'supersecret123';
+ * client.login(email, password);
+ */
+ login(token) {
+ return this.rest.methods.login(token);
+ }
+
+ /**
+ * Destroys the client and logs out.
+ * @returns {Promise}
+ */
+ destroy() {
+ for (const t of this._timeouts) clearTimeout(t);
+ for (const i of this._intervals) clearInterval(i);
+ this._timeouts.clear();
+ this._intervals.clear();
+ return this.manager.destroy();
+ }
+
+ /**
+ * This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however
+ * if you wish to force a sync of guild data, you can use this.
+ * <warn>This is only available when using a user account.</warn>
+ * @param {Guild[]|Collection<string, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
+ */
+ syncGuilds(guilds = this.guilds) {
+ if (this.user.bot) return;
+ this.ws.send({
+ op: 12,
+ d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
+ });
+ }
+
+ /**
+ * Caches a user, or obtains it from the cache if it's already cached.
+ * <warn>This is only available when using a bot account.</warn>
+ * @param {string} id The ID of the user to obtain
+ * @returns {Promise<User>}
+ */
+ fetchUser(id) {
+ if (this.users.has(id)) return Promise.resolve(this.users.get(id));
+ return this.rest.methods.getUser(id);
+ }
+
+ /**
+ * Fetches an invite object from an invite code.
+ * @param {InviteResolvable} invite An invite code or URL
+ * @returns {Promise<Invite>}
+ */
+ fetchInvite(invite) {
+ const code = this.resolver.resolveInviteCode(invite);
+ return this.rest.methods.getInvite(code);
+ }
+
+ /**
+ * Fetch a webhook by ID.
+ * @param {string} id ID of the webhook
+ * @param {string} [token] Token for the webhook
+ * @returns {Promise<Webhook>}
+ */
+ fetchWebhook(id, token) {
+ return this.rest.methods.getWebhook(id, token);
+ }
+
+ /**
+ * Sweeps all channels' messages and removes the ones older than the max message lifetime.
+ * If the message has been edited, the time of the edit is used rather than the time of the original message.
+ * @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds)
+ * will be removed from the caches. The default is based on the client's `messageCacheLifetime` option.
+ * @returns {number} Amount of messages that were removed from the caches,
+ * or -1 if the message cache lifetime is unlimited
+ */
+ sweepMessages(lifetime = this.options.messageCacheLifetime) {
+ if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('The lifetime must be a number.');
+ if (lifetime <= 0) {
+ this.emit('debug', 'Didn\'t sweep messages - lifetime is unlimited');
+ return -1;
+ }
+
+ const lifetimeMs = lifetime * 1000;
+ const now = Date.now();
+ let channels = 0;
+ let messages = 0;
+
+ for (const channel of this.channels.values()) {
+ if (!channel.messages) continue;
+ channels++;
+
+ for (const message of channel.messages.values()) {
+ if (now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs) {
+ channel.messages.delete(message.id);
+ messages++;
+ }
+ }
+ }
+
+ this.emit('debug', `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`);
+ return messages;
+ }
+
+ /**
+ * Gets the bot's OAuth2 application.
+ * <warn>This is only available when using a bot account.</warn>
+ * @returns {Promise<ClientOAuth2Application>}
+ */
+ fetchApplication() {
+ if (!this.user.bot) throw new Error(Constants.Errors.NO_BOT_ACCOUNT);
+ return this.rest.methods.getMyApplication();
+ }
+
+ /**
+ * Generate an invite link for your bot
+ * @param {PermissionResolvable[]|number} [permissions] An array of permissions to request
+ * @returns {Promise<string>} The invite link
+ * @example
+ * client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'])
+ * .then(link => {
+ * console.log(`Generated bot invite link: ${link}`);
+ * });
+ */
+ generateInvite(permissions) {
+ if (permissions) {
+ if (permissions instanceof Array) permissions = this.resolver.resolvePermissions(permissions);
+ } else {
+ permissions = 0;
+ }
+ return this.fetchApplication().then(application =>
+ `https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
+ );
+ }
+
+ /**
+ * Sets a timeout that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ setTimeout(fn, delay, ...args) {
+ const timeout = setTimeout(() => {
+ fn();
+ this._timeouts.delete(timeout);
+ }, delay, ...args);
+ this._timeouts.add(timeout);
+ return timeout;
+ }
+
+ /**
+ * Clears a timeout
+ * @param {Timeout} timeout Timeout to cancel
+ */
+ clearTimeout(timeout) {
+ clearTimeout(timeout);
+ this._timeouts.delete(timeout);
+ }
+
+ /**
+ * Sets an interval that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ setInterval(fn, delay, ...args) {
+ const interval = setInterval(fn, delay, ...args);
+ this._intervals.add(interval);
+ return interval;
+ }
+
+ /**
+ * Clears an interval
+ * @param {Timeout} interval Interval to cancel
+ */
+ clearInterval(interval) {
+ clearInterval(interval);
+ this._intervals.delete(interval);
+ }
+
+ _pong(startTime) {
+ this.pings.unshift(Date.now() - startTime);
+ if (this.pings.length > 3) this.pings.length = 3;
+ this.ws.lastHeartbeatAck = true;
+ }
+
+ _setPresence(id, presence) {
+ if (this.presences.get(id)) {
+ this.presences.get(id).update(presence);
+ return;
+ }
+ this.presences.set(id, new Presence(presence));
+ }
+
+ _eval(script) {
+ return eval(script);
+ }
+
+ _validateOptions(options = this.options) {
+ if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) {
+ throw new TypeError('The shardCount option must be a number.');
+ }
+ if (typeof options.shardId !== 'number' || isNaN(options.shardId)) {
+ throw new TypeError('The shardId option must be a number.');
+ }
+ if (options.shardCount < 0) throw new RangeError('The shardCount option must be at least 0.');
+ if (options.shardId < 0) throw new RangeError('The shardId option must be at least 0.');
+ if (options.shardId !== 0 && options.shardId >= options.shardCount) {
+ throw new RangeError('The shardId option must be less than shardCount.');
+ }
+ if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) {
+ throw new TypeError('The messageCacheMaxSize option must be a number.');
+ }
+ if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
+ throw new TypeError('The messageCacheLifetime option must be a number.');
+ }
+ if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
+ throw new TypeError('The messageSweepInterval option must be a number.');
+ }
+ if (typeof options.fetchAllMembers !== 'boolean') {
+ throw new TypeError('The fetchAllMembers option must be a boolean.');
+ }
+ if (typeof options.disableEveryone !== 'boolean') {
+ throw new TypeError('The disableEveryone option must be a boolean.');
+ }
+ if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
+ throw new TypeError('The restWsBridgeTimeout option must be a number.');
+ }
+ if (!(options.disabledEvents instanceof Array)) throw new TypeError('The disabledEvents option must be an Array.');
+ }
+}
+
+module.exports = Client;
+
+/**
+ * Emitted for general warnings
+ * @event Client#warn
+ * @param {string} info The warning
+ */
+
+/**
+ * Emitted for general debugging information
+ * @event Client#debug
+ * @param {string} info The debug information
+ */
diff --git a/node_modules/discord.js/src/client/ClientDataManager.js b/node_modules/discord.js/src/client/ClientDataManager.js
new file mode 100644
index 0000000..32b442b
--- /dev/null
+++ b/node_modules/discord.js/src/client/ClientDataManager.js
@@ -0,0 +1,129 @@
+const Constants = require('../util/Constants');
+const cloneObject = require('../util/CloneObject');
+const Guild = require('../structures/Guild');
+const User = require('../structures/User');
+const DMChannel = require('../structures/DMChannel');
+const Emoji = require('../structures/Emoji');
+const TextChannel = require('../structures/TextChannel');
+const VoiceChannel = require('../structures/VoiceChannel');
+const GuildChannel = require('../structures/GuildChannel');
+const GroupDMChannel = require('../structures/GroupDMChannel');
+
+class ClientDataManager {
+ constructor(client) {
+ this.client = client;
+ }
+
+ get pastReady() {
+ return this.client.ws.status === Constants.Status.READY;
+ }
+
+ newGuild(data) {
+ const already = this.client.guilds.has(data.id);
+ const guild = new Guild(this.client, data);
+ this.client.guilds.set(guild.id, guild);
+ if (this.pastReady && !already) {
+ /**
+ * Emitted whenever the client joins a guild.
+ * @event Client#guildCreate
+ * @param {Guild} guild The created guild
+ */
+ if (this.client.options.fetchAllMembers) {
+ guild.fetchMembers().then(() => { this.client.emit(Constants.Events.GUILD_CREATE, guild); });
+ } else {
+ this.client.emit(Constants.Events.GUILD_CREATE, guild);
+ }
+ }
+
+ return guild;
+ }
+
+ newUser(data) {
+ if (this.client.users.has(data.id)) return this.client.users.get(data.id);
+ const user = new User(this.client, data);
+ this.client.users.set(user.id, user);
+ return user;
+ }
+
+ newChannel(data, guild) {
+ const already = this.client.channels.has(data.id);
+ let channel;
+ if (data.type === Constants.ChannelTypes.DM) {
+ channel = new DMChannel(this.client, data);
+ } else if (data.type === Constants.ChannelTypes.groupDM) {
+ channel = new GroupDMChannel(this.client, data);
+ } else {
+ guild = guild || this.client.guilds.get(data.guild_id);
+ if (guild) {
+ if (data.type === Constants.ChannelTypes.text) {
+ channel = new TextChannel(guild, data);
+ guild.channels.set(channel.id, channel);
+ } else if (data.type === Constants.ChannelTypes.voice) {
+ channel = new VoiceChannel(guild, data);
+ guild.channels.set(channel.id, channel);
+ }
+ }
+ }
+
+ if (channel) {
+ if (this.pastReady && !already) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
+ this.client.channels.set(channel.id, channel);
+ return channel;
+ }
+
+ return null;
+ }
+
+ newEmoji(data, guild) {
+ const already = guild.emojis.has(data.id);
+ if (data && !already) {
+ let emoji = new Emoji(guild, data);
+ this.client.emit(Constants.Events.GUILD_EMOJI_CREATE, emoji);
+ guild.emojis.set(emoji.id, emoji);
+ return emoji;
+ } else if (already) {
+ return guild.emojis.get(data.id);
+ }
+
+ return null;
+ }
+
+ killEmoji(emoji) {
+ if (!(emoji instanceof Emoji && emoji.guild)) return;
+ this.client.emit(Constants.Events.GUILD_EMOJI_DELETE, emoji);
+ emoji.guild.emojis.delete(emoji.id);
+ }
+
+ killGuild(guild) {
+ const already = this.client.guilds.has(guild.id);
+ this.client.guilds.delete(guild.id);
+ if (already && this.pastReady) this.client.emit(Constants.Events.GUILD_DELETE, guild);
+ }
+
+ killUser(user) {
+ this.client.users.delete(user.id);
+ }
+
+ killChannel(channel) {
+ this.client.channels.delete(channel.id);
+ if (channel instanceof GuildChannel) channel.guild.channels.delete(channel.id);
+ }
+
+ updateGuild(currentGuild, newData) {
+ const oldGuild = cloneObject(currentGuild);
+ currentGuild.setup(newData);
+ if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
+ }
+
+ updateChannel(currentChannel, newData) {
+ currentChannel.setup(newData);
+ }
+
+ updateEmoji(currentEmoji, newData) {
+ const oldEmoji = cloneObject(currentEmoji);
+ currentEmoji.setup(newData);
+ this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
+ }
+}
+
+module.exports = ClientDataManager;
diff --git a/node_modules/discord.js/src/client/ClientDataResolver.js b/node_modules/discord.js/src/client/ClientDataResolver.js
new file mode 100644
index 0000000..d38fb7c
--- /dev/null
+++ b/node_modules/discord.js/src/client/ClientDataResolver.js
@@ -0,0 +1,309 @@
+const path = require('path');
+const fs = require('fs');
+const request = require('superagent');
+
+const Constants = require('../util/Constants');
+const convertArrayBuffer = require('../util/ConvertArrayBuffer');
+const User = require('../structures/User');
+const Message = require('../structures/Message');
+const Guild = require('../structures/Guild');
+const Channel = require('../structures/Channel');
+const GuildMember = require('../structures/GuildMember');
+const Emoji = require('../structures/Emoji');
+const ReactionEmoji = require('../structures/ReactionEmoji');
+
+/**
+ * The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
+ * extracting a User from a Message object.
+ * @private
+ */
+class ClientDataResolver {
+ /**
+ * @param {Client} client The client the resolver is for
+ */
+ constructor(client) {
+ this.client = client;
+ }
+
+ /**
+ * Data that resolves to give a User object. This can be:
+ * * A User object
+ * * A user ID
+ * * A Message object (resolves to the message author)
+ * * A Guild object (owner of the guild)
+ * * A GuildMember object
+ * @typedef {User|string|Message|Guild|GuildMember} UserResolvable
+ */
+
+ /**
+ * Resolves a UserResolvable to a User object
+ * @param {UserResolvable} user The UserResolvable to identify
+ * @returns {?User}
+ */
+ resolveUser(user) {
+ if (user instanceof User) return user;
+ if (typeof user === 'string') return this.client.users.get(user) || null;
+ if (user instanceof GuildMember) return user.user;
+ if (user instanceof Message) return user.author;
+ if (user instanceof Guild) return user.owner;
+ return null;
+ }
+
+ /**
+ * Resolves a UserResolvable to a user ID string
+ * @param {UserResolvable} user The UserResolvable to identify
+ * @returns {?string}
+ */
+ resolveUserID(user) {
+ if (user instanceof User || user instanceof GuildMember) return user.id;
+ if (typeof user === 'string') return user || null;
+ if (user instanceof Message) return user.author.id;
+ if (user instanceof Guild) return user.ownerID;
+ return null;
+ }
+
+ /**
+ * Data that resolves to give a Guild object. This can be:
+ * * A Guild object
+ * * A Guild ID
+ * @typedef {Guild|string} GuildResolvable
+ */
+
+ /**
+ * Resolves a GuildResolvable to a Guild object
+ * @param {GuildResolvable} guild The GuildResolvable to identify
+ * @returns {?Guild}
+ */
+ resolveGuild(guild) {
+ if (guild instanceof Guild) return guild;
+ if (typeof guild === 'string') return this.client.guilds.get(guild) || null;
+ return null;
+ }
+
+ /**
+ * Data that resolves to give a GuildMember object. This can be:
+ * * A GuildMember object
+ * * A User object
+ * @typedef {Guild} GuildMemberResolvable
+ */
+
+ /**
+ * Resolves a GuildMemberResolvable to a GuildMember object
+ * @param {GuildResolvable} guild The guild that the member is part of
+ * @param {UserResolvable} user The user that is part of the guild
+ * @returns {?GuildMember}
+ */
+ resolveGuildMember(guild, user) {
+ if (user instanceof GuildMember) return user;
+ guild = this.resolveGuild(guild);
+ user = this.resolveUser(user);
+ if (!guild || !user) return null;
+ return guild.members.get(user.id) || null;
+ }
+
+ /**
+ * Data that can be resolved to give a Channel. This can be:
+ * * A Channel object
+ * * A Message object (the channel the message was sent in)
+ * * A Guild object (the #general channel)
+ * * A channel ID
+ * @typedef {Channel|Guild|Message|string} ChannelResolvable
+ */
+
+ /**
+ * Resolves a ChannelResolvable to a Channel object
+ * @param {ChannelResolvable} channel The channel resolvable to resolve
+ * @returns {?Channel}
+ */
+ resolveChannel(channel) {
+ if (channel instanceof Channel) return channel;
+ if (channel instanceof Message) return channel.channel;
+ if (channel instanceof Guild) return channel.channels.get(channel.id) || null;
+ if (typeof channel === 'string') return this.client.channels.get(channel) || null;
+ return null;
+ }
+
+ /**
+ * Data that can be resolved to give an invite code. This can be:
+ * * An invite code
+ * * An invite URL
+ * @typedef {string} InviteResolvable
+ */
+
+ /**
+ * Resolves InviteResolvable to an invite code
+ * @param {InviteResolvable} data The invite resolvable to resolve
+ * @returns {string}
+ */
+ resolveInviteCode(data) {
+ const inviteRegex = /discord(?:app)?\.(?:gg|com\/invite)\/([a-z0-9]{5})/i;
+ const match = inviteRegex.exec(data);
+ if (match && match[1]) return match[1];
+ return data;
+ }
+
+ /**
+ * Data that can be resolved to give a permission number. This can be:
+ * * A string
+ * * A permission number
+ *
+ * Possible strings:
+ * ```js
+ * [
+ * "CREATE_INSTANT_INVITE",
+ * "KICK_MEMBERS",
+ * "BAN_MEMBERS",
+ * "ADMINISTRATOR",
+ * "MANAGE_CHANNELS",
+ * "MANAGE_GUILD",
+ * "ADD_REACTIONS", // add reactions to messages
+ * "READ_MESSAGES",
+ * "SEND_MESSAGES",
+ * "SEND_TTS_MESSAGES",
+ * "MANAGE_MESSAGES",
+ * "EMBED_LINKS",
+ * "ATTACH_FILES",
+ * "READ_MESSAGE_HISTORY",
+ * "MENTION_EVERYONE",
+ * "EXTERNAL_EMOJIS", // use external emojis
+ * "CONNECT", // connect to voice
+ * "SPEAK", // speak on voice
+ * "MUTE_MEMBERS", // globally mute members on voice
+ * "DEAFEN_MEMBERS", // globally deafen members on voice
+ * "MOVE_MEMBERS", // move member's voice channels
+ * "USE_VAD", // use voice activity detection
+ * "CHANGE_NICKNAME",
+ * "MANAGE_NICKNAMES", // change nicknames of others
+ * "MANAGE_ROLES_OR_PERMISSIONS",
+ * "MANAGE_WEBHOOKS",
+ * "MANAGE_EMOJIS"
+ * ]
+ * ```
+ * @typedef {string|number} PermissionResolvable
+ */
+
+ /**
+ * Resolves a PermissionResolvable to a permission number
+ * @param {PermissionResolvable} permission The permission resolvable to resolve
+ * @returns {number}
+ */
+ resolvePermission(permission) {
+ if (typeof permission === 'string') permission = Constants.PermissionFlags[permission];
+ if (typeof permission !== 'number' || permission < 1) throw new Error(Constants.Errors.NOT_A_PERMISSION);
+ return permission;
+ }
+
+ /**
+ * Turn an array of permissions into a valid Discord permission bitfield
+ * @param {PermissionResolvable[]} permissions Permissions to resolve together
+ * @returns {number}
+ */
+ resolvePermissions(permissions) {
+ let bitfield = 0;
+ for (const permission of permissions) bitfield |= this.resolvePermission(permission);
+ return bitfield;
+ }
+
+ /**
+ * Data that can be resolved to give a string. This can be:
+ * * A string
+ * * An array (joined with a new line delimiter to give a string)
+ * * Any value
+ * @typedef {string|Array|*} StringResolvable
+ */
+
+ /**
+ * Resolves a StringResolvable to a string
+ * @param {StringResolvable} data The string resolvable to resolve
+ * @returns {string}
+ */
+ resolveString(data) {
+ if (typeof data === 'string') return data;
+ if (data instanceof Array) return data.join('\n');
+ return String(data);
+ }
+
+ /**
+ * Data that resolves to give a Base64 string, typically for image uploading. This can be:
+ * * A Buffer
+ * * A base64 string
+ * @typedef {Buffer|string} Base64Resolvable
+ */
+
+ /**
+ * Resolves a Base64Resolvable to a Base 64 image
+ * @param {Base64Resolvable} data The base 64 resolvable you want to resolve
+ * @returns {?string}
+ */
+ resolveBase64(data) {
+ if (data instanceof Buffer) return `data:image/jpg;base64,${data.toString('base64')}`;
+ return data;
+ }
+
+ /**
+ * Data that can be resolved to give a Buffer. This can be:
+ * * A Buffer
+ * * The path to a local file
+ * * A URL
+ * @typedef {string|Buffer} BufferResolvable
+ */
+
+ /**
+ * Resolves a BufferResolvable to a Buffer
+ * @param {BufferResolvable} resource The buffer resolvable to resolve
+ * @returns {Promise<Buffer>}
+ */
+ resolveBuffer(resource) {
+ if (resource instanceof Buffer) return Promise.resolve(resource);
+ if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertArrayBuffer(resource));
+
+ if (typeof resource === 'string') {
+ return new Promise((resolve, reject) => {
+ if (/^https?:\/\//.test(resource)) {
+ const req = request.get(resource).set('Content-Type', 'blob');
+ if (this.client.browser) req.responseType('arraybuffer');
+ req.end((err, res) => {
+ if (err) return reject(err);
+ if (this.client.browser) return resolve(convertArrayBuffer(res.xhr.response));
+ if (!(res.body instanceof Buffer)) return reject(new TypeError('The response body isn\'t a Buffer.'));
+ return resolve(res.body);
+ });
+ } else {
+ const file = path.resolve(resource);
+ fs.stat(file, (err, stats) => {
+ if (err) reject(err);
+ if (!stats || !stats.isFile()) throw new Error(`The file could not be found: ${file}`);
+ fs.readFile(file, (err2, data) => {
+ if (err2) reject(err2); else resolve(data);
+ });
+ });
+ }
+ });
+ }
+
+ return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
+ }
+
+ /**
+ * Data that can be resolved to give an emoji identifier. This can be:
+ * * A string
+ * * An Emoji
+ * * A ReactionEmoji
+ * @typedef {string|Emoji|ReactionEmoji} EmojiIdentifierResolvable
+ */
+
+ /**
+ * Resolves an EmojiResolvable to an emoji identifier
+ * @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
+ * @returns {string}
+ */
+ resolveEmojiIdentifier(emoji) {
+ if (emoji instanceof Emoji || emoji instanceof ReactionEmoji) return emoji.identifier;
+ if (typeof emoji === 'string') {
+ if (!emoji.includes('%')) return encodeURIComponent(emoji);
+ }
+ return null;
+ }
+}
+
+module.exports = ClientDataResolver;
diff --git a/node_modules/discord.js/src/client/ClientManager.js b/node_modules/discord.js/src/client/ClientManager.js
new file mode 100644
index 0000000..0cfbbfd
--- /dev/null
+++ b/node_modules/discord.js/src/client/ClientManager.js
@@ -0,0 +1,67 @@
+const Constants = require('../util/Constants');
+
+/**
+ * Manages the State and Background Tasks of the Client
+ * @private
+ */
+class ClientManager {
+ constructor(client) {
+ /**
+ * The Client that instantiated this Manager
+ * @type {Client}
+ */
+ this.client = client;
+
+ /**
+ * The heartbeat interval, null if not yet set
+ * @type {?number}
+ */
+ this.heartbeatInterval = null;
+ }
+
+ /**
+ * Connects the Client to the WebSocket
+ * @param {string} token The authorization token
+ * @param {Function} resolve Function to run when connection is successful
+ * @param {Function} reject Function to run when connection fails
+ */
+ connectToWebSocket(token, resolve, reject) {
+ this.client.emit(Constants.Events.DEBUG, `Authenticated using token ${token}`);
+ this.client.token = token;
+ const timeout = this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
+ this.client.rest.methods.getGateway().then(gateway => {
+ this.client.emit(Constants.Events.DEBUG, `Using gateway ${gateway}`);
+ this.client.ws.connect(gateway);
+ this.client.ws.once('close', event => {
+ if (event.code === 4004) reject(new Error(Constants.Errors.BAD_LOGIN));
+ if (event.code === 4010) reject(new Error(Constants.Errors.INVALID_SHARD));
+ });
+ this.client.once(Constants.Events.READY, () => {
+ resolve(token);
+ this.client.clearTimeout(timeout);
+ });
+ }, reject);
+ }
+
+ /**
+ * Sets up a keep-alive interval to keep the Client's connection valid
+ * @param {number} time The interval in milliseconds at which heartbeat packets should be sent
+ */
+ setupKeepAlive(time) {
+ this.heartbeatInterval = this.client.setInterval(() => this.client.ws.heartbeat(true), time);
+ }
+
+ destroy() {
+ this.client.ws.destroy();
+ if (this.client.user.bot) {
+ this.client.token = null;
+ return Promise.resolve();
+ } else {
+ return this.client.rest.methods.logout().then(() => {
+ this.client.token = null;
+ });
+ }
+ }
+}
+
+module.exports = ClientManager;
diff --git a/node_modules/discord.js/src/client/WebhookClient.js b/node_modules/discord.js/src/client/WebhookClient.js
new file mode 100644
index 0000000..68a8a94
--- /dev/null
+++ b/node_modules/discord.js/src/client/WebhookClient.js
@@ -0,0 +1,46 @@
+const Webhook = require('../structures/Webhook');
+const RESTManager = require('./rest/RESTManager');
+const ClientDataResolver = require('./ClientDataResolver');
+const mergeDefault = require('../util/MergeDefault');
+const Constants = require('../util/Constants');
+
+/**
+ * The Webhook Client
+ * @extends {Webhook}
+ */
+class WebhookClient extends Webhook {
+ /**
+ * @param {string} id The id of the webhook.
+ * @param {string} token the token of the webhook.
+ * @param {ClientOptions} [options] Options for the client
+ * @example
+ * // create a new webhook and send a message
+ * let hook = new Discord.WebhookClient('1234', 'abcdef')
+ * hook.sendMessage('This will send a message').catch(console.error)
+ */
+ constructor(id, token, options) {
+ super(null, id, token);
+
+ /**
+ * The options the client was instantiated with
+ * @type {ClientOptions}
+ */
+ this.options = mergeDefault(Constants.DefaultOptions, options);
+
+ /**
+ * The REST manager of the client
+ * @type {RESTManager}
+ * @private
+ */
+ this.rest = new RESTManager(this);
+
+ /**
+ * The Data Resolver of the Client
+ * @type {ClientDataResolver}
+ * @private
+ */
+ this.resolver = new ClientDataResolver(this);
+ }
+}
+
+module.exports = WebhookClient;
diff --git a/node_modules/discord.js/src/client/actions/Action.js b/node_modules/discord.js/src/client/actions/Action.js
new file mode 100644
index 0000000..8fdadc9
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/Action.js
@@ -0,0 +1,23 @@
+/*
+
+ABOUT ACTIONS
+
+Actions are similar to WebSocket Packet Handlers, but since introducing
+the REST API methods, in order to prevent rewriting code to handle data,
+"actions" have been introduced. They're basically what Packet Handlers
+used to be but they're strictly for manipulating data and making sure
+that WebSocket events don't clash with REST methods.
+
+*/
+
+class GenericAction {
+ constructor(client) {
+ this.client = client;
+ }
+
+ handle(data) {
+ return data;
+ }
+}
+
+module.exports = GenericAction;
diff --git a/node_modules/discord.js/src/client/actions/ActionsManager.js b/node_modules/discord.js/src/client/actions/ActionsManager.js
new file mode 100644
index 0000000..ac95aa7
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ActionsManager.js
@@ -0,0 +1,38 @@
+class ActionsManager {
+ constructor(client) {
+ this.client = client;
+
+ this.register(require('./MessageCreate'));
+ this.register(require('./MessageDelete'));
+ this.register(require('./MessageDeleteBulk'));
+ this.register(require('./MessageUpdate'));
+ this.register(require('./MessageReactionAdd'));
+ this.register(require('./MessageReactionRemove'));
+ this.register(require('./MessageReactionRemoveAll'));
+ this.register(require('./ChannelCreate'));
+ this.register(require('./ChannelDelete'));
+ this.register(require('./ChannelUpdate'));
+ this.register(require('./GuildDelete'));
+ this.register(require('./GuildUpdate'));
+ this.register(require('./GuildMemberGet'));
+ this.register(require('./GuildMemberRemove'));
+ this.register(require('./GuildBanRemove'));
+ this.register(require('./GuildRoleCreate'));
+ this.register(require('./GuildRoleDelete'));
+ this.register(require('./GuildRoleUpdate'));
+ this.register(require('./UserGet'));
+ this.register(require('./UserUpdate'));
+ this.register(require('./UserNoteUpdate'));
+ this.register(require('./GuildSync'));
+ this.register(require('./GuildEmojiCreate'));
+ this.register(require('./GuildEmojiDelete'));
+ this.register(require('./GuildEmojiUpdate'));
+ this.register(require('./GuildRolesPositionUpdate'));
+ }
+
+ register(Action) {
+ this[Action.name.replace(/Action$/, '')] = new Action(this.client);
+ }
+}
+
+module.exports = ActionsManager;
diff --git a/node_modules/discord.js/src/client/actions/ChannelCreate.js b/node_modules/discord.js/src/client/actions/ChannelCreate.js
new file mode 100644
index 0000000..dc47041
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ChannelCreate.js
@@ -0,0 +1,13 @@
+const Action = require('./Action');
+
+class ChannelCreateAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const channel = client.dataManager.newChannel(data);
+ return {
+ channel,
+ };
+ }
+}
+
+module.exports = ChannelCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/ChannelDelete.js b/node_modules/discord.js/src/client/actions/ChannelDelete.js
new file mode 100644
index 0000000..7b847ef
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ChannelDelete.js
@@ -0,0 +1,31 @@
+const Action = require('./Action');
+
+class ChannelDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ let channel = client.channels.get(data.id);
+ if (channel) {
+ client.dataManager.killChannel(channel);
+ this.deleted.set(channel.id, channel);
+ this.scheduleForDeletion(channel.id);
+ } else {
+ channel = this.deleted.get(data.id) || null;
+ }
+
+ return {
+ channel,
+ };
+ }
+
+ scheduleForDeletion(id) {
+ this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+module.exports = ChannelDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/ChannelUpdate.js b/node_modules/discord.js/src/client/actions/ChannelUpdate.js
new file mode 100644
index 0000000..df50ed4
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/ChannelUpdate.js
@@ -0,0 +1,34 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const cloneObject = require('../../util/CloneObject');
+
+class ChannelUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const channel = client.channels.get(data.id);
+ if (channel) {
+ const oldChannel = cloneObject(channel);
+ channel.setup(data);
+ client.emit(Constants.Events.CHANNEL_UPDATE, oldChannel, channel);
+ return {
+ old: oldChannel,
+ updated: channel,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a channel is updated - e.g. name change, topic change.
+ * @event Client#channelUpdate
+ * @param {Channel} oldChannel The channel before the update
+ * @param {Channel} newChannel The channel after the update
+ */
+
+module.exports = ChannelUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildBanRemove.js b/node_modules/discord.js/src/client/actions/GuildBanRemove.js
new file mode 100644
index 0000000..0276a52
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildBanRemove.js
@@ -0,0 +1,13 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildBanRemove extends Action {
+ handle(data) {
+ const client = this.client;
+ const guild = client.guilds.get(data.guild_id);
+ const user = client.dataManager.newUser(data.user);
+ if (guild && user) client.emit(Constants.Events.GUILD_BAN_REMOVE, guild, user);
+ }
+}
+
+module.exports = GuildBanRemove;
diff --git a/node_modules/discord.js/src/client/actions/GuildDelete.js b/node_modules/discord.js/src/client/actions/GuildDelete.js
new file mode 100644
index 0000000..1214263
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildDelete.js
@@ -0,0 +1,51 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ let guild = client.guilds.get(data.id);
+ if (guild) {
+ if (guild.available && data.unavailable) {
+ // guild is unavailable
+ guild.available = false;
+ client.emit(Constants.Events.GUILD_UNAVAILABLE, guild);
+
+ // stops the GuildDelete packet thinking a guild was actually deleted,
+ // handles emitting of event itself
+ return {
+ guild: null,
+ };
+ }
+
+ // delete guild
+ client.guilds.delete(guild.id);
+ this.deleted.set(guild.id, guild);
+ this.scheduleForDeletion(guild.id);
+ } else {
+ guild = this.deleted.get(data.id) || null;
+ }
+
+ return {
+ guild,
+ };
+ }
+
+ scheduleForDeletion(id) {
+ this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+/**
+ * Emitted whenever a guild becomes unavailable, likely due to a server outage.
+ * @event Client#guildUnavailable
+ * @param {Guild} guild The guild that has become unavailable.
+ */
+
+module.exports = GuildDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js b/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js
new file mode 100644
index 0000000..5df1ced
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojiCreate.js
@@ -0,0 +1,18 @@
+const Action = require('./Action');
+
+class GuildEmojiCreateAction extends Action {
+ handle(guild, createdEmoji) {
+ const client = this.client;
+ const emoji = client.dataManager.newEmoji(createdEmoji, guild);
+ return {
+ emoji,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a custom emoji is created in a guild
+ * @event Client#emojiCreate
+ * @param {Emoji} emoji The emoji that was created.
+ */
+module.exports = GuildEmojiCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js b/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js
new file mode 100644
index 0000000..8cfa205
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojiDelete.js
@@ -0,0 +1,18 @@
+const Action = require('./Action');
+
+class GuildEmojiDeleteAction extends Action {
+ handle(emoji) {
+ const client = this.client;
+ client.dataManager.killEmoji(emoji);
+ return {
+ emoji,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a custom guild emoji is deleted
+ * @event Client#emojiDelete
+ * @param {Emoji} emoji The emoji that was deleted.
+ */
+module.exports = GuildEmojiDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js b/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js
new file mode 100644
index 0000000..94bfa24
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js
@@ -0,0 +1,15 @@
+const Action = require('./Action');
+
+class GuildEmojiUpdateAction extends Action {
+ handle(oldEmoji, newEmoji) {
+ this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
+ }
+}
+
+/**
+ * Emitted whenever a custom guild emoji is updated
+ * @event Client#emojiUpdate
+ * @param {Emoji} oldEmoji The old emoji
+ * @param {Emoji} newEmoji The new emoji
+ */
+module.exports = GuildEmojiUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildMemberGet.js b/node_modules/discord.js/src/client/actions/GuildMemberGet.js
new file mode 100644
index 0000000..b00fa0f
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildMemberGet.js
@@ -0,0 +1,12 @@
+const Action = require('./Action');
+
+class GuildMemberGetAction extends Action {
+ handle(guild, data) {
+ const member = guild._addMember(data, false);
+ return {
+ member,
+ };
+ }
+}
+
+module.exports = GuildMemberGetAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildMemberRemove.js b/node_modules/discord.js/src/client/actions/GuildMemberRemove.js
new file mode 100644
index 0000000..d68b8b5
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildMemberRemove.js
@@ -0,0 +1,49 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildMemberRemoveAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ let member = guild.members.get(data.user.id);
+ if (member) {
+ guild.memberCount--;
+ guild._removeMember(member);
+ this.deleted.set(guild.id + data.user.id, member);
+ if (client.status === Constants.Status.READY) client.emit(Constants.Events.GUILD_MEMBER_REMOVE, member);
+ this.scheduleForDeletion(guild.id, data.user.id);
+ } else {
+ member = this.deleted.get(guild.id + data.user.id) || null;
+ }
+
+ return {
+ guild,
+ member,
+ };
+ }
+
+ return {
+ guild,
+ member: null,
+ };
+ }
+
+ scheduleForDeletion(guildID, userID) {
+ this.client.setTimeout(() => this.deleted.delete(guildID + userID), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+/**
+ * Emitted whenever a member leaves a guild, or is kicked.
+ * @event Client#guildMemberRemove
+ * @param {GuildMember} member The member that has left/been kicked from the guild.
+ */
+
+module.exports = GuildMemberRemoveAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildRoleCreate.js b/node_modules/discord.js/src/client/actions/GuildRoleCreate.js
new file mode 100644
index 0000000..82ea19a
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRoleCreate.js
@@ -0,0 +1,32 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const Role = require('../../structures/Role');
+
+class GuildRoleCreate extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ const already = guild.roles.has(data.role.id);
+ const role = new Role(guild, data.role);
+ guild.roles.set(role.id, role);
+ if (!already) client.emit(Constants.Events.GUILD_ROLE_CREATE, role);
+ return {
+ role,
+ };
+ }
+
+ return {
+ role: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a role is created.
+ * @event Client#roleCreate
+ * @param {Role} role The role that was created.
+ */
+
+module.exports = GuildRoleCreate;
diff --git a/node_modules/discord.js/src/client/actions/GuildRoleDelete.js b/node_modules/discord.js/src/client/actions/GuildRoleDelete.js
new file mode 100644
index 0000000..eeaa1e9
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRoleDelete.js
@@ -0,0 +1,46 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class GuildRoleDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ let role = guild.roles.get(data.role_id);
+ if (role) {
+ guild.roles.delete(data.role_id);
+ this.deleted.set(guild.id + data.role_id, role);
+ this.scheduleForDeletion(guild.id, data.role_id);
+ client.emit(Constants.Events.GUILD_ROLE_DELETE, role);
+ } else {
+ role = this.deleted.get(guild.id + data.role_id) || null;
+ }
+
+ return {
+ role,
+ };
+ }
+
+ return {
+ role: null,
+ };
+ }
+
+ scheduleForDeletion(guildID, roleID) {
+ this.client.setTimeout(() => this.deleted.delete(guildID + roleID), this.client.options.restWsBridgeTimeout);
+ }
+}
+
+/**
+ * Emitted whenever a guild role is deleted.
+ * @event Client#roleDelete
+ * @param {Role} role The role that was deleted.
+ */
+
+module.exports = GuildRoleDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js b/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js
new file mode 100644
index 0000000..8270517
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRoleUpdate.js
@@ -0,0 +1,41 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const cloneObject = require('../../util/CloneObject');
+
+class GuildRoleUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ const roleData = data.role;
+ let oldRole = null;
+
+ const role = guild.roles.get(roleData.id);
+ if (role) {
+ oldRole = cloneObject(role);
+ role.setup(data.role);
+ client.emit(Constants.Events.GUILD_ROLE_UPDATE, oldRole, role);
+ }
+
+ return {
+ old: oldRole,
+ updated: role,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a guild role is updated.
+ * @event Client#roleUpdate
+ * @param {Role} oldRole The role before the update.
+ * @param {Role} newRole The role after the update.
+ */
+
+module.exports = GuildRoleUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js b/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js
new file mode 100644
index 0000000..a95c923
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js
@@ -0,0 +1,23 @@
+const Action = require('./Action');
+
+class GuildRolesPositionUpdate extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ for (const partialRole of data.roles) {
+ const role = guild.roles.get(partialRole.id);
+ if (role) {
+ role.position = partialRole.position;
+ }
+ }
+ }
+
+ return {
+ guild,
+ };
+ }
+}
+
+module.exports = GuildRolesPositionUpdate;
diff --git a/node_modules/discord.js/src/client/actions/GuildSync.js b/node_modules/discord.js/src/client/actions/GuildSync.js
new file mode 100644
index 0000000..7b94ec8
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildSync.js
@@ -0,0 +1,27 @@
+const Action = require('./Action');
+
+class GuildSync extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.id);
+ if (guild) {
+ data.presences = data.presences || [];
+ for (const presence of data.presences) {
+ guild._setPresence(presence.user.id, presence);
+ }
+
+ data.members = data.members || [];
+ for (const syncMember of data.members) {
+ const member = guild.members.get(syncMember.user.id);
+ if (member) {
+ guild._updateMember(member, syncMember);
+ } else {
+ guild._addMember(syncMember, false);
+ }
+ }
+ }
+ }
+}
+
+module.exports = GuildSync;
diff --git a/node_modules/discord.js/src/client/actions/GuildUpdate.js b/node_modules/discord.js/src/client/actions/GuildUpdate.js
new file mode 100644
index 0000000..efda7f7
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/GuildUpdate.js
@@ -0,0 +1,34 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const cloneObject = require('../../util/CloneObject');
+
+class GuildUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const guild = client.guilds.get(data.id);
+ if (guild) {
+ const oldGuild = cloneObject(guild);
+ guild.setup(data);
+ client.emit(Constants.Events.GUILD_UPDATE, oldGuild, guild);
+ return {
+ old: oldGuild,
+ updated: guild,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a guild is updated - e.g. name change.
+ * @event Client#guildUpdate
+ * @param {Guild} oldGuild The guild before the update.
+ * @param {Guild} newGuild The guild after the update.
+ */
+
+module.exports = GuildUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageCreate.js b/node_modules/discord.js/src/client/actions/MessageCreate.js
new file mode 100644
index 0000000..00fc1e9
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageCreate.js
@@ -0,0 +1,40 @@
+const Action = require('./Action');
+const Message = require('../../structures/Message');
+
+class MessageCreateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const channel = client.channels.get((data instanceof Array ? data[0] : data).channel_id);
+ const user = client.users.get((data instanceof Array ? data[0] : data).author.id);
+ if (channel) {
+ const member = channel.guild ? channel.guild.member(user) : null;
+ if (data instanceof Array) {
+ const messages = new Array(data.length);
+ for (let i = 0; i < data.length; i++) {
+ messages[i] = channel._cacheMessage(new Message(channel, data[i], client));
+ }
+ channel.lastMessageID = messages[messages.length - 1].id;
+ if (user) user.lastMessageID = messages[messages.length - 1].id;
+ if (member) member.lastMessageID = messages[messages.length - 1].id;
+ return {
+ messages,
+ };
+ } else {
+ const message = channel._cacheMessage(new Message(channel, data, client));
+ channel.lastMessageID = data.id;
+ if (user) user.lastMessageID = data.id;
+ if (member) member.lastMessageID = data.id;
+ return {
+ message,
+ };
+ }
+ }
+
+ return {
+ message: null,
+ };
+ }
+}
+
+module.exports = MessageCreateAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageDelete.js b/node_modules/discord.js/src/client/actions/MessageDelete.js
new file mode 100644
index 0000000..beb8050
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageDelete.js
@@ -0,0 +1,40 @@
+const Action = require('./Action');
+
+class MessageDeleteAction extends Action {
+ constructor(client) {
+ super(client);
+ this.deleted = new Map();
+ }
+
+ handle(data) {
+ const client = this.client;
+
+ const channel = client.channels.get(data.channel_id);
+ if (channel) {
+ let message = channel.messages.get(data.id);
+
+ if (message) {
+ channel.messages.delete(message.id);
+ this.deleted.set(channel.id + message.id, message);
+ this.scheduleForDeletion(channel.id, message.id);
+ } else {
+ message = this.deleted.get(channel.id + data.id) || null;
+ }
+
+ return {
+ message,
+ };
+ }
+
+ return {
+ message: null,
+ };
+ }
+
+ scheduleForDeletion(channelID, messageID) {
+ this.client.setTimeout(() => this.deleted.delete(channelID + messageID),
+ this.client.options.restWsBridgeTimeout);
+ }
+}
+
+module.exports = MessageDeleteAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js b/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js
new file mode 100644
index 0000000..6a12ef1
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageDeleteBulk.js
@@ -0,0 +1,24 @@
+const Action = require('./Action');
+const Collection = require('../../util/Collection');
+const Constants = require('../../util/Constants');
+
+class MessageDeleteBulkAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const channel = client.channels.get(data.channel_id);
+
+ const ids = data.ids;
+ const messages = new Collection();
+ for (const id of ids) {
+ const message = channel.messages.get(id);
+ if (message) messages.set(message.id, message);
+ }
+
+ if (messages.size > 0) client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages);
+ return {
+ messages,
+ };
+ }
+}
+
+module.exports = MessageDeleteBulkAction;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionAdd.js b/node_modules/discord.js/src/client/actions/MessageReactionAdd.js
new file mode 100644
index 0000000..f57ec2e
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionAdd.js
@@ -0,0 +1,43 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+/*
+{ user_id: 'id',
+ message_id: 'id',
+ emoji: { name: '�', id: null },
+ channel_id: 'id' } }
+*/
+
+class MessageReactionAdd extends Action {
+ handle(data) {
+ const user = this.client.users.get(data.user_id);
+ if (!user) return false;
+
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+
+ if (!data.emoji) return false;
+
+ const reaction = message._addReaction(data.emoji, user);
+
+ if (reaction) {
+ this.client.emit(Constants.Events.MESSAGE_REACTION_ADD, reaction, user);
+ }
+
+ return {
+ message,
+ reaction,
+ user,
+ };
+ }
+}
+/**
+ * Emitted whenever a reaction is added to a message.
+ * @event Client#messageReactionAdd
+ * @param {MessageReaction} messageReaction The reaction object.
+ * @param {User} user The user that applied the emoji or reaction emoji.
+ */
+module.exports = MessageReactionAdd;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionRemove.js b/node_modules/discord.js/src/client/actions/MessageReactionRemove.js
new file mode 100644
index 0000000..98a958d
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionRemove.js
@@ -0,0 +1,43 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+/*
+{ user_id: 'id',
+ message_id: 'id',
+ emoji: { name: '�', id: null },
+ channel_id: 'id' } }
+*/
+
+class MessageReactionRemove extends Action {
+ handle(data) {
+ const user = this.client.users.get(data.user_id);
+ if (!user) return false;
+
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+
+ if (!data.emoji) return false;
+
+ const reaction = message._removeReaction(data.emoji, user);
+
+ if (reaction) {
+ this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE, reaction, user);
+ }
+
+ return {
+ message,
+ reaction,
+ user,
+ };
+ }
+}
+/**
+ * Emitted whenever a reaction is removed from a message.
+ * @event Client#messageReactionRemove
+ * @param {MessageReaction} messageReaction The reaction object.
+ * @param {User} user The user that removed the emoji or reaction emoji.
+ */
+module.exports = MessageReactionRemove;
diff --git a/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js b/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js
new file mode 100644
index 0000000..f35b785
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js
@@ -0,0 +1,25 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class MessageReactionRemoveAll extends Action {
+ handle(data) {
+ const channel = this.client.channels.get(data.channel_id);
+ if (!channel || channel.type === 'voice') return false;
+
+ const message = channel.messages.get(data.message_id);
+ if (!message) return false;
+
+ message._clearReactions();
+ this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_ALL, message);
+
+ return {
+ message,
+ };
+ }
+}
+/**
+ * Emitted whenever all reactions are removed from a message.
+ * @event Client#messageReactionRemoveAll
+ * @param {MessageReaction} messageReaction The reaction object.
+ */
+module.exports = MessageReactionRemoveAll;
diff --git a/node_modules/discord.js/src/client/actions/MessageUpdate.js b/node_modules/discord.js/src/client/actions/MessageUpdate.js
new file mode 100644
index 0000000..a62c332
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/MessageUpdate.js
@@ -0,0 +1,43 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const cloneObject = require('../../util/CloneObject');
+
+class MessageUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const channel = client.channels.get(data.channel_id);
+ if (channel) {
+ const message = channel.messages.get(data.id);
+ if (message) {
+ const oldMessage = cloneObject(message);
+ message.patch(data);
+ message._edits.unshift(oldMessage);
+ client.emit(Constants.Events.MESSAGE_UPDATE, oldMessage, message);
+ return {
+ old: oldMessage,
+ updated: message,
+ };
+ }
+
+ return {
+ old: message,
+ updated: message,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a message is updated - e.g. embed or content change.
+ * @event Client#messageUpdate
+ * @param {Message} oldMessage The message before the update.
+ * @param {Message} newMessage The message after the update.
+ */
+
+module.exports = MessageUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/UserGet.js b/node_modules/discord.js/src/client/actions/UserGet.js
new file mode 100644
index 0000000..65e7c95
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/UserGet.js
@@ -0,0 +1,13 @@
+const Action = require('./Action');
+
+class UserGetAction extends Action {
+ handle(data) {
+ const client = this.client;
+ const user = client.dataManager.newUser(data);
+ return {
+ user,
+ };
+ }
+}
+
+module.exports = UserGetAction;
diff --git a/node_modules/discord.js/src/client/actions/UserNoteUpdate.js b/node_modules/discord.js/src/client/actions/UserNoteUpdate.js
new file mode 100644
index 0000000..4c2cc21
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/UserNoteUpdate.js
@@ -0,0 +1,30 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+
+class UserNoteUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ const oldNote = client.user.notes.get(data.id);
+ const note = data.note.length ? data.note : null;
+
+ client.user.notes.set(data.id, note);
+
+ client.emit(Constants.Events.USER_NOTE_UPDATE, data.id, oldNote, note);
+
+ return {
+ old: oldNote,
+ updated: note,
+ };
+ }
+}
+
+/**
+ * Emitted whenever a note is updated.
+ * @event Client#userNoteUpdate
+ * @param {User} user The user the note belongs to
+ * @param {string} oldNote The note content before the update
+ * @param {string} newNote The note content after the update
+ */
+
+module.exports = UserNoteUpdateAction;
diff --git a/node_modules/discord.js/src/client/actions/UserUpdate.js b/node_modules/discord.js/src/client/actions/UserUpdate.js
new file mode 100644
index 0000000..b361eca
--- /dev/null
+++ b/node_modules/discord.js/src/client/actions/UserUpdate.js
@@ -0,0 +1,33 @@
+const Action = require('./Action');
+const Constants = require('../../util/Constants');
+const cloneObject = require('../../util/CloneObject');
+
+class UserUpdateAction extends Action {
+ handle(data) {
+ const client = this.client;
+
+ if (client.user) {
+ if (client.user.equals(data)) {
+ return {
+ old: client.user,
+ updated: client.user,
+ };
+ }
+
+ const oldUser = cloneObject(client.user);
+ client.user.patch(data);
+ client.emit(Constants.Events.USER_UPDATE, oldUser, client.user);
+ return {
+ old: oldUser,
+ updated: client.user,
+ };
+ }
+
+ return {
+ old: null,
+ updated: null,
+ };
+ }
+}
+
+module.exports = UserUpdateAction;
diff --git a/node_modules/discord.js/src/client/rest/APIRequest.js b/node_modules/discord.js/src/client/rest/APIRequest.js
new file mode 100644
index 0000000..36c2d8f
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/APIRequest.js
@@ -0,0 +1,49 @@
+const request = require('superagent');
+const Constants = require('../../util/Constants');
+
+function getRoute(url) {
+ let route = url.split('?')[0];
+ if (route.includes('/channels/') || route.includes('/guilds/')) {
+ const startInd = route.includes('/channels/') ? route.indexOf('/channels/') : route.indexOf('/guilds/');
+ const majorID = route.substring(startInd).split('/')[2];
+ route = route.replace(/(\d{8,})/g, ':id').replace(':id', majorID);
+ }
+ return route;
+}
+
+class APIRequest {
+ constructor(rest, method, url, auth, data, file) {
+ this.rest = rest;
+ this.method = method;
+ this.url = url;
+ this.auth = auth;
+ this.data = data;
+ this.file = file;
+ this.route = getRoute(this.url);
+ }
+
+ getAuth() {
+ if (this.rest.client.token && this.rest.client.user && this.rest.client.user.bot) {
+ return `Bot ${this.rest.client.token}`;
+ } else if (this.rest.client.token) {
+ return this.rest.client.token;
+ }
+ throw new Error(Constants.Errors.NO_TOKEN);
+ }
+
+ gen() {
+ const apiRequest = request[this.method](this.url);
+ if (this.auth) apiRequest.set('authorization', this.getAuth());
+ if (this.file && this.file.file) {
+ apiRequest.attach('file', this.file.file, this.file.name);
+ this.data = this.data || {};
+ apiRequest.field('payload_json', JSON.stringify(this.data));
+ } else if (this.data) {
+ apiRequest.send(this.data);
+ }
+ if (!this.rest.client.browser) apiRequest.set('User-Agent', this.rest.userAgentManager.userAgent);
+ return apiRequest;
+ }
+}
+
+module.exports = APIRequest;
diff --git a/node_modules/discord.js/src/client/rest/RESTManager.js b/node_modules/discord.js/src/client/rest/RESTManager.js
new file mode 100644
index 0000000..ac1ce6e
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RESTManager.js
@@ -0,0 +1,51 @@
+const UserAgentManager = require('./UserAgentManager');
+const RESTMethods = require('./RESTMethods');
+const SequentialRequestHandler = require('./RequestHandlers/Sequential');
+const BurstRequestHandler = require('./RequestHandlers/Burst');
+const APIRequest = require('./APIRequest');
+const Constants = require('../../util/Constants');
+
+class RESTManager {
+ constructor(client) {
+ this.client = client;
+ this.handlers = {};
+ this.userAgentManager = new UserAgentManager(this);
+ this.methods = new RESTMethods(this);
+ this.rateLimitedEndpoints = {};
+ this.globallyRateLimited = false;
+ }
+
+ push(handler, apiRequest) {
+ return new Promise((resolve, reject) => {
+ handler.push({
+ request: apiRequest,
+ resolve,
+ reject,
+ });
+ });
+ }
+
+ getRequestHandler() {
+ switch (this.client.options.apiRequestMethod) {
+ case 'sequential':
+ return SequentialRequestHandler;
+ case 'burst':
+ return BurstRequestHandler;
+ default:
+ throw new Error(Constants.Errors.INVALID_RATE_LIMIT_METHOD);
+ }
+ }
+
+ makeRequest(method, url, auth, data, file) {
+ const apiRequest = new APIRequest(this, method, url, auth, data, file);
+
+ if (!this.handlers[apiRequest.route]) {
+ const RequestHandlerType = this.getRequestHandler();
+ this.handlers[apiRequest.route] = new RequestHandlerType(this, apiRequest.route);
+ }
+
+ return this.push(this.handlers[apiRequest.route], apiRequest);
+ }
+}
+
+module.exports = RESTManager;
diff --git a/node_modules/discord.js/src/client/rest/RESTMethods.js b/node_modules/discord.js/src/client/rest/RESTMethods.js
new file mode 100644
index 0000000..7006248
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RESTMethods.js
@@ -0,0 +1,653 @@
+const Constants = require('../../util/Constants');
+const Collection = require('../../util/Collection');
+const splitMessage = require('../../util/SplitMessage');
+const parseEmoji = require('../../util/ParseEmoji');
+const escapeMarkdown = require('../../util/EscapeMarkdown');
+
+const User = require('../../structures/User');
+const GuildMember = require('../../structures/GuildMember');
+const Message = require('../../structures/Message');
+const Role = require('../../structures/Role');
+const Invite = require('../../structures/Invite');
+const Webhook = require('../../structures/Webhook');
+const UserProfile = require('../../structures/UserProfile');
+const ClientOAuth2Application = require('../../structures/ClientOAuth2Application');
+
+class RESTMethods {
+ constructor(restManager) {
+ this.rest = restManager;
+ this.client = restManager.client;
+ }
+
+ login(token = this.client.token) {
+ return new Promise((resolve, reject) => {
+ if (typeof token !== 'string') throw new Error(Constants.Errors.INVALID_TOKEN);
+ token = token.replace(/^Bot\s*/i, '');
+ this.client.manager.connectToWebSocket(token, resolve, reject);
+ });
+ }
+
+ logout() {
+ return this.rest.makeRequest('post', Constants.Endpoints.logout, true, {});
+ }
+
+ getGateway() {
+ return this.rest.makeRequest('get', Constants.Endpoints.gateway, true).then(res => {
+ this.client.ws.gateway = `${res.url}/?v=${Constants.PROTOCOL_VERSION}`;
+ return this.client.ws.gateway;
+ });
+ }
+
+ getBotGateway() {
+ return this.rest.makeRequest('get', Constants.Endpoints.botGateway, true);
+ }
+
+ sendMessage(channel, content, { tts, nonce, embed, disableEveryone, split, code } = {}, file = null) {
+ return new Promise((resolve, reject) => {
+ if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content);
+
+ if (content) {
+ if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
+ content = escapeMarkdown(this.client.resolver.resolveString(content), true);
+ content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
+ }
+
+ if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) {
+ content = content.replace(/@(everyone|here)/g, '@\u200b$1');
+ }
+
+ if (split) content = splitMessage(content, typeof split === 'object' ? split : {});
+ }
+
+ const send = chan => {
+ if (content instanceof Array) {
+ const messages = [];
+ (function sendChunk(list, index) {
+ const options = index === list.length ? { tts, embed } : { tts };
+ chan.send(list[index], options, index === list.length ? file : null).then((message) => {
+ messages.push(message);
+ if (index >= list.length) return resolve(messages);
+ return sendChunk(list, ++index);
+ });
+ }(content, 0));
+ } else {
+ this.rest.makeRequest('post', Constants.Endpoints.channelMessages(chan.id), true, {
+ content, tts, nonce, embed,
+ }, file).then(data => resolve(this.client.actions.MessageCreate.handle(data).message), reject);
+ }
+ };
+
+ if (channel instanceof User || channel instanceof GuildMember) {
+ this.createDM(channel).then(send, reject);
+ } else {
+ send(channel);
+ }
+ });
+ }
+
+ updateMessage(message, content, { embed, code } = {}) {
+ content = this.client.resolver.resolveString(content);
+ if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
+ content = escapeMarkdown(this.client.resolver.resolveString(content), true);
+ content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
+ }
+ return this.rest.makeRequest('patch', Constants.Endpoints.channelMessage(message.channel.id, message.id), true, {
+ content, embed,
+ }).then(data => this.client.actions.MessageUpdate.handle(data).updated);
+ }
+
+ deleteMessage(message) {
+ return this.rest.makeRequest('del', Constants.Endpoints.channelMessage(message.channel.id, message.id), true)
+ .then(() =>
+ this.client.actions.MessageDelete.handle({
+ id: message.id,
+ channel_id: message.channel.id,
+ }).message
+ );
+ }
+
+ bulkDeleteMessages(channel, messages) {
+ return this.rest.makeRequest('post', `${Constants.Endpoints.channelMessages(channel.id)}/bulk_delete`, true, {
+ messages,
+ }).then(() =>
+ this.client.actions.MessageDeleteBulk.handle({
+ channel_id: channel.id,
+ ids: messages,
+ }).messages
+ );
+ }
+
+ createChannel(guild, channelName, channelType, overwrites) {
+ if (overwrites instanceof Collection) overwrites = overwrites.array();
+ return this.rest.makeRequest('post', Constants.Endpoints.guildChannels(guild.id), true, {
+ name: channelName,
+ type: channelType,
+ permission_overwrites: overwrites,
+ }).then(data => this.client.actions.ChannelCreate.handle(data).channel);
+ }
+
+ createDM(recipient) {
+ const dmChannel = this.getExistingDM(recipient);
+ if (dmChannel) return Promise.resolve(dmChannel);
+ return this.rest.makeRequest('post', Constants.Endpoints.userChannels(this.client.user.id), true, {
+ recipient_id: recipient.id,
+ }).then(data => this.client.actions.ChannelCreate.handle(data).channel);
+ }
+
+ getExistingDM(recipient) {
+ return this.client.channels.find(channel =>
+ channel.recipient && channel.recipient.id === recipient.id
+ );
+ }
+
+ deleteChannel(channel) {
+ if (channel instanceof User || channel instanceof GuildMember) channel = this.getExistingDM(channel);
+ if (!channel) return Promise.reject(new Error('No channel to delete.'));
+ return this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true).then(data => {
+ data.id = channel.id;
+ return this.client.actions.ChannelDelete.handle(data).channel;
+ });
+ }
+
+ updateChannel(channel, _data) {
+ const data = {};
+ data.name = (_data.name || channel.name).trim();
+ data.topic = _data.topic || channel.topic;
+ data.position = _data.position || channel.position;
+ data.bitrate = _data.bitrate || channel.bitrate;
+ data.user_limit = _data.userLimit || channel.userLimit;
+ return this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data).then(newData =>
+ this.client.actions.ChannelUpdate.handle(newData).updated
+ );
+ }
+
+ leaveGuild(guild) {
+ if (guild.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.'));
+ return this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true).then(() =>
+ this.client.actions.GuildDelete.handle({ id: guild.id }).guild
+ );
+ }
+
+ createGuild(options) {
+ options.icon = this.client.resolver.resolveBase64(options.icon) || null;
+ options.region = options.region || 'us-central';
+ return new Promise((resolve, reject) => {
+ this.rest.makeRequest('post', Constants.Endpoints.guilds, true, options).then(data => {
+ if (this.client.guilds.has(data.id)) {
+ resolve(this.client.guilds.get(data.id));
+ return;
+ }
+
+ const handleGuild = guild => {
+ if (guild.id === data.id) {
+ this.client.removeListener('guildCreate', handleGuild);
+ this.client.clearTimeout(timeout);
+ resolve(guild);
+ }
+ };
+ this.client.on('guildCreate', handleGuild);
+
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener('guildCreate', handleGuild);
+ reject(new Error('Took too long to receive guild data.'));
+ }, 10000);
+ }, reject);
+ });
+ }
+
+ // untested but probably will work
+ deleteGuild(guild) {
+ return this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true).then(() =>
+ this.client.actions.GuildDelete.handle({ id: guild.id }).guild
+ );
+ }
+
+ getUser(userID) {
+ return this.rest.makeRequest('get', Constants.Endpoints.user(userID), true).then(data =>
+ this.client.actions.UserGet.handle(data).user
+ );
+ }
+
+ updateCurrentUser(_data, password) {
+ const user = this.client.user;
+ const data = {};
+ data.username = _data.username || user.username;
+ data.avatar = this.client.resolver.resolveBase64(_data.avatar) || user.avatar;
+ if (!user.bot) {
+ data.email = _data.email || user.email;
+ data.password = password;
+ if (_data.new_password) data.new_password = _data.newPassword;
+ }
+ return this.rest.makeRequest('patch', Constants.Endpoints.me, true, data).then(newData =>
+ this.client.actions.UserUpdate.handle(newData).updated
+ );
+ }
+
+ updateGuild(guild, _data) {
+ const data = {};
+ if (_data.name) data.name = _data.name;
+ if (_data.region) data.region = _data.region;
+ if (_data.verificationLevel) data.verification_level = Number(_data.verificationLevel);
+ if (_data.afkChannel) data.afk_channel_id = this.client.resolver.resolveChannel(_data.afkChannel).id;
+ if (_data.afkTimeout) data.afk_timeout = Number(_data.afkTimeout);
+ if (_data.icon) data.icon = this.client.resolver.resolveBase64(_data.icon);
+ if (_data.owner) data.owner_id = this.client.resolver.resolveUser(_data.owner).id;
+ if (_data.splash) data.splash = this.client.resolver.resolveBase64(_data.splash);
+ return this.rest.makeRequest('patch', Constants.Endpoints.guild(guild.id), true, data).then(newData =>
+ this.client.actions.GuildUpdate.handle(newData).updated
+ );
+ }
+
+ kickGuildMember(guild, member) {
+ return this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true).then(() =>
+ this.client.actions.GuildMemberRemove.handle({
+ guild_id: guild.id,
+ user: member.user,
+ }).member
+ );
+ }
+
+ createGuildRole(guild) {
+ return this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true).then(role =>
+ this.client.actions.GuildRoleCreate.handle({
+ guild_id: guild.id,
+ role,
+ }).role
+ );
+ }
+
+ deleteGuildRole(role) {
+ return this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true).then(() =>
+ this.client.actions.GuildRoleDelete.handle({
+ guild_id: role.guild.id,
+ role_id: role.id,
+ }).role
+ );
+ }
+
+ setChannelOverwrite(channel, payload) {
+ return this.rest.makeRequest(
+ 'put', `${Constants.Endpoints.channelPermissions(channel.id)}/${payload.id}`, true, payload
+ );
+ }
+
+ deletePermissionOverwrites(overwrite) {
+ return this.rest.makeRequest(
+ 'del', `${Constants.Endpoints.channelPermissions(overwrite.channel.id)}/${overwrite.id}`, true
+ ).then(() => overwrite);
+ }
+
+ getChannelMessages(channel, payload = {}) {
+ const params = [];
+ if (payload.limit) params.push(`limit=${payload.limit}`);
+ if (payload.around) params.push(`around=${payload.around}`);
+ else if (payload.before) params.push(`before=${payload.before}`);
+ else if (payload.after) params.push(`after=${payload.after}`);
+
+ let endpoint = Constants.Endpoints.channelMessages(channel.id);
+ if (params.length > 0) endpoint += `?${params.join('&')}`;
+ return this.rest.makeRequest('get', endpoint, true);
+ }
+
+ getChannelMessage(channel, messageID) {
+ const msg = channel.messages.get(messageID);
+ if (msg) return Promise.resolve(msg);
+ return this.rest.makeRequest('get', Constants.Endpoints.channelMessage(channel.id, messageID), true);
+ }
+
+ getGuildMember(guild, user) {
+ return this.rest.makeRequest('get', Constants.Endpoints.guildMember(guild.id, user.id), true).then(data =>
+ this.client.actions.GuildMemberGet.handle(guild, data).member
+ );
+ }
+
+ updateGuildMember(member, data) {
+ if (data.channel) data.channel_id = this.client.resolver.resolveChannel(data.channel).id;
+ if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role);
+
+ let endpoint = Constants.Endpoints.guildMember(member.guild.id, member.id);
+ // fix your endpoints, discord ;-;
+ if (member.id === this.client.user.id) {
+ const keys = Object.keys(data);
+ if (keys.length === 1 && keys[0] === 'nick') {
+ endpoint = Constants.Endpoints.guildMemberNickname(member.guild.id);
+ }
+ }
+
+ return this.rest.makeRequest('patch', endpoint, true, data).then(newData =>
+ member.guild._updateMember(member, newData).mem
+ );
+ }
+
+ addMemberRole(member, role) {
+ return this.rest.makeRequest('put', Constants.Endpoints.guildMemberRole(member.guild.id, member.id, role.id), true)
+ .then(() => {
+ if (!member._roles.includes(role.id)) member._roles.push(role.id);
+ return member;
+ });
+ }
+
+ removeMemberRole(member, role) {
+ return this.rest.makeRequest(
+ 'delete',
+ Constants.Endpoints.guildMemberRole(member.guild.id, member.id, role.id),
+ true
+ ).then(() => {
+ const index = member._roles.indexOf(role.id);
+ if (index >= 0) member._roles.splice(index, 1);
+ return member;
+ });
+ }
+
+ sendTyping(channelID) {
+ return this.rest.makeRequest('post', `${Constants.Endpoints.channel(channelID)}/typing`, true);
+ }
+
+ banGuildMember(guild, member, deleteDays = 0) {
+ const id = this.client.resolver.resolveUserID(member);
+ if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.'));
+ return this.rest.makeRequest(
+ 'put', `${Constants.Endpoints.guildBans(guild.id)}/${id}?delete-message-days=${deleteDays}`, true, {
+ 'delete-message-days': deleteDays,
+ }
+ ).then(() => {
+ if (member instanceof GuildMember) return member;
+ const user = this.client.resolver.resolveUser(id);
+ if (user) {
+ member = this.client.resolver.resolveGuildMember(guild, user);
+ return member || user;
+ }
+ return id;
+ });
+ }
+
+ unbanGuildMember(guild, member) {
+ return new Promise((resolve, reject) => {
+ const id = this.client.resolver.resolveUserID(member);
+ if (!id) throw new Error('Couldn\'t resolve the user ID to unban.');
+
+ const listener = (eGuild, eUser) => {
+ if (eGuild.id === guild.id && eUser.id === id) {
+ this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
+ this.client.clearTimeout(timeout);
+ resolve(eUser);
+ }
+ };
+ this.client.on(Constants.Events.GUILD_BAN_REMOVE, listener);
+
+ const timeout = this.client.setTimeout(() => {
+ this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
+ reject(new Error('Took too long to receive the ban remove event.'));
+ }, 10000);
+
+ this.rest.makeRequest('del', `${Constants.Endpoints.guildBans(guild.id)}/${id}`, true).catch(err => {
+ this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
+ this.client.clearTimeout(timeout);
+ reject(err);
+ });
+ });
+ }
+
+ getGuildBans(guild) {
+ return this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true).then(banItems => {
+ const bannedUsers = new Collection();
+ for (const banItem of banItems) {
+ const user = this.client.dataManager.newUser(banItem.user);
+ bannedUsers.set(user.id, user);
+ }
+ return bannedUsers;
+ });
+ }
+
+ updateGuildRole(role, _data) {
+ const data = {};
+ data.name = _data.name || role.name;
+ data.position = typeof _data.position !== 'undefined' ? _data.position : role.position;
+ data.color = _data.color || role.color;
+ if (typeof data.color === 'string' && data.color.startsWith('#')) {
+ data.color = parseInt(data.color.replace('#', ''), 16);
+ }
+ data.hoist = typeof _data.hoist !== 'undefined' ? _data.hoist : role.hoist;
+ data.mentionable = typeof _data.mentionable !== 'undefined' ? _data.mentionable : role.mentionable;
+
+ if (_data.permissions) {
+ let perms = 0;
+ for (let perm of _data.permissions) {
+ if (typeof perm === 'string') perm = Constants.PermissionFlags[perm];
+ perms |= perm;
+ }
+ data.permissions = perms;
+ } else {
+ data.permissions = role.permissions;
+ }
+
+ return this.rest.makeRequest(
+ 'patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data
+ ).then(_role =>
+ this.client.actions.GuildRoleUpdate.handle({
+ role: _role,
+ guild_id: role.guild.id,
+ }).updated
+ );
+ }
+
+ pinMessage(message) {
+ return this.rest.makeRequest('put', `${Constants.Endpoints.channel(message.channel.id)}/pins/${message.id}`, true)
+ .then(() => message);
+ }
+
+ unpinMessage(message) {
+ return this.rest.makeRequest('del', `${Constants.Endpoints.channel(message.channel.id)}/pins/${message.id}`, true)
+ .then(() => message);
+ }
+
+ getChannelPinnedMessages(channel) {
+ return this.rest.makeRequest('get', `${Constants.Endpoints.channel(channel.id)}/pins`, true);
+ }
+
+ createChannelInvite(channel, options) {
+ const payload = {};
+ payload.temporary = options.temporary;
+ payload.max_age = options.maxAge;
+ payload.max_uses = options.maxUses;
+ return this.rest.makeRequest('post', `${Constants.Endpoints.channelInvites(channel.id)}`, true, payload)
+ .then(invite => new Invite(this.client, invite));
+ }
+
+ deleteInvite(invite) {
+ return this.rest.makeRequest('del', Constants.Endpoints.invite(invite.code), true).then(() => invite);
+ }
+
+ getInvite(code) {
+ return this.rest.makeRequest('get', Constants.Endpoints.invite(code), true).then(invite =>
+ new Invite(this.client, invite)
+ );
+ }
+
+ getGuildInvites(guild) {
+ return this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true).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;
+ });
+ }
+
+ pruneGuildMembers(guild, days, dry) {
+ return this.rest.makeRequest(dry ? 'get' : 'post', `${Constants.Endpoints.guildPrune(guild.id)}?days=${days}`, true)
+ .then(data => data.pruned);
+ }
+
+ createEmoji(guild, image, name) {
+ return this.rest.makeRequest('post', `${Constants.Endpoints.guildEmojis(guild.id)}`, true, { name, image })
+ .then(data => this.client.actions.EmojiCreate.handle(data, guild).emoji);
+ }
+
+ deleteEmoji(emoji) {
+ return this.rest.makeRequest('delete', `${Constants.Endpoints.guildEmojis(emoji.guild.id)}/${emoji.id}`, true)
+ .then(() => this.client.actions.EmojiDelete.handle(emoji).data);
+ }
+
+ getWebhook(id, token) {
+ return this.rest.makeRequest('get', Constants.Endpoints.webhook(id, token), !token).then(data =>
+ new Webhook(this.client, data)
+ );
+ }
+
+ getGuildWebhooks(guild) {
+ return this.rest.makeRequest('get', Constants.Endpoints.guildWebhooks(guild.id), true).then(data => {
+ const hooks = new Collection();
+ for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
+ return hooks;
+ });
+ }
+
+ getChannelWebhooks(channel) {
+ return this.rest.makeRequest('get', Constants.Endpoints.channelWebhooks(channel.id), true).then(data => {
+ const hooks = new Collection();
+ for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
+ return hooks;
+ });
+ }
+
+ createWebhook(channel, name, avatar) {
+ return this.rest.makeRequest('post', Constants.Endpoints.channelWebhooks(channel.id), true, { name, avatar })
+ .then(data => new Webhook(this.client, data));
+ }
+
+ editWebhook(webhook, name, avatar) {
+ return this.rest.makeRequest('patch', Constants.Endpoints.webhook(webhook.id, webhook.token), false, {
+ name,
+ avatar,
+ }).then(data => {
+ webhook.name = data.name;
+ webhook.avatar = data.avatar;
+ return webhook;
+ });
+ }
+
+ deleteWebhook(webhook) {
+ return this.rest.makeRequest('delete', Constants.Endpoints.webhook(webhook.id, webhook.token), false);
+ }
+
+ sendWebhookMessage(webhook, content, { avatarURL, tts, disableEveryone, embeds } = {}, file = null) {
+ if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content);
+ if (content) {
+ if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) {
+ content = content.replace(/@(everyone|here)/g, '@\u200b$1');
+ }
+ }
+ return this.rest.makeRequest('post', `${Constants.Endpoints.webhook(webhook.id, webhook.token)}?wait=true`, false, {
+ username: webhook.name,
+ avatar_url: avatarURL,
+ content,
+ tts,
+ file,
+ embeds,
+ });
+ }
+
+ sendSlackWebhookMessage(webhook, body) {
+ return this.rest.makeRequest(
+ 'post', `${Constants.Endpoints.webhook(webhook.id, webhook.token)}/slack?wait=true`, false, body
+ );
+ }
+
+ fetchUserProfile(user) {
+ return this.rest.makeRequest('get', Constants.Endpoints.userProfile(user.id), true).then(data =>
+ new UserProfile(user, data)
+ );
+ }
+
+ fetchMeMentions(options) {
+ if (options.guild) options.guild = options.guild.id ? options.guild.id : options.guild;
+ return this.rest.makeRequest(
+ 'get',
+ Constants.Endpoints.meMentions(options.limit, options.roles, options.everyone, options.guild)
+ ).then(res => res.body.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client)));
+ }
+
+ addFriend(user) {
+ return this.rest.makeRequest('post', Constants.Endpoints.relationships('@me'), true, {
+ username: user.username,
+ discriminator: user.discriminator,
+ }).then(() => user);
+ }
+
+ removeFriend(user) {
+ return this.rest.makeRequest('delete', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true)
+ .then(() => user);
+ }
+
+ blockUser(user) {
+ return this.rest.makeRequest('put', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true, { type: 2 })
+ .then(() => user);
+ }
+
+ unblockUser(user) {
+ return this.rest.makeRequest('delete', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true)
+ .then(() => user);
+ }
+
+ setRolePositions(guildID, roles) {
+ return this.rest.makeRequest('patch', Constants.Endpoints.guildRoles(guildID), true, roles).then(() =>
+ this.client.actions.GuildRolesPositionUpdate.handle({
+ guild_id: guildID,
+ roles,
+ }).guild
+ );
+ }
+
+ addMessageReaction(message, emoji) {
+ return this.rest.makeRequest(
+ 'put', Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji), true
+ ).then(() =>
+ this.client.actions.MessageReactionAdd.handle({
+ user_id: this.client.user.id,
+ message_id: message.id,
+ emoji: parseEmoji(emoji),
+ channel_id: message.channel.id,
+ }).reaction
+ );
+ }
+
+ removeMessageReaction(message, emoji, user) {
+ let endpoint = Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji);
+ if (user.id !== this.client.user.id) {
+ endpoint = Constants.Endpoints.userMessageReaction(message.channel.id, message.id, emoji, null, user.id);
+ }
+ return this.rest.makeRequest('delete', endpoint, true).then(() =>
+ this.client.actions.MessageReactionRemove.handle({
+ user_id: user.id,
+ message_id: message.id,
+ emoji: parseEmoji(emoji),
+ channel_id: message.channel.id,
+ }).reaction
+ );
+ }
+
+ removeMessageReactions(message) {
+ return this.rest.makeRequest('delete', Constants.Endpoints.messageReactions(message.channel.id, message.id), true)
+ .then(() => message);
+ }
+
+ getMessageReactionUsers(message, emoji, limit = 100) {
+ return this.rest.makeRequest(
+ 'get', Constants.Endpoints.messageReaction(message.channel.id, message.id, emoji, limit), true
+ );
+ }
+
+ getMyApplication() {
+ return this.rest.makeRequest('get', Constants.Endpoints.myApplication, true).then(app =>
+ new ClientOAuth2Application(this.client, app)
+ );
+ }
+
+ setNote(user, note) {
+ return this.rest.makeRequest('put', Constants.Endpoints.note(user.id), true, { note }).then(() => user);
+ }
+}
+
+module.exports = RESTMethods;
diff --git a/node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js b/node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js
new file mode 100644
index 0000000..2cc1a59
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js
@@ -0,0 +1,70 @@
+const RequestHandler = require('./RequestHandler');
+
+class BurstRequestHandler extends RequestHandler {
+ constructor(restManager, endpoint) {
+ super(restManager, endpoint);
+ this.requestRemaining = 1;
+ this.first = true;
+ }
+
+ push(request) {
+ super.push(request);
+ this.handle();
+ }
+
+ handleNext(time) {
+ if (this.waiting) return;
+ this.waiting = true;
+ this.restManager.client.setTimeout(() => {
+ this.requestRemaining = this.requestLimit;
+ this.waiting = false;
+ this.handle();
+ }, time);
+ }
+
+ execute(item) {
+ item.request.gen().end((err, res) => {
+ if (res && res.headers) {
+ this.requestLimit = res.headers['x-ratelimit-limit'];
+ this.requestResetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
+ this.requestRemaining = Number(res.headers['x-ratelimit-remaining']);
+ this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
+ this.handleNext(
+ this.requestResetTime - Date.now() + this.timeDifference + this.restManager.client.options.restTimeOffset
+ );
+ }
+ if (err) {
+ if (err.status === 429) {
+ this.requestRemaining = 0;
+ this.queue.unshift(item);
+ this.restManager.client.setTimeout(() => {
+ this.globalLimit = false;
+ this.handle();
+ }, Number(res.headers['retry-after']) + this.restManager.client.options.restTimeOffset);
+ if (res.headers['x-ratelimit-global']) this.globalLimit = true;
+ } else {
+ item.reject(err);
+ }
+ } else {
+ this.globalLimit = false;
+ const data = res && res.body ? res.body : {};
+ item.resolve(data);
+ if (this.first) {
+ this.first = false;
+ this.handle();
+ }
+ }
+ });
+ }
+
+ handle() {
+ super.handle();
+ if (this.requestRemaining < 1 || this.queue.length === 0 || this.globalLimit) return;
+ while (this.queue.length > 0 && this.requestRemaining > 0) {
+ this.execute(this.queue.shift());
+ this.requestRemaining--;
+ }
+ }
+}
+
+module.exports = BurstRequestHandler;
diff --git a/node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js b/node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js
new file mode 100644
index 0000000..a1a2f34
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js
@@ -0,0 +1,51 @@
+/**
+ * A base class for different types of rate limiting handlers for the REST API.
+ * @private
+ */
+class RequestHandler {
+ /**
+ * @param {RESTManager} restManager The REST manager to use
+ */
+ constructor(restManager) {
+ /**
+ * The RESTManager that instantiated this RequestHandler
+ * @type {RESTManager}
+ */
+ this.restManager = restManager;
+
+ /**
+ * A list of requests that have yet to be processed.
+ * @type {APIRequest[]}
+ */
+ this.queue = [];
+ }
+
+ /**
+ * Whether or not the client is being rate limited on every endpoint.
+ * @type {boolean}
+ */
+ get globalLimit() {
+ return this.restManager.globallyRateLimited;
+ }
+
+ set globalLimit(value) {
+ this.restManager.globallyRateLimited = value;
+ }
+
+ /**
+ * Push a new API request into this bucket
+ * @param {APIRequest} request The new request to push into the queue
+ */
+ push(request) {
+ this.queue.push(request);
+ }
+
+ /**
+ * Attempts to get this RequestHandler to process its current queue
+ */
+ handle() {
+ return;
+ }
+}
+
+module.exports = RequestHandler;
diff --git a/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js b/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js
new file mode 100644
index 0000000..0abf36d
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js
@@ -0,0 +1,104 @@
+const RequestHandler = require('./RequestHandler');
+
+/**
+ * Handles API Requests sequentially, i.e. we wait until the current request is finished before moving onto
+ * the next. This plays a _lot_ nicer in terms of avoiding 429's when there is more than one session of the account,
+ * but it can be slower.
+ * @extends {RequestHandler}
+ * @private
+ */
+class SequentialRequestHandler extends RequestHandler {
+ /**
+ * @param {RESTManager} restManager The REST manager to use
+ * @param {string} endpoint The endpoint to handle
+ */
+ constructor(restManager, endpoint) {
+ super(restManager, endpoint);
+
+ /**
+ * Whether this rate limiter is waiting for a response from a request
+ * @type {boolean}
+ */
+ this.waiting = false;
+
+ /**
+ * The endpoint that this handler is handling
+ * @type {string}
+ */
+ this.endpoint = endpoint;
+
+ /**
+ * The time difference between Discord's Dates and the local computer's Dates. A positive number means the local
+ * computer's time is ahead of Discord's.
+ * @type {number}
+ */
+ this.timeDifference = 0;
+ }
+
+ push(request) {
+ super.push(request);
+ this.handle();
+ }
+
+ /**
+ * Performs a request then resolves a promise to indicate its readiness for a new request
+ * @param {APIRequest} item The item to execute
+ * @returns {Promise<?Object|Error>}
+ */
+ execute(item) {
+ return new Promise(resolve => {
+ item.request.gen().end((err, res) => {
+ if (res && res.headers) {
+ this.requestLimit = res.headers['x-ratelimit-limit'];
+ this.requestResetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
+ this.requestRemaining = Number(res.headers['x-ratelimit-remaining']);
+ this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
+ }
+ if (err) {
+ if (err.status === 429) {
+ this.restManager.client.setTimeout(() => {
+ this.waiting = false;
+ this.globalLimit = false;
+ resolve();
+ }, Number(res.headers['retry-after']) + this.restManager.client.options.restTimeOffset);
+ if (res.headers['x-ratelimit-global']) this.globalLimit = true;
+ } else {
+ this.queue.shift();
+ this.waiting = false;
+ item.reject(err);
+ resolve(err);
+ }
+ } else {
+ this.queue.shift();
+ this.globalLimit = false;
+ const data = res && res.body ? res.body : {};
+ item.resolve(data);
+ if (this.requestRemaining === 0) {
+ this.restManager.client.setTimeout(
+ () => {
+ this.waiting = false;
+ resolve(data);
+ },
+ this.requestResetTime - Date.now() + this.timeDifference + this.restManager.client.options.restTimeOffset
+ );
+ } else {
+ this.waiting = false;
+ resolve(data);
+ }
+ }
+ });
+ });
+ }
+
+ handle() {
+ super.handle();
+
+ if (this.waiting || this.queue.length === 0 || this.globalLimit) return;
+ this.waiting = true;
+
+ const item = this.queue[0];
+ this.execute(item).then(() => this.handle());
+ }
+}
+
+module.exports = SequentialRequestHandler;
diff --git a/node_modules/discord.js/src/client/rest/UserAgentManager.js b/node_modules/discord.js/src/client/rest/UserAgentManager.js
new file mode 100644
index 0000000..12393ff
--- /dev/null
+++ b/node_modules/discord.js/src/client/rest/UserAgentManager.js
@@ -0,0 +1,22 @@
+const Constants = require('../../util/Constants');
+
+class UserAgentManager {
+ constructor(restManager) {
+ this.restManager = restManager;
+ this._userAgent = {
+ url: 'https://github.com/hydrabolt/discord.js',
+ version: Constants.Package.version,
+ };
+ }
+
+ set(info) {
+ this._userAgent.url = info.url || 'https://github.com/hydrabolt/discord.js';
+ this._userAgent.version = info.version || Constants.Package.version;
+ }
+
+ get userAgent() {
+ return `DiscordBot (${this._userAgent.url}, ${this._userAgent.version})`;
+ }
+}
+
+module.exports = UserAgentManager;
diff --git a/node_modules/discord.js/src/client/voice/ClientVoiceManager.js b/node_modules/discord.js/src/client/voice/ClientVoiceManager.js
new file mode 100644
index 0000000..ea83745
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/ClientVoiceManager.js
@@ -0,0 +1,245 @@
+const Collection = require('../../util/Collection');
+const mergeDefault = require('../../util/MergeDefault');
+const Constants = require('../../util/Constants');
+const VoiceConnection = require('./VoiceConnection');
+const EventEmitter = require('events').EventEmitter;
+
+/**
+ * Manages all the voice stuff for the Client
+ * @private
+ */
+class ClientVoiceManager {
+ constructor(client) {
+ /**
+ * The client that instantiated this voice manager
+ * @type {Client}
+ */
+ this.client = client;
+
+ /**
+ * A collection mapping connection IDs to the Connection objects
+ * @type {Collection<string, VoiceConnection>}
+ */
+ this.connections = new Collection();
+
+ /**
+ * Pending connection attempts, maps guild ID to VoiceChannel
+ * @type {Collection<string, VoiceChannel>}
+ */
+ this.pending = new Collection();
+
+ this.client.on('self.voiceServer', this.onVoiceServer.bind(this));
+ this.client.on('self.voiceStateUpdate', this.onVoiceStateUpdate.bind(this));
+ }
+
+ onVoiceServer(data) {
+ if (this.pending.has(data.guild_id)) this.pending.get(data.guild_id).setTokenAndEndpoint(data.token, data.endpoint);
+ }
+
+ onVoiceStateUpdate(data) {
+ if (this.pending.has(data.guild_id)) this.pending.get(data.guild_id).setSessionID(data.session_id);
+ }
+
+ /**
+ * Sends a request to the main gateway to join a voice channel
+ * @param {VoiceChannel} channel The channel to join
+ * @param {Object} [options] The options to provide
+ */
+ sendVoiceStateUpdate(channel, options = {}) {
+ if (!this.client.user) throw new Error('Unable to join because there is no client user.');
+ if (!channel.permissionsFor) {
+ throw new Error('Channel does not support permissionsFor; is it really a voice channel?');
+ }
+ const permissions = channel.permissionsFor(this.client.user);
+ if (!permissions) {
+ throw new Error('There is no permission set for the client user in this channel - are they part of the guild?');
+ }
+ if (!permissions.hasPermission('CONNECT')) {
+ throw new Error('You do not have permission to join this voice channel.');
+ }
+
+ options = mergeDefault({
+ guild_id: channel.guild.id,
+ channel_id: channel.id,
+ self_mute: false,
+ self_deaf: false,
+ }, options);
+
+ this.client.ws.send({
+ op: Constants.OPCodes.VOICE_STATE_UPDATE,
+ d: options,
+ });
+ }
+
+ /**
+ * Sets up a request to join a voice channel
+ * @param {VoiceChannel} channel The voice channel to join
+ * @returns {Promise<VoiceConnection>}
+ */
+ joinChannel(channel) {
+ return new Promise((resolve, reject) => {
+ if (this.pending.get(channel.guild.id)) throw new Error('Already connecting to this guild\'s voice server.');
+ if (!channel.joinable) throw new Error('You do not have permission to join this voice channel.');
+
+ const existingConnection = this.connections.get(channel.guild.id);
+ if (existingConnection) {
+ if (existingConnection.channel.id !== channel.id) {
+ this.sendVoiceStateUpdate(channel);
+ this.connections.get(channel.guild.id).channel = channel;
+ }
+ resolve(existingConnection);
+ return;
+ }
+
+ const pendingConnection = new PendingVoiceConnection(this, channel);
+ this.pending.set(channel.guild.id, pendingConnection);
+
+ pendingConnection.on('fail', reason => {
+ this.pending.delete(channel.guild.id);
+ reject(reason);
+ });
+
+ pendingConnection.on('pass', voiceConnection => {
+ this.pending.delete(channel.guild.id);
+ this.connections.set(channel.guild.id, voiceConnection);
+ voiceConnection.once('ready', () => resolve(voiceConnection));
+ voiceConnection.once('error', reject);
+ voiceConnection.once('disconnect', () => this.connections.delete(channel.guild.id));
+ });
+ });
+ }
+}
+
+/**
+ * Represents a Pending Voice Connection
+ * @private
+ */
+class PendingVoiceConnection extends EventEmitter {
+ constructor(voiceManager, channel) {
+ super();
+
+ /**
+ * The ClientVoiceManager that instantiated this pending connection
+ * @type {ClientVoiceManager}
+ */
+ this.voiceManager = voiceManager;
+
+ /**
+ * The channel that this pending voice connection will attempt to join
+ * @type {VoiceChannel}
+ */
+ this.channel = channel;
+
+ /**
+ * The timeout that will be invoked after 15 seconds signifying a failure to connect
+ * @type {Timeout}
+ */
+ this.deathTimer = this.voiceManager.client.setTimeout(
+ () => this.fail(new Error('Connection not established within 15 seconds.')), 15000);
+
+ /**
+ * An object containing data required to connect to the voice servers with
+ * @type {Object}
+ */
+ this.data = {};
+
+ this.sendVoiceStateUpdate();
+ }
+
+ checkReady() {
+ if (this.data.token && this.data.endpoint && this.data.session_id) {
+ this.pass();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Set the token and endpoint required to connect to the the voice servers
+ * @param {string} token the token
+ * @param {string} endpoint the endpoint
+ * @returns {void}
+ */
+ setTokenAndEndpoint(token, endpoint) {
+ if (!token) {
+ this.fail(new Error('Token not provided from voice server packet.'));
+ return;
+ }
+ if (!endpoint) {
+ this.fail(new Error('Endpoint not provided from voice server packet.'));
+ return;
+ }
+ if (this.data.token) {
+ this.fail(new Error('There is already a registered token for this connection.'));
+ return;
+ }
+ if (this.data.endpoint) {
+ this.fail(new Error('There is already a registered endpoint for this connection.'));
+ return;
+ }
+
+ endpoint = endpoint.match(/([^:]*)/)[0];
+
+ if (!endpoint) {
+ this.fail(new Error('Failed to find an endpoint.'));
+ return;
+ }
+
+ this.data.token = token;
+ this.data.endpoint = endpoint;
+
+ this.checkReady();
+ }
+
+ /**
+ * Sets the Session ID for the connection
+ * @param {string} sessionID the session ID
+ */
+ setSessionID(sessionID) {
+ if (!sessionID) {
+ this.fail(new Error('Session ID not supplied.'));
+ return;
+ }
+ if (this.data.session_id) {
+ this.fail(new Error('There is already a registered session ID for this connection.'));
+ return;
+ }
+ this.data.session_id = sessionID;
+
+ this.checkReady();
+ }
+
+ clean() {
+ clearInterval(this.deathTimer);
+ this.emit('fail', new Error('Clean-up triggered :fourTriggered:'));
+ }
+
+ pass() {
+ clearInterval(this.deathTimer);
+ this.emit('pass', this.upgrade());
+ }
+
+ fail(reason) {
+ this.emit('fail', reason);
+ this.clean();
+ }
+
+ sendVoiceStateUpdate() {
+ try {
+ this.voiceManager.sendVoiceStateUpdate(this.channel);
+ } catch (error) {
+ this.fail(error);
+ }
+ }
+
+ /**
+ * Upgrades this Pending Connection to a full Voice Connection
+ * @returns {VoiceConnection}
+ */
+ upgrade() {
+ return new VoiceConnection(this);
+ }
+}
+
+module.exports = ClientVoiceManager;
diff --git a/node_modules/discord.js/src/client/voice/VoiceConnection.js b/node_modules/discord.js/src/client/voice/VoiceConnection.js
new file mode 100644
index 0000000..ac44ff8
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceConnection.js
@@ -0,0 +1,276 @@
+const VoiceWebSocket = require('./VoiceWebSocket');
+const VoiceUDP = require('./VoiceUDPClient');
+const Constants = require('../../util/Constants');
+const AudioPlayer = require('./player/AudioPlayer');
+const VoiceReceiver = require('./receiver/VoiceReceiver');
+const EventEmitter = require('events').EventEmitter;
+const fs = require('fs');
+
+/**
+ * Represents a connection to a voice channel in Discord.
+ * ```js
+ * // obtained using:
+ * voiceChannel.join().then(connection => {
+ *
+ * });
+ * ```
+ * @extends {EventEmitter}
+ */
+class VoiceConnection extends EventEmitter {
+ constructor(pendingConnection) {
+ super();
+
+ /**
+ * The Voice Manager that instantiated this connection
+ * @type {ClientVoiceManager}
+ */
+ this.voiceManager = pendingConnection.voiceManager;
+
+ /**
+ * The voice channel this connection is currently serving
+ * @type {VoiceChannel}
+ */
+ this.channel = pendingConnection.channel;
+
+ /**
+ * Whether we're currently transmitting audio
+ * @type {boolean}
+ */
+ this.speaking = false;
+
+ /**
+ * An array of Voice Receivers that have been created for this connection
+ * @type {VoiceReceiver[]}
+ */
+ this.receivers = [];
+
+ /**
+ * The authentication data needed to connect to the voice server
+ * @type {Object}
+ * @private
+ */
+ this.authentication = pendingConnection.data;
+
+ /**
+ * The audio player for this voice connection
+ * @type {AudioPlayer}
+ */
+ this.player = new AudioPlayer(this);
+
+ this.player.on('debug', m => {
+ /**
+ * Debug info from the connection
+ * @event VoiceConnection#debug
+ * @param {string} message the debug message
+ */
+ this.emit('debug', `audio player - ${m}`);
+ });
+
+ this.player.on('error', e => {
+ /**
+ * Warning info from the connection
+ * @event VoiceConnection#warn
+ * @param {string|Error} warning the warning
+ */
+ this.emit('warn', e);
+ this.player.cleanup();
+ });
+
+ /**
+ * Map SSRC to speaking values
+ * @type {Map<number, boolean>}
+ * @private
+ */
+ this.ssrcMap = new Map();
+
+ /**
+ * Whether this connection is ready
+ * @type {boolean}
+ * @private
+ */
+ this.ready = false;
+
+ /**
+ * Object that wraps contains the `ws` and `udp` sockets of this voice connection
+ * @type {Object}
+ * @private
+ */
+ this.sockets = {};
+ this.connect();
+ }
+
+ /**
+ * Sets whether the voice connection should display as "speaking" or not
+ * @param {boolean} value whether or not to speak
+ * @private
+ */
+ setSpeaking(value) {
+ if (this.speaking === value) return;
+ this.speaking = value;
+ this.sockets.ws.sendPacket({
+ op: Constants.VoiceOPCodes.SPEAKING,
+ d: {
+ speaking: true,
+ delay: 0,
+ },
+ }).catch(e => {
+ this.emit('debug', e);
+ });
+ }
+
+ /**
+ * Disconnect the voice connection, causing a disconnect and closing event to be emitted.
+ */
+ disconnect() {
+ this.emit('closing');
+ this.voiceManager.client.ws.send({
+ op: Constants.OPCodes.VOICE_STATE_UPDATE,
+ d: {
+ guild_id: this.channel.guild.id,
+ channel_id: null,
+ self_mute: false,
+ self_deaf: false,
+ },
+ });
+ /**
+ * Emitted when the voice connection disconnects
+ * @event VoiceConnection#disconnect
+ */
+ this.emit('disconnect');
+ }
+
+ /**
+ * Connect the voice connection
+ * @private
+ */
+ connect() {
+ if (this.sockets.ws) throw new Error('There is already an existing WebSocket connection.');
+ if (this.sockets.udp) throw new Error('There is already an existing UDP connection.');
+ this.sockets.ws = new VoiceWebSocket(this);
+ this.sockets.udp = new VoiceUDP(this);
+ this.sockets.ws.on('error', e => this.emit('error', e));
+ this.sockets.udp.on('error', e => this.emit('error', e));
+ this.sockets.ws.once('ready', d => {
+ this.authentication.port = d.port;
+ this.authentication.ssrc = d.ssrc;
+ /**
+ * Emitted whenever the connection encounters an error.
+ * @event VoiceConnection#error
+ * @param {Error} error the encountered error
+ */
+ this.sockets.udp.findEndpointAddress()
+ .then(address => {
+ this.sockets.udp.createUDPSocket(address);
+ }, e => this.emit('error', e));
+ });
+ this.sockets.ws.once('sessionDescription', (mode, secret) => {
+ this.authentication.encryptionMode = mode;
+ this.authentication.secretKey = secret;
+ /**
+ * Emitted once the connection is ready, when a promise to join a voice channel resolves,
+ * the connection will already be ready.
+ * @event VoiceConnection#ready
+ */
+ this.emit('ready');
+ this.ready = true;
+ });
+ this.sockets.ws.on('speaking', data => {
+ const guild = this.channel.guild;
+ const user = this.voiceManager.client.users.get(data.user_id);
+ this.ssrcMap.set(+data.ssrc, user);
+ if (!data.speaking) {
+ for (const receiver of this.receivers) {
+ const opusStream = receiver.opusStreams.get(user.id);
+ const pcmStream = receiver.pcmStreams.get(user.id);
+ if (opusStream) {
+ opusStream.push(null);
+ opusStream.open = false;
+ receiver.opusStreams.delete(user.id);
+ }
+ if (pcmStream) {
+ pcmStream.push(null);
+ pcmStream.open = false;
+ receiver.pcmStreams.delete(user.id);
+ }
+ }
+ }
+ /**
+ * Emitted whenever a user starts/stops speaking
+ * @event VoiceConnection#speaking
+ * @param {User} user The user that has started/stopped speaking
+ * @param {boolean} speaking Whether or not the user is speaking
+ */
+ if (this.ready) this.emit('speaking', user, data.speaking);
+ guild._memberSpeakUpdate(data.user_id, data.speaking);
+ });
+ }
+
+ /**
+ * Options that can be passed to stream-playing methods:
+ * @typedef {Object} StreamOptions
+ * @property {number} [seek=0] The time to seek to
+ * @property {number} [volume=1] The volume to play at
+ * @property {number} [passes=1] How many times to send the voice packet to reduce packet loss
+ */
+
+ /**
+ * Play the given file in the voice connection.
+ * @param {string} file The path to the file
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ * @example
+ * // play files natively
+ * voiceChannel.join()
+ * .then(connection => {
+ * const dispatcher = connection.playFile('C:/Users/Discord/Desktop/music.mp3');
+ * })
+ * .catch(console.error);
+ */
+ playFile(file, options) {
+ return this.playStream(fs.createReadStream(file), options);
+ }
+
+ /**
+ * Plays and converts an audio stream in the voice connection.
+ * @param {ReadableStream} stream The audio stream to play
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ * @example
+ * // play streams using ytdl-core
+ * const ytdl = require('ytdl-core');
+ * const streamOptions = { seek: 0, volume: 1 };
+ * voiceChannel.join()
+ * .then(connection => {
+ * const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', {filter : 'audioonly'});
+ * const dispatcher = connection.playStream(stream, streamOptions);
+ * })
+ * .catch(console.error);
+ */
+ playStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
+ const options = { seek, volume, passes };
+ return this.player.playUnknownStream(stream, options);
+ }
+
+ /**
+ * Plays a stream of 16-bit signed stereo PCM at 48KHz.
+ * @param {ReadableStream} stream The audio stream to play.
+ * @param {StreamOptions} [options] Options for playing the stream
+ * @returns {StreamDispatcher}
+ */
+ playConvertedStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
+ const options = { seek, volume, passes };
+ return this.player.playPCMStream(stream, null, options);
+ }
+
+ /**
+ * Creates a VoiceReceiver so you can start listening to voice data. It's recommended to only create one of these.
+ * @returns {VoiceReceiver}
+ */
+ createReceiver() {
+ const receiver = new VoiceReceiver(this);
+ this.receivers.push(receiver);
+ return receiver;
+ }
+}
+
+module.exports = VoiceConnection;
diff --git a/node_modules/discord.js/src/client/voice/VoiceUDPClient.js b/node_modules/discord.js/src/client/voice/VoiceUDPClient.js
new file mode 100644
index 0000000..b7b0c0c
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceUDPClient.js
@@ -0,0 +1,145 @@
+const udp = require('dgram');
+const dns = require('dns');
+const Constants = require('../../util/Constants');
+const EventEmitter = require('events').EventEmitter;
+
+/**
+ * Represents a UDP Client for a Voice Connection
+ * @extends {EventEmitter}
+ * @private
+ */
+class VoiceConnectionUDPClient extends EventEmitter {
+ constructor(voiceConnection) {
+ super();
+
+ /**
+ * The voice connection that this UDP client serves
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = voiceConnection;
+
+ /**
+ * The UDP socket
+ * @type {?Socket}
+ */
+ this.socket = null;
+
+ /**
+ * The address of the discord voice server
+ * @type {?string}
+ */
+ this.discordAddress = null;
+
+ /**
+ * The local IP address
+ * @type {?string}
+ */
+ this.localAddress = null;
+
+ /**
+ * The local port
+ * @type {?string}
+ */
+ this.localPort = null;
+
+ this.voiceConnection.on('closing', this.shutdown.bind(this));
+ }
+
+ shutdown() {
+ if (this.socket) {
+ try {
+ this.socket.close();
+ } catch (e) {
+ return;
+ }
+ this.socket = null;
+ }
+ }
+
+ /**
+ * The port of the discord voice server
+ * @type {number}
+ * @readonly
+ */
+ get discordPort() {
+ return this.voiceConnection.authentication.port;
+ }
+
+ /**
+ * Tries to resolve the voice server endpoint to an address
+ * @returns {Promise<string>}
+ */
+ findEndpointAddress() {
+ return new Promise((resolve, reject) => {
+ dns.lookup(this.voiceConnection.authentication.endpoint, (error, address) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+ this.discordAddress = address;
+ resolve(address);
+ });
+ });
+ }
+
+ /**
+ * Send a packet to the UDP client
+ * @param {Object} packet the packet to send
+ * @returns {Promise<Object>}
+ */
+ send(packet) {
+ return new Promise((resolve, reject) => {
+ if (!this.socket) throw new Error('Tried to send a UDP packet, but there is no socket available.');
+ if (!this.discordAddress || !this.discordPort) throw new Error('Malformed UDP address or port.');
+ this.socket.send(packet, 0, packet.length, this.discordPort, this.discordAddress, error => {
+ if (error) reject(error); else resolve(packet);
+ });
+ });
+ }
+
+ createUDPSocket(address) {
+ this.discordAddress = address;
+ const socket = this.socket = udp.createSocket('udp4');
+
+ socket.once('message', message => {
+ const packet = parseLocalPacket(message);
+ if (packet.error) {
+ this.emit('error', packet.error);
+ return;
+ }
+
+ this.localAddress = packet.address;
+ this.localPort = packet.port;
+
+ this.voiceConnection.sockets.ws.sendPacket({
+ op: Constants.VoiceOPCodes.SELECT_PROTOCOL,
+ d: {
+ protocol: 'udp',
+ data: {
+ address: packet.address,
+ port: packet.port,
+ mode: 'xsalsa20_poly1305',
+ },
+ },
+ });
+ });
+
+ const blankMessage = new Buffer(70);
+ blankMessage.writeUIntBE(this.voiceConnection.authentication.ssrc, 0, 4);
+ this.send(blankMessage);
+ }
+}
+
+function parseLocalPacket(message) {
+ try {
+ const packet = new Buffer(message);
+ let address = '';
+ for (let i = 4; i < packet.indexOf(0, i); i++) address += String.fromCharCode(packet[i]);
+ const port = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10);
+ return { address, port };
+ } catch (error) {
+ return { error };
+ }
+}
+
+module.exports = VoiceConnectionUDPClient;
diff --git a/node_modules/discord.js/src/client/voice/VoiceWebSocket.js b/node_modules/discord.js/src/client/voice/VoiceWebSocket.js
new file mode 100644
index 0000000..bafa5dd
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/VoiceWebSocket.js
@@ -0,0 +1,249 @@
+const Constants = require('../../util/Constants');
+const SecretKey = require('./util/SecretKey');
+const EventEmitter = require('events').EventEmitter;
+
+let WebSocket;
+try {
+ WebSocket = require('uws');
+} catch (err) {
+ WebSocket = require('ws');
+}
+
+/**
+ * Represents a Voice Connection's WebSocket
+ * @extends {EventEmitter}
+ * @private
+ */
+class VoiceWebSocket extends EventEmitter {
+ constructor(voiceConnection) {
+ super();
+
+ /**
+ * The Voice Connection that this WebSocket serves
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = voiceConnection;
+
+ /**
+ * How many connection attempts have been made
+ * @type {number}
+ */
+ this.attempts = 0;
+
+ this.connect();
+ this.dead = false;
+ this.voiceConnection.on('closing', this.shutdown.bind(this));
+ }
+
+ shutdown() {
+ this.dead = true;
+ this.reset();
+ }
+
+ /**
+ * The client of this voice websocket
+ * @type {Client}
+ * @readonly
+ */
+ get client() {
+ return this.voiceConnection.voiceManager.client;
+ }
+
+ /**
+ * Resets the current WebSocket
+ */
+ reset() {
+ if (this.ws) {
+ if (this.ws.readyState !== WebSocket.CLOSED) this.ws.close();
+ this.ws = null;
+ }
+ this.clearHeartbeat();
+ }
+
+ /**
+ * Starts connecting to the Voice WebSocket Server.
+ */
+ connect() {
+ if (this.dead) return;
+ if (this.ws) this.reset();
+ if (this.attempts > 5) {
+ this.emit('error', new Error(`Too many connection attempts (${this.attempts}).`));
+ return;
+ }
+
+ this.attempts++;
+
+ /**
+ * The actual WebSocket used to connect to the Voice WebSocket Server.
+ * @type {WebSocket}
+ */
+ this.ws = new WebSocket(`wss://${this.voiceConnection.authentication.endpoint}`);
+ this.ws.onopen = this.onOpen.bind(this);
+ this.ws.onmessage = this.onMessage.bind(this);
+ this.ws.onclose = this.onClose.bind(this);
+ this.ws.onerror = this.onError.bind(this);
+ }
+
+ /**
+ * Sends data to the WebSocket if it is open.
+ * @param {string} data the data to send to the WebSocket
+ * @returns {Promise<string>}
+ */
+ send(data) {
+ return new Promise((resolve, reject) => {
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ throw new Error(`Voice websocket not open to send ${data}.`);
+ }
+ this.ws.send(data, null, error => {
+ if (error) reject(error); else resolve(data);
+ });
+ });
+ }
+
+ /**
+ * JSON.stringify's a packet and then sends it to the WebSocket Server.
+ * @param {Object} packet the packet to send
+ * @returns {Promise<string>}
+ */
+ sendPacket(packet) {
+ try {
+ packet = JSON.stringify(packet);
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ return this.send(packet);
+ }
+
+ /**
+ * Called whenever the WebSocket opens
+ */
+ onOpen() {
+ this.sendPacket({
+ op: Constants.OPCodes.DISPATCH,
+ d: {
+ server_id: this.voiceConnection.channel.guild.id,
+ user_id: this.client.user.id,
+ token: this.voiceConnection.authentication.token,
+ session_id: this.voiceConnection.authentication.session_id,
+ },
+ }).catch(() => {
+ this.emit('error', new Error('Tried to send join packet, but the WebSocket is not open.'));
+ });
+ }
+
+ /**
+ * Called whenever a message is received from the WebSocket
+ * @param {MessageEvent} event the message event that was received
+ * @returns {void}
+ */
+ onMessage(event) {
+ try {
+ return this.onPacket(JSON.parse(event.data));
+ } catch (error) {
+ return this.onError(error);
+ }
+ }
+
+ /**
+ * Called whenever the connection to the WebSocket Server is lost
+ */
+ onClose() {
+ if (!this.dead) this.client.setTimeout(this.connect.bind(this), this.attempts * 1000);
+ }
+
+ /**
+ * Called whenever an error occurs with the WebSocket.
+ * @param {Error} error the error that occurred
+ */
+ onError(error) {
+ this.emit('error', error);
+ }
+
+ /**
+ * Called whenever a valid packet is received from the WebSocket
+ * @param {Object} packet the received packet
+ */
+ onPacket(packet) {
+ switch (packet.op) {
+ case Constants.VoiceOPCodes.READY:
+ this.setHeartbeat(packet.d.heartbeat_interval);
+ /**
+ * Emitted once the voice websocket receives the ready packet
+ * @param {Object} packet the received packet
+ * @event VoiceWebSocket#ready
+ */
+ this.emit('ready', packet.d);
+ break;
+ case Constants.VoiceOPCodes.SESSION_DESCRIPTION:
+ /**
+ * Emitted once the Voice Websocket receives a description of this voice session
+ * @param {string} encryptionMode the type of encryption being used
+ * @param {SecretKey} secretKey the secret key used for encryption
+ * @event VoiceWebSocket#sessionDescription
+ */
+ this.emit('sessionDescription', packet.d.mode, new SecretKey(packet.d.secret_key));
+ break;
+ case Constants.VoiceOPCodes.SPEAKING:
+ /**
+ * Emitted whenever a speaking packet is received
+ * @param {Object} data
+ * @event VoiceWebSocket#speaking
+ */
+ this.emit('speaking', packet.d);
+ break;
+ default:
+ /**
+ * Emitted when an unhandled packet is received
+ * @param {Object} packet
+ * @event VoiceWebSocket#unknownPacket
+ */
+ this.emit('unknownPacket', packet);
+ break;
+ }
+ }
+
+ /**
+ * Sets an interval at which to send a heartbeat packet to the WebSocket
+ * @param {number} interval the interval at which to send a heartbeat packet
+ */
+ setHeartbeat(interval) {
+ if (!interval || isNaN(interval)) {
+ this.onError(new Error('Tried to set voice heartbeat but no valid interval was specified.'));
+ return;
+ }
+ if (this.heartbeatInterval) {
+ /**
+ * Emitted whenver the voice websocket encounters a non-fatal error
+ * @param {string} warn the warning
+ * @event VoiceWebSocket#warn
+ */
+ this.emit('warn', 'A voice heartbeat interval is being overwritten');
+ clearInterval(this.heartbeatInterval);
+ }
+ this.heartbeatInterval = this.client.setInterval(this.sendHeartbeat.bind(this), interval);
+ }
+
+ /**
+ * Clears a heartbeat interval, if one exists
+ */
+ clearHeartbeat() {
+ if (!this.heartbeatInterval) {
+ this.emit('warn', 'Tried to clear a heartbeat interval that does not exist');
+ return;
+ }
+ clearInterval(this.heartbeatInterval);
+ this.heartbeatInterval = null;
+ }
+
+ /**
+ * Sends a heartbeat packet
+ */
+ sendHeartbeat() {
+ this.sendPacket({ op: Constants.VoiceOPCodes.HEARTBEAT, d: null }).catch(() => {
+ this.emit('warn', 'Tried to send heartbeat, but connection is not open');
+ this.clearHeartbeat();
+ });
+ }
+}
+
+module.exports = VoiceWebSocket;
diff --git a/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js b/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js
new file mode 100644
index 0000000..e08a365
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js
@@ -0,0 +1,307 @@
+const EventEmitter = require('events').EventEmitter;
+const NaCl = require('tweetnacl');
+
+const nonce = new Buffer(24);
+nonce.fill(0);
+
+/**
+ * The class that sends voice packet data to the voice connection.
+ * ```js
+ * // obtained using:
+ * voiceChannel.join().then(connection => {
+ * // you can play a file or a stream here:
+ * const dispatcher = connection.playFile('./file.mp3');
+ * });
+ * ```
+ * @extends {EventEmitter}
+ */
+class StreamDispatcher extends EventEmitter {
+ constructor(player, stream, sd, streamOptions) {
+ super();
+ this.player = player;
+ this.stream = stream;
+ this.streamingData = {
+ channels: 2,
+ count: 0,
+ sequence: sd.sequence,
+ timestamp: sd.timestamp,
+ pausedTime: 0,
+ };
+ this._startStreaming();
+ this._triggered = false;
+ this._volume = streamOptions.volume;
+
+ /**
+ * How many passes the dispatcher should take when sending packets to reduce packet loss. Values over 5
+ * aren't recommended, as it means you are using 5x more bandwidth. You _can_ edit this at runtime.
+ * @type {number}
+ */
+ this.passes = streamOptions.passes || 1;
+
+ /**
+ * Whether playing is paused
+ * @type {boolean}
+ */
+ this.paused = false;
+
+ this.setVolume(streamOptions.volume || 1);
+ }
+
+ /**
+ * How long the stream dispatcher has been "speaking" for
+ * @type {number}
+ * @readonly
+ */
+ get time() {
+ return this.streamingData.count * (this.streamingData.length || 0);
+ }
+
+ /**
+ * The total time, taking into account pauses and skips, that the dispatcher has been streaming for
+ * @type {number}
+ * @readonly
+ */
+ get totalStreamTime() {
+ return this.time + this.streamingData.pausedTime;
+ }
+
+ /**
+ * The volume of the stream, relative to the stream's input volume
+ * @type {number}
+ * @readonly
+ */
+ get volume() {
+ return this._volume;
+ }
+
+ /**
+ * Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double.
+ * @param {number} volume The volume that you want to set
+ */
+ setVolume(volume) {
+ this._volume = volume;
+ }
+
+ /**
+ * Set the volume in decibels
+ * @param {number} db The decibels
+ */
+ setVolumeDecibels(db) {
+ this._volume = Math.pow(10, db / 20);
+ }
+
+ /**
+ * Set the volume so that a perceived value of 0.5 is half the perceived volume etc.
+ * @param {number} value The value for the volume
+ */
+ setVolumeLogarithmic(value) {
+ this._volume = Math.pow(value, 1.660964);
+ }
+
+ /**
+ * Stops sending voice packets to the voice connection (stream may still progress however)
+ */
+ pause() {
+ this._setPaused(true);
+ }
+
+ /**
+ * Resumes sending voice packets to the voice connection (may be further on in the stream than when paused)
+ */
+ resume() {
+ this._setPaused(false);
+ }
+
+ /**
+ * Stops the current stream permanently and emits an `end` event.
+ * @param {string} [reason='user'] An optional reason for stopping the dispatcher.
+ */
+ end(reason = 'user') {
+ this._triggerTerminalState('end', reason);
+ }
+
+ _setSpeaking(value) {
+ this.speaking = value;
+ /**
+ * Emitted when the dispatcher starts/stops speaking
+ * @event StreamDispatcher#speaking
+ * @param {boolean} value Whether or not the dispatcher is speaking
+ */
+ this.emit('speaking', value);
+ }
+
+ _sendBuffer(buffer, sequence, timestamp) {
+ let repeats = this.passes;
+ const packet = this._createPacket(sequence, timestamp, this.player.opusEncoder.encode(buffer));
+ while (repeats--) {
+ this.player.voiceConnection.sockets.udp.send(packet)
+ .catch(e => this.emit('debug', `Failed to send a packet ${e}`));
+ }
+ }
+
+ _createPacket(sequence, timestamp, buffer) {
+ const packetBuffer = new Buffer(buffer.length + 28);
+ packetBuffer.fill(0);
+ packetBuffer[0] = 0x80;
+ packetBuffer[1] = 0x78;
+
+ packetBuffer.writeUIntBE(sequence, 2, 2);
+ packetBuffer.writeUIntBE(timestamp, 4, 4);
+ packetBuffer.writeUIntBE(this.player.voiceConnection.authentication.ssrc, 8, 4);
+
+ packetBuffer.copy(nonce, 0, 0, 12);
+ buffer = NaCl.secretbox(buffer, nonce, this.player.voiceConnection.authentication.secretKey.key);
+
+ for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i];
+
+ return packetBuffer;
+ }
+
+ _applyVolume(buffer) {
+ if (this._volume === 1) return buffer;
+
+ const out = new Buffer(buffer.length);
+ for (let i = 0; i < buffer.length; i += 2) {
+ if (i >= buffer.length - 1) break;
+ const uint = Math.min(32767, Math.max(-32767, Math.floor(this._volume * buffer.readInt16LE(i))));
+ out.writeInt16LE(uint, i);
+ }
+
+ return out;
+ }
+
+ _send() {
+ try {
+ if (this._triggered) {
+ this._setSpeaking(false);
+ return;
+ }
+
+ const data = this.streamingData;
+
+ if (data.missed >= 5) {
+ this._triggerTerminalState('end', 'Stream is not generating quickly enough.');
+ return;
+ }
+
+ if (this.paused) {
+ // data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
+ data.pausedTime += data.length * 10;
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this._send(), data.length * 10);
+ return;
+ }
+
+ this._setSpeaking(true);
+
+ if (!data.startTime) {
+ /**
+ * Emitted once the dispatcher starts streaming
+ * @event StreamDispatcher#start
+ */
+ this.emit('start');
+ data.startTime = Date.now();
+ }
+
+ const bufferLength = 1920 * data.channels;
+ let buffer = this.stream.read(bufferLength);
+ if (!buffer) {
+ data.missed++;
+ data.pausedTime += data.length * 10;
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this._send(), data.length * 10);
+ return;
+ }
+
+ data.missed = 0;
+
+ if (buffer.length !== bufferLength) {
+ const newBuffer = new Buffer(bufferLength).fill(0);
+ buffer.copy(newBuffer);
+ buffer = newBuffer;
+ }
+
+ buffer = this._applyVolume(buffer);
+
+ data.count++;
+ data.sequence = (data.sequence + 1) < 65536 ? data.sequence + 1 : 0;
+ data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
+
+ this._sendBuffer(buffer, data.sequence, data.timestamp);
+
+ const nextTime = data.length + (data.startTime + data.pausedTime + (data.count * data.length) - Date.now());
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this._send(), nextTime);
+ } catch (e) {
+ this._triggerTerminalState('error', e);
+ }
+ }
+
+ _triggerEnd(reason) {
+ /**
+ * Emitted once the stream has ended. Attach a `once` listener to this.
+ * @event StreamDispatcher#end
+ * @param {string} reason The reason for the end of the dispatcher. If it ended because it reached the end of the
+ * stream, this would be `stream`. If you invoke `.end()` without specifying a reason, this would be `user`.
+ */
+ this.emit('end', reason);
+ }
+
+ _triggerError(err) {
+ this.emit('end');
+ /**
+ * Emitted once the stream has encountered an error. Attach a `once` listener to this. Also emits `end`.
+ * @event StreamDispatcher#error
+ * @param {Error} err The encountered error
+ */
+ this.emit('error', err);
+ }
+
+ _triggerTerminalState(state, err) {
+ if (this._triggered) return;
+ /**
+ * Emitted when the stream wants to give debug information.
+ * @event StreamDispatcher#debug
+ * @param {string} information The debug information
+ */
+ this.emit('debug', `Triggered terminal state ${state} - stream is now dead`);
+ this._triggered = true;
+ this._setSpeaking(false);
+ switch (state) {
+ case 'end':
+ this._triggerEnd(err);
+ break;
+ case 'error':
+ this._triggerError(err);
+ break;
+ default:
+ this.emit('error', 'Unknown trigger state');
+ break;
+ }
+ }
+
+ _startStreaming() {
+ if (!this.stream) {
+ this.emit('error', 'No stream');
+ return;
+ }
+
+ this.stream.on('end', err => this._triggerTerminalState('end', err || 'stream'));
+ this.stream.on('error', err => this._triggerTerminalState('error', err));
+
+ const data = this.streamingData;
+ data.length = 20;
+ data.missed = 0;
+
+ this.stream.once('readable', () => this._send());
+ }
+
+ _setPaused(paused) {
+ if (paused) {
+ this.paused = true;
+ this._setSpeaking(false);
+ } else {
+ this.paused = false;
+ this._setSpeaking(true);
+ }
+ }
+}
+
+module.exports = StreamDispatcher;
diff --git a/node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js b/node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js
new file mode 100644
index 0000000..6c3ba6e
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js
@@ -0,0 +1,15 @@
+class BaseOpus {
+ constructor(player) {
+ this.player = player;
+ }
+
+ encode(buffer) {
+ return buffer;
+ }
+
+ decode(buffer) {
+ return buffer;
+ }
+}
+
+module.exports = BaseOpus;
diff --git a/node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js b/node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js
new file mode 100644
index 0000000..10f287b
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js
@@ -0,0 +1,27 @@
+const OpusEngine = require('./BaseOpusEngine');
+
+let opus;
+
+class NodeOpusEngine extends OpusEngine {
+ constructor(player) {
+ super(player);
+ try {
+ opus = require('node-opus');
+ } catch (err) {
+ throw err;
+ }
+ this.encoder = new opus.OpusEncoder(48000, 2);
+ }
+
+ encode(buffer) {
+ super.encode(buffer);
+ return this.encoder.encode(buffer, 1920);
+ }
+
+ decode(buffer) {
+ super.decode(buffer);
+ return this.encoder.decode(buffer, 1920);
+ }
+}
+
+module.exports = NodeOpusEngine;
diff --git a/node_modules/discord.js/src/client/voice/opus/OpusEngineList.js b/node_modules/discord.js/src/client/voice/opus/OpusEngineList.js
new file mode 100644
index 0000000..ffd512a
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/OpusEngineList.js
@@ -0,0 +1,24 @@
+const list = [
+ require('./NodeOpusEngine'),
+ require('./OpusScriptEngine'),
+];
+
+function fetch(Encoder) {
+ try {
+ return new Encoder();
+ } catch (err) {
+ return null;
+ }
+}
+
+exports.add = encoder => {
+ list.push(encoder);
+};
+
+exports.fetch = () => {
+ for (const encoder of list) {
+ const fetched = fetch(encoder);
+ if (fetched) return fetched;
+ }
+ throw new Error('Couldn\'t find an Opus engine.');
+};
diff --git a/node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js b/node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js
new file mode 100644
index 0000000..33b4ff5
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js
@@ -0,0 +1,27 @@
+const OpusEngine = require('./BaseOpusEngine');
+
+let OpusScript;
+
+class NodeOpusEngine extends OpusEngine {
+ constructor(player) {
+ super(player);
+ try {
+ OpusScript = require('opusscript');
+ } catch (err) {
+ throw err;
+ }
+ this.encoder = new OpusScript(48000, 2);
+ }
+
+ encode(buffer) {
+ super.encode(buffer);
+ return this.encoder.encode(buffer, 960);
+ }
+
+ decode(buffer) {
+ super.decode(buffer);
+ return this.encoder.decode(buffer);
+ }
+}
+
+module.exports = NodeOpusEngine;
diff --git a/node_modules/discord.js/src/client/voice/pcm/ConverterEngine.js b/node_modules/discord.js/src/client/voice/pcm/ConverterEngine.js
new file mode 100644
index 0000000..6b7502f
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/pcm/ConverterEngine.js
@@ -0,0 +1,14 @@
+const EventEmitter = require('events').EventEmitter;
+
+class ConverterEngine extends EventEmitter {
+ constructor(player) {
+ super();
+ this.player = player;
+ }
+
+ createConvertStream() {
+ return;
+ }
+}
+
+module.exports = ConverterEngine;
diff --git a/node_modules/discord.js/src/client/voice/pcm/ConverterEngineList.js b/node_modules/discord.js/src/client/voice/pcm/ConverterEngineList.js
new file mode 100644
index 0000000..56d430e
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/pcm/ConverterEngineList.js
@@ -0,0 +1 @@
+exports.fetch = () => require('./FfmpegConverterEngine');
diff --git a/node_modules/discord.js/src/client/voice/pcm/FfmpegConverterEngine.js b/node_modules/discord.js/src/client/voice/pcm/FfmpegConverterEngine.js
new file mode 100644
index 0000000..8fb725b
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/pcm/FfmpegConverterEngine.js
@@ -0,0 +1,86 @@
+const ConverterEngine = require('./ConverterEngine');
+const ChildProcess = require('child_process');
+const EventEmitter = require('events').EventEmitter;
+
+class PCMConversionProcess extends EventEmitter {
+ constructor(process) {
+ super();
+ this.process = process;
+ this.input = null;
+ this.process.on('error', e => this.emit('error', e));
+ }
+
+ setInput(stream) {
+ this.input = stream;
+ stream.pipe(this.process.stdin, { end: false });
+ this.input.on('error', e => this.emit('error', e));
+ this.process.stdin.on('error', e => this.emit('error', e));
+ }
+
+ destroy() {
+ this.emit('debug', 'destroying a ffmpeg process:');
+ if (this.input && this.input.unpipe && this.process.stdin) {
+ this.input.unpipe(this.process.stdin);
+ this.emit('unpiped the user input stream from the process input stream');
+ }
+ if (this.process.stdin) {
+ this.process.stdin.end();
+ this.emit('ended the process stdin');
+ }
+ if (this.process.stdin.destroy) {
+ this.process.stdin.destroy();
+ this.emit('destroyed the process stdin');
+ }
+ if (this.process.kill) {
+ this.process.kill();
+ this.emit('killed the process');
+ }
+ }
+
+}
+
+class FfmpegConverterEngine extends ConverterEngine {
+ constructor(player) {
+ super(player);
+ this.command = chooseCommand();
+ }
+
+ handleError(encoder, err) {
+ if (encoder.destroy) encoder.destroy();
+ this.emit('error', err);
+ }
+
+ createConvertStream(seek = 0) {
+ super.createConvertStream();
+ const encoder = ChildProcess.spawn(this.command, [
+ '-analyzeduration', '0',
+ '-loglevel', '0',
+ '-i', '-',
+ '-f', 's16le',
+ '-ar', '48000',
+ '-ac', '2',
+ '-ss', String(seek),
+ 'pipe:1',
+ ], { stdio: ['pipe', 'pipe', 'ignore'] });
+ return new PCMConversionProcess(encoder);
+ }
+}
+
+function chooseCommand() {
+ for (const cmd of [
+ 'ffmpeg',
+ 'avconv',
+ './ffmpeg',
+ './avconv',
+ 'node_modules\\ffmpeg-binaries\\bin\\ffmpeg',
+ 'node_modules/ffmpeg-binaries/bin/ffmpeg',
+ ]) {
+ if (!ChildProcess.spawnSync(cmd, ['-h']).error) return cmd;
+ }
+ throw new Error(
+ 'FFMPEG was not found on your system, so audio cannot be played. ' +
+ 'Please make sure FFMPEG is installed and in your PATH.'
+ );
+}
+
+module.exports = FfmpegConverterEngine;
diff --git a/node_modules/discord.js/src/client/voice/player/AudioPlayer.js b/node_modules/discord.js/src/client/voice/player/AudioPlayer.js
new file mode 100644
index 0000000..96c6c24
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/player/AudioPlayer.js
@@ -0,0 +1,80 @@
+const PCMConverters = require('../pcm/ConverterEngineList');
+const OpusEncoders = require('../opus/OpusEngineList');
+const EventEmitter = require('events').EventEmitter;
+const StreamDispatcher = require('../dispatcher/StreamDispatcher');
+
+/**
+ * Represents the Audio Player of a Voice Connection
+ * @extends {EventEmitter}
+ * @private
+ */
+class AudioPlayer extends EventEmitter {
+ constructor(voiceConnection) {
+ super();
+ /**
+ * The voice connection the player belongs to
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = voiceConnection;
+ this.audioToPCM = new (PCMConverters.fetch())();
+ this.opusEncoder = OpusEncoders.fetch();
+ this.currentConverter = null;
+ /**
+ * The current stream dispatcher, if a stream is being played
+ * @type {StreamDispatcher}
+ */
+ this.dispatcher = null;
+ this.audioToPCM.on('error', e => this.emit('error', e));
+ this.streamingData = {
+ channels: 2,
+ count: 0,
+ sequence: 0,
+ timestamp: 0,
+ pausedTime: 0,
+ };
+ this.voiceConnection.on('closing', () => this.cleanup(null, 'voice connection closing'));
+ }
+
+ playUnknownStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
+ const options = { seek, volume, passes };
+ stream.on('end', () => {
+ this.emit('debug', 'Input stream to converter has ended');
+ });
+ stream.on('error', e => this.emit('error', e));
+ const conversionProcess = this.audioToPCM.createConvertStream(options.seek);
+ conversionProcess.on('error', e => this.emit('error', e));
+ conversionProcess.setInput(stream);
+ return this.playPCMStream(conversionProcess.process.stdout, conversionProcess, options);
+ }
+
+ cleanup(checkStream, reason) {
+ // cleanup is a lot less aggressive than v9 because it doesn't try to kill every single stream it is aware of
+ this.emit('debug', `Clean up triggered due to ${reason}`);
+ const filter = checkStream && this.dispatcher && this.dispatcher.stream === checkStream;
+ if (this.currentConverter && (checkStream ? filter : true)) {
+ this.currentConverter.destroy();
+ this.currentConverter = null;
+ }
+ }
+
+ playPCMStream(stream, converter, { seek = 0, volume = 1, passes = 1 } = {}) {
+ const options = { seek, volume, passes };
+ stream.on('end', () => this.emit('debug', 'PCM input stream ended'));
+ this.cleanup(null, 'outstanding play stream');
+ this.currentConverter = converter;
+ if (this.dispatcher) {
+ this.streamingData = this.dispatcher.streamingData;
+ }
+ stream.on('error', e => this.emit('error', e));
+ const dispatcher = new StreamDispatcher(this, stream, this.streamingData, options);
+ dispatcher.on('error', e => this.emit('error', e));
+ dispatcher.on('end', () => this.cleanup(dispatcher.stream, 'dispatcher ended'));
+ dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value));
+ this.dispatcher = dispatcher;
+ dispatcher.on('debug', m => this.emit('debug', `Stream dispatch - ${m}`));
+ return dispatcher;
+ }
+
+}
+
+module.exports = AudioPlayer;
diff --git a/node_modules/discord.js/src/client/voice/player/BasePlayer.js b/node_modules/discord.js/src/client/voice/player/BasePlayer.js
new file mode 100644
index 0000000..d5285cd
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/player/BasePlayer.js
@@ -0,0 +1,121 @@
+const OpusEngines = require('../opus/OpusEngineList');
+const ConverterEngines = require('../pcm/ConverterEngineList');
+const Constants = require('../../../util/Constants');
+const StreamDispatcher = require('../dispatcher/StreamDispatcher');
+const EventEmitter = require('events').EventEmitter;
+
+class VoiceConnectionPlayer extends EventEmitter {
+ constructor(connection) {
+ super();
+ this.connection = connection;
+ this.opusEncoder = OpusEngines.fetch();
+ const Engine = ConverterEngines.fetch();
+ this.converterEngine = new Engine(this);
+ this.converterEngine.on('error', err => {
+ this._shutdown();
+ this.emit('error', err);
+ });
+ this.speaking = false;
+ this.processMap = new Map();
+ this.dispatcher = null;
+ this._streamingData = {
+ sequence: 0,
+ timestamp: 0,
+ };
+ }
+
+ convertStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
+ const options = { seek, volume, passes };
+ const encoder = this.converterEngine.createConvertStream(options.seek);
+ const pipe = stream.pipe(encoder.stdin, { end: false });
+ pipe.on('unpipe', () => {
+ this.killStream(encoder.stdout);
+ pipe.destroy();
+ });
+ this.processMap.set(encoder.stdout, {
+ pcmConverter: encoder,
+ inputStream: stream,
+ });
+ return encoder.stdout;
+ }
+
+ _shutdown() {
+ this.speaking = false;
+ if (this.dispatcher) this.dispatcher._triggerTerminalState('end', 'ended by parent player shutdown');
+ for (const stream of this.processMap.keys()) this.killStream(stream);
+ }
+
+ killStream(stream) {
+ const streams = this.processMap.get(stream);
+ this._streamingData = this.dispatcher.streamingData;
+ this.emit(Constants.Events.DEBUG, 'Cleaning up player after audio stream ended or encountered an error');
+
+ const dummyHandler = () => null;
+
+ if (streams) {
+ this.processMap.delete(stream);
+ if (streams.inputStream && streams.pcmConverter) {
+ try {
+ streams.inputStream.once('error', dummyHandler);
+ streams.pcmConverter.once('error', dummyHandler);
+ streams.pcmConverter.stdin.once('error', dummyHandler);
+ streams.pcmConverter.stdout.once('error', dummyHandler);
+ if (streams.inputStream.unpipe) {
+ streams.inputStream.unpipe(streams.pcmConverter.stdin);
+ this.emit(Constants.Events.DEBUG, '- Unpiped input stream');
+ } else if (streams.inputStream.destroy) {
+ streams.inputStream.destroy();
+ this.emit(Constants.Events.DEBUG, '- Couldn\'t unpipe input stream, so destroyed input stream');
+ }
+ if (streams.pcmConverter.stdin) {
+ streams.pcmConverter.stdin.end();
+ this.emit(Constants.Events.DEBUG, '- Ended input stream to PCM converter');
+ }
+ if (streams.pcmConverter && streams.pcmConverter.kill) {
+ streams.pcmConverter.kill('SIGINT');
+ this.emit(Constants.Events.DEBUG, '- Killed the PCM converter');
+ }
+ } catch (err) {
+ // if an error happened make sure the pcm converter is killed anyway
+ try {
+ if (streams.pcmConverter && streams.pcmConverter.kill) {
+ streams.pcmConverter.kill('SIGINT');
+ this.emit(Constants.Events.DEBUG, '- Killed the PCM converter after previous error (abnormal)');
+ }
+ } catch (e) {
+ return e;
+ }
+ return err;
+ }
+ }
+ }
+ return null;
+ }
+
+ setSpeaking(value) {
+ if (this.speaking === value) return;
+ this.speaking = value;
+ this.connection.websocket.send({
+ op: Constants.VoiceOPCodes.SPEAKING,
+ d: {
+ speaking: true,
+ delay: 0,
+ },
+ }).catch(e => {
+ this.emit('debug', e);
+ });
+ }
+
+ playPCMStream(pcmStream, { seek = 0, volume = 1, passes = 1 } = {}) {
+ const options = { seek, volume, passes };
+ const dispatcher = new StreamDispatcher(this, pcmStream, this._streamingData, options);
+ dispatcher.on('speaking', value => this.setSpeaking(value));
+ dispatcher.on('end', () => this.killStream(pcmStream));
+ dispatcher.on('error', () => this.killStream(pcmStream));
+ dispatcher.setVolume(options.volume);
+ this.dispatcher = dispatcher;
+ return dispatcher;
+ }
+}
+
+module.exports = VoiceConnectionPlayer;
diff --git a/node_modules/discord.js/src/client/voice/player/DefaultPlayer.js b/node_modules/discord.js/src/client/voice/player/DefaultPlayer.js
new file mode 100644
index 0000000..b465e8c
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/player/DefaultPlayer.js
@@ -0,0 +1,19 @@
+const BasePlayer = require('./BasePlayer');
+const fs = require('fs');
+
+class DefaultPlayer extends BasePlayer {
+ playFile(file, { seek = 0, volume = 1 } = {}) {
+ const options = { seek: seek, volume: volume };
+ return this.playStream(fs.createReadStream(file), options);
+ }
+
+ playStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
+ this._shutdown();
+ const options = { seek, volume, passes };
+ const pcmStream = this.convertStream(stream, options);
+ const dispatcher = this.playPCMStream(pcmStream, options);
+ return dispatcher;
+ }
+}
+
+module.exports = DefaultPlayer;
diff --git a/node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js b/node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js
new file mode 100644
index 0000000..50ace27
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js
@@ -0,0 +1,19 @@
+const Readable = require('stream').Readable;
+
+class VoiceReadable extends Readable {
+ constructor() {
+ super();
+ this._packets = [];
+ this.open = true;
+ }
+
+ _read() {
+ return;
+ }
+
+ _push(d) {
+ if (this.open) this.push(d);
+ }
+}
+
+module.exports = VoiceReadable;
diff --git a/node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js b/node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js
new file mode 100644
index 0000000..bc9156f
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js
@@ -0,0 +1,154 @@
+const EventEmitter = require('events').EventEmitter;
+const NaCl = require('tweetnacl');
+const Readable = require('./VoiceReadable');
+
+const nonce = new Buffer(24);
+nonce.fill(0);
+
+/**
+ * Receives voice data from a voice connection.
+ * ```js
+ * // obtained using:
+ * voiceChannel.join().then(connection => {
+ * const receiver = connection.createReceiver();
+ * });
+ * ```
+ * @extends {EventEmitter}
+ */
+class VoiceReceiver extends EventEmitter {
+ constructor(connection) {
+ super();
+ /*
+ need a queue because we don't get the ssrc of the user speaking until after the first few packets,
+ so we queue up unknown SSRCs until they become known, then empty the queue.
+ */
+ this.queues = new Map();
+ this.pcmStreams = new Map();
+ this.opusStreams = new Map();
+
+ /**
+ * Whether or not this receiver has been destroyed.
+ * @type {boolean}
+ */
+ this.destroyed = false;
+
+ /**
+ * The VoiceConnection that instantiated this
+ * @type {VoiceConnection}
+ */
+ this.voiceConnection = connection;
+
+ this._listener = msg => {
+ const ssrc = +msg.readUInt32BE(8).toString(10);
+ const user = this.voiceConnection.ssrcMap.get(ssrc);
+ if (!user) {
+ if (!this.queues.has(ssrc)) this.queues.set(ssrc, []);
+ this.queues.get(ssrc).push(msg);
+ } else {
+ if (this.queues.get(ssrc)) {
+ this.queues.get(ssrc).push(msg);
+ this.queues.get(ssrc).map(m => this.handlePacket(m, user));
+ this.queues.delete(ssrc);
+ return;
+ }
+ this.handlePacket(msg, user);
+ }
+ };
+ this.voiceConnection.sockets.udp.socket.on('message', this._listener);
+ }
+
+ /**
+ * If this VoiceReceiver has been destroyed, running `recreate()` will recreate the listener.
+ * This avoids you having to create a new receiver.
+ * <info>Any streams that you had prior to destroying the receiver will not be recreated.</info>
+ */
+ recreate() {
+ if (!this.destroyed) return;
+ this.voiceConnection.sockets.udp.socket.on('message', this._listener);
+ this.destroyed = false;
+ return;
+ }
+
+ /**
+ * Destroy this VoiceReceiver, also ending any streams that it may be controlling.
+ */
+ destroy() {
+ this.voiceConnection.sockets.udp.socket.removeListener('message', this._listener);
+ for (const stream of this.pcmStreams) {
+ stream[1]._push(null);
+ this.pcmStreams.delete(stream[0]);
+ }
+ for (const stream of this.opusStreams) {
+ stream[1]._push(null);
+ this.opusStreams.delete(stream[0]);
+ }
+ this.destroyed = true;
+ }
+
+ /**
+ * Creates a readable stream for a user that provides opus data while the user is speaking. When the user
+ * stops speaking, the stream is destroyed.
+ * @param {UserResolvable} user The user to create the stream for
+ * @returns {ReadableStream}
+ */
+ createOpusStream(user) {
+ user = this.voiceConnection.voiceManager.client.resolver.resolveUser(user);
+ if (!user) throw new Error('Couldn\'t resolve the user to create Opus stream.');
+ if (this.opusStreams.get(user.id)) throw new Error('There is already an existing stream for that user.');
+ const stream = new Readable();
+ this.opusStreams.set(user.id, stream);
+ return stream;
+ }
+
+ /**
+ * Creates a readable stream for a user that provides PCM data while the user is speaking. When the user
+ * stops speaking, the stream is destroyed. The stream is 32-bit signed stereo PCM at 48KHz.
+ * @param {UserResolvable} user The user to create the stream for
+ * @returns {ReadableStream}
+ */
+ createPCMStream(user) {
+ user = this.voiceConnection.voiceManager.client.resolver.resolveUser(user);
+ if (!user) throw new Error('Couldn\'t resolve the user to create PCM stream.');
+ if (this.pcmStreams.get(user.id)) throw new Error('There is already an existing stream for that user.');
+ const stream = new Readable();
+ this.pcmStreams.set(user.id, stream);
+ return stream;
+ }
+
+ handlePacket(msg, user) {
+ msg.copy(nonce, 0, 0, 12);
+ let data = NaCl.secretbox.open(msg.slice(12), nonce, this.voiceConnection.authentication.secretKey.key);
+ if (!data) {
+ /**
+ * Emitted whenever a voice packet cannot be decrypted
+ * @event VoiceReceiver#warn
+ * @param {string} message The warning message
+ */
+ this.emit('warn', 'Failed to decrypt voice packet');
+ return;
+ }
+ data = new Buffer(data);
+ if (this.opusStreams.get(user.id)) this.opusStreams.get(user.id)._push(data);
+ /**
+ * Emitted whenever voice data is received from the voice connection. This is _always_ emitted (unlike PCM).
+ * @event VoiceReceiver#opus
+ * @param {User} user The user that is sending the buffer (is speaking)
+ * @param {Buffer} buffer The opus buffer
+ */
+ this.emit('opus', user, data);
+ if (this.listenerCount('pcm') > 0 || this.pcmStreams.size > 0) {
+ /**
+ * Emits decoded voice data when it's received. For performance reasons, the decoding will only
+ * happen if there is at least one `pcm` listener on this receiver.
+ * @event VoiceReceiver#pcm
+ * @param {User} user The user that is sending the buffer (is speaking)
+ * @param {Buffer} buffer The decoded buffer
+ */
+ const pcm = this.voiceConnection.player.opusEncoder.decode(data);
+ if (this.pcmStreams.get(user.id)) this.pcmStreams.get(user.id)._push(pcm);
+ this.emit('pcm', user, pcm);
+ }
+ }
+}
+
+module.exports = VoiceReceiver;
diff --git a/node_modules/discord.js/src/client/voice/util/SecretKey.js b/node_modules/discord.js/src/client/voice/util/SecretKey.js
new file mode 100644
index 0000000..508a1ba
--- /dev/null
+++ b/node_modules/discord.js/src/client/voice/util/SecretKey.js
@@ -0,0 +1,16 @@
+/**
+ * Represents a Secret Key used in encryption over voice
+ * @private
+ */
+class SecretKey {
+ constructor(key) {
+ /**
+ * The key used for encryption
+ * @type {Uint8Array}
+ */
+ this.key = new Uint8Array(new ArrayBuffer(key.length));
+ for (const index in key) this.key[index] = key[index];
+ }
+}
+
+module.exports = SecretKey;
diff --git a/node_modules/discord.js/src/client/websocket/WebSocketManager.js b/node_modules/discord.js/src/client/websocket/WebSocketManager.js
new file mode 100644
index 0000000..b67af59
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/WebSocketManager.js
@@ -0,0 +1,373 @@
+const browser = typeof window !== 'undefined';
+const EventEmitter = require('events').EventEmitter;
+const Constants = require('../../util/Constants');
+const convertArrayBuffer = require('../../util/ConvertArrayBuffer');
+const pako = require('pako');
+const zlib = require('zlib');
+const PacketManager = require('./packets/WebSocketPacketManager');
+
+let WebSocket, erlpack;
+let serialize = JSON.stringify;
+if (browser) {
+ WebSocket = window.WebSocket; // eslint-disable-line no-undef
+} else {
+ try {
+ WebSocket = require('uws');
+ } catch (err) {
+ WebSocket = require('ws');
+ }
+
+ try {
+ erlpack = require('erlpack');
+ serialize = erlpack.pack;
+ } catch (err) {
+ erlpack = null;
+ }
+}
+
+/**
+ * The WebSocket Manager of the Client
+ * @private
+ */
+class WebSocketManager extends EventEmitter {
+ constructor(client) {
+ super();
+ /**
+ * The Client that instantiated this WebSocketManager
+ * @type {Client}
+ */
+ this.client = client;
+
+ /**
+ * A WebSocket Packet manager, it handles all the messages
+ * @type {PacketManager}
+ */
+ this.packetManager = new PacketManager(this);
+
+ /**
+ * The status of the WebSocketManager, a type of Constants.Status. It defaults to IDLE.
+ * @type {number}
+ */
+ this.status = Constants.Status.IDLE;
+
+ /**
+ * The session ID of the connection, null if not yet available.
+ * @type {?string}
+ */
+ this.sessionID = null;
+
+ /**
+ * The packet count of the client, null if not yet available.
+ * @type {?number}
+ */
+ this.sequence = -1;
+
+ /**
+ * The gateway address for this WebSocket connection, null if not yet available.
+ * @type {?string}
+ */
+ this.gateway = null;
+
+ /**
+ * Whether READY was emitted normally (all packets received) or not
+ * @type {boolean}
+ */
+ this.normalReady = false;
+
+ /**
+ * The WebSocket connection to the gateway
+ * @type {?WebSocket}
+ */
+ this.ws = null;
+
+ /**
+ * An object with keys that are websocket event names that should be ignored
+ * @type {Object}
+ */
+ this.disabledEvents = {};
+ for (const event of client.options.disabledEvents) this.disabledEvents[event] = true;
+
+ this.first = true;
+
+ this.lastHeartbeatAck = true;
+ }
+
+ /**
+ * Connects the client to a given gateway
+ * @param {string} gateway The gateway to connect to
+ */
+ _connect(gateway) {
+ this.client.emit('debug', `Connecting to gateway ${gateway}`);
+ this.normalReady = false;
+ if (this.status !== Constants.Status.RECONNECTING) this.status = Constants.Status.CONNECTING;
+ this.ws = new WebSocket(gateway);
+ if (browser) this.ws.binaryType = 'arraybuffer';
+ this.ws.onopen = this.eventOpen.bind(this);
+ this.ws.onmessage = this.eventMessage.bind(this);
+ this.ws.onclose = this.eventClose.bind(this);
+ this.ws.onerror = this.eventError.bind(this);
+ this._queue = [];
+ this._remaining = 120;
+ this.client.setInterval(() => {
+ this._remaining = 120;
+ this._remainingReset = Date.now();
+ }, 60e3);
+ }
+
+ connect(gateway) {
+ gateway = `${gateway}&encoding=${erlpack ? 'etf' : 'json'}`;
+ if (this.first) {
+ this._connect(gateway);
+ this.first = false;
+ } else {
+ this.client.setTimeout(() => this._connect(gateway), 5500);
+ }
+ }
+
+ heartbeat(normal) {
+ if (normal && !this.lastHeartbeatAck) {
+ this.ws.close(1007);
+ return;
+ }
+
+ this.client.emit('debug', 'Sending heartbeat');
+ this.client._pingTimestamp = Date.now();
+ this.client.ws.send({
+ op: Constants.OPCodes.HEARTBEAT,
+ d: this.sequence,
+ }, true);
+
+ this.lastHeartbeatAck = false;
+ }
+
+ /**
+ * Sends a packet to the gateway
+ * @param {Object} data An object that can be JSON stringified
+ * @param {boolean} force Whether or not to send the packet immediately
+ */
+ send(data, force = false) {
+ if (force) {
+ this._send(serialize(data));
+ return;
+ }
+ this._queue.push(serialize(data));
+ this.doQueue();
+ }
+
+ destroy() {
+ this.ws.close(1000);
+ this._queue = [];
+ this.status = Constants.Status.IDLE;
+ }
+
+ _send(data) {
+ if (this.ws.readyState === WebSocket.OPEN) {
+ this.emit('send', data);
+ this.ws.send(data);
+ }
+ }
+
+ doQueue() {
+ const item = this._queue[0];
+ if (!(this.ws.readyState === WebSocket.OPEN && item)) return;
+ if (this.remaining === 0) {
+ this.client.setTimeout(this.doQueue.bind(this), Date.now() - this.remainingReset);
+ return;
+ }
+ this._remaining--;
+ this._send(item);
+ this._queue.shift();
+ this.doQueue();
+ }
+
+ /**
+ * Run whenever the gateway connections opens up
+ */
+ eventOpen() {
+ this.client.emit('debug', 'Connection to gateway opened');
+ this.lastHeartbeatAck = true;
+ if (this.status === Constants.Status.RECONNECTING) this._sendResume();
+ else this._sendNewIdentify();
+ }
+
+ /**
+ * Sends a gateway resume packet, in cases of unexpected disconnections.
+ */
+ _sendResume() {
+ if (!this.sessionID) {
+ this._sendNewIdentify();
+ return;
+ }
+ this.client.emit('debug', 'Identifying as resumed session');
+ const payload = {
+ token: this.client.token,
+ session_id: this.sessionID,
+ seq: this.sequence,
+ };
+
+ this.send({
+ op: Constants.OPCodes.RESUME,
+ d: payload,
+ });
+ }
+
+ /**
+ * Sends a new identification packet, in cases of new connections or failed reconnections.
+ */
+ _sendNewIdentify() {
+ this.reconnecting = false;
+ const payload = this.client.options.ws;
+ payload.token = this.client.token;
+ if (this.client.options.shardCount > 0) {
+ payload.shard = [Number(this.client.options.shardId), Number(this.client.options.shardCount)];
+ }
+ this.client.emit('debug', 'Identifying as new session');
+ this.send({
+ op: Constants.OPCodes.IDENTIFY,
+ d: payload,
+ });
+ this.sequence = -1;
+ }
+
+ /**
+ * @external CloseEvent
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
+ */
+
+ /**
+ * Run whenever the connection to the gateway is closed, it will try to reconnect the client.
+ * @param {CloseEvent} event The WebSocket close event
+ */
+ eventClose(event) {
+ this.emit('close', event);
+ this.client.clearInterval(this.client.manager.heartbeatInterval);
+ this.status = Constants.Status.DISCONNECTED;
+ this._queue = [];
+ /**
+ * Emitted whenever the client websocket is disconnected
+ * @event Client#disconnect
+ * @param {CloseEvent} event The WebSocket close event
+ */
+ if (!this.reconnecting) this.client.emit(Constants.Events.DISCONNECT, event);
+ if (event.code === 4004) return;
+ if (event.code === 4010) return;
+ if (!this.reconnecting && event.code !== 1000) this.tryReconnect();
+ }
+
+ /**
+ * Run whenever a message is received from the WebSocket. Returns `true` if the message
+ * was handled properly.
+ * @param {Object} event The received websocket data
+ * @returns {boolean}
+ */
+ eventMessage(event) {
+ const data = this.tryParseEventData(event.data);
+ if (data === null) {
+ this.eventError(new Error(Constants.Errors.BAD_WS_MESSAGE));
+ return false;
+ }
+
+ this.client.emit('raw', data);
+
+ if (data.op === Constants.OPCodes.HELLO) this.client.manager.setupKeepAlive(data.d.heartbeat_interval);
+ return this.packetManager.handle(data);
+ }
+
+ /**
+ * Parses the raw data from a websocket event, inflating it if necessary
+ * @param {*} data Event data
+ * @returns {Object}
+ */
+ parseEventData(data) {
+ if (erlpack) {
+ if (data instanceof ArrayBuffer) data = convertArrayBuffer(data);
+ return erlpack.unpack(data);
+ } else {
+ if (data instanceof ArrayBuffer) data = pako.inflate(data, { to: 'string' });
+ else if (data instanceof Buffer) data = zlib.inflateSync(data).toString();
+ return JSON.parse(data);
+ }
+ }
+
+ /**
+ * Tries to call `parseEventData()` and return its result, or returns `null` upon thrown errors.
+ * @param {*} data Event data
+ * @returns {?Object}
+ */
+ tryParseEventData(data) {
+ try {
+ return this.parseEventData(data);
+ } catch (err) {
+ return null;
+ }
+ }
+
+ /**
+ * Run whenever an error occurs with the WebSocket connection. Tries to reconnect
+ * @param {Error} err The encountered error
+ */
+ eventError(err) {
+ /**
+ * Emitted whenever the Client encounters a serious connection error
+ * @event Client#error
+ * @param {Error} error The encountered error
+ */
+ if (this.client.listenerCount('error') > 0) this.client.emit('error', err);
+ this.tryReconnect();
+ }
+
+ _emitReady(normal = true) {
+ /**
+ * Emitted when the Client becomes ready to start working
+ * @event Client#ready
+ */
+ this.status = Constants.Status.READY;
+ this.client.emit(Constants.Events.READY);
+ this.packetManager.handleQueue();
+ this.normalReady = normal;
+ }
+
+ /**
+ * Runs on new packets before `READY` to see if the Client is ready yet, if it is prepares
+ * the `READY` event.
+ */
+ checkIfReady() {
+ if (this.status !== Constants.Status.READY && this.status !== Constants.Status.NEARLY) {
+ let unavailableCount = 0;
+ for (const guildID of this.client.guilds.keys()) {
+ unavailableCount += this.client.guilds.get(guildID).available ? 0 : 1;
+ }
+ if (unavailableCount === 0) {
+ this.status = Constants.Status.NEARLY;
+ if (this.client.options.fetchAllMembers) {
+ const promises = this.client.guilds.map(g => g.fetchMembers());
+ Promise.all(promises).then(() => this._emitReady(), e => {
+ this.client.emit(Constants.Events.WARN, 'Error in pre-ready guild member fetching');
+ this.client.emit(Constants.Events.ERROR, e);
+ this._emitReady();
+ });
+ return;
+ }
+ this._emitReady();
+ }
+ }
+ }
+
+ /**
+ * Tries to reconnect the client, changing the status to Constants.Status.RECONNECTING.
+ */
+ tryReconnect() {
+ if (this.status === Constants.Status.RECONNECTING || this.status === Constants.Status.CONNECTING) return;
+ this.status = Constants.Status.RECONNECTING;
+ this.ws.close();
+ this.packetManager.handleQueue();
+ /**
+ * Emitted when the Client tries to reconnect after being disconnected
+ * @event Client#reconnecting
+ */
+ this.client.emit(Constants.Events.RECONNECTING);
+ this.connect(this.client.ws.gateway);
+ }
+}
+
+module.exports = WebSocketManager;
diff --git a/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js b/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js
new file mode 100644
index 0000000..78f5777
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js
@@ -0,0 +1,125 @@
+const Constants = require('../../../util/Constants');
+
+const BeforeReadyWhitelist = [
+ Constants.WSEvents.READY,
+ Constants.WSEvents.GUILD_CREATE,
+ Constants.WSEvents.GUILD_DELETE,
+ Constants.WSEvents.GUILD_MEMBERS_CHUNK,
+ Constants.WSEvents.GUILD_MEMBER_ADD,
+ Constants.WSEvents.GUILD_MEMBER_REMOVE,
+];
+
+class WebSocketPacketManager {
+ constructor(websocketManager) {
+ this.ws = websocketManager;
+ this.handlers = {};
+ this.queue = [];
+
+ this.register(Constants.WSEvents.READY, require('./handlers/Ready'));
+ this.register(Constants.WSEvents.GUILD_CREATE, require('./handlers/GuildCreate'));
+ this.register(Constants.WSEvents.GUILD_DELETE, require('./handlers/GuildDelete'));
+ this.register(Constants.WSEvents.GUILD_UPDATE, require('./handlers/GuildUpdate'));
+ this.register(Constants.WSEvents.GUILD_BAN_ADD, require('./handlers/GuildBanAdd'));
+ this.register(Constants.WSEvents.GUILD_BAN_REMOVE, require('./handlers/GuildBanRemove'));
+ this.register(Constants.WSEvents.GUILD_MEMBER_ADD, require('./handlers/GuildMemberAdd'));
+ this.register(Constants.WSEvents.GUILD_MEMBER_REMOVE, require('./handlers/GuildMemberRemove'));
+ this.register(Constants.WSEvents.GUILD_MEMBER_UPDATE, require('./handlers/GuildMemberUpdate'));
+ this.register(Constants.WSEvents.GUILD_ROLE_CREATE, require('./handlers/GuildRoleCreate'));
+ this.register(Constants.WSEvents.GUILD_ROLE_DELETE, require('./handlers/GuildRoleDelete'));
+ this.register(Constants.WSEvents.GUILD_ROLE_UPDATE, require('./handlers/GuildRoleUpdate'));
+ this.register(Constants.WSEvents.GUILD_EMOJIS_UPDATE, require('./handlers/GuildEmojisUpdate'));
+ this.register(Constants.WSEvents.GUILD_MEMBERS_CHUNK, require('./handlers/GuildMembersChunk'));
+ this.register(Constants.WSEvents.CHANNEL_CREATE, require('./handlers/ChannelCreate'));
+ this.register(Constants.WSEvents.CHANNEL_DELETE, require('./handlers/ChannelDelete'));
+ this.register(Constants.WSEvents.CHANNEL_UPDATE, require('./handlers/ChannelUpdate'));
+ this.register(Constants.WSEvents.CHANNEL_PINS_UPDATE, require('./handlers/ChannelPinsUpdate'));
+ this.register(Constants.WSEvents.PRESENCE_UPDATE, require('./handlers/PresenceUpdate'));
+ this.register(Constants.WSEvents.USER_UPDATE, require('./handlers/UserUpdate'));
+ this.register(Constants.WSEvents.USER_NOTE_UPDATE, require('./handlers/UserNoteUpdate'));
+ this.register(Constants.WSEvents.VOICE_STATE_UPDATE, require('./handlers/VoiceStateUpdate'));
+ this.register(Constants.WSEvents.TYPING_START, require('./handlers/TypingStart'));
+ this.register(Constants.WSEvents.MESSAGE_CREATE, require('./handlers/MessageCreate'));
+ this.register(Constants.WSEvents.MESSAGE_DELETE, require('./handlers/MessageDelete'));
+ this.register(Constants.WSEvents.MESSAGE_UPDATE, require('./handlers/MessageUpdate'));
+ this.register(Constants.WSEvents.MESSAGE_DELETE_BULK, require('./handlers/MessageDeleteBulk'));
+ this.register(Constants.WSEvents.VOICE_SERVER_UPDATE, require('./handlers/VoiceServerUpdate'));
+ this.register(Constants.WSEvents.GUILD_SYNC, require('./handlers/GuildSync'));
+ this.register(Constants.WSEvents.RELATIONSHIP_ADD, require('./handlers/RelationshipAdd'));
+ this.register(Constants.WSEvents.RELATIONSHIP_REMOVE, require('./handlers/RelationshipRemove'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_ADD, require('./handlers/MessageReactionAdd'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE, require('./handlers/MessageReactionRemove'));
+ this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE_ALL, require('./handlers/MessageReactionRemoveAll'));
+ }
+
+ get client() {
+ return this.ws.client;
+ }
+
+ register(event, Handler) {
+ this.handlers[event] = new Handler(this);
+ }
+
+ handleQueue() {
+ this.queue.forEach((element, index) => {
+ this.handle(this.queue[index]);
+ this.queue.splice(index, 1);
+ });
+ }
+
+ setSequence(s) {
+ if (s && s > this.ws.sequence) this.ws.sequence = s;
+ }
+
+ handle(packet) {
+ if (packet.op === Constants.OPCodes.RECONNECT) {
+ this.setSequence(packet.s);
+ this.ws.tryReconnect();
+ return false;
+ }
+
+ if (packet.op === Constants.OPCodes.INVALID_SESSION) {
+ if (packet.d) {
+ setTimeout(() => {
+ this.ws._sendResume();
+ }, 2500);
+ } else {
+ this.ws.sessionID = null;
+ this.ws._sendNewIdentify();
+ }
+ return false;
+ }
+
+ if (packet.op === Constants.OPCodes.HEARTBEAT_ACK) {
+ this.ws.client._pong(this.ws.client._pingTimestamp);
+ this.ws.lastHeartbeatAck = true;
+ this.ws.client.emit('debug', 'Heartbeat acknowledged');
+ } else if (packet.op === Constants.OPCodes.HEARTBEAT) {
+ this.client.ws.send({
+ op: Constants.OPCodes.HEARTBEAT,
+ d: this.client.ws.sequence,
+ });
+ this.ws.client.emit('debug', 'Received gateway heartbeat');
+ }
+
+ if (this.ws.status === Constants.Status.RECONNECTING) {
+ this.ws.reconnecting = false;
+ this.ws.checkIfReady();
+ }
+
+ this.setSequence(packet.s);
+
+ if (this.ws.disabledEvents[packet.t] !== undefined) return false;
+
+ if (this.ws.status !== Constants.Status.READY) {
+ if (BeforeReadyWhitelist.indexOf(packet.t) === -1) {
+ this.queue.push(packet);
+ return false;
+ }
+ }
+
+ if (this.handlers[packet.t]) return this.handlers[packet.t].handle(packet);
+ return false;
+ }
+}
+
+module.exports = WebSocketPacketManager;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js b/node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js
new file mode 100644
index 0000000..c1c2a5a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js
@@ -0,0 +1,11 @@
+class AbstractHandler {
+ constructor(packetManager) {
+ this.packetManager = packetManager;
+ }
+
+ handle(packet) {
+ return packet;
+ }
+}
+
+module.exports = AbstractHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js
new file mode 100644
index 0000000..04cb298
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js
@@ -0,0 +1,17 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class ChannelCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.ChannelCreate.handle(data);
+ }
+}
+
+/**
+ * Emitted whenever a channel is created.
+ * @event Client#channelCreate
+ * @param {Channel} channel The channel that was created
+ */
+
+module.exports = ChannelCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js
new file mode 100644
index 0000000..b25f585
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js
@@ -0,0 +1,20 @@
+const AbstractHandler = require('./AbstractHandler');
+
+const Constants = require('../../../../util/Constants');
+
+class ChannelDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.ChannelDelete.handle(data);
+ if (response.channel) client.emit(Constants.Events.CHANNEL_DELETE, response.channel);
+ }
+}
+
+/**
+ * Emitted whenever a channel is deleted.
+ * @event Client#channelDelete
+ * @param {Channel} channel The channel that was deleted
+ */
+
+module.exports = ChannelDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js
new file mode 100644
index 0000000..636df81
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js
@@ -0,0 +1,31 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+/*
+{ t: 'CHANNEL_PINS_UPDATE',
+ s: 666,
+ op: 0,
+ d:
+ { last_pin_timestamp: '2016-08-28T17:37:13.171774+00:00',
+ channel_id: '314866471639044027' } }
+*/
+
+class ChannelPinsUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const channel = client.channels.get(data.channel_id);
+ const time = new Date(data.last_pin_timestamp);
+ if (channel && time) client.emit(Constants.Events.CHANNEL_PINS_UPDATE, channel, time);
+ }
+}
+
+/**
+ * Emitted whenever the pins of a channel are updated. Due to the nature of the WebSocket event, not much information
+ * can be provided easily here - you need to manually check the pins yourself.
+ * @event Client#channelPinsUpdate
+ * @param {Channel} channel The channel that the pins update occured in
+ * @param {Date} time The time of the pins update
+ */
+
+module.exports = ChannelPinsUpdate;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js
new file mode 100644
index 0000000..fa535b1
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class ChannelUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.ChannelUpdate.handle(data);
+ }
+}
+
+module.exports = ChannelUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js
new file mode 100644
index 0000000..60ce72d
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js
@@ -0,0 +1,23 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class GuildBanAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ const user = client.users.get(data.user.id);
+ if (guild && user) client.emit(Constants.Events.GUILD_BAN_ADD, guild, user);
+ }
+}
+
+/**
+ * Emitted whenever a member is banned from a guild.
+ * @event Client#guildBanAdd
+ * @param {Guild} guild The guild that the ban occurred in
+ * @param {User} user The user that was banned
+ */
+
+module.exports = GuildBanAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js
new file mode 100644
index 0000000..c4edbde
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js
@@ -0,0 +1,20 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildBanRemoveHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildBanRemove.handle(data);
+ }
+}
+
+/**
+ * Emitted whenever a member is unbanned from a guild.
+ * @event Client#guildBanRemove
+ * @param {Guild} guild The guild that the unban occurred in
+ * @param {User} user The user that was unbanned
+ */
+
+module.exports = GuildBanRemoveHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js
new file mode 100644
index 0000000..c7fbd7e
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js
@@ -0,0 +1,22 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ const guild = client.guilds.get(data.id);
+ if (guild) {
+ if (!guild.available && !data.unavailable) {
+ // a newly available guild
+ guild.setup(data);
+ this.packetManager.ws.checkIfReady();
+ }
+ } else {
+ // a new guild
+ client.dataManager.newGuild(data);
+ }
+ }
+}
+
+module.exports = GuildCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js
new file mode 100644
index 0000000..35e3c53
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class GuildDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.GuildDelete.handle(data);
+ if (response.guild) client.emit(Constants.Events.GUILD_DELETE, response.guild);
+ }
+}
+
+/**
+ * Emitted whenever a guild is deleted/left.
+ * @event Client#guildDelete
+ * @param {Guild} guild The guild that was deleted
+ */
+
+module.exports = GuildDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js
new file mode 100644
index 0000000..c6ebdaa
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js
@@ -0,0 +1,40 @@
+const AbstractHandler = require('./AbstractHandler');
+
+function mappify(iterable) {
+ const map = new Map();
+ for (const x of iterable) map.set(...x);
+ return map;
+}
+
+class GuildEmojisUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ if (!guild || !guild.emojis) return;
+
+ const deletions = mappify(guild.emojis.entries());
+
+ for (const emoji of data.emojis) {
+ // determine type of emoji event
+ const cachedEmoji = guild.emojis.get(emoji.id);
+ if (cachedEmoji) {
+ deletions.delete(emoji.id);
+ if (!cachedEmoji.equals(emoji, true)) {
+ // emoji updated
+ client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji);
+ }
+ } else {
+ // emoji added
+ client.actions.GuildEmojiCreate.handle(guild, emoji);
+ }
+ }
+
+ for (const emoji of deletions.values()) {
+ // emoji deleted
+ client.actions.GuildEmojiDelete.handle(emoji);
+ }
+ }
+}
+
+module.exports = GuildEmojisUpdate;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js
new file mode 100644
index 0000000..d4d122f
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js
@@ -0,0 +1,17 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildMemberAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ guild.memberCount++;
+ guild._addMember(data);
+ }
+ }
+}
+
+module.exports = GuildMemberAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js
new file mode 100644
index 0000000..6ec1bfe
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js
@@ -0,0 +1,13 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildMemberRemoveHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildMemberRemove.handle(data);
+ }
+}
+
+module.exports = GuildMemberRemoveHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js
new file mode 100644
index 0000000..94ac71f
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js
@@ -0,0 +1,18 @@
+// ##untested handler##
+
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildMemberUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ const member = guild.members.get(data.user.id);
+ if (member) guild._updateMember(member, data);
+ }
+ }
+}
+
+module.exports = GuildMemberUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js
new file mode 100644
index 0000000..02a3c3c
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js
@@ -0,0 +1,28 @@
+// ##untested##
+
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class GuildMembersChunkHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const guild = client.guilds.get(data.guild_id);
+ if (!guild) return;
+
+ const members = data.members.map(member => guild._addMember(member, false));
+
+ guild._checkChunks();
+ client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, members);
+
+ client.ws.lastHeartbeatAck = true;
+ }
+}
+
+/**
+ * Emitted whenever a chunk of guild members is received (all members come from the same guild)
+ * @event Client#guildMembersChunk
+ * @param {GuildMember[]} members The members in the chunk
+ */
+
+module.exports = GuildMembersChunkHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js
new file mode 100644
index 0000000..8581d53
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildRoleCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildRoleCreate.handle(data);
+ }
+}
+
+module.exports = GuildRoleCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js
new file mode 100644
index 0000000..63439b0
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildRoleDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildRoleDelete.handle(data);
+ }
+}
+
+module.exports = GuildRoleDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js
new file mode 100644
index 0000000..6fbdc10
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildRoleUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildRoleUpdate.handle(data);
+ }
+}
+
+module.exports = GuildRoleUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js
new file mode 100644
index 0000000..0b9f5aa
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildSyncHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildSync.handle(data);
+ }
+}
+
+module.exports = GuildSyncHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js
new file mode 100644
index 0000000..70eff52
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class GuildUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.GuildUpdate.handle(data);
+ }
+}
+
+module.exports = GuildUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js
new file mode 100644
index 0000000..058dc85
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class MessageCreateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.MessageCreate.handle(data);
+ if (response.message) client.emit(Constants.Events.MESSAGE_CREATE, response.message);
+ }
+}
+
+/**
+ * Emitted whenever a message is created
+ * @event Client#message
+ * @param {Message} message The created message
+ */
+
+module.exports = MessageCreateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js
new file mode 100644
index 0000000..b06ce98
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class MessageDeleteHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const response = client.actions.MessageDelete.handle(data);
+ if (response.message) client.emit(Constants.Events.MESSAGE_DELETE, response.message);
+ }
+}
+
+/**
+ * Emitted whenever a message is deleted
+ * @event Client#messageDelete
+ * @param {Message} message The deleted message
+ */
+
+module.exports = MessageDeleteHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js
new file mode 100644
index 0000000..6cd3648
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js
@@ -0,0 +1,17 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageDeleteBulkHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageDeleteBulk.handle(data);
+ }
+}
+
+/**
+ * Emitted whenever messages are deleted in bulk
+ * @event Client#messageDeleteBulk
+ * @param {Collection<string, Message>} messages The deleted messages, mapped by their ID
+ */
+
+module.exports = MessageDeleteBulkHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js
new file mode 100644
index 0000000..a58db70
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionAdd.handle(data);
+ }
+}
+
+module.exports = MessageReactionAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js
new file mode 100644
index 0000000..cddde70
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionRemove extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionRemove.handle(data);
+ }
+}
+
+module.exports = MessageReactionRemove;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js
new file mode 100644
index 0000000..303da9c
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageReactionRemoveAll extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageReactionRemoveAll.handle(data);
+ }
+}
+
+module.exports = MessageReactionRemoveAll;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js
new file mode 100644
index 0000000..527632d
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class MessageUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.MessageUpdate.handle(data);
+ }
+}
+
+module.exports = MessageUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js
new file mode 100644
index 0000000..09d78a0
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js
@@ -0,0 +1,72 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+const cloneObject = require('../../../../util/CloneObject');
+
+class PresenceUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ let user = client.users.get(data.user.id);
+ const guild = client.guilds.get(data.guild_id);
+
+ // step 1
+ if (!user) {
+ if (data.user.username) {
+ user = client.dataManager.newUser(data.user);
+ } else {
+ return;
+ }
+ }
+
+ const oldUser = cloneObject(user);
+ user.patch(data.user);
+ if (!user.equals(oldUser)) {
+ client.emit(Constants.Events.USER_UPDATE, oldUser, user);
+ }
+
+ if (guild) {
+ let member = guild.members.get(user.id);
+ if (!member && data.status !== 'offline') {
+ member = guild._addMember({
+ user,
+ roles: data.roles,
+ deaf: false,
+ mute: false,
+ }, false);
+ client.emit(Constants.Events.GUILD_MEMBER_AVAILABLE, member);
+ }
+ if (member) {
+ const oldMember = cloneObject(member);
+ if (member.presence) {
+ oldMember.frozenPresence = cloneObject(member.presence);
+ }
+ guild._setPresence(user.id, data);
+ client.emit(Constants.Events.PRESENCE_UPDATE, oldMember, member);
+ } else {
+ guild._setPresence(user.id, data);
+ }
+ }
+ }
+}
+
+/**
+ * Emitted whenever a guild member's presence changes, or they change one of their details.
+ * @event Client#presenceUpdate
+ * @param {GuildMember} oldMember The member before the presence update
+ * @param {GuildMember} newMember The member after the presence update
+ */
+
+/**
+ * Emitted whenever a user's details (e.g. username) are changed.
+ * @event Client#userUpdate
+ * @param {User} oldUser The user before the update
+ * @param {User} newUser The user after the update
+ */
+
+/**
+ * Emitted whenever a member becomes available in a large guild
+ * @event Client#guildMemberAvailable
+ * @param {GuildMember} member The member that became available
+ */
+
+module.exports = PresenceUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js b/node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js
new file mode 100644
index 0000000..10bc6b2
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js
@@ -0,0 +1,69 @@
+const AbstractHandler = require('./AbstractHandler');
+
+const ClientUser = require('../../../../structures/ClientUser');
+
+class ReadyHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ client.ws.heartbeat();
+
+ const clientUser = new ClientUser(client, data.user);
+ client.user = clientUser;
+ client.readyAt = new Date();
+ client.users.set(clientUser.id, clientUser);
+
+ for (const guild of data.guilds) client.dataManager.newGuild(guild);
+ for (const privateDM of data.private_channels) client.dataManager.newChannel(privateDM);
+
+ for (const relation of data.relationships) {
+ const user = client.dataManager.newUser(relation.user);
+ if (relation.type === 1) {
+ client.user.friends.set(user.id, user);
+ } else if (relation.type === 2) {
+ client.user.blocked.set(user.id, user);
+ }
+ }
+
+ data.presences = data.presences || [];
+ for (const presence of data.presences) {
+ client.dataManager.newUser(presence.user);
+ client._setPresence(presence.user.id, presence);
+ }
+
+ if (data.notes) {
+ for (const user in data.notes) {
+ let note = data.notes[user];
+ if (!note.length) note = null;
+
+ client.user.notes.set(user, note);
+ }
+ }
+
+ if (!client.user.bot && client.options.sync) client.setInterval(client.syncGuilds.bind(client), 30000);
+ client.once('ready', client.syncGuilds.bind(client));
+
+ if (!client.users.has('1')) {
+ client.dataManager.newUser({
+ id: '1',
+ username: 'Clyde',
+ discriminator: '0000',
+ avatar: 'https://discordapp.com/assets/f78426a064bc9dd24847519259bc42af.png',
+ bot: true,
+ status: 'online',
+ game: null,
+ verified: true,
+ });
+ }
+
+ client.setTimeout(() => {
+ if (!client.ws.normalReady) client.ws._emitReady(false);
+ }, 1200 * data.guilds.length);
+
+ this.packetManager.ws.sessionID = data.session_id;
+ this.packetManager.ws.checkIfReady();
+ }
+}
+
+module.exports = ReadyHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js
new file mode 100644
index 0000000..122b4c5
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class RelationshipAddHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ if (data.type === 1) {
+ client.fetchUser(data.id).then(user => {
+ client.user.friends.set(user.id, user);
+ });
+ } else if (data.type === 2) {
+ client.fetchUser(data.id).then(user => {
+ client.user.blocked.set(user.id, user);
+ });
+ }
+ }
+}
+
+module.exports = RelationshipAddHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js
new file mode 100644
index 0000000..b57326a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class RelationshipRemoveHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ if (data.type === 2) {
+ if (client.user.blocked.has(data.id)) {
+ client.user.blocked.delete(data.id);
+ }
+ } else if (data.type === 1) {
+ if (client.user.friends.has(data.id)) {
+ client.user.friends.delete(data.id);
+ }
+ }
+ }
+}
+
+module.exports = RelationshipRemoveHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js b/node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js
new file mode 100644
index 0000000..1a35ca7
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js
@@ -0,0 +1,68 @@
+const AbstractHandler = require('./AbstractHandler');
+const Constants = require('../../../../util/Constants');
+
+class TypingStartHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ const channel = client.channels.get(data.channel_id);
+ const user = client.users.get(data.user_id);
+ const timestamp = new Date(data.timestamp * 1000);
+
+ if (channel && user) {
+ if (channel.type === 'voice') {
+ client.emit(Constants.Events.WARN, `Discord sent a typing packet to voice channel ${channel.id}`);
+ return;
+ }
+ if (channel._typing.has(user.id)) {
+ const typing = channel._typing.get(user.id);
+ typing.lastTimestamp = timestamp;
+ typing.resetTimeout(tooLate(channel, user));
+ } else {
+ channel._typing.set(user.id, new TypingData(client, timestamp, timestamp, tooLate(channel, user)));
+ client.emit(Constants.Events.TYPING_START, channel, user);
+ }
+ }
+ }
+}
+
+class TypingData {
+ constructor(client, since, lastTimestamp, _timeout) {
+ this.client = client;
+ this.since = since;
+ this.lastTimestamp = lastTimestamp;
+ this._timeout = _timeout;
+ }
+
+ resetTimeout(_timeout) {
+ this.client.clearTimeout(this._timeout);
+ this._timeout = _timeout;
+ }
+
+ get elapsedTime() {
+ return Date.now() - this.since;
+ }
+}
+
+function tooLate(channel, user) {
+ return channel.client.setTimeout(() => {
+ channel.client.emit(Constants.Events.TYPING_STOP, channel, user, channel._typing.get(user.id));
+ channel._typing.delete(user.id);
+ }, 6000);
+}
+
+/**
+ * Emitted whenever a user starts typing in a channel
+ * @event Client#typingStart
+ * @param {Channel} channel The channel the user started typing in
+ * @param {User} user The user that started typing
+ */
+
+/**
+ * Emitted whenever a user stops typing in a channel
+ * @event Client#typingStop
+ * @param {Channel} channel The channel the user stopped typing in
+ * @param {User} user The user that stopped typing
+ */
+
+module.exports = TypingStartHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js
new file mode 100644
index 0000000..1e4777a
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js
@@ -0,0 +1,12 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class UserNoteUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ client.actions.UserNoteUpdate.handle(data);
+ }
+}
+
+module.exports = UserNoteUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js
new file mode 100644
index 0000000..bc34f34
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js
@@ -0,0 +1,11 @@
+const AbstractHandler = require('./AbstractHandler');
+
+class UserUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.actions.UserUpdate.handle(data);
+ }
+}
+
+module.exports = UserUpdateHandler;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js
new file mode 100644
index 0000000..97885d6
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js
@@ -0,0 +1,19 @@
+const AbstractHandler = require('./AbstractHandler');
+
+/*
+{
+ "token": "my_token",
+ "guild_id": "41771983423143937",
+ "endpoint": "smart.loyal.discord.gg"
+}
+*/
+
+class VoiceServerUpdate extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+ client.emit('self.voiceServer', data);
+ }
+}
+
+module.exports = VoiceServerUpdate;
diff --git a/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js
new file mode 100644
index 0000000..ddbfbfc
--- /dev/null
+++ b/node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js
@@ -0,0 +1,49 @@
+const AbstractHandler = require('./AbstractHandler');
+
+const Constants = require('../../../../util/Constants');
+const cloneObject = require('../../../../util/CloneObject');
+
+class VoiceStateUpdateHandler extends AbstractHandler {
+ handle(packet) {
+ const client = this.packetManager.client;
+ const data = packet.d;
+
+ const guild = client.guilds.get(data.guild_id);
+ if (guild) {
+ const member = guild.members.get(data.user_id);
+ if (member) {
+ const oldVoiceChannelMember = cloneObject(member);
+ if (member.voiceChannel && member.voiceChannel.id !== data.channel_id) {
+ member.voiceChannel.members.delete(oldVoiceChannelMember.id);
+ }
+
+ // if the member left the voice channel, unset their speaking property
+ if (!data.channel_id) member.speaking = null;
+
+ if (member.user.id === client.user.id && data.channel_id) {
+ client.emit('self.voiceStateUpdate', data);
+ }
+
+ const newChannel = client.channels.get(data.channel_id);
+ if (newChannel) newChannel.members.set(member.user.id, member);
+
+ member.serverMute = data.mute;
+ member.serverDeaf = data.deaf;
+ member.selfMute = data.self_mute;
+ member.selfDeaf = data.self_deaf;
+ member.voiceSessionID = data.session_id;
+ member.voiceChannelID = data.channel_id;
+ client.emit(Constants.Events.VOICE_STATE_UPDATE, oldVoiceChannelMember, member);
+ }
+ }
+ }
+}
+
+/**
+ * Emitted whenever a user changes voice state - e.g. joins/leaves a channel, mutes/unmutes.
+ * @event Client#voiceStateUpdate
+ * @param {GuildMember} oldMember The member before the voice state update
+ * @param {GuildMember} newMember The member after the voice state update
+ */
+
+module.exports = VoiceStateUpdateHandler;