Discord.js Issue with .includes function

I’m trying to make my bot to return a message when the arg isn’t one of the colors in the variable but it keeps sending this message:

issue with the command

There are two issues I couldn’t fix

  1. the bot sends both messages for “red”
  2. the bot says “that is not a valid skin” even though I defined “black” in the colors variable
let skin = db.fetch(`${message.author.id}.skin`)
        if (skin === null) skin = "red"; //red
        let skin2 = client.emojis.cache.get(e[skin])
        var colors = ["red", "blue", "green", "pink", "orange", "yellow", "black", "white", "purple", "brown", "cyan", "cyan"]

        const attachment = new Discord.MessageAttachment(`./assets/colors/${skin}.png`, `${skin}.png`);
        const embed = new Discord.MessageEmbed()
        .attachFiles(attachment) // <- add attachment
        .setAuthor(`${message.author.tag}'s skins (${message.author.id})`, message.author.avatarURL())
        .setThumbnail(`attachment://${skin}.png`)
        .addField(`Current skin`, `${skin2}`)
        .addField(`Default skins`, `red, blue, green, pink, orange, yellow, black, white, purple, brown, cyan, lime`)
        .addField(`Unlocked skins`, `none`)
        .addField(`Locked skins`, `none`)
        .setDescription(`To set a skin use ${prefix}skin <skin>`)
        .setTimestamp()
        .setColor('#00ffff')
        .setFooter(message.member.user.tag, message.author.avatarURL());
        if(!args[0]) return message.channel.send(embed)
        for (var i=0; i < colors.length; i++) {
            if (args[0].includes(colors[i])) {
              let skin3 = args[0]
              db.set(`${message.author.id}.skin`, args[0])
              message.channel.send(`Set your skin to ${args[0]}`)

        
            } else if(!args[0].includes(colors[i])) {
                return message.channel.send(`That is not a valid skin`)
            }
        }

As you can see

var colors = ["red", "blue", "green", "pink", "orange", "yellow", "black", "white", "purple", "brown", "cyan", "cyan"]

already has “black” in it but it executes the else if function.

Answer

When you iterate over the colors array and check if (args[0].includes(colors[i])) you check if a string includes the colors[i] string. String#includes checks if one string (colors[i]) is found within another string (args[0]).

The example below shows how it works. It checks every item in colors one by one:

const colors = ['red', 'green', 'blue']
const args = ['red']

for (let i = 0; i < colors.length; i++) {
  console.log(`Does the string "${args[0]}" include the string "${colors[i]}"?`)
  if (args[0].includes(colors[i])) {
    console.log('yes')
  } else {
    console.log('no')
  }
}

Return statement will exit the loop

The problem is that you return if(!args[0].includes(colors[i])). In a for loop a return stops execution and exits the function.

In your first example, the first item in your colors array is "red". Inside your for loop, you check if the value of args[0] ("black") is included in the string of "red". As this is false, you return That is not a valid skin and exit the for loop. You don’t even check the second item in your colors array ("blue").

In your second example, your message is "au/skin red". You start the for loop. The first item in your colors array is "red". Inside your for loop, you check if the value of args[0] ("red") is included in the string of "red". As this is true, you send Set your skin to red but there is no return so this time you don’t exit the loop. You check the second item in the colors array ("blue"). As this is not red, you return That is not a valid skin and exit the for loop. That’s why you have two messages sent in your second example.

Array#includes

What you could use is Array#includes. It checks if an array (colors) includes a certain value (args[0]) among its entries. You don’t even need a for loop.

const colors = [
  'red', 'blue', 'green', 'pink', 'orange', 'yellow', 'black', 'white', 'purple', 'brown', 'cyan', 'lime',
];
const args = ['white']

if (colors.includes(args[0])) {
  console.log(`Set your skin to ${args[0]}`);
} else {
  console.log('That is not a valid skin');
}

Working code

// if fetch returns null, skin is 'red'
const skin = db.fetch(`${message.author.id}.skin`) || 'red';
const skin2 = client.emojis.cache.get(e[skin]);
const colors = [
  'red', 'blue', 'green', 'pink', 'orange', 'yellow', 'black', 'white', 'purple', 'brown', 'cyan', 'lime',
];

const attachment = new Discord.MessageAttachment(
  `./assets/colors/${skin}.png`,
  `${skin}.png`,
);
const embed = new Discord.MessageEmbed()
  .attachFiles(attachment) // <- add attachment
  .setAuthor(
    `${message.author.tag}'s skins (${message.author.id})`,
    message.author.avatarURL(),
  )
  .setThumbnail(`attachment://${skin}.png`)
  .addField(`Current skin`, `${skin2}`)
  .addField(`Default skins`, colors.join(', '))
  .addField(`Unlocked skins`, `none`)
  .addField(`Locked skins`, `none`)
  .setDescription(`To set a skin use ${prefix}skin <skin>`)
  .setTimestamp()
  .setColor('#00ffff')
  .setFooter(message.member.user.tag, message.author.avatarURL());

if (!args[0])
  return message.channel.send(embed);

if (!colors.includes(args[0]))
  return message.channel.send(`That is not a valid skin`);

db.set(`${message.author.id}.skin`, args[0]);
message.channel.send(`Set your skin to ${args[0]}`);