ext/Context.js

"use strict";
// Context.ts - Context for commands (noud02)
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const Eris = require("eris");
const tslint = require("tslint");
const ts = require("typescript");
const vm = require("vm");
/**
 * Context class
 *
 * @export
 * @class Context
 */
class Context {
    constructor(shard, msg, prefix, command, flags, args) {
        this.shard = shard;
        this.msg = msg;
        this.prefix = prefix;
        this.command = command;
        this.flags = flags;
        this.args = args;
        /**
         * Message author
         * @see https://abal.moe/Eris/docs/User
         *
         * @type {Eris.User}
         */
        this.author = this.msg.author;
        /**
         * Channel the message was sent in
         * @see https://abal.moe/Eris/docs/Channel
         * @see https://abal/moe/Eris/docs/GuildChannel
         * @see https://abal.moe/Eris/docs/PrivateChannel
         *
         * @type {(Eris.GuildChannel | Eris.PrivateChannel | Eris.GroupChannel)}
         */
        this.channel = this.msg.channel;
        /**
         * Channel mentions in the message
         *
         * @type {(string[] | undefined)}
         */
        this.channelMentions = this.msg.channelMentions;
        /**
         * Content of the message
         *
         * @type {(string | undefined)}
         */
        this.content = this.msg.content;
        /**
         * Embeds on the message
         *
         * @type {Eris.Embed[]}
         */
        this.embeds = this.msg.embeds;
        /**
         * Clean message content (<@id> -> @username)
         *
         * @type {(string | undefined)}
         */
        this.cleanContent = this.msg.cleanContent;
        /**
         * Guild the message was sent in
         * @see https://abal.moe/Eris/docs/Guild
         *
         * @type {(Eris.Guild | undefined)}
         */
        this.guild = this.msg.channel instanceof Eris.GuildChannel ? this.msg.channel.guild : undefined;
        /**
         * Member that sent the message
         * @see https://abal.moe/Eris/docs/Member
         *
         * @type {(Eris.Member | undefined)}
         */
        this.member = this.msg.member;
        /**
         * User mentions in the message
         *
         * @type {Eris.User[]}
         */
        this.mentions = this.msg.mentions;
        /**
         * When the message was sent
         *
         * @type {number}
         */
        this.timestamp = this.msg.timestamp;
        /**
         * When the message was last edited
         *
         * @type {(number | undefined)}
         */
        this.editedTimestamp = this.msg.editedTimestamp;
        /**
         * Role mentions in the message
         *
         * @type {string[]}
         */
        this.roleMentions = this.msg.roleMentions;
        /**
         * Message content without flags
         *
         * @type {string}
         */
        this.suffix = this.flags._.join(" ");
    }
    /**
     * Sends a message to the channel the message was sent in
     * @example ctx.send("a", "b", "nya") // a b nya
     *
     * @param {...any[]} args Content to send
     * @returns {Promise<Eris.Message>}
     */
    send(...args) {
        return this.msg.channel.createMessage(args.join(" "));
    }
    /**
     * Send a message like you normally would in Eris
     *
     * @see https://abal.moe/Eris/docs/Channel#function-createMessage
     *
     * @param {Eris.MessageContent} content Content to send
     * @param {Eris.MessageFile} [file] File to send
     * @returns {Promise<Eris.Message>}
     */
    createMessage(content, file) {
        return this.msg.channel.createMessage(content, file);
    }
    /**
     * Sends a codeblock
     *
     * @example ctx.sendCode("ts", "const nya: Nya<awoo> = new Nya<awoo>()")
     *
     * @param {string} type Code language
     * @param {...any[]} code Code to send
     * @returns {Promise<Eris.Message>}
     */
    sendCode(type, ...code) {
        return this.msg.channel.createMessage(`\`\`\`${type}\n${code.join(" ")}\`\`\``);
    }
    /**
     * Advanced (typescript) eval, linter included.
     *
     * @example const myEval = ctx.eval("interface IREEE { bool: boolean; }; const myREEE: IREEE = { bool: true }; myREEE;").eval // myEval = { bool: true }
     *
     * @param {string} code Code to run
     * @param {map} customProps Custom properties to include in the eval context
     * @returns {Promise<any>}
     */
    eval(code, customProps) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const builtins = require("repl")._builtinLibs;
            const context = vm.createContext();
            for (const builtin of builtins) {
                Object.defineProperty(context, builtin, {
                    get() {
                        context[builtin] = require(builtin);
                        return require(builtin);
                    },
                    set(val) {
                        delete context[builtin];
                        context[builtin] = val;
                    },
                    configurable: true,
                });
            }
            Object.defineProperty(context, "process", {
                get() {
                    context.process = process;
                    return process;
                },
                set(val) {
                    delete context.process;
                    context.process = val;
                },
                configurable: true,
            });
            for (const prop of Object.keys(customProps)) {
                Object.defineProperty(context, prop, {
                    get() {
                        context[prop] = customProps[prop];
                        return customProps[prop];
                    },
                    set(val) {
                        delete context[prop];
                        context[prop] = val;
                    },
                    configurable: true,
                });
            }
            const compilerOptions = {
                "target": ts.ScriptTarget.ES5,
                "module": ts.ModuleKind.CommonJS,
                "lib": ["es6"],
                "allowJs": false,
                "checkJs": false,
                "removeComments": false,
                "noEmit": false,
                "importHelpers": true,
                "downlevelIteration": true,
                "isolatedModules": false,
                "strict": true,
                "noImplicitAny": true,
                "strictNullChecks": true,
                "noImplicitThis": true,
                "alwaysStrict": false,
                "noUnusedLocals": true,
                "noUnusedParameters": true,
                "noImplicitReturns": true,
                "noFallthroughCasesInSwitch": true,
                "moduleResolution": ts.ModuleResolutionKind.NodeJs,
                "experimentalDecorators": true,
                "emitDecoratorMetadata": true,
                "plugins": [
                    {
                        "name": "tslint-language-service",
                    },
                ],
            };
            const compilerHost = ts.createCompilerHost(compilerOptions);
            const getSourceFile = compilerHost.getSourceFile;
            compilerHost.getSourceFile = function (fn) {
                if (fn === "eval.ts") {
                    return ts.createSourceFile(fn, `${code}\r\n`, ts.ScriptTarget.ES5, false);
                }
                return getSourceFile.apply(this, arguments);
            };
            let output = "";
            compilerHost.writeFile = (_fn, txt) => output += txt;
            const program = ts.createProgram(["eval.ts"], compilerOptions, compilerHost);
            const linter = new tslint.Linter({
                fix: false,
                formatter: "json",
            });
            linter.lint("eval.js", `${code}\r\n`, tslint.Configuration.findConfiguration("tslint.json", "../../").results);
            const errs = program.getSyntacticDiagnostics(program.getSourceFile("eval.ts"));
            errs.concat(program.getSemanticDiagnostics(program.getSourceFile("eval.ts")));
            errs.concat(program.getDeclarationDiagnostics(program.getSourceFile("eval.ts")));
            program.emit();
            const out = yield vm.runInContext(output, context);
            return {
                eval: out,
                ts: {
                    errs,
                    program,
                },
                tslint: linter.getResult(),
            };
        });
    }
}
exports.Context = Context;