Discord.js: only one reaction - node.js

I'm creating Discord bot which gives a specific role when users add a specific emoji reaction on a message.
I want to let users choose only one reaction;
or
if the user choose another one, other reactions of the same user should be removed.
I found code only for two situation:
Remove all emoji reactions on a message.
message.reactions.cache.get('emojiId').remove()
Remove the reaction from the user.
const userReactions = message.reactions.cache.filter(reaction => reaction.users.cache.has(userId));
try {
for (const reaction of userReactions.values()) {
await reaction.users.remove(userId);
}
} catch (error) {
console.error('Failed to remove reactions.');
}
Both of them doesn't suit me because they delete all reactiosn to the message.
A part of my code:
client.on('messageReactionAdd', async (reaction, user) => {
if(reaction.partial) await reaction.fetch()
const { message } = reaction
if (message.id === rulesMessageId) {
const member = message.channel.guild.members.cache.get(user.id)
if (reaction.emoji.id === emojiIds.HEAVEN ) {
for (var i = 0 in guildRoles) {
if (guildRoles[i] != guildRoles.HEAVEN)
member.roles.remove(guildRoles[i])
}
member.roles.add(guildRoles.HEAVEN)
}

I don't know how to use a part of your code in the answer so I have do my own :
client.on("messageReactionAdd", (reaction, Member) => {
if(Member.bot) return;
reaction.message.reactions.cache.map(x=>{
if(x._emoji.name != reaction._emoji.name&&x.users.cache.has(Member.id)) x.users.remove(Member.id)
})
});
I get all reaction in the message and map them to check if the user have react. I also check if the reaction is the same as the one currently reacting to remove only the others.
Hope it will help you even if it's 10 months too late ^^

Related

Ban user if they mention over 5 people

Heyo Axmyo here,
I want to autoban user if they mention over 5 humans, not bots
but the code I tried to use doesn't do anything..
Any help would be appreciated!
client.on("message", message => {
if(message.mentions.members.size > 5) {
message.author.ban();
}
})
You're in the right direction, your if statement is correct.
message.author returns a User.
but .ban() is available only on GuildMember.
So you need to convert the User to a GuildMember. Luckily the message object contains the Guild that it was sent in, so you can do the following:
client.on("message", message => {
if(message.mentions.members.size > 5) {
const user = message.author;
const guildMember = message.guild.member(user);
guildMember.ban();
}
})
Notice that .ban() returns a promise, so it might be a good idea to do the following, if you want to do additional things after the ban:
client.on("message", async message => {
if(message.mentions.members.size > 5) {
const user = message.author;
const guildMember = message.guild.member(user);
await guildMember.ban();
// Do other things...
}
})
Edit
message object has a member property on it, thanks Itamar S
client.on("message", async message => {
if(message.mentions.members.size <= 5) return;
await message.member.ban();
// Do other things...
})

How to get the discord id of users who have reacted to a specific message discord.js

I would like to grab the id of users who have reacted to a specific message and push them to the variable players, I've console.log() multiple things searching for a solution, used google, and asked friends, which was not successful. I hope that I can find someone with enough experience to help me find an answer or another question with an answer to this small problem I have.
let players = [message.author.id]
message.react('👌')
const filter = (reaction, user) => reaction.emoji.name === '👌'
message.awaitReactions(filter, { time: 20000 })
.then(collected => {
console.log(collected.user.id)
players.push(collected.user.id)
})
.catch(console.error);
awaitReactions returns a Collection of MessageReactions. Therefore collected.user will be undefined.
You'll have to get the first MessageReaction from the Collection and access its users property, filtering the bot's ID out.
// Creating a filter.
const Filter = (reaction, user) => reaction.emoji.name === "👌";
// Reacting to the message.
await message.react("👌");
// Awaiting reactions.
message.awaitReactions(Filter, { time: 6000 }).then((collected) => {
const reaction = collected.first();
// Making sure that at least one user reacted to the message
if (reaction) {
const players = reaction.users.cache.filter((user) => user.id !== client.user.id);
console.log(`Users that reacted: ${players.map((user) => user.username).join(", ")}`);
} else {
console.log("No one reacted to this message.");
}
});
If you are getting an error such as The 'await' operator can only be used within an async method. you'll have to make your method async.
Example:
client.on("message", async (message) => {
// Code
});
Check this documentation: https://discordjs.guide/popular-topics/reactions.html#reacting-in-order
const userReactions = message.reactions.cache.filter(reaction => reaction.users.cache.has(userId));
try {
for (const reaction of userReactions.values()) {
await reaction.users.remove(userId);
}
} catch (error) {
console.error('Failed to remove reactions.');
}
You can use this example but instead of removing the userId you can do something along the lines of:
const userReactions = message.reactions.cache.filter(reaction => reaction.users.cache.has(userId));
try {
players.push(userId)
} catch (error) {
console.error('Failed to push reaction');
}
Try this:
const filter = (reaction, user) => reaction.emoji.name === '👌'
message.awaitReactions(filter, { time: 20000 })
.then(collected => {
collected.first().users.cache.forEach(u => players.push(u.id))
})
.catch(console.error);
I couldn’t test it. Please tell me any errors you may have.

filter reactions from specific users discord.js

I am making a bot where players can challenge each other. After posting the challenge command, which involves listing the players they want to issue a challenge to, I have my bot get the discord ID's for each of those users.
Then I have the bot post in a challenges channel, listing the players and await the reactions from only those players that were challenged. I made a reaction collector to collect those reactions. I want to filter those reactions to only include the ones that were challenged to avoid random players from hijacking a challenge.
In the code below, uniquePlayersIDArray is an array of discord user ID's. I just want the bot to only collect reactions from users that are in that array.
I have tried using uniquePlayersIDArray.indexOf(user.id) to detect if the user is in the array. As shown below I've tried uniquePlayersIDArray.includes(user.id).
async function acceptQuestion() {
const agree = "✅"
const disagree = "❌"
let msg = await message.channel.send(`A challenge has been issued by ${author}?\n\n**IF** you are listed above, please select one of the following to accept or deny the challenge...`)
await msg.react(agree)
await msg.react(disagree)
const filter = (reaction, user) => {
return ['✅', '❌'].includes(reaction.emoji.name) && uniquePlayersIDArray.includes(user.id);
};
const collector = await msg.createReactionCollector(filter, { time: 15000 });
collector.on('collect', (reaction, reactionCollector) => {
console.log(`Collected ${reaction.emoji.name}`)
if (reaction.emoji.name === '✅') {
message.channel.send(`${reaction.user} has **accepted** the challenge! Please await 5 minutes to pass or for all challenged players to respond!`)
} else if (reaction.emoji.name === '❌') {
message.channel.send(`${reaction.user} has **declined* the challenge! Please await 5 minutes to pass or for all challenged players to respond!`)
}
});
collector.on('end', collected => {
console.log(`Collected ${collected.size} items`);
});
}
I know this works without adding anything after ".includes(reaction.emoji.name)" in the filter statement, but it picks up all reactions then.
I just want all reactions from people that were not challenged to be ignored.
Thanks for the help in advance.
You can add a MessageCollector to check if the author of the executor is the one reacting and use message.awaitReactions instead of createReactionCollector
message.awaitReactions(filter, { max: 1, time: 500000, errors: ['time'] })
.then(collected => {
const reaction = collected.first();
if (reaction.emoji.name === '✅') {
msg.channel.send(logSetup);
const collector = new Discord.MessageCollector(msg.channel, m => m.author.id === msg.author.id, { time: 500000 });
console.log(collector)
collector.on('collect', message => {
message.channel.send("You clicked ✅");
})
} else {
msg.reply('You clicked ❌');
}
})

New to discord.js, bot reads wrong text to verify a particular user when he joins my server

So im tryna make a little bot that verifies a particular group of users to access a reserved channel. The bot is supposed to ask a secret code, and when the answer is withing the question, the bot replies to itself and an error message is displayed. How do i fix this code?
I've tried using
if(message.author.bot){return;}
but it doesnt work for some reason welp
this is my code:
client.on('guildMemberAdd', member => {
const channel = member.guild.channels.find(ch => ch.name === 'vaayil');
channel.send("type in the secret code");
client.on('message', message =>{
if(message.author.bot){return;}
if(message.content === `secret`){
channel.send("verified");
}
if(message.content !== `secret`){
channel.send("not verified");
}
});
});
Nesting events like that is generally bad practice.
However, you can avoid doing so, and make your life coding this bot easier. In Discord.js, you can use TextChannel.awaitMessages() to wait for messages that pass a certain filter, and then do something with those messages.
Example:
const secretCode = 'sloths';
client.on('guildMemberAdd', async member => {
const channel = member.guild.channels.find(c => c.name === 'vaayil');
if (!channel) return console.log('Can\'t find channel.');
try {
await channel.send(`What\'s the secret code, ${member}?`);
const filter = m => m.author.id === member.id;
const messages = await channel.awaitMessages(filter, { max: 1 });
const message = messages.first(); // (The only message in the Collection)
if (message.content === secretCode) {
await channel.send('That\'s right!');
// Do something...
} else {
await channel.send('Nice try, but wrong.');
// Do something...
} catch(err) {
console.error(err);
}
});

Populate unpopulated reaction.users

On a messageReactionAdd event, one of the parameters is a MessageReaction. From that reaction, I do message.reaction to find the message that the reaction is from. I want to use that message to find all the reactions that the parameters user has reacted to on that message.
However, one obstacle is that when the bot restarts, it seems as though the message.reactions is not fully populated, and the data needs to be fetched. I need the users of a looped reaction, but I'm struggling on that part.
I have tried:
await message.reactions.reduce((p, c) => c.fetchUsers(), 0);
// I had thought this would of cached the users of every reaction on the message, but that did not work.
// next
message.reactions.forEach((x, y) => {
x.fetchUsers();
});
// this was also the same train of thought, but still to no avail.
What I mean by that users are not in the message.reaction.users object, I mean this:
// the bot restarts - and a reaction is added
console.log(reaction.users);
// outputs: Collection [Map] {}
// this is supposed to be populated with many users, but it's empty
// however, if I add ANY reaction again, not doing anything else
// it outputs a Collection with many users in it, which is expected
I have no idea how to do this.
Edit: relevant code
// raw.js (the raw event)
const events = {
MESSAGE_REACTION_ADD: 'messageReactionAdd',
};
const Discord = require('discord.js');
module.exports = async (client, event) => {
if (!events.hasOwnProperty(event.t)) return;
const { d: data } = event;
const user = client.users.get(data.user_id);
if (!user) return;
const channel = client.channels.get(data.channel_id);
if (!channel) return;
if (channel.messages.has(data.message_id)) return;
const message = await channel.fetchMessage(data.message_id);
const emojiKey = (data.emoji.id) ? `${data.emoji.name}:${data.emoji.id}` : data.emoji.name;
let reaction = message.reactions.get(emojiKey);
if (!reaction) {
const emoji = new Discord.Emoji(client.guilds.get(data.guild_id), data.emoji);
reaction = new Discord.MessageReaction(message, emoji, 1, data.user_id === client.user.id);
}
client.emit(events[event.t], reaction, user);
};
// messageReactionAdd.js (the messageReactionAdd event)
module.exports = async (client, reaction, user) => {
const message = reaction.message;
if (!message)
return;
//await message.reactions.reduce((p, c) => c.fetchUsers(), 0);
message.reactions.forEach((x,y) => {
x.fetchUsers();
});
reactions = await message.reactions.filter(r => r.users.has(`${user.id}`));
console.log(reactions);
};
This code is what fixed this problem for me.
// raw.js event file
await reaction.message.reactions.forEach(r => {
r.fetchUsers({before: `${reaction.message.author.id}`});
});
// emit the messageReactionAdd event
// messageReactionAdd.js event file
// the message.reactions.users should be populated, which I can use
reactions = await reaction.message.reactions.filter(r => r.users.has(`${reaction.message.author.id}`));
I had used this code in the wrong events which made no logical sense which made the results that I had thought was invalid.

Resources