diff options
| author | Alee14 <alee14498@gmail.com> | 2017-03-26 15:18:10 -0400 |
|---|---|---|
| committer | Alee14 <alee14498@gmail.com> | 2017-03-26 15:18:10 -0400 |
| commit | 29433e2f7dbd0e4a73d3c78ffe1005b922fb5982 (patch) | |
| tree | aa0ad3fe59468cbe452ee597e914839b68c01436 /node_modules/discord.js/src/sharding | |
| parent | 878fefb4c4e1f12b804ae5c0def433fa873f4c8b (diff) | |
| download | AleeBot-29433e2f7dbd0e4a73d3c78ffe1005b922fb5982.tar.gz AleeBot-29433e2f7dbd0e4a73d3c78ffe1005b922fb5982.tar.bz2 AleeBot-29433e2f7dbd0e4a73d3c78ffe1005b922fb5982.zip | |
Don't mind me i'm adding the discord.js files
Diffstat (limited to 'node_modules/discord.js/src/sharding')
| -rw-r--r-- | node_modules/discord.js/src/sharding/Shard.js | 164 | ||||
| -rw-r--r-- | node_modules/discord.js/src/sharding/ShardClientUtil.js | 143 | ||||
| -rw-r--r-- | node_modules/discord.js/src/sharding/ShardingManager.js | 193 |
3 files changed, 500 insertions, 0 deletions
diff --git a/node_modules/discord.js/src/sharding/Shard.js b/node_modules/discord.js/src/sharding/Shard.js new file mode 100644 index 0000000..ab9b923 --- /dev/null +++ b/node_modules/discord.js/src/sharding/Shard.js @@ -0,0 +1,164 @@ +const childProcess = require('child_process'); +const path = require('path'); +const makeError = require('../util/MakeError'); +const makePlainError = require('../util/MakePlainError'); + +/** + * Represents a Shard spawned by the ShardingManager. + */ +class Shard { + /** + * @param {ShardingManager} manager The sharding manager + * @param {number} id The ID of this shard + * @param {Array} [args=[]] Command line arguments to pass to the script + */ + constructor(manager, id, args = []) { + /** + * Manager that created the shard + * @type {ShardingManager} + */ + this.manager = manager; + + /** + * ID of the shard + * @type {number} + */ + this.id = id; + + /** + * The environment variables for the shard + * @type {Object} + */ + this.env = Object.assign({}, process.env, { + SHARD_ID: this.id, + SHARD_COUNT: this.manager.totalShards, + CLIENT_TOKEN: this.manager.token, + }); + + /** + * Process of the shard + * @type {ChildProcess} + */ + this.process = childProcess.fork(path.resolve(this.manager.file), args, { + env: this.env, + }); + this.process.on('message', this._handleMessage.bind(this)); + this.process.once('exit', () => { + if (this.manager.respawn) this.manager.createShard(this.id); + }); + + this._evals = new Map(); + this._fetches = new Map(); + } + + /** + * Sends a message to the shard's process. + * @param {*} message Message to send to the shard + * @returns {Promise<Shard>} + */ + send(message) { + return new Promise((resolve, reject) => { + const sent = this.process.send(message, err => { + if (err) reject(err); else resolve(this); + }); + if (!sent) throw new Error('Failed to send message to shard\'s process.'); + }); + } + + /** + * Fetches a Client property value of the shard. + * @param {string} prop Name of the Client property to get, using periods for nesting + * @returns {Promise<*>} + * @example + * shard.fetchClientValue('guilds.size').then(count => { + * console.log(`${count} guilds in shard ${shard.id}`); + * }).catch(console.error); + */ + fetchClientValue(prop) { + if (this._fetches.has(prop)) return this._fetches.get(prop); + + const promise = new Promise((resolve, reject) => { + const listener = message => { + if (!message || message._fetchProp !== prop) return; + this.process.removeListener('message', listener); + this._fetches.delete(prop); + resolve(message._result); + }; + this.process.on('message', listener); + + this.send({ _fetchProp: prop }).catch(err => { + this.process.removeListener('message', listener); + this._fetches.delete(prop); + reject(err); + }); + }); + + this._fetches.set(prop, promise); + return promise; + } + + /** + * Evaluates a script on the shard, in the context of the Client. + * @param {string} script JavaScript to run on the shard + * @returns {Promise<*>} Result of the script execution + */ + eval(script) { + if (this._evals.has(script)) return this._evals.get(script); + + const promise = new Promise((resolve, reject) => { + const listener = message => { + if (!message || message._eval !== script) return; + this.process.removeListener('message', listener); + this._evals.delete(script); + if (!message._error) resolve(message._result); else reject(makeError(message._error)); + }; + this.process.on('message', listener); + + this.send({ _eval: script }).catch(err => { + this.process.removeListener('message', listener); + this._evals.delete(script); + reject(err); + }); + }); + + this._evals.set(script, promise); + return promise; + } + + /** + * Handles an IPC message + * @param {*} message Message received + * @private + */ + _handleMessage(message) { + if (message) { + // Shard is requesting a property fetch + if (message._sFetchProp) { + this.manager.fetchClientValues(message._sFetchProp).then( + results => this.send({ _sFetchProp: message._sFetchProp, _result: results }), + err => this.send({ _sFetchProp: message._sFetchProp, _error: makePlainError(err) }) + ); + return; + } + + // Shard is requesting an eval broadcast + if (message._sEval) { + this.manager.broadcastEval(message._sEval).then( + results => this.send({ _sEval: message._sEval, _result: results }), + err => this.send({ _sEval: message._sEval, _error: makePlainError(err) }) + ); + return; + } + } + + /** + * Emitted upon recieving a message from a shard + * @event ShardingManager#message + * @param {Shard} shard Shard that sent the message + * @param {*} message Message that was received + */ + this.manager.emit('message', this, message); + } +} + +module.exports = Shard; diff --git a/node_modules/discord.js/src/sharding/ShardClientUtil.js b/node_modules/discord.js/src/sharding/ShardClientUtil.js new file mode 100644 index 0000000..6449941 --- /dev/null +++ b/node_modules/discord.js/src/sharding/ShardClientUtil.js @@ -0,0 +1,143 @@ +const makeError = require('../util/MakeError'); +const makePlainError = require('../util/MakePlainError'); + +/** + * Helper class for sharded clients spawned as a child process, such as from a ShardingManager + */ +class ShardClientUtil { + /** + * @param {Client} client Client of the current shard + */ + constructor(client) { + this.client = client; + process.on('message', this._handleMessage.bind(this)); + } + + /** + * ID of this shard + * @type {number} + * @readonly + */ + get id() { + return this.client.options.shardId; + } + + /** + * Total number of shards + * @type {number} + * @readonly + */ + get count() { + return this.client.options.shardCount; + } + + /** + * Sends a message to the master process + * @param {*} message Message to send + * @returns {Promise<void>} + */ + send(message) { + return new Promise((resolve, reject) => { + const sent = process.send(message, err => { + if (err) reject(err); else resolve(); + }); + if (!sent) throw new Error('Failed to send message to master process.'); + }); + } + + /** + * Fetches a Client property value of each shard. + * @param {string} prop Name of the Client property to get, using periods for nesting + * @returns {Promise<Array>} + * @example + * client.shard.fetchClientValues('guilds.size').then(results => { + * console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`); + * }).catch(console.error); + */ + fetchClientValues(prop) { + return new Promise((resolve, reject) => { + const listener = message => { + if (!message || message._sFetchProp !== prop) return; + process.removeListener('message', listener); + if (!message._error) resolve(message._result); else reject(makeError(message._error)); + }; + process.on('message', listener); + + this.send({ _sFetchProp: prop }).catch(err => { + process.removeListener('message', listener); + reject(err); + }); + }); + } + + /** + * Evaluates a script on all shards, in the context of the Clients. + * @param {string} script JavaScript to run on each shard + * @returns {Promise<Array>} Results of the script execution + */ + broadcastEval(script) { + return new Promise((resolve, reject) => { + const listener = message => { + if (!message || message._sEval !== script) return; + process.removeListener('message', listener); + if (!message._error) resolve(message._result); else reject(makeError(message._error)); + }; + process.on('message', listener); + + this.send({ _sEval: script }).catch(err => { + process.removeListener('message', listener); + reject(err); + }); + }); + } + + /** + * Handles an IPC message + * @param {*} message Message received + * @private + */ + _handleMessage(message) { + if (!message) return; + if (message._fetchProp) { + const props = message._fetchProp.split('.'); + let value = this.client; + for (const prop of props) value = value[prop]; + this._respond('fetchProp', { _fetchProp: message._fetchProp, _result: value }); + } else if (message._eval) { + try { + this._respond('eval', { _eval: message._eval, _result: this.client._eval(message._eval) }); + } catch (err) { + this._respond('eval', { _eval: message._eval, _error: makePlainError(err) }); + } + } + } + + /** + * Sends a message to the master process, emitting an error from the client upon failure + * @param {string} type Type of response to send + * @param {*} message Message to send + * @private + */ + _respond(type, message) { + this.send(message).catch(err => { + err.message = `Error when sending ${type} response to master process: ${err.message}`; + this.client.emit('error', err); + }); + } + + /** + * Creates/gets the singleton of this class + * @param {Client} client Client to use + * @returns {ShardClientUtil} + */ + static singleton(client) { + if (!this._singleton) { + this._singleton = new this(client); + } else { + client.emit('warn', 'Multiple clients created in child process; only the first will handle sharding helpers.'); + } + return this._singleton; + } +} + +module.exports = ShardClientUtil; diff --git a/node_modules/discord.js/src/sharding/ShardingManager.js b/node_modules/discord.js/src/sharding/ShardingManager.js new file mode 100644 index 0000000..671b5d7 --- /dev/null +++ b/node_modules/discord.js/src/sharding/ShardingManager.js @@ -0,0 +1,193 @@ +const path = require('path'); +const fs = require('fs'); +const EventEmitter = require('events').EventEmitter; +const mergeDefault = require('../util/MergeDefault'); +const Shard = require('./Shard'); +const Collection = require('../util/Collection'); +const fetchRecommendedShards = require('../util/FetchRecommendedShards'); + +/** + * This is a utility class that can be used to help you spawn shards of your Client. Each shard is completely separate + * from the other. The Shard Manager takes a path to a file and spawns it under the specified amount of shards safely. + * If you do not select an amount of shards, the manager will automatically decide the best amount. + * @extends {EventEmitter} + */ +class ShardingManager extends EventEmitter { + /** + * @param {string} file Path to your shard script file + * @param {Object} [options] Options for the sharding manager + * @param {number|string} [options.totalShards='auto'] Number of shards to spawn, or "auto" + * @param {boolean} [options.respawn=true] Whether shards should automatically respawn upon exiting + * @param {string[]} [options.shardArgs=[]] Arguments to pass to the shard script when spawning + * @param {string} [options.token] Token to use for automatic shard count and passing to shards + */ + constructor(file, options = {}) { + super(); + options = mergeDefault({ + totalShards: 'auto', + respawn: true, + shardArgs: [], + token: null, + }, options); + + /** + * Path to the shard script file + * @type {string} + */ + this.file = file; + if (!file) throw new Error('File must be specified.'); + if (!path.isAbsolute(file)) this.file = path.resolve(process.cwd(), file); + const stats = fs.statSync(this.file); + if (!stats.isFile()) throw new Error('File path does not point to a file.'); + + /** + * Amount of shards that this manager is going to spawn + * @type {number|string} + */ + this.totalShards = options.totalShards; + if (this.totalShards !== 'auto') { + if (typeof this.totalShards !== 'number' || isNaN(this.totalShards)) { + throw new TypeError('Amount of shards must be a number.'); + } + if (this.totalShards < 1) throw new RangeError('Amount of shards must be at least 1.'); + if (this.totalShards !== Math.floor(this.totalShards)) { + throw new RangeError('Amount of shards must be an integer.'); + } + } + + /** + * Whether shards should automatically respawn upon exiting + * @type {boolean} + */ + this.respawn = options.respawn; + + /** + * An array of arguments to pass to shards. + * @type {string[]} + */ + this.shardArgs = options.shardArgs; + + /** + * Token to use for obtaining the automatic shard count, and passing to shards + * @type {?string} + */ + this.token = options.token ? options.token.replace(/^Bot\s*/i, '') : null; + + /** + * A collection of shards that this manager has spawned + * @type {Collection<number, Shard>} + */ + this.shards = new Collection(); + } + + /** + * Spawns a single shard. + * @param {number} id The ID of the shard to spawn. **This is usually not necessary.** + * @returns {Promise<Shard>} + */ + createShard(id = this.shards.size) { + const shard = new Shard(this, id, this.shardArgs); + this.shards.set(id, shard); + /** + * Emitted upon launching a shard + * @event ShardingManager#launch + * @param {Shard} shard Shard that was launched + */ + this.emit('launch', shard); + return Promise.resolve(shard); + } + + /** + * Spawns multiple shards. + * @param {number} [amount=this.totalShards] Number of shards to spawn + * @param {number} [delay=5500] How long to wait in between spawning each shard (in milliseconds) + * @returns {Promise<Collection<number, Shard>>} + */ + spawn(amount = this.totalShards, delay = 5500) { + if (amount === 'auto') { + return fetchRecommendedShards(this.token).then(count => { + this.totalShards = count; + return this._spawn(count, delay); + }); + } else { + if (typeof amount !== 'number' || isNaN(amount)) throw new TypeError('Amount of shards must be a number.'); + if (amount < 1) throw new RangeError('Amount of shards must be at least 1.'); + if (amount !== Math.floor(amount)) throw new TypeError('Amount of shards must be an integer.'); + return this._spawn(amount, delay); + } + } + + /** + * Actually spawns shards, unlike that poser above >:( + * @param {number} amount Number of shards to spawn + * @param {number} delay How long to wait in between spawning each shard (in milliseconds) + * @returns {Promise<Collection<number, Shard>>} + * @private + */ + _spawn(amount, delay) { + return new Promise(resolve => { + if (this.shards.size >= amount) throw new Error(`Already spawned ${this.shards.size} shards.`); + this.totalShards = amount; + + this.createShard(); + if (this.shards.size >= this.totalShards) { + resolve(this.shards); + return; + } + + if (delay <= 0) { + while (this.shards.size < this.totalShards) this.createShard(); + resolve(this.shards); + } else { + const interval = setInterval(() => { + this.createShard(); + if (this.shards.size >= this.totalShards) { + clearInterval(interval); + resolve(this.shards); + } + }, delay); + } + }); + } + + /** + * Send a message to all shards. + * @param {*} message Message to be sent to the shards + * @returns {Promise<Shard[]>} + */ + broadcast(message) { + const promises = []; + for (const shard of this.shards.values()) promises.push(shard.send(message)); + return Promise.all(promises); + } + + /** + * Evaluates a script on all shards, in the context of the Clients. + * @param {string} script JavaScript to run on each shard + * @returns {Promise<Array>} Results of the script execution + */ + broadcastEval(script) { + const promises = []; + for (const shard of this.shards.values()) promises.push(shard.eval(script)); + return Promise.all(promises); + } + + /** + * Fetches a Client property value of each shard. + * @param {string} prop Name of the Client property to get, using periods for nesting + * @returns {Promise<Array>} + * @example + * manager.fetchClientValues('guilds.size').then(results => { + * console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`); + * }).catch(console.error); + */ + fetchClientValues(prop) { + if (this.shards.size === 0) return Promise.reject(new Error('No shards have been spawned.')); + if (this.shards.size !== this.totalShards) return Promise.reject(new Error('Still spawning shards.')); + const promises = []; + for (const shard of this.shards.values()) promises.push(shard.fetchClientValue(prop)); + return Promise.all(promises); + } +} + +module.exports = ShardingManager; |
