argv-esque argument parsing
This commit is contained in:
parent
45fd492301
commit
e2819ce5a5
7 changed files with 108 additions and 59 deletions
|
@ -1,6 +1,58 @@
|
||||||
const logger = require("./logger.js");
|
const logger = require("./logger.js");
|
||||||
const {pastelize, getTopColor} = require("./utils.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) {
|
function parseArguments(str) {
|
||||||
return str.match(/\\?.|^$/g).reduce(
|
return str.match(/\\?.|^$/g).reduce(
|
||||||
(p, c) => {
|
(p, c) => {
|
||||||
|
@ -18,7 +70,7 @@ function parseArguments(str) {
|
||||||
).a;
|
).a;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runCommand(msg, cmd, line, args) {
|
async function runCommand(msg, cmd, line) {
|
||||||
let cmdObj = hf.commands.get(cmd);
|
let cmdObj = hf.commands.get(cmd);
|
||||||
if (!cmdObj) {
|
if (!cmdObj) {
|
||||||
for (const c of hf.commands.values()) {
|
for (const c of hf.commands.values()) {
|
||||||
|
@ -43,7 +95,10 @@ async function runCommand(msg, cmd, line, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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) {
|
if (ret instanceof Promise) {
|
||||||
return await ret;
|
return await ret;
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,12 +135,10 @@ async function CommandDispatcher(msg) {
|
||||||
cmd = cmd.toLowerCase();
|
cmd = cmd.toLowerCase();
|
||||||
line = line.join(" ");
|
line = line.join(" ");
|
||||||
|
|
||||||
const args = parseArguments(line);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
msg.hasRan = true;
|
const response = await runCommand(msg, cmd, line);
|
||||||
const response = await runCommand(msg, cmd, line, args);
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
|
msg.hasRan = true;
|
||||||
if (response.file) {
|
if (response.file) {
|
||||||
const newFile = response.file;
|
const newFile = response.file;
|
||||||
delete response.file;
|
delete response.file;
|
||||||
|
|
|
@ -12,7 +12,7 @@ const guildSettings = require("../lib/guildSettings.js");
|
||||||
|
|
||||||
function spawn(args) {
|
function spawn(args) {
|
||||||
const shell =
|
const shell =
|
||||||
process.env.SHELL || (process.platform == "win32" ? "powershell" : "bash");
|
process.env.SHELL || (process.platform == "win32" ? "powershell" : "sh");
|
||||||
|
|
||||||
const newArgs = [];
|
const newArgs = [];
|
||||||
if (shell.match(/powershell/i) && process.platform == "win32") {
|
if (shell.match(/powershell/i) && process.platform == "win32") {
|
||||||
|
@ -122,7 +122,7 @@ exec.category = CATEGORY;
|
||||||
exec.helpText = "Executes a command";
|
exec.helpText = "Executes a command";
|
||||||
exec.callback = async function (msg, line) {
|
exec.callback = async function (msg, line) {
|
||||||
const proc = spawn(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) => {
|
proc.stdout.on("data", (data) => {
|
||||||
out += data;
|
out += data;
|
||||||
});
|
});
|
||||||
|
@ -131,7 +131,7 @@ exec.callback = async function (msg, line) {
|
||||||
});
|
});
|
||||||
|
|
||||||
proc.on("close", async (code) => {
|
proc.on("close", async (code) => {
|
||||||
out += `====\nExited with ${code}`;
|
out += `\x1b[1m====\n${code != 0 ? "\x1b[31m" : ""}Exited with ${code}`;
|
||||||
if (out.length > 1980) {
|
if (out.length > 1980) {
|
||||||
const haste = await hastebin(out);
|
const haste = await hastebin(out);
|
||||||
msg.channel.createMessage({
|
msg.channel.createMessage({
|
||||||
|
@ -145,7 +145,7 @@ exec.callback = async function (msg, line) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
msg.channel.createMessage({
|
msg.channel.createMessage({
|
||||||
content: `\`\`\`\n${out}\`\`\``,
|
content: `\`\`\`ansi\n${out}\`\`\``,
|
||||||
allowedMentions: {
|
allowedMentions: {
|
||||||
repliedUser: false,
|
repliedUser: false,
|
||||||
},
|
},
|
||||||
|
@ -161,7 +161,7 @@ hf.registerCommand(exec);
|
||||||
const settings = new Command("settings");
|
const settings = new Command("settings");
|
||||||
settings.category = CATEGORY;
|
settings.category = CATEGORY;
|
||||||
settings.helpText = "Manage guild specific bot settings";
|
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.";
|
if (!msg.guildID) "This command only works in guilds.";
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ const utsuholights = new Command("utsuholights");
|
||||||
utsuholights.category = CATEGORY;
|
utsuholights.category = CATEGORY;
|
||||||
utsuholights.helpText = "Utsuho Lights";
|
utsuholights.helpText = "Utsuho Lights";
|
||||||
utsuholights.usage = "<hex> [brightness]";
|
utsuholights.usage = "<hex> [brightness]";
|
||||||
utsuholights.callback = async function (msg, line, hex, bri) {
|
utsuholights.callback = async function (msg, line, [hex, bri]) {
|
||||||
if (!hex) {
|
if (!hex) {
|
||||||
return "Hex color required.";
|
return "Hex color required.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ const dumpy = new Command("dumpy");
|
||||||
dumpy.category = CATEGORY;
|
dumpy.category = CATEGORY;
|
||||||
dumpy.helpText = "Among Us Dumpy GIF Creator";
|
dumpy.helpText = "Among Us Dumpy GIF Creator";
|
||||||
dumpy.usage = "<width> [url]";
|
dumpy.usage = "<width> [url]";
|
||||||
dumpy.callback = async function (msg, line, width, url) {
|
dumpy.callback = async function (msg, line, [width, url]) {
|
||||||
if (isNaN(parseInt(width))) url = width;
|
if (isNaN(parseInt(width))) url = width;
|
||||||
width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48);
|
width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48);
|
||||||
|
|
||||||
|
|
|
@ -76,13 +76,8 @@ wolfram.category = CATEGORY;
|
||||||
wolfram.helpText = "Wolfram Alpha";
|
wolfram.helpText = "Wolfram Alpha";
|
||||||
wolfram.usage = "<-v> [query]";
|
wolfram.usage = "<-v> [query]";
|
||||||
wolfram.addAlias("wa");
|
wolfram.addAlias("wa");
|
||||||
wolfram.callback = async function (msg, line) {
|
wolfram.callback = async function (msg, line, [query], {verbose, v}) {
|
||||||
let verbose = false;
|
const _verbose = verbose ?? v;
|
||||||
|
|
||||||
if (line.includes("-v")) {
|
|
||||||
line = line.replace("-v", "").trim();
|
|
||||||
verbose = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const req = await fetch(
|
const req = await fetch(
|
||||||
`http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(
|
`http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(
|
||||||
|
@ -97,7 +92,7 @@ wolfram.callback = async function (msg, line) {
|
||||||
// fake no answer
|
// fake no answer
|
||||||
if (data[0].subpods[0].plaintext.includes("geoIP")) return WA_NO_ANSWER;
|
if (data[0].subpods[0].plaintext.includes("geoIP")) return WA_NO_ANSWER;
|
||||||
|
|
||||||
if (verbose) {
|
if (_verbose) {
|
||||||
const embed = {
|
const embed = {
|
||||||
title: `Result for: \`${safeString(line)}\``,
|
title: `Result for: \`${safeString(line)}\``,
|
||||||
fields: [],
|
fields: [],
|
||||||
|
@ -225,7 +220,7 @@ const poll = new Command("poll");
|
||||||
poll.category = CATEGORY;
|
poll.category = CATEGORY;
|
||||||
poll.helpText = "Start a poll";
|
poll.helpText = "Start a poll";
|
||||||
poll.usage = "[topic] [option 1] [option 2] [...option 3-10]";
|
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)
|
if (!line || !topic)
|
||||||
return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"';
|
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.category = CATEGORY;
|
||||||
vote.helpText = "Start a yes/no vote";
|
vote.helpText = "Start a yes/no vote";
|
||||||
vote.usage = "[topic]";
|
vote.usage = "[topic]";
|
||||||
vote.callback = async function (msg, line) {
|
vote.callback = async function (msg, line, [topic], {maybe}) {
|
||||||
if (!line) return "No topic given.";
|
if (!topic) return "No topic given.";
|
||||||
|
|
||||||
return {
|
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: [
|
addReactions: [
|
||||||
":ms_tick:503341995348066313",
|
":ms_tick:503341995348066313",
|
||||||
":ms_cross:503341994974773250",
|
":ms_cross:503341994974773250",
|
||||||
],
|
maybe && ":ms_tilda:581268710925271095",
|
||||||
|
].filter((x) => x != null),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
hf.registerCommand(vote);
|
hf.registerCommand(vote);
|
||||||
|
@ -271,7 +271,7 @@ const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||||
const anonradio = new Command("anonradio");
|
const anonradio = new Command("anonradio");
|
||||||
anonradio.category = CATEGORY;
|
anonradio.category = CATEGORY;
|
||||||
anonradio.helpText = "aNONradio.net schedule";
|
anonradio.helpText = "aNONradio.net schedule";
|
||||||
anonradio.callback = async function (msg, line) {
|
anonradio.callback = async function () {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
const playing = await fetch("https://anonradio.net/playing").then((res) =>
|
const playing = await fetch("https://anonradio.net/playing").then((res) =>
|
||||||
|
|
|
@ -9,7 +9,7 @@ tidy.addAlias("purge");
|
||||||
tidy.category = CATEGORY;
|
tidy.category = CATEGORY;
|
||||||
tidy.helpText = "Clean up messages";
|
tidy.helpText = "Clean up messages";
|
||||||
tidy.usage = "[subcommand] <count> <extra>";
|
tidy.usage = "[subcommand] <count> <extra>";
|
||||||
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")) {
|
if (!msg.channel.permissionsOf(msg.author.id).has("MANAGE_MESSAGES")) {
|
||||||
return "You do not have `Manage Messages` permission.";
|
return "You do not have `Manage Messages` permission.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,8 @@ const avatar = new Command("avatar");
|
||||||
avatar.category = CATEGORY;
|
avatar.category = CATEGORY;
|
||||||
avatar.helpText = "Get avatar of a user";
|
avatar.helpText = "Get avatar of a user";
|
||||||
avatar.usage = "<user>";
|
avatar.usage = "<user>";
|
||||||
avatar.callback = async function (msg, line) {
|
avatar.callback = async function (msg, line, [user], {server, guild}) {
|
||||||
if (line == "--server" || line == "--guild") {
|
if (server || guild) {
|
||||||
if (!msg.guildID) {
|
if (!msg.guildID) {
|
||||||
return "`--server/--guild` can only be used within guilds.";
|
return "`--server/--guild` can only be used within guilds.";
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,23 +87,23 @@ avatar.callback = async function (msg, line) {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (line) {
|
} else if (user) {
|
||||||
const user = await lookupUser(msg, line);
|
const lookup = await lookupUser(msg, user);
|
||||||
if (
|
if (
|
||||||
user == "No results" ||
|
lookup == "No results" ||
|
||||||
user == "Canceled" ||
|
lookup == "Canceled" ||
|
||||||
user == "Request timed out"
|
lookup == "Request timed out"
|
||||||
) {
|
) {
|
||||||
return user;
|
return lookup;
|
||||||
} else {
|
} else {
|
||||||
let member = user;
|
let member = lookup;
|
||||||
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
||||||
if (guild) {
|
if (guild) {
|
||||||
if (guild.members.has(user.id)) {
|
if (guild.members.has(lookup.id)) {
|
||||||
member = guild.members.get(user.id);
|
member = guild.members.get(lookup.id);
|
||||||
} else {
|
} else {
|
||||||
const fetched = await guild.fetchMembers({
|
const fetched = await guild.fetchMembers({
|
||||||
userIDs: [user.id],
|
userIDs: [lookup.id],
|
||||||
});
|
});
|
||||||
member = fetched[0];
|
member = fetched[0];
|
||||||
}
|
}
|
||||||
|
@ -203,10 +203,10 @@ const banner = new Command("banner");
|
||||||
banner.category = CATEGORY;
|
banner.category = CATEGORY;
|
||||||
banner.helpText = "Get banner of a user";
|
banner.helpText = "Get banner of a user";
|
||||||
banner.usage = "<user>";
|
banner.usage = "<user>";
|
||||||
banner.callback = async function (msg, line) {
|
banner.callback = async function (msg, line, [user], {server, guild}) {
|
||||||
let id = msg.author.id;
|
let id = msg.author.id;
|
||||||
|
|
||||||
if (line == "--server" || line == "--guild") {
|
if (server || guild) {
|
||||||
if (!msg.guildID) {
|
if (!msg.guildID) {
|
||||||
return "`--server/--guild` can only be used within guilds.";
|
return "`--server/--guild` can only be used within guilds.";
|
||||||
} else {
|
} else {
|
||||||
|
@ -229,8 +229,8 @@ banner.callback = async function (msg, line) {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (line) {
|
} else if (user) {
|
||||||
const lookup = await lookupUser(msg, line);
|
const lookup = await lookupUser(msg, user);
|
||||||
if (
|
if (
|
||||||
lookup == "No results" ||
|
lookup == "No results" ||
|
||||||
lookup == "Canceled" ||
|
lookup == "Canceled" ||
|
||||||
|
@ -390,13 +390,8 @@ const snowflake = new Command("snowflake");
|
||||||
snowflake.category = CATEGORY;
|
snowflake.category = CATEGORY;
|
||||||
snowflake.helpText = "Converts a snowflake ID into readable time.";
|
snowflake.helpText = "Converts a snowflake ID into readable time.";
|
||||||
snowflake.usage = "<--twitter> [snowflake]";
|
snowflake.usage = "<--twitter> [snowflake]";
|
||||||
snowflake.callback = function (msg, line) {
|
snowflake.callback = function (msg, line, [snowflake], {twitter}) {
|
||||||
let twitter = false;
|
const num = parseInt(snowflake);
|
||||||
if (line.startsWith("--twitter")) {
|
|
||||||
twitter = true;
|
|
||||||
line.replace("--twitter ", "");
|
|
||||||
}
|
|
||||||
const num = parseInt(line);
|
|
||||||
if (!isNaN(num)) {
|
if (!isNaN(num)) {
|
||||||
let binary = num.toString(2);
|
let binary = num.toString(2);
|
||||||
binary = "0".repeat(64 - binary.length) + binary;
|
binary = "0".repeat(64 - binary.length) + binary;
|
||||||
|
@ -404,9 +399,9 @@ snowflake.callback = function (msg, line) {
|
||||||
parseInt(binary.substr(0, 42), 2) +
|
parseInt(binary.substr(0, 42), 2) +
|
||||||
(twitter ? 1288834974657 : 1420070400000);
|
(twitter ? 1288834974657 : 1420070400000);
|
||||||
|
|
||||||
return `The timestamp for \`${line}\` is ${new Date(
|
return `The timestamp for \`${snowflake}\` is <t:${Math.floor(
|
||||||
timestamp
|
timestamp / 1000
|
||||||
).toUTCString()}`;
|
)}:F>`;
|
||||||
} else {
|
} else {
|
||||||
return "Argument provided is not a number.";
|
return "Argument provided is not a number.";
|
||||||
}
|
}
|
||||||
|
@ -490,12 +485,13 @@ const flagdump = new Command("flagdump");
|
||||||
flagdump.category = CATEGORY;
|
flagdump.category = CATEGORY;
|
||||||
flagdump.helpText = "Dumps Discord user flags.";
|
flagdump.helpText = "Dumps Discord user flags.";
|
||||||
flagdump.usage = "[flags or user mention]";
|
flagdump.usage = "[flags or user mention]";
|
||||||
flagdump.callback = async function (msg, line) {
|
flagdump.callback = async function (msg, line, [numOrMention], {id}) {
|
||||||
const num = parseInt(line);
|
const num = Number(numOrMention);
|
||||||
if (/<@!?([0-9]*)>/.test(line)) {
|
if (/<@!?(\d+)>/.test(numOrMention) || !isNaN(id)) {
|
||||||
const id = line.match(/<@!?([0-9]*)>/)[1];
|
const targetId = id || numOrMention.match(/<@!?(\d+)>/)?.[1];
|
||||||
|
if (!targetId) return "Got null ID.";
|
||||||
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
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]) {
|
if (!user || !user[0]) {
|
||||||
user = hf.bot.users.get(id);
|
user = hf.bot.users.get(id);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue