UNPKG

detritus-client

Version:

A Typescript NodeJS library to interact with Discord's API, both Rest and Gateway.

256 lines (255 loc) 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClusterProcessChild = void 0; const detritus_utils_1 = require("detritus-utils"); const basecollection_1 = require("../collections/basecollection"); const baseset_1 = require("../collections/baseset"); const constants_1 = require("../constants"); const errors_1 = require("../errors"); const utils_1 = require("../utils"); class ClusterProcessChild extends detritus_utils_1.EventSpewer { constructor(cluster) { super(); this._restRequestsWaiting = new basecollection_1.BaseCollection(); this._shardsIdentifying = new baseset_1.BaseSet(); this.clusterCount = 1; this.clusterId = 0; this.cluster = cluster; this.clusterCount = +(process.env.CLUSTER_COUNT || this.clusterCount); this.clusterId = +(process.env.CLUSTER_ID || this.clusterId); process.on('message', this.onMessage.bind(this)); process.on('message', this.emit.bind(this, 'ipc')); this.cluster.on('ready', () => this.sendIPC(constants_1.ClusterIPCOpCodes.READY)); this.cluster.on('shard', ({ shard }) => { shard.gateway.on('state', async ({ state }) => { const { shardId } = shard; if (state === constants_1.SocketStates.READY) { this._shardsIdentifying.delete(shardId); } const data = { shardId, state }; await this.sendIPCOrWarn(constants_1.ClusterIPCOpCodes.SHARD_STATE, data, false); }); shard.gateway.on('close', async (payload) => { const data = { ...payload, shardId: shard.shardId, }; await this.sendIPCOrWarn(constants_1.ClusterIPCOpCodes.CLOSE, data, false); }); shard.gateway.onIdentifyCheck = async () => { const { shardId } = shard; if (this._shardsIdentifying.has(shardId)) { return true; } else { await this.sendIPC(constants_1.ClusterIPCOpCodes.IDENTIFY_REQUEST, { shardId }); } return false; }; }); Object.defineProperties(this, { cluster: { enumerable: false, writable: false }, clusterId: { writable: false }, }); } get hasMultipleClusters() { return 1 < this.clusterCount; } async onMessage(message) { if (!message || typeof (message) !== 'object') { return; } try { switch (message.op) { case constants_1.ClusterIPCOpCodes.EVAL: { if (message.request) { // we received a request to eval from the parent const data = message.data; if (message.shard != null && !this.cluster.shards.has(message.shard)) { await this.sendIPC(constants_1.ClusterIPCOpCodes.EVAL, { ...data, ignored: true, }); } else { try { const result = await Promise.resolve(this.cluster._eval(data.code)); await this.sendIPC(constants_1.ClusterIPCOpCodes.EVAL, { ...data, result, }); } catch (error) { await this.sendIPC(constants_1.ClusterIPCOpCodes.EVAL, { ...data, error: { message: error.message, name: error.name, stack: error.stack, }, }); } } } } ; return; case constants_1.ClusterIPCOpCodes.FILL_INTERACTION_COMMANDS: { const { data } = message.data; if (this.cluster.interactionCommandClient) { this.cluster.interactionCommandClient.validateCommandsFromRaw(data); } } ; return; case constants_1.ClusterIPCOpCodes.IDENTIFY_REQUEST: { // we received an ok to identify const { shardId } = message.data; const shard = this.cluster.shards.get(shardId); if (shard) { this._shardsIdentifying.add(shardId); shard.gateway.identify(); } } ; return; case constants_1.ClusterIPCOpCodes.REST_REQUEST: { const { clusterId } = message; if (clusterId === this.clusterId) { const { error, hash, result } = message.data; if (this._restRequestsWaiting.has(hash)) { const waiting = this._restRequestsWaiting.get(hash); if (error) { waiting.reject(new errors_1.ClusterIPCError(error)); } else { waiting.resolve(result); } } this._restRequestsWaiting.delete(hash); } } ; return; } } catch (error) { const payload = { error }; this.cluster.emit(constants_1.ClientEvents.WARN, payload); } } async send(message) { const parent = process; return new Promise((resolve, reject) => { parent.send(message, (error) => { if (error) { reject(error); } else { resolve(); } }); }); } async sendIPC(op, data = null, request = false, shard) { return this.send({ op, data, request, clusterId: this.clusterId, shard }); } async sendIPCOrWarn(op, data = null, request = false, shard) { try { await this.sendIPC(op, data, request, shard); } catch (error) { const payload = { error }; this.cluster.emit(constants_1.ClientEvents.WARN, payload); } } async broadcastEval(code, ...args) { const parent = process; const nonce = utils_1.Snowflake.generate().id; return new Promise(async (resolve, reject) => { const listener = (message) => { if (message && typeof (message) === 'object') { if (message.request) { return; } switch (message.op) { case constants_1.ClusterIPCOpCodes.EVAL: { const data = message.data; if (data.nonce === nonce) { parent.removeListener('message', listener); if (data.error) { reject(new errors_1.ClusterIPCError(data.error)); } else { const results = (data.results || []).map(([result, isError]) => { if (isError) { return new errors_1.ClusterIPCError(result); } return result; }); resolve(results); } } } ; break; } } }; parent.addListener('message', listener); if (typeof (code) === 'function') { const evalArgs = ['this']; for (let arg of args) { switch (typeof (arg)) { case 'boolean': case 'number': { evalArgs.push(`${arg}`); } ; break; default: { evalArgs.push(`"${arg}"`); } ; } } code = `(${String(code)})(${evalArgs.join(', ')})`; } try { await this.sendIPC(constants_1.ClusterIPCOpCodes.EVAL, { code, nonce }, true); } catch (error) { parent.removeListener('message', listener); reject(error); } }); } async sendRestRequest(name, args) { // add a timeout const hash = (args) ? `${name}-${JSON.stringify(args)}` : name; const promise = new Promise(async (resolve, reject) => { if (this._restRequestsWaiting.has(hash)) { const waiting = this._restRequestsWaiting.get(hash); resolve(waiting.promise); } else { await this.sendIPC(constants_1.ClusterIPCOpCodes.REST_REQUEST, { args, hash, name }, true); const waiting = { promise, reject, resolve }; this._restRequestsWaiting.set(hash, waiting); } }); return promise; } on(event, listener) { super.on(event, listener); return this; } } exports.ClusterProcessChild = ClusterProcessChild;