How can I add multiple parameters to message.content? - node.js

Need some help with my code. I started coding my first Discord bot today using Node.JS and the Discord.JS library, and others soon. I am currently using a YouTube video to guide me through Node.JS. Here's my question.
Here is my current code.
const Discord = require('discord.js');
const bot = new Discord.Client();
bot.on('message', (message) => {
if(message.content == 'ping') {
message.reply('pong');
}
if(message.content == 'xd') {
message.reply('smh');
}
});
This code works fine. If you go over to the second message.content, it has 'xd' written inside it. When I write the word xd in my Discord server, the bot will return smh. The problem with this is that the bot will only return smh when the capitalization is exactly like it is in xd, but I want it to work for all capitalization.
Like this.
if(message.content == 'xd', 'xD', 'XD', 'Xd',) {
message.reply('pong');
}
Obviously, the above code doesn't work, but is there any easy way to do this?
Cheers.

Before I answer the question, make sure hide your bot's token when sharing your source code because then people can use your bot to do some harmful stuff if it has the right permissions.
Okay so the answer:
At the very beginning, declare a variable called msg or something that stores message.content.toLowerCase();. So no matter how the person types it, it will always be lower case.
Be aware that it will only work if message.content is "xd" only so if a person type "xD something", this wouldn't work.
Additional stuff to your code:
If you want to check if a person used a certain prefix to signify a command, you can check if message.content.startsWith("prefix"); - this will return true or false.
It's always good to break down the content of the message into separate variables allowing you to easily do what you wanna do with the message, for example, a command and args variable (convert message.content into an array first).

You can call .toLowerCase() on message.content to transform it to all lower case:
if (message.content.toLowerCase() == 'xd') {
message.reply('pong');
}
This way the message's casing is practically ignored.

You need to lowercase the message.content using method toLowerCase() method then compare it with "xd string using === operator
ex:
if(message.content && message.content.toLowerCase() === 'xd') {
message.reply("smh");
}

There are 2 other main ways to allow for multiple parameters besides the toLowerCase() function.
The first way would be to use the || operator or the or operator. This method allows for you to check for completely different words while being able to have the same outcome. For example:
if (message.content == "xd" || message.content == "xD" || message.content == "XD") {
message.reply("Why would you do that");
}
The second way would be to use regular expressions like regexes. Regexes check for words inside strings by using the .test() method. For example:
let myRegex = /xd/i;
if (myRegex.test(message.content)) { //We are assuming that message.content = "XD"
message.reply("smh");
}
This method works because the regex checks whether the word is equal to the regex it is assigned to. The i after the /xd/ means that it doesn't care about the casing so this is a shorter way to check for certain words without caring about letter case.

use toLowerCase on the content and then test it
if(message.content.toLowerCase() === 'xd') {
message.reply('pong');
}

A better way todo this is have aliases...
//defined early on.
let commands = [
{
aliases:["xd","lol",":smile:"],
run:function(msg){
msg.send(`pong`);
}
},
{
aliases:[":("],
run:function(msg){
msg.send(`why the long face?`);
}
}
]
//later
client.on(`message`,function(msg){
for(let i =0;i<commands;i++){
if(commands[i].includes(msg.content.toLowerCase())){
commands[i].run();
}
}
});
string.toLowerCase as answered above doesn't always work when you want multiple things to run the same command.
The code above allows aliases for every commands

you can use something like || to have multiple parameters.
In your case it would look like this
if(message.content == 'xd' || 'Xd' || 'xD' || 'XD') {
message.reply('idk');
}
But in your case (because it is always the two same letters in the same order) you can simply add .toLowercase() and then the message will be treated as if it had only lowercase letters and you won't have to worry.
example:
if(message.content.toLowercase() === 'xd') {
message.reply('idk');
}
However there's also a third option to have multiple parameters, you can simply use a constant (const)
example:
const 1 = [ "xd" , "lmao" , "lmfao" ]
if(message.content == 1) {
message.reply('idk');
}
You can change "1" for info
Have a nice day :)
oh and thanks to the moderator that corrected me and sorry for my mistake.

Related

Slowmode command always sends the same thing

I'm trying to get my slowmode command working. Basically when I type >sm 2s it replies with "Please follow this example: ;sm 5" <-- this reply should just send if args are null.
if(args[1] == null) {
return message.channel.send("Please follow this example: ;sm 5")
}if (args[1] !== null) {
message.channel.setRateLimitPerUser(args[0])
message.channel.send(`Slowmode is now ${args[0]}s`)
}}
module.exports.config = {
name: "sm",
aliases: []
}```
In JavaScript and most other languages, you could refer to ! in functions as not.
For example, let's take message.member.hasPermission(). If we add ! at the start, giving us:
if (!message.member.hasPermission('ADMINISTRATOR') return
We're basically telling the client, if the message member does **not** have the administrator permission, return the command.
Now, let's take your if statement, saying if (!args[1] == null) {, you're basically telling the client if not args[1] equals to null do this:, which let's face it makes totally no sense.
When we want to compare a variable with a certain value, we would want to tell the client if args[1] is **not** equal to null, do this. Hence why you should fix you if statement into saying:
if (args[1] !== null) {
Sorry for the pretty long answer, felt like giving a little lesson to someone :p

Detect links in message in specific category

I would like to allow the link to be sent only to channels in certain categories and not to channels in other categories, but it doesn't work.
Here is what I have tried:
if (message.content.includes('discord.gg/'||'discordapp.com/invite/'||'https://'||'http://'))
{
if (message.author.guild.channels.get(c => c.name == "Link" && c.type == "category"))
return;
}
else
message.delete().then(message.reply('U Cant Send Link In Here. Go To Link category'))
You try get message.author.guild. message author its a user so he dont have any guild property. When you try find message.guild.channels or message.member.guild.channels its always will return true, if guild has this channnel. It does not depend on the channel for sending the message. The right way is check channel parrent and if parrent name !== somethink, then delete msg.
And still better replace message.channel.parent.name === 'Link' to message.channel.parent.id === 'CATEGORY ID'
if (message.content.includes('discord.gg/'||'discordapp.com/invite/'||'https://'||'http://')) {
if(message.channel.parent.name === 'Link') return
message.delete().then(msg => message.reply('U Cant Send Link In Here. Go To Link category'))
}
message.author.guild.channels.get(c => c.name == "Link" && c.type == "category")wont work, since it only takes a single key. See here: Map#get
What you would need to do it to use the find function. Since Discord.js uses Collections (basically Map's with additional functions) you have access to additional utility functions. See here: Discord.js#Collections. And via find() you can use functions just fine.
message.author.guild.channels.find(c => c.name == "Link" && c.type == "category")

If statements not working with JSON array

I have a JSON file of 2 discord client IDs `{
{
"premium": [
"a random string of numbers that is a client id",
"a random string of numbers that is a client id"
]
}
I have tried to access these client IDs to do things in the program using a for loop + if statement:
for(i in premium.premium){
if(premium.premium[i] === msg.author.id){
//do some stuff
}else{
//do some stuff
When the program is ran, it runs the for loop and goes to the else first and runs the code in there (not supposed to happen), then runs the code in the if twice. But there are only 2 client IDs and the for loop has ran 3 times, and the first time it runs it goes instantly to the else even though the person who sent the message has their client ID in the JSON file.
How can I fix this? Any help is greatly appreciated.
You may want to add a return statement within your for loop. Otherwise, the loop will continue running until a condition has been met, or it has nothing else to loop over. See the documentation on for loops here.
For example, here it is without return statements:
const json = {
"premium": [
"aaa-1",
"bbb-1"
]
}
for (i in json.premium) {
if (json.premium[i] === "aaa-1") {
console.log("this is aaa-1!!!!")
} else {
console.log("this is not what you're looking for-1...")
}
}
And here it is with return statements:
const json = {
"premium": [
"aaa-2",
"bbb-2"
]
}
function loopOverJson() {
for (i in json.premium) {
if (json.premium[i] === "aaa-2") {
console.log("this is aaa-2!!!!")
return
} else {
console.log("this is not what you're looking for-2...")
return
}
}
}
loopOverJson()
Note: without wrapping the above in a function, the console will show: "Syntax Error: Illegal return statement."
for(i in premium.premium){
if(premium.premium[i] === msg.author.id){
//do some stuff
} else{
//do some stuff
}
}
1) It will loop through all your premium.premium entries. If there are 3 entries it will execute three times. You could use a break statement if you want to exit the loop once a match is found.
2) You should check the type of your msg.author.id. Since you are using the strict comparison operator === it will evaluate to false if your msg.author.id is an integer since you are comparing to a string (based on your provided json).
Use implicit casting: if (premium.premium[i] == msg.author.id)
Use explicit casting: if (premium.premium[i] === String(msg.author.id))
The really fun and easy way to solve problems like this is to use the built-in Array methods like map, reduce or filter. Then you don't have to worry about your iterator values.
eg.
const doSomethingAuthorRelated = (el) => console.log(el, 'whoohoo!');
const authors = premiums
.filter((el) => el === msg.author.id)
.map(doSomethingAuthorRelated);
As John Lonowski points out in the comment link, using for ... in for JavaScript arrays is not reliable, because its designed to iterate over Object properties, so you can't be really sure what its iterating on, unless you've clearly defined the data and are working in an environment where you know no other library has mucked with the Array object.

Elegant way to check if multiple strings are empty

How can I check if multiple strings are empty in an elegant way? This is how I currently do it:
//if one required field is empty, close the connection
if (registerRequest.Email == "") ||
(registerRequest.PhoneNumber == "")||
(registerRequest.NachName =="") ||
(registerRequest.VorName =="") ||
(registerRequest.Password =="") ||
(registerRequest.VerificationId ==""){
//Could not proceed
w.WriteHeader(UNABLE_TO_PROCEED)
w.Write([]byte("Unable to register account."))
return
}
Note: You may use the solution below if you keep the "is-valid" condition in your handler, and also if you separate your condition into another function or method.
You can create a simple helper function, which has a variadic parameter, and you can call it with any number of string values:
func containsEmpty(ss ...string) bool {
for _, s := range ss {
if s == "" {
return true
}
}
return false
}
Example using it:
if containsEmpty("one", "two", "") {
fmt.Println("One is empty!")
} else {
fmt.Println("All is non-empty.")
}
if containsEmpty("one", "two", "three") {
fmt.Println("One is empty!")
} else {
fmt.Println("All is non-empty.")
}
Output of the above (try it on the Go Playground):
One is empty!
All is non-empty.
Your example would look like this:
if containsEmpty(registerRequest.Email,
registerRequest.PhoneNumber,
registerRequest.NachName,
registerRequest.VorName,
registerRequest.Password,
registerRequest.VerificationId) {
// One of the listed strings is empty
}
Also registerRequest is a kinda long name, it could be shortened to like r. If you can't or don't want to rename it in the surrounding code and if you want to shorten the condition, you could also do something like this:
If registerRequest is a pointer (or interface), you could also write:
if r := registerRequest; containsEmpty(r.Email,
r.PhoneNumber,
r.NachName,
r.VorName,
r.Password,
r.VerificationId) {
// One of the listed strings is empty
}
Actually you can do this even if registerRequest is not a pointer, but then the struct will be copied. If registerRequest is a struct, then you can take its address to avoid having to copy it like this:
if r := &registerRequest; containsEmpty(r.Email,
r.PhoneNumber,
r.NachName,
r.VorName,
r.Password,
r.VerificationId) {
// One of the listed strings is empty
}
As Mario Santini mentioned in comment, a way to increase testability, encapsulate this logic, and decouple it from your handler method (which judging by the number of fields looks like it is at risk of changing at a different rate than your handler) could be to put this logic in a function:
func validRequest(registerRequest ?) bool {
return registerRequest.Email == "" ||
registerRequest.PhoneNumber == "" ||
registerRequest.NachName == "" ||
registerRequest.VorName == "" ||
registerRequest.Password == "" ||
registerRequest.VerificationId == ""
}
This now supports very focused, table driven tests, that can exercise what it means to be a valid request independent of any method involving writing headers.
It allows you to verify the valid/invalid path of your enclosing function, but to have very focused tests here. It also allows you to change what it means to be a valid request and verify it independent of your enclosing function.
You can use a switch:
switch "" {
case registerRequest.Email,
registerRequest.NachName,
registerRequest.Password,
registerRequest.PhoneNumber,
registerRequest.VerificationId,
registerRequest.VorName:
w.WriteHeader(UNABLE_TO_PROCEED)
w.Write([]byte("Unable to register account."))
return
}
https://golang.org/ref/spec#Switch_statements

Protractor: Is it possible to check if an element doesn't contain certain text?

On the page that I am testing, a user can have a single currency or multiple currencies (i.e EUR and USD)the currency/currencies will appear in the same div at the top of the page.
If a user has multiple currencies, a tab for each currency will appear further down the page, if a user has only one currency, no tabs will appear (as there is no need for the user to switch tabs).
I am able to test multi currency users by checking to see if the text contained in the header matches the text contained in the currencies tabs.
However, as no tabs appear for a single currency, I'm not sure how to test this.
For example, if I have only a 'EUR' currency, is there a way to do something like
if element(by.className("currencies"))contains 'EUR'
&& doesn't contain 'USD' && doesn't contain 'GBP'
expect element(by.className("tabs").toDisplay.toBeFalsy()
This is the code for the page object file
this.checkCurrency = function(currency) {
var checkBalance = element(by.className("balances"));
checkBalance.getText().then(function (text) {
if (text.indexOf("GBP" && "EUR")>= 0) {
expect(element.all(by.linkText("GBP")).isDisplayed()).toBeTruthy();
console.log("EUR GBP buyer");
}
else if (text.indexOf("GBP" && "USD")>= 0) {
expect(element.all(by.linkText('USD')).isDisplayed()).toBeTruthy();
console.log("USD GBP buyer");
}
else
{
console.log("false");
}
});
};
From your description I'm not quite sure where the failure is. In general you want to keep this kind of logic out of your page object. Your test should understand what state the page should be in and call different functions. I know that's not always possible, but it works out so much better if you can. Here is some general condition advise that should help.
You can catch the success state and a failed state of a promise. Most people use the pass function, but forget about the fail function.
promise.then(passFunction, failFunction)
You can use this in several different ways. If you realize that almost everything in protractor is returning a promise.
Example:
element(by.className("currencies")).getText()
.then(
function(text) {
//check on something
},function(error){
//don't check on something
if(someCondition) {
throw error;
} else {
//the test continues
}
});
You can even do it with and expect
expect(element(by.className("currencies")).getText()).not.toContain("EUR")
.then(
function(passed) {
//check on something
},function(failed){
//don't check on something
if(someCondition) {
throw failed;
} else {
//the test continues
}
});
Or a simple findElement
element(by.className("currencies"))
.then(
function(element) {
//check on something
},function(error){
//don't check on something
if(someCondition) {
throw failed;
} else {
//the test continues
}
});

Resources