diff --git a/discord.master.js b/discord.master.js index 7b2d7c24..bfeeed30 100644 --- a/discord.master.js +++ b/discord.master.js @@ -33,9 +33,6 @@ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ -/******/ // identity function for calling harmony imports with the correct context -/******/ __webpack_require__.i = function(value) { return value; }; -/******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { @@ -63,14 +60,14 @@ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 186); +/******/ return __webpack_require__(__webpack_require__.s = 77); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(process) {exports.Package = __webpack_require__(44); +/* WEBPACK VAR INJECTION */(function(process) {exports.Package = __webpack_require__(55); const { Error, RangeError } = __webpack_require__(5); /** @@ -127,7 +124,7 @@ exports.DefaultOptions = { */ ws: { large_threshold: 250, - compress: __webpack_require__(26).platform() !== 'browser', + compress: __webpack_require__(29).platform() !== 'browser', properties: { $os: process ? process.platform : 'discord.js', $browser: 'discord.js', @@ -139,8 +136,9 @@ exports.DefaultOptions = { }, http: { version: 7, - host: 'https://discordapp.com', + api: 'https://discordapp.com/api', cdn: 'https://cdn.discordapp.com', + invite: 'https://discord.gg', }, }; @@ -196,7 +194,7 @@ exports.Endpoints = { }, }; }, - invite: code => `https://discord.gg/${code}`, + invite: (root, code) => `${root}/${code}`, botGateway: '/gateway/bot', }; @@ -1171,7 +1169,7 @@ module.exports = Collection; /* 4 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(Buffer) {const snekfetch = __webpack_require__(41); +/* WEBPACK VAR INJECTION */(function(Buffer) {const snekfetch = __webpack_require__(33); const Constants = __webpack_require__(0); const ConstantsHttp = Constants.DefaultOptions.http; const { RangeError, TypeError } = __webpack_require__(5); @@ -1473,8 +1471,8 @@ module.exports = Util; /* 5 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = __webpack_require__(72); -module.exports.Messages = __webpack_require__(185); +module.exports = __webpack_require__(56); +module.exports.Messages = __webpack_require__(110); /***/ }), @@ -1492,9 +1490,9 @@ module.exports.Messages = __webpack_require__(185); -var base64 = __webpack_require__(81) -var ieee754 = __webpack_require__(84) -var isArray = __webpack_require__(59) +var base64 = __webpack_require__(78) +var ieee754 = __webpack_require__(79) +var isArray = __webpack_require__(45) exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer @@ -3272,87 +3270,33 @@ function isnan (val) { return val !== val // eslint-disable-line no-self-compare } -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11))) +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7))) /***/ }), /* 7 */ -/***/ (function(module, exports, __webpack_require__) { +/***/ (function(module, exports) { -const Long = __webpack_require__(36); - -// Discord epoch (2015-01-01T00:00:00.000Z) -const EPOCH = 1420070400000; -let INCREMENT = 0; - -/** - * A container for useful snowflake-related methods. - */ -class SnowflakeUtil { - constructor() { - throw new Error(`The ${this.constructor.name} class may not be instantiated.`); - } - - /** - * A Twitter snowflake, except the epoch is 2015-01-01T00:00:00.000Z - * ``` - * If we have a snowflake '266241948824764416' we can represent it as binary: - * - * 64 22 17 12 0 - * 000000111011000111100001101001000101000000 00001 00000 000000000000 - * number of ms since Discord epoch worker pid increment - * ``` - * @typedef {string} Snowflake - */ - - /** - * Generates a Discord snowflake. - * This hardcodes the worker ID as 1 and the process ID as 0. - * @returns {Snowflake} The generated snowflake - */ - static generate() { - if (INCREMENT >= 4095) INCREMENT = 0; - const BINARY = `${pad((Date.now() - EPOCH).toString(2), 42)}0000100000${pad((INCREMENT++).toString(2), 12)}`; - return Long.fromString(BINARY, 2).toString(); - } - - /** - * A deconstructed snowflake. - * @typedef {Object} DeconstructedSnowflake - * @property {number} timestamp Timestamp the snowflake was created - * @property {Date} date Date the snowflake was created - * @property {number} workerID Worker ID in the snowflake - * @property {number} processID Process ID in the snowflake - * @property {number} increment Increment in the snowflake - * @property {string} binary Binary representation of the snowflake - */ - - /** - * Deconstructs a Discord snowflake. - * @param {Snowflake} snowflake Snowflake to deconstruct - * @returns {DeconstructedSnowflake} Deconstructed snowflake - */ - static deconstruct(snowflake) { - const BINARY = pad(Long.fromString(snowflake).toString(2), 64); - const res = { - timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH, - workerID: parseInt(BINARY.substring(42, 47), 2), - processID: parseInt(BINARY.substring(47, 52), 2), - increment: parseInt(BINARY.substring(52, 64), 2), - binary: BINARY, - }; - Object.defineProperty(res, 'date', { - get: function get() { return new Date(this.timestamp); }, - enumerable: true, - }); - return res; - } -} - -function pad(v, n, c = '0') { - return String(v).length >= n ? String(v) : (String(c).repeat(n) + v).slice(-n); -} - -module.exports = SnowflakeUtil; +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || Function("return this")() || (1,eval)("this"); +} catch(e) { + // This works if the window reference is available + if(typeof window === "object") + g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; /***/ }), @@ -3549,15 +3493,96 @@ process.umask = function() { return 0; }; /* 9 */ /***/ (function(module, exports, __webpack_require__) { -const Mentions = __webpack_require__(51); -const Attachment = __webpack_require__(49); -const Embed = __webpack_require__(29); -const MessageReaction = __webpack_require__(52); -const ReactionCollector = __webpack_require__(56); +const Long = __webpack_require__(40); + +// Discord epoch (2015-01-01T00:00:00.000Z) +const EPOCH = 1420070400000; +let INCREMENT = 0; + +/** + * A container for useful snowflake-related methods. + */ +class SnowflakeUtil { + constructor() { + throw new Error(`The ${this.constructor.name} class may not be instantiated.`); + } + + /** + * A Twitter snowflake, except the epoch is 2015-01-01T00:00:00.000Z + * ``` + * If we have a snowflake '266241948824764416' we can represent it as binary: + * + * 64 22 17 12 0 + * 000000111011000111100001101001000101000000 00001 00000 000000000000 + * number of ms since Discord epoch worker pid increment + * ``` + * @typedef {string} Snowflake + */ + + /** + * Generates a Discord snowflake. + * This hardcodes the worker ID as 1 and the process ID as 0. + * @returns {Snowflake} The generated snowflake + */ + static generate() { + if (INCREMENT >= 4095) INCREMENT = 0; + const BINARY = `${pad((Date.now() - EPOCH).toString(2), 42)}0000100000${pad((INCREMENT++).toString(2), 12)}`; + return Long.fromString(BINARY, 2).toString(); + } + + /** + * A deconstructed snowflake. + * @typedef {Object} DeconstructedSnowflake + * @property {number} timestamp Timestamp the snowflake was created + * @property {Date} date Date the snowflake was created + * @property {number} workerID Worker ID in the snowflake + * @property {number} processID Process ID in the snowflake + * @property {number} increment Increment in the snowflake + * @property {string} binary Binary representation of the snowflake + */ + + /** + * Deconstructs a Discord snowflake. + * @param {Snowflake} snowflake Snowflake to deconstruct + * @returns {DeconstructedSnowflake} Deconstructed snowflake + */ + static deconstruct(snowflake) { + const BINARY = pad(Long.fromString(snowflake).toString(2), 64); + const res = { + timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH, + workerID: parseInt(BINARY.substring(42, 47), 2), + processID: parseInt(BINARY.substring(47, 52), 2), + increment: parseInt(BINARY.substring(52, 64), 2), + binary: BINARY, + }; + Object.defineProperty(res, 'date', { + get: function get() { return new Date(this.timestamp); }, + enumerable: true, + }); + return res; + } +} + +function pad(v, n, c = '0') { + return String(v).length >= n ? String(v) : (String(c).repeat(n) + v).slice(-n); +} + +module.exports = SnowflakeUtil; + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +const Mentions = __webpack_require__(61); +const Attachment = __webpack_require__(62); +const Embed = __webpack_require__(30); +const MessageReaction = __webpack_require__(63); +const ReactionCollector = __webpack_require__(64); const Util = __webpack_require__(4); const Collection = __webpack_require__(3); const Constants = __webpack_require__(0); -const Permissions = __webpack_require__(10); +const Permissions = __webpack_require__(11); const { TypeError } = __webpack_require__(5); let GuildMember; @@ -3900,7 +3925,7 @@ class Message { */ isMemberMentioned(member) { // Lazy-loading is used here to get around a circular dependency that breaks things - if (!GuildMember) GuildMember = __webpack_require__(20); + if (!GuildMember) GuildMember = __webpack_require__(25); if (this.mentions.everyone) return true; if (this.mentions.users.has(member.id)) return true; if (member instanceof GuildMember && member.roles.some(r => this.mentions.roles.has(r.id))) return true; @@ -3910,6 +3935,7 @@ class Message { /** * Options that can be passed into editMessage. * @typedef {Object} MessageEditOptions + * @property {string} [content] Content to be edited * @property {Object} [embed] An embed to be added/edited * @property {string|boolean} [code] Language for optional codeblock formatting to apply */ @@ -3932,6 +3958,7 @@ class Message { } else if (!options) { options = {}; } + if (typeof options.content !== 'undefined') content = options.content; if (typeof content !== 'undefined') content = Util.resolveString(content); @@ -4152,7 +4179,7 @@ module.exports = Message; /***/ }), -/* 10 */ +/* 11 */ /***/ (function(module, exports, __webpack_require__) { const { RangeError } = __webpack_require__(5); @@ -4339,446 +4366,10 @@ Permissions.DEFAULT = 104324097; module.exports = Permissions; -/***/ }), -/* 11 */ -/***/ (function(module, exports) { - -var g; - -// This works in non-strict mode -g = (function() { - return this; -})(); - -try { - // This works if eval is allowed (see CSP) - g = g || Function("return this")() || (1,eval)("this"); -} catch(e) { - // This works if the window reference is available - if(typeof window === "object") - g = window; -} - -// g can still be undefined, but nothing to do about it... -// We return undefined, instead of nothing here, so it's -// easier to handle this case. if(!global) { ...} - -module.exports = g; - - /***/ }), /* 12 */ /***/ (function(module, exports) { -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} - - -/***/ }), -/* 13 */ -/***/ (function(module, exports, __webpack_require__) { - -const TextBasedChannel = __webpack_require__(25); -const Constants = __webpack_require__(0); -const { Presence } = __webpack_require__(14); -const UserProfile = __webpack_require__(188); -const Snowflake = __webpack_require__(7); - -/** - * Represents a user on Discord. - * @implements {TextBasedChannel} - */ -class User { - constructor(client, data) { - /** - * The client that created the instance of the the user - * @name User#client - * @type {} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - - if (data) this.setup(data); - } - - setup(data) { - /** - * The ID of the user - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The username of the user - * @type {string} - */ - this.username = data.username; - - /** - * A discriminator based on username for the user - * @type {string} - */ - this.discriminator = data.discriminator; - - /** - * The ID of the user's avatar - * @type {string} - */ - this.avatar = data.avatar; - - /** - * Whether or not the user is a bot - * @type {boolean} - */ - this.bot = Boolean(data.bot); - - /** - * The ID of the last message sent by the user, if one was sent - * @type {?Snowflake} - */ - this.lastMessageID = null; - - /** - * The Message object of the last message sent by the user, if one was sent - * @type {?Message} - */ - this.lastMessage = null; - } - - patch(data) { - for (const prop of ['id', 'username', 'discriminator', 'avatar', 'bot']) { - if (typeof data[prop] !== 'undefined') this[prop] = data[prop]; - } - if (data.token) this.client.token = data.token; - } - - /** - * The timestamp the user was created at - * @type {number} - * @readonly - */ - get createdTimestamp() { - return Snowflake.deconstruct(this.id).timestamp; - } - - /** - * The time the user was created - * @type {Date} - * @readonly - */ - get createdAt() { - return new Date(this.createdTimestamp); - } - - /** - * The presence of this user - * @type {Presence} - * @readonly - */ - get presence() { - if (this.client.presences.has(this.id)) return this.client.presences.get(this.id); - for (const guild of this.client.guilds.values()) { - if (guild.presences.has(this.id)) return guild.presences.get(this.id); - } - return new Presence(); - } - - /** - * A link to the user's avatar - * @param {Object} [options={}] Options for the avatar url - * @param {string} [options.format='webp'] One of `webp`, `png`, `jpg`, `gif`. If no format is provided, - * it will be `gif` for animated avatars or otherwise `webp` - * @param {number} [options.size=128] One of `128`, `256`, `512`, `1024`, `2048` - * @returns {?string} - */ - avatarURL({ format, size } = {}) { - if (!this.avatar) return null; - return Constants.Endpoints.CDN(this.client.options.http.cdn).Avatar(this.id, this.avatar, format, size); - } - - /** - * A link to the user's default avatar - * @type {string} - * @readonly - */ - get defaultAvatarURL() { - return Constants.Endpoints.CDN(this.client.options.http.cdn).DefaultAvatar(this.discriminator % 5); - } - - /** - * A link to the user's avatar if they have one. Otherwise a link to their default avatar will be returned - * @param {Object} [options={}] Options for the avatar url - * @param {string} [options.format='webp'] One of `webp`, `png`, `jpg`, `gif`. If no format is provided, - * it will be `gif` for animated avatars or otherwise `webp` - * @param {number} [options.size=128] One of `128`, '256', `512`, `1024`, `2048` - * @returns {string} - */ - displayAvatarURL(options) { - return this.avatarURL(options) || this.defaultAvatarURL; - } - - /** - * The Discord "tag" for this user - * @type {string} - * @readonly - */ - get tag() { - return `${this.username}#${this.discriminator}`; - } - - /** - * The note that is set for the user - * This is only available when using a user account. - * @type {?string} - * @readonly - */ - get note() { - return this.client.user.notes.get(this.id) || null; - } - - /** - * Check whether the user is typing in a channel. - * @param {ChannelResolvable} channel The channel to check in - * @returns {boolean} - */ - typingIn(channel) { - channel = this.client.resolver.resolveChannel(channel); - return channel._typing.has(this.id); - } - - /** - * Get the time that the user started typing. - * @param {ChannelResolvable} channel The channel to get the time in - * @returns {?Date} - */ - typingSinceIn(channel) { - channel = this.client.resolver.resolveChannel(channel); - return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null; - } - - /** - * Get the amount of time the user has been typing in a channel for (in milliseconds), or -1 if they're not typing. - * @param {ChannelResolvable} channel The channel to get the time in - * @returns {number} - */ - typingDurationIn(channel) { - channel = this.client.resolver.resolveChannel(channel); - return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1; - } - - /** - * The DM between the client's user and this user - * @type {?DMChannel} - * @readonly - */ - get dmChannel() { - return this.client.channels.filter(c => c.type === 'dm').find(c => c.recipient.id === this.id); - } - - /** - * Creates a DM channel between the client and the user. - * @returns {Promise} - */ - createDM() { - if (this.dmChannel) return Promise.resolve(this.dmChannel); - return this.client.api.users[this.client.user.id].channels.post({ data: { - recipient_id: this.id, - } }) - .then(data => this.client.actions.ChannelCreate.handle(data).channel); - } - - /** - * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful. - * @returns {Promise} - */ - deleteDM() { - if (!this.dmChannel) return Promise.reject(new Error('No DM Channel exists!')); - return this.client.api.channels[this.dmChannel.id].delete().then(data => - this.client.actions.ChannelDelete.handle(data).channel - ); - } - - /** - * Get the profile of the user. - * This is only available when using a user account. - * @returns {Promise} - */ - fetchProfile() { - return this.client.api.users[this.id].profile.get().then(data => new UserProfile(this, data)); - } - - /** - * Sets a note for the user. - * This is only available when using a user account. - * @param {string} note The note to set for the user - * @returns {Promise} - */ - setNote(note) { - return this.client.api.users['@me'].notes[this.id].put({ data: { note } }) - .then(() => this); - } - - /** - * Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags. - * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties. - * @param {User} user User to compare with - * @returns {boolean} - */ - equals(user) { - let equal = user && - this.id === user.id && - this.username === user.username && - this.discriminator === user.discriminator && - this.avatar === user.avatar && - this.bot === Boolean(user.bot); - - return equal; - } - - /** - * When concatenated with a string, this automatically concatenates the user's mention instead of the User object. - * @returns {string} - * @example - * // logs: Hello from <@123456789>! - * console.log(`Hello from ${user}!`); - */ - toString() { - return `<@${this.id}>`; - } - - // These are here only for documentation purposes - they are implemented by TextBasedChannel - /* eslint-disable no-empty-function */ - send() {} -} - -TextBasedChannel.applyToClass(User); - -module.exports = User; - - -/***/ }), -/* 14 */ -/***/ (function(module, exports) { - -/** - * Represents a user's presence. - */ -class Presence { - constructor(data = {}) { - /** - * The status of the presence: - * - * * **`online`** - user is online - * * **`offline`** - user is offline or invisible - * * **`idle`** - user is AFK - * * **`dnd`** - user is in Do not Disturb - * @type {string} - */ - this.status = data.status || 'offline'; - - /** - * The game that the user is playing - * @type {?Game} - */ - this.game = data.game ? new Game(data.game) : null; - } - - update(data) { - this.status = data.status || this.status; - this.game = data.game ? new Game(data.game) : null; - } - - /** - * Whether this presence is equal to another - * @param {Presence} presence The presence to compare with - * @returns {boolean} - */ - equals(presence) { - return this === presence || ( - presence && - this.status === presence.status && - this.game ? this.game.equals(presence.game) : !presence.game - ); - } -} - -/** - * Represents a game that is part of a user's presence. - */ -class Game { - constructor(data) { - /** - * The name of the game being played - * @type {string} - */ - this.name = data.name; - - /** - * The type of the game status - * @type {number} - */ - this.type = data.type; - - /** - * If the game is being streamed, a link to the stream - * @type {?string} - */ - this.url = data.url || null; - } - - /** - * Whether or not the game is being streamed - * @type {boolean} - * @readonly - */ - get streaming() { - return this.type === 1; - } - - /** - * Whether this game is equal to another game - * @param {Game} game The game to compare with - * @returns {boolean} - */ - equals(game) { - return this === game || ( - game && - this.name === game.name && - this.type === game.type && - this.url === game.url - ); - } -} - -exports.Presence = Presence; -exports.Game = Game; - - -/***/ }), -/* 15 */ -/***/ (function(module, exports) { - // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -5084,10 +4675,60 @@ function isUndefined(arg) { /***/ }), -/* 16 */ +/* 13 */ +/***/ (function(module, exports) { + +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + + +/***/ }), +/* 14 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + // a duplex stream is just a stream that is both readable and writable. // Since JS doesn't have multiple prototypal inheritance, this class // prototypally inherits from Readable, and then parasitically from @@ -5097,6 +4738,10 @@ function isUndefined(arg) { /**/ +var processNextTick = __webpack_require__(27); +/**/ + +/**/ var objectKeys = Object.keys || function (obj) { var keys = []; for (var key in obj) { @@ -5108,16 +4753,12 @@ var objectKeys = Object.keys || function (obj) { module.exports = Duplex; /**/ -var processNextTick = __webpack_require__(37); +var util = __webpack_require__(20); +util.inherits = __webpack_require__(13); /**/ -/**/ -var util = __webpack_require__(23); -util.inherits = __webpack_require__(12); -/**/ - -var Readable = __webpack_require__(60); -var Writable = __webpack_require__(39); +var Readable = __webpack_require__(46); +var Writable = __webpack_require__(36); util.inherits(Duplex, Readable); @@ -5158,6 +4799,34 @@ function onEndNT(self) { self.end(); } +Object.defineProperty(Duplex.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); + +Duplex.prototype._destroy = function (err, cb) { + this.push(null); + this.end(); + + processNextTick(cb, err); +}; + function forEach(xs, f) { for (var i = 0, l = xs.length; i < l; i++) { f(xs[i], i); @@ -5165,10 +4834,291 @@ function forEach(xs, f) { } /***/ }), -/* 17 */ +/* 15 */ /***/ (function(module, exports, __webpack_require__) { -const Snowflake = __webpack_require__(7); +const TextBasedChannel = __webpack_require__(23); +const Constants = __webpack_require__(0); +const { Presence } = __webpack_require__(17); +const UserProfile = __webpack_require__(120); +const Snowflake = __webpack_require__(9); + +/** + * Represents a user on Discord. + * @implements {TextBasedChannel} + */ +class User { + constructor(client, data) { + /** + * The client that created the instance of the the user + * @name User#client + * @type {} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + if (data) this.setup(data); + } + + setup(data) { + /** + * The ID of the user + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The username of the user + * @type {string} + */ + this.username = data.username; + + /** + * A discriminator based on username for the user + * @type {string} + */ + this.discriminator = data.discriminator; + + /** + * The ID of the user's avatar + * @type {string} + */ + this.avatar = data.avatar; + + /** + * Whether or not the user is a bot + * @type {boolean} + */ + this.bot = Boolean(data.bot); + + /** + * The ID of the last message sent by the user, if one was sent + * @type {?Snowflake} + */ + this.lastMessageID = null; + + /** + * The Message object of the last message sent by the user, if one was sent + * @type {?Message} + */ + this.lastMessage = null; + } + + patch(data) { + for (const prop of ['id', 'username', 'discriminator', 'avatar', 'bot']) { + if (typeof data[prop] !== 'undefined') this[prop] = data[prop]; + } + if (data.token) this.client.token = data.token; + } + + /** + * The timestamp the user was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the user was created + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * The presence of this user + * @type {Presence} + * @readonly + */ + get presence() { + if (this.client.presences.has(this.id)) return this.client.presences.get(this.id); + for (const guild of this.client.guilds.values()) { + if (guild.presences.has(this.id)) return guild.presences.get(this.id); + } + return new Presence(); + } + + /** + * A link to the user's avatar + * @param {Object} [options={}] Options for the avatar url + * @param {string} [options.format='webp'] One of `webp`, `png`, `jpg`, `gif`. If no format is provided, + * it will be `gif` for animated avatars or otherwise `webp` + * @param {number} [options.size=128] One of `128`, `256`, `512`, `1024`, `2048` + * @returns {?string} + */ + avatarURL({ format, size } = {}) { + if (!this.avatar) return null; + return Constants.Endpoints.CDN(this.client.options.http.cdn).Avatar(this.id, this.avatar, format, size); + } + + /** + * A link to the user's default avatar + * @type {string} + * @readonly + */ + get defaultAvatarURL() { + return Constants.Endpoints.CDN(this.client.options.http.cdn).DefaultAvatar(this.discriminator % 5); + } + + /** + * A link to the user's avatar if they have one. Otherwise a link to their default avatar will be returned + * @param {Object} [options={}] Options for the avatar url + * @param {string} [options.format='webp'] One of `webp`, `png`, `jpg`, `gif`. If no format is provided, + * it will be `gif` for animated avatars or otherwise `webp` + * @param {number} [options.size=128] One of `128`, '256', `512`, `1024`, `2048` + * @returns {string} + */ + displayAvatarURL(options) { + return this.avatarURL(options) || this.defaultAvatarURL; + } + + /** + * The Discord "tag" for this user + * @type {string} + * @readonly + */ + get tag() { + return `${this.username}#${this.discriminator}`; + } + + /** + * The note that is set for the user + * This is only available when using a user account. + * @type {?string} + * @readonly + */ + get note() { + return this.client.user.notes.get(this.id) || null; + } + + /** + * Check whether the user is typing in a channel. + * @param {ChannelResolvable} channel The channel to check in + * @returns {boolean} + */ + typingIn(channel) { + channel = this.client.resolver.resolveChannel(channel); + return channel._typing.has(this.id); + } + + /** + * Get the time that the user started typing. + * @param {ChannelResolvable} channel The channel to get the time in + * @returns {?Date} + */ + typingSinceIn(channel) { + channel = this.client.resolver.resolveChannel(channel); + return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null; + } + + /** + * Get the amount of time the user has been typing in a channel for (in milliseconds), or -1 if they're not typing. + * @param {ChannelResolvable} channel The channel to get the time in + * @returns {number} + */ + typingDurationIn(channel) { + channel = this.client.resolver.resolveChannel(channel); + return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1; + } + + /** + * The DM between the client's user and this user + * @type {?DMChannel} + * @readonly + */ + get dmChannel() { + return this.client.channels.filter(c => c.type === 'dm').find(c => c.recipient.id === this.id); + } + + /** + * Creates a DM channel between the client and the user. + * @returns {Promise} + */ + createDM() { + if (this.dmChannel) return Promise.resolve(this.dmChannel); + return this.client.api.users[this.client.user.id].channels.post({ data: { + recipient_id: this.id, + } }) + .then(data => this.client.actions.ChannelCreate.handle(data).channel); + } + + /** + * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful. + * @returns {Promise} + */ + deleteDM() { + if (!this.dmChannel) return Promise.reject(new Error('No DM Channel exists!')); + return this.client.api.channels[this.dmChannel.id].delete() + .then(data => this.client.actions.ChannelDelete.handle(data).channel); + } + + /** + * Get the profile of the user. + * This is only available when using a user account. + * @returns {Promise} + */ + fetchProfile() { + return this.client.api.users[this.id].profile.get().then(data => new UserProfile(this, data)); + } + + /** + * Sets a note for the user. + * This is only available when using a user account. + * @param {string} note The note to set for the user + * @returns {Promise} + */ + setNote(note) { + return this.client.api.users['@me'].notes[this.id].put({ data: { note } }) + .then(() => this); + } + + /** + * Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags. + * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties. + * @param {User} user User to compare with + * @returns {boolean} + */ + equals(user) { + let equal = user && + this.id === user.id && + this.username === user.username && + this.discriminator === user.discriminator && + this.avatar === user.avatar && + this.bot === Boolean(user.bot); + + return equal; + } + + /** + * When concatenated with a string, this automatically concatenates the user's mention instead of the User object. + * @returns {string} + * @example + * // logs: Hello from <@123456789>! + * console.log(`Hello from ${user}!`); + */ + toString() { + return `<@${this.id}>`; + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + send() {} +} + +TextBasedChannel.applyToClass(User); + +module.exports = User; + + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +const Snowflake = __webpack_require__(9); /** * Represents any channel on Discord. @@ -5240,250 +5190,726 @@ module.exports = Channel; /***/ }), -/* 18 */ -/***/ (function(module, exports, __webpack_require__) { - -const Constants = __webpack_require__(0); -const Collection = __webpack_require__(3); -const Snowflake = __webpack_require__(7); +/* 17 */ +/***/ (function(module, exports) { /** - * Represents a custom emoji. + * Represents a user's presence. */ -class Emoji { - constructor(guild, data) { +class Presence { + constructor(data = {}) { /** - * The client that instantiated this object - * @name Emoji#client - * @type {Client} - * @readonly + * The status of the presence: + * + * * **`online`** - user is online + * * **`offline`** - user is offline or invisible + * * **`idle`** - user is AFK + * * **`dnd`** - user is in Do not Disturb + * @type {string} */ - Object.defineProperty(this, 'client', { value: guild.client }); + this.status = data.status || 'offline'; /** - * The guild this emoji is part of - * @type {Guild} + * The game that the user is playing + * @type {?Game} */ - this.guild = guild; - - this.setup(data); + this.game = data.game ? new Game(data.game) : null; } - setup(data) { - /** - * The ID of the emoji - * @type {Snowflake} - */ - this.id = data.id; + update(data) { + this.status = data.status || this.status; + this.game = data.game ? new Game(data.game) : null; + } + /** + * Whether this presence is equal to another + * @param {Presence} presence The presence to compare with + * @returns {boolean} + */ + equals(presence) { + return this === presence || ( + presence && + this.status === presence.status && + this.game ? this.game.equals(presence.game) : !presence.game + ); + } +} + +/** + * Represents a game that is part of a user's presence. + */ +class Game { + constructor(data) { /** - * The name of the emoji + * The name of the game being played * @type {string} */ this.name = data.name; /** - * Whether or not this emoji requires colons surrounding it - * @type {boolean} + * The type of the game status + * @type {number} */ - this.requiresColons = data.require_colons; + this.type = data.type; /** - * Whether this emoji is managed by an external service - * @type {boolean} + * If the game is being streamed, a link to the stream + * @type {?string} */ - this.managed = data.managed; - - this._roles = data.roles; + this.url = data.url || null; } /** - * The timestamp the emoji was created at - * @type {number} + * Whether or not the game is being streamed + * @type {boolean} * @readonly */ - get createdTimestamp() { - return Snowflake.deconstruct(this.id).timestamp; + get streaming() { + return this.type === 1; } /** - * The time the emoji was created - * @type {Date} - * @readonly + * Whether this game is equal to another game + * @param {Game} game The game to compare with + * @returns {boolean} */ - get createdAt() { - return new Date(this.createdTimestamp); - } - - /** - * A collection of roles this emoji is active for (empty if all), mapped by role ID - * @type {Collection} - * @readonly - */ - get roles() { - const roles = new Collection(); - for (const role of this._roles) { - if (this.guild.roles.has(role)) roles.set(role, this.guild.roles.get(role)); - } - return roles; - } - - /** - * The URL to the emoji file - * @type {string} - * @readonly - */ - get url() { - return Constants.Endpoints.CDN(this.client.options.http.cdn).Emoji(this.id); - } - - /** - * The identifier of this emoji, used for message reactions - * @type {string} - * @readonly - */ - get identifier() { - if (this.id) return `${this.name}:${this.id}`; - return encodeURIComponent(this.name); - } - - /** - * Data for editing an emoji. - * @typedef {Object} EmojiEditData - * @property {string} [name] The name of the emoji - * @property {Collection|Array} [roles] Roles to restrict emoji to - */ - - /** - * Edits the emoji. - * @param {EmojiEditData} data The new data for the emoji - * @param {string} [reason] Reason for editing this emoji - * @returns {Promise} - * @example - * // Edit a emoji - * emoji.edit({name: 'newemoji'}) - * .then(e => console.log(`Edited emoji ${e}`)) - * .catch(console.error); - */ - edit(data, reason) { - return this.client.api.guilds[this.guild.id].emojis[this.id] - .patch({ data: { - name: data.name, - roles: data.roles ? data.roles.map(r => r.id ? r.id : r) : [], - }, reason }) - .then(() => this); - } - - /** - * Set the name of the emoji. - * @param {string} name The new name for the emoji - * @returns {Promise} - */ - setName(name) { - return this.edit({ name }); - } - - /** - * Add a role to the list of roles that can use this emoji. - * @param {Role} role The role to add - * @returns {Promise} - */ - addRestrictedRole(role) { - return this.addRestrictedRoles([role]); - } - - /** - * Add multiple roles to the list of roles that can use this emoji. - * @param {Role[]} roles Roles to add - * @returns {Promise} - */ - addRestrictedRoles(roles) { - const newRoles = new Collection(this.roles); - for (const role of roles) { - if (this.guild.roles.has(role.id)) newRoles.set(role.id, role); - } - return this.edit({ roles: newRoles }); - } - - /** - * Remove a role from the list of roles that can use this emoji. - * @param {Role} role The role to remove - * @returns {Promise} - */ - removeRestrictedRole(role) { - return this.removeRestrictedRoles([role]); - } - - /** - * Remove multiple roles from the list of roles that can use this emoji. - * @param {Role[]} roles Roles to remove - * @returns {Promise} - */ - removeRestrictedRoles(roles) { - const newRoles = new Collection(this.roles); - for (const role of roles) { - if (newRoles.has(role.id)) newRoles.delete(role.id); - } - return this.edit({ roles: newRoles }); - } - - /** - * When concatenated with a string, this automatically returns the emoji mention rather than the object. - * @returns {string} - * @example - * // Send an emoji: - * const emoji = guild.emojis.first(); - * msg.reply(`Hello! ${emoji}`); - */ - toString() { - return this.requiresColons ? `<:${this.name}:${this.id}>` : this.name; - } - - /** - * Whether this emoji is the same as another one. - * @param {Emoji|Object} other The emoji to compare it to - * @returns {boolean} Whether the emoji is equal to the given emoji or not - */ - equals(other) { - if (other instanceof Emoji) { - return ( - other.id === this.id && - other.name === this.name && - other.managed === this.managed && - other.requiresColons === this.requiresColons - ); - } else { - return ( - other.id === this.id && - other.name === this.name - ); - } + equals(game) { + return this === game || ( + game && + this.name === game.name && + this.type === game.type && + this.url === game.url + ); } } -module.exports = Emoji; +exports.Presence = Presence; +exports.Game = Game; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +const path = __webpack_require__(21); +const Util = __webpack_require__(4); +const Embed = __webpack_require__(30); + +/** + * Represents a webhook. + */ +class Webhook { + constructor(client, dataOrID, token) { + if (client) { + /** + * The client that instantiated the webhook + * @name Webhook#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + if (dataOrID) this.setup(dataOrID); + } else { + this.id = dataOrID; + this.token = token; + Object.defineProperty(this, 'client', { value: this }); + } + } + + setup(data) { + /** + * The name of the webhook + * @type {string} + */ + this.name = data.name; + + /** + * The token for the webhook + * @type {string} + */ + this.token = data.token; + + /** + * The avatar for the webhook + * @type {string} + */ + this.avatar = data.avatar; + + /** + * The ID of the webhook + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The guild the webhook belongs to + * @type {Snowflake} + */ + this.guildID = data.guild_id; + + /** + * The channel the webhook belongs to + * @type {Snowflake} + */ + this.channelID = data.channel_id; + + if (data.user) { + /** + * The owner of the webhook + * @type {?User|Object} + */ + this.owner = this.client.users ? this.client.users.get(data.user.id) : data.user; + } else { + this.owner = null; + } + } + + /** + * Options that can be passed into send. + * @typedef {Object} WebhookMessageOptions + * @property {string} [username=this.name] Username override for the message + * @property {string} [avatarURL] Avatar URL override for the message + * @property {boolean} [tts=false] Whether or not the message should be spoken aloud + * @property {string} [nonce=''] The nonce for the message + * @property {Object[]} [embeds] An array of embeds for the message + * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details) + * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here + * should be replaced with plain-text + * @property {FileOptions|string} [file] A file to send with the message + * @property {FileOptions[]|string[]} [files] Files to send with the message + * @property {string|boolean} [code] Language for optional codeblock formatting to apply + * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if + * it exceeds the character limit. If an object is provided, these are the options for splitting the message. + */ + + /** + * Send a message with this webhook. + * @param {StringResolvable} [content] The content to send + * @param {WebhookMessageOptions} [options={}] The options to provide + * @returns {Promise} + * @example + * // Send a message + * webhook.send('hello!') + * .then(message => console.log(`Sent message: ${message.content}`)) + * .catch(console.error); + */ + send(content, options) { + if (!options && typeof content === 'object' && !(content instanceof Array)) { + options = content; + content = ''; + } else if (!options) { + options = {}; + } + + if (!options.username) options.username = this.name; + + if (options.avatarURL) { + options.avatar_url = options.avatarURL; + options.avatarURL = null; + } + + if (typeof content !== 'undefined') content = Util.resolveString(content); + if (content) { + if (options.disableEveryone || + (typeof options.disableEveryone === 'undefined' && this.client.options.disableEveryone) + ) { + content = content.replace(/@(everyone|here)/g, '@\u200b$1'); + } + } + options.content = content; + + if (options.embeds) options.embeds = options.embeds.map(embed => new Embed(embed)._apiTransform()); + + if (options.file) { + if (options.files) options.files.push(options.file); + else options.files = [options.file]; + } + + if (options.files) { + for (let i = 0; i < options.files.length; i++) { + let file = options.files[i]; + if (typeof file === 'string') file = { attachment: file }; + if (!file.name) { + if (typeof file.attachment === 'string') { + file.name = path.basename(file.attachment); + } else if (file.attachment && file.attachment.path) { + file.name = path.basename(file.attachment.path); + } else { + file.name = 'file.jpg'; + } + } + options.files[i] = file; + } + + return Promise.all(options.files.map(file => + this.client.resolver.resolveBuffer(file.attachment).then(buffer => { + file.file = buffer; + return file; + }) + )).then(files => this.client.api.webhooks.opts(this.id, this.token).post({ + data: options, + query: { wait: true }, + files, + auth: false, + })); + } + + return this.client.api.webhooks.opts(this.id, this.token).post({ + data: options, + query: { wait: true }, + auth: false, + }).then(data => { + if (!this.client.channels) return data; + const Message = __webpack_require__(10); + return new Message(this.client.channels.get(data.channel_id), data, this.client); + }); + } + + /** + * Send a raw slack message with this webhook. + * @param {Object} body The raw body to send + * @returns {Promise} + * @example + * // Send a slack message + * webhook.sendSlackMessage({ + * 'username': 'Wumpus', + * 'attachments': [{ + * 'pretext': 'this looks pretty cool', + * 'color': '#F0F', + * 'footer_icon': 'http://snek.s3.amazonaws.com/topSnek.png', + * 'footer': 'Powered by sneks', + * 'ts': Date.now() / 1000 + * }] + * }).catch(console.error); + */ + sendSlackMessage(body) { + return this.client.api.webhooks.opts(this.id, this.token).slack.post({ + query: { wait: true }, + auth: false, + data: body, + }).then(data => { + if (!this.client.channels) return data; + const Message = __webpack_require__(10); + return new Message(this.client.channels.get(data.channel_id), data, this.client); + }); + } + + /** + * Edit the webhook. + * @param {Object} options Options + * @param {string} [options.name] New name for this webhook + * @param {BufferResolvable} [options.avatar] New avatar for this webhook + * @param {string} [reason] Reason for editing this webhook + * @returns {Promise} + */ + edit({ name = this.name, avatar }, reason) { + if (avatar && (typeof avatar === 'string' && !avatar.startsWith('data:'))) { + return this.client.resolver.resolveBuffer(avatar).then(file => { + const dataURI = this.client.resolver.resolveBase64(file); + return this.edit({ name, avatar: dataURI }, reason); + }); + } + return this.client.api.webhooks.opts(this.id, this.token).patch({ + data: { name, avatar }, + reason, + }).then(data => { + this.name = data.name; + this.avatar = data.avatar; + return this; + }); + } + + /** + * Delete the webhook. + * @param {string} [reason] Reason for deleting this webhook + * @returns {Promise} + */ + delete(reason) { + return this.client.api.webhooks.opts(this.id, this.token).delete({ reason }); + } +} + +module.exports = Webhook; /***/ }), /* 19 */ /***/ (function(module, exports, __webpack_require__) { -const Long = __webpack_require__(36); -const User = __webpack_require__(13); -const Role = __webpack_require__(21); -const Emoji = __webpack_require__(18); -const Invite = __webpack_require__(28); -const GuildAuditLogs = __webpack_require__(48); -const Webhook = __webpack_require__(22); -const { Presence } = __webpack_require__(14); -const GuildMember = __webpack_require__(20); -const VoiceRegion = __webpack_require__(73); +exports = module.exports = __webpack_require__(46); +exports.Stream = exports; +exports.Readable = exports; +exports.Writable = __webpack_require__(36); +exports.Duplex = __webpack_require__(14); +exports.Transform = __webpack_require__(50); +exports.PassThrough = __webpack_require__(86); + + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(Buffer) {// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) + +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(process) {// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); +}; + + +// path.relative(from, to) +// posix version +exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +}; + +exports.sep = '/'; +exports.delimiter = ':'; + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +function filter (xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = 'ab'.substr(-1) === 'b' + ? function (str, start, len) { return str.substr(start, len) } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + } +; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + +const Long = __webpack_require__(40); +const User = __webpack_require__(15); +const Role = __webpack_require__(26); +const Emoji = __webpack_require__(24); +const Invite = __webpack_require__(31); +const GuildAuditLogs = __webpack_require__(67); +const Webhook = __webpack_require__(18); +const { Presence } = __webpack_require__(17); +const GuildMember = __webpack_require__(25); +const VoiceRegion = __webpack_require__(68); const Constants = __webpack_require__(0); const Collection = __webpack_require__(3); const Util = __webpack_require__(4); -const Snowflake = __webpack_require__(7); -const Permissions = __webpack_require__(10); -const Shared = __webpack_require__(74); +const Snowflake = __webpack_require__(9); +const Permissions = __webpack_require__(11); +const Shared = __webpack_require__(60); const { Error, TypeError } = __webpack_require__(5); /** @@ -5864,14 +6290,14 @@ class Guild { */ fetchInvites() { return this.client.api.guilds[this.id].invites.get() - .then(inviteItems => { - const invites = new Collection(); - for (const inviteItem of inviteItems) { - const invite = new Invite(this.client, inviteItem); - invites.set(invite.code, invite); - } - return invites; - }); + .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; + }); } /** @@ -5960,10 +6386,10 @@ class Guild { if (!user) return Promise.reject(new Error('User is not cached. Use Client.fetchUser first.')); if (this.members.has(user.id)) return Promise.resolve(this.members.get(user.id)); return this.client.api.guilds[this.id].members[user.id].get() - .then(data => { - if (cache) return this.client.actions.GuildMemberGet.handle(this, data).member; - else return new GuildMember(this, data); - }); + .then(data => { + if (cache) return this.client.actions.GuildMemberGet.handle(this, data).member; + else return new GuildMember(this, data); + }); } /** @@ -6064,7 +6490,7 @@ class Guild { _data.explicit_content_filter = Number(data.explicitContentFilter); } return this.client.api.guilds[this.id].patch({ data: _data, reason }) - .then(newData => this.client.actions.GuildUpdate.handle(newData).updated); + .then(newData => this.client.actions.GuildUpdate.handle(newData).updated); } /** @@ -6209,11 +6635,11 @@ class Guild { */ acknowledge() { return this.client.api.guilds[this.id].ack - .post({ data: { token: this.client.rest._ackToken } }) - .then(res => { - if (res.token) this.client.rest._ackToken = res.token; - return this; - }); + .post({ data: { token: this.client.rest._ackToken } }) + .then(res => { + if (res.token) this.client.rest._ackToken = res.token; + return this; + }); } /** @@ -6248,15 +6674,15 @@ class Guild { const id = this.client.resolver.resolveUserID(user); if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.')); return this.client.api.guilds[this.id].bans[id].put({ query: options }) - .then(() => { - if (user instanceof GuildMember) return user; - const _user = this.client.resolver.resolveUser(id); - if (_user) { - const member = this.client.resolver.resolveGuildMember(this, _user); - return member || _user; - } - return id; - }); + .then(() => { + if (user instanceof GuildMember) return user; + const _user = this.client.resolver.resolveUser(id); + if (_user) { + const member = this.client.resolver.resolveGuildMember(this, _user); + return member || _user; + } + return id; + }); } /** @@ -6435,10 +6861,10 @@ class Guild { .then(emoji => this.client.actions.GuildEmojiCreate.handle(this, emoji).emoji); } else { return this.client.resolver.resolveBuffer(attachment) - .then(data => { - const dataURI = this.client.resolver.resolveBase64(data); - return this.createEmoji(dataURI, name, roles); - }); + .then(data => { + const dataURI = this.client.resolver.resolveBase64(data); + return this.createEmoji(dataURI, name, roles); + }); } } @@ -6450,7 +6876,7 @@ class Guild { deleteEmoji(emoji) { if (!(emoji instanceof Emoji)) emoji = this.emojis.get(emoji); return this.client.api.guilds(this.id).emojis[emoji.id].delete() - .then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data); + .then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data); } /** @@ -6465,7 +6891,7 @@ class Guild { leave() { if (this.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.')); return this.client.api.users['@me'].guilds[this.id].delete() - .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); + .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); } /** @@ -6479,7 +6905,7 @@ class Guild { */ delete() { return this.client.api.guilds[this.id].delete() - .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); + .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); } /** @@ -6637,12 +7063,12 @@ class Guild { updatedRoles = updatedRoles.map((r, i) => ({ id: r.id, position: i })); return this.client.api.guilds[this.id].roles.patch({ data: updatedRoles }) - .then(() => - this.client.actions.GuildRolesPositionUpdate.handle({ - guild_id: this.id, - roles: updatedRoles, - }).guild - ); + .then(() => + this.client.actions.GuildRolesPositionUpdate.handle({ + guild_id: this.id, + roles: updatedRoles, + }).guild + ); } /** @@ -6667,12 +7093,12 @@ class Guild { updatedChannels = updatedChannels.map((r, i) => ({ id: r.id, position: i })); return this.client.api.guilds[this.id].channels.patch({ data: updatedChannels }) - .then(() => - this.client.actions.GuildChannelsPositionUpdate.handle({ - guild_id: this.id, - roles: updatedChannels, - }).guild - ); + .then(() => + this.client.actions.GuildChannelsPositionUpdate.handle({ + guild_id: this.id, + roles: updatedChannels, + }).guild + ); } /** @@ -6699,8 +7125,8 @@ class Guild { _sortPositionWithID(collection) { return collection.sort((a, b) => a.position !== b.position ? - a.position - b.position : - Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber() + a.position - b.position : + Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber() ); } } @@ -6709,14 +7135,656 @@ module.exports = Guild; /***/ }), -/* 20 */ +/* 23 */ /***/ (function(module, exports, __webpack_require__) { -const TextBasedChannel = __webpack_require__(25); -const Role = __webpack_require__(21); -const Permissions = __webpack_require__(10); +const path = __webpack_require__(21); +const MessageCollector = __webpack_require__(59); +const Shared = __webpack_require__(60); const Collection = __webpack_require__(3); -const { Presence } = __webpack_require__(14); +const Snowflake = __webpack_require__(9); +const { Error, RangeError, TypeError } = __webpack_require__(5); + +/** + * Interface for classes that have text-channel-like features. + * @interface + */ +class TextBasedChannel { + constructor() { + /** + * A collection containing the messages sent to this channel + * @type {Collection} + */ + this.messages = new Collection(); + + /** + * The ID of the last message in the channel, if one was sent + * @type {?Snowflake} + */ + this.lastMessageID = null; + + /** + * The Message object of the last message in the channel, if one was sent + * @type {?Message} + */ + this.lastMessage = null; + } + + /** + * Options provided when sending or editing a message. + * @typedef {Object} MessageOptions + * @property {boolean} [tts=false] Whether or not the message should be spoken aloud + * @property {string} [nonce=''] The nonce for the message + * @property {MessageEmbed|Object} [embed] An embed for the message + * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details) + * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here + * should be replaced with plain-text + * @property {FileOptions[]|string[]} [files] Files to send with the message + * @property {string|boolean} [code] Language for optional codeblock formatting to apply + * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if + * it exceeds the character limit. If an object is provided, these are the options for splitting the message + * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs) + */ + + /** + * @typedef {Object} FileOptions + * @property {BufferResolvable} attachment File to attach + * @property {string} [name='file.jpg'] Filename of the attachment + */ + + /** + * Options for splitting a message. + * @typedef {Object} SplitOptions + * @property {number} [maxLength=1950] Maximum character length per message piece + * @property {string} [char='\n'] Character to split the message with + * @property {string} [prepend=''] Text to prepend to every piece except the first + * @property {string} [append=''] Text to append to every piece except the last + */ + + /** + * Send a message to this channel. + * @param {StringResolvable} [content] Text for the message + * @param {MessageOptions} [options={}] Options for the message + * @returns {Promise} + * @example + * // Send a message + * channel.send('hello!') + * .then(message => console.log(`Sent message: ${message.content}`)) + * .catch(console.error); + */ + send(content, options) { + if (!options && typeof content === 'object' && !(content instanceof Array)) { + options = content; + content = ''; + } else if (!options) { + options = {}; + } + + if (!options.content) options.content = content; + + if (options.embed && options.embed.file) { + if (options.files) options.files.push(options.embed.file); + else options.files = [options.embed.file]; + } + + if (options.files) { + for (let i = 0; i < options.files.length; i++) { + let file = options.files[i]; + if (typeof file === 'string') file = { attachment: file }; + if (!file.name) { + if (typeof file.attachment === 'string') { + file.name = path.basename(file.attachment); + } else if (file.attachment && file.attachment.path) { + file.name = path.basename(file.attachment.path); + } else { + file.name = 'file.jpg'; + } + } + options.files[i] = file; + } + + return Promise.all(options.files.map(file => + this.client.resolver.resolveBuffer(file.attachment).then(buffer => { + file.file = buffer; + return file; + }) + )).then(files => { + options.files = files; + return Shared.sendMessage(this, options); + }); + } + + return Shared.sendMessage(this, options); + } + + /** + * Gets a single message from this channel, regardless of it being cached or not. Since the single message fetching + * endpoint is reserved for bot accounts, this abstracts the `fetchMessages` method to obtain the single message when + * using a user account. + * @param {Snowflake} messageID ID of the message to get + * @returns {Promise} + * @example + * // Get message + * channel.fetchMessage('99539446449315840') + * .then(message => console.log(message.content)) + * .catch(console.error); + */ + fetchMessage(messageID) { + const Message = __webpack_require__(10); + if (!this.client.user.bot) { + return this.fetchMessages({ limit: 1, around: messageID }) + .then(messages => { + const msg = messages.get(messageID); + if (!msg) throw new Error('MESSAGE_MISSING'); + return msg; + }); + } + return this.client.api.channels[this.id].messages[messageID].get() + .then(data => { + const msg = data instanceof Message ? data : new Message(this, data, this.client); + this._cacheMessage(msg); + return msg; + }); + } + + /** + * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and + * `after` are mutually exclusive. All the parameters are optional. + * @typedef {Object} ChannelLogsQueryOptions + * @property {number} [limit=50] Number of messages to acquire + * @property {Snowflake} [before] ID of a message to get the messages that were posted before it + * @property {Snowflake} [after] ID of a message to get the messages that were posted after it + * @property {Snowflake} [around] ID of a message to get the messages that were posted around it + */ + + /** + * Gets the past messages sent in this channel. Resolves with a collection mapping message ID's to Message objects. + * @param {ChannelLogsQueryOptions} [options={}] Query parameters to pass in + * @returns {Promise>} + * @example + * // Get messages + * channel.fetchMessages({limit: 10}) + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + */ + fetchMessages(options = {}) { + const Message = __webpack_require__(10); + return this.client.api.channels[this.id].messages.get({ query: options }) + .then(data => { + const messages = new Collection(); + for (const message of data) { + const msg = new Message(this, message, this.client); + messages.set(message.id, msg); + this._cacheMessage(msg); + } + return messages; + }); + } + + /** + * Fetches the pinned messages of this channel and returns a collection of them. + * @returns {Promise>} + */ + fetchPinnedMessages() { + const Message = __webpack_require__(10); + return this.client.api.channels[this.id].pins.get().then(data => { + const messages = new Collection(); + for (const message of data) { + const msg = new Message(this, message, this.client); + messages.set(message.id, msg); + this._cacheMessage(msg); + } + return messages; + }); + } + + /** + * Performs a search within the channel. + * This is only available when using a user account. + * @param {MessageSearchOptions} [options={}] Options to pass to the search + * @returns {Promise} + * @example + * channel.search({ + * content: 'discord.js', + * before: '2016-11-17' + * }).then(res => { + * const hit = res.results[0].find(m => m.hit).content; + * console.log(`I found: **${hit}**, total results: ${res.total}`); + * }).catch(console.error); + */ + search(options = {}) { + return Shared.search(this, options); + } + + /** + * Starts a typing indicator in the channel. + * @param {number} [count] The number of times startTyping should be considered to have been called + * @example + * // Start typing in a channel + * channel.startTyping(); + */ + startTyping(count) { + if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT'); + if (!this.client.user._typing.has(this.id)) { + const endpoint = this.client.api.channels[this.id].typing; + this.client.user._typing.set(this.id, { + count: count || 1, + interval: this.client.setInterval(() => { + endpoint.post(); + }, 9000), + }); + endpoint.post(); + } else { + const entry = this.client.user._typing.get(this.id); + entry.count = count || entry.count + 1; + } + } + + /** + * Stops the typing indicator in the channel. + * The indicator will only stop if this is called as many times as startTyping(). + * It can take a few seconds for the client user to stop typing. + * @param {boolean} [force=false] Whether or not to reset the call count and force the indicator to stop + * @example + * // Stop typing in a channel + * channel.stopTyping(); + * @example + * // Force typing to fully stop in a channel + * channel.stopTyping(true); + */ + stopTyping(force = false) { + if (this.client.user._typing.has(this.id)) { + const entry = this.client.user._typing.get(this.id); + entry.count--; + if (entry.count <= 0 || force) { + this.client.clearInterval(entry.interval); + this.client.user._typing.delete(this.id); + } + } + } + + /** + * Whether or not the typing indicator is being shown in the channel + * @type {boolean} + * @readonly + */ + get typing() { + return this.client.user._typing.has(this.id); + } + + /** + * Number of times `startTyping` has been called + * @type {number} + * @readonly + */ + get typingCount() { + if (this.client.user._typing.has(this.id)) return this.client.user._typing.get(this.id).count; + return 0; + } + + /** + * Creates a Message Collector. + * @param {CollectorFilter} filter The filter to create the collector with + * @param {MessageCollectorOptions} [options={}] The options to pass to the collector + * @returns {MessageCollector} + * @example + * // Create a message collector + * const collector = channel.createMessageCollector( + * m => m.content.includes('discord'), + * { time: 15000 } + * ); + * collector.on('collect', m => console.log(`Collected ${m.content}`)); + * collector.on('end', collected => console.log(`Collected ${collected.size} items`)); + */ + createMessageCollector(filter, options = {}) { + return new MessageCollector(this, filter, options); + } + + /** + * An object containing the same properties as CollectorOptions, but a few more: + * @typedef {MessageCollectorOptions} AwaitMessagesOptions + * @property {string[]} [errors] Stop/end reasons that cause the promise to reject + */ + + /** + * Similar to createCollector but in promise form. Resolves with a collection of messages that pass the specified + * filter. + * @param {CollectorFilter} filter The filter function to use + * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector + * @returns {Promise>} + * @example + * // Await !vote messages + * const filter = m => m.content.startsWith('!vote'); + * // Errors: ['time'] treats ending because of the time limit as an error + * channel.awaitMessages(filter, { max: 4, time: 60000, errors: ['time'] }) + * .then(collected => console.log(collected.size)) + * .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`)); + */ + awaitMessages(filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createMessageCollector(filter, options); + collector.once('end', (collection, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(collection); + } else { + resolve(collection); + } + }); + }); + } + + /** + * Bulk delete given messages that are newer than two weeks. + * This is only available when using a bot account. + * @param {Collection|Message[]|number} messages Messages or number of messages to delete + * @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically + * @returns {Promise>} Deleted messages + */ + bulkDelete(messages, filterOld = false) { + if (!isNaN(messages)) return this.fetchMessages({ limit: messages }).then(msgs => this.bulkDelete(msgs, filterOld)); + if (messages instanceof Array || messages instanceof Collection) { + let messageIDs = messages instanceof Collection ? messages.keyArray() : messages.map(m => m.id); + if (filterOld) { + messageIDs = messageIDs.filter(id => + Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000 + ); + } + return this.client.api.channels[this.id].messages['bulk-delete'] + .post({ data: { messages: messageIDs } }) + .then(() => + this.client.actions.MessageDeleteBulk.handle({ + channel_id: this.id, + ids: messageIDs, + }).messages + ); + } + throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); + } + + /** + * Marks all messages in this channel as read. + * This is only available when using a user account. + * @returns {Promise} + */ + acknowledge() { + if (!this.lastMessageID) return Promise.resolve(this); + return this.client.api.channels[this.id].messages[this.lastMessageID].ack + .post({ data: { token: this.client.rest._ackToken } }) + .then(res => { + if (res.token) this.client.rest._ackToken = res.token; + return this; + }); + } + + _cacheMessage(message) { + const maxSize = this.client.options.messageCacheMaxSize; + if (maxSize === 0) return null; + if (this.messages.size >= maxSize && maxSize > 0) this.messages.delete(this.messages.firstKey()); + this.messages.set(message.id, message); + return message; + } +} + +exports.applyToClass = (structure, full = false, ignore = []) => { + const props = ['send']; + if (full) { + props.push( + '_cacheMessage', + 'acknowledge', + 'fetchMessages', + 'fetchMessage', + 'search', + 'bulkDelete', + 'startTyping', + 'stopTyping', + 'typing', + 'typingCount', + 'fetchPinnedMessages', + 'createMessageCollector', + 'awaitMessages' + ); + } + for (const prop of props) { + if (ignore.includes(prop)) continue; + Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop)); + } +}; + + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +const Constants = __webpack_require__(0); +const Collection = __webpack_require__(3); +const Snowflake = __webpack_require__(9); + +/** + * Represents a custom emoji. + */ +class Emoji { + constructor(guild, data) { + /** + * The client that instantiated this object + * @name Emoji#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: guild.client }); + + /** + * The guild this emoji is part of + * @type {Guild} + */ + this.guild = guild; + + this.setup(data); + } + + setup(data) { + /** + * The ID of the emoji + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The name of the emoji + * @type {string} + */ + this.name = data.name; + + /** + * Whether or not this emoji requires colons surrounding it + * @type {boolean} + */ + this.requiresColons = data.require_colons; + + /** + * Whether this emoji is managed by an external service + * @type {boolean} + */ + this.managed = data.managed; + + this._roles = data.roles; + } + + /** + * The timestamp the emoji was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the emoji was created + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * A collection of roles this emoji is active for (empty if all), mapped by role ID + * @type {Collection} + * @readonly + */ + get roles() { + const roles = new Collection(); + for (const role of this._roles) { + if (this.guild.roles.has(role)) roles.set(role, this.guild.roles.get(role)); + } + return roles; + } + + /** + * The URL to the emoji file + * @type {string} + * @readonly + */ + get url() { + return Constants.Endpoints.CDN(this.client.options.http.cdn).Emoji(this.id); + } + + /** + * The identifier of this emoji, used for message reactions + * @type {string} + * @readonly + */ + get identifier() { + if (this.id) return `${this.name}:${this.id}`; + return encodeURIComponent(this.name); + } + + /** + * Data for editing an emoji. + * @typedef {Object} EmojiEditData + * @property {string} [name] The name of the emoji + * @property {Collection|Array} [roles] Roles to restrict emoji to + */ + + /** + * Edits the emoji. + * @param {EmojiEditData} data The new data for the emoji + * @param {string} [reason] Reason for editing this emoji + * @returns {Promise} + * @example + * // Edit a emoji + * emoji.edit({name: 'newemoji'}) + * .then(e => console.log(`Edited emoji ${e}`)) + * .catch(console.error); + */ + edit(data, reason) { + return this.client.api.guilds[this.guild.id].emojis[this.id] + .patch({ data: { + name: data.name, + roles: data.roles ? data.roles.map(r => r.id ? r.id : r) : [], + }, reason }) + .then(() => this); + } + + /** + * Set the name of the emoji. + * @param {string} name The new name for the emoji + * @returns {Promise} + */ + setName(name) { + return this.edit({ name }); + } + + /** + * Add a role to the list of roles that can use this emoji. + * @param {Role} role The role to add + * @returns {Promise} + */ + addRestrictedRole(role) { + return this.addRestrictedRoles([role]); + } + + /** + * Add multiple roles to the list of roles that can use this emoji. + * @param {Role[]} roles Roles to add + * @returns {Promise} + */ + addRestrictedRoles(roles) { + const newRoles = new Collection(this.roles); + for (const role of roles) { + if (this.guild.roles.has(role.id)) newRoles.set(role.id, role); + } + return this.edit({ roles: newRoles }); + } + + /** + * Remove a role from the list of roles that can use this emoji. + * @param {Role} role The role to remove + * @returns {Promise} + */ + removeRestrictedRole(role) { + return this.removeRestrictedRoles([role]); + } + + /** + * Remove multiple roles from the list of roles that can use this emoji. + * @param {Role[]} roles Roles to remove + * @returns {Promise} + */ + removeRestrictedRoles(roles) { + const newRoles = new Collection(this.roles); + for (const role of roles) { + if (newRoles.has(role.id)) newRoles.delete(role.id); + } + return this.edit({ roles: newRoles }); + } + + /** + * When concatenated with a string, this automatically returns the emoji mention rather than the object. + * @returns {string} + * @example + * // Send an emoji: + * const emoji = guild.emojis.first(); + * msg.reply(`Hello! ${emoji}`); + */ + toString() { + return this.requiresColons ? `<:${this.name}:${this.id}>` : this.name; + } + + /** + * Whether this emoji is the same as another one. + * @param {Emoji|Object} other The emoji to compare it to + * @returns {boolean} Whether the emoji is equal to the given emoji or not + */ + equals(other) { + if (other instanceof Emoji) { + return ( + other.id === this.id && + other.name === this.name && + other.managed === this.managed && + other.requiresColons === this.requiresColons + ); + } else { + return ( + other.id === this.id && + other.name === this.name + ); + } + } +} + +module.exports = Emoji; + + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + +const TextBasedChannel = __webpack_require__(23); +const Role = __webpack_require__(26); +const Permissions = __webpack_require__(11); +const Collection = __webpack_require__(3); +const { Presence } = __webpack_require__(17); const { Error } = __webpack_require__(5); /** @@ -7199,12 +8267,12 @@ class GuildMember { */ kick(reason) { return this.client.api.guilds[this.guild.id].members[this.user.id].delete({ reason }) - .then(() => - this.client.actions.GuildMemberRemove.handle({ - guild_id: this.guild.id, - user: this.user, - }).member - ); + .then(() => + this.client.actions.GuildMemberRemove.handle({ + guild_id: this.guild.id, + user: this.user, + }).member + ); } /** @@ -7244,11 +8312,11 @@ module.exports = GuildMember; /***/ }), -/* 21 */ +/* 26 */ /***/ (function(module, exports, __webpack_require__) { -const Snowflake = __webpack_require__(7); -const Permissions = __webpack_require__(10); +const Snowflake = __webpack_require__(9); +const Permissions = __webpack_require__(11); const Util = __webpack_require__(4); /** @@ -7461,7 +8529,7 @@ class Role { }, reason, }) - .then(role => this.client.actions.GuildRoleUpdate.handle({ role, guild_id: this.guild.id }).updated); + .then(role => this.client.actions.GuildRoleUpdate.handle({ role, guild_id: this.guild.id }).updated); } /** @@ -7561,9 +8629,9 @@ class Role { */ delete(reason) { return this.client.api.guilds[this.guild.id].roles[this.id].delete({ reason }) - .then(() => - this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id }).role - ); + .then(() => + this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id }).role + ); } /** @@ -7610,796 +8678,64 @@ module.exports = Role; /***/ }), -/* 22 */ +/* 27 */ /***/ (function(module, exports, __webpack_require__) { -const path = __webpack_require__(31); -const Util = __webpack_require__(4); -const Embed = __webpack_require__(29); +"use strict"; +/* WEBPACK VAR INJECTION */(function(process) { -/** - * Represents a webhook. - */ -class Webhook { - constructor(client, dataOrID, token) { - if (client) { - /** - * The client that instantiated the webhook - * @name Webhook#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - if (dataOrID) this.setup(dataOrID); - } else { - this.id = dataOrID; - this.token = token; - Object.defineProperty(this, 'client', { value: this }); - } +if (!process.version || + process.version.indexOf('v0.') === 0 || + process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { + module.exports = nextTick; +} else { + module.exports = process.nextTick; +} + +function nextTick(fn, arg1, arg2, arg3) { + if (typeof fn !== 'function') { + throw new TypeError('"callback" argument must be a function'); } - - setup(data) { - /** - * The name of the webhook - * @type {string} - */ - this.name = data.name; - - /** - * The token for the webhook - * @type {string} - */ - this.token = data.token; - - /** - * The avatar for the webhook - * @type {string} - */ - this.avatar = data.avatar; - - /** - * The ID of the webhook - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The guild the webhook belongs to - * @type {Snowflake} - */ - this.guildID = data.guild_id; - - /** - * The channel the webhook belongs to - * @type {Snowflake} - */ - this.channelID = data.channel_id; - - if (data.user) { - /** - * The owner of the webhook - * @type {?User|Object} - */ - this.owner = this.client.users ? this.client.users.get(data.user.id) : data.user; - } else { - this.owner = null; - } - } - - /** - * Options that can be passed into send. - * @typedef {Object} WebhookMessageOptions - * @property {string} [username=this.name] Username override for the message - * @property {string} [avatarURL] Avatar URL override for the message - * @property {boolean} [tts=false] Whether or not the message should be spoken aloud - * @property {string} [nonce=''] The nonce for the message - * @property {Object[]} [embeds] An array of embeds for the message - * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details) - * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here - * should be replaced with plain-text - * @property {FileOptions|string} [file] A file to send with the message - * @property {FileOptions[]|string[]} [files] Files to send with the message - * @property {string|boolean} [code] Language for optional codeblock formatting to apply - * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if - * it exceeds the character limit. If an object is provided, these are the options for splitting the message. - */ - - /** - * Send a message with this webhook. - * @param {StringResolvable} [content] The content to send - * @param {WebhookMessageOptions} [options={}] The options to provide - * @returns {Promise} - * @example - * // Send a message - * webhook.send('hello!') - * .then(message => console.log(`Sent message: ${message.content}`)) - * .catch(console.error); - */ - send(content, options) { - if (!options && typeof content === 'object' && !(content instanceof Array)) { - options = content; - content = ''; - } else if (!options) { - options = {}; - } - - if (!options.username) options.username = this.name; - - if (options.avatarURL) { - options.avatar_url = options.avatarURL; - options.avatarURL = null; - } - - if (typeof content !== 'undefined') content = Util.resolveString(content); - if (content) { - if (options.disableEveryone || - (typeof options.disableEveryone === 'undefined' && this.client.options.disableEveryone) - ) { - content = content.replace(/@(everyone|here)/g, '@\u200b$1'); - } - } - options.content = content; - - if (options.embeds) options.embeds = options.embeds.map(embed => new Embed(embed)._apiTransform()); - - if (options.file) { - if (options.files) options.files.push(options.file); - else options.files = [options.file]; - } - - if (options.files) { - for (let i = 0; i < options.files.length; i++) { - let file = options.files[i]; - if (typeof file === 'string') file = { attachment: file }; - if (!file.name) { - if (typeof file.attachment === 'string') { - file.name = path.basename(file.attachment); - } else if (file.attachment && file.attachment.path) { - file.name = path.basename(file.attachment.path); - } else { - file.name = 'file.jpg'; - } - } - options.files[i] = file; - } - - return Promise.all(options.files.map(file => - this.client.resolver.resolveBuffer(file.attachment).then(buffer => { - file.file = buffer; - return file; - }) - )).then(files => this.client.api.webhooks.opts(this.id, this.token).post({ - data: options, - query: { wait: true }, - files, - auth: false, - })); - } - - return this.client.api.webhooks.opts(this.id, this.token).post({ - data: options, - query: { wait: true }, - auth: false, - }).then(data => { - if (!this.client.channels) return data; - const Message = __webpack_require__(9); - return new Message(this.client.channels.get(data.channel_id), data, this.client); + var len = arguments.length; + var args, i; + switch (len) { + case 0: + case 1: + return process.nextTick(fn); + case 2: + return process.nextTick(function afterTickOne() { + fn.call(null, arg1); }); - } - - /** - * Send a raw slack message with this webhook. - * @param {Object} body The raw body to send - * @returns {Promise} - * @example - * // Send a slack message - * webhook.sendSlackMessage({ - * 'username': 'Wumpus', - * 'attachments': [{ - * 'pretext': 'this looks pretty cool', - * 'color': '#F0F', - * 'footer_icon': 'http://snek.s3.amazonaws.com/topSnek.png', - * 'footer': 'Powered by sneks', - * 'ts': Date.now() / 1000 - * }] - * }).catch(console.error); - */ - sendSlackMessage(body) { - return this.client.api.webhooks.opts(this.id, this.token).slack.post({ - query: { wait: true }, - auth: false, - data: body, - }).then(data => { - if (!this.client.channels) return data; - const Message = __webpack_require__(9); - return new Message(this.client.channels.get(data.channel_id), data, this.client); + case 3: + return process.nextTick(function afterTickTwo() { + fn.call(null, arg1, arg2); }); - } - - /** - * Edit the webhook. - * @param {Object} options Options - * @param {string} [options.name] New name for this webhook - * @param {BufferResolvable} [options.avatar] New avatar for this webhook - * @param {string} [reason] Reason for editing this webhook - * @returns {Promise} - */ - edit({ name = this.name, avatar }, reason) { - if (avatar && (typeof avatar === 'string' && !avatar.startsWith('data:'))) { - return this.client.resolver.resolveBuffer(avatar).then(file => { - const dataURI = this.client.resolver.resolveBase64(file); - return this.edit({ name, avatar: dataURI }, reason); - }); + case 4: + return process.nextTick(function afterTickThree() { + fn.call(null, arg1, arg2, arg3); + }); + default: + args = new Array(len - 1); + i = 0; + while (i < args.length) { + args[i++] = arguments[i]; } - return this.client.api.webhooks.opts(this.id, this.token).patch({ - data: { name, avatar }, - reason, - }).then(data => { - this.name = data.name; - this.avatar = data.avatar; - return this; + return process.nextTick(function afterTick() { + fn.apply(null, args); }); } - - /** - * Delete the webhook. - * @param {string} [reason] Reason for deleting this webhook - * @returns {Promise} - */ - delete(reason) { - return this.client.api.webhooks.opts(this.id, this.token).delete({ reason }); - } } -module.exports = Webhook; +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) + +/***/ }), +/* 28 */ +/***/ (function(module, exports) { + /***/ }), -/* 23 */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(Buffer) {// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. - -function isArray(arg) { - if (Array.isArray) { - return Array.isArray(arg); - } - return objectToString(arg) === '[object Array]'; -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = Buffer.isBuffer; - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) - -/***/ }), -/* 24 */ -/***/ (function(module, exports, __webpack_require__) { - -exports = module.exports = __webpack_require__(60); -exports.Stream = exports; -exports.Readable = exports; -exports.Writable = __webpack_require__(39); -exports.Duplex = __webpack_require__(16); -exports.Transform = __webpack_require__(61); -exports.PassThrough = __webpack_require__(89); - - -/***/ }), -/* 25 */ -/***/ (function(module, exports, __webpack_require__) { - -const path = __webpack_require__(31); -const MessageCollector = __webpack_require__(50); -const Shared = __webpack_require__(74); -const Collection = __webpack_require__(3); -const Snowflake = __webpack_require__(7); -const { Error, RangeError, TypeError } = __webpack_require__(5); - -/** - * Interface for classes that have text-channel-like features. - * @interface - */ -class TextBasedChannel { - constructor() { - /** - * A collection containing the messages sent to this channel - * @type {Collection} - */ - this.messages = new Collection(); - - /** - * The ID of the last message in the channel, if one was sent - * @type {?Snowflake} - */ - this.lastMessageID = null; - - /** - * The Message object of the last message in the channel, if one was sent - * @type {?Message} - */ - this.lastMessage = null; - } - - /** - * Options provided when sending or editing a message. - * @typedef {Object} MessageOptions - * @property {boolean} [tts=false] Whether or not the message should be spoken aloud - * @property {string} [nonce=''] The nonce for the message - * @property {MessageEmbed|Object} [embed] An embed for the message - * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details) - * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here - * should be replaced with plain-text - * @property {FileOptions[]|string[]} [files] Files to send with the message - * @property {string|boolean} [code] Language for optional codeblock formatting to apply - * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if - * it exceeds the character limit. If an object is provided, these are the options for splitting the message - * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs) - */ - - /** - * @typedef {Object} FileOptions - * @property {BufferResolvable} attachment File to attach - * @property {string} [name='file.jpg'] Filename of the attachment - */ - - /** - * Options for splitting a message. - * @typedef {Object} SplitOptions - * @property {number} [maxLength=1950] Maximum character length per message piece - * @property {string} [char='\n'] Character to split the message with - * @property {string} [prepend=''] Text to prepend to every piece except the first - * @property {string} [append=''] Text to append to every piece except the last - */ - - /** - * Send a message to this channel. - * @param {StringResolvable} [content] Text for the message - * @param {MessageOptions} [options={}] Options for the message - * @returns {Promise} - * @example - * // Send a message - * channel.send('hello!') - * .then(message => console.log(`Sent message: ${message.content}`)) - * .catch(console.error); - */ - send(content, options) { - if (!options && typeof content === 'object' && !(content instanceof Array)) { - options = content; - content = ''; - } else if (!options) { - options = {}; - } - - if (!options.content) options.content = content; - - if (options.embed && options.embed.file) { - if (options.files) options.files.push(options.embed.file); - else options.files = [options.embed.file]; - } - - if (options.files) { - for (let i = 0; i < options.files.length; i++) { - let file = options.files[i]; - if (typeof file === 'string') file = { attachment: file }; - if (!file.name) { - if (typeof file.attachment === 'string') { - file.name = path.basename(file.attachment); - } else if (file.attachment && file.attachment.path) { - file.name = path.basename(file.attachment.path); - } else { - file.name = 'file.jpg'; - } - } - options.files[i] = file; - } - - return Promise.all(options.files.map(file => - this.client.resolver.resolveBuffer(file.attachment).then(buffer => { - file.file = buffer; - return file; - }) - )).then(files => { - options.files = files; - return Shared.sendMessage(this, options); - }); - } - - return Shared.sendMessage(this, options); - } - - /** - * Gets a single message from this channel, regardless of it being cached or not. Since the single message fetching - * endpoint is reserved for bot accounts, this abstracts the `fetchMessages` method to obtain the single message when - * using a user account. - * @param {Snowflake} messageID ID of the message to get - * @returns {Promise} - * @example - * // Get message - * channel.fetchMessage('99539446449315840') - * .then(message => console.log(message.content)) - * .catch(console.error); - */ - fetchMessage(messageID) { - const Message = __webpack_require__(9); - if (!this.client.user.bot) { - return this.fetchMessages({ limit: 1, around: messageID }) - .then(messages => { - const msg = messages.get(messageID); - if (!msg) throw new Error('MESSAGE_MISSING'); - return msg; - }); - } - return this.client.api.channels[this.id].messages[messageID].get() - .then(data => { - const msg = data instanceof Message ? data : new Message(this, data, this.client); - this._cacheMessage(msg); - return msg; - }); - } - - /** - * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and - * `after` are mutually exclusive. All the parameters are optional. - * @typedef {Object} ChannelLogsQueryOptions - * @property {number} [limit=50] Number of messages to acquire - * @property {Snowflake} [before] ID of a message to get the messages that were posted before it - * @property {Snowflake} [after] ID of a message to get the messages that were posted after it - * @property {Snowflake} [around] ID of a message to get the messages that were posted around it - */ - - /** - * Gets the past messages sent in this channel. Resolves with a collection mapping message ID's to Message objects. - * @param {ChannelLogsQueryOptions} [options={}] Query parameters to pass in - * @returns {Promise>} - * @example - * // Get messages - * channel.fetchMessages({limit: 10}) - * .then(messages => console.log(`Received ${messages.size} messages`)) - * .catch(console.error); - */ - fetchMessages(options = {}) { - const Message = __webpack_require__(9); - return this.client.api.channels[this.id].messages.get({ query: options }) - .then(data => { - const messages = new Collection(); - for (const message of data) { - const msg = new Message(this, message, this.client); - messages.set(message.id, msg); - this._cacheMessage(msg); - } - return messages; - }); - } - - /** - * Fetches the pinned messages of this channel and returns a collection of them. - * @returns {Promise>} - */ - fetchPinnedMessages() { - const Message = __webpack_require__(9); - return this.client.api.channels[this.id].pins.get().then(data => { - const messages = new Collection(); - for (const message of data) { - const msg = new Message(this, message, this.client); - messages.set(message.id, msg); - this._cacheMessage(msg); - } - return messages; - }); - } - - /** - * Performs a search within the channel. - * This is only available when using a user account. - * @param {MessageSearchOptions} [options={}] Options to pass to the search - * @returns {Promise} - * @example - * channel.search({ - * content: 'discord.js', - * before: '2016-11-17' - * }).then(res => { - * const hit = res.results[0].find(m => m.hit).content; - * console.log(`I found: **${hit}**, total results: ${res.total}`); - * }).catch(console.error); - */ - search(options = {}) { - return Shared.search(this, options); - } - - /** - * Starts a typing indicator in the channel. - * @param {number} [count] The number of times startTyping should be considered to have been called - * @example - * // Start typing in a channel - * channel.startTyping(); - */ - startTyping(count) { - if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT'); - if (!this.client.user._typing.has(this.id)) { - const endpoint = this.client.api.channels[this.id].typing; - this.client.user._typing.set(this.id, { - count: count || 1, - interval: this.client.setInterval(() => { - endpoint.post(); - }, 9000), - }); - endpoint.post(); - } else { - const entry = this.client.user._typing.get(this.id); - entry.count = count || entry.count + 1; - } - } - - /** - * Stops the typing indicator in the channel. - * The indicator will only stop if this is called as many times as startTyping(). - * It can take a few seconds for the client user to stop typing. - * @param {boolean} [force=false] Whether or not to reset the call count and force the indicator to stop - * @example - * // Stop typing in a channel - * channel.stopTyping(); - * @example - * // Force typing to fully stop in a channel - * channel.stopTyping(true); - */ - stopTyping(force = false) { - if (this.client.user._typing.has(this.id)) { - const entry = this.client.user._typing.get(this.id); - entry.count--; - if (entry.count <= 0 || force) { - this.client.clearInterval(entry.interval); - this.client.user._typing.delete(this.id); - } - } - } - - /** - * Whether or not the typing indicator is being shown in the channel - * @type {boolean} - * @readonly - */ - get typing() { - return this.client.user._typing.has(this.id); - } - - /** - * Number of times `startTyping` has been called - * @type {number} - * @readonly - */ - get typingCount() { - if (this.client.user._typing.has(this.id)) return this.client.user._typing.get(this.id).count; - return 0; - } - - /** - * Creates a Message Collector. - * @param {CollectorFilter} filter The filter to create the collector with - * @param {MessageCollectorOptions} [options={}] The options to pass to the collector - * @returns {MessageCollector} - * @example - * // Create a message collector - * const collector = channel.createMessageCollector( - * m => m.content.includes('discord'), - * { time: 15000 } - * ); - * collector.on('collect', m => console.log(`Collected ${m.content}`)); - * collector.on('end', collected => console.log(`Collected ${collected.size} items`)); - */ - createMessageCollector(filter, options = {}) { - return new MessageCollector(this, filter, options); - } - - /** - * An object containing the same properties as CollectorOptions, but a few more: - * @typedef {MessageCollectorOptions} AwaitMessagesOptions - * @property {string[]} [errors] Stop/end reasons that cause the promise to reject - */ - - /** - * Similar to createCollector but in promise form. Resolves with a collection of messages that pass the specified - * filter. - * @param {CollectorFilter} filter The filter function to use - * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector - * @returns {Promise>} - * @example - * // Await !vote messages - * const filter = m => m.content.startsWith('!vote'); - * // Errors: ['time'] treats ending because of the time limit as an error - * channel.awaitMessages(filter, { max: 4, time: 60000, errors: ['time'] }) - * .then(collected => console.log(collected.size)) - * .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`)); - */ - awaitMessages(filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createMessageCollector(filter, options); - collector.once('end', (collection, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(collection); - } else { - resolve(collection); - } - }); - }); - } - - /** - * Bulk delete given messages that are newer than two weeks. - * This is only available when using a bot account. - * @param {Collection|Message[]|number} messages Messages or number of messages to delete - * @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically - * @returns {Promise>} Deleted messages - */ - bulkDelete(messages, filterOld = false) { - if (!isNaN(messages)) return this.fetchMessages({ limit: messages }).then(msgs => this.bulkDelete(msgs, filterOld)); - if (messages instanceof Array || messages instanceof Collection) { - let messageIDs = messages instanceof Collection ? messages.keyArray() : messages.map(m => m.id); - if (filterOld) { - messageIDs = messageIDs.filter(id => - Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000 - ); - } - return this.client.api.channels[this.id].messages['bulk-delete'] - .post({ data: { messages: messageIDs } }) - .then(() => - this.client.actions.MessageDeleteBulk.handle({ - channel_id: this.id, - ids: messageIDs, - }).messages - ); - } - throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); - } - - /** - * Marks all messages in this channel as read. - * This is only available when using a user account. - * @returns {Promise} - */ - acknowledge() { - if (!this.lastMessageID) return Promise.resolve(this); - return this.client.api.channels[this.id].messages[this.lastMessageID].ack - .post({ data: { token: this.client.rest._ackToken } }) - .then(res => { - if (res.token) this.client.rest._ackToken = res.token; - return this; - }); - } - - _cacheMessage(message) { - const maxSize = this.client.options.messageCacheMaxSize; - if (maxSize === 0) return null; - if (this.messages.size >= maxSize && maxSize > 0) this.messages.delete(this.messages.firstKey()); - this.messages.set(message.id, message); - return message; - } -} - -exports.applyToClass = (structure, full = false, ignore = []) => { - const props = ['send']; - if (full) { - props.push( - '_cacheMessage', - 'acknowledge', - 'fetchMessages', - 'fetchMessage', - 'search', - 'bulkDelete', - 'startTyping', - 'stopTyping', - 'typing', - 'typingCount', - 'fetchPinnedMessages', - 'createMessageCollector', - 'awaitMessages' - ); - } - for (const prop of props) { - if (ignore.includes(prop)) continue; - Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop)); - } -}; - - -/***/ }), -/* 26 */ +/* 29 */ /***/ (function(module, exports) { exports.endianness = function () { return 'LE' }; @@ -8450,554 +8786,7 @@ exports.EOL = '\n'; /***/ }), -/* 27 */ -/***/ (function(module, exports, __webpack_require__) { - -const Channel = __webpack_require__(17); -const Role = __webpack_require__(21); -const Invite = __webpack_require__(28); -const PermissionOverwrites = __webpack_require__(55); -const Permissions = __webpack_require__(10); -const Collection = __webpack_require__(3); - -/** - * Represents a guild channel (i.e. text channels and voice channels). - * @extends {Channel} - */ -class GuildChannel extends Channel { - constructor(guild, data) { - super(guild.client, data); - - /** - * The guild the channel is in - * @type {Guild} - */ - this.guild = guild; - } - - setup(data) { - super.setup(data); - - /** - * The name of the guild channel - * @type {string} - */ - this.name = data.name; - - /** - * The position of the channel in the list - * @type {number} - */ - this.position = data.position; - - /** - * A map of permission overwrites in this channel for roles and users - * @type {Collection} - */ - this.permissionOverwrites = new Collection(); - if (data.permission_overwrites) { - for (const overwrite of data.permission_overwrites) { - this.permissionOverwrites.set(overwrite.id, new PermissionOverwrites(this, overwrite)); - } - } - } - - /** - * The position of the channel - * @type {number} - * @readonly - */ - get calculatedPosition() { - const sorted = this.guild._sortedChannels(this.type); - return sorted.array().indexOf(sorted.get(this.id)); - } - - /** - * Gets the overall set of permissions for a user in this channel, taking into account roles and permission - * overwrites. - * @param {GuildMemberResolvable} member The user that you want to obtain the overall permissions for - * @returns {?Permissions} - */ - permissionsFor(member) { - member = this.client.resolver.resolveGuildMember(this.guild, member); - if (!member) return null; - if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL); - - let permissions = 0; - - const roles = member.roles; - for (const role of roles.values()) permissions |= role.permissions; - - const overwrites = this.overwritesFor(member, true, roles); - - if (overwrites.everyone) { - permissions &= ~overwrites.everyone._denied; - permissions |= overwrites.everyone._allowed; - } - - let allow = 0; - for (const overwrite of overwrites.roles) { - permissions &= ~overwrite._denied; - allow |= overwrite._allowed; - } - permissions |= allow; - - if (overwrites.member) { - permissions &= ~overwrites.member._denied; - permissions |= overwrites.member._allowed; - } - - const admin = Boolean(permissions & Permissions.FLAGS.ADMINISTRATOR); - if (admin) permissions = Permissions.ALL; - - return new Permissions(permissions); - } - - overwritesFor(member, verified = false, roles = null) { - if (!verified) member = this.client.resolver.resolveGuildMember(this.guild, member); - if (!member) return []; - - roles = roles || member.roles; - const roleOverwrites = []; - let memberOverwrites; - let everyoneOverwrites; - - for (const overwrite of this.permissionOverwrites.values()) { - if (overwrite.id === this.guild.id) { - everyoneOverwrites = overwrite; - } else if (roles.has(overwrite.id)) { - roleOverwrites.push(overwrite); - } else if (overwrite.id === member.id) { - memberOverwrites = overwrite; - } - } - - return { - everyone: everyoneOverwrites, - roles: roleOverwrites, - member: memberOverwrites, - }; - } - - /** - * An object mapping permission flags to `true` (enabled) or `false` (disabled). - * ```js - * { - * 'SEND_MESSAGES': true, - * 'ATTACH_FILES': false, - * } - * ``` - * @typedef {Object} PermissionOverwriteOptions - */ - - /** - * Overwrites the permissions for a user or role in this channel. - * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update - * @param {PermissionOverwriteOptions} options The configuration for the update - * @param {string} [reason] Reason for creating/editing this overwrite - * @returns {Promise} - * @example - * // Overwrite permissions for a message author - * message.channel.overwritePermissions(message.author, { - * SEND_MESSAGES: false - * }) - * .then(() => console.log('Done!')) - * .catch(console.error); - */ - overwritePermissions(userOrRole, options, reason) { - const payload = { - allow: 0, - deny: 0, - }; - - if (userOrRole instanceof Role) { - payload.type = 'role'; - } else if (this.guild.roles.has(userOrRole)) { - userOrRole = this.guild.roles.get(userOrRole); - payload.type = 'role'; - } else { - userOrRole = this.client.resolver.resolveUser(userOrRole); - payload.type = 'member'; - if (!userOrRole) return Promise.reject(new TypeError('Supplied parameter was neither a User nor a Role.')); - } - - payload.id = userOrRole.id; - - const prevOverwrite = this.permissionOverwrites.get(userOrRole.id); - - if (prevOverwrite) { - payload.allow = prevOverwrite._allowed; - payload.deny = prevOverwrite._denied; - } - - for (const perm in options) { - if (options[perm] === true) { - payload.allow |= Permissions.FLAGS[perm] || 0; - payload.deny &= ~(Permissions.FLAGS[perm] || 0); - } else if (options[perm] === false) { - payload.allow &= ~(Permissions.FLAGS[perm] || 0); - payload.deny |= Permissions.FLAGS[perm] || 0; - } else if (options[perm] === null) { - payload.allow &= ~(Permissions.FLAGS[perm] || 0); - payload.deny &= ~(Permissions.FLAGS[perm] || 0); - } - } - - return this.client.api.channels[this.id].permissions[payload.id] - .put({ data: payload, reason }) - .then(() => this); - } - - /** - * The data for a guild channel. - * @typedef {Object} ChannelData - * @property {string} [name] The name of the channel - * @property {number} [position] The position of the channel - * @property {string} [topic] The topic of the text channel - * @property {number} [bitrate] The bitrate of the voice channel - * @property {number} [userLimit] The user limit of the channel - */ - - /** - * Edits the channel. - * @param {ChannelData} data The new data for the channel - * @param {string} [reason] Reason for editing this channel - * @returns {Promise} - * @example - * // Edit a channel - * channel.edit({name: 'new-channel'}) - * .then(c => console.log(`Edited channel ${c}`)) - * .catch(console.error); - */ - edit(data, reason) { - return this.client.api.channels[this.id].patch({ - data: { - name: (data.name || this.name).trim(), - topic: data.topic || this.topic, - position: data.position || this.position, - bitrate: data.bitrate || this.bitrate, - user_limit: data.userLimit || this.userLimit, - }, - reason, - }).then(newData => this.client.actions.ChannelUpdate.handle(newData).updated); - } - - /** - * Set a new name for the guild channel. - * @param {string} name The new name for the guild channel - * @returns {Promise} - * @example - * // Set a new channel name - * channel.setName('not_general') - * .then(newChannel => console.log(`Channel's new name is ${newChannel.name}`)) - * .catch(console.error); - */ - setName(name) { - return this.edit({ name }); - } - - /** - * Set a new position for the guild channel. - * @param {number} position The new position for the guild channel - * @param {boolean} [relative=false] Move the position relative to its current value - * @returns {Promise} - * @example - * // Set a new channel position - * channel.setPosition(2) - * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) - * .catch(console.error); - */ - setPosition(position, relative) { - return this.guild.setChannelPosition(this, position, relative).then(() => this); - } - - /** - * Set a new topic for the guild channel. - * @param {string} topic The new topic for the guild channel - * @returns {Promise} - * @example - * // Set a new channel topic - * channel.setTopic('needs more rate limiting') - * .then(newChannel => console.log(`Channel's new topic is ${newChannel.topic}`)) - * .catch(console.error); - */ - setTopic(topic) { - return this.edit({ topic }); - } - - /** - * Options given when creating a guild channel invite. - * @typedef {Object} InviteOptions - - */ - - /** - * Create an invite to this guild channel. - * @param {InviteOptions} [options={}] Options for the invite - * @param {boolean} [options.temporary=false] Whether members that joined via the invite should be automatically - * kicked after 24 hours if they have not yet received a role - * @param {number} [options.maxAge=86400] How long the invite should last (in seconds, 0 for forever) - * @param {number} [options.maxUses=0] Maximum number of uses - * @param {string} [options.reason] Reason for creating this - * @returns {Promise} - */ - createInvite({ temporary = false, maxAge = 86400, maxUses = 0, reason } = {}) { - return this.client.api.channels[this.id].invites.post({ data: { - temporary, max_age: maxAge, max_uses: maxUses, - }, reason }) - .then(invite => new Invite(this.client, invite)); - } - - /** - * Clone this channel. - * @param {string} [name=this.name] Optional name for the new channel, otherwise it has the name of this channel - * @param {boolean} [withPermissions=true] Whether to clone the channel with this channel's permission overwrites - * @param {boolean} [withTopic=true] Whether to clone the channel with this channel's topic - * @returns {Promise} - */ - clone(name = this.name, withPermissions = true, withTopic = true) { - return this.guild.createChannel(name, this.type, { overwrites: withPermissions ? this.permissionOverwrites : [] }) - .then(channel => withTopic ? channel.setTopic(this.topic) : channel); - } - - /** - * Checks if this channel has the same type, topic, position, name, overwrites and ID as another channel. - * In most cases, a simple `channel.id === channel2.id` will do, and is much faster too. - * @param {GuildChannel} channel Channel to compare with - * @returns {boolean} - */ - equals(channel) { - let equal = channel && - this.id === channel.id && - this.type === channel.type && - this.topic === channel.topic && - this.position === channel.position && - this.name === channel.name; - - if (equal) { - if (this.permissionOverwrites && channel.permissionOverwrites) { - equal = this.permissionOverwrites.equals(channel.permissionOverwrites); - } else { - equal = !this.permissionOverwrites && !channel.permissionOverwrites; - } - } - - return equal; - } - - /** - * Whether the channel is deletable by the client user - * @type {boolean} - * @readonly - */ - get deletable() { - return this.id !== this.guild.id && - this.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS); - } - - /** - * Deletes this channel. - * @param {string} [reason] Reason for deleting this channel - * @returns {Promise} - * @example - * // Delete the channel - * channel.delete('making room for new channels') - * .then() // Success - * .catch(console.error); // Log error - */ - delete(reason) { - return this.client.api.channels[this.id].delete({ reason }).then(() => this); - } - - /** - * When concatenated with a string, this automatically returns the channel's mention instead of the Channel object. - * @returns {string} - * @example - * // Outputs: Hello from #general - * console.log(`Hello from ${channel}`); - * @example - * // Outputs: Hello from #general - * console.log('Hello from ' + channel); - */ - toString() { - return `<#${this.id}>`; - } -} - -module.exports = GuildChannel; - - -/***/ }), -/* 28 */ -/***/ (function(module, exports, __webpack_require__) { - -const PartialGuild = __webpack_require__(53); -const PartialGuildChannel = __webpack_require__(54); -const Constants = __webpack_require__(0); - -/** - * Represents an invitation to a guild channel. - * The only guaranteed properties are `code`, `guild` and `channel`. Other properties can be missing. - */ -class Invite { - constructor(client, data) { - /** - * The client that instantiated the invite - * @name Invite#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - - this.setup(data); - } - - setup(data) { - /** - * The guild the invite is for. If this guild is already known, this will be a guild object. If the guild is - * unknown, this will be a PartialGuild object - * @type {Guild|PartialGuild} - */ - this.guild = this.client.guilds.get(data.guild.id) || new PartialGuild(this.client, data.guild); - - /** - * The code for this invite - * @type {string} - */ - this.code = data.code; - - /** - * The approximate number of online members of the guild this invite is for - * @type {number} - */ - this.presenceCount = data.approximate_presence_count; - - /** - * The approximate total number of members of the guild this invite is for - * @type {number} - */ - this.memberCount = data.approximate_member_count; - - /** - * The number of text channels the guild this invite goes to has - * @type {number} - */ - this.textChannelCount = data.guild.text_channel_count; - - /** - * The number of voice channels the guild this invite goes to has - * @type {number} - */ - this.voiceChannelCount = data.guild.voice_channel_count; - - /** - * Whether or not this invite is temporary - * @type {boolean} - */ - this.temporary = data.temporary; - - /** - * The maximum age of the invite, in seconds - * @type {?number} - */ - this.maxAge = data.max_age; - - /** - * How many times this invite has been used - * @type {number} - */ - this.uses = data.uses; - - /** - * The maximum uses of this invite - * @type {number} - */ - this.maxUses = data.max_uses; - - if (data.inviter) { - /** - * The user who created this invite - * @type {User} - */ - this.inviter = this.client.dataManager.newUser(data.inviter); - } - - /** - * The channel the invite is for. If this channel is already known, this will be a GuildChannel object. - * If the channel is unknown, this will be a PartialGuildChannel object. - * @type {GuildChannel|PartialGuildChannel} - */ - this.channel = this.client.channels.get(data.channel.id) || new PartialGuildChannel(this.client, data.channel); - - /** - * The timestamp the invite was created at - * @type {number} - */ - this.createdTimestamp = new Date(data.created_at).getTime(); - } - - /** - * The time the invite was created - * @type {Date} - * @readonly - */ - get createdAt() { - return new Date(this.createdTimestamp); - } - - /** - * The timestamp the invite will expire at - * @type {number} - * @readonly - */ - get expiresTimestamp() { - return this.createdTimestamp + (this.maxAge * 1000); - } - - /** - * The time the invite will expire - * @type {Date} - * @readonly - */ - get expiresAt() { - return new Date(this.expiresTimestamp); - } - - /** - * The URL to the invite - * @type {string} - * @readonly - */ - get url() { - return Constants.Endpoints.invite(this.code); - } - - /** - * Deletes this invite. - * @param {string} [reason] Reason for deleting this invite - * @returns {Promise} - */ - delete(reason) { - return this.client.api.invites[this.code].delete({ reason }).then(() => this); - } - - /** - * When concatenated with a string, this automatically concatenates the invite's URL instead of the object. - * @returns {string} - * @example - * // Logs: Invite: https://discord.gg/A1b2C3 - * console.log(`Invite: ${invite}`); - */ - toString() { - return this.url; - } -} - -module.exports = Invite; - - -/***/ }), -/* 29 */ +/* 30 */ /***/ (function(module, exports, __webpack_require__) { const Util = __webpack_require__(4); @@ -9317,20 +9106,22 @@ module.exports = MessageEmbed; /***/ }), -/* 30 */ +/* 31 */ /***/ (function(module, exports, __webpack_require__) { -const Snowflake = __webpack_require__(7); +const PartialGuild = __webpack_require__(65); +const PartialGuildChannel = __webpack_require__(66); const Constants = __webpack_require__(0); /** - * Represents an OAuth2 Application. + * Represents an invitation to a guild channel. + * The only guaranteed properties are `code`, `guild` and `channel`. Other properties can be missing. */ -class OAuth2Application { +class Invite { constructor(client, data) { /** - * The client that instantiated the application - * @name OAuth2Application#client + * The client that instantiated the invite + * @name Invite#client * @type {Client} * @readonly */ @@ -9341,97 +9132,90 @@ class OAuth2Application { setup(data) { /** - * The ID of the app - * @type {Snowflake} + * The guild the invite is for. If this guild is already known, this will be a guild object. If the guild is + * unknown, this will be a PartialGuild object + * @type {Guild|PartialGuild} */ - this.id = data.id; + this.guild = this.client.guilds.get(data.guild.id) || new PartialGuild(this.client, data.guild); /** - * The name of the app + * The code for this invite * @type {string} */ - this.name = data.name; + this.code = data.code; /** - * The app's description - * @type {string} - */ - this.description = data.description; - - /** - * The app's icon hash - * @type {string} - */ - this.icon = data.icon; - - /** - * The app's RPC origins - * @type {?string[]} - */ - this.rpcOrigins = data.rpc_origins; - - /** - * The app's redirect URIs - * @type {string[]} - */ - this.redirectURIs = data.redirect_uris; - - /** - * If this app's bot requires a code grant when using the OAuth2 flow - * @type {boolean} - */ - this.botRequireCodeGrant = data.bot_require_code_grant; - - /** - * If this app's bot is public - * @type {boolean} - */ - this.botPublic = data.bot_public; - - /** - * If this app can use rpc - * @type {boolean} - */ - this.rpcApplicationState = data.rpc_application_state; - - /** - * Object containing basic info about this app's bot - * @type {Object} - */ - this.bot = data.bot; - - /** - * The flags for the app + * The approximate number of online members of the guild this invite is for * @type {number} */ - this.flags = data.flags; + this.presenceCount = data.approximate_presence_count; /** - * OAuth2 secret for the application + * The approximate total number of members of the guild this invite is for + * @type {number} + */ + this.memberCount = data.approximate_member_count; + + /** + * The number of text channels the guild this invite goes to has + * @type {number} + */ + this.textChannelCount = data.guild.text_channel_count; + + /** + * The number of voice channels the guild this invite goes to has + * @type {number} + */ + this.voiceChannelCount = data.guild.voice_channel_count; + + /** + * Whether or not this invite is temporary * @type {boolean} */ - this.secret = data.secret; + this.temporary = data.temporary; - if (data.owner) { + /** + * The maximum age of the invite, in seconds + * @type {?number} + */ + this.maxAge = data.max_age; + + /** + * How many times this invite has been used + * @type {number} + */ + this.uses = data.uses; + + /** + * The maximum uses of this invite + * @type {number} + */ + this.maxUses = data.max_uses; + + if (data.inviter) { /** - * The owner of this OAuth application - * @type {?User} + * The user who created this invite + * @type {User} */ - this.owner = this.client.dataManager.newUser(data.owner); + this.inviter = this.client.dataManager.newUser(data.inviter); } + + /** + * The channel the invite is for. If this channel is already known, this will be a GuildChannel object. + * If the channel is unknown, this will be a PartialGuildChannel object. + * @type {GuildChannel|PartialGuildChannel} + */ + this.channel = this.client.channels.get(data.channel.id) || new PartialGuildChannel(this.client, data.channel); + + /** + * The timestamp the invite was created at + * @type {number} + */ + this.createdTimestamp = new Date(data.created_at).getTime(); } /** - * The timestamp the app was created at - * @type {number} - * @readonly - */ - get createdTimestamp() { - return Snowflake.deconstruct(this.id).timestamp; - } - - /** - * The time the app was created + * The time the invite was created * @type {Date} * @readonly */ @@ -9440,54 +9224,451 @@ class OAuth2Application { } /** - * A link to the application's icon - * @param {Object} [options={}] Options for the icon url - * @param {string} [options.format='webp'] One of `webp`, `png`, `jpg` - * @param {number} [options.size=128] One of `128`, '256', `512`, `1024`, `2048` - * @returns {?string} URL to the icon + * The timestamp the invite will expire at + * @type {number} + * @readonly */ - iconURL({ format, size } = {}) { - if (!this.icon) return null; - return Constants.Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.icon, format, size); + get expiresTimestamp() { + return this.createdTimestamp + (this.maxAge * 1000); } /** - * Reset the app's secret. - * This is only available when using a user account. - * @returns {OAuth2Application} + * The time the invite will expire + * @type {Date} + * @readonly */ - resetSecret() { - return this.client.api.oauth2.applications[this.id].reset.post() - .then(app => new OAuth2Application(this.client, app)); + get expiresAt() { + return new Date(this.expiresTimestamp); } /** - * Reset the app's bot token. - * This is only available when using a user account. - * @returns {OAuth2Application} + * The URL to the invite + * @type {string} + * @readonly */ - resetToken() { - return this.client.api.oauth2.applications[this.id].bot.reset.post() - .then(app => new OAuth2Application(this.client, Object.assign({}, this, { bot: app }))); + get url() { + return Constants.Endpoints.invite(this.client.options.http.invite, this.code); } /** - * When concatenated with a string, this automatically concatenates the app name rather than the app object. + * Deletes this invite. + * @param {string} [reason] Reason for deleting this invite + * @returns {Promise} + */ + delete(reason) { + return this.client.api.invites[this.code].delete({ reason }).then(() => this); + } + + /** + * When concatenated with a string, this automatically concatenates the invite's URL instead of the object. * @returns {string} + * @example + * // Logs: Invite: https://discord.gg/A1b2C3 + * console.log(`Invite: ${invite}`); */ toString() { - return this.name; + return this.url; } } -module.exports = OAuth2Application; +module.exports = Invite; /***/ }), -/* 31 */ +/* 32 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(process) {// Copyright Joyent, Inc. and other Node contributors. +const Channel = __webpack_require__(16); +const Role = __webpack_require__(26); +const Invite = __webpack_require__(31); +const PermissionOverwrites = __webpack_require__(71); +const Permissions = __webpack_require__(11); +const Collection = __webpack_require__(3); + +/** + * Represents a guild channel (i.e. text channels and voice channels). + * @extends {Channel} + */ +class GuildChannel extends Channel { + constructor(guild, data) { + super(guild.client, data); + + /** + * The guild the channel is in + * @type {Guild} + */ + this.guild = guild; + } + + setup(data) { + super.setup(data); + + /** + * The name of the guild channel + * @type {string} + */ + this.name = data.name; + + /** + * The position of the channel in the list + * @type {number} + */ + this.position = data.position; + + /** + * A map of permission overwrites in this channel for roles and users + * @type {Collection} + */ + this.permissionOverwrites = new Collection(); + if (data.permission_overwrites) { + for (const overwrite of data.permission_overwrites) { + this.permissionOverwrites.set(overwrite.id, new PermissionOverwrites(this, overwrite)); + } + } + } + + /** + * The position of the channel + * @type {number} + * @readonly + */ + get calculatedPosition() { + const sorted = this.guild._sortedChannels(this.type); + return sorted.array().indexOf(sorted.get(this.id)); + } + + /** + * Gets the overall set of permissions for a user in this channel, taking into account roles and permission + * overwrites. + * @param {GuildMemberResolvable} member The user that you want to obtain the overall permissions for + * @returns {?Permissions} + */ + permissionsFor(member) { + member = this.client.resolver.resolveGuildMember(this.guild, member); + if (!member) return null; + if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL); + + let permissions = 0; + + const roles = member.roles; + for (const role of roles.values()) permissions |= role.permissions; + + const overwrites = this.overwritesFor(member, true, roles); + + if (overwrites.everyone) { + permissions &= ~overwrites.everyone._denied; + permissions |= overwrites.everyone._allowed; + } + + let allow = 0; + for (const overwrite of overwrites.roles) { + permissions &= ~overwrite._denied; + allow |= overwrite._allowed; + } + permissions |= allow; + + if (overwrites.member) { + permissions &= ~overwrites.member._denied; + permissions |= overwrites.member._allowed; + } + + const admin = Boolean(permissions & Permissions.FLAGS.ADMINISTRATOR); + if (admin) permissions = Permissions.ALL; + + return new Permissions(permissions); + } + + overwritesFor(member, verified = false, roles = null) { + if (!verified) member = this.client.resolver.resolveGuildMember(this.guild, member); + if (!member) return []; + + roles = roles || member.roles; + const roleOverwrites = []; + let memberOverwrites; + let everyoneOverwrites; + + for (const overwrite of this.permissionOverwrites.values()) { + if (overwrite.id === this.guild.id) { + everyoneOverwrites = overwrite; + } else if (roles.has(overwrite.id)) { + roleOverwrites.push(overwrite); + } else if (overwrite.id === member.id) { + memberOverwrites = overwrite; + } + } + + return { + everyone: everyoneOverwrites, + roles: roleOverwrites, + member: memberOverwrites, + }; + } + + /** + * An object mapping permission flags to `true` (enabled) or `false` (disabled). + * ```js + * { + * 'SEND_MESSAGES': true, + * 'ATTACH_FILES': false, + * } + * ``` + * @typedef {Object} PermissionOverwriteOptions + */ + + /** + * Overwrites the permissions for a user or role in this channel. + * @param {RoleResolvable|UserResolvable} userOrRole The user or role to update + * @param {PermissionOverwriteOptions} options The configuration for the update + * @param {string} [reason] Reason for creating/editing this overwrite + * @returns {Promise} + * @example + * // Overwrite permissions for a message author + * message.channel.overwritePermissions(message.author, { + * SEND_MESSAGES: false + * }) + * .then(() => console.log('Done!')) + * .catch(console.error); + */ + overwritePermissions(userOrRole, options, reason) { + const payload = { + allow: 0, + deny: 0, + }; + + if (userOrRole instanceof Role) { + payload.type = 'role'; + } else if (this.guild.roles.has(userOrRole)) { + userOrRole = this.guild.roles.get(userOrRole); + payload.type = 'role'; + } else { + userOrRole = this.client.resolver.resolveUser(userOrRole); + payload.type = 'member'; + if (!userOrRole) return Promise.reject(new TypeError('Supplied parameter was neither a User nor a Role.')); + } + + payload.id = userOrRole.id; + + const prevOverwrite = this.permissionOverwrites.get(userOrRole.id); + + if (prevOverwrite) { + payload.allow = prevOverwrite._allowed; + payload.deny = prevOverwrite._denied; + } + + for (const perm in options) { + if (options[perm] === true) { + payload.allow |= Permissions.FLAGS[perm] || 0; + payload.deny &= ~(Permissions.FLAGS[perm] || 0); + } else if (options[perm] === false) { + payload.allow &= ~(Permissions.FLAGS[perm] || 0); + payload.deny |= Permissions.FLAGS[perm] || 0; + } else if (options[perm] === null) { + payload.allow &= ~(Permissions.FLAGS[perm] || 0); + payload.deny &= ~(Permissions.FLAGS[perm] || 0); + } + } + + return this.client.api.channels[this.id].permissions[payload.id] + .put({ data: payload, reason }) + .then(() => this); + } + + /** + * The data for a guild channel. + * @typedef {Object} ChannelData + * @property {string} [name] The name of the channel + * @property {number} [position] The position of the channel + * @property {string} [topic] The topic of the text channel + * @property {number} [bitrate] The bitrate of the voice channel + * @property {number} [userLimit] The user limit of the channel + */ + + /** + * Edits the channel. + * @param {ChannelData} data The new data for the channel + * @param {string} [reason] Reason for editing this channel + * @returns {Promise} + * @example + * // Edit a channel + * channel.edit({name: 'new-channel'}) + * .then(c => console.log(`Edited channel ${c}`)) + * .catch(console.error); + */ + edit(data, reason) { + return this.client.api.channels[this.id].patch({ + data: { + name: (data.name || this.name).trim(), + topic: data.topic || this.topic, + position: data.position || this.position, + bitrate: data.bitrate || this.bitrate, + user_limit: data.userLimit || this.userLimit, + }, + reason, + }).then(newData => this.client.actions.ChannelUpdate.handle(newData).updated); + } + + /** + * Set a new name for the guild channel. + * @param {string} name The new name for the guild channel + * @returns {Promise} + * @example + * // Set a new channel name + * channel.setName('not_general') + * .then(newChannel => console.log(`Channel's new name is ${newChannel.name}`)) + * .catch(console.error); + */ + setName(name) { + return this.edit({ name }); + } + + /** + * Set a new position for the guild channel. + * @param {number} position The new position for the guild channel + * @param {boolean} [relative=false] Move the position relative to its current value + * @returns {Promise} + * @example + * // Set a new channel position + * channel.setPosition(2) + * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) + * .catch(console.error); + */ + setPosition(position, relative) { + return this.guild.setChannelPosition(this, position, relative).then(() => this); + } + + /** + * Set a new topic for the guild channel. + * @param {string} topic The new topic for the guild channel + * @returns {Promise} + * @example + * // Set a new channel topic + * channel.setTopic('needs more rate limiting') + * .then(newChannel => console.log(`Channel's new topic is ${newChannel.topic}`)) + * .catch(console.error); + */ + setTopic(topic) { + return this.edit({ topic }); + } + + /** + * Options given when creating a guild channel invite. + * @typedef {Object} InviteOptions + + */ + + /** + * Create an invite to this guild channel. + * @param {InviteOptions} [options={}] Options for the invite + * @param {boolean} [options.temporary=false] Whether members that joined via the invite should be automatically + * kicked after 24 hours if they have not yet received a role + * @param {number} [options.maxAge=86400] How long the invite should last (in seconds, 0 for forever) + * @param {number} [options.maxUses=0] Maximum number of uses + * @param {boolean} [options.unique=false] Create a unique invite, or use an existing one with similar settings + * @param {string} [options.reason] Reason for creating this + * @returns {Promise} + */ + createInvite({ temporary = false, maxAge = 86400, maxUses = 0, unique, reason } = {}) { + return this.client.api.channels[this.id].invites.post({ data: { + temporary, max_age: maxAge, max_uses: maxUses, unique, + }, reason }) + .then(invite => new Invite(this.client, invite)); + } + + /** + * Clone this channel. + * @param {string} [name=this.name] Optional name for the new channel, otherwise it has the name of this channel + * @param {boolean} [withPermissions=true] Whether to clone the channel with this channel's permission overwrites + * @param {boolean} [withTopic=true] Whether to clone the channel with this channel's topic + * @returns {Promise} + */ + clone(name = this.name, withPermissions = true, withTopic = true) { + return this.guild.createChannel(name, this.type, { overwrites: withPermissions ? this.permissionOverwrites : [] }) + .then(channel => withTopic ? channel.setTopic(this.topic) : channel); + } + + /** + * Checks if this channel has the same type, topic, position, name, overwrites and ID as another channel. + * In most cases, a simple `channel.id === channel2.id` will do, and is much faster too. + * @param {GuildChannel} channel Channel to compare with + * @returns {boolean} + */ + equals(channel) { + let equal = channel && + this.id === channel.id && + this.type === channel.type && + this.topic === channel.topic && + this.position === channel.position && + this.name === channel.name; + + if (equal) { + if (this.permissionOverwrites && channel.permissionOverwrites) { + equal = this.permissionOverwrites.equals(channel.permissionOverwrites); + } else { + equal = !this.permissionOverwrites && !channel.permissionOverwrites; + } + } + + return equal; + } + + /** + * Whether the channel is deletable by the client user + * @type {boolean} + * @readonly + */ + get deletable() { + return this.id !== this.guild.id && + this.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS); + } + + /** + * Deletes this channel. + * @param {string} [reason] Reason for deleting this channel + * @returns {Promise} + * @example + * // Delete the channel + * channel.delete('making room for new channels') + * .then() // Success + * .catch(console.error); // Log error + */ + delete(reason) { + return this.client.api.channels[this.id].delete({ reason }).then(() => this); + } + + /** + * When concatenated with a string, this automatically returns the channel's mention instead of the Channel object. + * @returns {string} + * @example + * // Outputs: Hello from #general + * console.log(`Hello from ${channel}`); + * @example + * // Outputs: Hello from #general + * console.log('Hello from ' + channel); + */ + toString() { + return `<#${this.id}>`; + } +} + +module.exports = GuildChannel; + + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +const Snekfetch = __webpack_require__(80); + +// Sync stuff might go here + +module.exports = Snekfetch; + + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the @@ -9508,214 +9689,1459 @@ module.exports = OAuth2Application; // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -// resolves . and .. elements in a path array with directory names there -// must be no slashes, empty elements, or device names (c:\) in the array -// (so also no leading and trailing slashes - it does not distinguish -// relative and absolute paths) -function normalizeArray(parts, allowAboveRoot) { - // if the path tries to go above the root, `up` ends up > 0 - var up = 0; - for (var i = parts.length - 1; i >= 0; i--) { - var last = parts[i]; - if (last === '.') { - parts.splice(i, 1); - } else if (last === '..') { - parts.splice(i, 1); - up++; - } else if (up) { - parts.splice(i, 1); - up--; - } - } +module.exports = Stream; - // if the path is allowed to go above the root, restore leading ..s - if (allowAboveRoot) { - for (; up--; up) { - parts.unshift('..'); - } - } +var EE = __webpack_require__(12).EventEmitter; +var inherits = __webpack_require__(13); - return parts; +inherits(Stream, EE); +Stream.Readable = __webpack_require__(19); +Stream.Writable = __webpack_require__(87); +Stream.Duplex = __webpack_require__(88); +Stream.Transform = __webpack_require__(89); +Stream.PassThrough = __webpack_require__(90); + +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; + + + +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. + +function Stream() { + EE.call(this); } -// Split a filename into [root, dir, basename, ext], unix version -// 'root' is just a slash, or nothing. -var splitPathRe = - /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; -var splitPath = function(filename) { - return splitPathRe.exec(filename).slice(1); -}; +Stream.prototype.pipe = function(dest, options) { + var source = this; -// path.resolve([from ...], to) -// posix version -exports.resolve = function() { - var resolvedPath = '', - resolvedAbsolute = false; - - for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - var path = (i >= 0) ? arguments[i] : process.cwd(); - - // Skip empty and invalid entries - if (typeof path !== 'string') { - throw new TypeError('Arguments to path.resolve must be strings'); - } else if (!path) { - continue; - } - - resolvedPath = path + '/' + resolvedPath; - resolvedAbsolute = path.charAt(0) === '/'; - } - - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.cwd() fails) - - // Normalize the path - resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { - return !!p; - }), !resolvedAbsolute).join('/'); - - return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; -}; - -// path.normalize(path) -// posix version -exports.normalize = function(path) { - var isAbsolute = exports.isAbsolute(path), - trailingSlash = substr(path, -1) === '/'; - - // Normalize the path - path = normalizeArray(filter(path.split('/'), function(p) { - return !!p; - }), !isAbsolute).join('/'); - - if (!path && !isAbsolute) { - path = '.'; - } - if (path && trailingSlash) { - path += '/'; - } - - return (isAbsolute ? '/' : '') + path; -}; - -// posix version -exports.isAbsolute = function(path) { - return path.charAt(0) === '/'; -}; - -// posix version -exports.join = function() { - var paths = Array.prototype.slice.call(arguments, 0); - return exports.normalize(filter(paths, function(p, index) { - if (typeof p !== 'string') { - throw new TypeError('Arguments to path.join must be strings'); - } - return p; - }).join('/')); -}; - - -// path.relative(from, to) -// posix version -exports.relative = function(from, to) { - from = exports.resolve(from).substr(1); - to = exports.resolve(to).substr(1); - - function trim(arr) { - var start = 0; - for (; start < arr.length; start++) { - if (arr[start] !== '') break; - } - - var end = arr.length - 1; - for (; end >= 0; end--) { - if (arr[end] !== '') break; - } - - if (start > end) return []; - return arr.slice(start, end - start + 1); - } - - var fromParts = trim(from.split('/')); - var toParts = trim(to.split('/')); - - var length = Math.min(fromParts.length, toParts.length); - var samePartsLength = length; - for (var i = 0; i < length; i++) { - if (fromParts[i] !== toParts[i]) { - samePartsLength = i; - break; + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } } } - var outputParts = []; - for (var i = samePartsLength; i < fromParts.length; i++) { - outputParts.push('..'); - } + source.on('data', ondata); - outputParts = outputParts.concat(toParts.slice(samePartsLength)); - - return outputParts.join('/'); -}; - -exports.sep = '/'; -exports.delimiter = ':'; - -exports.dirname = function(path) { - var result = splitPath(path), - root = result[0], - dir = result[1]; - - if (!root && !dir) { - // No dirname whatsoever - return '.'; - } - - if (dir) { - // It has a dirname, strip trailing slash - dir = dir.substr(0, dir.length - 1); - } - - return root + dir; -}; - - -exports.basename = function(path, ext) { - var f = splitPath(path)[2]; - // TODO: make this comparison case-insensitive on windows? - if (ext && f.substr(-1 * ext.length) === ext) { - f = f.substr(0, f.length - ext.length); - } - return f; -}; - - -exports.extname = function(path) { - return splitPath(path)[3]; -}; - -function filter (xs, f) { - if (xs.filter) return xs.filter(f); - var res = []; - for (var i = 0; i < xs.length; i++) { - if (f(xs[i], i, xs)) res.push(xs[i]); + function ondrain() { + if (source.readable && source.resume) { + source.resume(); } - return res; -} + } -// String.prototype.substr - negative index don't work in IE8 -var substr = 'ab'.substr(-1) === 'b' - ? function (str, start, len) { return str.substr(start, len) } - : function (str, start, len) { - if (start < 0) start = str.length + start; - return str.substr(start, len); + dest.on('drain', ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); + } + + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); + } + + + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, 'error') === 0) { + throw er; // Unhandled stream error in pipe. } -; + } + + source.on('error', onerror); + dest.on('error', onerror); + + // remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); + + source.removeListener('end', onend); + source.removeListener('close', onclose); + + source.removeListener('error', onerror); + dest.removeListener('error', onerror); + + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); + + dest.removeListener('close', cleanup); + } + + source.on('end', cleanup); + source.on('close', cleanup); + + dest.on('close', cleanup); + + dest.emit('pipe', source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) /***/ }), -/* 32 */ +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + +/* eslint-disable node/no-deprecated-api */ +var buffer = __webpack_require__(6) +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(process, setImmediate, global) {// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. + + + +/**/ + +var processNextTick = __webpack_require__(27); +/**/ + +module.exports = Writable; + +/* */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} + +// It seems a linked list but it is not +// there will be only 2 of these for each stream +function CorkedRequest(state) { + var _this = this; + + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ + +/**/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick; +/**/ + +/**/ +var Duplex; +/**/ + +Writable.WritableState = WritableState; + +/**/ +var util = __webpack_require__(20); +util.inherits = __webpack_require__(13); +/**/ + +/**/ +var internalUtil = { + deprecate: __webpack_require__(85) +}; +/**/ + +/**/ +var Stream = __webpack_require__(47); +/**/ + +/**/ +var Buffer = __webpack_require__(35).Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} +/**/ + +var destroyImpl = __webpack_require__(48); + +util.inherits(Writable, Stream); + +function nop() {} + +function WritableState(options, stream) { + Duplex = Duplex || __webpack_require__(14); + + options = options || {}; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // if _final has been called + this.finalCalled = false; + + // drain event flag. + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // has it been destroyed + this.destroyed = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.bufferedRequest = null; + this.lastBufferedRequest = null; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; + + // count buffered requests + this.bufferedRequestCount = 0; + + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} + +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; + } + return out; +}; + +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); + +// Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. +var realHasInstance; +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (realHasInstance.call(this, object)) return true; + + return object && object._writableState instanceof WritableState; + } + }); +} else { + realHasInstance = function (object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || __webpack_require__(14); + + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { + return new Writable(options); + } + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + + if (typeof options.writev === 'function') this._writev = options.writev; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); +}; + +function writeAfterEnd(stream, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + processNextTick(cb, er); +} + +// Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. +function validChunk(stream, state, chunk, cb) { + var valid = true; + var er = false; + + if (chunk === null) { + er = new TypeError('May not write null values to stream'); + } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + if (er) { + stream.emit('error', er); + processNextTick(cb, er); + valid = false; + } + return valid; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = _isUint8Array(chunk) && !state.objectMode; + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + + if (typeof cb !== 'function') cb = nop; + + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function () { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + } +}; + +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); + } + return chunk; +} + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; + } + } + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; + + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); + } + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; + + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + processNextTick(cb, er); + // this can emit finish, and it will always happen + // after error + processNextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + // this can emit finish, but finish must + // always follow error + finishMaybe(stream, state); + } +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state); + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + /**/ + asyncWrite(afterWrite, stream, state, finished, cb); + /**/ + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; + + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + + var count = 0; + var allBuffers = true; + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + buffer.allBuffers = allBuffers; + + doWrite(stream, state, true, state.length, buffer, '', holder.finish); + + // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + state.pendingcb++; + state.lastBufferedRequest = null; + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + break; + } + } + + if (entry === null) state.lastBufferedRequest = null; + } + + state.bufferedRequestCount = 0; + state.bufferedRequest = entry; + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); +}; + +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + if (err) { + stream.emit('error', err); + } + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function') { + state.pendingcb++; + state.finalCalled = true; + processNextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state) { + var need = needFinish(state); + if (need) { + prefinish(stream, state); + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + } + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) processNextTick(cb);else stream.once('finish', cb); + } + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } + if (state.corkedRequestsFree) { + state.corkedRequestsFree.next = corkReq; + } else { + state.corkedRequestsFree = corkReq; + } +} + +Object.defineProperty(Writable.prototype, 'destroyed', { + get: function () { + if (this._writableState === undefined) { + return false; + } + return this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._writableState.destroyed = value; + } +}); + +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; +Writable.prototype._destroy = function (err, cb) { + this.end(); + cb(err); +}; +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8), __webpack_require__(83).setImmediate, __webpack_require__(7))) + +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +exports.decode = exports.parse = __webpack_require__(91); +exports.encode = exports.stringify = __webpack_require__(92); + + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = __webpack_require__(108); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = __webpack_require__(109); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7), __webpack_require__(8))) + +/***/ }), +/* 39 */ /***/ (function(module, exports) { /** @@ -9772,428 +11198,7 @@ module.exports = DiscordAPIError; /***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { - -const Channel = __webpack_require__(17); -const TextBasedChannel = __webpack_require__(25); -const Collection = __webpack_require__(3); - -/* -{ type: 3, - recipients: - [ { username: 'Charlie', - id: '123', - discriminator: '6631', - avatar: '123' }, - { username: 'Ben', - id: '123', - discriminator: '2055', - avatar: '123' }, - { username: 'Adam', - id: '123', - discriminator: '2406', - avatar: '123' } ], - owner_id: '123', - name: null, - last_message_id: '123', - id: '123', - icon: null } -*/ - -/** - * Represents a Group DM on Discord. - * @extends {Channel} - * @implements {TextBasedChannel} - */ -class GroupDMChannel extends Channel { - constructor(client, data) { - super(client, data); - this.type = 'group'; - this.messages = new Collection(); - this._typing = new Map(); - } - - setup(data) { - super.setup(data); - - /** - * The name of this Group DM, can be null if one isn't set - * @type {string} - */ - this.name = data.name; - - /** - * A hash of the Group DM icon. - * @type {string} - */ - this.icon = data.icon; - - /** - * The user ID of this Group DM's owner - * @type {string} - */ - this.ownerID = data.owner_id; - - /** - * If the DM is managed by an application - * @type {boolean} - */ - this.managed = data.managed; - - /** - * Application ID of the application that made this Group DM, if applicable - * @type {?string} - */ - this.applicationID = data.application_id; - - /** - * Nicknames for group members - * @type {?Collection} - */ - if (data.nicks) this.nicks = new Collection(data.nicks.map(n => [n.id, n.nick])); - - if (!this.recipients) { - /** - * A collection of the recipients of this DM, mapped by their ID - * @type {Collection} - */ - this.recipients = new Collection(); - } - - if (data.recipients) { - for (const recipient of data.recipients) { - const user = this.client.dataManager.newUser(recipient); - this.recipients.set(user.id, user); - } - } - - this.lastMessageID = data.last_message_id; - } - - /** - * The owner of this Group DM - * @type {User} - * @readonly - */ - get owner() { - return this.client.users.get(this.ownerID); - } - - /** - * Whether this channel equals another channel. It compares all properties, so for most operations - * it is advisable to just compare `channel.id === channel2.id` as it is much faster and is often - * what most users need. - * @param {GroupDMChannel} channel Channel to compare with - * @returns {boolean} - */ - equals(channel) { - const equal = channel && - this.id === channel.id && - this.name === channel.name && - this.icon === channel.icon && - this.ownerID === channel.ownerID; - - if (equal) { - return this.recipients.equals(channel.recipients); - } - - return equal; - } - - /** - * Add a user to the DM - * @param {UserResolvable|string} accessTokenOrUser Access token or user resolvable - * @param {string} [nick] Permanent nickname to give the user (only available if a bot is creating the DM) - * @returns {Promise} - */ - addUser(accessTokenOrUser, nick) { - const id = this.client.resolver.resolveUserID(accessTokenOrUser); - const data = this.client.user.bot ? - { nick, access_token: accessTokenOrUser } : - { recipient: id }; - return this.client.api.channels[this.id].recipients[id].put({ data }) - .then(() => this); - } - - /** - * When concatenated with a string, this automatically concatenates the channel's name instead of the Channel object. - * @returns {string} - * @example - * // Logs: Hello from My Group DM! - * console.log(`Hello from ${channel}!`); - * @example - * // Logs: Hello from My Group DM! - * console.log(`Hello from ' + channel + '!'); - */ - toString() { - return this.name; - } - - // These are here only for documentation purposes - they are implemented by TextBasedChannel - /* eslint-disable no-empty-function */ - send() {} - fetchMessage() {} - fetchMessages() {} - fetchPinnedMessages() {} - search() {} - startTyping() {} - stopTyping() {} - get typing() {} - get typingCount() {} - createMessageCollector() {} - awaitMessages() {} - // Doesn't work on Group DMs; bulkDelete() {} - acknowledge() {} - _cacheMessage() {} -} - -TextBasedChannel.applyToClass(GroupDMChannel, true, ['bulkDelete']); - -module.exports = GroupDMChannel; - - -/***/ }), -/* 34 */ -/***/ (function(module, exports) { - -/** - * Represents a limited emoji set used for both custom and unicode emojis. Custom emojis - * will use this class opposed to the Emoji class when the client doesn't know enough - * information about them. - */ -class ReactionEmoji { - constructor(reaction, name, id) { - /** - * The message reaction this emoji refers to - * @type {MessageReaction} - */ - this.reaction = reaction; - - /** - * The name of this reaction emoji - * @type {string} - */ - this.name = name; - - /** - * The ID of this reaction emoji - * @type {?Snowflake} - */ - this.id = id; - } - - /** - * The identifier of this emoji, used for message reactions - * @type {string} - * @readonly - */ - get identifier() { - if (this.id) return `${this.name}:${this.id}`; - return encodeURIComponent(this.name); - } - - /** - * Creates the text required to form a graphical emoji on Discord. - * @example - * // Send the emoji used in a reaction to the channel the reaction is part of - * reaction.message.channel.sendMessage(`The emoji used is ${reaction.emoji}`); - * @returns {string} - */ - toString() { - return this.id ? `<:${this.name}:${this.id}>` : this.name; - } -} - -module.exports = ReactionEmoji; - - -/***/ }), -/* 35 */ -/***/ (function(module, exports, __webpack_require__) { - -const Collection = __webpack_require__(3); -const EventEmitter = __webpack_require__(15); - -/** - * Filter to be applied to the collector. - * @typedef {Function} CollectorFilter - * @param {...*} args Any arguments received by the listener - * @returns {boolean} To collect or not collect - */ - -/** - * Options to be applied to the collector. - * @typedef {Object} CollectorOptions - * @property {number} [time] How long to run the collector for - */ - -/** - * Abstract class for defining a new Collector. - * @abstract - */ -class Collector extends EventEmitter { - constructor(client, filter, options = {}) { - super(); - - /** - * The client - * @name Collector#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - - /** - * The filter applied to this collector - * @type {CollectorFilter} - */ - this.filter = filter; - - /** - * The options of this collector - * @type {CollectorOptions} - */ - this.options = options; - - /** - * The items collected by this collector - * @type {Collection} - */ - this.collected = new Collection(); - - /** - * Whether this collector has finished collecting - * @type {boolean} - */ - this.ended = false; - - /** - * Timeout for cleanup - * @type {?Timeout} - * @private - */ - this._timeout = null; - - /** - * Call this to handle an event as a collectable element - * Accepts any event data as parameters - * @type {Function} - * @private - */ - this.listener = this._handle.bind(this); - if (options.time) this._timeout = this.client.setTimeout(() => this.stop('time'), options.time); - } - - /** - * @param {...*} args The arguments emitted by the listener - * @emits Collector#collect - * @private - */ - _handle(...args) { - const collect = this.handle(...args); - if (!collect || !this.filter(...args)) return; - - this.collected.set(collect.key, collect.value); - - /** - * Emitted whenever an element is collected. - * @event Collector#collect - * @param {*} element The element that got collected - * @param {Collector} collector The collector - */ - this.emit('collect', collect.value, this); - - const post = this.postCheck(...args); - if (post) this.stop(post); - } - - /** - * Return a promise that resolves with the next collected element; - * rejects with collected elements if the collector finishes without receving a next element - * @type {Promise} - * @readonly - */ - get next() { - return new Promise((resolve, reject) => { - if (this.ended) { - reject(this.collected); - return; - } - - const cleanup = () => { - this.removeListener('collect', onCollect); - this.removeListener('end', onEnd); - }; - - const onCollect = item => { - cleanup(); - resolve(item); - }; - - const onEnd = () => { - cleanup(); - reject(this.collected); // eslint-disable-line prefer-promise-reject-errors - }; - - this.on('collect', onCollect); - this.on('end', onEnd); - }); - } - - /** - * Stop this collector and emit the `end` event. - * @param {string} [reason='user'] The reason this collector is ending - * @emits Collector#end - */ - stop(reason = 'user') { - if (this.ended) return; - - if (this._timeout) this.client.clearTimeout(this._timeout); - this.ended = true; - this.cleanup(); - - /** - * Emitted when the collector is finished collecting. - * @event Collector#end - * @param {Collection} collected The elements collected by the collector - * @param {string} reason The reason the collector ended - */ - this.emit('end', this.collected, reason); - } - - /* eslint-disable no-empty-function, valid-jsdoc */ - /** - * Handles incoming events from the `listener` function. Returns null if the event should not be collected, - * or returns an object describing the data that should be stored. - * @see Collector#listener - * @param {...*} args Any args the event listener emits - * @returns {?{key: string, value}} Data to insert into collection, if any - * @abstract - */ - handle() {} - - /** - * This method runs after collection to see if the collector should finish. - * @param {...*} args Any args the event listener emits - * @returns {?string} Reason to end the collector, if any - * @abstract - */ - postCheck() {} - - /** - * Called when the collector is ending. - * @abstract - */ - cleanup() {} - /* eslint-enable no-empty-function, valid-jsdoc */ -} - -module.exports = Collector; - - -/***/ }), -/* 36 */ +/* 40 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* @@ -11410,695 +12415,610 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ }); -/***/ }), -/* 37 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(process) { - -if (!process.version || - process.version.indexOf('v0.') === 0 || - process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { - module.exports = nextTick; -} else { - module.exports = process.nextTick; -} - -function nextTick(fn, arg1, arg2, arg3) { - if (typeof fn !== 'function') { - throw new TypeError('"callback" argument must be a function'); - } - var len = arguments.length; - var args, i; - switch (len) { - case 0: - case 1: - return process.nextTick(fn); - case 2: - return process.nextTick(function afterTickOne() { - fn.call(null, arg1); - }); - case 3: - return process.nextTick(function afterTickTwo() { - fn.call(null, arg1, arg2); - }); - case 4: - return process.nextTick(function afterTickThree() { - fn.call(null, arg1, arg2, arg3); - }); - default: - args = new Array(len - 1); - i = 0; - while (i < args.length) { - args[i++] = arguments[i]; - } - return process.nextTick(function afterTick() { - fn.apply(null, args); - }); - } -} - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) - -/***/ }), -/* 38 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -exports.decode = exports.parse = __webpack_require__(86); -exports.encode = exports.stringify = __webpack_require__(87); - - -/***/ }), -/* 39 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(process, setImmediate) {// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. - - - -module.exports = Writable; - -/**/ -var processNextTick = __webpack_require__(37); -/**/ - -/**/ -var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick; -/**/ - -/**/ -var Duplex; -/**/ - -Writable.WritableState = WritableState; - -/**/ -var util = __webpack_require__(23); -util.inherits = __webpack_require__(12); -/**/ - -/**/ -var internalUtil = { - deprecate: __webpack_require__(106) -}; -/**/ - -/**/ -var Stream = __webpack_require__(62); -/**/ - -/**/ -var Buffer = __webpack_require__(40).Buffer; -/**/ - -util.inherits(Writable, Stream); - -function nop() {} - -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} - -function WritableState(options, stream) { - Duplex = Duplex || __webpack_require__(16); - - options = options || {}; - - // object stream flag to indicate whether or not this stream - // contains buffers or objects. - this.objectMode = !!options.objectMode; - - if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode; - - // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - var hwm = options.highWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; - - // cast to ints. - this.highWaterMark = ~~this.highWaterMark; - - // drain event flag. - this.needDrain = false; - // at the start of calling end() - this.ending = false; - // when end() has been called, and returned - this.ended = false; - // when 'finish' is emitted - this.finished = false; - - // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - this.length = 0; - - // a flag to see when we're in the middle of a write. - this.writing = false; - - // when true all writes will be buffered until .uncork() call - this.corked = 0; - - // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - this.sync = true; - - // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - this.bufferProcessing = false; - - // the callback that's passed to _write(chunk,cb) - this.onwrite = function (er) { - onwrite(stream, er); - }; - - // the callback that the user supplies to write(chunk,encoding,cb) - this.writecb = null; - - // the amount that is being written when _write is called. - this.writelen = 0; - - this.bufferedRequest = null; - this.lastBufferedRequest = null; - - // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - this.pendingcb = 0; - - // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - this.prefinished = false; - - // True if the error was already emitted and should not be thrown again - this.errorEmitted = false; - - // count buffered requests - this.bufferedRequestCount = 0; - - // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - this.corkedRequestsFree = new CorkedRequest(this); -} - -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - while (current) { - out.push(current); - current = current.next; - } - return out; -}; - -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function () { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.') - }); - } catch (_) {} -})(); - -// Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. -var realHasInstance; -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function (object) { - if (realHasInstance.call(this, object)) return true; - - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function (object) { - return object instanceof this; - }; -} - -function Writable(options) { - Duplex = Duplex || __webpack_require__(16); - - // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { - return new Writable(options); - } - - this._writableState = new WritableState(options, this); - - // legacy. - this.writable = true; - - if (options) { - if (typeof options.write === 'function') this._write = options.write; - - if (typeof options.writev === 'function') this._writev = options.writev; - } - - Stream.call(this); -} - -// Otherwise people can pipe Writable streams, which is just wrong. -Writable.prototype.pipe = function () { - this.emit('error', new Error('Cannot pipe, not readable')); -}; - -function writeAfterEnd(stream, cb) { - var er = new Error('write after end'); - // TODO: defer error events consistently everywhere, not just the cb - stream.emit('error', er); - processNextTick(cb, er); -} - -// Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. -function validChunk(stream, state, chunk, cb) { - var valid = true; - var er = false; - - if (chunk === null) { - er = new TypeError('May not write null values to stream'); - } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - if (er) { - stream.emit('error', er); - processNextTick(cb, er); - valid = false; - } - return valid; -} - -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - var isBuf = Buffer.isBuffer(chunk); - - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - - if (typeof cb !== 'function') cb = nop; - - if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - - return ret; -}; - -Writable.prototype.cork = function () { - var state = this._writableState; - - state.corked++; -}; - -Writable.prototype.uncork = function () { - var state = this._writableState; - - if (state.corked) { - state.corked--; - - if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; - -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; - -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - return chunk; -} - -// if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - chunk = decodeChunk(state, chunk, encoding); - if (Buffer.isBuffer(chunk)) encoding = 'buffer'; - } - var len = state.objectMode ? 1 : chunk.length; - - state.length += len; - - var ret = state.length < state.highWaterMark; - // we must ensure that previous needDrain will not be reset to false. - if (!ret) state.needDrain = true; - - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = new WriteReq(chunk, encoding, cb); - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - - return ret; -} - -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} - -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - if (sync) processNextTick(cb, er);else cb(er); - - stream._writableState.errorEmitted = true; - stream.emit('error', er); -} - -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} - -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - - onwriteStateUpdate(state); - - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state); - - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - - if (sync) { - /**/ - asyncWrite(afterWrite, stream, state, finished, cb); - /**/ - } else { - afterWrite(stream, state, finished, cb); - } - } -} - -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} - -// Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} - -// if there's something in the buffer waiting, then process it -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - - var count = 0; - while (entry) { - buffer[count] = entry; - entry = entry.next; - count += 1; - } - - doWrite(stream, state, true, state.length, buffer, '', holder.finish); - - // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - state.pendingcb++; - state.lastBufferedRequest = null; - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - if (state.writing) { - break; - } - } - - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequestCount = 0; - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new Error('_write() is not implemented')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); - - // .end() fully uncorks - if (state.corked) { - state.corked = 1; - this.uncork(); - } - - // ignore unnecessary end() calls. - if (!state.ending && !state.finished) endWritable(this, state, cb); -}; - -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} - -function prefinish(stream, state) { - if (!state.prefinished) { - state.prefinished = true; - stream.emit('prefinish'); - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - if (need) { - if (state.pendingcb === 0) { - prefinish(stream, state); - state.finished = true; - stream.emit('finish'); - } else { - prefinish(stream, state); - } - } - return need; -} - -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - if (cb) { - if (state.finished) processNextTick(cb);else stream.once('finish', cb); - } - state.ended = true; - stream.writable = false; -} - -// It seems a linked list but it is not -// there will be only 2 of these for each stream -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - this.finish = function (err) { - var entry = _this.entry; - _this.entry = null; - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } - if (state.corkedRequestsFree) { - state.corkedRequestsFree.next = _this; - } else { - state.corkedRequestsFree = _this; - } - }; -} -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8), __webpack_require__(103).setImmediate)) - -/***/ }), -/* 40 */ -/***/ (function(module, exports, __webpack_require__) { - -module.exports = __webpack_require__(6) - - /***/ }), /* 41 */ /***/ (function(module, exports, __webpack_require__) { -const Snekfetch = __webpack_require__(97); +const Collection = __webpack_require__(3); +const EventEmitter = __webpack_require__(12); -// const ENV_VAR = '__SNEKFETCH_SYNC_REQUEST'; -// let first = true; -// -// for (let method of Snekfetch.METHODS) { -// method = method === 'M-SEARCH' ? 'msearch' : method.toLowerCase(); -// Snekfetch[`${method}Sync`] = (url, options = {}) => { -// if (first) { -// first = false; -// console.error( -// 'Performing sync requests is a really stupid thing to do. ' + -// 'https://www.google.com/search?q=why+sync+requests+are+bad+nodejs' -// ); -// } -// options.url = url; -// options.method = method; -// const cp = require('child_process'); -// const result = JSON.parse( -// cp.execSync(`node ${__dirname}/index.js`, { -// env: { [ENV_VAR]: JSON.stringify(options) }, -// }).toString(), -// (k, v) => { -// if (v === null) return v; -// if (v.type === 'Buffer' && Array.isArray(v.data)) return new Buffer(v.data); -// if (v.__CONVERT_TO_ERROR) { -// const e = new Error(); -// for (const key of Object.keys(v)) { -// if (key === '__CONVERT_TO_ERROR') continue; -// e[key] = v[key]; -// } -// return e; -// } -// return v; -// } -// ); -// if (result.error) throw result.error; -// return result; -// }; -// } -// -// if (process.env[ENV_VAR]) { -// const options = JSON.parse(process.env[ENV_VAR]); -// const request = Snekfetch[options.method](options.url); -// if (options.headers) request.set(options.headers); -// if (options.body) request.send(options.body); -// request.end((err, res = {}) => { -// if (err) { -// const alt = {}; -// for (const name of Object.getOwnPropertyNames(err)) alt[name] = err[name]; -// res.error = alt; -// res.error.__CONVERT_TO_ERROR = true; -// } -// // circulars -// res.request = null; -// process.stdout.write(JSON.stringify(res)); -// }); -// } +/** + * Filter to be applied to the collector. + * @typedef {Function} CollectorFilter + * @param {...*} args Any arguments received by the listener + * @returns {boolean} To collect or not collect + */ -module.exports = Snekfetch; +/** + * Options to be applied to the collector. + * @typedef {Object} CollectorOptions + * @property {number} [time] How long to run the collector for + */ + +/** + * Abstract class for defining a new Collector. + * @abstract + */ +class Collector extends EventEmitter { + constructor(client, filter, options = {}) { + super(); + + /** + * The client + * @name Collector#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The filter applied to this collector + * @type {CollectorFilter} + */ + this.filter = filter; + + /** + * The options of this collector + * @type {CollectorOptions} + */ + this.options = options; + + /** + * The items collected by this collector + * @type {Collection} + */ + this.collected = new Collection(); + + /** + * Whether this collector has finished collecting + * @type {boolean} + */ + this.ended = false; + + /** + * Timeout for cleanup + * @type {?Timeout} + * @private + */ + this._timeout = null; + + /** + * Call this to handle an event as a collectable element + * Accepts any event data as parameters + * @type {Function} + * @private + */ + this.listener = this._handle.bind(this); + if (options.time) this._timeout = this.client.setTimeout(() => this.stop('time'), options.time); + } + + /** + * @param {...*} args The arguments emitted by the listener + * @emits Collector#collect + * @private + */ + _handle(...args) { + const collect = this.handle(...args); + if (!collect || !this.filter(...args)) return; + + this.collected.set(collect.key, collect.value); + + /** + * Emitted whenever an element is collected. + * @event Collector#collect + * @param {*} element The element that got collected + * @param {Collector} collector The collector + */ + this.emit('collect', collect.value, this); + + const post = this.postCheck(...args); + if (post) this.stop(post); + } + + /** + * Return a promise that resolves with the next collected element; + * rejects with collected elements if the collector finishes without receving a next element + * @type {Promise} + * @readonly + */ + get next() { + return new Promise((resolve, reject) => { + if (this.ended) { + reject(this.collected); + return; + } + + const cleanup = () => { + this.removeListener('collect', onCollect); + this.removeListener('end', onEnd); + }; + + const onCollect = item => { + cleanup(); + resolve(item); + }; + + const onEnd = () => { + cleanup(); + reject(this.collected); // eslint-disable-line prefer-promise-reject-errors + }; + + this.on('collect', onCollect); + this.on('end', onEnd); + }); + } + + /** + * Stop this collector and emit the `end` event. + * @param {string} [reason='user'] The reason this collector is ending + * @emits Collector#end + */ + stop(reason = 'user') { + if (this.ended) return; + + if (this._timeout) this.client.clearTimeout(this._timeout); + this.ended = true; + this.cleanup(); + + /** + * Emitted when the collector is finished collecting. + * @event Collector#end + * @param {Collection} collected The elements collected by the collector + * @param {string} reason The reason the collector ended + */ + this.emit('end', this.collected, reason); + } + + /* eslint-disable no-empty-function, valid-jsdoc */ + /** + * Handles incoming events from the `listener` function. Returns null if the event should not be collected, + * or returns an object describing the data that should be stored. + * @see Collector#listener + * @param {...*} args Any args the event listener emits + * @returns {?{key: string, value}} Data to insert into collection, if any + * @abstract + */ + handle() {} + + /** + * This method runs after collection to see if the collector should finish. + * @param {...*} args Any args the event listener emits + * @returns {?string} Reason to end the collector, if any + * @abstract + */ + postCheck() {} + + /** + * Called when the collector is ending. + * @abstract + */ + cleanup() {} + /* eslint-enable no-empty-function, valid-jsdoc */ +} + +module.exports = Collector; /***/ }), /* 42 */ +/***/ (function(module, exports) { + +/** + * Represents a limited emoji set used for both custom and unicode emojis. Custom emojis + * will use this class opposed to the Emoji class when the client doesn't know enough + * information about them. + */ +class ReactionEmoji { + constructor(reaction, name, id) { + /** + * The message reaction this emoji refers to + * @type {MessageReaction} + */ + this.reaction = reaction; + + /** + * The name of this reaction emoji + * @type {string} + */ + this.name = name; + + /** + * The ID of this reaction emoji + * @type {?Snowflake} + */ + this.id = id; + } + + /** + * The identifier of this emoji, used for message reactions + * @type {string} + * @readonly + */ + get identifier() { + if (this.id) return `${this.name}:${this.id}`; + return encodeURIComponent(this.name); + } + + /** + * Creates the text required to form a graphical emoji on Discord. + * @example + * // Send the emoji used in a reaction to the channel the reaction is part of + * reaction.message.channel.sendMessage(`The emoji used is ${reaction.emoji}`); + * @returns {string} + */ + toString() { + return this.id ? `<:${this.name}:${this.id}>` : this.name; + } +} + +module.exports = ReactionEmoji; + + +/***/ }), +/* 43 */ /***/ (function(module, exports, __webpack_require__) { +const Channel = __webpack_require__(16); +const TextBasedChannel = __webpack_require__(23); +const Collection = __webpack_require__(3); + +/* +{ type: 3, + recipients: + [ { username: 'Charlie', + id: '123', + discriminator: '6631', + avatar: '123' }, + { username: 'Ben', + id: '123', + discriminator: '2055', + avatar: '123' }, + { username: 'Adam', + id: '123', + discriminator: '2406', + avatar: '123' } ], + owner_id: '123', + name: null, + last_message_id: '123', + id: '123', + icon: null } +*/ + +/** + * Represents a Group DM on Discord. + * @extends {Channel} + * @implements {TextBasedChannel} + */ +class GroupDMChannel extends Channel { + constructor(client, data) { + super(client, data); + this.type = 'group'; + this.messages = new Collection(); + this._typing = new Map(); + } + + setup(data) { + super.setup(data); + + /** + * The name of this Group DM, can be null if one isn't set + * @type {string} + */ + this.name = data.name; + + /** + * A hash of the Group DM icon. + * @type {string} + */ + this.icon = data.icon; + + /** + * The user ID of this Group DM's owner + * @type {string} + */ + this.ownerID = data.owner_id; + + /** + * If the DM is managed by an application + * @type {boolean} + */ + this.managed = data.managed; + + /** + * Application ID of the application that made this Group DM, if applicable + * @type {?string} + */ + this.applicationID = data.application_id; + + /** + * Nicknames for group members + * @type {?Collection} + */ + if (data.nicks) this.nicks = new Collection(data.nicks.map(n => [n.id, n.nick])); + + if (!this.recipients) { + /** + * A collection of the recipients of this DM, mapped by their ID + * @type {Collection} + */ + this.recipients = new Collection(); + } + + if (data.recipients) { + for (const recipient of data.recipients) { + const user = this.client.dataManager.newUser(recipient); + this.recipients.set(user.id, user); + } + } + + this.lastMessageID = data.last_message_id; + } + + /** + * The owner of this Group DM + * @type {User} + * @readonly + */ + get owner() { + return this.client.users.get(this.ownerID); + } + + /** + * Whether this channel equals another channel. It compares all properties, so for most operations + * it is advisable to just compare `channel.id === channel2.id` as it is much faster and is often + * what most users need. + * @param {GroupDMChannel} channel Channel to compare with + * @returns {boolean} + */ + equals(channel) { + const equal = channel && + this.id === channel.id && + this.name === channel.name && + this.icon === channel.icon && + this.ownerID === channel.ownerID; + + if (equal) { + return this.recipients.equals(channel.recipients); + } + + return equal; + } + + /** + * Add a user to the DM + * @param {UserResolvable|string} accessTokenOrUser Access token or user resolvable + * @param {string} [nick] Permanent nickname to give the user (only available if a bot is creating the DM) + * @returns {Promise} + */ + addUser(accessTokenOrUser, nick) { + const id = this.client.resolver.resolveUserID(accessTokenOrUser); + const data = this.client.user.bot ? + { nick, access_token: accessTokenOrUser } : + { recipient: id }; + return this.client.api.channels[this.id].recipients[id].put({ data }) + .then(() => this); + } + + /** + * When concatenated with a string, this automatically concatenates the channel's name instead of the Channel object. + * @returns {string} + * @example + * // Logs: Hello from My Group DM! + * console.log(`Hello from ${channel}!`); + * @example + * // Logs: Hello from My Group DM! + * console.log(`Hello from ' + channel + '!'); + */ + toString() { + return this.name; + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + send() {} + fetchMessage() {} + fetchMessages() {} + fetchPinnedMessages() {} + search() {} + startTyping() {} + stopTyping() {} + get typing() {} + get typingCount() {} + createMessageCollector() {} + awaitMessages() {} + // Doesn't work on Group DMs; bulkDelete() {} + acknowledge() {} + _cacheMessage() {} +} + +TextBasedChannel.applyToClass(GroupDMChannel, true, ['bulkDelete']); + +module.exports = GroupDMChannel; + + +/***/ }), +/* 44 */ +/***/ (function(module, exports, __webpack_require__) { + +const Snowflake = __webpack_require__(9); +const Constants = __webpack_require__(0); + +/** + * Represents an OAuth2 Application. + */ +class OAuth2Application { + constructor(client, data) { + /** + * The client that instantiated the application + * @name OAuth2Application#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + this.setup(data); + } + + setup(data) { + /** + * The ID of the app + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The name of the app + * @type {string} + */ + this.name = data.name; + + /** + * The app's description + * @type {string} + */ + this.description = data.description; + + /** + * The app's icon hash + * @type {string} + */ + this.icon = data.icon; + + /** + * The app's RPC origins + * @type {?string[]} + */ + this.rpcOrigins = data.rpc_origins; + + /** + * The app's redirect URIs + * @type {string[]} + */ + this.redirectURIs = data.redirect_uris; + + /** + * If this app's bot requires a code grant when using the OAuth2 flow + * @type {boolean} + */ + this.botRequireCodeGrant = data.bot_require_code_grant; + + /** + * If this app's bot is public + * @type {boolean} + */ + this.botPublic = data.bot_public; + + /** + * If this app can use rpc + * @type {boolean} + */ + this.rpcApplicationState = data.rpc_application_state; + + /** + * Object containing basic info about this app's bot + * @type {Object} + */ + this.bot = data.bot; + + /** + * The flags for the app + * @type {number} + */ + this.flags = data.flags; + + /** + * OAuth2 secret for the application + * @type {boolean} + */ + this.secret = data.secret; + + if (data.owner) { + /** + * The owner of this OAuth application + * @type {?User} + */ + this.owner = this.client.dataManager.newUser(data.owner); + } + } + + /** + * The timestamp the app was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the app was created + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * A link to the application's icon + * @param {Object} [options={}] Options for the icon url + * @param {string} [options.format='webp'] One of `webp`, `png`, `jpg` + * @param {number} [options.size=128] One of `128`, '256', `512`, `1024`, `2048` + * @returns {?string} URL to the icon + */ + iconURL({ format, size } = {}) { + if (!this.icon) return null; + return Constants.Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.icon, format, size); + } + + /** + * Reset the app's secret. + * This is only available when using a user account. + * @returns {OAuth2Application} + */ + resetSecret() { + return this.client.api.oauth2.applications[this.id].reset.post() + .then(app => new OAuth2Application(this.client, app)); + } + + /** + * Reset the app's bot token. + * This is only available when using a user account. + * @returns {OAuth2Application} + */ + resetToken() { + return this.client.api.oauth2.applications[this.id].bot.reset.post() + .then(app => new OAuth2Application(this.client, Object.assign({}, this, { bot: app }))); + } + + /** + * When concatenated with a string, this automatically concatenates the app name rather than the app object. + * @returns {string} + */ + toString() { + return this.name; + } +} + +module.exports = OAuth2Application; + + +/***/ }), +/* 45 */ +/***/ (function(module, exports) { + +var toString = {}.toString; + +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; + + +/***/ }), +/* 46 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; /* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -12120,2428 +13040,17 @@ module.exports = Snekfetch; // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); - } - return objects.join(' '); - } - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); - } - } - return str; -}; +/**/ -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - // Allow for deprecating things in the process of starting up. - if (isUndefined(global.process)) { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } - - if (process.noDeprecation === true) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -}; - - -var debugs = {}; -var debugEnviron; -exports.debuglog = function(set) { - if (isUndefined(debugEnviron)) - debugEnviron = process.env.NODE_DEBUG || ''; - set = set.toUpperCase(); - if (!debugs[set]) { - if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } - } - return debugs[set]; -}; - - -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. - */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); - } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); -} -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; - - -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; - } -} - - -function stylizeNoColor(str, styleType) { - return str; -} - - -function arrayToHash(array) { - var hash = {}; - - array.forEach(function(val, idx) { - hash[val] = true; - }); - - return hash; -} - - -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } - - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } - - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); - - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); - } - - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); - } - - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } - - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } - - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; - } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } - - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } - - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } - - ctx.seen.push(value); - - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } - - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - - -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -function isArray(ar) { - return Array.isArray(ar); -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = __webpack_require__(108); - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - - -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); -} - - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} - - -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = __webpack_require__(107); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; - - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; - } - return origin; -}; - -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11), __webpack_require__(8))) - -/***/ }), -/* 43 */ -/***/ (function(module, exports) { - - - -/***/ }), -/* 44 */ -/***/ (function(module, exports) { - -module.exports = { - "name": "discord.js", - "version": "12.0.0-dev", - "description": "A powerful library for interacting with the Discord API", - "main": "./src/index", - "types": "./typings/index.d.ts", - "scripts": { - "test": "npm run lint && npm run docs:test", - "docs": "docgen --source src --custom docs/index.yml --output docs/docs.json", - "docs:test": "docgen --source src --custom docs/index.yml", - "lint": "eslint src", - "lint:fix": "eslint --fix src", - "webpack": "parallel-webpack" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/hydrabolt/discord.js.git" - }, - "keywords": [ - "discord", - "api", - "bot", - "client", - "node", - "discordapp" - ], - "author": "Amish Shah ", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/hydrabolt/discord.js/issues" - }, - "homepage": "https://github.com/hydrabolt/discord.js#readme", - "runkitExampleFilename": "./docs/examples/ping.js", - "dependencies": { - "long": "^3.2.0", - "prism-media": "^0.0.1", - "snekfetch": "^3.1.0", - "tweetnacl": "^1.0.0", - "ws": "^3.0.0" - }, - "peerDependencies": { - "bufferutil": "^3.0.0", - "erlpack": "hammerandchisel/erlpack", - "node-opus": "^0.2.5", - "opusscript": "^0.0.3", - "sodium": "^2.0.1", - "libsodium-wrappers": "^0.5.1", - "uws": "^0.14.1" - }, - "devDependencies": { - "@types/node": "^7.0.0", - "discord.js-docgen": "hydrabolt/discord.js-docgen", - "eslint": "^3.19.0", - "parallel-webpack": "^1.6.0", - "uglify-js": "mishoo/UglifyJS2#harmony-v2.8.22", - "uglifyjs-webpack-plugin": "^0.4.3", - "webpack": "^2.2.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "browser": { - "ws": false, - "uws": false, - "erlpack": false, - "prism-media": false, - "opusscript": false, - "node-opus": false, - "tweetnacl": false, - "sodium": false, - "src/sharding/Shard.js": false, - "src/sharding/ShardClientUtil.js": false, - "src/sharding/ShardingManager.js": false, - "src/client/voice/dispatcher/StreamDispatcher.js": false, - "src/client/voice/opus/BaseOpusEngine.js": false, - "src/client/voice/opus/NodeOpusEngine.js": false, - "src/client/voice/opus/OpusEngineList.js": false, - "src/client/voice/opus/OpusScriptEngine.js": false, - "src/client/voice/pcm/ConverterEngine.js": false, - "src/client/voice/pcm/ConverterEngineList.js": false, - "src/client/voice/pcm/FfmpegConverterEngine.js": false, - "src/client/voice/player/AudioPlayer.js": false, - "src/client/voice/receiver/VoiceReadable.js": false, - "src/client/voice/receiver/VoiceReceiver.js": false, - "src/client/voice/util/Secretbox.js": false, - "src/client/voice/util/SecretKey.js": false, - "src/client/voice/util/VolumeInterface.js": false, - "src/client/voice/ClientVoiceManager.js": false, - "src/client/voice/VoiceBroadcast.js": false, - "src/client/voice/VoiceConnection.js": false, - "src/client/voice/VoiceUDPClient.js": false, - "src/client/voice/VoiceWebSocket.js": false - } -}; - -/***/ }), -/* 45 */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(Buffer) {const User = __webpack_require__(13); -const Collection = __webpack_require__(3); -const ClientUserSettings = __webpack_require__(46); -const Constants = __webpack_require__(0); -const Util = __webpack_require__(4); -const Guild = __webpack_require__(19); -const Message = __webpack_require__(9); -const GroupDMChannel = __webpack_require__(33); -const { TypeError } = __webpack_require__(5); - -/** - * Represents the logged in client's Discord user. - * @extends {User} - */ -class ClientUser extends User { - setup(data) { - super.setup(data); - - /** - * Whether or not this account has been verified - * @type {boolean} - */ - this.verified = data.verified; - - /** - * The email of this account - * @type {string} - */ - this.email = data.email; - this.localPresence = {}; - this._typing = new Map(); - - /** - * A Collection of friends for the logged in user - * This is only filled when using a user account. - * @type {Collection} - */ - this.friends = new Collection(); - - /** - * A Collection of blocked users for the logged in user - * This is only filled when using a user account. - * @type {Collection} - */ - this.blocked = new Collection(); - - /** - * A Collection of notes for the logged in user - * This is only filled when using a user account. - * @type {Collection} - */ - this.notes = new Collection(); - - /** - * If the user has Discord premium (nitro) - * This is only filled when using a user account. - * @type {?boolean} - */ - this.premium = typeof data.premium === 'boolean' ? data.premium : null; - - /** - * If the user has MFA enabled on their account - * This is only filled when using a user account. - * @type {?boolean} - */ - this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null; - - /** - * If the user has ever used a mobile device on Discord - * This is only filled when using a user account. - * @type {?boolean} - */ - this.mobile = typeof data.mobile === 'boolean' ? data.mobile : null; - - /** - * Various settings for this user - * This is only filled when using a user account. - * @type {?ClientUserSettings} - */ - if (data.user_settings) this.settings = new ClientUserSettings(this, data.user_settings); - } - - edit(data, password) { - const _data = {}; - _data.username = data.username || this.username; - _data.avatar = this.client.resolver.resolveBase64(data.avatar); - - if (!this.bot) { - _data.email = data.email || this.email; - _data.password = password; - if (data.new_password) _data.new_password = data.newPassword; - } - - return this.client.api.users['@me'].patch({ data }) - .then(newData => this.client.actions.UserUpdate.handle(newData).updated); - } - - /** - * Set the username of the logged in client. - * Changing usernames in Discord is heavily rate limited, with only 2 requests - * every hour. Use this sparingly! - * @param {string} username The new username - * @param {string} [password] Current password (only for user accounts) - * @returns {Promise} - * @example - * // Set username - * client.user.setUsername('discordjs') - * .then(user => console.log(`My new username is ${user.username}`)) - * .catch(console.error); - */ - setUsername(username, password) { - return this.edit({ username }, password); - } - - /** - * Changes the email for the client user's account. - * This is only available when using a user account. - * @param {string} email New email to change to - * @param {string} password Current password - * @returns {Promise} - * @example - * // Set email - * client.user.setEmail('bob@gmail.com', 'some amazing password 123') - * .then(user => console.log(`My new email is ${user.email}`)) - * .catch(console.error); - */ - setEmail(email, password) { - return this.edit({ email }, password); - } - - /** - * Changes the password for the client user's account. - * This is only available when using a user account. - * @param {string} newPassword New password to change to - * @param {string} oldPassword Current password - * @returns {Promise} - * @example - * // Set password - * client.user.setPassword('some new amazing password 456', 'some amazing password 123') - * .then(user => console.log('New password set!')) - * .catch(console.error); - */ - setPassword(newPassword, oldPassword) { - return this.edit({ password: newPassword }, oldPassword); - } - - /** - * Set the avatar of the logged in client. - * @param {BufferResolvable|Base64Resolvable} avatar The new avatar - * @returns {Promise} - * @example - * // Set avatar - * client.user.setAvatar('./avatar.png') - * .then(user => console.log(`New avatar set!`)) - * .catch(console.error); - */ - setAvatar(avatar) { - if (typeof avatar === 'string' && avatar.startsWith('data:')) { - return this.edit({ avatar }); - } else { - return this.client.resolver.resolveBuffer(avatar || Buffer.alloc(0)) - .then(data => this.edit({ avatar: this.client.resolver.resolveBase64(data) || null })); - } - } - - /** - * Data resembling a raw Discord presence. - * @typedef {Object} PresenceData - * @property {PresenceStatus} [status] Status of the user - * @property {boolean} [afk] Whether the user is AFK - * @property {Object} [game] Game the user is playing - * @property {string} [game.name] Name of the game - * @property {string} [game.url] Twitch stream URL - */ - - /** - * Sets the full presence of the client user. - * @param {PresenceData} data Data for the presence - * @returns {Promise} - */ - setPresence(data) { - // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}} - return new Promise(resolve => { - let status = this.localPresence.status || this.presence.status; - let game = this.localPresence.game; - let afk = this.localPresence.afk || this.presence.afk; - - if (!game && this.presence.game) { - game = { - name: this.presence.game.name, - type: this.presence.game.type, - url: this.presence.game.url, - }; - } - - if (data.status) { - if (typeof data.status !== 'string') throw new TypeError('STATUS_TYPE'); - if (this.bot) { - status = data.status; - } else { - this.settings.update(Constants.UserSettingsMap.status, data.status); - status = 'invisible'; - } - } - - if (data.game) { - game = data.game; - if (game.url) game.type = 1; - } else if (typeof data.game !== 'undefined') { - game = null; - } - - if (typeof data.afk !== 'undefined') afk = data.afk; - afk = Boolean(afk); - - this.localPresence = { status, game, afk }; - this.localPresence.since = 0; - this.localPresence.game = this.localPresence.game || null; - - this.client.ws.send({ - op: 3, - d: this.localPresence, - }); - - this.client._setPresence(this.id, this.localPresence); - - resolve(this); - }); - } - - /** - * A user's status. Must be one of: - * - `online` - * - `idle` - * - `invisible` - * - `dnd` (do not disturb) - * @typedef {string} PresenceStatus - */ - - /** - * Sets the status of the client user. - * @param {PresenceStatus} status Status to change to - * @returns {Promise} - */ - setStatus(status) { - return this.setPresence({ status }); - } - - /** - * Sets the game the client user is playing. - * @param {?string} game Game being played - * @param {string} [streamingURL] Twitch stream URL - * @returns {Promise} - */ - setGame(game, streamingURL) { - if (!game) return this.setPresence({ game: null }); - return this.setPresence({ - game: { - name: game, - url: streamingURL, - }, - }); - } - - /** - * Sets/removes the AFK flag for the client user. - * @param {boolean} afk Whether or not the user is AFK - * @returns {Promise} - */ - setAFK(afk) { - return this.setPresence({ afk }); - } - - /** - * Fetches messages that mentioned the client's user. - * @param {Object} [options] Options for the fetch - * @param {number} [options.limit=25] Maximum number of mentions to retrieve - * @param {boolean} [options.roles=true] Whether to include role mentions - * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions - * @param {Guild|Snowflake} [options.guild] Limit the search to a specific guild - * @returns {Promise} - */ - fetchMentions(options = {}) { - if (options.guild instanceof Guild) options.guild = options.guild.id; - Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options); - - return this.client.api.users['@me'].mentions.get({ query: options }) - .then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))); - } - - /** - * Creates a guild. - * This is only available when using a user account. - * @param {string} name The name of the guild - * @param {Object} [options] Options for the creating - * @param {string} [options.region] The region for the server, defaults to the closest one available - * @param {BufferResolvable|Base64Resolvable} [options.icon=null] The icon for the guild - * @returns {Promise} The guild that was created - */ - createGuild(name, { region, icon = null } = {}) { - if (!icon || (typeof icon === 'string' && icon.startsWith('data:'))) { - return new Promise((resolve, reject) => - this.client.api.guilds.post({ data: { name, region, icon } }) - .then(data => { - if (this.client.guilds.has(data.id)) return resolve(this.client.guilds.get(data.id)); - - const handleGuild = guild => { - if (guild.id === data.id) { - this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild); - this.client.clearTimeout(timeout); - resolve(guild); - } - }; - this.client.on(Constants.Events.GUILD_CREATE, handleGuild); - - const timeout = this.client.setTimeout(() => { - this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild); - resolve(this.client.dataManager.newGuild(data)); - }, 10000); - return undefined; - }, reject) - ); - } else { - return this.client.resolver.resolveBuffer(icon) - .then(data => this.createGuild(name, { region, icon: this.client.resolver.resolveBase64(data) || null })); - } - } - - /** - * An object containing either a user or access token, and an optional nickname. - * @typedef {Object} GroupDMRecipientOptions - * @property {UserResolvable} [user] User to add to the Group DM - * (only available if a user is creating the DM) - * @property {string} [accessToken] Access token to use to add a user to the Group DM - * (only available if a bot is creating the DM) - * @property {string} [nick] Permanent nickname (only available if a bot is creating the DM) - * @property {string} [id] If no user resolveable is provided and you want to assign nicknames - * you must provide user ids instead - */ - - /** - * Creates a Group DM. - * @param {GroupDMRecipientOptions[]} recipients The recipients - * @returns {Promise} - */ - createGroupDM(recipients) { - const data = this.bot ? { - access_tokens: recipients.map(u => u.accessToken), - nicks: recipients.reduce((o, r) => { - if (r.nick) o[r.user ? r.user.id : r.id] = r.nick; - return o; - }, {}), - } : { recipients: recipients.map(u => this.client.resolver.resolveUserID(u)) }; - return this.client.api.users['@me'].channels.post({ data }) - .then(res => new GroupDMChannel(this.client, res)); - } -} - -module.exports = ClientUser; - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) - -/***/ }), -/* 46 */ -/***/ (function(module, exports, __webpack_require__) { - -const Constants = __webpack_require__(0); -const Util = __webpack_require__(4); - -/** - * A wrapper around the ClientUser's settings. - */ -class ClientUserSettings { - constructor(user, data) { - this.user = user; - this.patch(data); - } - - /** - * Patch the data contained in this class with new partial data. - * @param {Object} data Data to patch this with - */ - patch(data) { - for (const [key, value] of Object.entries(Constants.UserSettingsMap)) { - if (!data.hasOwnProperty(key)) continue; - if (typeof value === 'function') { - this[value.name] = value(data[key]); - } else { - this[value] = data[key]; - } - } - } - - /** - * Update a specific property of of user settings. - * @param {string} name Name of property - * @param {value} value Value to patch - * @returns {Promise} - */ - update(name, value) { - return this.user.client.api.users['@me'].settings.patch({ data: { [name]: value } }); - } - - /** - * @param {Guild} guild The guild to move - * @param {number} position Absolute or relative position - * @param {boolean} [relative=false] Whether to position relatively or absolutely - * @returns {Promise} - */ - setGuildPosition(guild, position, relative) { - const temp = Object.assign([], this.guildPositions); - Util.moveElementInArray(temp, guild.id, position, relative); - return this.update('guild_positions', temp).then(() => guild); - } - - /** - * Add a guild to the list of restricted guilds. - * @param {Guild} guild The guild to add - * @returns {Promise} - */ - addRestrictedGuild(guild) { - const temp = Object.assign([], this.restrictedGuilds); - if (temp.includes(guild.id)) return Promise.reject(new Error('Guild is already restricted')); - temp.push(guild.id); - return this.update('restricted_guilds', temp).then(() => guild); - } - - /** - * Remove a guild from the list of restricted guilds. - * @param {Guild} guild The guild to remove - * @returns {Promise} - */ - removeRestrictedGuild(guild) { - const temp = Object.assign([], this.restrictedGuilds); - const index = temp.indexOf(guild.id); - if (index < 0) return Promise.reject(new Error('Guild is not restricted')); - temp.splice(index, 1); - return this.update('restricted_guilds', temp).then(() => guild); - } -} - -module.exports = ClientUserSettings; - - -/***/ }), -/* 47 */ -/***/ (function(module, exports, __webpack_require__) { - -const Channel = __webpack_require__(17); -const TextBasedChannel = __webpack_require__(25); -const Collection = __webpack_require__(3); - -/** - * Represents a direct message channel between two users. - * @extends {Channel} - * @implements {TextBasedChannel} - */ -class DMChannel extends Channel { - constructor(client, data) { - super(client, data); - this.type = 'dm'; - this.messages = new Collection(); - this._typing = new Map(); - } - - setup(data) { - super.setup(data); - - /** - * The recipient on the other end of the DM - * @type {User} - */ - this.recipient = this.client.dataManager.newUser(data.recipients[0]); - - this.lastMessageID = data.last_message_id; - } - - /** - * When concatenated with a string, this automatically concatenates the recipient's mention instead of the - * DM channel object. - * @returns {string} - */ - toString() { - return this.recipient.toString(); - } - - // These are here only for documentation purposes - they are implemented by TextBasedChannel - /* eslint-disable no-empty-function */ - send() {} - fetchMessage() {} - fetchMessages() {} - fetchPinnedMessages() {} - search() {} - startTyping() {} - stopTyping() {} - get typing() {} - get typingCount() {} - createMessageCollector() {} - awaitMessages() {} - // Doesn't work on DM channels; bulkDelete() {} - acknowledge() {} - _cacheMessage() {} -} - -TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']); - -module.exports = DMChannel; - - -/***/ }), -/* 48 */ -/***/ (function(module, exports, __webpack_require__) { - -const Collection = __webpack_require__(3); -const Snowflake = __webpack_require__(7); - -const Targets = { - ALL: 'ALL', - GUILD: 'GUILD', - CHANNEL: 'CHANNEL', - USER: 'USER', - ROLE: 'ROLE', - INVITE: 'INVITE', - WEBHOOK: 'WEBHOOK', - EMOJI: 'EMOJI', - MESSAGE: 'MESSAGE', - UNKNOWN: 'UNKNOWN', -}; - -const Actions = { - ALL: null, - GUILD_UPDATE: 1, - CHANNEL_CREATE: 10, - CHANNEL_UPDATE: 11, - CHANNEL_DELETE: 12, - CHANNEL_OVERWRITE_CREATE: 13, - CHANNEL_OVERWRITE_UPDATE: 14, - CHANNEL_OVERWRITE_DELETE: 15, - MEMBER_KICK: 20, - MEMBER_PRUNE: 21, - MEMBER_BAN_ADD: 22, - MEMBER_BAN_REMOVE: 23, - MEMBER_UPDATE: 24, - MEMBER_ROLE_UPDATE: 25, - ROLE_CREATE: 30, - ROLE_UPDATE: 31, - ROLE_DELETE: 32, - INVITE_CREATE: 40, - INVITE_UPDATE: 41, - INVITE_DELETE: 42, - WEBHOOK_CREATE: 50, - WEBHOOK_UPDATE: 51, - WEBHOOK_DELETE: 52, - EMOJI_CREATE: 60, - EMOJI_UPDATE: 61, - EMOJI_DELETE: 62, - MESSAGE_DELETE: 72, -}; - - -/** - * Audit logs entries are held in this class. - */ -class GuildAuditLogs { - constructor(guild, data) { - if (data.users) for (const user of data.users) guild.client.dataManager.newUser(user); - - /** - * The entries for this guild's audit logs - * @type {Collection} - */ - this.entries = new Collection(); - for (const item of data.audit_log_entries) { - const entry = new GuildAuditLogsEntry(guild, item); - this.entries.set(entry.id, entry); - } - } - - /** - * Handles possible promises for entry targets. - * @returns {Promise} - */ - static build(...args) { - const logs = new GuildAuditLogs(...args); - return Promise.all(logs.entries.map(e => e.target)).then(() => logs); - } - - /** - * Find target type from entry action. - * @param {number} target The action target - * @returns {?string} - */ - static targetType(target) { - if (target < 10) return Targets.GUILD; - if (target < 20) return Targets.CHANNEL; - if (target < 30) return Targets.USER; - if (target < 40) return Targets.ROLE; - if (target < 50) return Targets.INVITE; - if (target < 60) return Targets.WEBHOOK; - if (target < 70) return Targets.EMOJI; - if (target < 80) return Targets.MESSAGE; - return Targets.UNKNOWN; - } - - - /** - * Find action type from entry action. - * @param {string} action The action target - * @returns {string} - */ - static actionType(action) { - if ([ - Actions.CHANNEL_CREATE, - Actions.CHANNEL_OVERWRITE_CREATE, - Actions.MEMBER_BAN_REMOVE, - Actions.ROLE_CREATE, - Actions.INVITE_CREATE, - Actions.WEBHOOK_CREATE, - Actions.EMOJI_CREATE, - ].includes(action)) return 'CREATE'; - - if ([ - Actions.CHANNEL_DELETE, - Actions.CHANNEL_OVERWRITE_DELETE, - Actions.MEMBER_KICK, - Actions.MEMBER_PRUNE, - Actions.MEMBER_BAN_ADD, - Actions.ROLE_DELETE, - Actions.INVITE_DELETE, - Actions.WEBHOOK_DELETE, - Actions.EMOJI_DELETE, - Actions.MESSAGE_DELETE, - ].includes(action)) return 'DELETE'; - - if ([ - Actions.GUILD_UPDATE, - Actions.CHANNEL_UPDATE, - Actions.CHANNEL_OVERWRITE_UPDATE, - Actions.MEMBER_UPDATE, - Actions.MEMBER_ROLE_UPDATE, - Actions.ROLE_UPDATE, - Actions.INVITE_UPDATE, - Actions.WEBHOOK_UPDATE, - Actions.EMOJI_UPDATE, - ].includes(action)) return 'UPDATE'; - - return 'ALL'; - } -} - -/** - * Audit logs entry. - */ -class GuildAuditLogsEntry { - constructor(guild, data) { - const targetType = GuildAuditLogs.targetType(data.action_type); - /** - * The target type of this entry - * @type {string} - */ - this.targetType = targetType; - - /** - * The action type of this entry - * @type {string} - */ - this.actionType = GuildAuditLogs.actionType(data.action_type); - - /** - * Specific action type of this entry - * @type {string} - */ - this.action = Object.keys(Actions).find(k => Actions[k] === data.action_type); - - /** - * The reason of this entry - * @type {?string} - */ - this.reason = data.reason || null; - - /** - * The user that executed this entry - * @type {User} - */ - this.executor = guild.client.users.get(data.user_id); - - /** - * An entry in the audit log representing a specific change - * @typedef {object} AuditLogChange - * @property {string} key The property that was changed, e.g. `nick` for nickname changes - * @property {*} [old] The old value of the change, e.g. for nicknames, the old nickname - * @property {*} [new] The new value of the change, e.g. for nicknames, the new nickname - */ - - /** - * Specific property changes - * @type {AuditLogChange[]} - */ - this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null; - - /** - * The ID of this entry - * @type {Snowflake} - */ - this.id = data.id; - - /** - * Any extra data from the entry - * @type {?Object|Role|GuildMember} - */ - this.extra = null; - if (data.options) { - if (data.action_type === Actions.MEMBER_PRUNE) { - this.extra = { - removed: data.options.members_removed, - days: data.options.delete_member_days, - }; - } else if (data.action_type === Actions.MESSAGE_DELETE) { - this.extra = { - count: data.options.count, - channel: guild.channels.get(data.options.channel_id), - }; - } else { - switch (data.options.type) { - case 'member': - this.extra = guild.members.get(data.options.id); - if (!this.extra) this.extra = { id: data.options.id }; - break; - case 'role': - this.extra = guild.roles.get(data.options.id); - if (!this.extra) this.extra = { id: data.options.id, name: data.options.role_name }; - break; - default: - break; - } - } - } - - - if (targetType === Targets.UNKNOWN) { - /** - * The target of this entry - * @type {Snowflake|Guild|User|Role|Emoji|Invite|Webhook} - */ - this.target = data.target_id; - } else if ([Targets.USER, Targets.GUILD].includes(targetType)) { - this.target = guild.client[`${targetType.toLowerCase()}s`].get(data.target_id); - } else if (targetType === Targets.WEBHOOK) { - this.target = guild.fetchWebhooks() - .then(hooks => { - this.target = hooks.find(h => h.id === data.target_id); - return this.target; - }); - } else if (targetType === Targets.INVITE) { - const change = this.changes.find(c => c.key === 'code'); - this.target = guild.fetchInvites() - .then(invites => { - this.target = invites.find(i => i.code === (change.new_value || change.old_value)); - return this.target; - }); - } else if (targetType === Targets.MESSAGE) { - this.target = guild.client.users.get(data.target_id); - } else { - this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id); - } - } - - /** - * The timestamp this entry was created at - * @type {number} - * @readonly - */ - get createdTimestamp() { - return Snowflake.deconstruct(this.id).timestamp; - } - - /** - * The time this entry was created - * @type {Date} - * @readonly - */ - get createdAt() { - return new Date(this.createdTimestamp); - } -} - -GuildAuditLogs.Actions = Actions; -GuildAuditLogs.Targets = Targets; -GuildAuditLogs.Entry = GuildAuditLogsEntry; - -module.exports = GuildAuditLogs; - - -/***/ }), -/* 49 */ -/***/ (function(module, exports) { - -/** - * Represents an attachment in a message. - */ -class MessageAttachment { - constructor(message, data) { - /** - * The client that instantiated this MessageAttachment - * @name MessageAttachment#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: message.client }); - - /** - * The message this attachment is part of - * @type {Message} - */ - this.message = message; - - this.setup(data); - } - - setup(data) { - /** - * The ID of this attachment - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The file name of this attachment - * @type {string} - */ - this.filename = data.filename; - - /** - * The size of this attachment in bytes - * @type {number} - */ - this.filesize = data.size; - - /** - * The URL to this attachment - * @type {string} - */ - this.url = data.url; - - /** - * The Proxy URL to this attachment - * @type {string} - */ - this.proxyURL = data.proxy_url; - - /** - * The height of this attachment (if an image) - * @type {?number} - */ - this.height = data.height; - - /** - * The width of this attachment (if an image) - * @type {?number} - */ - this.width = data.width; - } -} - -module.exports = MessageAttachment; - - -/***/ }), -/* 50 */ -/***/ (function(module, exports, __webpack_require__) { - -const Collector = __webpack_require__(35); - -/** - * @typedef {CollectorOptions} MessageCollectorOptions - * @property {number} max The maximum amount of messages to collect - * @property {number} maxProcessed The maximum amount of messages to process - */ - -/** - * Collects messages on a channel. - * @extends {Collector} - */ -class MessageCollector extends Collector { - - /** - * @param {TextChannel|DMChannel|GroupDMChannel} channel The channel - * @param {CollectorFilter} filter The filter to be applied to this collector - * @param {MessageCollectorOptions} options The options to be applied to this collector - * @emits MessageCollector#message - */ - constructor(channel, filter, options = {}) { - super(channel.client, filter, options); - - /** - * @type {TextBasedChannel} channel The channel - */ - this.channel = channel; - - /** - * Total number of messages that were received in the channel during message collection - * @type {number} - */ - this.received = 0; - - this.client.on('message', this.listener); - } - - /** - * Handle an incoming message for possible collection. - * @param {Message} message The message that could be collected - * @returns {?{key: Snowflake, value: Message}} Message data to collect - * @private - */ - handle(message) { - if (message.channel.id !== this.channel.id) return null; - this.received++; - return { - key: message.id, - value: message, - }; - } - - /** - * Check after collection to see if the collector is done. - * @returns {?string} Reason to end the collector, if any - * @private - */ - postCheck() { - if (this.options.max && this.collected.size >= this.options.max) return 'limit'; - if (this.options.maxProcessed && this.received === this.options.maxProcessed) return 'processedLimit'; - return null; - } - - /** - * Removes event listeners. - * @private - */ - cleanup() { - this.client.removeListener('message', this.listener); - } -} - -module.exports = MessageCollector; - - -/***/ }), -/* 51 */ -/***/ (function(module, exports, __webpack_require__) { - -const Collection = __webpack_require__(3); - -/** - * Keeps track of mentions in a {@link Message}. - */ -class MessageMentions { - constructor(message, users, roles, everyone) { - /** - * Whether `@everyone` or `@here` were mentioned - * @type {boolean} - */ - this.everyone = Boolean(everyone); - - if (users) { - if (users instanceof Collection) { - /** - * Any users that were mentioned - * @type {Collection} - */ - this.users = new Collection(users); - } else { - this.users = new Collection(); - for (const mention of users) { - let user = message.client.users.get(mention.id); - if (!user) user = message.client.dataManager.newUser(mention); - this.users.set(user.id, user); - } - } - } else { - this.users = new Collection(); - } - - if (roles) { - if (roles instanceof Collection) { - /** - * Any roles that were mentioned - * @type {Collection} - */ - this.roles = new Collection(roles); - } else { - this.roles = new Collection(); - for (const mention of roles) { - const role = message.channel.guild.roles.get(mention); - if (role) this.roles.set(role.id, role); - } - } - } else { - this.roles = new Collection(); - } - - /** - * Content of the message - * @type {Message} - * @private - */ - this._content = message.content; - - /** - * The client the message is from - * @type {Client} - * @private - */ - this._client = message.client; - - /** - * The guild the message is in - * @type {?Guild} - * @private - */ - this._guild = message.channel.guild; - - /** - * Cached members for {@MessageMention#members} - * @type {?Collection} - * @private - */ - this._members = null; - - /** - * Cached channels for {@MessageMention#channels} - * @type {?Collection} - * @private - */ - this._channels = null; - } - - /** - * Any members that were mentioned (only in {@link TextChannel}s) - * @type {?Collection} - * @readonly - */ - get members() { - if (this._members) return this._members; - if (!this._guild) return null; - this._members = new Collection(); - this.users.forEach(user => { - const member = this._guild.member(user); - if (member) this._members.set(member.user.id, member); - }); - return this._members; - } - - /** - * Any channels that were mentioned - * @type {?Collection} - * @readonly - */ - get channels() { - if (this._channels) return this._channels; - this._channels = new Collection(); - let matches; - while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) { - const chan = this._client.channels.get(matches[1]); - if (chan) this._channels.set(chan.id, chan); - } - return this._channels; - } -} - -/** - * Regular expression that globally matches `@everyone` and `@here` - * @type {RegExp} - */ -MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g; - -/** - * Regular expression that globally matches user mentions like `<@81440962496172032>` - * @type {RegExp} - */ -MessageMentions.USERS_PATTERN = /<@!?[0-9]+>/g; - -/** - * Regular expression that globally matches role mentions like `<@&297577916114403338>` - * @type {RegExp} - */ -MessageMentions.ROLES_PATTERN = /<@&[0-9]+>/g; - -/** - * Regular expression that globally matches channel mentions like `<#222079895583457280>` - * @type {RegExp} - */ -MessageMentions.CHANNELS_PATTERN = /<#([0-9]+)>/g; - -module.exports = MessageMentions; - - -/***/ }), -/* 52 */ -/***/ (function(module, exports, __webpack_require__) { - -const Collection = __webpack_require__(3); -const Emoji = __webpack_require__(18); -const ReactionEmoji = __webpack_require__(34); - -/** - * Represents a reaction to a message. - */ -class MessageReaction { - constructor(message, emoji, count, me) { - /** - * The message that this reaction refers to - * @type {Message} - */ - this.message = message; - - /** - * Whether the client has given this reaction - * @type {boolean} - */ - this.me = me; - - /** - * The number of people that have given the same reaction - * @type {number} - */ - this.count = count || 0; - - /** - * The users that have given this reaction, mapped by their ID - * @type {Collection} - */ - this.users = new Collection(); - - this._emoji = new ReactionEmoji(this, emoji.name, emoji.id); - } - - /** - * The emoji of this reaction, either an Emoji object for known custom emojis, or a ReactionEmoji - * object which has fewer properties. Whatever the prototype of the emoji, it will still have - * `name`, `id`, `identifier` and `toString()` - * @type {Emoji|ReactionEmoji} - * @readonly - */ - get emoji() { - if (this._emoji instanceof Emoji) return this._emoji; - // Check to see if the emoji has become known to the client - if (this._emoji.id) { - const emojis = this.message.client.emojis; - if (emojis.has(this._emoji.id)) { - const emoji = emojis.get(this._emoji.id); - this._emoji = emoji; - return emoji; - } - } - return this._emoji; - } - - /** - * Removes a user from this reaction. - * @param {UserResolvable} [user=this.message.client.user] The user to remove the reaction of - * @returns {Promise} - */ - remove(user = this.message.client.user) { - const userID = this.message.client.resolver.resolveUserID(user); - if (!userID) return Promise.reject(new Error('Couldn\'t resolve the user ID to remove from the reaction.')); - return this.message.client.api.channels[this.message.channel.id].messages[this.message.id] - .reactions[this.emoji.identifier][userID === this.message.client.user.id ? '@me' : userID] - .delete() - .then(() => - this.message.client.actions.MessageReactionRemove.handle({ - user_id: userID, - message_id: this.message.id, - emoji: this.emoji, - channel_id: this.message.channel.id, - }).reaction - ); - } - - /** - * Fetch all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs. - * @param {number} [limit=100] The maximum amount of users to fetch, defaults to 100 - * @returns {Promise>} - */ - fetchUsers(limit = 100) { - const message = this.message; - return message.client.api.channels[message.channel.id].messages[message.id] - .reactions[this.emoji.identifier] - .get({ query: { limit } }) - .then(users => { - this.users = new Collection(); - for (const rawUser of users) { - const user = message.client.dataManager.newUser(rawUser); - this.users.set(user.id, user); - } - this.count = this.users.size; - return this.users; - }); - } -} - -module.exports = MessageReaction; - - -/***/ }), -/* 53 */ -/***/ (function(module, exports) { - -/* -{ splash: null, - id: '123123123', - icon: '123123123', - name: 'name' } -*/ - -/** - * Represents a guild that the client only has limited information for - e.g. from invites. - */ -class PartialGuild { - constructor(client, data) { - /** - * The client that instantiated this PartialGuild - * @name PartialGuild#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - - this.setup(data); - } - - setup(data) { - /** - * The ID of this guild - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The name of this guild - * @type {string} - */ - this.name = data.name; - - /** - * The hash of this guild's icon - * @type {?string} - */ - this.icon = data.icon; - - /** - * The hash of the guild splash image (VIP only) - * @type {?string} - */ - this.splash = data.splash; - } -} - -module.exports = PartialGuild; - - -/***/ }), -/* 54 */ -/***/ (function(module, exports, __webpack_require__) { - -const Constants = __webpack_require__(0); - -/* -{ type: 0, id: '123123', name: 'heavy-testing' } } -*/ - -/** - * Represents a guild channel that the client only has limited information for - e.g. from invites. - */ -class PartialGuildChannel { - constructor(client, data) { - /** - * The client that instantiated this PartialGuildChannel - * @name PartialGuildChannel#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - - this.setup(data); - } - - setup(data) { - /** - * The ID of this guild channel - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The name of this guild channel - * @type {string} - */ - this.name = data.name; - - /** - * The type of this guild channel - `text` or `voice` - * @type {string} - */ - this.type = Constants.ChannelTypes.TEXT === data.type ? 'text' : 'voice'; - } -} - -module.exports = PartialGuildChannel; - - -/***/ }), -/* 55 */ -/***/ (function(module, exports, __webpack_require__) { - -const Permissions = __webpack_require__(10); - -/** - * Represents a permission overwrite for a role or member in a guild channel. - */ -class PermissionOverwrites { - constructor(guildChannel, data) { - /** - * The GuildChannel this overwrite is for - * @name PermissionOverwrites#channel - * @type {GuildChannel} - * @readonly - */ - Object.defineProperty(this, 'channel', { value: guildChannel }); - - if (data) this.setup(data); - } - - setup(data) { - /** - * The ID of this overwrite, either a user ID or a role ID - * @type {Snowflake} - */ - this.id = data.id; - - /** - * The type of this overwrite - * @type {string} - */ - this.type = data.type; - - this._denied = data.deny; - this._allowed = data.allow; - - /** - * The permissions that are denied for the user or role. - * @type {Permissions} - */ - this.denied = new Permissions(this._denied); - - /** - * The permissions that are allowed for the user or role. - * @type {Permissions} - */ - this.allowed = new Permissions(this._allowed); - } - - /** - * Delete this Permission Overwrite. - * @param {string} [reason] Reason for deleting this overwrite - * @returns {Promise} - */ - delete(reason) { - return this.channel.client.api.channels[this.channel.id].permissions[this.id] - .delete({ reason }) - .then(() => this); - } -} - -module.exports = PermissionOverwrites; - - -/***/ }), -/* 56 */ -/***/ (function(module, exports, __webpack_require__) { - -const Collector = __webpack_require__(35); -const Collection = __webpack_require__(3); - -/** - * @typedef {CollectorOptions} ReactionCollectorOptions - * @property {number} max The maximum total amount of reactions to collect - * @property {number} maxEmojis The maximum number of emojis to collect - * @property {number} maxUsers The maximum number of users to react - */ - -/** - * Collects reactions on messages. - * @extends {Collector} - */ -class ReactionCollector extends Collector { - - /** - * @param {Message} message The message upon which to collect reactions - * @param {CollectorFilter} filter The filter to apply to this collector - * @param {ReactionCollectorOptions} [options={}] The options to apply to this collector - */ - constructor(message, filter, options = {}) { - super(message.client, filter, options); - - /** - * The message - * @type {Message} - */ - this.message = message; - - /** - * The users which have reacted - * @type {Collection} - */ - this.users = new Collection(); - - /** - * The total number of reactions collected - * @type {number} - */ - this.total = 0; - - this.client.on('messageReactionAdd', this.listener); - } - - /** - * Handle an incoming reaction for possible collection. - * @param {MessageReaction} reaction The reaction to possibly collect - * @returns {?{key: Snowflake, value: MessageReaction}} Reaction data to collect - * @private - */ - handle(reaction) { - if (reaction.message.id !== this.message.id) return null; - return { - key: reaction.emoji.id || reaction.emoji.name, - value: reaction, - }; - } - - /** - * Check after collection to see if the collector is done. - * @param {MessageReaction} reaction The reaction that was collected - * @param {User} user The user that reacted - * @returns {?string} Reason to end the collector, if any - * @private - */ - postCheck(reaction, user) { - this.users.set(user.id, user); - if (this.options.max && ++this.total >= this.options.max) return 'limit'; - if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit'; - if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit'; - return null; - } - - /** - * Remove event listeners. - * @private - */ - cleanup() { - this.client.removeListener('messageReactionAdd', this.listener); - } -} - -module.exports = ReactionCollector; - - -/***/ }), -/* 57 */ -/***/ (function(module, exports, __webpack_require__) { - -const GuildChannel = __webpack_require__(27); -const Webhook = __webpack_require__(22); -const TextBasedChannel = __webpack_require__(25); -const Collection = __webpack_require__(3); - -/** - * Represents a guild text channel on Discord. - * @extends {GuildChannel} - * @implements {TextBasedChannel} - */ -class TextChannel extends GuildChannel { - constructor(guild, data) { - super(guild, data); - this.type = 'text'; - this.messages = new Collection(); - this._typing = new Map(); - } - - setup(data) { - super.setup(data); - - /** - * The topic of the text channel - * @type {?string} - */ - this.topic = data.topic; - - this.lastMessageID = data.last_message_id; - } - - /** - * A collection of members that can see this channel, mapped by their ID - * @type {Collection} - * @readonly - */ - get members() { - const members = new Collection(); - for (const member of this.guild.members.values()) { - if (this.permissionsFor(member).has('READ_MESSAGES')) { - members.set(member.id, member); - } - } - return members; - } - - /** - * If the Discord considers this channel NSFW - * @type {boolean} - * @readonly - */ - get nsfw() { - return /^nsfw(-|$)/.test(this.name); - } - - /** - * Fetch all webhooks for the channel. - * @returns {Promise>} - */ - fetchWebhooks() { - return this.client.api.channels[this.id].webhooks.get().then(data => { - const hooks = new Collection(); - for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); - return hooks; - }); - } - - /** - * Create a webhook for the channel. - * @param {string} name The name of the webhook - * @param {BufferResolvable|Base64Resolvable} avatar The avatar for the webhook - * @returns {Promise} webhook The created webhook - * @example - * channel.createWebhook('Snek', 'http://snek.s3.amazonaws.com/topSnek.png') - * .then(webhook => console.log(`Created webhook ${webhook}`)) - * .catch(console.error) - */ - createWebhook(name, avatar) { - if (typeof avatar === 'string' && avatar.startsWith('data:')) { - return this.client.api.channels[this.id].webhooks.post({ data: { - name, avatar, - } }).then(data => new Webhook(this.client, data)); - } else { - return this.client.resolver.resolveBuffer(avatar).then(data => - this.createWebhook(name, this.client.resolver.resolveBase64(data) || null)); - } - } - - // These are here only for documentation purposes - they are implemented by TextBasedChannel - /* eslint-disable no-empty-function */ - send() {} - fetchMessage() {} - fetchMessages() {} - fetchPinnedMessages() {} - search() {} - startTyping() {} - stopTyping() {} - get typing() {} - get typingCount() {} - createMessageCollector() {} - awaitMessages() {} - bulkDelete() {} - acknowledge() {} - _cacheMessage() {} -} - -TextBasedChannel.applyToClass(TextChannel, true); - -module.exports = TextChannel; - - -/***/ }), -/* 58 */ -/***/ (function(module, exports, __webpack_require__) { - -const GuildChannel = __webpack_require__(27); -const Collection = __webpack_require__(3); - -/** - * Represents a guild voice channel on Discord. - * @extends {GuildChannel} - */ -class VoiceChannel extends GuildChannel { - constructor(guild, data) { - super(guild, data); - - /** - * The members in this voice channel - * @type {Collection} - */ - this.members = new Collection(); - - this.type = 'voice'; - } - - setup(data) { - super.setup(data); - - /** - * The bitrate of this voice channel - * @type {number} - */ - this.bitrate = data.bitrate; - - /** - * The maximum amount of users allowed in this channel - 0 means unlimited. - * @type {number} - */ - this.userLimit = data.user_limit; - } - - /** - * The voice connection for this voice channel, if the client is connected - * @type {?VoiceConnection} - * @readonly - */ - get connection() { - const connection = this.guild.voiceConnection; - if (connection && connection.channel.id === this.id) return connection; - return null; - } - - /** - * Checks if the voice channel is full - * @type {boolean} - * @readonly - */ - get full() { - return this.userLimit > 0 && this.members.size >= this.userLimit; - } - - /** - * Checks if the client has permission join the voice channel - * @type {boolean} - * @readonly - */ - get joinable() { - if (this.client.browser) return false; - if (!this.permissionsFor(this.client.user).has('CONNECT')) return false; - if (this.full && !this.permissionsFor(this.client.user).has('MOVE_MEMBERS')) return false; - return true; - } - - /** - * Checks if the client has permission to send audio to the voice channel - * @type {boolean} - * @readonly - */ - get speakable() { - return this.permissionsFor(this.client.user).has('SPEAK'); - } - - /** - * Sets the bitrate of the channel. - * @param {number} bitrate The new bitrate - * @returns {Promise} - * @example - * // Set the bitrate of a voice channel - * voiceChannel.setBitrate(48000) - * .then(vc => console.log(`Set bitrate to ${vc.bitrate} for ${vc.name}`)) - * .catch(console.error); - */ - setBitrate(bitrate) { - return this.edit({ bitrate }); - } - - /** - * Sets the user limit of the channel. - * @param {number} userLimit The new user limit - * @returns {Promise} - * @example - * // Set the user limit of a voice channel - * voiceChannel.setUserLimit(42) - * .then(vc => console.log(`Set user limit to ${vc.userLimit} for ${vc.name}`)) - * .catch(console.error); - */ - setUserLimit(userLimit) { - return this.edit({ userLimit }); - } - - /** - * Attempts to join this voice channel. - * @returns {Promise} - * @example - * // Join a voice channel - * voiceChannel.join() - * .then(connection => console.log('Connected!')) - * .catch(console.error); - */ - join() { - if (this.client.browser) return Promise.reject(new Error('Voice connections are not available in browsers.')); - return this.client.voice.joinChannel(this); - } - - /** - * Leaves this voice channel. - * @example - * // Leave a voice channel - * voiceChannel.leave(); - */ - leave() { - if (this.client.browser) return; - const connection = this.client.voice.connections.get(this.guild.id); - if (connection && connection.channel.id === this.id) connection.disconnect(); - } -} - -module.exports = VoiceChannel; - - -/***/ }), -/* 59 */ -/***/ (function(module, exports) { - -var toString = {}.toString; - -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; - - -/***/ }), -/* 60 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(process) { +var processNextTick = __webpack_require__(27); +/**/ module.exports = Readable; /**/ -var processNextTick = __webpack_require__(37); -/**/ - -/**/ -var isArray = __webpack_require__(59); +var isArray = __webpack_require__(45); /**/ /**/ @@ -14551,7 +13060,7 @@ var Duplex; Readable.ReadableState = ReadableState; /**/ -var EE = __webpack_require__(15).EventEmitter; +var EE = __webpack_require__(12).EventEmitter; var EElistenerCount = function (emitter, type) { return emitter.listeners(type).length; @@ -14559,20 +13068,29 @@ var EElistenerCount = function (emitter, type) { /**/ /**/ -var Stream = __webpack_require__(62); +var Stream = __webpack_require__(47); +/**/ + +// TODO(bmeurer): Change this back to const once hole checks are +// properly optimized away early in Ignition+TurboFan. +/**/ +var Buffer = __webpack_require__(35).Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} /**/ /**/ -var Buffer = __webpack_require__(40).Buffer; +var util = __webpack_require__(20); +util.inherits = __webpack_require__(13); /**/ /**/ -var util = __webpack_require__(23); -util.inherits = __webpack_require__(12); -/**/ - -/**/ -var debugUtil = __webpack_require__(191); +var debugUtil = __webpack_require__(81); var debug = void 0; if (debugUtil && debugUtil.debuglog) { debug = debugUtil.debuglog('stream'); @@ -14581,7 +13099,8 @@ if (debugUtil && debugUtil.debuglog) { } /**/ -var BufferList = __webpack_require__(90); +var BufferList = __webpack_require__(82); +var destroyImpl = __webpack_require__(48); var StringDecoder; util.inherits(Readable, Stream); @@ -14603,7 +13122,7 @@ function prependListener(emitter, event, fn) { } function ReadableState(options, stream) { - Duplex = Duplex || __webpack_require__(16); + Duplex = Duplex || __webpack_require__(14); options = options || {}; @@ -14620,7 +13139,7 @@ function ReadableState(options, stream) { this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; // cast to ints. - this.highWaterMark = ~~this.highWaterMark; + this.highWaterMark = Math.floor(this.highWaterMark); // A linked list is used to store data chunks instead of an array because the // linked list can remove elements from the beginning faster than @@ -14634,10 +13153,10 @@ function ReadableState(options, stream) { this.endEmitted = false; this.reading = false; - // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. + // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. this.sync = true; // whenever we return null, then we set a flag to say @@ -14647,15 +13166,14 @@ function ReadableState(options, stream) { this.readableListening = false; this.resumeScheduled = false; + // has it been destroyed + this.destroyed = false; + // Crypto is kind of old and crusty. Historically, its default string // encoding is 'binary' so we have to make this configurable. // Everything else in the universe uses 'utf8', though. this.defaultEncoding = options.defaultEncoding || 'utf8'; - // when piping, we only care about 'readable' events that happen - // after read()ing all the bytes and not getting any pushback. - this.ranOut = false; - // the number of writers that are awaiting a drain event in .pipe()s this.awaitDrain = 0; @@ -14665,14 +13183,14 @@ function ReadableState(options, stream) { this.decoder = null; this.encoding = null; if (options.encoding) { - if (!StringDecoder) StringDecoder = __webpack_require__(67).StringDecoder; + if (!StringDecoder) StringDecoder = __webpack_require__(49).StringDecoder; this.decoder = new StringDecoder(options.encoding); this.encoding = options.encoding; } } function Readable(options) { - Duplex = Duplex || __webpack_require__(16); + Duplex = Duplex || __webpack_require__(14); if (!(this instanceof Readable)) return new Readable(options); @@ -14681,87 +13199,129 @@ function Readable(options) { // legacy this.readable = true; - if (options && typeof options.read === 'function') this._read = options.read; + if (options) { + if (typeof options.read === 'function') this._read = options.read; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + } Stream.call(this); } +Object.defineProperty(Readable.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined) { + return false; + } + return this._readableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + } +}); + +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; +Readable.prototype._destroy = function (err, cb) { + this.push(null); + cb(err); +}; + // Manually shove something into the read() buffer. // This returns true if the highWaterMark has not been hit yet, // similar to how Writable.write() returns true if you should // write() some more. Readable.prototype.push = function (chunk, encoding) { var state = this._readableState; + var skipChunkCheck; - if (!state.objectMode && typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + skipChunkCheck = true; } + } else { + skipChunkCheck = true; } - return readableAddChunk(this, state, chunk, encoding, false); + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); }; // Unshift should *always* be something directly out of read() Readable.prototype.unshift = function (chunk) { - var state = this._readableState; - return readableAddChunk(this, state, chunk, '', true); + return readableAddChunk(this, chunk, null, true, false); }; -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; - -function readableAddChunk(stream, state, chunk, encoding, addToFront) { - var er = chunkInvalid(state, chunk); - if (er) { - stream.emit('error', er); - } else if (chunk === null) { +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + var state = stream._readableState; + if (chunk === null) { state.reading = false; onEofChunk(stream, state); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (state.ended && !addToFront) { - var e = new Error('stream.push() after EOF'); - stream.emit('error', e); - } else if (state.endEmitted && addToFront) { - var _e = new Error('stream.unshift() after end event'); - stream.emit('error', _e); - } else { - var skipAdd; - if (state.decoder && !addToFront && !encoding) { - chunk = state.decoder.write(chunk); - skipAdd = !state.objectMode && chunk.length === 0; + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); } - if (!addToFront) state.reading = false; - - // Don't add to the buffer if we've decoded to an empty string chunk and - // we're not in object mode - if (!skipAdd) { - // if we want the data now, just emit it. - if (state.flowing && state.length === 0 && !state.sync) { - stream.emit('data', chunk); - stream.read(0); + if (addToFront) { + if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); + } else if (state.ended) { + stream.emit('error', new Error('stream.push() after EOF')); + } else { + state.reading = false; + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - - if (state.needReadable) emitReadable(stream); + addChunk(stream, state, chunk, false); } } - - maybeReadMore(stream, state); + } else if (!addToFront) { + state.reading = false; } - } else if (!addToFront) { - state.reading = false; } return needMoreData(state); } +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); + + if (state.needReadable) emitReadable(stream); + } + maybeReadMore(stream, state); +} + +function chunkInvalid(state, chunk) { + var er; + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + // if it's past the high water mark, we can push in some more. // Also, if we have no data yet, we can stand some // more bytes. This is to work around cases where hwm=0, @@ -14773,9 +13333,13 @@ function needMoreData(state) { return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); } +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; + // backwards compatibility. Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = __webpack_require__(67).StringDecoder; + if (!StringDecoder) StringDecoder = __webpack_require__(49).StringDecoder; this._readableState.decoder = new StringDecoder(enc); this._readableState.encoding = enc; return this; @@ -14921,14 +13485,6 @@ Readable.prototype.read = function (n) { return ret; }; -function chunkInvalid(state, chunk) { - var er = null; - if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - return er; -} - function onEofChunk(stream, state) { if (state.ended) return; if (state.decoder) { @@ -15020,10 +13576,13 @@ Readable.prototype.pipe = function (dest, pipeOpts) { if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn); dest.on('unpipe', onunpipe); - function onunpipe(readable) { + function onunpipe(readable, unpipeInfo) { debug('onunpipe'); if (readable === src) { - cleanup(); + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } } } @@ -15142,6 +13701,7 @@ function pipeOnDrain(src) { Readable.prototype.unpipe = function (dest) { var state = this._readableState; + var unpipeInfo = { hasUnpiped: false }; // if we're not piping anywhere, then do nothing. if (state.pipesCount === 0) return this; @@ -15157,7 +13717,7 @@ Readable.prototype.unpipe = function (dest) { state.pipes = null; state.pipesCount = 0; state.flowing = false; - if (dest) dest.emit('unpipe', this); + if (dest) dest.emit('unpipe', this, unpipeInfo); return this; } @@ -15172,7 +13732,7 @@ Readable.prototype.unpipe = function (dest) { state.flowing = false; for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this); + dests[i].emit('unpipe', this, unpipeInfo); }return this; } @@ -15184,7 +13744,7 @@ Readable.prototype.unpipe = function (dest) { state.pipesCount -= 1; if (state.pipesCount === 1) state.pipes = state.pipes[0]; - dest.emit('unpipe', this); + dest.emit('unpipe', this, unpipeInfo); return this; }; @@ -15205,7 +13765,7 @@ Readable.prototype.on = function (ev, fn) { if (!state.reading) { processNextTick(nReadingNextTick, this); } else if (state.length) { - emitReadable(this, state); + emitReadable(this); } } } @@ -15466,13 +14026,346 @@ function indexOf(xs, x) { } return -1; } -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7), __webpack_require__(8))) /***/ }), -/* 61 */ +/* 47 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(12).EventEmitter; + + +/***/ }), +/* 48 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + + +/**/ + +var processNextTick = __webpack_require__(27); +/**/ + +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; + + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + processNextTick(emitErrorNT, this, err); + } + return; + } + + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + if (this._readableState) { + this._readableState.destroyed = true; + } + + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + processNextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; + } + } else if (cb) { + cb(err); + } + }); +} + +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } + + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} + +function emitErrorNT(self, err) { + self.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy +}; + +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var Buffer = __webpack_require__(6).Buffer; + +var isBufferEncoding = Buffer.isEncoding + || function(encoding) { + switch (encoding && encoding.toLowerCase()) { + case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; + default: return false; + } + } + + +function assertEncoding(encoding) { + if (encoding && !isBufferEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); + } +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. CESU-8 is handled as part of the UTF-8 encoding. +// +// @TODO Handling all encodings inside a single object makes it very difficult +// to reason about this code, so it should be split up in the future. +// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code +// points as used by CESU-8. +var StringDecoder = exports.StringDecoder = function(encoding) { + this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); + assertEncoding(encoding); + switch (this.encoding) { + case 'utf8': + // CESU-8 represents each of Surrogate Pair by 3-bytes + this.surrogateSize = 3; + break; + case 'ucs2': + case 'utf16le': + // UTF-16 represents each of Surrogate Pair by 2-bytes + this.surrogateSize = 2; + this.detectIncompleteChar = utf16DetectIncompleteChar; + break; + case 'base64': + // Base-64 stores 3 bytes in 4 chars, and pads the remainder. + this.surrogateSize = 3; + this.detectIncompleteChar = base64DetectIncompleteChar; + break; + default: + this.write = passThroughWrite; + return; + } + + // Enough space to store all bytes of a single character. UTF-8 needs 4 + // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate). + this.charBuffer = new Buffer(6); + // Number of bytes received for the current incomplete multi-byte character. + this.charReceived = 0; + // Number of bytes expected for the current incomplete multi-byte character. + this.charLength = 0; +}; + + +// write decodes the given buffer and returns it as JS string that is +// guaranteed to not contain any partial multi-byte characters. Any partial +// character found at the end of the buffer is buffered up, and will be +// returned when calling write again with the remaining bytes. +// +// Note: Converting a Buffer containing an orphan surrogate to a String +// currently works, but converting a String to a Buffer (via `new Buffer`, or +// Buffer#write) will replace incomplete surrogates with the unicode +// replacement character. See https://codereview.chromium.org/121173009/ . +StringDecoder.prototype.write = function(buffer) { + var charStr = ''; + // if our last write ended with an incomplete multibyte character + while (this.charLength) { + // determine how many remaining bytes this buffer has to offer for this char + var available = (buffer.length >= this.charLength - this.charReceived) ? + this.charLength - this.charReceived : + buffer.length; + + // add the new bytes to the char buffer + buffer.copy(this.charBuffer, this.charReceived, 0, available); + this.charReceived += available; + + if (this.charReceived < this.charLength) { + // still not enough chars in this buffer? wait for more ... + return ''; + } + + // remove bytes belonging to the current character from the buffer + buffer = buffer.slice(available, buffer.length); + + // get the character that was split + charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); + + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + var charCode = charStr.charCodeAt(charStr.length - 1); + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + this.charLength += this.surrogateSize; + charStr = ''; + continue; + } + this.charReceived = this.charLength = 0; + + // if there are no more bytes in this buffer, just emit our char + if (buffer.length === 0) { + return charStr; + } + break; + } + + // determine and set charLength / charReceived + this.detectIncompleteChar(buffer); + + var end = buffer.length; + if (this.charLength) { + // buffer the incomplete character bytes we got + buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end); + end -= this.charReceived; + } + + charStr += buffer.toString(this.encoding, 0, end); + + var end = charStr.length - 1; + var charCode = charStr.charCodeAt(end); + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + var size = this.surrogateSize; + this.charLength += size; + this.charReceived += size; + this.charBuffer.copy(this.charBuffer, size, 0, size); + buffer.copy(this.charBuffer, 0, 0, size); + return charStr.substring(0, end); + } + + // or just emit the charStr + return charStr; +}; + +// detectIncompleteChar determines if there is an incomplete UTF-8 character at +// the end of the given buffer. If so, it sets this.charLength to the byte +// length that character, and sets this.charReceived to the number of bytes +// that are available for this character. +StringDecoder.prototype.detectIncompleteChar = function(buffer) { + // determine how many bytes we have to check at the end of this buffer + var i = (buffer.length >= 3) ? 3 : buffer.length; + + // Figure out if one of the last i bytes of our buffer announces an + // incomplete char. + for (; i > 0; i--) { + var c = buffer[buffer.length - i]; + + // See http://en.wikipedia.org/wiki/UTF-8#Description + + // 110XXXXX + if (i == 1 && c >> 5 == 0x06) { + this.charLength = 2; + break; + } + + // 1110XXXX + if (i <= 2 && c >> 4 == 0x0E) { + this.charLength = 3; + break; + } + + // 11110XXX + if (i <= 3 && c >> 3 == 0x1E) { + this.charLength = 4; + break; + } + } + this.charReceived = i; +}; + +StringDecoder.prototype.end = function(buffer) { + var res = ''; + if (buffer && buffer.length) + res = this.write(buffer); + + if (this.charReceived) { + var cr = this.charReceived; + var buf = this.charBuffer; + var enc = this.encoding; + res += buf.slice(0, cr).toString(enc); + } + + return res; +}; + +function passThroughWrite(buffer) { + return buffer.toString(this.encoding); +} + +function utf16DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 2; + this.charLength = this.charReceived ? 2 : 0; +} + +function base64DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 3; + this.charLength = this.charReceived ? 3 : 0; +} + + +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + // a transform stream is a readable/writable stream where you do // something with the data. Sometimes it's called a "filter", // but that's not a great name for it, since that implies a thing where @@ -15519,11 +14412,11 @@ function indexOf(xs, x) { module.exports = Transform; -var Duplex = __webpack_require__(16); +var Duplex = __webpack_require__(14); /**/ -var util = __webpack_require__(23); -util.inherits = __webpack_require__(12); +var util = __webpack_require__(20); +util.inherits = __webpack_require__(13); /**/ util.inherits(Transform, Duplex); @@ -15546,7 +14439,9 @@ function afterTransform(stream, er, data) { var cb = ts.writecb; - if (!cb) return stream.emit('error', new Error('no writecb in Transform class')); + if (!cb) { + return stream.emit('error', new Error('write callback called multiple times')); + } ts.writechunk = null; ts.writecb = null; @@ -15639,6 +14534,15 @@ Transform.prototype._read = function (n) { } }; +Transform.prototype._destroy = function (err, cb) { + var _this = this; + + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this.emit('close'); + }); +}; + function done(stream, er, data) { if (er) return stream.emit('error', er); @@ -15657,153 +14561,13 @@ function done(stream, er, data) { } /***/ }), -/* 62 */ +/* 51 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = __webpack_require__(15).EventEmitter; - - -/***/ }), -/* 63 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -module.exports = Stream; - -var EE = __webpack_require__(15).EventEmitter; -var inherits = __webpack_require__(12); - -inherits(Stream, EE); -Stream.Readable = __webpack_require__(24); -Stream.Writable = __webpack_require__(93); -Stream.Duplex = __webpack_require__(88); -Stream.Transform = __webpack_require__(92); -Stream.PassThrough = __webpack_require__(91); - -// Backwards-compat with node 0.4.x -Stream.Stream = Stream; - - - -// old-style streams. Note that the pipe method (the only relevant -// part of this class) is overridden in the Readable class. - -function Stream() { - EE.call(this); -} - -Stream.prototype.pipe = function(dest, options) { - var source = this; - - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - - source.on('data', ondata); - - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - - dest.on('drain', ondrain); - - // If the 'end' option is not supplied, dest.end() will be called when - // source gets the 'end' or 'close' events. Only dest.end() once. - if (!dest._isStdio && (!options || options.end !== false)) { - source.on('end', onend); - source.on('close', onclose); - } - - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - - dest.end(); - } - - - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - - if (typeof dest.destroy === 'function') dest.destroy(); - } - - // don't leave dangling pipes when there are errors. - function onerror(er) { - cleanup(); - if (EE.listenerCount(this, 'error') === 0) { - throw er; // Unhandled stream error in pipe. - } - } - - source.on('error', onerror); - dest.on('error', onerror); - - // remove all the event listeners that were added. - function cleanup() { - source.removeListener('data', ondata); - dest.removeListener('drain', ondrain); - - source.removeListener('end', onend); - source.removeListener('close', onclose); - - source.removeListener('error', onerror); - dest.removeListener('error', onerror); - - source.removeListener('end', cleanup); - source.removeListener('close', cleanup); - - dest.removeListener('close', cleanup); - } - - source.on('end', cleanup); - source.on('close', cleanup); - - dest.on('close', cleanup); - - dest.emit('pipe', source); - - // Allow for unix-like usage: A.pipe(B).pipe(C) - return dest; -}; - - -/***/ }), -/* 64 */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(global) {var ClientRequest = __webpack_require__(101) -var extend = __webpack_require__(110) -var statusCodes = __webpack_require__(82) -var url = __webpack_require__(66) +/* WEBPACK VAR INJECTION */(function(global) {var ClientRequest = __webpack_require__(93) +var extend = __webpack_require__(96) +var statusCodes = __webpack_require__(97) +var url = __webpack_require__(53) var http = exports @@ -15879,10 +14643,10 @@ http.METHODS = [ 'UNLOCK', 'UNSUBSCRIBE' ] -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11))) +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7))) /***/ }), -/* 65 */ +/* 52 */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global) {exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableStream) @@ -15955,10 +14719,10 @@ function isFunction (value) { xhr = null // Help gc -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11))) +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7))) /***/ }), -/* 66 */ +/* 53 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15985,8 +14749,8 @@ xhr = null // Help gc -var punycode = __webpack_require__(85); -var util = __webpack_require__(105); +var punycode = __webpack_require__(98); +var util = __webpack_require__(100); exports.parse = urlParse; exports.resolve = urlResolve; @@ -16061,7 +14825,7 @@ var protocolPattern = /^([a-z0-9.+-]+:)/i, 'gopher:': true, 'file:': true }, - querystring = __webpack_require__(38); + querystring = __webpack_require__(37); function urlParse(url, parseQueryString, slashesDenoteHost) { if (url && util.isObject(url) && url instanceof Url) return url; @@ -16697,491 +15461,204 @@ Url.prototype.parseHost = function() { /***/ }), -/* 67 */ +/* 54 */ /***/ (function(module, exports, __webpack_require__) { -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. +const mimes = __webpack_require__(104); +const mimeOfBuffer = __webpack_require__(105); -var Buffer = __webpack_require__(6).Buffer; - -var isBufferEncoding = Buffer.isEncoding - || function(encoding) { - switch (encoding && encoding.toLowerCase()) { - case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; - default: return false; - } - } - - -function assertEncoding(encoding) { - if (encoding && !isBufferEncoding(encoding)) { - throw new Error('Unknown encoding: ' + encoding); - } +function lookupMime(ext) { + return mimes[ext.replace(/^\./, '')] || mimes.bin; } -// StringDecoder provides an interface for efficiently splitting a series of -// buffers into a series of JS strings without breaking apart multi-byte -// characters. CESU-8 is handled as part of the UTF-8 encoding. -// -// @TODO Handling all encodings inside a single object makes it very difficult -// to reason about this code, so it should be split up in the future. -// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code -// points as used by CESU-8. -var StringDecoder = exports.StringDecoder = function(encoding) { - this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); - assertEncoding(encoding); - switch (this.encoding) { - case 'utf8': - // CESU-8 represents each of Surrogate Pair by 3-bytes - this.surrogateSize = 3; - break; - case 'ucs2': - case 'utf16le': - // UTF-16 represents each of Surrogate Pair by 2-bytes - this.surrogateSize = 2; - this.detectIncompleteChar = utf16DetectIncompleteChar; - break; - case 'base64': - // Base-64 stores 3 bytes in 4 chars, and pads the remainder. - this.surrogateSize = 3; - this.detectIncompleteChar = base64DetectIncompleteChar; - break; - default: - this.write = passThroughWrite; - return; - } - - // Enough space to store all bytes of a single character. UTF-8 needs 4 - // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate). - this.charBuffer = new Buffer(6); - // Number of bytes received for the current incomplete multi-byte character. - this.charReceived = 0; - // Number of bytes expected for the current incomplete multi-byte character. - this.charLength = 0; -}; - - -// write decodes the given buffer and returns it as JS string that is -// guaranteed to not contain any partial multi-byte characters. Any partial -// character found at the end of the buffer is buffered up, and will be -// returned when calling write again with the remaining bytes. -// -// Note: Converting a Buffer containing an orphan surrogate to a String -// currently works, but converting a String to a Buffer (via `new Buffer`, or -// Buffer#write) will replace incomplete surrogates with the unicode -// replacement character. See https://codereview.chromium.org/121173009/ . -StringDecoder.prototype.write = function(buffer) { - var charStr = ''; - // if our last write ended with an incomplete multibyte character - while (this.charLength) { - // determine how many remaining bytes this buffer has to offer for this char - var available = (buffer.length >= this.charLength - this.charReceived) ? - this.charLength - this.charReceived : - buffer.length; - - // add the new bytes to the char buffer - buffer.copy(this.charBuffer, this.charReceived, 0, available); - this.charReceived += available; - - if (this.charReceived < this.charLength) { - // still not enough chars in this buffer? wait for more ... - return ''; - } - - // remove bytes belonging to the current character from the buffer - buffer = buffer.slice(available, buffer.length); - - // get the character that was split - charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); - - // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character - var charCode = charStr.charCodeAt(charStr.length - 1); - if (charCode >= 0xD800 && charCode <= 0xDBFF) { - this.charLength += this.surrogateSize; - charStr = ''; - continue; - } - this.charReceived = this.charLength = 0; - - // if there are no more bytes in this buffer, just emit our char - if (buffer.length === 0) { - return charStr; - } - break; - } - - // determine and set charLength / charReceived - this.detectIncompleteChar(buffer); - - var end = buffer.length; - if (this.charLength) { - // buffer the incomplete character bytes we got - buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end); - end -= this.charReceived; - } - - charStr += buffer.toString(this.encoding, 0, end); - - var end = charStr.length - 1; - var charCode = charStr.charCodeAt(end); - // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character - if (charCode >= 0xD800 && charCode <= 0xDBFF) { - var size = this.surrogateSize; - this.charLength += size; - this.charReceived += size; - this.charBuffer.copy(this.charBuffer, size, 0, size); - buffer.copy(this.charBuffer, 0, 0, size); - return charStr.substring(0, end); - } - - // or just emit the charStr - return charStr; -}; - -// detectIncompleteChar determines if there is an incomplete UTF-8 character at -// the end of the given buffer. If so, it sets this.charLength to the byte -// length that character, and sets this.charReceived to the number of bytes -// that are available for this character. -StringDecoder.prototype.detectIncompleteChar = function(buffer) { - // determine how many bytes we have to check at the end of this buffer - var i = (buffer.length >= 3) ? 3 : buffer.length; - - // Figure out if one of the last i bytes of our buffer announces an - // incomplete char. - for (; i > 0; i--) { - var c = buffer[buffer.length - i]; - - // See http://en.wikipedia.org/wiki/UTF-8#Description - - // 110XXXXX - if (i == 1 && c >> 5 == 0x06) { - this.charLength = 2; - break; - } - - // 1110XXXX - if (i <= 2 && c >> 4 == 0x0E) { - this.charLength = 3; - break; - } - - // 11110XXX - if (i <= 3 && c >> 3 == 0x1E) { - this.charLength = 4; - break; - } - } - this.charReceived = i; -}; - -StringDecoder.prototype.end = function(buffer) { - var res = ''; - if (buffer && buffer.length) - res = this.write(buffer); - - if (this.charReceived) { - var cr = this.charReceived; - var buf = this.charBuffer; - var enc = this.encoding; - res += buf.slice(0, cr).toString(enc); - } - - return res; -}; - -function passThroughWrite(buffer) { - return buffer.toString(this.encoding); +function lookupBuffer(buffer) { + return mimeOfBuffer(buffer) || mimes.bin; } -function utf16DetectIncompleteChar(buffer) { - this.charReceived = buffer.length % 2; - this.charLength = this.charReceived ? 2 : 0; -} - -function base64DetectIncompleteChar(buffer) { - this.charReceived = buffer.length % 3; - this.charLength = this.charReceived ? 3 : 0; -} +module.exports = { + buffer: lookupBuffer, + lookup: lookupMime, +}; /***/ }), -/* 68 */ +/* 55 */ +/***/ (function(module, exports) { + +module.exports = { + "name": "discord.js", + "version": "12.0.0-dev", + "description": "A powerful library for interacting with the Discord API", + "main": "./src/index", + "types": "./typings/index.d.ts", + "scripts": { + "test": "npm run lint && npm run docs:test", + "docs": "docgen --source src --custom docs/index.yml --output docs/docs.json", + "docs:test": "docgen --source src --custom docs/index.yml", + "lint": "eslint src", + "lint:fix": "eslint --fix src", + "webpack": "parallel-webpack" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hydrabolt/discord.js.git" + }, + "keywords": [ + "discord", + "api", + "bot", + "client", + "node", + "discordapp" + ], + "author": "Amish Shah ", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/hydrabolt/discord.js/issues" + }, + "homepage": "https://github.com/hydrabolt/discord.js#readme", + "runkitExampleFilename": "./docs/examples/ping.js", + "dependencies": { + "long": "^3.2.0", + "prism-media": "^0.0.1", + "snekfetch": "^3.2.0", + "tweetnacl": "^1.0.0", + "ws": "^3.0.0" + }, + "peerDependencies": { + "bufferutil": "^3.0.0", + "erlpack": "hammerandchisel/erlpack", + "node-opus": "^0.2.5", + "opusscript": "^0.0.3", + "sodium": "^2.0.1", + "libsodium-wrappers": "^0.5.1", + "uws": "^8.14.0" + }, + "devDependencies": { + "@types/node": "^8.0.0", + "discord.js-docgen": "hydrabolt/discord.js-docgen", + "eslint": "^4.0.0", + "parallel-webpack": "^2.0.0", + "uglifyjs-webpack-plugin": "iCrawl/uglifyjs-webpack-plugin", + "webpack": "^3.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "browser": { + "ws": false, + "uws": false, + "erlpack": false, + "prism-media": false, + "opusscript": false, + "node-opus": false, + "tweetnacl": false, + "sodium": false, + "src/sharding/Shard.js": false, + "src/sharding/ShardClientUtil.js": false, + "src/sharding/ShardingManager.js": false, + "src/client/voice/dispatcher/StreamDispatcher.js": false, + "src/client/voice/opus/BaseOpusEngine.js": false, + "src/client/voice/opus/NodeOpusEngine.js": false, + "src/client/voice/opus/OpusEngineList.js": false, + "src/client/voice/opus/OpusScriptEngine.js": false, + "src/client/voice/pcm/ConverterEngine.js": false, + "src/client/voice/pcm/ConverterEngineList.js": false, + "src/client/voice/pcm/FfmpegConverterEngine.js": false, + "src/client/voice/player/AudioPlayer.js": false, + "src/client/voice/receiver/VoiceReadable.js": false, + "src/client/voice/receiver/VoiceReceiver.js": false, + "src/client/voice/util/Secretbox.js": false, + "src/client/voice/util/SecretKey.js": false, + "src/client/voice/util/VolumeInterface.js": false, + "src/client/voice/ClientVoiceManager.js": false, + "src/client/voice/VoiceBroadcast.js": false, + "src/client/voice/VoiceConnection.js": false, + "src/client/voice/VoiceUDPClient.js": false, + "src/client/voice/VoiceWebSocket.js": false + } +}; + +/***/ }), +/* 56 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(Buffer) {const path = __webpack_require__(31); -const fs = __webpack_require__(43); -const snekfetch = __webpack_require__(41); +// Heavily inspired by node's `internal/errors` module -const Util = __webpack_require__(4); -const User = __webpack_require__(13); -const Message = __webpack_require__(9); -const Guild = __webpack_require__(19); -const Channel = __webpack_require__(17); -const GuildMember = __webpack_require__(20); -const Emoji = __webpack_require__(18); -const ReactionEmoji = __webpack_require__(34); -const { Error, TypeError } = __webpack_require__(5); +const kCode = Symbol('code'); +const messages = new Map(); +const assert = __webpack_require__(107); +const util = __webpack_require__(38); /** - * 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 + * Extend an error of some sort into a DiscordjsError + * @param {Error} Base Base error to extend + * @returns {DiscordjsError} */ -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|Snowflake|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 {?Snowflake} - */ - 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|Snowflake} 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 {GuildMember|User} 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 object. 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|Snowflake} 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 (typeof channel === 'string') return this.client.channels.get(channel) || null; - if (channel instanceof Message) return channel.channel; - if (channel instanceof Guild) return channel.channels.get(channel.id) || null; - return null; - } - - /** - * Resolves a ChannelResolvable to a channel ID. - * @param {ChannelResolvable} channel The channel resolvable to resolve - * @returns {?Snowflake} - */ - resolveChannelID(channel) { - if (channel instanceof Channel) return channel.id; - if (typeof channel === 'string') return channel; - if (channel instanceof Message) return channel.channel.id; - if (channel instanceof Guild) return channel.defaultChannel.id; - 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\.com\/invite|\.gg)\/([\w-]{2,255})/i; - const match = inviteRegex.exec(data); - if (match && match[1]) return match[1]; - return 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} - */ - resolveBuffer(resource) { - if (resource instanceof Buffer) return Promise.resolve(resource); - if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(Util.convertToBuffer(resource)); - - if (typeof resource === 'string') { - return new Promise((resolve, reject) => { - if (/^https?:\/\//.test(resource)) { - snekfetch.get(resource) - .end((err, res) => { - if (err) return reject(err); - if (!(res.body instanceof Buffer)) return reject(new TypeError('REQ_BODY_TYPE')); - return resolve(res.body); - }); - } else { - const file = path.resolve(resource); - fs.stat(file, (err, stats) => { - if (err) return reject(err); - if (!stats || !stats.isFile()) return reject(new Error('FILE_NOT_FOUND', file)); - fs.readFile(file, (err2, data) => { - if (err2) reject(err2); else resolve(data); - }); - return null; - }); - } - }); +function makeDiscordjsError(Base) { + return class DiscordjsError extends Base { + constructor(key, ...args) { + super(message(key, args)); + this[kCode] = key; + if (Error.captureStackTrace) Error.captureStackTrace(this, DiscordjsError); } - return Promise.reject(new TypeError('REQ_RESOURCE_TYPE')); - } - - /** - * Data that can be resolved to give an emoji identifier. This can be: - * * The unicode representation of an emoji - * * A custom emoji ID - * * An Emoji object - * * A ReactionEmoji object - * @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 (this.client.emojis.has(emoji)) return this.client.emojis.get(emoji).identifier; - else if (!emoji.includes('%')) return encodeURIComponent(emoji); - else return emoji; + get name() { + return `${super.name} [${this[kCode]}]`; } - return null; - } + + get code() { + return this[kCode]; + } + }; } -module.exports = ClientDataResolver; +/** + * Format the message for an error + * @param {string} key Error key + * @param {Array<*>} args Arguments to pass for util format or as function args + * @returns {string} Formatted string + */ +function message(key, args) { + assert.strictEqual(typeof key, 'string'); + const msg = messages.get(key); + assert(msg, `An invalid error message key was used: ${key}.`); + let fmt = util.format; + if (typeof msg === 'function') { + fmt = msg; + } else { + if (args === undefined || args.length === 0) return msg; + args.unshift(msg); + } + return String(fmt(...args)); +} + +/** + * Register an error code and message + * @param {string} sym Unique name for the error + * @param {*} val Value of the error + */ +function register(sym, val) { + messages.set(sym, typeof val === 'function' ? val : String(val)); +} + +module.exports = { + register, + Error: makeDiscordjsError(Error), + TypeError: makeDiscordjsError(TypeError), + RangeError: makeDiscordjsError(RangeError), +}; -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) /***/ }), -/* 69 */ +/* 57 */ /***/ (function(module, exports, __webpack_require__) { -const UserAgentManager = __webpack_require__(146); -const SequentialRequestHandler = __webpack_require__(145); -const BurstRequestHandler = __webpack_require__(144); -const APIRequest = __webpack_require__(142); -const mountApi = __webpack_require__(143); +const UserAgentManager = __webpack_require__(112); +const SequentialRequestHandler = __webpack_require__(113); +const BurstRequestHandler = __webpack_require__(114); +const APIRequest = __webpack_require__(115); +const mountApi = __webpack_require__(116); const { Error } = __webpack_require__(5); class RESTManager { @@ -17239,7 +15716,7 @@ module.exports = RESTManager; /***/ }), -/* 70 */ +/* 58 */ /***/ (function(module, exports) { /** @@ -17298,18 +15775,1365 @@ class RequestHandler { module.exports = RequestHandler; +/***/ }), +/* 59 */ +/***/ (function(module, exports, __webpack_require__) { + +const Collector = __webpack_require__(41); + +/** + * @typedef {CollectorOptions} MessageCollectorOptions + * @property {number} max The maximum amount of messages to collect + * @property {number} maxProcessed The maximum amount of messages to process + */ + +/** + * Collects messages on a channel. + * @extends {Collector} + */ +class MessageCollector extends Collector { + /** + * @param {TextChannel|DMChannel|GroupDMChannel} channel The channel + * @param {CollectorFilter} filter The filter to be applied to this collector + * @param {MessageCollectorOptions} options The options to be applied to this collector + * @emits MessageCollector#message + */ + constructor(channel, filter, options = {}) { + super(channel.client, filter, options); + + /** + * @type {TextBasedChannel} channel The channel + */ + this.channel = channel; + + /** + * Total number of messages that were received in the channel during message collection + * @type {number} + */ + this.received = 0; + + this.client.on('message', this.listener); + } + + /** + * Handle an incoming message for possible collection. + * @param {Message} message The message that could be collected + * @returns {?{key: Snowflake, value: Message}} Message data to collect + * @private + */ + handle(message) { + if (message.channel.id !== this.channel.id) return null; + this.received++; + return { + key: message.id, + value: message, + }; + } + + /** + * Check after collection to see if the collector is done. + * @returns {?string} Reason to end the collector, if any + * @private + */ + postCheck() { + if (this.options.max && this.collected.size >= this.options.max) return 'limit'; + if (this.options.maxProcessed && this.received === this.options.maxProcessed) return 'processedLimit'; + return null; + } + + /** + * Removes event listeners. + * @private + */ + cleanup() { + this.client.removeListener('message', this.listener); + } +} + +module.exports = MessageCollector; + + +/***/ }), +/* 60 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = { + search: __webpack_require__(118), + sendMessage: __webpack_require__(119), +}; + + +/***/ }), +/* 61 */ +/***/ (function(module, exports, __webpack_require__) { + +const Collection = __webpack_require__(3); + +/** + * Keeps track of mentions in a {@link Message}. + */ +class MessageMentions { + constructor(message, users, roles, everyone) { + /** + * Whether `@everyone` or `@here` were mentioned + * @type {boolean} + */ + this.everyone = Boolean(everyone); + + if (users) { + if (users instanceof Collection) { + /** + * Any users that were mentioned + * @type {Collection} + */ + this.users = new Collection(users); + } else { + this.users = new Collection(); + for (const mention of users) { + let user = message.client.users.get(mention.id); + if (!user) user = message.client.dataManager.newUser(mention); + this.users.set(user.id, user); + } + } + } else { + this.users = new Collection(); + } + + if (roles) { + if (roles instanceof Collection) { + /** + * Any roles that were mentioned + * @type {Collection} + */ + this.roles = new Collection(roles); + } else { + this.roles = new Collection(); + for (const mention of roles) { + const role = message.channel.guild.roles.get(mention); + if (role) this.roles.set(role.id, role); + } + } + } else { + this.roles = new Collection(); + } + + /** + * Content of the message + * @type {Message} + * @private + */ + this._content = message.content; + + /** + * The client the message is from + * @type {Client} + * @private + */ + this._client = message.client; + + /** + * The guild the message is in + * @type {?Guild} + * @private + */ + this._guild = message.channel.guild; + + /** + * Cached members for {@MessageMention#members} + * @type {?Collection} + * @private + */ + this._members = null; + + /** + * Cached channels for {@MessageMention#channels} + * @type {?Collection} + * @private + */ + this._channels = null; + } + + /** + * Any members that were mentioned (only in {@link TextChannel}s) + * @type {?Collection} + * @readonly + */ + get members() { + if (this._members) return this._members; + if (!this._guild) return null; + this._members = new Collection(); + this.users.forEach(user => { + const member = this._guild.member(user); + if (member) this._members.set(member.user.id, member); + }); + return this._members; + } + + /** + * Any channels that were mentioned + * @type {?Collection} + * @readonly + */ + get channels() { + if (this._channels) return this._channels; + this._channels = new Collection(); + let matches; + while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) { + const chan = this._client.channels.get(matches[1]); + if (chan) this._channels.set(chan.id, chan); + } + return this._channels; + } +} + +/** + * Regular expression that globally matches `@everyone` and `@here` + * @type {RegExp} + */ +MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g; + +/** + * Regular expression that globally matches user mentions like `<@81440962496172032>` + * @type {RegExp} + */ +MessageMentions.USERS_PATTERN = /<@!?[0-9]+>/g; + +/** + * Regular expression that globally matches role mentions like `<@&297577916114403338>` + * @type {RegExp} + */ +MessageMentions.ROLES_PATTERN = /<@&[0-9]+>/g; + +/** + * Regular expression that globally matches channel mentions like `<#222079895583457280>` + * @type {RegExp} + */ +MessageMentions.CHANNELS_PATTERN = /<#([0-9]+)>/g; + +module.exports = MessageMentions; + + +/***/ }), +/* 62 */ +/***/ (function(module, exports) { + +/** + * Represents an attachment in a message. + */ +class MessageAttachment { + constructor(message, data) { + /** + * The client that instantiated this MessageAttachment + * @name MessageAttachment#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: message.client }); + + /** + * The message this attachment is part of + * @type {Message} + */ + this.message = message; + + this.setup(data); + } + + setup(data) { + /** + * The ID of this attachment + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The file name of this attachment + * @type {string} + */ + this.filename = data.filename; + + /** + * The size of this attachment in bytes + * @type {number} + */ + this.filesize = data.size; + + /** + * The URL to this attachment + * @type {string} + */ + this.url = data.url; + + /** + * The Proxy URL to this attachment + * @type {string} + */ + this.proxyURL = data.proxy_url; + + /** + * The height of this attachment (if an image) + * @type {?number} + */ + this.height = data.height; + + /** + * The width of this attachment (if an image) + * @type {?number} + */ + this.width = data.width; + } +} + +module.exports = MessageAttachment; + + +/***/ }), +/* 63 */ +/***/ (function(module, exports, __webpack_require__) { + +const Collection = __webpack_require__(3); +const Emoji = __webpack_require__(24); +const ReactionEmoji = __webpack_require__(42); + +/** + * Represents a reaction to a message. + */ +class MessageReaction { + constructor(message, emoji, count, me) { + /** + * The message that this reaction refers to + * @type {Message} + */ + this.message = message; + + /** + * Whether the client has given this reaction + * @type {boolean} + */ + this.me = me; + + /** + * The number of people that have given the same reaction + * @type {number} + */ + this.count = count || 0; + + /** + * The users that have given this reaction, mapped by their ID + * @type {Collection} + */ + this.users = new Collection(); + + this._emoji = new ReactionEmoji(this, emoji.name, emoji.id); + } + + /** + * The emoji of this reaction, either an Emoji object for known custom emojis, or a ReactionEmoji + * object which has fewer properties. Whatever the prototype of the emoji, it will still have + * `name`, `id`, `identifier` and `toString()` + * @type {Emoji|ReactionEmoji} + * @readonly + */ + get emoji() { + if (this._emoji instanceof Emoji) return this._emoji; + // Check to see if the emoji has become known to the client + if (this._emoji.id) { + const emojis = this.message.client.emojis; + if (emojis.has(this._emoji.id)) { + const emoji = emojis.get(this._emoji.id); + this._emoji = emoji; + return emoji; + } + } + return this._emoji; + } + + /** + * Removes a user from this reaction. + * @param {UserResolvable} [user=this.message.client.user] The user to remove the reaction of + * @returns {Promise} + */ + remove(user = this.message.client.user) { + const userID = this.message.client.resolver.resolveUserID(user); + if (!userID) return Promise.reject(new Error('Couldn\'t resolve the user ID to remove from the reaction.')); + return this.message.client.api.channels[this.message.channel.id].messages[this.message.id] + .reactions[this.emoji.identifier][userID === this.message.client.user.id ? '@me' : userID] + .delete() + .then(() => + this.message.client.actions.MessageReactionRemove.handle({ + user_id: userID, + message_id: this.message.id, + emoji: this.emoji, + channel_id: this.message.channel.id, + }).reaction + ); + } + + /** + * Fetch all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs. + * @param {number} [limit=100] The maximum amount of users to fetch, defaults to 100 + * @returns {Promise>} + */ + fetchUsers(limit = 100) { + const message = this.message; + return message.client.api.channels[message.channel.id].messages[message.id] + .reactions[this.emoji.identifier] + .get({ query: { limit } }) + .then(users => { + this.users = new Collection(); + for (const rawUser of users) { + const user = message.client.dataManager.newUser(rawUser); + this.users.set(user.id, user); + } + this.count = this.users.size; + return this.users; + }); + } +} + +module.exports = MessageReaction; + + +/***/ }), +/* 64 */ +/***/ (function(module, exports, __webpack_require__) { + +const Collector = __webpack_require__(41); +const Collection = __webpack_require__(3); + +/** + * @typedef {CollectorOptions} ReactionCollectorOptions + * @property {number} max The maximum total amount of reactions to collect + * @property {number} maxEmojis The maximum number of emojis to collect + * @property {number} maxUsers The maximum number of users to react + */ + +/** + * Collects reactions on messages. + * @extends {Collector} + */ +class ReactionCollector extends Collector { + /** + * @param {Message} message The message upon which to collect reactions + * @param {CollectorFilter} filter The filter to apply to this collector + * @param {ReactionCollectorOptions} [options={}] The options to apply to this collector + */ + constructor(message, filter, options = {}) { + super(message.client, filter, options); + + /** + * The message + * @type {Message} + */ + this.message = message; + + /** + * The users which have reacted + * @type {Collection} + */ + this.users = new Collection(); + + /** + * The total number of reactions collected + * @type {number} + */ + this.total = 0; + + this.client.on('messageReactionAdd', this.listener); + } + + /** + * Handle an incoming reaction for possible collection. + * @param {MessageReaction} reaction The reaction to possibly collect + * @returns {?{key: Snowflake, value: MessageReaction}} Reaction data to collect + * @private + */ + handle(reaction) { + if (reaction.message.id !== this.message.id) return null; + return { + key: reaction.emoji.id || reaction.emoji.name, + value: reaction, + }; + } + + /** + * Check after collection to see if the collector is done. + * @param {MessageReaction} reaction The reaction that was collected + * @param {User} user The user that reacted + * @returns {?string} Reason to end the collector, if any + * @private + */ + postCheck(reaction, user) { + this.users.set(user.id, user); + if (this.options.max && ++this.total >= this.options.max) return 'limit'; + if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit'; + if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit'; + return null; + } + + /** + * Remove event listeners. + * @private + */ + cleanup() { + this.client.removeListener('messageReactionAdd', this.listener); + } +} + +module.exports = ReactionCollector; + + +/***/ }), +/* 65 */ +/***/ (function(module, exports) { + +/* +{ splash: null, + id: '123123123', + icon: '123123123', + name: 'name' } +*/ + +/** + * Represents a guild that the client only has limited information for - e.g. from invites. + */ +class PartialGuild { + constructor(client, data) { + /** + * The client that instantiated this PartialGuild + * @name PartialGuild#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + this.setup(data); + } + + setup(data) { + /** + * The ID of this guild + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The name of this guild + * @type {string} + */ + this.name = data.name; + + /** + * The hash of this guild's icon + * @type {?string} + */ + this.icon = data.icon; + + /** + * The hash of the guild splash image (VIP only) + * @type {?string} + */ + this.splash = data.splash; + } +} + +module.exports = PartialGuild; + + +/***/ }), +/* 66 */ +/***/ (function(module, exports, __webpack_require__) { + +const Constants = __webpack_require__(0); + +/* +{ type: 0, id: '123123', name: 'heavy-testing' } } +*/ + +/** + * Represents a guild channel that the client only has limited information for - e.g. from invites. + */ +class PartialGuildChannel { + constructor(client, data) { + /** + * The client that instantiated this PartialGuildChannel + * @name PartialGuildChannel#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + this.setup(data); + } + + setup(data) { + /** + * The ID of this guild channel + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The name of this guild channel + * @type {string} + */ + this.name = data.name; + + /** + * The type of this guild channel - `text` or `voice` + * @type {string} + */ + this.type = Constants.ChannelTypes.TEXT === data.type ? 'text' : 'voice'; + } +} + +module.exports = PartialGuildChannel; + + +/***/ }), +/* 67 */ +/***/ (function(module, exports, __webpack_require__) { + +const Collection = __webpack_require__(3); +const Snowflake = __webpack_require__(9); +const Webhook = __webpack_require__(18); + +const Targets = { + ALL: 'ALL', + GUILD: 'GUILD', + CHANNEL: 'CHANNEL', + USER: 'USER', + ROLE: 'ROLE', + INVITE: 'INVITE', + WEBHOOK: 'WEBHOOK', + EMOJI: 'EMOJI', + MESSAGE: 'MESSAGE', + UNKNOWN: 'UNKNOWN', +}; + +const Actions = { + ALL: null, + GUILD_UPDATE: 1, + CHANNEL_CREATE: 10, + CHANNEL_UPDATE: 11, + CHANNEL_DELETE: 12, + CHANNEL_OVERWRITE_CREATE: 13, + CHANNEL_OVERWRITE_UPDATE: 14, + CHANNEL_OVERWRITE_DELETE: 15, + MEMBER_KICK: 20, + MEMBER_PRUNE: 21, + MEMBER_BAN_ADD: 22, + MEMBER_BAN_REMOVE: 23, + MEMBER_UPDATE: 24, + MEMBER_ROLE_UPDATE: 25, + ROLE_CREATE: 30, + ROLE_UPDATE: 31, + ROLE_DELETE: 32, + INVITE_CREATE: 40, + INVITE_UPDATE: 41, + INVITE_DELETE: 42, + WEBHOOK_CREATE: 50, + WEBHOOK_UPDATE: 51, + WEBHOOK_DELETE: 52, + EMOJI_CREATE: 60, + EMOJI_UPDATE: 61, + EMOJI_DELETE: 62, + MESSAGE_DELETE: 72, +}; + + +/** + * Audit logs entries are held in this class. + */ +class GuildAuditLogs { + constructor(guild, data) { + if (data.users) for (const user of data.users) guild.client.dataManager.newUser(user); + /** + * Cached webhooks + * @type {Collection} + * @private + */ + this.webhooks = new Collection(); + if (data.webhooks) { + for (const hook of data.webhooks) { + this.webhooks.set(hook.id, new Webhook(guild.client, hook)); + } + } + + /** + * The entries for this guild's audit logs + * @type {Collection} + */ + this.entries = new Collection(); + for (const item of data.audit_log_entries) { + const entry = new GuildAuditLogsEntry(guild, item); + this.entries.set(entry.id, entry); + } + } + + /** + * Handles possible promises for entry targets. + * @returns {Promise} + */ + static build(...args) { + const logs = new GuildAuditLogs(...args); + return Promise.all(logs.entries.map(e => e.target)).then(() => logs); + } + + /** + * Find target type from entry action. + * @param {number} target The action target + * @returns {?string} + */ + static targetType(target) { + if (target < 10) return Targets.GUILD; + if (target < 20) return Targets.CHANNEL; + if (target < 30) return Targets.USER; + if (target < 40) return Targets.ROLE; + if (target < 50) return Targets.INVITE; + if (target < 60) return Targets.WEBHOOK; + if (target < 70) return Targets.EMOJI; + if (target < 80) return Targets.MESSAGE; + return Targets.UNKNOWN; + } + + + /** + * Find action type from entry action. + * @param {string} action The action target + * @returns {string} + */ + static actionType(action) { + if ([ + Actions.CHANNEL_CREATE, + Actions.CHANNEL_OVERWRITE_CREATE, + Actions.MEMBER_BAN_REMOVE, + Actions.ROLE_CREATE, + Actions.INVITE_CREATE, + Actions.WEBHOOK_CREATE, + Actions.EMOJI_CREATE, + ].includes(action)) return 'CREATE'; + + if ([ + Actions.CHANNEL_DELETE, + Actions.CHANNEL_OVERWRITE_DELETE, + Actions.MEMBER_KICK, + Actions.MEMBER_PRUNE, + Actions.MEMBER_BAN_ADD, + Actions.ROLE_DELETE, + Actions.INVITE_DELETE, + Actions.WEBHOOK_DELETE, + Actions.EMOJI_DELETE, + Actions.MESSAGE_DELETE, + ].includes(action)) return 'DELETE'; + + if ([ + Actions.GUILD_UPDATE, + Actions.CHANNEL_UPDATE, + Actions.CHANNEL_OVERWRITE_UPDATE, + Actions.MEMBER_UPDATE, + Actions.MEMBER_ROLE_UPDATE, + Actions.ROLE_UPDATE, + Actions.INVITE_UPDATE, + Actions.WEBHOOK_UPDATE, + Actions.EMOJI_UPDATE, + ].includes(action)) return 'UPDATE'; + + return 'ALL'; + } +} + +/** + * Audit logs entry. + */ +class GuildAuditLogsEntry { + constructor(guild, data) { + const targetType = GuildAuditLogs.targetType(data.action_type); + /** + * The target type of this entry + * @type {string} + */ + this.targetType = targetType; + + /** + * The action type of this entry + * @type {string} + */ + this.actionType = GuildAuditLogs.actionType(data.action_type); + + /** + * Specific action type of this entry + * @type {string} + */ + this.action = Object.keys(Actions).find(k => Actions[k] === data.action_type); + + /** + * The reason of this entry + * @type {?string} + */ + this.reason = data.reason || null; + + /** + * The user that executed this entry + * @type {User} + */ + this.executor = guild.client.users.get(data.user_id); + + /** + * An entry in the audit log representing a specific change + * @typedef {object} AuditLogChange + * @property {string} key The property that was changed, e.g. `nick` for nickname changes + * @property {*} [old] The old value of the change, e.g. for nicknames, the old nickname + * @property {*} [new] The new value of the change, e.g. for nicknames, the new nickname + */ + + /** + * Specific property changes + * @type {AuditLogChange[]} + */ + this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null; + + /** + * The ID of this entry + * @type {Snowflake} + */ + this.id = data.id; + + /** + * Any extra data from the entry + * @type {?Object|Role|GuildMember} + */ + this.extra = null; + if (data.options) { + if (data.action_type === Actions.MEMBER_PRUNE) { + this.extra = { + removed: data.options.members_removed, + days: data.options.delete_member_days, + }; + } else if (data.action_type === Actions.MESSAGE_DELETE) { + this.extra = { + count: data.options.count, + channel: guild.channels.get(data.options.channel_id), + }; + } else { + switch (data.options.type) { + case 'member': + this.extra = guild.members.get(data.options.id); + if (!this.extra) this.extra = { id: data.options.id }; + break; + case 'role': + this.extra = guild.roles.get(data.options.id); + if (!this.extra) this.extra = { id: data.options.id, name: data.options.role_name }; + break; + default: + break; + } + } + } + + + if (targetType === Targets.UNKNOWN) { + /** + * The target of this entry + * @type {Snowflake|Guild|User|Role|Emoji|Invite|Webhook} + */ + this.target = data.target_id; + } else if ([Targets.USER, Targets.GUILD].includes(targetType)) { + this.target = guild.client[`${targetType.toLowerCase()}s`].get(data.target_id); + } else if (targetType === Targets.WEBHOOK) { + this.target = this.webhooks.get(data.target_id); + } else if (targetType === Targets.INVITE) { + const change = this.changes.find(c => c.key === 'code'); + this.target = guild.fetchInvites() + .then(invites => { + this.target = invites.find(i => i.code === (change.new_value || change.old_value)); + return this.target; + }); + } else if (targetType === Targets.MESSAGE) { + this.target = guild.client.users.get(data.target_id); + } else { + this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id); + } + } + + /** + * The timestamp this entry was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time this entry was created + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } +} + +GuildAuditLogs.Actions = Actions; +GuildAuditLogs.Targets = Targets; +GuildAuditLogs.Entry = GuildAuditLogsEntry; + +module.exports = GuildAuditLogs; + + +/***/ }), +/* 68 */ +/***/ (function(module, exports) { + +/** + * Represents a Discord voice region for guilds. + */ +class VoiceRegion { + constructor(data) { + /** + * The ID of the region + * @type {string} + */ + this.id = data.id; + + /** + * Name of the region + * @type {string} + */ + this.name = data.name; + + /** + * Whether the region is VIP-only + * @type {boolean} + */ + this.vip = data.vip; + + /** + * Whether the region is deprecated + * @type {boolean} + */ + this.deprecated = data.deprecated; + + /** + * Whether the region is optimal + * @type {boolean} + */ + this.optimal = data.optimal; + + /** + * Whether the region is custom + * @type {boolean} + */ + this.custom = data.custom; + + /** + * A sample hostname for what a connection might look like + * @type {string} + */ + this.sampleHostname = data.sample_hostname; + } +} + +module.exports = VoiceRegion; + + +/***/ }), +/* 69 */ +/***/ (function(module, exports, __webpack_require__) { + +const Channel = __webpack_require__(16); +const TextBasedChannel = __webpack_require__(23); +const Collection = __webpack_require__(3); + +/** + * Represents a direct message channel between two users. + * @extends {Channel} + * @implements {TextBasedChannel} + */ +class DMChannel extends Channel { + constructor(client, data) { + super(client, data); + this.type = 'dm'; + this.messages = new Collection(); + this._typing = new Map(); + } + + setup(data) { + super.setup(data); + + /** + * The recipient on the other end of the DM + * @type {User} + */ + this.recipient = this.client.dataManager.newUser(data.recipients[0]); + + this.lastMessageID = data.last_message_id; + } + + /** + * When concatenated with a string, this automatically concatenates the recipient's mention instead of the + * DM channel object. + * @returns {string} + */ + toString() { + return this.recipient.toString(); + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + send() {} + fetchMessage() {} + fetchMessages() {} + fetchPinnedMessages() {} + search() {} + startTyping() {} + stopTyping() {} + get typing() {} + get typingCount() {} + createMessageCollector() {} + awaitMessages() {} + // Doesn't work on DM channels; bulkDelete() {} + acknowledge() {} + _cacheMessage() {} +} + +TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']); + +module.exports = DMChannel; + + +/***/ }), +/* 70 */ +/***/ (function(module, exports, __webpack_require__) { + +const GuildChannel = __webpack_require__(32); +const Webhook = __webpack_require__(18); +const TextBasedChannel = __webpack_require__(23); +const Collection = __webpack_require__(3); + +/** + * Represents a guild text channel on Discord. + * @extends {GuildChannel} + * @implements {TextBasedChannel} + */ +class TextChannel extends GuildChannel { + constructor(guild, data) { + super(guild, data); + this.type = 'text'; + this.messages = new Collection(); + this._typing = new Map(); + } + + setup(data) { + super.setup(data); + + /** + * The topic of the text channel + * @type {?string} + */ + this.topic = data.topic; + + this.lastMessageID = data.last_message_id; + } + + /** + * A collection of members that can see this channel, mapped by their ID + * @type {Collection} + * @readonly + */ + get members() { + const members = new Collection(); + for (const member of this.guild.members.values()) { + if (this.permissionsFor(member).has('READ_MESSAGES')) { + members.set(member.id, member); + } + } + return members; + } + + /** + * If the Discord considers this channel NSFW + * @type {boolean} + * @readonly + */ + get nsfw() { + return /^nsfw(-|$)/.test(this.name); + } + + /** + * Fetch all webhooks for the channel. + * @returns {Promise>} + */ + fetchWebhooks() { + return this.client.api.channels[this.id].webhooks.get().then(data => { + const hooks = new Collection(); + for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); + return hooks; + }); + } + + /** + * Create a webhook for the channel. + * @param {string} name The name of the webhook + * @param {BufferResolvable|Base64Resolvable} avatar The avatar for the webhook + * @returns {Promise} webhook The created webhook + * @example + * channel.createWebhook('Snek', 'http://snek.s3.amazonaws.com/topSnek.png') + * .then(webhook => console.log(`Created webhook ${webhook}`)) + * .catch(console.error) + */ + createWebhook(name, avatar) { + if (typeof avatar === 'string' && avatar.startsWith('data:')) { + return this.client.api.channels[this.id].webhooks.post({ data: { + name, avatar, + } }).then(data => new Webhook(this.client, data)); + } else { + return this.client.resolver.resolveBuffer(avatar).then(data => + this.createWebhook(name, this.client.resolver.resolveBase64(data) || null)); + } + } + + // These are here only for documentation purposes - they are implemented by TextBasedChannel + /* eslint-disable no-empty-function */ + send() {} + fetchMessage() {} + fetchMessages() {} + fetchPinnedMessages() {} + search() {} + startTyping() {} + stopTyping() {} + get typing() {} + get typingCount() {} + createMessageCollector() {} + awaitMessages() {} + bulkDelete() {} + acknowledge() {} + _cacheMessage() {} +} + +TextBasedChannel.applyToClass(TextChannel, true); + +module.exports = TextChannel; + + /***/ }), /* 71 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(Buffer) {const browser = __webpack_require__(26).platform() === 'browser'; -const EventEmitter = __webpack_require__(15); +const Permissions = __webpack_require__(11); + +/** + * Represents a permission overwrite for a role or member in a guild channel. + */ +class PermissionOverwrites { + constructor(guildChannel, data) { + /** + * The GuildChannel this overwrite is for + * @name PermissionOverwrites#channel + * @type {GuildChannel} + * @readonly + */ + Object.defineProperty(this, 'channel', { value: guildChannel }); + + if (data) this.setup(data); + } + + setup(data) { + /** + * The ID of this overwrite, either a user ID or a role ID + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The type of this overwrite + * @type {string} + */ + this.type = data.type; + + this._denied = data.deny; + this._allowed = data.allow; + + /** + * The permissions that are denied for the user or role. + * @type {Permissions} + */ + this.denied = new Permissions(this._denied); + + /** + * The permissions that are allowed for the user or role. + * @type {Permissions} + */ + this.allowed = new Permissions(this._allowed); + } + + /** + * Delete this Permission Overwrite. + * @param {string} [reason] Reason for deleting this overwrite + * @returns {Promise} + */ + delete(reason) { + return this.channel.client.api.channels[this.channel.id].permissions[this.id] + .delete({ reason }) + .then(() => this); + } +} + +module.exports = PermissionOverwrites; + + +/***/ }), +/* 72 */ +/***/ (function(module, exports, __webpack_require__) { + +const GuildChannel = __webpack_require__(32); +const Collection = __webpack_require__(3); + +/** + * Represents a guild voice channel on Discord. + * @extends {GuildChannel} + */ +class VoiceChannel extends GuildChannel { + constructor(guild, data) { + super(guild, data); + + /** + * The members in this voice channel + * @type {Collection} + */ + this.members = new Collection(); + + this.type = 'voice'; + } + + setup(data) { + super.setup(data); + + /** + * The bitrate of this voice channel + * @type {number} + */ + this.bitrate = data.bitrate; + + /** + * The maximum amount of users allowed in this channel - 0 means unlimited. + * @type {number} + */ + this.userLimit = data.user_limit; + } + + /** + * The voice connection for this voice channel, if the client is connected + * @type {?VoiceConnection} + * @readonly + */ + get connection() { + const connection = this.guild.voiceConnection; + if (connection && connection.channel.id === this.id) return connection; + return null; + } + + /** + * Checks if the voice channel is full + * @type {boolean} + * @readonly + */ + get full() { + return this.userLimit > 0 && this.members.size >= this.userLimit; + } + + /** + * Checks if the client has permission join the voice channel + * @type {boolean} + * @readonly + */ + get joinable() { + if (this.client.browser) return false; + if (!this.permissionsFor(this.client.user).has('CONNECT')) return false; + if (this.full && !this.permissionsFor(this.client.user).has('MOVE_MEMBERS')) return false; + return true; + } + + /** + * Checks if the client has permission to send audio to the voice channel + * @type {boolean} + * @readonly + */ + get speakable() { + return this.permissionsFor(this.client.user).has('SPEAK'); + } + + /** + * Sets the bitrate of the channel. + * @param {number} bitrate The new bitrate + * @returns {Promise} + * @example + * // Set the bitrate of a voice channel + * voiceChannel.setBitrate(48000) + * .then(vc => console.log(`Set bitrate to ${vc.bitrate} for ${vc.name}`)) + * .catch(console.error); + */ + setBitrate(bitrate) { + return this.edit({ bitrate }); + } + + /** + * Sets the user limit of the channel. + * @param {number} userLimit The new user limit + * @returns {Promise} + * @example + * // Set the user limit of a voice channel + * voiceChannel.setUserLimit(42) + * .then(vc => console.log(`Set user limit to ${vc.userLimit} for ${vc.name}`)) + * .catch(console.error); + */ + setUserLimit(userLimit) { + return this.edit({ userLimit }); + } + + /** + * Attempts to join this voice channel. + * @returns {Promise} + * @example + * // Join a voice channel + * voiceChannel.join() + * .then(connection => console.log('Connected!')) + * .catch(console.error); + */ + join() { + if (this.client.browser) return Promise.reject(new Error('Voice connections are not available in browsers.')); + return this.client.voice.joinChannel(this); + } + + /** + * Leaves this voice channel. + * @example + * // Leave a voice channel + * voiceChannel.leave(); + */ + leave() { + if (this.client.browser) return; + const connection = this.client.voice.connections.get(this.guild.id); + if (connection && connection.channel.id === this.id) connection.disconnect(); + } +} + +module.exports = VoiceChannel; + + +/***/ }), +/* 73 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(Buffer) {const browser = __webpack_require__(29).platform() === 'browser'; +const EventEmitter = __webpack_require__(12); const Constants = __webpack_require__(0); -const zlib = __webpack_require__(43); -const PacketManager = __webpack_require__(148); +const zlib = __webpack_require__(28); +const PacketManager = __webpack_require__(123); const erlpack = (function findErlpack() { try { - const e = __webpack_require__(195); + const e = __webpack_require__(160); if (!e.pack) return null; return e; } catch (e) { @@ -17320,9 +17144,9 @@ const erlpack = (function findErlpack() { const WebSocket = (function findWebSocket() { if (browser) return window.WebSocket; // eslint-disable-line no-undef try { - return __webpack_require__(196); + return __webpack_require__(161); } catch (e) { - return __webpack_require__(197); + return __webpack_require__(162); } }()); @@ -17809,1383 +17633,775 @@ module.exports = WebSocketConnection; /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) -/***/ }), -/* 72 */ -/***/ (function(module, exports, __webpack_require__) { - -// Heavily inspired by node's `internal/errors` module - -const kCode = Symbol('code'); -const messages = new Map(); -const assert = __webpack_require__(80); -const util = __webpack_require__(42); - -/** - * Extend an error of some sort into a DiscordjsError - * @param {Error} Base Base error to extend - * @returns {DiscordjsError} - */ -function makeDiscordjsError(Base) { - return class DiscordjsError extends Base { - constructor(key, ...args) { - super(message(key, args)); - this[kCode] = key; - if (Error.captureStackTrace) Error.captureStackTrace(this, DiscordjsError); - } - - get name() { - return `${super.name} [${this[kCode]}]`; - } - - get code() { - return this[kCode]; - } - }; -} - -/** - * Format the message for an error - * @param {string} key Error key - * @param {Array<*>} args Arguments to pass for util format or as function args - * @returns {string} Formatted string - */ -function message(key, args) { - assert.strictEqual(typeof key, 'string'); - const msg = messages.get(key); - assert(msg, `An invalid error message key was used: ${key}.`); - let fmt = util.format; - if (typeof msg === 'function') { - fmt = msg; - } else { - if (args === undefined || args.length === 0) return msg; - args.unshift(msg); - } - return String(fmt(...args)); -} - -/** - * Register an error code and message - * @param {string} sym Unique name for the error - * @param {*} val Value of the error - */ -function register(sym, val) { - messages.set(sym, typeof val === 'function' ? val : String(val)); -} - -module.exports = { - register, - Error: makeDiscordjsError(Error), - TypeError: makeDiscordjsError(TypeError), - RangeError: makeDiscordjsError(RangeError), -}; - - -/***/ }), -/* 73 */ -/***/ (function(module, exports) { - -/** - * Represents a Discord voice region for guilds. - */ -class VoiceRegion { - constructor(data) { - /** - * The ID of the region - * @type {string} - */ - this.id = data.id; - - /** - * Name of the region - * @type {string} - */ - this.name = data.name; - - /** - * Whether the region is VIP-only - * @type {boolean} - */ - this.vip = data.vip; - - /** - * Whether the region is deprecated - * @type {boolean} - */ - this.deprecated = data.deprecated; - - /** - * Whether the region is optimal - * @type {boolean} - */ - this.optimal = data.optimal; - - /** - * Whether the region is custom - * @type {boolean} - */ - this.custom = data.custom; - - /** - * A sample hostname for what a connection might look like - * @type {string} - */ - this.sampleHostname = data.sample_hostname; - } -} - -module.exports = VoiceRegion; - - /***/ }), /* 74 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = { - search: __webpack_require__(189), - sendMessage: __webpack_require__(190), -}; +/* WEBPACK VAR INJECTION */(function(Buffer) {const User = __webpack_require__(15); +const Collection = __webpack_require__(3); +const ClientUserSettings = __webpack_require__(75); +const Constants = __webpack_require__(0); +const Util = __webpack_require__(4); +const Guild = __webpack_require__(22); +const Message = __webpack_require__(10); +const GroupDMChannel = __webpack_require__(43); +const { TypeError } = __webpack_require__(5); +/** + * Represents the logged in client's Discord user. + * @extends {User} + */ +class ClientUser extends User { + setup(data) { + super.setup(data); + + /** + * Whether or not this account has been verified + * @type {boolean} + */ + this.verified = data.verified; + + /** + * The email of this account + * @type {string} + */ + this.email = data.email; + this.localPresence = {}; + this._typing = new Map(); + + /** + * A Collection of friends for the logged in user + * This is only filled when using a user account. + * @type {Collection} + */ + this.friends = new Collection(); + + /** + * A Collection of blocked users for the logged in user + * This is only filled when using a user account. + * @type {Collection} + */ + this.blocked = new Collection(); + + /** + * A Collection of notes for the logged in user + * This is only filled when using a user account. + * @type {Collection} + */ + this.notes = new Collection(); + + /** + * If the user has Discord premium (nitro) + * This is only filled when using a user account. + * @type {?boolean} + */ + this.premium = typeof data.premium === 'boolean' ? data.premium : null; + + /** + * If the user has MFA enabled on their account + * This is only filled when using a user account. + * @type {?boolean} + */ + this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null; + + /** + * If the user has ever used a mobile device on Discord + * This is only filled when using a user account. + * @type {?boolean} + */ + this.mobile = typeof data.mobile === 'boolean' ? data.mobile : null; + + /** + * Various settings for this user + * This is only filled when using a user account. + * @type {?ClientUserSettings} + */ + if (data.user_settings) this.settings = new ClientUserSettings(this, data.user_settings); + } + + edit(data, password) { + const _data = {}; + _data.username = data.username || this.username; + _data.avatar = this.client.resolver.resolveBase64(data.avatar); + + if (!this.bot) { + _data.email = data.email || this.email; + _data.password = password; + if (data.new_password) _data.new_password = data.newPassword; + } + + return this.client.api.users['@me'].patch({ data }) + .then(newData => this.client.actions.UserUpdate.handle(newData).updated); + } + + /** + * Set the username of the logged in client. + * Changing usernames in Discord is heavily rate limited, with only 2 requests + * every hour. Use this sparingly! + * @param {string} username The new username + * @param {string} [password] Current password (only for user accounts) + * @returns {Promise} + * @example + * // Set username + * client.user.setUsername('discordjs') + * .then(user => console.log(`My new username is ${user.username}`)) + * .catch(console.error); + */ + setUsername(username, password) { + return this.edit({ username }, password); + } + + /** + * Changes the email for the client user's account. + * This is only available when using a user account. + * @param {string} email New email to change to + * @param {string} password Current password + * @returns {Promise} + * @example + * // Set email + * client.user.setEmail('bob@gmail.com', 'some amazing password 123') + * .then(user => console.log(`My new email is ${user.email}`)) + * .catch(console.error); + */ + setEmail(email, password) { + return this.edit({ email }, password); + } + + /** + * Changes the password for the client user's account. + * This is only available when using a user account. + * @param {string} newPassword New password to change to + * @param {string} oldPassword Current password + * @returns {Promise} + * @example + * // Set password + * client.user.setPassword('some new amazing password 456', 'some amazing password 123') + * .then(user => console.log('New password set!')) + * .catch(console.error); + */ + setPassword(newPassword, oldPassword) { + return this.edit({ password: newPassword }, oldPassword); + } + + /** + * Set the avatar of the logged in client. + * @param {BufferResolvable|Base64Resolvable} avatar The new avatar + * @returns {Promise} + * @example + * // Set avatar + * client.user.setAvatar('./avatar.png') + * .then(user => console.log(`New avatar set!`)) + * .catch(console.error); + */ + setAvatar(avatar) { + if (typeof avatar === 'string' && avatar.startsWith('data:')) { + return this.edit({ avatar }); + } else { + return this.client.resolver.resolveBuffer(avatar || Buffer.alloc(0)) + .then(data => this.edit({ avatar: this.client.resolver.resolveBase64(data) || null })); + } + } + + /** + * Data resembling a raw Discord presence. + * @typedef {Object} PresenceData + * @property {PresenceStatus} [status] Status of the user + * @property {boolean} [afk] Whether the user is AFK + * @property {Object} [game] Game the user is playing + * @property {string} [game.name] Name of the game + * @property {string} [game.url] Twitch stream URL + */ + + /** + * Sets the full presence of the client user. + * @param {PresenceData} data Data for the presence + * @returns {Promise} + */ + setPresence(data) { + // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}} + return new Promise(resolve => { + let status = this.localPresence.status || this.presence.status; + let game = this.localPresence.game; + let afk = this.localPresence.afk || this.presence.afk; + + if (!game && this.presence.game) { + game = { + name: this.presence.game.name, + type: this.presence.game.type, + url: this.presence.game.url, + }; + } + + if (data.status) { + if (typeof data.status !== 'string') throw new TypeError('STATUS_TYPE'); + if (this.bot) { + status = data.status; + } else { + this.settings.update(Constants.UserSettingsMap.status, data.status); + status = 'invisible'; + } + } + + if (data.game) { + game = data.game; + if (game.url) game.type = 1; + } else if (typeof data.game !== 'undefined') { + game = null; + } + + if (typeof data.afk !== 'undefined') afk = data.afk; + afk = Boolean(afk); + + this.localPresence = { status, game, afk }; + this.localPresence.since = 0; + this.localPresence.game = this.localPresence.game || null; + + this.client.ws.send({ + op: 3, + d: this.localPresence, + }); + + this.client._setPresence(this.id, this.localPresence); + + resolve(this); + }); + } + + /** + * A user's status. Must be one of: + * - `online` + * - `idle` + * - `invisible` + * - `dnd` (do not disturb) + * @typedef {string} PresenceStatus + */ + + /** + * Sets the status of the client user. + * @param {PresenceStatus} status Status to change to + * @returns {Promise} + */ + setStatus(status) { + return this.setPresence({ status }); + } + + /** + * Sets the game the client user is playing. + * @param {?string} game Game being played + * @param {string} [streamingURL] Twitch stream URL + * @returns {Promise} + */ + setGame(game, streamingURL) { + if (!game) return this.setPresence({ game: null }); + return this.setPresence({ + game: { + name: game, + url: streamingURL, + }, + }); + } + + /** + * Sets/removes the AFK flag for the client user. + * @param {boolean} afk Whether or not the user is AFK + * @returns {Promise} + */ + setAFK(afk) { + return this.setPresence({ afk }); + } + + /** + * Fetches messages that mentioned the client's user. + * @param {Object} [options] Options for the fetch + * @param {number} [options.limit=25] Maximum number of mentions to retrieve + * @param {boolean} [options.roles=true] Whether to include role mentions + * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions + * @param {Guild|Snowflake} [options.guild] Limit the search to a specific guild + * @returns {Promise} + */ + fetchMentions(options = {}) { + if (options.guild instanceof Guild) options.guild = options.guild.id; + Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options); + + return this.client.api.users['@me'].mentions.get({ query: options }) + .then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))); + } + + /** + * Creates a guild. + * This is only available when using a user account. + * @param {string} name The name of the guild + * @param {Object} [options] Options for the creating + * @param {string} [options.region] The region for the server, defaults to the closest one available + * @param {BufferResolvable|Base64Resolvable} [options.icon=null] The icon for the guild + * @returns {Promise} The guild that was created + */ + createGuild(name, { region, icon = null } = {}) { + if (!icon || (typeof icon === 'string' && icon.startsWith('data:'))) { + return new Promise((resolve, reject) => + this.client.api.guilds.post({ data: { name, region, icon } }) + .then(data => { + if (this.client.guilds.has(data.id)) return resolve(this.client.guilds.get(data.id)); + + const handleGuild = guild => { + if (guild.id === data.id) { + this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild); + this.client.clearTimeout(timeout); + resolve(guild); + } + }; + this.client.on(Constants.Events.GUILD_CREATE, handleGuild); + + const timeout = this.client.setTimeout(() => { + this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild); + resolve(this.client.dataManager.newGuild(data)); + }, 10000); + return undefined; + }, reject) + ); + } else { + return this.client.resolver.resolveBuffer(icon) + .then(data => this.createGuild(name, { region, icon: this.client.resolver.resolveBase64(data) || null })); + } + } + + /** + * An object containing either a user or access token, and an optional nickname. + * @typedef {Object} GroupDMRecipientOptions + * @property {UserResolvable} [user] User to add to the Group DM + * (only available if a user is creating the DM) + * @property {string} [accessToken] Access token to use to add a user to the Group DM + * (only available if a bot is creating the DM) + * @property {string} [nick] Permanent nickname (only available if a bot is creating the DM) + * @property {string} [id] If no user resolveable is provided and you want to assign nicknames + * you must provide user ids instead + */ + + /** + * Creates a Group DM. + * @param {GroupDMRecipientOptions[]} recipients The recipients + * @returns {Promise} + */ + createGroupDM(recipients) { + const data = this.bot ? { + access_tokens: recipients.map(u => u.accessToken), + nicks: recipients.reduce((o, r) => { + if (r.nick) o[r.user ? r.user.id : r.id] = r.nick; + return o; + }, {}), + } : { recipients: recipients.map(u => this.client.resolver.resolveUserID(u)) }; + return this.client.api.users['@me'].channels.post({ data }) + .then(res => new GroupDMChannel(this.client, res)); + } +} + +module.exports = ClientUser; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) /***/ }), /* 75 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(process) {const os = __webpack_require__(26); -const EventEmitter = __webpack_require__(15); const Constants = __webpack_require__(0); -const Permissions = __webpack_require__(10); const Util = __webpack_require__(4); -const RESTManager = __webpack_require__(69); -const ClientDataManager = __webpack_require__(111); -const ClientManager = __webpack_require__(112); -const ClientDataResolver = __webpack_require__(68); -const ClientVoiceManager = __webpack_require__(193); -const WebSocketManager = __webpack_require__(147); -const ActionsManager = __webpack_require__(113); -const Collection = __webpack_require__(3); -const { Presence } = __webpack_require__(14); -const VoiceRegion = __webpack_require__(73); -const Webhook = __webpack_require__(22); -const User = __webpack_require__(13); -const Invite = __webpack_require__(28); -const OAuth2Application = __webpack_require__(30); -const ShardClientUtil = __webpack_require__(192); -const VoiceBroadcast = __webpack_require__(194); -const { Error, TypeError, RangeError } = __webpack_require__(5); /** - * The main hub for interacting with the Discord API, and the starting point for any bot. - * @extends {EventEmitter} + * A wrapper around the ClientUser's settings. */ -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 = Util.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 {@link ShardingManager}) - * @type {?ShardClientUtil} - */ - this.shard = process.send ? ShardClientUtil.singleton(this) : null; - - /** - * All of the {@link User} objects that have been cached at any point, mapped by their IDs - * @type {Collection} - */ - this.users = new Collection(); - - /** - * All of the guilds the client is currently handling, mapped by their IDs - - * as long as sharding isn't being used, this will be *every* guild the bot is a member of - * @type {Collection} - */ - this.guilds = new Collection(); - - /** - * All of the {@link Channel}s that the client is currently handling, mapped by their IDs - - * as long as sharding isn't being used, this will be *every* channel in *every* guild, and all DM channels - * @type {Collection} - */ - this.channels = new Collection(); - - /** - * Presences that have been received for the client user's friends, mapped by user IDs - * This is only filled when using a user account. - * @type {Collection} - */ - this.presences = new Collection(); - - Object.defineProperty(this, 'token', { writable: true }); - if (!this.token && 'CLIENT_TOKEN' in process.env) { - /** - * Authorization token for the logged in user/bot - * This should be kept private at all times. - * @type {?string} - */ - this.token = process.env.CLIENT_TOKEN; - } else { - this.token = null; - } - - /** - * User that the client is logged in as - * @type {?ClientUser} - */ - this.user = null; - - /** - * Time at which the client was last regarded as being in the `READY` state - * (each time the client disconnects and successfully reconnects, this will be overwritten) - * @type {?Date} - */ - this.readyAt = null; - - /** - * Active voice broadcasts that have been created - * @type {VoiceBroadcast[]} - */ - this.broadcasts = []; - - /** - * Previous heartbeat pings of the websocket (most recent first, limited to three elements) - * @type {number[]} - */ - this.pings = []; - - /** - * Timeouts set by {@link Client#setTimeout} that are still active - * @type {Set} - * @private - */ - this._timeouts = new Set(); - - /** - * Intervals set by {@link Client#setInterval} that are still active - * @type {Set} - * @private - */ - this._intervals = new Set(); - - if (this.options.messageSweepInterval > 0) { - this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000); - } +class ClientUserSettings { + constructor(user, data) { + this.user = user; + this.patch(data); } /** - * Timestamp of the latest ping's start time - * @type {number} - * @private + * Patch the data contained in this class with new partial data. + * @param {Object} data Data to patch this with */ - get _pingTimestamp() { - return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0; - } - - /** - * API shortcut - * @type {Object} - * @private - */ - get api() { - return this.rest.api; - } - - /** - * Current status of the client's connection to Discord - * @type {?Status} - * @readonly - */ - get status() { - return this.ws.connection.status; - } - - /** - * How long it has been since the client last entered the `READY` state - * @type {?number} - * @readonly - */ - get uptime() { - return this.readyAt ? Date.now() - this.readyAt : null; - } - - /** - * Average heartbeat ping of the websocket, obtained by averaging the {@link Client#pings} property - * @type {number} - * @readonly - */ - get ping() { - return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length; - } - - /** - * All active voice connections that have been established, mapped by channel ID - * @type {Collection} - * @readonly - */ - get voiceConnections() { - if (this.browser) return new Collection(); - return this.voice.connections; - } - - /** - * All custom emojis that the client has access to, mapped by their IDs - * @type {Collection} - * @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; - } - - /** - * Timestamp of the time 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 os.platform() === 'browser'; - } - - /** - * Creates a voice broadcast. - * @returns {VoiceBroadcast} - */ - createVoiceBroadcast() { - const broadcast = new VoiceBroadcast(this); - this.broadcasts.push(broadcast); - return broadcast; - } - - /** - * Logs the client in, establishing a websocket connection to Discord. - * Both bot and regular user accounts are supported, but it is highly recommended to use a bot account whenever - * possible. User accounts are subject to harsher ratelimits and other restrictions that don't apply to bot accounts. - * Bot accounts also have access to many features that user accounts cannot utilise. User accounts that are found to - * be abusing/overusing the API will be banned, locking you out of Discord entirely. - * @param {string} token Token of the account to log in with - * @returns {Promise} Token of the account used - * @example - * client.login('my token'); - */ - login(token) { - return new Promise((resolve, reject) => { - if (typeof token !== 'string') throw new Error('TOKEN_INVALID'); - token = token.replace(/^Bot\s*/i, ''); - this.manager.connectToWebSocket(token, resolve, reject); - }); - } - - /** - * Logs out, terminates the connection to Discord, and destroys the client. - * @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(); - } - - /** - * Requests a sync of guild data with Discord. - * This can be done automatically every 30 seconds by enabling {@link ClientOptions#sync}. - * This is only available when using a user account. - * @param {Guild[]|Collection} [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), - }); - } - - /** - * Obtains a user from Discord, or the user cache if it's already available. - * This is only available when using a bot account. - * @param {Snowflake} id ID of the user - * @param {boolean} [cache=true] Whether to cache the new user object if it isn't already - * @returns {Promise} - */ - fetchUser(id, cache = true) { - if (this.users.has(id)) return Promise.resolve(this.users.get(id)); - return this.api.users[id].get().then(data => - cache ? this.dataManager.newUser(data) : new User(this, data) - ); - } - - /** - * Obtains an invite from Discord. - * @param {InviteResolvable} invite Invite code or URL - * @returns {Promise} - */ - fetchInvite(invite) { - const code = this.resolver.resolveInviteCode(invite); - return this.api.invites[code].get({ query: { with_counts: true } }) - .then(data => new Invite(this, data)); - } - - /** - * Obtains a webhook from Discord. - * @param {Snowflake} id ID of the webhook - * @param {string} [token] Token for the webhook - * @returns {Promise} - */ - fetchWebhook(id, token) { - return this.api.webhooks.opts(id, token).get().then(data => new Webhook(this, data)); - } - - /** - * Obtains the available voice regions from Discord. - * @returns {Collection} - */ - fetchVoiceRegions() { - return this.api.voice.regions.get().then(res => { - const regions = new Collection(); - for (const region of res) regions.set(region.id, new VoiceRegion(region)); - return regions; - }); - } - - /** - * Sweeps all text-based 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 {@link ClientOptions#messageCacheLifetime} - * @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('CLIENT_INVALID_OPTION', 'Lifetime', '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++; - } + patch(data) { + for (const [key, value] of Object.entries(Constants.UserSettingsMap)) { + if (!data.hasOwnProperty(key)) continue; + if (typeof value === 'function') { + this[value.name] = value(data[key]); + } else { + this[value] = data[key]; } } - - this.emit('debug', `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`); - return messages; } /** - * Obtains the OAuth Application of the bot from Discord. - * @param {Snowflake} [id='@me'] ID of application to fetch - * @returns {Promise} + * Update a specific property of of user settings. + * @param {string} name Name of property + * @param {value} value Value to patch + * @returns {Promise} */ - fetchApplication(id = '@me') { - return this.api.oauth2.applications[id].get() - .then(app => new OAuth2Application(this, app)); + update(name, value) { + return this.user.client.api.users['@me'].settings.patch({ data: { [name]: value } }); } /** - * Generates a link that can be used to invite the bot to a guild. - * This is only available when using a bot account. - * @param {PermissionResolvable[]|number} [permissions] Permissions to request - * @returns {Promise} - * @example - * client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE']) - * .then(link => { - * console.log(`Generated bot invite link: ${link}`); - * }); + * @param {Guild} guild The guild to move + * @param {number} position Absolute or relative position + * @param {boolean} [relative=false] Whether to position relatively or absolutely + * @returns {Promise} */ - generateInvite(permissions) { - if (permissions) { - if (permissions instanceof Array) permissions = Permissions.resolve(permissions); - } else { - permissions = 0; - } - return this.fetchApplication().then(application => - `https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot` - ); + setGuildPosition(guild, position, relative) { + const temp = Object.assign([], this.guildPositions); + Util.moveElementInArray(temp, guild.id, position, relative); + return this.update('guild_positions', temp).then(() => guild); } /** - * 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} + * Add a guild to the list of restricted guilds. + * @param {Guild} guild The guild to add + * @returns {Promise} */ - setTimeout(fn, delay, ...args) { - const timeout = setTimeout(() => { - fn(...args); - this._timeouts.delete(timeout); - }, delay); - this._timeouts.add(timeout); - return timeout; + addRestrictedGuild(guild) { + const temp = Object.assign([], this.restrictedGuilds); + if (temp.includes(guild.id)) return Promise.reject(new Error('Guild is already restricted')); + temp.push(guild.id); + return this.update('restricted_guilds', temp).then(() => guild); } /** - * Clears a timeout. - * @param {Timeout} timeout Timeout to cancel + * Remove a guild from the list of restricted guilds. + * @param {Guild} guild The guild to remove + * @returns {Promise} */ - 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); - } - - /** - * Adds a ping to {@link Client#pings}. - * @param {number} startTime Starting time of the ping - * @private - */ - _pong(startTime) { - this.pings.unshift(Date.now() - startTime); - if (this.pings.length > 3) this.pings.length = 3; - this.ws.lastHeartbeatAck = true; - } - - /** - * Adds/updates a friend's presence in {@link Client#presences}. - * @param {Snowflake} id ID of the user - * @param {Object} presence Raw presence object from Discord - * @private - */ - _setPresence(id, presence) { - if (this.presences.has(id)) { - this.presences.get(id).update(presence); - return; - } - this.presences.set(id, new Presence(presence)); - } - - /** - * Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script - * with the client as `this`. - * @param {string} script Script to eval - * @returns {*} - * @private - */ - _eval(script) { - return eval(script); - } - - /** - * Validates the client options. - * @param {ClientOptions} [options=this.options] Options to validate - * @private - */ - _validateOptions(options = this.options) { - if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number'); - } - if (typeof options.shardId !== 'number' || isNaN(options.shardId)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shardId', 'a number'); - } - if (options.shardCount < 0) throw new RangeError('CLIENT_INVALID_OPTION', 'shardCount', 'at least 0'); - if (options.shardId < 0) throw new RangeError('CLIENT_INVALID_OPTION', 'shardId', 'at least 0'); - if (options.shardId !== 0 && options.shardId >= options.shardCount) { - throw new RangeError('CLIENT_INVALID_OPTION', 'shardId', 'less than shardCount'); - } - if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'messageCacheMaxSize', 'a number'); - } - if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number'); - } - if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'messageSweepInterval', 'a number'); - } - if (typeof options.fetchAllMembers !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'fetchAllMembers', 'a boolean'); - } - if (typeof options.disableEveryone !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'disableEveryone', 'a boolean'); - } - if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number'); - } - if (typeof options.internalSharding !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'internalSharding', 'a boolean'); - } - if (!(options.disabledEvents instanceof Array)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'disabledEvents', 'an Array'); - } + removeRestrictedGuild(guild) { + const temp = Object.assign([], this.restrictedGuilds); + const index = temp.indexOf(guild.id); + if (index < 0) return Promise.reject(new Error('Guild is not restricted')); + temp.splice(index, 1); + return this.update('restricted_guilds', temp).then(() => guild); } } -module.exports = Client; +module.exports = ClientUserSettings; -/** - * 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 - */ - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) /***/ }), /* 76 */ /***/ (function(module, exports, __webpack_require__) { -const Webhook = __webpack_require__(22); -const RESTManager = __webpack_require__(69); -const ClientDataResolver = __webpack_require__(68); -const Constants = __webpack_require__(0); +/* WEBPACK VAR INJECTION */(function(Buffer) {const path = __webpack_require__(21); +const fs = __webpack_require__(28); +const snekfetch = __webpack_require__(33); + const Util = __webpack_require__(4); +const User = __webpack_require__(15); +const Message = __webpack_require__(10); +const Guild = __webpack_require__(22); +const Channel = __webpack_require__(16); +const GuildMember = __webpack_require__(25); +const Emoji = __webpack_require__(24); +const ReactionEmoji = __webpack_require__(42); +const { Error, TypeError } = __webpack_require__(5); /** - * The webhook client. - * @extends {Webhook} + * 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 WebhookClient extends Webhook { +class ClientDataResolver { /** - * @param {Snowflake} id ID of the webhook - * @param {string} token Token of the webhook - * @param {ClientOptions} [options] Options for the client - * @example - * // Create a new webhook and send a message - * const hook = new Discord.WebhookClient('1234', 'abcdef'); - * hook.sendMessage('This will send a message').catch(console.error); + * @param {Client} client The client the resolver is for */ - constructor(id, token, options) { - super(null, id, token); - - /** - * The options the client was instantiated with - * @type {ClientOptions} - */ - this.options = Util.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); - - /** - * Timeouts set by {@link WebhookClient#setTimeout} that are still active - * @type {Set} - * @private - */ - this._timeouts = new Set(); - - /** - * Intervals set by {@link WebhookClient#setInterval} that are still active - * @type {Set} - * @private - */ - this._intervals = new Set(); + constructor(client) { + this.client = client; } /** - * API shortcut - * @type {Object} - * @private + * 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|Snowflake|Message|Guild|GuildMember} UserResolvable */ - get api() { - return this.rest.api; + + /** + * 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; } /** - * 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} + * Resolves a UserResolvable to a user ID string. + * @param {UserResolvable} user The UserResolvable to identify + * @returns {?Snowflake} */ - setTimeout(fn, delay, ...args) { - const timeout = setTimeout(() => { - fn(...args); - this._timeouts.delete(timeout); - }, delay); - this._timeouts.add(timeout); - return timeout; + 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; } /** - * Clears a timeout. - * @param {Timeout} timeout Timeout to cancel + * Data that resolves to give a Guild object. This can be: + * * A Guild object + * * A Guild ID + * @typedef {Guild|Snowflake} GuildResolvable */ - clearTimeout(timeout) { - clearTimeout(timeout); - this._timeouts.delete(timeout); + + /** + * 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; } /** - * 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} + * Data that resolves to give a GuildMember object. This can be: + * * A GuildMember object + * * A User object + * @typedef {GuildMember|User} GuildMemberResolvable */ - setInterval(fn, delay, ...args) { - const interval = setInterval(fn, delay, ...args); - this._intervals.add(interval); - return interval; + + /** + * 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; } /** - * Clears an interval. - * @param {Timeout} interval Interval to cancel + * Data that can be resolved to give a Channel object. 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|Snowflake} ChannelResolvable */ - clearInterval(interval) { - clearInterval(interval); - this._intervals.delete(interval); - } - /** - * Destroys the client. + * Resolves a ChannelResolvable to a Channel object. + * @param {ChannelResolvable} channel The channel resolvable to resolve + * @returns {?Channel} */ - destroy() { - for (const t of this._timeouts) clearTimeout(t); - for (const i of this._intervals) clearInterval(i); - this._timeouts.clear(); - this._intervals.clear(); + resolveChannel(channel) { + if (channel instanceof Channel) return channel; + if (typeof channel === 'string') return this.client.channels.get(channel) || null; + if (channel instanceof Message) return channel.channel; + if (channel instanceof Guild) return channel.channels.get(channel.id) || null; + return null; + } + + /** + * Resolves a ChannelResolvable to a channel ID. + * @param {ChannelResolvable} channel The channel resolvable to resolve + * @returns {?Snowflake} + */ + resolveChannelID(channel) { + if (channel instanceof Channel) return channel.id; + if (typeof channel === 'string') return channel; + if (channel instanceof Message) return channel.channel.id; + if (channel instanceof Guild) return channel.defaultChannel.id; + 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\.com\/invite|\.gg)\/([\w-]{2,255})/i; + const match = inviteRegex.exec(data); + if (match && match[1]) return match[1]; + return 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} + */ + resolveBuffer(resource) { + if (resource instanceof Buffer) return Promise.resolve(resource); + if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(Util.convertToBuffer(resource)); + + if (typeof resource === 'string') { + return new Promise((resolve, reject) => { + if (/^https?:\/\//.test(resource)) { + snekfetch.get(resource) + .end((err, res) => { + if (err) return reject(err); + if (!(res.body instanceof Buffer)) return reject(new TypeError('REQ_BODY_TYPE')); + return resolve(res.body); + }); + } else { + const file = path.resolve(resource); + fs.stat(file, (err, stats) => { + if (err) return reject(err); + if (!stats || !stats.isFile()) return reject(new Error('FILE_NOT_FOUND', file)); + fs.readFile(file, (err2, data) => { + if (err2) reject(err2); else resolve(data); + }); + return null; + }); + } + }); + } + + return Promise.reject(new TypeError('REQ_RESOURCE_TYPE')); + } + + /** + * Data that can be resolved to give an emoji identifier. This can be: + * * The unicode representation of an emoji + * * A custom emoji ID + * * An Emoji object + * * A ReactionEmoji object + * @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 (this.client.emojis.has(emoji)) return this.client.emojis.get(emoji).identifier; + else if (!emoji.includes('%')) return encodeURIComponent(emoji); + else return emoji; + } + return null; } } -module.exports = WebhookClient; +module.exports = ClientDataResolver; +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) /***/ }), /* 77 */ -/***/ (function(module, exports) { +/***/ (function(module, exports, __webpack_require__) { + +const Util = __webpack_require__(4); + +module.exports = { + // "Root" classes (starting points) + Client: __webpack_require__(111), + Shard: __webpack_require__(196), + ShardClientUtil: __webpack_require__(197), + ShardingManager: __webpack_require__(198), + WebhookClient: __webpack_require__(199), + + // Utilities + Collection: __webpack_require__(3), + Constants: __webpack_require__(0), + DiscordAPIError: __webpack_require__(39), + EvaluatedPermissions: __webpack_require__(11), + Permissions: __webpack_require__(11), + Snowflake: __webpack_require__(9), + SnowflakeUtil: __webpack_require__(9), + Util: Util, + util: Util, + version: __webpack_require__(55).version, + + // Shortcuts to Util methods + escapeMarkdown: Util.escapeMarkdown, + fetchRecommendedShards: Util.fetchRecommendedShards, + splitMessage: Util.splitMessage, + + // Structures + Channel: __webpack_require__(16), + ClientUser: __webpack_require__(74), + ClientUserSettings: __webpack_require__(75), + Collector: __webpack_require__(41), + DMChannel: __webpack_require__(69), + Emoji: __webpack_require__(24), + Game: __webpack_require__(17).Game, + GroupDMChannel: __webpack_require__(43), + Guild: __webpack_require__(22), + GuildAuditLogs: __webpack_require__(67), + GuildChannel: __webpack_require__(32), + GuildMember: __webpack_require__(25), + Invite: __webpack_require__(31), + Message: __webpack_require__(10), + MessageAttachment: __webpack_require__(62), + MessageCollector: __webpack_require__(59), + MessageEmbed: __webpack_require__(30), + MessageMentions: __webpack_require__(61), + MessageReaction: __webpack_require__(63), + OAuth2Application: __webpack_require__(44), + ClientOAuth2Application: __webpack_require__(44), + PartialGuild: __webpack_require__(65), + PartialGuildChannel: __webpack_require__(66), + PermissionOverwrites: __webpack_require__(71), + Presence: __webpack_require__(17).Presence, + ReactionEmoji: __webpack_require__(42), + ReactionCollector: __webpack_require__(64), + Role: __webpack_require__(26), + TextChannel: __webpack_require__(70), + User: __webpack_require__(15), + VoiceChannel: __webpack_require__(72), + Webhook: __webpack_require__(18), +}; + +if (__webpack_require__(29).platform() === 'browser') window.Discord = module.exports; // eslint-disable-line no-undef -/* (ignored) */ /***/ }), /* 78 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 79 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 80 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { - -// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js -// original notice: - -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -function compare(a, b) { - if (a === b) { - return 0; - } - - var x = a.length; - var y = b.length; - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i]; - y = b[i]; - break; - } - } - - if (x < y) { - return -1; - } - if (y < x) { - return 1; - } - return 0; -} -function isBuffer(b) { - if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { - return global.Buffer.isBuffer(b); - } - return !!(b != null && b._isBuffer); -} - -// based on node assert, original notice: - -// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 -// -// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! -// -// Originally from narwhal.js (http://narwhaljs.org) -// Copyright (c) 2009 Thomas Robinson <280north.com> -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the 'Software'), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -var util = __webpack_require__(42); -var hasOwn = Object.prototype.hasOwnProperty; -var pSlice = Array.prototype.slice; -var functionsHaveNames = (function () { - return function foo() {}.name === 'foo'; -}()); -function pToString (obj) { - return Object.prototype.toString.call(obj); -} -function isView(arrbuf) { - if (isBuffer(arrbuf)) { - return false; - } - if (typeof global.ArrayBuffer !== 'function') { - return false; - } - if (typeof ArrayBuffer.isView === 'function') { - return ArrayBuffer.isView(arrbuf); - } - if (!arrbuf) { - return false; - } - if (arrbuf instanceof DataView) { - return true; - } - if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { - return true; - } - return false; -} -// 1. The assert module provides functions that throw -// AssertionError's when particular conditions are not met. The -// assert module must conform to the following interface. - -var assert = module.exports = ok; - -// 2. The AssertionError is defined in assert. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }) - -var regex = /\s*function\s+([^\(\s]*)\s*/; -// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js -function getName(func) { - if (!util.isFunction(func)) { - return; - } - if (functionsHaveNames) { - return func.name; - } - var str = func.toString(); - var match = str.match(regex); - return match && match[1]; -} -assert.AssertionError = function AssertionError(options) { - this.name = 'AssertionError'; - this.actual = options.actual; - this.expected = options.expected; - this.operator = options.operator; - if (options.message) { - this.message = options.message; - this.generatedMessage = false; - } else { - this.message = getMessage(this); - this.generatedMessage = true; - } - var stackStartFunction = options.stackStartFunction || fail; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, stackStartFunction); - } else { - // non v8 browsers so we can have a stacktrace - var err = new Error(); - if (err.stack) { - var out = err.stack; - - // try to strip useless frames - var fn_name = getName(stackStartFunction); - var idx = out.indexOf('\n' + fn_name); - if (idx >= 0) { - // once we have located the function frame - // we need to strip out everything before it (and its line) - var next_line = out.indexOf('\n', idx + 1); - out = out.substring(next_line + 1); - } - - this.stack = out; - } - } -}; - -// assert.AssertionError instanceof Error -util.inherits(assert.AssertionError, Error); - -function truncate(s, n) { - if (typeof s === 'string') { - return s.length < n ? s : s.slice(0, n); - } else { - return s; - } -} -function inspect(something) { - if (functionsHaveNames || !util.isFunction(something)) { - return util.inspect(something); - } - var rawname = getName(something); - var name = rawname ? ': ' + rawname : ''; - return '[Function' + name + ']'; -} -function getMessage(self) { - return truncate(inspect(self.actual), 128) + ' ' + - self.operator + ' ' + - truncate(inspect(self.expected), 128); -} - -// At present only the three keys mentioned above are used and -// understood by the spec. Implementations or sub modules can pass -// other keys to the AssertionError's constructor - they will be -// ignored. - -// 3. All of the following functions must throw an AssertionError -// when a corresponding condition is not met, with a message that -// may be undefined if not provided. All assertion methods provide -// both the actual and expected values to the assertion error for -// display purposes. - -function fail(actual, expected, message, operator, stackStartFunction) { - throw new assert.AssertionError({ - message: message, - actual: actual, - expected: expected, - operator: operator, - stackStartFunction: stackStartFunction - }); -} - -// EXTENSION! allows for well behaved errors defined elsewhere. -assert.fail = fail; - -// 4. Pure assertion tests whether a value is truthy, as determined -// by !!guard. -// assert.ok(guard, message_opt); -// This statement is equivalent to assert.equal(true, !!guard, -// message_opt);. To test strictly for the value true, use -// assert.strictEqual(true, guard, message_opt);. - -function ok(value, message) { - if (!value) fail(value, true, message, '==', assert.ok); -} -assert.ok = ok; - -// 5. The equality assertion tests shallow, coercive equality with -// ==. -// assert.equal(actual, expected, message_opt); - -assert.equal = function equal(actual, expected, message) { - if (actual != expected) fail(actual, expected, message, '==', assert.equal); -}; - -// 6. The non-equality assertion tests for whether two objects are not equal -// with != assert.notEqual(actual, expected, message_opt); - -assert.notEqual = function notEqual(actual, expected, message) { - if (actual == expected) { - fail(actual, expected, message, '!=', assert.notEqual); - } -}; - -// 7. The equivalence assertion tests a deep equality relation. -// assert.deepEqual(actual, expected, message_opt); - -assert.deepEqual = function deepEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'deepEqual', assert.deepEqual); - } -}; - -assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); - } -}; - -function _deepEqual(actual, expected, strict, memos) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - } else if (isBuffer(actual) && isBuffer(expected)) { - return compare(actual, expected) === 0; - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (util.isDate(actual) && util.isDate(expected)) { - return actual.getTime() === expected.getTime(); - - // 7.3 If the expected value is a RegExp object, the actual value is - // equivalent if it is also a RegExp object with the same source and - // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). - } else if (util.isRegExp(actual) && util.isRegExp(expected)) { - return actual.source === expected.source && - actual.global === expected.global && - actual.multiline === expected.multiline && - actual.lastIndex === expected.lastIndex && - actual.ignoreCase === expected.ignoreCase; - - // 7.4. Other pairs that do not both pass typeof value == 'object', - // equivalence is determined by ==. - } else if ((actual === null || typeof actual !== 'object') && - (expected === null || typeof expected !== 'object')) { - return strict ? actual === expected : actual == expected; - - // If both values are instances of typed arrays, wrap their underlying - // ArrayBuffers in a Buffer each to increase performance - // This optimization requires the arrays to have the same type as checked by - // Object.prototype.toString (aka pToString). Never perform binary - // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their - // bit patterns are not identical. - } else if (isView(actual) && isView(expected) && - pToString(actual) === pToString(expected) && - !(actual instanceof Float32Array || - actual instanceof Float64Array)) { - return compare(new Uint8Array(actual.buffer), - new Uint8Array(expected.buffer)) === 0; - - // 7.5 For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical 'prototype' property. Note: this - // accounts for both named and indexed properties on Arrays. - } else if (isBuffer(actual) !== isBuffer(expected)) { - return false; - } else { - memos = memos || {actual: [], expected: []}; - - var actualIndex = memos.actual.indexOf(actual); - if (actualIndex !== -1) { - if (actualIndex === memos.expected.indexOf(expected)) { - return true; - } - } - - memos.actual.push(actual); - memos.expected.push(expected); - - return objEquiv(actual, expected, strict, memos); - } -} - -function isArguments(object) { - return Object.prototype.toString.call(object) == '[object Arguments]'; -} - -function objEquiv(a, b, strict, actualVisitedObjects) { - if (a === null || a === undefined || b === null || b === undefined) - return false; - // if one is a primitive, the other must be same - if (util.isPrimitive(a) || util.isPrimitive(b)) - return a === b; - if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) - return false; - var aIsArgs = isArguments(a); - var bIsArgs = isArguments(b); - if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) - return false; - if (aIsArgs) { - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b, strict); - } - var ka = objectKeys(a); - var kb = objectKeys(b); - var key, i; - // having the same number of owned properties (keys incorporates - // hasOwnProperty) - if (ka.length !== kb.length) - return false; - //the same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - //~~~cheap key test - for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] !== kb[i]) - return false; - } - //equivalent values for every corresponding key, and - //~~~possibly expensive deep test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) - return false; - } - return true; -} - -// 8. The non-equivalence assertion tests for any deep inequality. -// assert.notDeepEqual(actual, expected, message_opt); - -assert.notDeepEqual = function notDeepEqual(actual, expected, message) { - if (_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); - } -}; - -assert.notDeepStrictEqual = notDeepStrictEqual; -function notDeepStrictEqual(actual, expected, message) { - if (_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); - } -} - - -// 9. The strict equality assertion tests strict equality, as determined by ===. -// assert.strictEqual(actual, expected, message_opt); - -assert.strictEqual = function strictEqual(actual, expected, message) { - if (actual !== expected) { - fail(actual, expected, message, '===', assert.strictEqual); - } -}; - -// 10. The strict non-equality assertion tests for strict inequality, as -// determined by !==. assert.notStrictEqual(actual, expected, message_opt); - -assert.notStrictEqual = function notStrictEqual(actual, expected, message) { - if (actual === expected) { - fail(actual, expected, message, '!==', assert.notStrictEqual); - } -}; - -function expectedException(actual, expected) { - if (!actual || !expected) { - return false; - } - - if (Object.prototype.toString.call(expected) == '[object RegExp]') { - return expected.test(actual); - } - - try { - if (actual instanceof expected) { - return true; - } - } catch (e) { - // Ignore. The instanceof check doesn't work for arrow functions. - } - - if (Error.isPrototypeOf(expected)) { - return false; - } - - return expected.call({}, actual) === true; -} - -function _tryBlock(block) { - var error; - try { - block(); - } catch (e) { - error = e; - } - return error; -} - -function _throws(shouldThrow, block, expected, message) { - var actual; - - if (typeof block !== 'function') { - throw new TypeError('"block" argument must be a function'); - } - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - actual = _tryBlock(block); - - message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + - (message ? ' ' + message : '.'); - - if (shouldThrow && !actual) { - fail(actual, expected, 'Missing expected exception' + message); - } - - var userProvidedMessage = typeof message === 'string'; - var isUnwantedException = !shouldThrow && util.isError(actual); - var isUnexpectedException = !shouldThrow && actual && !expected; - - if ((isUnwantedException && - userProvidedMessage && - expectedException(actual, expected)) || - isUnexpectedException) { - fail(actual, expected, 'Got unwanted exception' + message); - } - - if ((shouldThrow && actual && expected && - !expectedException(actual, expected)) || (!shouldThrow && actual)) { - throw actual; - } -} - -// 11. Expected to throw an error: -// assert.throws(block, Error_opt, message_opt); - -assert.throws = function(block, /*optional*/error, /*optional*/message) { - _throws(true, block, error, message); -}; - -// EXTENSION! This is annoying to write outside this module. -assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { - _throws(false, block, error, message); -}; - -assert.ifError = function(err) { if (err) throw err; }; - -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - if (hasOwn.call(obj, key)) keys.push(key); - } - return keys; -}; - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11))) - -/***/ }), -/* 81 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -19224,22 +18440,22 @@ function placeHoldersCount (b64) { function byteLength (b64) { // base64 is 4/3 + up to two characters of the original data - return b64.length * 3 / 4 - placeHoldersCount(b64) + return (b64.length * 3 / 4) - placeHoldersCount(b64) } function toByteArray (b64) { - var i, j, l, tmp, placeHolders, arr + var i, l, tmp, placeHolders, arr var len = b64.length placeHolders = placeHoldersCount(b64) - arr = new Arr(len * 3 / 4 - placeHolders) + arr = new Arr((len * 3 / 4) - placeHolders) // if there are placeholders, only get up to the last complete 4 chars l = placeHolders > 0 ? len - 4 : len var L = 0 - for (i = 0, j = 0; i < l; i += 4, j += 3) { + for (i = 0; i < l; i += 4) { tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] arr[L++] = (tmp >> 16) & 0xFF arr[L++] = (tmp >> 8) & 0xFF @@ -19306,97 +18522,7 @@ function fromByteArray (uint8) { /***/ }), -/* 82 */ -/***/ (function(module, exports) { - -module.exports = { - "100": "Continue", - "101": "Switching Protocols", - "102": "Processing", - "200": "OK", - "201": "Created", - "202": "Accepted", - "203": "Non-Authoritative Information", - "204": "No Content", - "205": "Reset Content", - "206": "Partial Content", - "207": "Multi-Status", - "208": "Already Reported", - "226": "IM Used", - "300": "Multiple Choices", - "301": "Moved Permanently", - "302": "Found", - "303": "See Other", - "304": "Not Modified", - "305": "Use Proxy", - "307": "Temporary Redirect", - "308": "Permanent Redirect", - "400": "Bad Request", - "401": "Unauthorized", - "402": "Payment Required", - "403": "Forbidden", - "404": "Not Found", - "405": "Method Not Allowed", - "406": "Not Acceptable", - "407": "Proxy Authentication Required", - "408": "Request Timeout", - "409": "Conflict", - "410": "Gone", - "411": "Length Required", - "412": "Precondition Failed", - "413": "Payload Too Large", - "414": "URI Too Long", - "415": "Unsupported Media Type", - "416": "Range Not Satisfiable", - "417": "Expectation Failed", - "418": "I'm a teapot", - "421": "Misdirected Request", - "422": "Unprocessable Entity", - "423": "Locked", - "424": "Failed Dependency", - "425": "Unordered Collection", - "426": "Upgrade Required", - "428": "Precondition Required", - "429": "Too Many Requests", - "431": "Request Header Fields Too Large", - "451": "Unavailable For Legal Reasons", - "500": "Internal Server Error", - "501": "Not Implemented", - "502": "Bad Gateway", - "503": "Service Unavailable", - "504": "Gateway Timeout", - "505": "HTTP Version Not Supported", - "506": "Variant Also Negotiates", - "507": "Insufficient Storage", - "508": "Loop Detected", - "509": "Bandwidth Limit Exceeded", - "510": "Not Extended", - "511": "Network Authentication Required" -} - - -/***/ }), -/* 83 */ -/***/ (function(module, exports, __webpack_require__) { - -var http = __webpack_require__(64); - -var https = module.exports; - -for (var key in http) { - if (http.hasOwnProperty(key)) https[key] = http[key]; -}; - -https.request = function (params, cb) { - if (!params) params = {}; - params.scheme = 'https'; - params.protocol = 'https:'; - return http.request.call(this, params, cb); -} - - -/***/ }), -/* 84 */ +/* 79 */ /***/ (function(module, exports) { exports.read = function (buffer, offset, isLE, mLen, nBytes) { @@ -19485,10 +18611,1631 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { } +/***/ }), +/* 80 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(Buffer) {__webpack_require__(34); +const zlib = __webpack_require__(28); +const qs = __webpack_require__(37); +const http = __webpack_require__(51); +const https = __webpack_require__(101); +const URL = __webpack_require__(53); +const Package = __webpack_require__(102); +const Stream = __webpack_require__(34); +const FormData = __webpack_require__(103); +const fileLoader = __webpack_require__(106); + +/** + * Snekfetch + * @extends Stream.Readable + * @extends Promise + */ +class Snekfetch extends Stream.Readable { + /** + * Create a request, but you probably wanna use `snekfetch#method` + * @param {string} method HTTP method + * @param {string} url URL + * @param {Object} opts Options + * @param {Object} [opts.headers] Headers to initialize the request with + * @param {Object|string|Buffer} [opts.data] Data to initialize the request with + * @param {string|Object} [opts.query] Query to intialize the request with + */ + constructor(method, url, opts = { headers: null, data: null, query: null }) { + super(); + + const options = URL.parse(url); + options.method = method.toUpperCase(); + if (opts.headers) options.headers = opts.headers; + + this.request = { https, http, file: fileLoader }[options.protocol.replace(':', '')].request(options); + if (opts.query) this.query(opts.query); + if (opts.data) this.send(opts.data); + } + + /** + * Add a query param to the request + * @param {string|Object} name Name of query param or object to add to query + * @param {string} [value] If name is a string value, this will be the value of the query param + * @returns {Snekfetch} This request + */ + query(name, value) { + if (this.response) throw new Error('Cannot modify query after being sent!'); + if (!this.request.query) this.request.query = {}; + if (name !== null && typeof name === 'object') { + this.request.query = Object.assign(this.request.query, name); + } else { + this.request.query[name] = value; + } + return this; + } + + /** + * Add a header to the request + * @param {string|Object} name Name of query param or object to add to headers + * @param {string} [value] If name is a string value, this will be the value of the header + * @returns {Snekfetch} This request + */ + set(name, value) { + if (this.response) throw new Error('Cannot modify headers after being sent!'); + if (name !== null && typeof name === 'object') { + for (const key of Object.keys(name)) this.set(key, name[key]); + } else { + this.request.setHeader(name, value); + } + return this; + } + + /** + * Attach a form data object + * @param {string} name Name of the form attachment + * @param {string|Object|Buffer} data Data for the attachment + * @param {string} [filename] Optional filename if form attachment name needs to be overridden + * @returns {Snekfetch} This request + */ + attach(name, data, filename) { + if (this.response) throw new Error('Cannot modify data after being sent!'); + const form = this._getFormData(); + this.set('Content-Type', `multipart/form-data; boundary=${form.boundary}`); + form.append(name, data, filename); + this.data = form; + return this; + } + + /** + * Send data with the request + * @param {string|Buffer|Object} data Data to send + * @returns {Snekfetch} This request + */ + send(data) { + if (this.response) throw new Error('Cannot modify data after being sent!'); + if (data !== null && typeof data === 'object') { + const header = this._getRequestHeader('content-type'); + let serialize; + if (header) { + if (header.includes('json')) serialize = JSON.stringify; + else if (header.includes('urlencoded')) serialize = qs.stringify; + } else { + this.set('Content-Type', 'application/json'); + serialize = JSON.stringify; + } + this.data = serialize(data); + } else { + this.data = data; + } + return this; + } + + then(resolver, rejector) { + return new Promise((resolve, reject) => { + const request = this.request; + + const handleError = (err) => { + if (!err) err = new Error('Unknown error occured'); + err.request = request; + reject(err); + }; + + request.once('abort', handleError); + request.once('aborted', handleError); + request.once('error', handleError); + + request.once('response', (response) => { + const stream = new Stream.PassThrough(); + if (this._shouldUnzip(response)) { + response.pipe(zlib.createUnzip({ + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH, + })).pipe(stream); + } else { + response.pipe(stream); + } + + const body = []; + + stream.on('data', (chunk) => { + if (!this.push(chunk)) this.pause(); + body.push(chunk); + }); + + stream.once('end', () => { + this.push(null); + const concated = Buffer.concat(body); + + if (this._shouldRedirect(response)) { + let method = this.request.method; + if ([301, 302].includes(response.statusCode)) { + if (method !== 'HEAD') method = 'GET'; + this.data = null; + } else if (response.statusCode === 303) { + method = 'GET'; + } + + const headers = {}; + if (this.request._headerNames) { + for (const name of Object.keys(this.request._headerNames)) { + if (name.toLowerCase() === 'host') continue; + headers[this.request._headerNames[name]] = this.request._headers[name]; + } + } else { + for (const name of Object.keys(this.request._headers)) { + if (name.toLowerCase() === 'host') continue; + const header = this.request._headers[name]; + headers[header.name] = header.value; + } + } + + const newURL = /^https?:\/\//i.test(response.headers.location) ? + response.headers.location : + URL.resolve(makeURLFromRequest(request), response.headers.location); + resolve(new Snekfetch(method, newURL, { data: this.data, headers })); + return; + } + + /** + * @typedef {Object} SnekfetchResponse + * @prop {HTTP.Request} request + * @prop {?string|object|Buffer} body Processed response body + * @prop {string} text Raw response body + * @prop {boolean} ok If the response code is >= 200 and < 300 + * @prop {number} status HTTP status code + * @prop {string} statusText Human readable HTTP status + */ + const res = { + request: this.request, + get body() { + delete res.body; + const type = response.headers['content-type']; + if (type && type.includes('application/json')) { + try { + res.body = JSON.parse(res.text); + } catch (err) { + res.body = res.text; + } // eslint-disable-line no-empty + } else if (type && type.includes('application/x-www-form-urlencoded')) { + res.body = qs.parse(res.text); + } else { + res.body = concated; + } + + return res.body; + }, + text: concated.toString(), + ok: response.statusCode >= 200 && response.statusCode < 300, + headers: response.headers, + status: response.statusCode, + statusText: response.statusText || http.STATUS_CODES[response.statusCode], + }; + + if (res.ok) { + resolve(res); + } else { + const err = new Error(`${res.status} ${res.statusText}`.trim()); + Object.assign(err, res); + reject(err); + } + }); + }); + + this._addFinalHeaders(); + if (this.request.query) this.request.path = `${this.request.path}?${qs.stringify(this.request.query)}`; + request.end(this.data ? this.data.end ? this.data.end() : this.data : null); + }) + .then(resolver, rejector); + } + + catch(rejector) { + return this.then(null, rejector); + } + + /** + * End the request + * @param {Function} [cb] Optional callback to handle the response + * @returns {Snekfetch} This request + */ + end(cb) { + return this.then( + (res) => cb ? cb(null, res) : res, + (err) => cb ? cb(err, err.status ? err : null) : err + ); + } + + _read() { + this.resume(); + if (this.response) return; + this.catch((err) => this.emit('error', err)); + } + + _shouldUnzip(res) { + if (res.statusCode === 204 || res.statusCode === 304) return false; + if (res.headers['content-length'] === '0') return false; + return /^\s*(?:deflate|gzip)\s*$/.test(res.headers['content-encoding']); + } + + _shouldRedirect(res) { + return [301, 302, 303, 307, 308].includes(res.statusCode); + } + + _getFormData() { + if (!this._formData) this._formData = new FormData(); + return this._formData; + } + + _addFinalHeaders() { + if (!this.request) return; + if (!this._getRequestHeader('user-agent')) { + this.set('User-Agent', `snekfetch/${Snekfetch.version} (${Package.repository.url.replace(/\.?git/, '')})`); + } + if (this.request.method !== 'HEAD') this.set('Accept-Encoding', 'gzip, deflate'); + } + + get response() { + return this.request ? this.request.res || this.request._response || null : null; + } + + _getRequestHeader(header) { + // https://github.com/jhiesey/stream-http/pull/77 + try { + return this.request.getHeader(header); + } catch (err) { + return null; + } + } +} + +Snekfetch.version = Package.version; + +Snekfetch.METHODS = http.METHODS ? + http.METHODS.concat('BREW') : + ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD']; +for (const method of Snekfetch.METHODS) { + Snekfetch[method === 'M-SEARCH' ? 'msearch' : method.toLowerCase()] = (url) => new Snekfetch(method, url); +} + +if (true) module.exports = Snekfetch; +else if (typeof window !== 'undefined') window.Snekfetch = Snekfetch; + +function makeURLFromRequest(request) { + return URL.format({ + protocol: request.connection.encrypted ? 'https:' : 'http:', + hostname: request.getHeader('host'), + pathname: request.path.split('?')[0], + query: request.query, + }); +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer)) + +/***/ }), +/* 81 */ +/***/ (function(module, exports) { + +/* (ignored) */ + +/***/ }), +/* 82 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/**/ + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Buffer = __webpack_require__(35).Buffer; +/**/ + +function copyBuffer(src, target, offset) { + src.copy(target, offset); +} + +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; + } + + BufferList.prototype.push = function push(v) { + var entry = { data: v, next: null }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + }; + + BufferList.prototype.unshift = function unshift(v) { + var entry = { data: v, next: this.head }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + }; + + BufferList.prototype.shift = function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + }; + + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; + + BufferList.prototype.join = function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) { + ret += s + p.data; + }return ret; + }; + + BufferList.prototype.concat = function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + if (this.length === 1) return this.head.data; + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + return ret; + }; + + return BufferList; +}(); + +/***/ }), +/* 83 */ +/***/ (function(module, exports, __webpack_require__) { + +var apply = Function.prototype.apply; + +// DOM APIs, for completeness + +exports.setTimeout = function() { + return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); +}; +exports.setInterval = function() { + return new Timeout(apply.call(setInterval, window, arguments), clearInterval); +}; +exports.clearTimeout = +exports.clearInterval = function(timeout) { + if (timeout) { + timeout.close(); + } +}; + +function Timeout(id, clearFn) { + this._id = id; + this._clearFn = clearFn; +} +Timeout.prototype.unref = Timeout.prototype.ref = function() {}; +Timeout.prototype.close = function() { + this._clearFn.call(window, this._id); +}; + +// Does not start the time, just sets up the members needed. +exports.enroll = function(item, msecs) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = msecs; +}; + +exports.unenroll = function(item) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = -1; +}; + +exports._unrefActive = exports.active = function(item) { + clearTimeout(item._idleTimeoutId); + + var msecs = item._idleTimeout; + if (msecs >= 0) { + item._idleTimeoutId = setTimeout(function onTimeout() { + if (item._onTimeout) + item._onTimeout(); + }, msecs); + } +}; + +// setimmediate attaches itself to the global object +__webpack_require__(84); +exports.setImmediate = setImmediate; +exports.clearImmediate = clearImmediate; + + +/***/ }), +/* 84 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) { + "use strict"; + + if (global.setImmediate) { + return; + } + + var nextHandle = 1; // Spec says greater than zero + var tasksByHandle = {}; + var currentlyRunningATask = false; + var doc = global.document; + var registerImmediate; + + function setImmediate(callback) { + // Callback can either be a function or a string + if (typeof callback !== "function") { + callback = new Function("" + callback); + } + // Copy function arguments + var args = new Array(arguments.length - 1); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i + 1]; + } + // Store and register the task + var task = { callback: callback, args: args }; + tasksByHandle[nextHandle] = task; + registerImmediate(nextHandle); + return nextHandle++; + } + + function clearImmediate(handle) { + delete tasksByHandle[handle]; + } + + function run(task) { + var callback = task.callback; + var args = task.args; + switch (args.length) { + case 0: + callback(); + break; + case 1: + callback(args[0]); + break; + case 2: + callback(args[0], args[1]); + break; + case 3: + callback(args[0], args[1], args[2]); + break; + default: + callback.apply(undefined, args); + break; + } + } + + function runIfPresent(handle) { + // From the spec: "Wait until any invocations of this algorithm started before this one have completed." + // So if we're currently running a task, we'll need to delay this invocation. + if (currentlyRunningATask) { + // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a + // "too much recursion" error. + setTimeout(runIfPresent, 0, handle); + } else { + var task = tasksByHandle[handle]; + if (task) { + currentlyRunningATask = true; + try { + run(task); + } finally { + clearImmediate(handle); + currentlyRunningATask = false; + } + } + } + } + + function installNextTickImplementation() { + registerImmediate = function(handle) { + process.nextTick(function () { runIfPresent(handle); }); + }; + } + + function canUsePostMessage() { + // The test against `importScripts` prevents this implementation from being installed inside a web worker, + // where `global.postMessage` means something completely different and can't be used for this purpose. + if (global.postMessage && !global.importScripts) { + var postMessageIsAsynchronous = true; + var oldOnMessage = global.onmessage; + global.onmessage = function() { + postMessageIsAsynchronous = false; + }; + global.postMessage("", "*"); + global.onmessage = oldOnMessage; + return postMessageIsAsynchronous; + } + } + + function installPostMessageImplementation() { + // Installs an event handler on `global` for the `message` event: see + // * https://developer.mozilla.org/en/DOM/window.postMessage + // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages + + var messagePrefix = "setImmediate$" + Math.random() + "$"; + var onGlobalMessage = function(event) { + if (event.source === global && + typeof event.data === "string" && + event.data.indexOf(messagePrefix) === 0) { + runIfPresent(+event.data.slice(messagePrefix.length)); + } + }; + + if (global.addEventListener) { + global.addEventListener("message", onGlobalMessage, false); + } else { + global.attachEvent("onmessage", onGlobalMessage); + } + + registerImmediate = function(handle) { + global.postMessage(messagePrefix + handle, "*"); + }; + } + + function installMessageChannelImplementation() { + var channel = new MessageChannel(); + channel.port1.onmessage = function(event) { + var handle = event.data; + runIfPresent(handle); + }; + + registerImmediate = function(handle) { + channel.port2.postMessage(handle); + }; + } + + function installReadyStateChangeImplementation() { + var html = doc.documentElement; + registerImmediate = function(handle) { + // Create a