diff --git a/.gitignore b/.gitignore index f96c9fa4..d234bfb4 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ report*.json .env config.json command-leaderboard.json +command-last-run.json # Tesseract Trained Data *.traineddata diff --git a/Xiao.js b/Xiao.js index 578dd122..87c4afc5 100644 --- a/Xiao.js +++ b/Xiao.js @@ -92,13 +92,26 @@ client.on('ready', async () => { client.logger.error(`[LEADERBOARD] Could not parse command-leaderboard.json:\n${err.stack}`); } - // Export command-leaderboard.json every 30 minutes + // Export command-last-run.json + try { + const results = client.importLastRun(); + if (!results) client.logger.error('[LASTRUN] command-last-run.json is not formatted correctly.'); + } catch (err) { + client.logger.error(`[LASTRUN] Could not parse command-last-run.json:\n${err.stack}`); + } + + // Export command-leaderboard.json and command-last-run.json every 30 minutes client.setInterval(() => { try { client.exportCommandLeaderboard(); } catch (err) { client.logger.error(`[LEADERBOARD] Failed to export command-leaderboard.json:\n${err.stack}`); } + try { + client.exportLastRun(); + } catch (err) { + client.logger.error(`[LASTRUN] Failed to export command-last-run.json:\n${err.stack}`); + } }, 1.8e+6); }); @@ -178,6 +191,7 @@ client.on('guildMemberRemove', async member => { client.on('disconnect', event => { client.logger.error(`[DISCONNECT] Disconnected with code ${event.code}.`); client.exportCommandLeaderboard(); + client.exportLastRun(); process.exit(0); }); diff --git a/commands/util-public/last-run.js b/commands/util-public/last-run.js index fe253c1c..20f0481c 100644 --- a/commands/util-public/last-run.js +++ b/commands/util-public/last-run.js @@ -22,7 +22,7 @@ module.exports = class LastRunCommand extends Command { run(msg, { command }) { if (command.lastRun === undefined) return msg.reply('That command\'s usage stats aren\'t being tracked.'); - if (!command.lastRun) return msg.reply(`The \`${command.name}\` command has not been run since last reboot.`); + if (!command.lastRun) return msg.reply(`The \`${command.name}\` command has never been run.`); const displayTime = moment.utc(command.lastRun).format('MM/DD/YYYY h:mm A'); return msg.say(`The \`${command.name}\` command was last run on **${displayTime}**.`); } diff --git a/commands/util/command-last-run-export.js b/commands/util/command-last-run-export.js new file mode 100644 index 00000000..abb3b7b5 --- /dev/null +++ b/commands/util/command-last-run-export.js @@ -0,0 +1,30 @@ +const Command = require('../../structures/Command'); + +module.exports = class CommandLastRunExportCommand extends Command { + constructor(client) { + super(client, { + name: 'command-last-run-export', + aliases: [ + 'cmd-lr-export', + 'cmd-last-run-export', + 'command-lr-export', + 'export-cmd-lr', + 'export-cmd-last-run', + 'export-command-lr', + 'export-command-last-run' + ], + group: 'util', + memberName: 'command-last-run-export', + description: 'Exports a command last run JSON file.', + details: 'Only the bot owner(s) may use this command.', + ownerOnly: true, + guarded: true + }); + } + + async run(msg) { + const result = this.client.exportLastRun(); + await msg.direct({ files: [{ attachment: result, name: 'command-last-run.json' }] }); + return msg.say('📬 Sent `command-last-run.json` to your DMs!'); + } +}; diff --git a/commands/util/command-last-run-import.js b/commands/util/command-last-run-import.js new file mode 100644 index 00000000..b976815d --- /dev/null +++ b/commands/util/command-last-run-import.js @@ -0,0 +1,34 @@ +const Command = require('../../structures/Command'); + +module.exports = class CommandLastRunImportCommand extends Command { + constructor(client) { + super(client, { + name: 'command-last-run-import', + aliases: [ + 'cmd-lr-import', + 'cmd-last-run-import', + 'command-lr-import', + 'import-cmd-lr', + 'import-cmd-last-run', + 'import-command-lr', + 'import-command-last-run' + ], + group: 'util', + memberName: 'command-last-run-import', + description: 'Imports a command last run JSON file.', + details: 'Only the bot owner(s) may use this command.', + ownerOnly: true, + guarded: true + }); + } + + run(msg) { + try { + const results = this.client.importLastRun(); + if (!results) return msg.reply('The JSON file provided is invalid.'); + return msg.say('Successfully imported command last run.'); + } catch (err) { + return msg.reply(`Could not read \`command-last-run.json\`: \`${err.message}\`.`); + } + } +}; diff --git a/commands/util/reload.js b/commands/util/reload.js index 7016a091..e56e4f5f 100644 --- a/commands/util/reload.js +++ b/commands/util/reload.js @@ -24,8 +24,10 @@ module.exports = class ReloadCommand extends Command { run(msg, { command }) { this.client.exportCommandLeaderboard(); + this.client.exportLastRun(); command.reload(); this.client.importCommandLeaderboard(); + this.client.importLastRun(); return msg.say(`Reloaded the \`${command.name}\` command.`); } }; diff --git a/commands/util/shutdown.js b/commands/util/shutdown.js index 925bdfac..dfcafa69 100644 --- a/commands/util/shutdown.js +++ b/commands/util/shutdown.js @@ -50,6 +50,7 @@ module.exports = class ShutdownCommand extends Command { try { this.uses++; this.client.exportCommandLeaderboard(); + this.client.exportLastRun(); this.client.logger.info('[SHUTDOWN] Manual shutdown engaged.'); const text = texts[Math.floor(Math.random() * texts.length)]; await msg.say(text); diff --git a/package.json b/package.json index 48f7fe98..a4784706 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "128.4.1", + "version": "128.4.2", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/structures/Client.js b/structures/Client.js index f14839e3..4d5bd678 100644 --- a/structures/Client.js +++ b/structures/Client.js @@ -91,6 +91,38 @@ module.exports = class XiaoClient extends CommandoClient { return buf; } + importLastRun() { + const read = fs.readFileSync(path.join(__dirname, '..', 'command-last-run.json'), { + encoding: 'utf8' + }); + const file = JSON.parse(read); + if (typeof file !== 'object' || Array.isArray(file)) return null; + for (const [id, value] of Object.entries(file)) { + if (!value) continue; + const date = new Date(value); + if (date.toString() === 'Invalid Date') continue; + const found = this.registry.commands.get(id); + if (!found || found.lastRun === undefined) continue; + found.lastRun = date; + } + return file; + } + + exportLastRun() { + let text = '{'; + for (const command of this.registry.commands.values()) { + if (command.lastRun === undefined) continue; + text += `\n "${command.name}": "${command.lastRun ? command.lastRun.toISOString() : null}",`; + } + text = text.slice(0, -1); + text += '\n}\n'; + const buf = Buffer.from(text); + fs.writeFileSync(path.join(__dirname, '..', 'command-last-run.json'), buf, { + encoding: 'utf8' + }); + return buf; + } + fetchReportChannel() { if (!REPORT_CHANNEL_ID) return null; return this.channels.fetch(REPORT_CHANNEL_ID);