UNPKG

@wilcosp/rex

Version:

Rex is an automated command manager for discord js

454 lines (453 loc) 20.3 kB
"use strict"; /*! * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.importer = exports.RexCommandManager = void 0; const v10_1 = require("discord-api-types/v10"); const promises_1 = __importDefault(require("fs/promises")); const path_1 = require("path"); const applicationCommandBase_js_1 = require("./applicationCommandBase.js"); const button_js_1 = require("./components/button.js"); const mentionSelectMenu_js_1 = require("./components/mentionSelectMenu.js"); const roleSelectMenu_js_1 = require("./components/roleSelectMenu.js"); const selectMenuBase_js_1 = require("./components/selectMenuBase.js"); const stringSelectMenu_js_1 = require("./components/stringSelectMenu.js"); const userSelectMenu_js_1 = require("./components/userSelectMenu.js"); const contextMenuCommand_js_1 = require("./contextmenu/contextMenuCommand.js"); const asyncWait_js_1 = require("./helpers/asyncWait.js"); const modalExecute_js_1 = require("./modals/modalExecute.js"); const commandBase_js_1 = require("./slashCommands/commandBase.js"); const group_js_1 = require("./slashCommands/group.js"); const sub_js_1 = require("./slashCommands/sub.js"); const types_js_1 = require("./types/types.js"); var commandTypes; (function (commandTypes) { commandTypes["slash"] = "slash"; })(commandTypes || (commandTypes = {})); const RELOAD_REGEX = /(\/)?rex-?reload/i; class RexCommandManager { constructor(client, config) { this.client = client; this.config = config; this.commands = new Map(); this.components = new Map(); this.modals = new Map(); this.initializing = true; this.registerQueue = new asyncWait_js_1.RexAsyncDefferedQueue(); this.client.once("ready", () => { this.logger("loading commands"); this.initializeCommands(); process.stdin.on("data", data => { if (RELOAD_REGEX.test(data.toString("utf8"))) { console.log("reloading all commands"); this.initializeCommands(); } }); }); this.client.on("interactionCreate", async (inter) => { try { switch (inter.type) { case v10_1.InteractionType.ApplicationCommandAutocomplete: { this.handleAutoComplete(inter); return; } case v10_1.InteractionType.ApplicationCommand: { switch (inter.commandType) { case v10_1.ApplicationCommandType.ChatInput: { this.handleSlashCommand(inter); return; } case v10_1.ApplicationCommandType.User: case v10_1.ApplicationCommandType.Message: { this.handleContextMenuCommand(inter); return; } } throw new TypeError("Type of interaction not supported", { cause: inter }); } case v10_1.InteractionType.MessageComponent: { this.handleComponent(inter); return; } case v10_1.InteractionType.ModalSubmit: { this.handleModal(inter); return; } default: throw new TypeError("Type of interaction not supported", { cause: inter }); } } catch (e) { this.emitError(e); } }); } errorHandler(error) { throw error; } emitError(error) { this.errorHandler.apply(null, [error]); } onError(fun) { this.errorHandler = fun; } async initializeCommands() { this.initializing = true; this.commands.clear(); this.components.clear(); this.fetchedCommands = this.getFetchedCommands(); this.registerQueue.restart(); const promises = [this.loadCommands(this.config.slashCommandsDir), this.loadCommands(this.config.contextMenuDir)]; Promise.all(promises) .then(() => this.unregisterCommands()) .catch(e => this.emitError(e)) .finally(() => { this.initializing = false; this.registerQueue.end(); }); this.loadComponents()?.catch(e => this.emitError(e)); this.loadModals()?.catch(e => this.emitError(e)); } async fetchGuilds() { let fetched; const guilds = []; let i = 1; do { fetched = await this.client.guilds.fetch({ after: guilds[guilds.length - 1], }); guilds.push(...fetched.map(guild => guild.id)); } while (fetched.size > 199); return guilds; } async getFetchedCommands() { const fetchingGuilds = this.fetchGuilds(); this.DSCommandManager ?? (this.DSCommandManager = this.DSCommandManager = this.client.application?.commands); if (!this.DSCommandManager) { throw Error("discord js command manager isn't present"); } const fetchedCommands = await this.DSCommandManager.fetch({ force: true, withLocalizations: true, }).then(fetched => { const sorted = new Map(); mapping: for (const [, fetchCom] of fetched) { const type = types_js_1.RexCommandMappingByNumber[fetchCom.type]; if (fetchCom.guildId) { sorted.set(`${fetchCom.name}.${type}.${fetchCom.guildId}`, fetchCom); this.logger("fetched command", `${fetchCom.name}.${type}.${fetchCom.guildId}`); continue mapping; } sorted.set(`${fetchCom.name}.${type}.global`, fetchCom); this.logger("fetched command", `${fetchCom.name}.${type}.global`); continue mapping; } return sorted; }); for (const guild of await fetchingGuilds) { await this.DSCommandManager.fetch({ guildId: guild, force: true, withLocalizations: true, }) .then(fetched => { for (const [, fetchCom] of fetched) { const type = types_js_1.RexCommandMappingByNumber[fetchCom.type]; fetchedCommands.set(`${fetchCom.name}.${type}.${fetchCom.guildId}`, fetchCom); this.logger("fetched command", `${fetchCom.name}.${type}.${fetchCom.guildId}`); } }) .catch(e => this.emitError(e)); } return fetchedCommands; } async loadCommands(dir) { if (!dir) return; const directory = (0, path_1.join)((0, path_1.dirname)(process.argv[1]), dir); return promises_1.default .readdir(directory) .then(files => files.filter(file => file.endsWith(".js") || file.endsWith(".mjs") || file.endsWith("cjs"))) .then(async (files) => { const promises = []; for (const file of files) { const filepath = (0, path_1.join)(directory, file); promises.push(promises_1.default .stat(filepath) .then(stats => importer(`${filepath}?t=${Math.floor(stats.ctimeMs)}`)) .then(com => { if (com instanceof applicationCommandBase_js_1.RexApplicationCommandBase) { return com; } if (com.default instanceof applicationCommandBase_js_1.RexApplicationCommandBase) { return com.default; } return null; }) .then(async (com) => { if (!com) { return; } if (com instanceof group_js_1.RexSlashCommandGroup || com instanceof sub_js_1.RexSlashCommandSub) { await com.load(directory); } if (com.guilds && com.guilds?.length > 0) { for (const guild of com.guilds) { await this.registerCommand(com, guild); } return; } return this.registerCommand(com, "global"); }) .catch(e => this.emitError(e))); } return Promise.all(promises); }) .catch(e => this.emitError(e)); } async registerCommand(rexCom, to) { const fetched = await this.fetchedCommands; const type = types_js_1.RexCommandMapping[rexCom.type]; const fetchedCommand = fetched?.get(`${rexCom.name}.${type}.${to}`); if (fetchedCommand && rexCom.equalToCommand(fetchedCommand)) { this.commands.set(`${rexCom.name}.${type}.${to}`, rexCom); fetched.delete(`${rexCom.name}.${type}.${to}`); rexCom.setCommandId(fetchedCommand.id); this.logger("loaded command", `${rexCom.name}.${type}.${to}`); return; } return this.registerQueue .wait() .then(() => this.logger("going to register command", `${rexCom.name}.${type}.${to}`)) .then(() => this.DSCommandManager?.create(rexCom.toCommand(), to == "global" ? undefined : to)?.then(r => { this.commands.set(`${rexCom.name}.${type}.${to}`, rexCom); fetched.delete(`${rexCom.name}.${type}.${to}`); rexCom.setCommandId(r.id); this.logger("registered command", `${rexCom.name}.${type}.${to}`); })) .catch(e => this.emitError(e)) .finally(() => this.registerQueue.next()); } handleSlashCommand(inter) { const appCom = inter.command; const rexCom = this.commands.get(`${appCom?.name}.${types_js_1.RexCommandMapping.ChatInput}.${appCom?.guildId ?? "global"}`); if (rexCom instanceof commandBase_js_1.RexSlashCommandBase) { if (!rexCom) { if (this.initializing) { return; } return inter.reply({ content: "command not found", ephemeral: true, }); } return rexCom.run(inter, this.commands); } } handleAutoComplete(inter) { const appCom = inter.command; const rexCom = this.commands.get(`${appCom?.name}.${types_js_1.RexCommandMapping.ChatInput}.${appCom?.guildId ?? "global"}`); if (rexCom instanceof commandBase_js_1.RexSlashCommandBase) { if (!rexCom) { return; } return rexCom.runAutoComplete(inter, new Map(this.commands)); } } handleContextMenuCommand(inter) { const appCom = inter.command; if (!appCom) { return inter.reply({ ephemeral: true, content: "command couldn't be found", }); } const type = types_js_1.RexCommandMappingByNumber[appCom.type]; const rexCom = this.commands.get(`${appCom?.name}.${type}.${appCom.guildId ?? "global"}`); if (!rexCom || !(rexCom instanceof contextMenuCommand_js_1.RexContextMenuCommand)) { return inter.reply({ ephemeral: true, content: "can't find the command", }); } return rexCom.run(inter); } async unregisterCommands() { await this.registerQueue.wait(); this.registerQueue.next(); if (this.registerQueue.size > 0) { return this.unregisterCommands(); } const fetched = await this.fetchedCommands; if (!fetched || fetched.size < 1) return; for (const [, fetchCommand] of fetched) { await fetchCommand.delete(); this.logger("unregistered command", `${fetchCommand.name}.${types_js_1.RexCommandMappingByNumber[fetchCommand.type]}.${fetchCommand.guildId ?? "global"}`); } this.logger("done with unregistering commands"); } loadComponents() { if (!this.config.componentsDir) return; const directory = (0, path_1.join)((0, path_1.dirname)(process.argv[1]), this.config.componentsDir); return promises_1.default .readdir(directory) .then(files => files.filter(file => file.endsWith(".js") || file.endsWith(".mjs") || file.endsWith("cjs"))) .then(async (files) => { for (const file of files) { const filepath = (0, path_1.join)(directory, file); promises_1.default.stat(filepath) .then(stats => importer(`${filepath}?t=${stats.ctimeMs}`)) .then(com => { if (com instanceof stringSelectMenu_js_1.RexStringSelectMenuComponent || com instanceof button_js_1.RexButtonComponent || com instanceof selectMenuBase_js_1.RexSelectMenuBase) { return com; } return com.default; }) .then(com => { if (com instanceof stringSelectMenu_js_1.RexStringSelectMenuComponent) { this.components.set(`${com.customId}.${types_js_1.RexComponentMapping.string}`, com); this.logger(`loaded string select menu ${com.customId}`); return; } if (com instanceof button_js_1.RexButtonComponent) { this.components.set(`${com.customId}.${types_js_1.RexComponentMapping.button}`, com); this.logger(`loaded button ${com.customId}`); return; } if (com instanceof roleSelectMenu_js_1.RexRoleSelectComponent) { this.components.set(`${com.customId}.${types_js_1.RexComponentMapping.role}`, com); this.logger(`loaded role select menu ${com.customId}`); return; } if (com instanceof userSelectMenu_js_1.RexUserSelectMenuComponent) { this.components.set(`${com.customId}.${types_js_1.RexComponentMapping.user}`, com); this.logger(`loaded user select menu ${com.customId}`); return; } if (com instanceof mentionSelectMenu_js_1.RexMentionSelectMenuComponent) { this.components.set(`${com.customId}.${types_js_1.RexComponentMapping.mention}`, com); this.logger(`loaded mention select menu ${com.customId}`); return; } }) .catch(e => this.emitError(e)); } }) .catch(e => this.emitError(e)); } handleComponent(inter) { if (inter.isButton()) { const rexComp = this.components.get(`${inter.customId}.${types_js_1.RexComponentMapping.button}`); if (rexComp instanceof button_js_1.RexButtonComponent) { return rexComp.run(inter); } } if (inter.isStringSelectMenu()) { const rexComp = this.components.get(`${inter.customId}.${types_js_1.RexComponentMapping.string}`); if (rexComp instanceof stringSelectMenu_js_1.RexStringSelectMenuComponent) { return rexComp.run(inter); } } if (inter.isRoleSelectMenu()) { const rexComp = this.components.get(`${inter.customId}.${types_js_1.RexComponentMapping.role}`); if (rexComp instanceof roleSelectMenu_js_1.RexRoleSelectComponent) { return rexComp.run(inter); } } if (inter.isUserSelectMenu()) { const rexComp = this.components.get(`${inter.customId}.${types_js_1.RexComponentMapping.user}`); if (rexComp instanceof userSelectMenu_js_1.RexUserSelectMenuComponent) { return rexComp.run(inter); } } if (inter.isMentionableSelectMenu()) { const rexComp = this.components.get(`${inter.customId}.${types_js_1.RexComponentMapping.mention}`); if (rexComp instanceof mentionSelectMenu_js_1.RexMentionSelectMenuComponent) { return rexComp.run(inter); } } } loadModals() { if (!this.config.modalDir) return; const directory = (0, path_1.join)((0, path_1.dirname)(process.argv[1]), this.config.modalDir); return promises_1.default .readdir(directory) .then(files => files.filter(file => file.endsWith(".js") || file.endsWith(".mjs") || file.endsWith("cjs"))) .then(async (files) => { for (const file of files) { const filepath = (0, path_1.join)(directory, file); promises_1.default.stat(filepath) .then(stats => importer(`${filepath}?t=${stats.ctimeMs}`)) .then(com => { if (com instanceof modalExecute_js_1.RexModalExecute) { return com; } return com.default; }) .then(com => { if (com instanceof modalExecute_js_1.RexModalExecute) { this.logger(`loaded modal ${com.customId}`); this.modals.set(`${com.customId}.modal`, com); return; } }) .catch(e => this.emitError(e)); } }) .catch(e => this.emitError(e)); } handleModal(inter) { const RexModal = this.modals.get(`${inter.customId}.modal`); if (RexModal instanceof modalExecute_js_1.RexModalExecute) { return RexModal.run(inter); } } logger(...parms) { if (this.config.log) { console.log(...parms); } } } exports.RexCommandManager = RexCommandManager; async function importer(file) { if (file.endsWith(".mjs") || file.includes(".mjs?")) { return esmImport(file); } return _a = file, Promise.resolve().then(() => __importStar(require(_a))); } exports.importer = importer; const esmImport = new Function("file", "return import(file)"); const mdlExtensions = /\.(m|c)?js/i;