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.
Related
What I tried to do for my music bot was getting him to report when he gets kicked out of a voice channel, by saying for example "I was forcefully disconnected". I didnt find a useful property or method to check this, so I asked for help to some friends and tried this:
client.on('voiceStateUpdate', (oldMember, newMember) => {
let newUserChannel = newMember.voiceChannel
if (newUserChannel === undefined) return console.log("I was kicked from the voice channel")
})
It didnt work.
So, is there any way to solve my problem?
I had the same proble some time ago.
This is the way i used to solve it:
Client.on('voiceStateUpdate', (oldState, newState) => {
// Represents a mute/deafen update
if(oldState.channelId === newState.chanelId) return console.log('Mute/Deafen Update');
// Some connection
if(!oldState.channelId && newState.channelId) return console.log('Connection Update');
// Disconnection
if(oldState.channelId && !newState.channelId){
console.log('Disconnection Update');
// Bot was disconnected?
if(newState.id === Client.user.id) return console.log(`${Client.user.username} was disconnected!`);
}
});
Since the voiceStateUpdate event is fired in everything related to voice channels, you should add as much scenarios as possible to make things work as intended.
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
client.on("message", message =>{
if(message.channeltype = 'dm')
if (message.content === "confess")
message.channel.send("OOOOOOoooooo A SECRET??!?!?! WHAT IS IT?!??!")
message.channel.awaitMessages("response", message =>{
C = message.content
message.reply("Um.... theres two servers that this bot works on. 1: Loves in the snow or 2:🌙Moomins and Shep's Therapy Service🌙. Pick a number to send it to one of them")
client.on("message", message => { //////// SWITCH THIS LINE OUT FOR THE THING THAT WAITS FOR NEW MESSAGES
if (message.content === "2")
WDI = message.author
WDID = message.author.id
message.channel.get(WDIC2).send("Ok ok so who did it is" + WDI + "and their id is" + WDID)
message.channel.get(CC2).send(C)
if (message.content === "1")
WDI = message.author
WDID = message.author.id
message.channel.get(WDIC1).send("Ok ok so who did it is" + WDI + "and their id is" + WDID)
message.channel.get(CC1).send(C)
})
})})
Why does my bot not reply to my message?
I first put in confess then it would say the message but it doesnt detect that I continue and reply to that. WHat do I do?
Okay first off, try to be a lot more specific when asking questions there is a stackoverflow guide on how you should ask them effectively. Right now you are being unclear and I'm going to have to assume some stuff. Also, include a stack trace whenever possible, which there should have been as you provide some invalid properties, if you don't have one please include that there is not a stack trace in the question.
There is a lot of things wrong with this code. First off, the reason your bot isn't responding to you at all is because message.channeltype is not a property. Use message.channel.type instead. There is also something wrong with your message.channel.awaitMessages(). You are passing in a "response" string as a parameter, where there should be a filter. Since you don't need a filter since it is a dm channel you can just pass it in as null message.channel.awaitMessages(null, msg => { });. And awaitMessages is not the right call here. Await messages takes in messages in a certain time period, then resolving a promise with all of the collected messages. You should be using a discord.js collector. You should also not be using client.on("message") here. I'm assuming that you want to wait for messages in any channel after somebody "shared their secret". You can do that with a collector. If you want to collect messages all over the bot, or in just one discord server channel (using the filter which can be found on the docs) you can use a collector. This will wait for, in a number of milliseconds, for any event and filter out event triggers that aren't allowed. And when it gets it, it will run a lambda. Like this:
const filter = m => m.channel === message.channel && m.author = message.author); // The filter that will sort out the messages
const collector = message.channel.createMessageCollector(filter, { time: 30000 }); // creates a collector that will wait for 30 seconds
collector.on('collect', m => { // When somebody sends a message
// ...
if (message.content === '2') {
// ...
} else if (message.content === '1') {
// ...
} else {
message.reply('you need to say either 1 or 2. Please try again.');
return;
}
collector.stop(); // Makes sure that the collector no longer will receive anymore messages.
});
Final code (fill in the gaps I'm not going to code it all for you)
client.on("message", message => {
if(message.channel.type = 'dm')
if (message.content === "confess")
const filter = m => m.channel === message.channel && m.author = message.author; // The filter that will sort out the messages
const collector = message.channel.createMessageCollector(filter, {time: 30000})
collector.on('collect', message => {
const otherCollector = message.channel.createMessageCollector(filter, { time: 30000})
otherCollector.on('collect', message => {
if (message.content === '2') {
// ...
} else if (message.content === '1') {
// ...
} else {
message.reply('you need to say either 1 or 2. Please try again.');
return;
}
collector.stop(); // Makes sure that the collector no longer will receive anymore messages.
})
})
})
Also, before asking on any forum or discord server, always look up the documentation. It helps out 99% of cases, unless the docs is absolutely garbage.
Please read the docs. I'd say that the Discord.JS docs are a pretty handy reference.
For the first part, message.channeltype is not a real property, and do not use =. That is for setting variables, use == or === instead (What is the difference?). You can use message.channel.type or you can use instanceof as followed: message.channel instanceof Discord.DMChannel. Replace Discord with whatever your Discord.JS import variable is called.
Your ifs are missing some {}, they should be used as followed.:
if(message.channel instanceof Discord.DMChannel) {
//If the channel IS a DMChannel
} else return;
FYI, if you're planning to make multiple commands, I'd use a switch, and a way to convert message.content to arguments (string array).
message.channel.get is not a thing, use client.channels.cache.get. The variables C, and WDI / WDID need to have var before them.
Then you need to add a filter plus maximum messages, time limit, and minimum messages for your awaitMessages function. Plus you need then and catch, so now your code might look like this
if(message.channel instanceof Discord.DMChannel) {
if (message.content === "confess") {
message.channel.send("OOOOOOoooooo A SECRET??!?!?! WHAT IS IT?!??!")
message.reply("Um.... theres two servers that this bot works on. 1: Loves in the snow or 2:🌙Moomins and Shep's Therapy Service🌙. Pick a number to send it to one of them")
const filter = m => m.author == message.author;
message.channel.awaitMessages(filter, {/* whatever your parameters/limits are */})
.then(col => {
if (col.content === "2") {
/** Add in code */
} else if (col.content === "1") {
/** Add in code */
}
})
.catch(col => {/** Whatever you want to do if times up. */})
}
}
You may want to tweak the filter on this.
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'],
});
I need it so that a bot will send a message and if anyone on the server reacts to that message with a reaction it will give them a role.
I Have already tried multiple times, different code samples which allow reactions anywhere in the guild but i want it specific to one channel.
client.on("messageReactionAdd", (reaction, user) => {
if (user.bot) return;
const member = reaction.message.member
switch (reaction.name) {
case "😎":
member.addRole("597011179545690121").then((res) => {
reaction.message.channel.send(`You've been given the \`${res.name}\` role!`)
}).catch(console.error);
break;
case "🕵":
member.addRole("597011179545690121").then((res) => {
reaction.message.channel.send(`You've been given the \`${res.name}\` role!`)
}).catch(console.error);
};
})
client.on("messageReactionRemove", (reaction, user) => {
if (user.bot) return;
const member = reaction.message.member
switch (reaction.name) {
case "emoji_name_1":
member.removeRole("roleID").then((res) =>
reaction.message.channel.send(`You've been removed from the \`${res.name}\` role!`)
}).catch(console.error);
break;
case "emoji_name_2":
member.removeRole("someOtherRole").then((res) => {
reaction.message.channel.send(`You've been removed from the \`${res.name}\` role!`)
}).catch(console.error);
};
})
I Want the outcome to be that the person reacts with say a smiley face and they get a certain role but they have to react to a certain message sent by the bot in its own dedicated channel same as pre-made bots such as Reaction Roles bot.
First we have to fetch our Message.
let channel_id = "ChanelID of message";
let message_id = "ID of message";
client.on("ready", (reaction, user) => {
client.channels.get(channel_id).fetchMessage(message_id).then(m => {
console.log("Cached reaction message.");
}).catch(e => {
console.error("Error loading message.");
console.error(e);
});
Then we'll check if someone reacted to our message and give them the appropriate Role
client.on("messageReactionAdd", (reaction, user) => {
if(reaction.emoji.id == "EMOJI ID HERE" && reaction.message.id === message_id)
{
guild.fetchMember(user) // fetch the user that reacted
.then((member) =>
{
let role = (member.guild.roles.find(role => role.name === "YOUR ROLE NAME HERE"));
member.addRole(role)
.then(() =>
{
console.log(`Added the role to ${member.displayName}`);
}
);
});
}
}
I advise against using this code for multiple Roles at once as that would be rather inefficient.
const member = reaction.message.member
Problem: This is defining member as the GuildMember that sent the message, not the one that added the reaction.
Solution: Use the Guild.member() method, for example...
const member = reaction.message.guild.member(user);
switch (reaction.name) {...}
Problem: name is a not a valid property of a MessageReaction.
Solution: What you're looking for is ReactionEmoji.name, accessed via reaction.emoji.name.
...[the users] have to react to a certain message...
Problem: Your current code isn't checking anything about the message that's been reacted to. Therefore, it's triggered for any reaction.
Solution: Check the message ID to make sure it's the exact one you want. If you'd like to allow reactions on any message within a certain channel, check the message's channel ID.
Consider these examples:
if (reaction.message.id !== "insert message ID here") return;
if (reaction.message.channel.id !== "insert channel ID here") return;
Problem: The messageReactionAdd event is not emitted for reactions on uncached messages.
Solution: Use the raw event to fetch the required information and emit the event yourself, as shown here. Alternatively, use Caltrop's solution and fetch the message when the client is ready via TextChannel.fetchMessage().