Accessing collection of reactions after awaitReaction node.js - javascript

The code below works well, until I try to send the messages after either an X or check is selected. I get this instead https://i.gyazo.com/2b8c4cfcd047121df364218ef0e8d7e9.png
My understanding is that this collection is a map. I have tried various ways of accessing a map and all have been either undefined or [object object] etc... instead of the username of the person who sent the reaction that was collected. According to the docs, reaction.users is how I think the collections is accessed, but it isn't working for me...
I know it acknowledged who sent the reaction, because I added the line "message.reply('You accepted the challenge.')" right above the message.channel.send(`${reaction.users}..." line, and it does reply with that line immediately after I react and it does display the username correctly. I have tried using collection.get() like I've seen some people use, that doesn't work either... I cannot progress further for my bot without getting this to work.
I require the username, id, etc.. for the user who reacted, because their info is used to submit them into a mysql table that holds the stats for the game this challenge is for.
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`);
});
}
As I said above, instead of [object map] I want to get the username of the person who sent the reaction. I will also need to get the id for the user as well. Nothing I try has worked.
Thanks in advance for any help!

I think instead of reaction.user you'll need to use MessageReaction.users which will give a collection of all the users who reacted to the message. Then you can get the first user from the collection and get his/her name.
Some example code (this isn't tested but should get you in the right direction):
collector.on('collect', (reaction, reactionCollector) => {
console.log(`Collected ${reaction.emoji.name}`)
let firstUser = reaction.users.filter(user => !user.bot).first();
if (reaction.emoji.name === '✅') {
message.channel.send(`${firstUser.username} 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(`${firstUser.username} has **declined* the challenge! Please await 5 minutes to pass or for all challenged players to respond!`)
}
});

Related

How to make button count dynamic in discord.js or discord.py

I want to make a role store for an economic bot in discord, where you can buy and set roles, but I have 1 problem, I don’t know how to make dynamic buttons (an indefinite number) for buying, I want to do it like in the screenshot below
enter image description here
I tried to somehow solve this problem using the nextcord library (Python), but in the end these attempts were unsuccessful, I also started to study discord.js (JavaScript), but there is still not enough knowledge to solve the problem
Looks like you want a polling where users reaction are the votes in discordjs? here's the script for that, not sure about the buying logic.
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('message', async message => {
if (message.content === '!poll') {
// Create the poll message
const pollMessage = await message.channel.send('What is your favorite color?');
// Add the emoji reactions
await pollMessage.react('🔴');
await pollMessage.react('🟢');
await pollMessage.react('🔵');
// Create a collector to collect the user reactions (adjust voting time in milliseconds)
const filter = (reaction) => reaction.emoji.name === '🔴' || reaction.emoji.name === '🟢' || reaction.emoji.name === '🔵';
const collector = pollMessage.createReactionCollector(filter, { time: 15000 });
// Create a variable to store the results
let results = {'🔴': 0, '🟢': 0, '🔵': 0};
// Collect the reactions
collector.on('collect', (reaction, user) => {
results[reaction.emoji.name]++;
});
// Print the results when the collector stops (you can put the buying logic here)
collector.on('end', () => {
message.channel.send(`Results: \n🔴: ${results['🔴']} \n🟢: ${results['🟢']} \n🔵: ${results['🔵']}`);
});
}
});
client.login(YOUR_TOKEN);

discord.js v13 TypeError: Cannot read properties of undefined (reading 'presence')

I'm trying to make a joke command with my friends to ban people who are playing Roblox or if he has the Roblox game status. I've searched everything in the internet and in stack overflow if there's a solution but I can't find a single working one. Here's my code:
module.exports = async (client) => {
const guild = client.guilds.cache.get('877016354581004389');
if(guild){
setInterval(async () => {
if(!guild.me.permissions.has('ADMINISTRATOR')) return;
const channel = guild.channels.cache.get('877016354581004392');
if(!channel) return console.log('ANNOUNCEMENT CHANNEL NOT FOUND...')
let bannedAmount = 0;
await guild.members.fetch().then(members => {
members.forEach(member => {
for(const allMember of Object.keys(member)){
if(allMember.user.presence.activities){
const activity = allMember.user.presence.activities[0]
if(activity.name.toLowerCase() == "roblox"){
channel.send(`${allMember} got banned because he's playing ROBLOX!!`)
allMember.ban({ reason: `he played roblox lol`});
bannedAmount = bannedAmount + 1
break;
}
}
}
})
});
let members;
if(bannedAmount > 1) members = 'members';
let kid;
if(bannedAmount > 1) kid = 'kids';
if(bannedAmount <= 0 || null){
console.log(`No member banned for playing Roblox...`);
} else if(bannedAmount > 0) {
channel.send(`${bannedAmount} ${kid || 'kid'} got banned in this server for playing Roblox. Don't do it kids!`)
console.log(`${bannedAmount} ${members || 'member'} got banned for playing Roblox...`)
}
}, 20000);
}
}
I have no hate with the game Roblox guys just fooling around lol
PS: I have already enabled intents in both discord dev website and also in the index.js file and also had used <member>.presence.activities but it looks like only user has presence property.
Well, the problem is that GuildMember#presence returns or a Presence class OR undefined, because it's an optional property. What you do in your code is taking the presence property of a user who probably doesn't has any presence and that's why allMember.user.presence returns undefined and allMember.user.presence.activities[0] too. Also keep in mind that you take the User of a member, but presences go on GuildMembers and not on Users.
Debugging:
Just use allMember instead of allMember.user, so u take the presence of a GuildMember instead of a User, after this a Member who has a presence will return the presence data too.
Add a if statement that controls if a member has a presence or not, so you don't run in the same error again.
I don't know if you tought about this, but if a member has more than one presence and the first presence in the array isn't Roblox, but for example the second presence in the array is Roblox the member won't get banned.
I hope this helps you, if you have questions just contact me on Discord
~ Iliannnn#0001

