Last active
April 23, 2016 19:57
-
-
Save ScottKaye/03a370ae20c8b7b250840b82d048e6b4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const vm = require("vm"); | |
const util = require("util"); | |
const Constants = require("../constants"); | |
let helpText = `you can evaluate **JS code and expressions** with \`!eval\`! | |
:information_source: Just use \`!eval <code>\`. | |
:arrow_right: \`<code>\` is any kind of code block (text surrounded by \\\`\\\`\\\` or \\\`\\\`). You can also specify syntax highlighting (for \`js\`) - it will not be evaluated. | |
`; | |
module.exports = function(Bot) { | |
let publics = { base, unsafe }; | |
function getCode(content) { | |
// Get code from between single or triple backticks, with an optional language of `js` | |
// Then remove all empty items so the final match is at the end of the array | |
let code = (content.match(/(```(?:js)?([\s\S]*?)```|`(.+?)`)/) || []).filter(Boolean); | |
code = code.pop() || null; | |
return code ? code.trim() : null; | |
} | |
function prepareEval(unsafe) { | |
let code = getCode(this.content); | |
if (!code) { | |
return Bot.reply(this, helpText); | |
} | |
doEval.call(this, code, unsafe).then(output => { | |
if (output.length > 1500) { | |
return Bot.reply(this, `:no_entry_sign: Output over 1500 bytes (it's ${ output.length } bytes), not printing.`); | |
} | |
Bot.reply(this, `:white_check_mark: Success! ${ output }`); | |
}).catch(err => { | |
Bot.reply(this, `:warning: Failed!\n${ err }`); | |
}); | |
} | |
function doEval(code, unsafe, retry) { | |
return new Promise((resolve, reject) => { | |
// Did the current call fail to fix itself, or is the code empty? | |
if (!code.length) { | |
return reject("this code doesn't appear to do anything..."); | |
} | |
try { | |
let finalOutput = ["\n"]; | |
let sandbox = {}; | |
let output = []; | |
let evalled = false; | |
// General-purpose logger | |
function gpLog() { | |
let vals = Object.keys(this).map(k => JSON.stringify(this[k], null, " ")); | |
output.push.apply(output, vals); | |
} | |
// Internal, non-enumerable sandbox objects | |
Object.defineProperty(sandbox, "console", { | |
get: () => ({ | |
error: function() { gpLog.call(arguments); }, | |
info: function() { gpLog.call(arguments); }, | |
log: function() { gpLog.call(arguments); }, | |
warn: function() { gpLog.call(arguments); } | |
}) | |
}); | |
// Run code in strict mode | |
if (unsafe) { | |
sandbox = eval(`"use strict"; ${ code }`); | |
} | |
else { | |
vm.runInNewContext(`"use strict"; ${ code }`, sandbox, { | |
filename: "repl.js", | |
timeout: 2000 | |
}); | |
} | |
if (Object.keys(sandbox).length) { | |
let out = util.inspect(sandbox, { depth: null }); | |
// Just show the value of "__expr" if it's the only one there | |
if (retry && Object.keys(sandbox).length === 1) { | |
out = util.inspect(sandbox.__expr, { depth: null }); | |
} | |
// If there's something to print | |
if (out.length) { | |
finalOutput.push(":zap: **Evaluated:**\n```\n" + out + "\n```"); | |
evalled = true; | |
} | |
} | |
if (output.length) { | |
let p = output.join(" "); | |
finalOutput.push(":notepad_spiral: **Logged:**\n```\n" + p + "\n```"); | |
} | |
// If the expression did not evaluate, try again as an expression | |
if (!retry && !evalled) { | |
doEval("var __expr = eval(`" + code.replace(/`/g, "\\`") + "`);", unsafe, true) | |
.then(resolve) | |
.catch(reject); | |
} | |
else { | |
return resolve(finalOutput.join("\n")); | |
} | |
} | |
catch(ex) { | |
return reject(`:red_circle: ${ ex }`); | |
} | |
}); | |
} | |
function base(args) { | |
prepareEval.call(this, false); | |
} | |
function unsafe(args) { | |
prepareEval.call(this, true); | |
} | |
unsafe.restrictTo = [Constants.WRANGLER, "MORE_IDS"]; | |
return publics; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment