Discord.js. Reaction Roles - discord

I'm trying to script a bot that gives people a role when they click on an emoji.
My problem is that people don't get the role when they click on the emoji. When I enter the first Command t!role the Message with the emoji is sent correctly. However, the people reacting will not receive the role. I tried everything that was suggested in the Comments. Unfortunatley nothing seems to work.
const bot = new Discord.Client( {
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MEMBERS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGE_REACTIONS,
],
partials: ["MESSAGE", "CHANNEL", "REACTION"]
});
else if(parts[0].toLowerCase() == "t!role") {
let sendMessage = await message.channel.send('React with 👮to get the role')
sendMessage.react('👮')
bot.on('messageReactionAdd', async (reaction, user, channel) => {
if(reaction.message.partial) await reaction.message.fetch();
if(reaction.partial) await reaction.fetch();
if(user.bot) return;
if(!reaction.message.guild) return;
if(reaction.message.channel.id === '8786607442820137464') {
if(reaction.emoji.id === '8789972642138767364') {
reaction.message.guild.members.cache.get(user.id).roles.add('8779841244749004804')
}
}
})

Discord had added a few new features in their developer portal a few months ago and most of the documentation and tutorials skip over that but it is necessary for them to be active in order to have a functional reaction roles feature.
The process to turn these on is to go to the application page of your bot, go to the bot page under the application and activate Presence Intent and Server Members Intent. Hopefully after this your reaction roles should start working.

Enable partials in your client initialization. Discord.js requires you now to add partials and intent flags you actually gonna use. The messageReactionAdd event requires some partials as well: REACTION, MESSAGE, CHANNEL.
For example, this is how you should use partials.
const bot = new Client({
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MEMBERS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGE_REACTIONS,
],
partials: ["MESSAGE", "CHANNEL", "REACTION"]
});
I have used the same exact code in your post, only changed a few id's on my side, and it's working as intended. As my comment before:
Bots can only give roles below their own, even if they have Administrator permissions. Make sure the role is below the bot's role. Use .catch((error) => console.log(error.message)); to log any errors if giving out the roles has failed for some reason.
bot.on('messageReactionAdd', async (reaction, user, channel) => {
if(reaction.message.partial) await reaction.message.fetch();
if(reaction.partial) await reaction.fetch();
if(user.bot) return;
if(!reaction.message.guild) return;
if(reaction.message.channel.id === '8786607442820137464') {
if(reaction.emoji.id === '8789972642138767364') {
reaction.message.guild.members.cache.get(user.id).roles.add('8779841244749004804')
}
}
});

Related

Discordjs 14.7.1 Skips First Reaction

Problem is the Discord bot is skipping the first reaction emoji.
If I un-react and re-react with the same emoji, then the bot registers it.
I've been trying to figure this out for a while, and am stumped as to what I am missing in my logic or if I need to find another solution.
Intention:
Discord bot gathers and prints out all reactions to a message by a user
Relevant Information:
Discordjs 14.7.1
Discord Developer Website:
Presence Intent: Enabled
Server Members Intent: Enabled
Message Content Intent: Enabled
Code:
const { Client, Events, GatewayIntentBits, Partials } = require('discord.js');
const { token } = require('./includes/config.json');
// create a new discord client
const client = new Client({
intents: [
GatewayIntentBits.DirectMessageReactions,
GatewayIntentBits.DirectMessageTyping,
GatewayIntentBits.DirectMessages,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.Guilds,
GatewayIntentBits.MessageContent,
],
partials: [
Partials.Channel,
Partials.GuildMember,
Partials.Message,
Partials.Reaction,
Partials.User,
]
});
// ready
client.once(Events.ClientReady, () => {
console.log('Ready!');
});
// if partial, fetch full
client.on(Events.MessageReactionAdd, async (reaction, user) => {
if (reaction.partial) {
try {
await reaction.fetch();
} catch (error) {
console.error(error);
return;
}
}
// define the message component
const message = reaction.message;
// all userReactions by user.id
const userReactions = await message.reactions.cache.filter(reaction => reaction.users.cache.has(user.id));
// print emojis
try {
for (const reaction of userReactions.values()) {
console.log(`Emoji: ${reaction.emoji}`);
}
} catch(error) {
console.error(error);
}
})
// login
client.login(token);
Problem:
The bot is not registering the first reaction placed
Example:
- I react to a message in this order: 😄❤️🔥
- the logfile shows:
Emoji: ❤️
Emoji: ❤️
Emoji: 🔥
Expected result:
Emoji: 😄
Emoji: 😄
Emoji: ❤️
Emoji: 😄
Emoji: ❤️
Emoji: 🔥
Observations:
I noticed that the bot works fine for all messages sent after it is running
The problematic behaviour is experienced only for messages sent before the bot was running
Conclusion:
MessageReactionAdd() does get called, as tested by placing 'console.log(test output); statements. I learned that it works on cached messages only, which I thought was mitigated by fetching the full reaction. Such was not the case. I tried testing by forcing a message fetch manually by channel.id and message.id, it still did not work.
My Solution:
Used an sqlite database to keep track of reactions.
From within MessageReactionAdd() I was able to check for the reactions I was interested in logging, and added them to an sqlite database.
Within an equivalent MessageReactionRemove() section, I was able to check for removal of the reactions and delete the pertinent row from the database.
Thank you for reading and assisting. :)

How can I DM the user an embed before he is kicked (discord.js)

I'm trying to make a blacklist command, and how do I make it so it sends an embed instead of normal message.
(Also, I'm new to discord.js)
Here's my script:
module.exports = {
data: new SlashCommandBuilder()
.setName('blacklist')
.setDescription('Blacklist someone from the server')
.addUserOption((option) => option.setName('member')
.setDescription('Who are you blacklisting?')),
async execute(interaction) {
await interaction.deferReply();
const member = interaction.options.getMember('member');
if (!interaction.member.roles.cache.some(r => r.name === "Blacklist perms")) {
return interaction.editReply({ content: 'You do not have permission to use this command!' });
}
member.send("You have been blacklisted!")
member.kick().catch(err => {
interaction.editReply({ content: `I do not have enough permissions to do that.`})
})
await interaction.editReply({ content: `${member} has been succesfully blacklisted!` });
},
};```
Since your function is already async, simply await sending the message before you call kick.
await member.send("You have been blacklisted!")
member.kick().catch(err => {
interaction.editReply({ content: 'I do not have enough permissions to do that.'})
})
You might also wanna catch() on the send method because users can have DMs from server members disabled or blocked your bot.
You also might consider banning the user instead, because this system would fail if your bot ever happens to go offline when a blacklisted user tries to join. Additionally, depending on how fast your bot reacts, the user may still have time to spam in channels or do something harmful before your bot is able to kick them.
To send an embed either use an EmbedBuilder or a plain JavaScript object with the correct embed structure.
Example:
member.send({ embeds:[{
title: "Title",
description: "Hello World!"
}] })

How to make a raw event run only once

I have a raw event:
this.on('raw', packet => {
if (!['MESSAGE_REACTION_ADD', 'MESSAGE_REACTION_REMOVE'].includes(packet.t)) return;
const channel = this.channels.cache.get(packet.d.channel_id);
if (channel.messages.cache.has(packet.d.message_id)) return;
channel.messages.fetch(packet.d.message_id).then(message => {
const emoji = packet.d.emoji.id ? `${packet.d.emoji.name}:${packet.d.emoji.id}` : packet.d.emoji.name;
const reaction = message.reactions.cache.get(emoji);
if (reaction) reaction.users.cache.set(packet.d.user_id, this.users.cache.get(packet.d.user_id));
if (packet.t === 'MESSAGE_REACTION_ADD') {
this.emit('messageReactionAdd', reaction, this.users.cache.get(packet.d.user_id));
}
if (packet.t === 'MESSAGE_REACTION_REMOVE') {
this.emit('messageReactionRemove', reaction, this.users.cache.get(packet.d.user_id));
}
});
});
This event spams continuously when one reaction is added, I want to make it so if you react it will run once. How can I do this?
You should not use the raw event past discord.js version 12. As there are some issues when your bot grows.
Instead use Partials as explained in the offical Discord.js Guide
const Discord = require('discord.js');
const client = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] });
client.on('messageReactionAdd', async (reaction, user) => {
// When we receive a reaction we check if the reaction is partial or not
if (reaction.partial) {
// If the message this reaction belongs to was removed the fetching might result in an API error, which we need to handle
try {
await reaction.fetch();
} catch (error) {
console.error('Something went wrong when fetching the message: ', error);
// Return as `reaction.message.author` may be undefined/null
return;
}
}
// Now the message has been cached and is fully available
console.log(`${reaction.message.author}'s message "${reaction.message.content}" gained a reaction!`);
// The reaction is now also fully available and the properties will be reflected accurately:
console.log(`${reaction.count} user(s) have given the same reaction to this message!`);
});
Source and more information: https://discordjs.guide/popular-topics/reactions.html#listening-for-reactions-on-old-messages

