I've been trying to create a Discord bot. A lot of interactions are done through reactions, and I've been aware of the fact that only cached messages triggered the messageReactionAdd event. So I picked the following piece of code that is supposed to emit the packets corresponding to reactions added to "old" (not cached) messages. But it seems to completely block any packets concerning reactions adding because now none is emitted. Is there something that I've been doing wrong ?
Thanks.
My "raw.js" file :
module.exports = {
run: (client, packet) => {
// We don't want this to run on unrelated packets
if (!['MESSAGE_REACTION_ADD', 'MESSAGE_REACTION_REMOVE'].includes(packet.t)) return;
// Grab the channel to check the message from
const channel = client.guilds.cache.get(packet.d.guild_id).channels.cache.get(packet.d.channel_id);
const messageID = packet.d.message_id;
// There's no need to emit if the message is cached, because the event will fire anyway for that
if (channel.messages.has(messageID)) return;
// Since we have confirmed the message is not cached, let's fetch it
channel.messages.fetch(messageID).then(message => {
// Emojis can have identifiers of name:id format, so we have to account for that case as well
const emoji = packet.d.emoji.id ? `${packet.d.emoji.name}:${packet.d.emoji.id}` : packet.d.emoji.name;
// This gives us the reaction we need to emit the event properly, in top of the message object
const reaction = message.reactions.get(emoji);
// Adds the currently reacting user to the reaction's users collection.
if (reaction) reaction.users.set(packet.d.user_id, client.users.get(packet.d.user_id));
// Check which type of event it is before emitting
if (packet.t === 'MESSAGE_REACTION_ADD') {
client.emit('messageReactionAdd', reaction, client.users.get(packet.d.user_id));
}
if (packet.t === 'MESSAGE_REACTION_REMOVE') {
client.emit('messageReactionRemove', reaction, client.users.get(packet.d.user_id));
}
});
}
};
My "event fetcher" :
const fs = require('fs')
fs.readdir('./events/', (err, files) => {
if (err) return console.error(err);
files.forEach((file) => {
const eventFunction = require(`./events/${file}`);
if (eventFunction.disabled) return;
const event = eventFunction.event || file.split('.')[0];
const emitter = (typeof eventFunction.emitter === 'string' ? client[eventFunction.emitter] : eventFunction.emitter) || client;
const { once } = eventFunction;
try {
emitter[once ? 'once' : 'on'](event, (...args) => eventFunction.run(client, ...args));
} catch (error) {
console.error(error.stack);
}
});
});
From the discord.js discord server's #faq channel:
Bug:
• 'messageReactionAdd' does not fire despite proper partials enabled
PR: https://github.com/discordjs/discord.js/pull/4969 (merged, waiting for release)
Temporary fix is to ensure the addition of GUILD_MEMBER partial in the client definition
new Discord.Client({
partials: ['USER', 'GUILD_MEMBER', 'CHANNEL', 'MESSAGE', 'REACTION'],
});
Related
I try to do a anti link bot discord that only who have the VIEW_AUDIT_LOG permission can send link this is my code :
client.on("messageCreate", message => {
const { member } = message;
let blacklisted = ['http://', 'www.', 'https://'];
let foundInText = false;
if(!member.permissions.has(Permissions.FLAGS.VIEW_AUDIT_LOG)) {
for (var i in blacklisted) {
if (message.content.toLowerCase().includes(blacklisted[i].toLowerCase())) foundInText = true;
}
if (foundInText) {
const logembed = new MessageEmbed()
.setColor("DARK_AQUA")
.addField(' with message :', ` ${message.content} `, true)
if(!message.member.roles.cache.has("929434049011941386")) {
message.guild.channels.cache.get('941704496185221221').send({ content: `<#${message.author.id}> tried to send links in <#${message.channel.id}>`, embeds: [logembed] });;
message.delete();
message.channel.send("No links here, " + `${message.author}`);
}
}
}
});
but it give this error when he delete a message with link :
if(!member.permissions.has(Permissions.FLAGS.VIEW_AUDIT_LOG)) {
^
TypeError: Cannot read properties of null (reading 'permissions')
ok so here's my guess....you're hitting this event handler twice or more times...the first time is the message the user typed. you will get their member info, if you console.log(message.member). The second time through, you'll be getting another event, which is the message(s) you send as a handler.
So the easy solution is to add something like this at or near the top of this event handler:
if (message.author.bot) return;
this basically tests to see if it's a bot or not that's calling your event. It's also good hygiene to ensure you're not accepting/reacting to other bots, which could result in spam you likely don't want.
I am working on trying to filter events for only 1 server rather than all servers the bot is in, but I'm trying to figure out how to exactly save each guild the bot is in as an collection so I can just filter events based on a guild ID I desire. Is this even possible? This is a snippet of the code I have to date, it's able to display the server names and ID's the bot is currently in, the events are working properly but triggering for all servers rather than the one I desire, how would I go about filtering for one guild collection?
const Discord = require('discord.js')
const bot = new Discord.Client()
const config = require('./config.json')
bot.on('ready', () => {
console.log(`Logged in as ${bot.user.tag}!`);
//Sets Activity
//client.user.setStatus('invisible')
bot.user.setActivity("Discord Cooks", { type: "WATCHING"})
console.log("Set User Activity!");
//Online Webhook
const webhookClient = new Discord.WebhookClient('','');
const embed = new Discord.MessageEmbed()
.setTitle(`${bot.user.tag} is online`)
.setColor('#FFFF00')
.setTimestamp()
webhookClient.send(embed);
bot.guilds.cache.forEach((guild) => {
console.log(guild.name, guild.id);
});
});
bot.on("channelCreate", (channel) => {
console.log(`channelCreate: ID: ${channel.id} Name: ${channel.name}`);
});
bot.on("channelUpdate", (oldChannel, newChannel) => {
console.log(`channelUpdate -> ${oldChannel.name} to ${newChannel.name}`);
});
bot.on("channelDelete", (channel) => {
console.log(`channelDelete: ID: ${channel.id} Name: ${channel.name}`);
});
bot.login(config.bottoken)
You can only execute code if event happened on exact server in much easier way:
if(guild.id == "GUILD_ID") {
// code
}
Also as #MrMythical said, you can just use if (message.guild.id !== "GUILD_ID") return; if you only need to run your code for 1 guild!
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
The current code is not logging to the console when a reaction is added to a message.
I know the messageReactionAdd event is triggered by calling the event separately underneath, however I am not sure why this is not logging.
// This makes the events used a bit more readable
const events = {
ADD: 'messageReactionAdd',
REMOVE: 'messageReactionRemove',
};
// This event handles adding/removing users from the role(s) they chose
client.on('raw', async event => {
if (!events.hasOwnProperty(event.t)) return;
const { d: data } = event;
const user = client.users.get(data.user_id);
const channel = client.channels.get(data.channel_id);
const message = await channel.fetchMessage(data.message_id);
const member = message.guild.members.get(user.id);
const emojiKey = (data.emoji.id) ? `${data.emoji.name}:${data.emoji.id}` : data.emoji.name;
let reaction = message.reactions.get(emojiKey);
if (!reaction) {
// Create an object that can be passed through the event like normal
const emoji = new Emoji(client.guilds.get(data.guild_id), data.emoji);
reaction = new MessageReaction(message, emoji, 1, data.user_id === client.user.id);
}
if(event.t === 'ADD') {
console.log("Reaction: Added")
}
});
There is no errors, console completely blank.
For what it seems, you are using if (!events.hasOwnProperty(event.t)) return; to check if event.t is equal to any of them, but when someome react in a message event.t returns MESSAGE_REACTION_ADD. So it's coming back from the beginning.
"Just change the ADD property to fix IT"
But, in if(event.t === 'ADD') you check if event.t is equal to ADD, So this will not execute, since event.p returns MESSAGE_REACTION_ADD
"Just change the if(event.t === 'ADD') to if(event.p === 'MESSAGE_REACTION_ADD) {
Make this to fix your code.
i have a problem with the message reaction, i made the bot to delete any message sent on channel names appeal and send it to another channel names the appeals and react to the message with :white_check_mark: and if someone reacted to the message with the :white_check_mark:, the bot will automaticly delete the bot,
thats working but there is a problem, if i restarting the bot and reacting to the message sent before the restarting , the bot don't deleting the message
why?
client.on('message', async message => {
if(message.author.bot) return;
var muted = message.guild.member(message.author).roles.find(j => j.id === "505763004797812766");
if (muted && message.channel.id === "563944611693854721"){
var muted = message.guild.member(message.author).roles.find(j => j.id === "505763004797812766");
const args = message.content.split(" ").slice(0).join(" ");
const appeal = new Discord.RichEmbed()
.setAuthor(message.author.username, message.author.avatarURL)
.setTitle(message.author.username + " appeal")
.setColor("RED")
.addField("Message", args);
message.guild.channels.find(ch => ch.id === "563966341980225536").send(appeal).then(msg => {
msg.react('✅');
client.on('messageReactionAdd', (reaction, user) => {
if(reaction.emoji.name === "✅") {
const whitecheckmark = (reaction, user) => reaction.emoji.name === "✅";
const done = msg.createReactionCollector(whitecheckmark, {time: 60000});
done.on('collect', r => {
msg.delete();
message.guild.channels.find(ch => ch.id === "563966341980225536").send(message.author + " Appeal ended by: " + reaction.users.last())
})
}
});
})
message.delete();
message.channel.overwritePermissions(message.author, {SEND_MESSAGES: false});
}
else if(!muted && message.channel.id === "563944611693854721"){
message.channel.overwritePermissions(message.author, {SEND_MESSAGES: true});
}
});
This is events by design. The only way you can get around this is by queuing these events in some kind of durable queue like RabbitMQ or NATS assuming the events make it to your listeners before it restarts.
In general, it isn't good practice to "nest" events (that is, add listeners within others). If you place the messageReactionAdd listener outside of the message event on its own, it will listen without needing a message to. Then, if the message is sent and the bot restarts, the reaction event will still be fired. Just make sure to confirm that the message triggering the event is indeed one that should be.