Friday, September 20, 2024 12:14:41 AM
> settings

Customize


Authenticate

> esm_command.js
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);
        }
    }
}
All opinions represented herein are my own
- © 2024 itsthedevman
- build 340fbb8