UNPKG

selfbot-discord

Version:
147 lines (134 loc) • 4.33 kB
const Util = require('../util/Util'); /** * Helper class for sharded clients spawned as a child process, such as from a ShardingManager. */ class ShardClientUtil { /** * @param {Client} client The client of the current shard */ constructor(client) { this.client = client; process.on('message', this._handleMessage.bind(this)); client.on('ready', () => { process.send({ _ready: true }); }); client.on('disconnect', () => { process.send({ _disconnect: true }); }); client.on('reconnecting', () => { process.send({ _reconnecting: true }); }); } /** * 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) => { process.send(message, err => { if (err) reject(err); else resolve(); }); }); } /** * 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(Util.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(Util.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: Util.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 The 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;