// Copyright 2021 Campbell Crowley. All rights reserved. // Author: Campbell Crowley (dev@campbellcrowley.com) const SubModule = require('./subModule.js'); /** * @description The script that shuts down SpikeyBot New Years 2021. * @augments SubModule */ class Shutdown extends SubModule { /** * Instantiate Shutdown SubModule. */ constructor() { super(); /** @inheritdoc */ this.myName = 'Shutdown'; /** * @description Stores whether the bot is currently shutting down. * @private * @type {string} * @constant * @default */ this._statusFile = './save/raging.json'; /** * @description List of userIds where the bot will not leave if the person * is in the server. * @private * @type {string[]} * @constant * @default */ this._friends = [ '124733888177111041', '126464376059330562', '165315069717118979', '479294447184773130', ]; /** * @description Guild IDs of which to not leave. * @private * @type {object.<boolean>} * @default */ this._guildWhitelist = { '420045052690169856': true, '650626200020058124': true, }; /** * @description Is the bot currently shutting itself down. * @private * @type {boolean} * @default */ this._shuttingDown = false; /** * @description Do everything except actually leave. * @private * @type {boolean} * @default */ this._dryRun = true; this.save = this.save.bind(this); this._commandRage = this._commandRage.bind(this); this._commandAbortRage = this._commandAbortRage.bind(this); this._beginShutdown = this._beginShutdown.bind(this); this._step = this._step.bind(this); this._abortRaging = this._abortRaging.bind(this); } /** @inheritdoc */ initialize() { this.command.on( new this.command.SingleCommand(['rage'], this._commandRage, { defaultDisabled: true, permissions: this.Discord.PermissionsBitField.Flags.Administrator, })); this.command.on( new this.command.SingleCommand(['abortrage'], this._commandAbortRage, { defaultDisabled: true, permissions: this.Discord.PermissionsBitField.Flags.Administrator, })); this.common.readAndParse(this._statusFile, (err, data) => { if (err) return; this._beginShutdown(data.dryRun); }); this.client.abortRaging = this._abortRaging; this.client.beginRage = this._beginShutdown; } /** @inheritdoc */ shutdown() { this.command.removeListener('rage'); this.command.removeListener('abortrage'); this.client.abortRaging = null; this.client.beginRage = null; } /** * Rage, rage against the dying of the light! * * @private * @this {Shutdown} * @type {commandHandler} * @param {Discord~Message} msg Message that triggered command. * @listens Command#rage */ _commandRage(msg) { this.common.reply( msg, 'Do not go gentle into that good night,\n' + 'Old age should burn and rave at close of day;\n' + 'Rage, rage against the dying of the light.\n\n' + 'Though wise men at their end know dark is right,\n' + 'Because their words had forked no lightning they\n' + 'Do not go gentle into that good night.\n\n' + 'Good men, the last wave by, crying how bright\n' + 'Their frail deeds might have danced in a green bay,\n' + 'Rage, rage against the dying of the light.\n\n' + 'Wild men who caught and sang the sun in flight,\n' + 'And learn, too late, they grieved it on its way,\n' + 'Do not go gentle into that good night.\n\n' + 'Grave men, near death, who see with blinding sight\n' + 'Blind eyes could blaze like meteors and be gay,\n' + 'Rage, rage against the dying of the light.\n\n' + 'And you, my father, there on the sad height,\n' + 'Curse, bless, me now with your fierce tears, I pray.\n' + 'Do not go gentle into that good night.\n\n' + 'Rage, rage against the dying of the light.\n\n' + '**- Dylan Thomas**'); if (msg.author.id !== this.common.spikeyId) return; const dryRun = msg.text != ' rage'; // this._beginShutdown(dryRun); this.common.mkAndWrite(this._statusFile, null, {dryRun: dryRun}, (err) => { if (err) this.error(err); this.client.shard.broadcastEval(`this.beginRage(${dryRun})`) .catch((err) => { this.error(err); this.common.reply('Failed'); }); }); } /** * Abort the shutdown. * * @private * @this {Shutdown} * @type {commandHandler} * @param {Discord~Message} msg Message that triggered command. * @listens Command#abortrage */ _commandAbortRage(msg) { if (msg.author.id !== this.common.spikeyId) return; this.common.unlink(this._statusFile, (err) => { if (err) this.error(err); this.client.shard.broadcastEval('this.abortRaging()') .then(() => this.common.reply(msg, 'Aborted')) .catch((err) => { this.error(err); this.common.reply(msg, 'Failed to abort'); }); }); } /** * @description Abort shutdown procedures. * @private */ _abortRaging() { this._shuttingDown = false; this.warn('Aborted Rage'); } /** * @description Starts the process of shutting down the bot. * @private * * @param {boolean} [dryRun=true] Do everything except actually leave. */ _beginShutdown(dryRun = true) { this._dryRun = dryRun; this._shuttingDown = true; this._step(); } /** * @description Recursively leave one guild at a time. * @private */ _step() { if (!this._shuttingDown) return; let guild = null; const list = [...this.client.guilds.cache.keys()]; let i = -1; while (!guild && i < list.length - 1) { i++; if (this._guildWhitelist[list[i]]) continue; guild = this.client.guilds.cache.get(list[i]); } if (!guild) { this.log('Left all guilds.'); return; } guild.members.fetch({user: this._friends}).then((el) => { if (el.size > 0) { this._guildWhitelist[guild.id] = true; this._step(); } else { if (this._dryRun) { console.log(guild.id); this._guildWhitelist[guild.id] = true; setTimeout(this._step, 100); } else { guild.leave().then(() => this._step()).catch(() => { this.error(`Failed to leave guild: ${guild.id}`); setTimeout(this._step, 5000); }); } } }).catch((err) => { this.error(`Failed to fetch friends: ${guild.id}`); console.error(err); setTimeout(this._step, 5000); }); } } module.exports = new Shutdown();