Discord.js for loop behaving oddly - javascript

I'm working on a kick command for a Discord bot in Discord.js. How it works is a previous function passes an array of user IDs to attempt to kick from the server. For some reason, the for loop is repeating more than it should and the i value doesn't seem to change sometimes.
Here's my code:
exports.kick = async (guild, targets, member, reason, bot) => {
var modRoleQuery = await exports.queryModRole(guild, member, bot);
var outputMessage = '';
if (modRoleQuery === false && !member.hasPermission('ADMINISTRATOR') && member != guild.me) return `You do not have permission to execute this command.`;
else if (targets.length === 0) return `Command usage: ${config.prefix}kick <#mentions/IDs> [reason]`;
else if (!guild.me.hasPermission('KICK_MEMBERS')) return `I require the \`Kick Members\` permission to execute this command.`;
else if (targets.length > 10) return `You may only kick 10 members at a time.`;
else if (reason.length > 1000) return `The kick reason must not exceed 1000 characters. Currently, it is ${reason.length}.`;
for (i = 0; i < targets.length; i++) {
console.log(`checking ${targets[i]}, ${i}`);
let targetMember = guild.member(targets[i]);
if (targetMember) {
if (targetMember.kickable) {
let targetMemberModRole = await exports.queryModRole(guild, targetMember, bot);
if ((targetMemberModRole || targetMember.hasPermission('ADMINISTRATOR')) && !member.hasPermission('ADMINISTRATOR')) {
outputMessage += `Unable to kick \`${targetMember.user.tag}\`: Only administrators can kick people with the moderator role and/or admin perms.\n`
} else {
await targetMember.user.send(`You were kicked from ${targetMember.guild.name} by ${member.user.tag}:\nReason: \`${reason}\``).catch(err => {});
await targetMember.kick(`[${member.user.tag}] ${reason}`);
exports.modLogEvent(bot, guild, `KICK`, targetMember.user, member.user, reason);
outputMessage += `Successfully kicked \`${targetMember.user.tag}\`\n`;
}
} else {
outputMessage += `Unable to kick \`${targetMember.user.tag}\`: I don't have permission to kick them.\n`;
}
} else {
outputMessage += `Unable to kick \`${targets[i]}\`: They don't seem to be in this server.\n`;
console.log(`${targets[i]} is not a member`)
}
}
outputMessage += `**Kick Reason:**\n\`${reason}\``;
return outputMessage;
}
When the size of the array is 1, this works as expected, however when it is more than 1, it doesn't. I've put in some console.log() functions for debugging purposes.
For some reason, the i value gets stuck on 1. I also ran it a bit earlier (this is before I added the console.log()s) and it behaved even more oddly:
I've been trying to debug this for over a day now and I'm not getting anywhere. Any assistance would be greatly appreciated.

Related

Discord.js v13 why is my tempmute command not working?