How can I add specific role to user who react to the message

I'm trying do create a bot that can add specific role to the user who react to the message with the emoji listed.
With the code below, I can check who reacted to the message and i can also check what emoji they react with, but when I am trying to add role to them, error pops up say user.addRole is not a function is there any way to solve this problem? Thanks a lot!
Code that create an embed message for user to react
let args = message.content.substring(PREFIX.length).split(" ");
if(message.content.toLowerCase() === '?roles' && message.author.id === 'adminId' && message.channel.id === 'channel id'){
const role = new Discord.MessageEmbed()
.setTitle("ROLES")
.setColor('#6a0dad')
.setDescription('🥕 - ROLE1\n⚔️ - ROLE2\n🧊 - ROLE3')
message.channel.send(role).then(re => {re.react('🥕'),re.react('⚔️'),re.react('🧊')});
message.awaitReactions().then(collected=>{
const reaction = collected.first();
})
}
Code that get the react user and trying to add role
const bot = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] });
bot.on('messageReactionAdd', async (reaction, user) => {
if (reaction.partial) {
try {
await reaction.fetch();
} catch (error) {
console.log('Something went wrong when fetching the message: ', error);
return;
}
}
if(reaction.message.id === 'id of that embed message sent'){
if(reaction.emoji.name === "🥕"){
//console.log('ROLE1');
user.addRole('id of role 1');
}
if(reaction.emoji.name === '⚔️')
//console.log('ROLE2');
user.addRole('id of role 2');
if(reaction.emoji.name === '🧊')
//console.log('ROLE3');
user.addRole('id of role 3');
}
});
Looks like you're trying to add a role to a User. When you should be adding the role to a GuildMember. As you can see here: messageReactionAdd returns a User. However Users don't have a .roles only GuildMembers do. However you have two ways you can get the GuildMember easily:
This way you have to make sure the message is from a TextChannel not a DMchannel.
if(reaction.message.type === "text") let member = reaction.message.member;
OR
This way allows the user to react to ANY message the bot has cached.
let member = bot.guilds.get('<id of the server>').members.get(user.id);
Then you do what #Syntle said: member.roles.resolve('<id of role>');
The choice of how to get the member is up to you.
user.addRole() needs to be replaced with member.roles.add.

Add a role to people that react with a certain emoji in a certain channel

I'm trying to make a discord.js bot for my server I made for people in my school. I'm trying to make a #classes channel and if you react to certain messages it gives you a role (which gives you access to the text channel for that class).
client.on('messageReactionAdd', (reaction, user) => {
console.log("client.on completed.")
if (message.channel.name === 'classes') {
console.log("if(message) completed.")
if (reaction.emoji.name === "reminder_ribbon") {
console.log("emoji test completed.")
const guildMember = reaction.message.guild.members.get(user.id);
const role = reaction.message.guild.roles.find(role => role.name === "FACS");
guildMember.addRole(role);
}
}
});
This is what I have tried so far, however, it does not give me/the other people reacted to it the role nor does it return an error message.
P.S. Also, how would I be able to make it so when they unreact it removes the role?
Edit: It seems it only gets reactions from cached messages/messages sent after bot startup. Also, message is not defined on the first if(message.channel.id) message.
Try to use the following code:
client.on('messageReactionAdd', (reaction, user) => {
if (reaction.message.channel.id === '552708152232116224') {
if (reaction.emoji.name === "reminder_ribbon") {
const guildMember = reaction.message.guild.members.get(user.id);
const role = reaction.message.guild.roles.get('552709290427940875');
guildMember.addRole(role);
}
}
});
First of all, reaction.users is an Object with all the users that reacted on the message, so you first have to define to which user you want to assign the role. I fixed this fetching the guildMember with user.id.
The second mistake was that you tried to assign a role ID to a guildMember although you first have to fetch the role and then assign the role Object to the guildMember.

Resources