I'm trying to make a !calculate command in Discord.js v12.
I'm using mathjs and this is my current code:
client.on('message', msg => {
if(!msg.content.startsWith('!')) return;
const args = msg.content.split(/[\ ]/g);
const cmd = args[0].slice(1).toLowerCase();
switch (cmd) {
case 'calculate':
case 'calc':
if(!args[0]) return msg.channel.send('Please input a calculation.');
let resp;
try {
resp = math.evaluate(args.join(''));
} catch (e) {
console.log(e);
}
const membed = new Discord.MessageEmbed()
.setColor('#ffaa00')
.setTitle('Math Calculation')
.addField('Input', `\`\`\`css\n${args.slice(1).join(' ')}\`\`\``)
.addField('Output', `\`\`\`css\n${resp}\`\`\``);
msg.channel.send(membed);
break;
However, I get the following console error:
SyntaxError: Value expected (char 1)
at createSyntaxError (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1705:17)
at parseEnd (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1669:13)
at parseParentheses (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1655:12)
at parseNumber (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1626:12)
at parseObject (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1607:12)
at parseMatrix (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1532:12)
at parseSingleQuotesString (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1433:12)
at parseDoubleQuotesString (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1382:12)
at parseSymbol (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1270:12)
at parseCustomNodes (D:\Documents\test\Node.js\Discord.js\calculate\node_modules\mathjs\lib\expression\parse.js:1239:12) {
char: 1
}
The embed looks like this:
You are handling the args array incorrectly. There are 2 issues:
args[0] needs to be changed to args[1], as args[0] is
referencing the command, not the calculation.
if(!args[0]) return msg.channel.send('Please input a calculation.');
// should become
if(!args[1]) return msg.channel.send('Please input a calculation.');
args.join('') needs to be changed to args.slice(1).join(''), so that it only contains the calculation and not the command at the start.
resp = math.evaluate(args.join(''));
// should become
resp = math.evaluate(args.slice(1).join(''));
Related
I using following code, but some URL from URL List, get 500 Error because of structure of the page.
I get the error exactly on .map((htmlOnePage, index) => line when some URLs page not valid, and the flow control of program goes at Catch Part. How I can find which URL is invalid?
const requestPromise = require('request-promise');
const Promise = require('bluebird');
const cheerio = require('cheerio');
for (var i = 1; i <= 250; i++) {
p = "https://mywebsite.com/" + i.toString()
urls[i - 1] = p
}
Promise.map(urls, requestPromise)
.map((htmlOnePage, index) => {
const $ = cheerio.load(htmlOnePage);
$('.txtSearch1').each(function() {
var h = "";
h = $(this).text()
h = h.replace(/(\r\n|\n|\r)/gm, "")
html44.push(h)
})
shareTuple[urls[index]] = html44;
html44 = []
fs.writeFileSync("data.json", JSON.stringify(shareTuple))
}, {
concurrency: 1
})
.then()
.catch((e) => console.log('We encountered an error' + e));
in other word how I can show which URL get into catch?
You should add try catch phrases inside all iterating functions to pin point the problem. and do the logging for every one based on op that they are catching.
For example i would wrap it like this:
try {
const $ = cheerio.load(htmlOnePage);
$('.txtSearch1').each(function () {
try {
var h="";
h=$(this).text()
h= h.replace(/(\r\n|\n|\r)/gm, "")
html44.push (h)
}catch (error) {
console.log('Error in getting p text');
console.log(error);
}
} catch (error) {
console.log('Error in loading'+ htmlOnePage);
console.log(error);
}
You can break down the error more by looking up through error object values to find the problem if you want to manualy remove it.
The other way to programaticaly remove it is to try doing the request for the page after creating it and also wrapping it in try catch. If it throws an exception you can add in catch to remove it from the list.
That text before console log of error is just so you can see where it broke.
Use a wrapper around requestPromise that catches the error. Note, the return undefined is not really needed. It's just for clarification, that in case of an error nothing is returned.
const requestPromise = require('request-promise');
....
const noThrowRequest = async (url) => {
try {
return await requestPromise(url);
} catch (e) {
return undefined;
}
}
Or if you prefer .then().catch() you can do it as follows
const noThrowRequest = (url) => {
return requestPromise(url)
.then(result => { return result; })
.catch(e => { return undefined; });
}
And then use that wrapper instead of requestPromise and check whether the current result valid or not. I don't know what you want to do in case of an invalid result, so I just return from the callback without any further ado. Adapt that if necessary.
Promise.map(urls, noThrowRequest)
.map((htmlOnePage, index) => {
if (htmlOnePage === undefined) {
console.log(`there was an error at index ${index}`);
return; //stop callback for erronous indexes.
}
...
}
I've tried to look for typo and other inaccuracies and tried to add permission requirement for the prune command but still, the ping pong and the "not a valid number" replies work but not the prune when I enter the amount.
Details: I'm trying to make a Discord bot that can prune based on input. I use DJS v12 and follow(ed) this guide https://v12.discordjs.guide/creating-your-bot/commands-with-user-input.html#number-ranges
if (!msg.content.startsWith(prefix) || msg.author.bot) return;
if (!msg.member.hasPermission("BAN_MEMBERS")) {
msg.channel.send("You don\'t have permission.");
}
const args = msg.content.slice(prefix.length).trim().split('/ +/');
const cmd = args.shift().toLowerCase();
if (cmd === `ping`) {
msg.reply('pong.');
} else if (cmd === `prune`) {
if (!msg.guild.me.hasPermission("MANAGE_MESSAGES")) return;
const amount = parseInt(args[0]) + 1;
if (isNaN(amount)) {
return msg.reply('Not a valid number.');
} else if (amount <= 1 || amount > 100) {
return msg.reply('Please input a number between 1 and 99.');
}
msg.channel.bulkDelete(amount, true).catch(err => {
console.error(err);
msg.channel.send('Error!');
});
}
});
The reason why your prune command doesn't work, is because of your command parsing. args is always null whereas cmd always contains the whole string.
So if you enter $prune 3, your args will be empty and cmd contains prune 3. That's why your if here:
else if (cmd === `prune`)
doesn't match (if you specified arguments) and your prune command never gets executed.
To fix that, you have change your command parsing:
const cmd = msg.content.split(" ")[0].slice(prefix.length);
const args = msg.content.split(" ").slice(1);
Note: Also you seem to have a typo in your question:
if (!msg.guild.me.hasPermission("MANAGE_MESSAGES") return;
// Missing ")" here ----^
So change that line to
if (!msg.guild.me.hasPermission("MANAGE_MESSAGES")) return;
I am having an issue trying to share an alias between two steps in cypress. Basically what I am trying to do is in the first then step, I get the value and pass it in as a variable storedOddsValue. In the second then step, when I try to get that alias value and complete the step, it fails because I beleive it's coming back with undefined.
I know this because instead of using:
let storedOddsValue = oddslib.from("fractional", cy.get("#storedOddsValue"));
If I replace the alias with the hardcoded value like so:
let storedOddsValue = oddslib.from("fractional", "13/5");
The whole code below executes fine and the assertion passes. So my question is what am I doing wrong, why is it not getting the alias?
Then ("The prices are formatted in fractions by default", () => {
priceFormatElements.priceFormatDropdown().should('have.value', 'Fraction');
const oddsValue = oddsSelectionElements.oddsButton().first().invoke("text");
oddsValue.then((oddsValue) => {
expect(oddsValue.trim()).contains("/");
oddsValue.trim();
}).as("storedOddsValue")
})
Then ("The prices are formatted in {string}", (priceFormat) => {
let expectedOddsValue;
let currentOddsValue = oddsSelectionElements.oddsButton().first().invoke("text");
//let storedOddsValue = oddslib.from("fractional", "13/5");
let storedOddsValue = oddslib.from("fractional", cy.get("#storedOddsValue"));
switch (priceFormat) {
case "Fractional":
expectedOddsValue = storedOddsValue.to("fractional");
break;
case "Decimal":
expectedOddsValue = storedOddsValue.to("decimal", {precision: 2});
break;
case "American":
expectedOddsValue = "+" + storedOddsValue.to("moneyline");
break;
default:
throw new Error('unknown price format: ' + priceFormat);
}
currentOddsValue.then((oddsValue) => {
expect(oddsValue.trim()).equals(expectedOddsValue);
});
})
Not sure what oddslib.from does, but to use it with an alias you need to nest in this way
Then (... () => {
let expectedOddsValue
let currentOddsValue = oddsSelectionElements.oddsButton().first().invoke("text")
cy.get("#storedOddsValue").then(aliasValue => {
let storedOddsValue = oddslib.from("fractional", aliasValue)
switch (priceFormat) {
...
}
currentOddsValue.then((oddsValue) => {
expect(oddsValue.trim()).equals(expectedOddsValue);
});
}) // end of cy.get("#storedOddsValue")
})
Because cy.get("#storedOddsValue") is asynchronous, and the switch() runs before it gives it's value.
I have this code:
const func = () => {
throw new Error('hey') + 'boo'
return 'OK'
}
try {
const val = func()
console.log(val)
} catch (error) {
console.log(error)
}
When launched the result is a console line:
"Error: heyboo"
But this is not clear reading the code.
What's the reason for this?
It's because you're doing new Error('hey') + 'boo' and throwing that (which may be surprising). Here's what the code is doing:
Creates the error object
Does + on the error object and 'boo', which converts the error object to string ("Error: hey") and appends "boo" to it
Throws the resulting "Error: heyboo" string
...which you then catch and display.
JavaScript is slightly unusual compared to other languages in that it lets you throw any value, including a string. You aren't restricted to throwing Error objects.
This code does the same thing, hopefully making it a bit clearer by breaking things into steps:
const func = () => {
// Create error
const error = new Error("hey");
// Append `"boo"` to it to get a string
const str = error + "boo";
// Throw the string
throw str;
// This is never reached
return "OK";
};
try {
const val = func();
console.log(val);
} catch (error) {
console.log(error);
}
The [text] is being combined with the [username] (you can place any username where "realdonaldtrump" is. anything after that is the actual message.
I've tried numerous things, only thing I ever got to work was having trump being the default tweet and not being able to change it so it would've been >tweet [message] instead of >tweet [username] [message] but I'd like to have custom usernames. Any clue on how to remove the [username] from the [text]
Here's the code
exports.exec = async (client, message, args, level, settings, texts) => {
const user = args[0];
// Fires Error message that the command wasn't ran correctly.
if (!user) {
return client.emit('commandUsage', message, this.help);
}
// Fires Error message that the command wasn't ran correctly.
const text = args.join(" ");
// Below is a self-deletion message prior to image sending when it's fetching the actual image.
message.channel.send({
embed: {
color: 0,
description: `${message.author} Generating image.`
}
}).then(msg => {
msg.delete(5000).catch(() => { });
}).catch(e => {
});
// Above is a self-deletion message prior to image sending when it's fetching the actual image.
try {
const { body } = await snekfetch.get(`https://nekobot.xyz/api/imagegen?type=${user.toLowerCase() === "realdonaldtrump" ? "trumptweet" : "tweet"}&username=${user.startsWith("#") ? user.slice(1) : user}&text=${encodeURIComponent(text)}`);
message.channel.send("", { file: body.message });
// Below is a automatic logger
} catch (err) {
const errorlogs = client.channels.get('480735959944527886')
const embed = new discord.RichEmbed()
.setAuthor("ERROR", "https://i.imgur.com/Omg7uJV.png")
.setDescription(`${message.author} An error has occured using this command, this has automatically be logged and sent to ${client.channels.get('480735959944527886')} to be reviewed.`)
.setColor(0)
.setTimestamp()
message.channel.send({ embed });
errorlogs.send(`Error with \`$tweet\` command!\n\nError:\n\n ${err}`)
}
// Above is a automatic logger
};
You are concatenating your args to set your text variable
const text = args.join(" ");
But as args value is ["realdonaltrump", "yeeet"] in your example, it results in text having the value "realdonaldtrump yeet".
Just do as you did for the uservariable:
const text = args[1]
You might need to validate the value of the argument.
You can use string.replace method and it will replace the username with nothing:
const text = args.join(" ").replace(new RegExp(`${user}`), "")
Hope it helps!!!