diff --git a/src/lib/commandDispatcher.js b/src/lib/commandDispatcher.js index a156d60..67cfa13 100644 --- a/src/lib/commandDispatcher.js +++ b/src/lib/commandDispatcher.js @@ -1,6 +1,58 @@ const logger = require("./logger.js"); const {pastelize, getTopColor} = require("./utils.js"); +function tobool(val) { + if (isNaN(val)) { + if (val.toString().toLowerCase() === "true") { + return true; + } else if (val.toString().toLowerCase() === "false") { + return false; + } else { + return val; + } + } else { + return Number(val); + } +} + +function removeStartHyphens(val) { + return val.replace(/^-+/g, ""); +} + +// taken from ethanent/gar +// modified to make - arguments only be bools unless = +function parseAsArgv(argv) { + const optional = {}; + const args = []; + + for (const arg of argv) { + const equalsIndex = arg.indexOf("="); + const argName = + equalsIndex === -1 + ? removeStartHyphens(arg) + : removeStartHyphens(arg.slice(0, equalsIndex)); + + if (equalsIndex !== -1) { + props[argName] = convertIfApplicable(arg.slice(equalsIndex + 1)); + } else if (arg.charAt(0) === "-") { + if (arg.charAt(1) === "-") { + props[argName] = true; + } else { + for (let b = 0; b < argName.length; b++) { + props[argName.charAt(b)] = true; + } + } + } else { + args.push(convertIfApplicable(argName)); + } + } + + return { + optional, + args, + }; +} + function parseArguments(str) { return str.match(/\\?.|^$/g).reduce( (p, c) => { @@ -18,7 +70,7 @@ function parseArguments(str) { ).a; } -async function runCommand(msg, cmd, line, args) { +async function runCommand(msg, cmd, line) { let cmdObj = hf.commands.get(cmd); if (!cmdObj) { for (const c of hf.commands.values()) { @@ -43,7 +95,10 @@ async function runCommand(msg, cmd, line, args) { } try { - const ret = cmdObj.callback(msg, line, ...args); + const args = parseArguments(line); + const argv = parseAsArgv(args); + + const ret = cmdObj.callback(msg, line, argv.args, argv.optional); if (ret instanceof Promise) { return await ret; } else { @@ -80,12 +135,10 @@ async function CommandDispatcher(msg) { cmd = cmd.toLowerCase(); line = line.join(" "); - const args = parseArguments(line); - try { - msg.hasRan = true; - const response = await runCommand(msg, cmd, line, args); + const response = await runCommand(msg, cmd, line); if (response != null) { + msg.hasRan = true; if (response.file) { const newFile = response.file; delete response.file; diff --git a/src/modules/bot.js b/src/modules/bot.js index a0cd806..377ae22 100644 --- a/src/modules/bot.js +++ b/src/modules/bot.js @@ -12,7 +12,7 @@ const guildSettings = require("../lib/guildSettings.js"); function spawn(args) { const shell = - process.env.SHELL || (process.platform == "win32" ? "powershell" : "bash"); + process.env.SHELL || (process.platform == "win32" ? "powershell" : "sh"); const newArgs = []; if (shell.match(/powershell/i) && process.platform == "win32") { @@ -122,7 +122,7 @@ exec.category = CATEGORY; exec.helpText = "Executes a command"; exec.callback = async function (msg, line) { const proc = spawn(line); - let out = `Spawned ${proc.pid}: \`${line}'\n`; + let out = `\x1b[1mSpawned ${proc.pid}: \`${line}'\x1b[0m\n`; proc.stdout.on("data", (data) => { out += data; }); @@ -131,7 +131,7 @@ exec.callback = async function (msg, line) { }); proc.on("close", async (code) => { - out += `====\nExited with ${code}`; + out += `\x1b[1m====\n${code != 0 ? "\x1b[31m" : ""}Exited with ${code}`; if (out.length > 1980) { const haste = await hastebin(out); msg.channel.createMessage({ @@ -145,7 +145,7 @@ exec.callback = async function (msg, line) { }); } else { msg.channel.createMessage({ - content: `\`\`\`\n${out}\`\`\``, + content: `\`\`\`ansi\n${out}\`\`\``, allowedMentions: { repliedUser: false, }, @@ -161,7 +161,7 @@ hf.registerCommand(exec); const settings = new Command("settings"); settings.category = CATEGORY; settings.helpText = "Manage guild specific bot settings"; -settings.callback = async function (msg, line, cmd, key, value) { +settings.callback = async function (msg, line, [cmd, key, value]) { if (!msg.guildID) "This command only works in guilds."; switch (cmd) { diff --git a/src/modules/foxwells.js b/src/modules/foxwells.js index e56e9f6..65509a7 100644 --- a/src/modules/foxwells.js +++ b/src/modules/foxwells.js @@ -24,7 +24,7 @@ const utsuholights = new Command("utsuholights"); utsuholights.category = CATEGORY; utsuholights.helpText = "Utsuho Lights"; utsuholights.usage = " [brightness]"; -utsuholights.callback = async function (msg, line, hex, bri) { +utsuholights.callback = async function (msg, line, [hex, bri]) { if (!hex) { return "Hex color required."; } diff --git a/src/modules/image.js b/src/modules/image.js index 26ca47f..0c517b2 100644 --- a/src/modules/image.js +++ b/src/modules/image.js @@ -33,7 +33,7 @@ const dumpy = new Command("dumpy"); dumpy.category = CATEGORY; dumpy.helpText = "Among Us Dumpy GIF Creator"; dumpy.usage = " [url]"; -dumpy.callback = async function (msg, line, width, url) { +dumpy.callback = async function (msg, line, [width, url]) { if (isNaN(parseInt(width))) url = width; width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48); diff --git a/src/modules/misc.js b/src/modules/misc.js index 7191579..eca25a2 100644 --- a/src/modules/misc.js +++ b/src/modules/misc.js @@ -76,13 +76,8 @@ wolfram.category = CATEGORY; wolfram.helpText = "Wolfram Alpha"; wolfram.usage = "<-v> [query]"; wolfram.addAlias("wa"); -wolfram.callback = async function (msg, line) { - let verbose = false; - - if (line.includes("-v")) { - line = line.replace("-v", "").trim(); - verbose = true; - } +wolfram.callback = async function (msg, line, [query], {verbose, v}) { + const _verbose = verbose ?? v; const req = await fetch( `http://api.wolframalpha.com/v2/query?input=${encodeURIComponent( @@ -97,7 +92,7 @@ wolfram.callback = async function (msg, line) { // fake no answer if (data[0].subpods[0].plaintext.includes("geoIP")) return WA_NO_ANSWER; - if (verbose) { + if (_verbose) { const embed = { title: `Result for: \`${safeString(line)}\``, fields: [], @@ -225,7 +220,7 @@ const poll = new Command("poll"); poll.category = CATEGORY; poll.helpText = "Start a poll"; poll.usage = "[topic] [option 1] [option 2] [...option 3-10]"; -poll.callback = async function (msg, line, topic, ...options) { +poll.callback = async function (msg, line, [topic, ...options]) { if (!line || !topic) return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"'; @@ -254,15 +249,20 @@ const vote = new Command("vote"); vote.category = CATEGORY; vote.helpText = "Start a yes/no vote"; vote.usage = "[topic]"; -vote.callback = async function (msg, line) { - if (!line) return "No topic given."; +vote.callback = async function (msg, line, [topic], {maybe}) { + if (!topic) return "No topic given."; return { - content: `**${msg.author.tag}** has started a vote:\n**__${line}__**\n<:ms_tick:503341995348066313>: Yes\n<:ms_cross:503341994974773250>: No`, + content: `**${ + msg.author.tag + }** has started a vote:\n**__${topic}__**\n<:ms_tick:503341995348066313>: Yes\n<:ms_cross:503341994974773250>: No${ + maybe ? "\n<:ms_tilda:581268710925271095> Maybe/Uncertain" : "" + }`, addReactions: [ ":ms_tick:503341995348066313", ":ms_cross:503341994974773250", - ], + maybe && ":ms_tilda:581268710925271095", + ].filter((x) => x != null), }; }; hf.registerCommand(vote); @@ -271,7 +271,7 @@ const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const anonradio = new Command("anonradio"); anonradio.category = CATEGORY; anonradio.helpText = "aNONradio.net schedule"; -anonradio.callback = async function (msg, line) { +anonradio.callback = async function () { const now = new Date(); const playing = await fetch("https://anonradio.net/playing").then((res) => diff --git a/src/modules/moderation.js b/src/modules/moderation.js index be33f18..7df4134 100644 --- a/src/modules/moderation.js +++ b/src/modules/moderation.js @@ -9,7 +9,7 @@ tidy.addAlias("purge"); tidy.category = CATEGORY; tidy.helpText = "Clean up messages"; tidy.usage = "[subcommand] "; -tidy.callback = async function (msg, line, subcommand, count, extra) { +tidy.callback = async function (msg, line, [subcommand, count, extra]) { if (!msg.channel.permissionsOf(msg.author.id).has("MANAGE_MESSAGES")) { return "You do not have `Manage Messages` permission."; } diff --git a/src/modules/utility.js b/src/modules/utility.js index 16f31ab..ab022a9 100644 --- a/src/modules/utility.js +++ b/src/modules/utility.js @@ -66,8 +66,8 @@ const avatar = new Command("avatar"); avatar.category = CATEGORY; avatar.helpText = "Get avatar of a user"; avatar.usage = ""; -avatar.callback = async function (msg, line) { - if (line == "--server" || line == "--guild") { +avatar.callback = async function (msg, line, [user], {server, guild}) { + if (server || guild) { if (!msg.guildID) { return "`--server/--guild` can only be used within guilds."; } else { @@ -87,23 +87,23 @@ avatar.callback = async function (msg, line) { ], }; } - } else if (line) { - const user = await lookupUser(msg, line); + } else if (user) { + const lookup = await lookupUser(msg, user); if ( - user == "No results" || - user == "Canceled" || - user == "Request timed out" + lookup == "No results" || + lookup == "Canceled" || + lookup == "Request timed out" ) { - return user; + return lookup; } else { - let member = user; + let member = lookup; const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID); if (guild) { - if (guild.members.has(user.id)) { - member = guild.members.get(user.id); + if (guild.members.has(lookup.id)) { + member = guild.members.get(lookup.id); } else { const fetched = await guild.fetchMembers({ - userIDs: [user.id], + userIDs: [lookup.id], }); member = fetched[0]; } @@ -203,10 +203,10 @@ const banner = new Command("banner"); banner.category = CATEGORY; banner.helpText = "Get banner of a user"; banner.usage = ""; -banner.callback = async function (msg, line) { +banner.callback = async function (msg, line, [user], {server, guild}) { let id = msg.author.id; - if (line == "--server" || line == "--guild") { + if (server || guild) { if (!msg.guildID) { return "`--server/--guild` can only be used within guilds."; } else { @@ -229,8 +229,8 @@ banner.callback = async function (msg, line) { ], }; } - } else if (line) { - const lookup = await lookupUser(msg, line); + } else if (user) { + const lookup = await lookupUser(msg, user); if ( lookup == "No results" || lookup == "Canceled" || @@ -390,13 +390,8 @@ const snowflake = new Command("snowflake"); snowflake.category = CATEGORY; snowflake.helpText = "Converts a snowflake ID into readable time."; snowflake.usage = "<--twitter> [snowflake]"; -snowflake.callback = function (msg, line) { - let twitter = false; - if (line.startsWith("--twitter")) { - twitter = true; - line.replace("--twitter ", ""); - } - const num = parseInt(line); +snowflake.callback = function (msg, line, [snowflake], {twitter}) { + const num = parseInt(snowflake); if (!isNaN(num)) { let binary = num.toString(2); binary = "0".repeat(64 - binary.length) + binary; @@ -404,9 +399,9 @@ snowflake.callback = function (msg, line) { parseInt(binary.substr(0, 42), 2) + (twitter ? 1288834974657 : 1420070400000); - return `The timestamp for \`${line}\` is ${new Date( - timestamp - ).toUTCString()}`; + return `The timestamp for \`${snowflake}\` is `; } else { return "Argument provided is not a number."; } @@ -490,12 +485,13 @@ const flagdump = new Command("flagdump"); flagdump.category = CATEGORY; flagdump.helpText = "Dumps Discord user flags."; flagdump.usage = "[flags or user mention]"; -flagdump.callback = async function (msg, line) { - const num = parseInt(line); - if (/<@!?([0-9]*)>/.test(line)) { - const id = line.match(/<@!?([0-9]*)>/)[1]; +flagdump.callback = async function (msg, line, [numOrMention], {id}) { + const num = Number(numOrMention); + if (/<@!?(\d+)>/.test(numOrMention) || !isNaN(id)) { + const targetId = id || numOrMention.match(/<@!?(\d+)>/)?.[1]; + if (!targetId) return "Got null ID."; const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID); - let user = guild && (await guild.fetchMembers({userIDs: [id]})); + let user = guild && (await guild.fetchMembers({userIDs: [targetId]})); if (!user || !user[0]) { user = hf.bot.users.get(id); } else {