const CooldownService = require("../services/cooldown_service"); module.exports = class ESMCommand { constructor(bot) { this.ESMBot = bot; this.client = this.ESMBot.client; this.util = this.ESMBot.util; this.db = this.ESMBot.db; this.wss = this.ESMBot.wss; this.regex = this.ESMBot.util.regex; this.errors = this.ESMBot.errors; this.logger = this.ESMBot.logger; this.colors = this.ESMBot.colors; this.info = {}; this.id = ""; this.name = this.constructor.name.replace("ESMCommand_", "").toLowerCase(); this.serverID = ""; this.params = {}; this.message = {}; this.permissions = {}; this.commandParams = {}; this.information = {}; this.configuration = {}; } async exec() { this.info = await this.db.startCommand(this); try { if (!this.util.isEmpty(this.message)) { this.info.author = { id: this.message.author.id, tag: this.message.author.toString() }; this.info.channel = this.message.channel.id; this.db.updateCommand(this.info.id, this.info); await this.fromDiscord(); } else { this.db.updateCommand(this.info.id, { returned: _.isArray(this.params) ? this.params : [this.params] }); await this.fromServer(); } } catch (err) { this.error(err); } } async send(info) { try { // Server ID if (!this.util.isNull(info.server_id)) { this.info.server_id = info.server_id; } else if (!this.util.isNull(this.serverID)) { this.info.server_id = this.serverID; } else if (!this.util.isNull(this.params.server_id)) { this.info.server_id = this.params.server_id; } else { this.info.server_id = ""; } // Channel if (!this.util.isNull(info.channel)) { this.info.channel = info.channel; } else if (this.message.channel) { this.info.channel = this.message.channel.id; } else if (!this.util.isNull(this.info.channel)) { this.info.channel = this.info.channel; } else { this.info.channel = ""; } // Author if (!this.util.isNull(info.author)) { this.info.author = info.author; } else if (!this.util.isNull(this.message.author)) { this.info.author = { tag: this.message.author.toString(), id: this.message.author.id }; } else if (!this.util.isNull(this.info.author)) { this.info.author = this.info.author; } else { this.info.author = { tag: "", id: "" }; } // Command if (!this.util.isNull(info.command)) { this.info.command = info.command; } else { this.info.command = this.name; } // Parameters if (!this.util.isNull(info.parameters)) { this.info.parameters = info.parameters; } else { this.info.parameters = {}; } // Checks if (!this.wss.isServerOnline(this.info.server_id)) { this.resetCooldown(); return this.ESMBot.send(this.info.channel, `${this.info.author.tag}, **${this.info.server_id}** is currently offline. Please try again later`); } if (this.wss.isServerLocked(this.info.server_id)) { this.resetCooldown(); return this.ESMBot.send(this.info.channel, `${this.info.author.tag}, **${this.info.server_id}** is currently locked. Please try again later`); } this.db.updateCommand(this.info.id, this.info); // Send it if (this.ESMBot.config.DEBUG) { console.log(`SENDING:\n${this.ESMBot.inspect(this.info)}`); } await this.wss.send(this.info); } catch (err) { this.error(err); } } async error(err) { if (this.ESMBot.config.DEBUG) { this.ESMBot.logger.error(this.ESMBot.inspect(err)); this.ESMBot.send(this.info.channel, this.ESMBot.inspect(err)); } else { this.ESMBot.logger.error(err); this.db.updateCommand(this.info.id, { error: `${err}` }); if (this.info) { this.ESMBot.send(this.info.channel, this.errors.format(this.info.author.tag, "PROCESS_COMMAND")); } } } async fromDiscord() {} async fromServer() {} toString() { return `Command Info: ${this.info}\nParams: ${this.params}\nServer ID: ${this.serverID}`; } ////////////////////////////////////// // Utility functions ////////////////////////////////////// markAsInvalid() { this.isValidCommand = false; } markAsValid() { this.isValidCommand = true; } isRestricted() { return this.permissions.restricted; } hasCommandParameters() { return !_.isEmpty(this.commandParams); } parameterRegex() { let regex = `^${this.ESMBot.config.COMMAND_SYMBOL}${this.name}`; for (let paramKey in this.commandParams) { let param = this.commandParams[paramKey]; if (param.nullable) { regex += `\\s*(?:(${param.regex.source}))?`; } else { regex += `\\s+(${param.regex.source})`; } } return new RegExp(regex, "i"); } async addCooldown() { await this.db.addCooldown(this); } async onCooldown() { let cooldown = await this.db.getCooldown(this); // We didn't get a cooldown, let them through if (_.isEmpty(cooldown)) return false; // Check our cooldown if (cooldown.type === "times") { // This is a "times" cooldown if (cooldown.amount >= cooldown.quantity) { this.sendCooldownTextTimes(); return true; } } else { // Our usual time objects, min, hour, day, etc. let timeLeft = moment.utc().diff(cooldown.expires_at, "s"); // By default, it's negative. if (timeLeft < 0) { this.sendCooldownText(timeLeft); return true; } } // Default return false; } sendHelpText() { let embed = new this.ESMBot.discord.MessageEmbed() .setColor(this.ESMBot.colors.BLUE) .setDescription(`${this.message.author.toString()}, here is some help info for \`${this.ESMBot.config.COMMAND_SYMBOL}${this.name}\``) .addField("Description", this.information.help) .addField("Usage", `\`${this.ESMBot.config.COMMAND_SYMBOL}${this.name} ${this.information.params}\``); if (this.permissions.requires_premium) { embed.setFooter("This command can only be used on a premium server"); } this.ESMBot.send(this.message.channel, embed); this.markAsInvalid(); } sendMissingParamsText() { let embed = new this.ESMBot.discord.MessageEmbed() .setDescription(`${this.message.author.toString()}, you are missing some arguments for that command.`) .addField("Description", this.information.help) .addField("Usage", `\`${this.ESMBot.config.COMMAND_SYMBOL}${this.name} ${this.information.params}\``); if (this.permissions.requires_premium) { embed.setFooter("This command can only be used on a premium server"); } this.ESMBot.send(this.message.channel, embed); this.markAsInvalid(); } sendCooldownTextTimes() { this.ESMBot.send(this.message.channel, { title: "Cooldown Warning", timestamp: moment.utc(), color: this.ESMBot.colors.YELLOW, description: `You've exceeded the amount of times you can run this command` }); this.markAsInvalid(); } sendCooldownText(timeLeft) { timeLeft = Math.abs(timeLeft); this.ESMBot.send(this.message.channel, { title: "Cooldown Warning", timestamp: moment.utc(), color: this.ESMBot.colors.YELLOW, description: `You cannot use this command for another \`${timeLeft > 60 ? this.util.parseTime(timeLeft) : `${timeLeft} seconds`}\`` }); this.markAsInvalid(); } async checkPermissions(message) { let messageAsArray = message.content.split(" "); this.params = {}; this.message = message; if (message.channel.type === "text") { this.community = await this.db.getCommunityFromGuildID(this.message.guild.id); } // DEBUG only if (this.permissions.debug_only && !this.ESMBot.config.DEBUG) { return this.markAsInvalid(); } // Command is Admin Only if (this.permissions.admin_only || this.ESMBot.config.DEBUG) { if ( !( this.ESMBot.config.DEVS.includes(message.author.id) || this.ESMBot.config.ADMINS.includes(message.author.id) || this.ESMBot.config.DEBUG_EXEMPT.includes(message.author.id) ) ) { return this.markAsInvalid(); } } // Command is Dev Only if (this.permissions.dev_only || this.ESMBot.config.DEBUG) { if ( !( this.ESMBot.config.DEVS.includes(message.author.id) || this.ESMBot.config.DEBUG_EXEMPT.includes(message.author.id) ) ) { return this.markAsInvalid(); } } // Requesting help text if (!this.util.isEmpty(messageAsArray[1]) && (messageAsArray[1].toLowerCase() === "--help")) { return this.sendHelpText(); } // If the command has parameters, try to parse them if (this.hasCommandParameters()) { if (!this.parseParameters()) return this.sendMissingParamsText(); } // Command is only allowed in text channels if (this.permissions.text_only && !(message.channel.type === "text")) { this.ESMBot.send(message.channel, `${message.author.toString()}, \`${this.ESMBot.config.COMMAND_SYMBOL}${this.name}\` can only be used in a **text channel**`); return this.markAsInvalid(); } // Command is DM only if ((this.community && !this.community.player_mode_enabled) && (this.permissions.dm_only && !(message.channel.type === "dm"))) { this.ESMBot.send( message.channel, `${message.author.toString()}, \`${this.ESMBot.config.COMMAND_SYMBOL}${this.name}\` can only be used in a **direct message** with me` ); return this.markAsInvalid(); } // Does the command require registration if (this.permissions.requires_registration) { let isRegistered = await this.db.isRegistered(message.author.id); if (!isRegistered) { this.ESMBot.send(message.channel, this.ESMBot.errors.format(this.message.author, "COMMAND_REQUIRES_REGISTRATION")); return this.markAsInvalid(); } this.params.steamUID = await this.db.getSteamUIDFromID(message.author.id); } // Does this command require a server? if (this.permissions.requires_server) { if (!(await this.db.isValidServer(this.params.serverID))) { let reply = "Please double check your spelling and try again"; let correction = _.map(this.ESMBot.serverIDMatcher.list(this.params.serverID), "value"); if (!this.util.isEmpty(correction)) { reply = `Did you mean: ${this.util.arrayToStringPretty(correction, ", ", "\`")}?`; } this.ESMBot.send(message.channel, `${message.author.toString()}, I was unable to find server **${this.params.serverID}**. ${reply}`); return this.markAsInvalid(); } if (!this.wss.isServerOnline(this.params.serverID)) { this.ESMBot.send(message.channel, `${message.author.toString()}, **${this.params.serverID}** is currently offline. Please try again later`); return this.markAsInvalid(); } this.serverID = this.params.serverID; this.communityID = this.util.getCommunityID(this.serverID); } // Does this command require premium? // if (this.permissions.requires_premium) { // let isPremium = await this.db.isPremium(this.params.serverID); // if (!isPremium) { // this.ESMBot.send(message.channel, `${message.author.toString()}, \`!${this.name}\` requires a premium server`); // return this.markAsInvalid(); // } // } try { this.ESMBot.logger.trace(` **Processing Message** Message Content: ${message.content} User Tag: ${this.message.author.tag} User ID: ${this.message.author.id} Command Name: ${this.name} Community ID: ${this.community.id} Server ID: ${this.serverID ? this.serverID : "N/A"} Params: ${this.ESMBot.inspect(this.params)} `); } catch (err) { } // Handle guild permissions if (!(this.ESMBot.config.DEVS.includes(message.author.id))) { if (message.channel.type === "text") { if (!(await this.allowedInGuild())) return this.markAsInvalid(); } else { if (!(await this.allowedInPM())) return this.markAsInvalid(); } } // Check if we have a cooldown if (await this.onCooldown()) return; // Add the cooldown await this.addCooldown(); this.markAsValid(); } async allowedInPM() { if (!this.permissions.requires_server) return true; let isAllowed = await this.db.isCommandAllowedHerePM(this); if (isAllowed !== true) { this.ESMBot.send(this.message.channel, isAllowed); return false; }; return true; } async allowedInGuild() { if (!this.message.guild.available) { this.ESMBot.send(this.message.channel, this.ESMBot.errors.format(this.message.author, "GUILD_UNAVAILABLE")) return false; } if (this.community.player_mode_enabled && this.permissions.disabled_in_player_mode) { this.ESMBot.send(this.message.channel, this.ESMBot.errors.format(this.message.author, "PLAYER_MODE_ENABLED")); return false; } let isAllowed = await this.db.isCommandAllowedHere(this); if (isAllowed !== true) { this.ESMBot.send(this.message.channel, isAllowed); return false; }; return true; } parseParameters() { let params = {}; let keys = _.keys(this.commandParams); let match = this.parameterRegex().exec(this.message.content); if (this.util.isEmpty(match)) return false; for (let i = 0; i < keys.length; i++) { let key = keys[i]; let param = this.commandParams[key]; let currentMatch = match[i + 1]; if (currentMatch == null && !param.nullable) return false; let value = null; if (currentMatch) { try { switch (param.parseAs) { case "int": value = parseInt(currentMatch); break; case "float": value = parseFloat(currentMatch); break; case "array": case "object": value = JSON.parse(currentMatch); break; default: if (param.preserve_case) { value = currentMatch; } else { value = currentMatch.toLowerCase(); } break; } } catch (err) { value = currentMatch; } } params[key] = value; } this.params = params; return true; } async resetCooldown() { if (["server_initialization"].includes(this.name)) return; let cooldownService = new CooldownService(this.ESMBot.db, this); return await cooldownService.resetCooldownForCommand(); } resetTyping() { if (!_.isEmpty(this.message)) { this.ESMBot.stopTyping(this.message.channel.id); } else if (!_.isEmpty(this.info)) { this.ESMBot.stopTyping(this.info.channel.id); } } }