I've made a tempmute command for my discord bot and it almost works. It has quite a few foolproofing measures like preventing the bot from muting themselves, not working if the time isn't specified and such. I am using the npm ms package to deal with the mute duration (https://www.npmjs.com/package/ms). when instead of specifying an amount of time I type in gibberish it works as intended and replies with the correct message. The problem is that when I type in a 100% correct command it responds asthough I didn't specify the time correctly instead of muting the user for that amount of time. here's how it looks. Any ideas as to why that is?
My code is here:
const ms = require('ms');
const { Permissions, MessageActionRow, UserFlags } = require('discord.js');
module.exports = {
name: 'tempmute',
description: "Temporarily mutes a user",
execute(message, args)
{
const target = message.mentions.members.first();
let muteRole = message.guild.roles.cache.find(role => role.name === "muted");
if(message.member.permissions.has(Permissions.FLAGS.MODERATE_MEMBERS))
{
if(target)
{
let memberTarget = message.guild.members.cache.get(target.id);
if(target.id == 'myBotsID')
{
message.reply("I can't mute myself.")
}
else if(message.member == target)
{
message.reply("You can't mute yourself!")
}
else if(memberTarget.permissions.has(Permissions.FLAGS.MODERATE_MEMBERS))
{
message.channel.send(`<#${memberTarget.user.id}> has been muted for ${ms(ms(args[1]))}`);
}
else
{
if(!args[1])
{
return message.reply("Time not specified.")
}
else
{
let time = ms(args[1])
memberTarget.roles.add(muteRole.id);
try {
message.reply("<#" + memberTarget.user.id + ">" + "has been muted for " + ms(ms(args[1])).catch(console.error))
setTimeout(function () {
memberTarget.roles.remove(muteRole.id);
}, time);
} catch (err) {
message.reply("Can't transform that into milliseconds `"+args[1]+"`")
return
}
}
}
}
else
{
message.reply("You have to mention a valid member of this server.")
}
}
else
{
message.reply("You can't use that.")
}
}
}
Okay so I figured it out. Here's the problematic code:
try {
message.reply("<#" + memberTarget.user.id + ">" + "has been muted for " + ms(ms(args[1])).catch(console.error))
setTimeout(function () {
memberTarget.roles.remove(muteRole.id);
}, time);
} catch (err) {
message.reply("Can't transform that into milliseconds `"+args[1]+"`")
return
}
IDK why but the ".catch(console.error))" (which is a leftover code and shouldn't be there in the first place) caused it to behave differently aka treat everything as an incorrectly specified time and returned the corresponding message instead of muting the member for the specified amount of time. After removing that short part of code everything is working as intended.

Discord JS, how to not allow user to execute a command while answering another command's question, unless it's finished

So, I have these two (kind of messy) commands.
Here are the codes, though their content doesn't really matter (I guess so):
This one is a find the hidden ball inside the boxes game.
client.on("message", async message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
const { MessageCollector } = require("discord.js-collector");
function randomIntFromInterval(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
const rndInt = randomIntFromInterval(1, 5)
console.log(rndInt)
let answerBox = "";
if (command === "b" || command === "box") {
const user = message.author.id
switch(rndInt) {
case 1:
answerBox = 1;
break;
case 2:
answerBox = 2;
break;
case 3:
answerBox = 3;
break;
case 4:
answerBox = 1;
break;
case 5:
answerBox = 2;
break;
default:
message.channel.send("ERROR: If you received this message, please contact the support.");
};
message.channel.send(`A ball is hidden under the boxes below!!
:gift: ── :one:
:gift: ── :two:
:gift: ── :three:`);
const botMessage = await message.channel.send(`<#${user}>, Type how many coins you wanna bet.`);
const userMessage = await MessageCollector.asyncQuestion({
botMessage,
user: message.author.id,
});
let coinsBet = userMessage.content;
if (coinsTotal < coinsBet) {
await message.channel.send("You don't have enough coins to bet.");
} else if (isNaN(coinsBet) == false) {
const botMessage = await message.channel.send("Type which box do you think the ball is (1, 2 or 3).");
const userMessage = await MessageCollector.asyncQuestion({
botMessage,
user: message.author.id
});
let bet = userMessage.content;
if (userMessage.content == answerBox) {
await message.reply("You got it right and won double the coins!");
coinsTotal += coinsBet * 2;
} else if (isNaN(bet) == true || bet >= 4) {
await message.channel.send("That wasn't a valid input, please type =b ou =box.");
} else {
await message.reply(`You got it wrong and lost all the coins which you bet. The right answer was ${answerBox}.`);
coinsTotal -= coinsBet;
}
} else {
await message.channel.send("That wasn't a valid input, please type =b ou =box.");
}
};
});
And this other one opens up a store:
client.on("message", (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if (command === "store") {
message.channel.send(`╭⋟────────────────────────╮
──•~ Type the product code (number) to buy it.
✦✧✦✧ STORE
» 1 - Green Seed: 0 coins.
╰────────────────────────⋞`)
message.channel.awaitMessages(m => m.author.id == message.author.id,
{max: 1, time: 10000}).then(collected => {
if (collected.first().content.toLowerCase() == '1') {
message.reply(`You bought 1 "Green seed" with success!`);
seedGreen++;
seedTotal++;
}
else
message.reply(`Something happened, and it wasn't possible to complete your order..`);
}).catch(() => {
message.reply('Your order was cancelled due to lack of response.');
});
};
});
Notice how none of that is actually connected to a database, but that's not the point. Thing is, everything is actually working without any issues, except one thing.
In the store code, if an user type "=b" or "=box" instead of "1" or another wrong answer, the bot starts the box game, and whenever an user answers the box game questions with "=store", it also starts the store code.
I tried to figure out how to prevent this from happening, once I created a "let isActive = false" variable on top of the code, and set it to true whenever the bot started the store or box code, and set to false when it was finished, so I could put (&& isActive === false) at the first if of those codes and let them execute only if they are "inactive" (not doing any event I coded).
But it was all in vain, it didn't really work, so I'm pretty much stuck here, and I would gladly accept any kind of help to overcome this.
There are many ways to solve this issue. Here's one that I thought out: At the beginning of the script, you can provide some sort of state prop to the client. I called it userState and set it to a Collection:
client.userState = new Discord.Collection();
Then whenever a user starts a multi-step command that you don't want them to execute while still in the command, you can set that user's id to some descriptive state, e.g. "boxGame":
client.userState.set(message.author.id, "boxGame");
And whenever you need to check if they're in a multi-step command, you can check with:
client.userState.get(message.author.id);
Which will either return "boxGame" or undefined, and you can easily differentiate between them to stop them or let them execute a command, for example, using a switch statement:
if (client.userState.has(message.author.id)) {
const state = client.userState.get(message.author.id);
switch (state) {
case "boxGame":
message.channel.send("you have to complete the box game first bro");
break;
default:
message.channel.send("You're doing something, finish that something first!");
}
}
And when they complete the box game, you can
client.userState.delete(message.author.id);
to clear their state.
Edit: Just reminding you again that this is only one solution on how to solve it, and there are probably better solutions to this than mine.

Giving multiple prompts over DM's

So I need a command to send 4 different messages to the user, each message with a new prompt, so for example "Prompt 1" and what ever the user responds will be pushed into an array called "config". I thought about using message collectors, but couldn't set it up to collect multiple answers.
Pseudo code:
let config = new Array();
message.author.send("Prompt 1");
config.push(collected.answer).then(
message.author.send("Prompt 2");
config.push(collected.answer).then(
ect...
)
You CAN use message collectors. However, you need to have it in a variable. Here is an example:
msg.author.send('some prompt').then(m => {
let i = 0;
var collector = m.channel.createMessageCollector(me =>
me.author.id === msg.author.id && me.channel === m.channel, {max: /*some number*/})
collector.on('collect', collected => {
if(collected.content === 'end') return collector.stop();
//basically if you want to stop all the prompts and do nothing
i +=1
if(i === 1) return collected.channel.send(/*something*/); //next prompt
if(i === 2) return collected.channel.send(/*something*/); //and so on until you get to the last prompt
})
collector.on('end', collectedMsgs => {
if(collectedMsgs.size < /*amount of prompts*/) {
return collectedMsgs.first().channel.send('Ended early, nothing was done.');
}
//some action you would do after all are finished
})
})
There may be some missing parentheses, you will have to add them.

Discord Giveaway bot always gives the bot as winner

The bot should return that "no one entered" when no one enters the giveaway, but even if that's the case, it still says "#Dbot winner". Am I missing something in the code?
let msg = await message.channel.send(
"**🥳GIVEAWAY🥳**",
{
embed: {
title: `${(args.slice(2, args.length)).join(" ")}`,
color: 3447003,
description: `React with 🎉 to enter!\nTime to enter: ${args[0]}\nWinners: ${args[1]}`,
footer: {
text: `dbot 2021 © giveaway ends ${endTime.toLocaleTimeString()}`
}
}
});
await msg.react('🎉')
setTimeout(() => {
msg.reactions.cache.get('🎉').users.remove(msg.author.id)
setTimeout(() => {
let winner = msg.reactions.cache.get('🎉').users.cache.random();
if (msg.reactions.cache.get('🎉').users.cache.size < 1) {
msg.channel.send(`No one entered giveaway :sadge:`);
}
if (!msg.reactions.cache.get('🎉').users.cache.size < 1) {
console.log(winner);
msg.channel.send(`${winner} is our winner!🎉🎉🎉 check 1`);
}
}, 3000);
}, timerMilliseconds);
It's because you first check if users.cache.size is less than 1, then if (!msg.reactions.cache.get('🎉').users.cache.size < 1).
users.cache.size will return a number and in JavaScript !number will return a boolean. Check the snippet below:
const num0 = 0;
const num1 = 1;
const num2 = 2;
console.log(num0, !num0);
console.log(num1, !num1);
console.log(num2, !num2);
This means that with your second if statement, you're checking if false is less than 1, which is true in JavaScript.
To fix this, you simply need to convert the second if to an else statement:
if (msg.reactions.cache.get('🎉').users.cache.size < 1) {
msg.channel.send(`No one entered giveaway :sadge:`);
} else {
console.log(winner);
msg.channel.send(`${winner} is our winner!🎉🎉🎉 check 1`);
}
You need to enable the MEMBERS intent on the Discord Developer Console and add that intent to your ClientOptions.

Reason after blacklisting command Discord.js

I want to add a reason to my blacklists (with the command !blacklist {userid} {reason}) which are visible in the embeds below like .addField ("💬 Reason:", somecode) how can I fix this?
if (command === "blacklist") {
if(!config["allowed-users"].includes(message.member.id)) return;
const user = client.users.cache.get(args[0]);
if(!user) {
return message.channel.send("This user does not exist")
}
if(blacklist.has(user.id)) {
return message.channel.send("This user is already on the blacklist")
}
blacklist.set(user.id, 'blacklisted');
let set = db.fetch(`g_${message.guild.id}`);
var embed = new Discord.MessageEmbed()
.setTitle(":warning: Blacklisted :warning:")
.setColor('#fc5a03')
.addField("👮 Moderator:", message.author.tag)
.addField("👤 User:", user.username)
.addField("🆔 User ID:", user.id)
.addField("🕒 Blacklisted on:", message.createdAt)
.setFooter("© 2020 - 2021 GlobalChat", "https://cdn.discordapp.com/avatars/759021875962576916/cc32b2b08fdd52ae86294516d34532c5.png?size=128")
.setThumbnail(user.avatarURL({ dynamic:true }))
.addField("Unblacklist?", "Please contact <#267818548431290369> or <#331736522782932993>");
client.guilds.cache.forEach(g => {
try {
client.channels.cache.get(db.fetch(`g_${g.id}`)).send(embed);
} catch (e) {
return;
}
});
}
First you'll want to check if there is no reason, this can be simple done by checking, for both approaches, if the second argument is undefined, like so
if (args[1] === undefined) {
const reason = "No reason.";
}
This solution will work for both approaches, since if the second argument is undefined there can be no more after it
You could take reason as an argument.
Inside the command add
const reason = args[1];
OR if you wanted to have the rest of the blacklist args dedicated to the reason you could add something along the lines of
let reason = ""
for (let i = 1; i < args.length; i++) {
// It's very important that i starts as 1, so we do not take the first argument into account for the reason
reason += args[i];
}
And then you can add to the embed
.addField("💬 Reason:", reason);
If you went with the first approach, the blacklist command would work like this
!blacklist 012345678910111213 the_reason_here
// or
!blacklist 012345678910111213 reason
The limitation to this approach is that a multi word reason isn't very intuitive.
If you went with the second approach though, the blacklist command would work like this
!blacklist 012345678910111213 The reason the user was banned and it can go on and on and on as long as the writer wants
You'll want to fetch the reason in the same way that you fetched the user id, like this:
const reason = args[1];
After that, in order to make sure that the reason doesn't show as undefined, you'll want to add a check in the form of an if statement, like this:
if (!reason) {
reason = "No reason";
}
After that, add .addField("💬 Reason:", reason) in the position of fields you want it to be.
Your code should look something like this:
if (command === "blacklist") {
if (!config["allowed-users"].includes(message.member.id)) return;
const user = client.users.cache.get(args[0]);
const reason = args[1];
if (!user) {
return message.channel.send("This user does not exist")
}
if (blacklist.has(user.id)) {
return message.channel.send("This user is already on the blacklist")
}
if (!reason) {
reason = "No reason";
}
blacklist.set(user.id, 'blacklisted');
let set = db.fetch(`g_${message.guild.id}`);
var embed = new Discord.MessageEmbed()
.setTitle(":warning: Blacklisted :warning:")
.setColor('#fc5a03')
.addField("👮 Moderator:", message.author.tag)
.addField("👤 User:", user.username)
.addField("🆔 User ID:", user.id)
.addField("🕒 Blacklisted on:", message.createdAt)
.addField(("💬 Reason:", reason)
.setFooter("© 2020 - 2021 GlobalChat", "https://cdn.discordapp.com/avatars/759021875962576916/cc32b2b08fdd52ae86294516d34532c5.png?size=128")
.setThumbnail(user.avatarURL({
dynamic: true
}))
.addField("Unblacklist?", "Please contact <#267818548431290369> or <#331736522782932993>");
client.guilds.cache.forEach(g => {
try {
client.channels.cache.get(db.fetch(`g_${g.id}`)).send(embed);
} catch (e) {
return;
}
});
}

Categories

Resources