discord.js Javascript string manipulation

so i am creating a bot with a kick command and would like to be able to add a reason for said action, i've heard from somewhere that i may have to do string manipulation. currently i have a standalone reason as shown in the code below:
client.on("message", (message) => {
// Ignore messages that aren't from a guild
if (!message.guild) return;
// If the message starts with ".kick"
if (message.content.startsWith(".kick")) {
// Assuming we mention someone in the message, this will return the user
const user = message.mentions.users.first();
// If we have a user mentioned
if (user) {
// Now we get the member from the user
const member = message.guild.member(user);
// If the member is in the server
if (member) {
member
.kick("Optional reason that will display in the audit logs")
.then(() => {
// lets the message author know we were able to kick the person
message.reply(`Successfully kicked ${user.tag}`);
})
.catch((err) => {
// An error happened
// This is generally due to the bot not being able to kick the member,
// either due to missing permissions or role hierarchy
message.reply(
"I was unable to kick the member (this could be due to missing permissions or role hierarchy"
);
// Log the error
console.error(err);
});
} else {
// The mentioned user isn't in this server
message.reply("That user isn't in this server!");
}
// Otherwise, if no user was mentioned
} else {
message.reply("You didn't mention the user to kick!");
}
}
});
Split message.content and slice the first 2 array elements, this will leave you with the elements that make up the reason. Join the remaining elements back to a string.
const user = message.mentions.users.first();
const reason = message.content.split(' ').slice(2).join(' ');
Here is something that could help:
const args = message.content.slice(1).split(" "); //1 is the prefix length
const command = args.shift();
//that works as a pretty good command structure
if(command === 'kick') {
const user = message.mentions.users.first();
args.shift();
const reason = args.join(" ");
user.kick(reason);
//really close to Elitezen's answer but you might have a very terrible problem
//if you mention a user inside the reason, depending on the users' id, the bot could kick
//the user in the reason instead!
}
Here's how you can take away that problem (with regex)
const userMention = message.content.match(/<#!?[0-9]+>/);
//you may have to do some more "escapes"
//this works since regex stops at the first one, unless you make it global
var userId = userMention.slice(2, userMention.length-1);
if(userId.startsWith("!")) userId = userId.slice(1);
const user = message.guild.members.cache.get(userId);
args.shift();
args.shift();
user.kick(args.join(" "))
.then(user => message.reply(user.username + " was kicked successfully"))
.catch(err => message.reply("An error occured: " + err.message))
I assume you want your full command to look something like
.kick #user Being hostile to other members
If you want to assume that everything in the command that isn't a mention or the ".kick" command is the reason, then to get the reason from that string, you can do some simple string manipulation to extract the command and mentions from the string, and leave everything else.
Never used the Discord API, but from what I've pieced from the documentation, this should work.
let reason = message.content.replaceAll(".kick", "")
message.mentions.forEach((mentionedUser) => reason.replaceAll("#" + mentionedUser.username, "")
// assume everything else left in `reason` is the sentence given by the user as a reason
if (member) {
member
.kick(reason)
.then(() => {
// lets the message author know we were able to kick the person
message.reply(`Successfully kicked ${user.tag}`);
})
}

Message Collector doesn't start when bot DMs user to collect response

So this is moretheless in relation to the previous question, which was answered (the previous one) The bot is no longer spitting out a lot of errors whenever somebody runs the command, and the bot successfully DMs the user the response prompt, but it seems as if the Message Collector isn't starting? The bot DMs the user, nothing spits out in Console, and that's it. You can respond to the bot all day, and it wont collect it and send it to the channel ID. Any pointers?
Here's the code I believe it may revolve around:
collector.on('collect', (message, col) => {
console.log("Collected message: " + message.content);
counter++; ```
And here is all of the code (just in case it actually doesn't revolve around that):
``` if(message.content.toLowerCase() === '&reserveupdate') {
message.author.send('**Thanks for updating us on the reserve. Please enter what you are taking from the reserve below this message:**');
let filter = m => !m.author.bot;
let counter = 0;
let collector = new discord.MessageCollector(message.author, m => m.author.id, filter);
let destination = client.channels.cache.get('my channel id');
collector.on('collect', (message, col) => {
console.log("Collected message: " + message.content);
counter++;
if(counter === 1) {
message.author.send("**Thanks for updating us on the reserve, it is highly appreciated.**");
collector.stop();
}
I think you might be wrong on how you created your message collector.
According to the docs, you should make it this way :
const filter = m => !m.author.bot;
// If you are in an async function :
const channel = await message.author.createDM();
// Paste code here
// Otherwise :
message.author.createDM.then(channel => {
// Paste code here
});
// Code to paste :
const collector = channel.createMessageCollector(filter, { max: 1, time: 60000 });
collector.on('collect', msg => {
// ...
});
Hope this will help you to solve your issue ! :)

Issues awaiting replies in public channel

The following code is resulting in no errors to the console. After I type the command, the first message.reply line executes properly, but the bot doesn't seem to acknowledge someone types 'accept' or 'deny'. Been messing with this for quite a long time. I've done commands like this in private messages and it works. But for some reason since this is in a public channel, it doesn't seem to work.
module.exports.run = async(bot, message, args) => {
//!endbrawl winner [username] loser [username]
let messageArray = message.content.split(" ")
let winner = messageArray[2]
let loser = messageArray[4]
message.reply(`${message.author} wants to close this brawl with ${winner} as the victor and ${loser} as the loser. \n ${winner}, do you accept the result? If yes, type 'accept'. If not, type 'deny'.`);
let winnerUser = message.mentions.users.first();
let filter = m => m.author.id == winnerUser.id;
message.channel.awaitMessages(filter, {
maxMatches: 1,
}).then(collected => {
if (message.author.bot) return;
if (collected.first().content === "accept") {
return message.reply(`${winner} has accepted the proposed result.`)
// put in code asking loser to agree with proposed result
} else if (collected.first().content === "deny") {
return message.reply(`${winner} has denied the proposed result.`)
} else {
return message.reply(`${winner}, your reply was invalid.`)
}
})
}
I have looked for ways to solve this, but most involve private messaging or what was told doesn't work for me. No errors in any of those attempts. It just seems like it isn't even looking at the replies.
Thanks for any and all help! It is greatly appreciated!
message.channel.awaitMessages(filter, {
max: 1,
}).then(...);
That max means that the maximun number of messages that will be processed will be 1: that means that if the next message is not sent by the author the bot will stop listening for new messages. The other message could even be your bot's reply, since you're not waiting for that to be finished before setting the awaitMessages.
Try using maxMatches instead of max.
message.channel.awaitMessages(filter, {
maxMatches: 1,
}).then(...);
Reference: MessageCollectorOptions
With that said, you want the winner to be able to send that message and so you'll need to change your filter function.
You'll first need to get the User object of the winner, then make the filter so that it checks for their id.
let winnerUser = message.mentions.users.first();
let filter = m => m.author.id == winnerUser.id;
You could also match the id from the plain mention, but I find it easier to use the object instead.

Categories

Resources