Removing just one reaction from message (Discord.js) - javascript

I am creating an embed where you can navigate through pages by reacting. By following a tutorial I came up with this but here's the problem: it removes all reactions of the same kind when the user react to it (basically If I react with ❤️, the bot removes either my reaction and theirs, letting ⚙️ be the only reaction available)
let embed= new Discord.MessageEmbed()
.setTitle(Title)
.setURL(url)
.setThumbnail(image)
const filter1 = (reaction, user) => reaction.emoji.name === '⚙️' && user.id === message.author.id
const filter2 = (reaction, user) => reaction.emoji.name === '❤️' && user.id === message.author.id
let msg = await message.channel.send(embed)
await msg.react('⚙️')
await msg.react('❤️')
const collector1= await msg.createReactionCollector(filter1, {time: 60000})
const collector2= await msg.createReactionCollector(filter2, {time: 60000})
collector1.on('collect', async r => {
embed.setDescription('Page 1')
r.remove(message.author.id) // <<== This removes also the bot reaction
msg.edit(embed)
})
collector2.on('collect', async r => {
embed.setDescription('Page 2')
r.remove(message.author.id) // <<== This removes also the bot reaction
msg.edit(embed)
})
Hopefully, you understood my problem, I just wanted to have the bot removing my reaction, not all.

According to the docs, MessageReaction.remove() removes the entire reaction, not just 1 user. If you call the remove() function on the ReactionUserManager, you can remove 1 user from the reaction.
Take a look at the example code below and give it a try.
collector1.on('collect', async (reaction, user) => {
embed.setDescription('Page 1');
reaction.users.remove(user);
msg.edit(embed);
});

Related

(Discord Bot) Get message that follows command prefix

I'm trying to reference the previous message sent by a client using discord.js
How would I go about getting the last message sent?
client.on("messageCreate", (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'set-reminder'){
channelName = client.channels.cache.find(channel => channel.name === "spam");
message.channel.send("Command Works");
channelName.messages.fetch({ limit: 1 }).then(messages => {
// find "command works" message (previous message from client)
let lastMessage = messages.first();
console.log(lastMessage.content);
})
}
})```
The message.channel.send function is async
You can await the send function or use .then
The send function also returns the message, you don't need a query to get it
await
let message = await message.channel.send("Command Works");
console.log(message.content);
then
message.channel.send("Command Works").then((message) => {
console.log(message.content);
});
(async means not-synchronous : you have to wait to get its result)

I got problems with my reaction collector for my discord bot

i recently made a discord bot and added a special function that makes the bot collect a specific emoji in order to switch a page in an embed menu in dms of the bot. But it turned out to not really work so im asking here if someone can help me solve this.
const filter = (reaction, user) => {
return ['➡️'].includes(reaction.emoji.name) && user.id !== client.user.id;
};
const collector = message.createReactionCollector(filter, { time: 60000 });
collector.on('collect', (reaction, user) => {
console.log(`Collected ${reaction.emoji.name} from ${user.tag}`);
});
collector.on('end', collected => {
console.log(`Collected ${collected.size} items`);
});

"mute" command gives random output

if(command === "mute"){
const target = message.mentions.users.first();
if (target) {
if (!message.member.hasPermission("ADMINISTRATOR")) {
return message.reply("Unauthorized");
}
let muted = client.settings.get(message.guild.id, 'mute')
let muteRole = message.guild.roles.cache.find(role => role.name === muted);
let memberTarget = message.guild.members.cache.get(target.id);
let rolelist = memberTarget.roles.cache
.map(r => r)
if (!args[1]) {
memberTarget.roles.remove(rolelist);
memberTarget.roles.add(muteRole);
message.channel.send(`<#${memberTarget.user.id}> has been muted`);
return
}
memberTarget.roles.remove(rolelist);
memberTarget.roles.add(muteRole);
message.channel.send(`<#${memberTarget.user.id}> will be muted for ${ms(ms(args[1]))}`);
setTimeout(function () {
memberTarget.roles.add(rolelist);
memberTarget.roles.remove(muteRole);
}, ms(args[1]));
} else {
message.channel.send('User not found!');
}
So I have this mute command which removes all your roles, gives you the muteRole, when time is up, it gives back your previous roles and removes the muteRole. The problem I'm having is that it sometimes removes all your roles and doesn't give you the muteRole, other times it doesn't remove the muteRole when the time is up. Sometimes it removes all your roles and doesn't give you the
muteRole and when the times up it doesn't give your roles back...
Basically the outcome is always random. So my question is how do I make it consistent?
The methods .add() and .remove() return a Promise so you should try using await / .then().
If you do it with this, the next function or .then() will not be executed until the promise is completely processed.
Example:
memberTarget.roles.remove(rolelist)
.then(() => memberTarget.roles.add(muteRole))
.then(() => message.channel.send('some message'));
Now you're nearly finished. The last thing you need is the .catch() block, because if the code above fails / throws an error you should catch that:
.catch(err => console.log(`An error occured: ${err}`))
Final result:
memberTarget.roles.remove(rolelist)
.then(() => memberTarget.roles.add(muteRole))
.then(() => message.channel.send('some message'))
.catch(err => console.log(`An error occured: ${err}`));
References
GuildMemberRoleManager (Discord.js)
Promises
Handle promises
Await

How do I use an API in Discord.js?

I was looking for a cool way I could automate some interesting news articles on my discord server. I wanted to use webhooks at first but then decided to go with APIs. I looked all around and saw that I should go with New York Times API but when I went to code it, it came up with a few errors.
const Discord = require("discord.js");
const client = new Discord.Client();
const token = require("./token.js");
const fetch = require('node-fetch');
const prefix = '!';
const trim = (str, max) => str.length > max ? `${str.slice(0, max - 3)}...` : str;
client.once('ready', () => {
console.log('Ready!');
});
client.on('message', async message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'news') {
const { file } = await fetch('https://api.nytimes.com/svc/topstories/v2/technology.json?api-key=').then(response => response.json());
message.channel.sendMessage(file);
}
}).then((state) => {
assert(state.action === 'DONE', 'should change state');
})
.catch((error) => {
assert.isNotOk(error, 'Promise error');
});
throw
client.login(token);
This is my code, I know this is probably riddled with mistakes but I am just starting out with node.js
I looked at the example from the discord.js website and took some stuff from there. I don't know what I should do and if you could explain it out a little to help me learn that would be great. I keep getting the Unhandled Rejection Promise Warning and the Cannot send an empty message errors. I am using Visual Studio Code.
You can use the async/await to get the results array from the API response, then send the details in embeds. You can either send the first article, a random article or more than one articles.
The following should work, it sends the first three articles:
const { Client, MessageEmbed } = require('discord.js');
const fetch = require('node-fetch');
const token = require("./token.js");
const client = new Client();
const API_KEY = 'QerEdX953-NOT-REAL-hdvgx7UPs';
const prefix = '!';
client.on('message', async (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'news') {
try {
const response = await fetch(
`https://api.nytimes.com/svc/topstories/v2/technology.json?api-key=${API_KEY}`,
);
const data = await response.json();
if (data.results.length === 0) {
return message.channel.send('There are no top stories for you today 🙊');
}
const embed = new MessageEmbed();
// you could also get the number of stories from args[0]
const MAX_STORIES = 3;
data.results.slice(0, MAX_STORIES).forEach((result) => {
embed.setTitle(result.title);
embed.setDescription(result.abstract);
embed.setURL(result.url);
embed.setTimestamp(result.published_date);
if (result.multimedia.length && result.multimedia[2]) {
embed.setThumbnail(result.multimedia[2].url);
}
message.channel.send(embed);
});
} catch (error) {
message.channel.send('Oops, there was an error fetching the API');
console.log(error);
}
}
});
The response from the Top Stories API does not have a file key and so is undefined. You want to destructure results to access the articles array.
The error message says that you cannot call sendMessage with an empty message (undefined in this case).
I'm not sure of the message you want to send but you can send the link to the first article with the following code, for example.
const res = await fetch(`https://api.nytimes.com/svc/topstories/v2/technology.json?api-key=${NYTIMES_API_KEY}`)
const { results } = await res.json()
if (results.length) {
message.channel.sendMessage(results[0].url)
} else {
message.channel.sendMessage('No articles found')
}

How to make a reaction disappear after a certain amount of reactions?

I want to make a poll command, where I use the command, and when the reaction reaches a certain amount of reactions it (the reaction) gets deleted. That would be useful to see who wins if you understand me.
That's what I'm using right now for only adding a reaction.
bot.on('message', msg => {
if(msg.content === "memes"){
msg.react("🤏🏾")
}
})
You can use reaction collectors to collect user reactions on a message. The collector has options like maxUsers that you can use to set the maximum number of users to react. Once it's reached, the collector.on('end') fires, so you can use it to get the number of users reacted, etc.
You can remove the reactions using message.reactions.removeAll();
Check the snippet below:
if (message.content === 'memes') {
const EMOJI = '🤏🏾';
const filter = (reaction, user) =>
reaction.emoji.name === EMOJI && !user.bot;
const collector = message.createReactionCollector(filter, {
// optional, the maximum time in ms the collector collects reactions
time: 10000,
// maximum number of users to react
// it includes the bot's reaction too
maxUsers: 3,
});
message.react(EMOJI);
collector.on('collect', (reaction, user) => {
// it fires every time someone reacts to the message
console.log('collected', reaction);
});
collector.on('end', async (collected, reason) => {
const reaction = collected.get(EMOJI);
if (!reaction) {
return message.channel.send('Oops, there were no reactions');
}
// it includes the bot too
const allUsers = await reaction.users.fetch();
// collection of users who reacted to the message, bot removed
const users = allUsers.filter((user) => user != client.user);
message.channel.send(
`Poll has ended because of ${reason}. Received ${users.size} reactions.`
);
message.channel.send(
`Users reacted: ${users.array().join(', ')}`
);
// remove reactions
message.reactions.removeAll();
});
}
If you want to have an array of emojis, you can use an EMOJIS array instead:
if (message.content === 'memes') {
const EMOJIS = ['🤏🏾', '😱'];
const filter = (reaction, user) =>
EMOJIS.includes(reaction.emoji.name) && !user.bot;
const collector = message.createReactionCollector(filter, {
time: 10000,
maxUsers: 3,
});
EMOJIS.forEach((emoji) => message.react(emoji));
collector.on('collect', (reaction, user) => {
// it fires every time someone reacts to the message
});
collector.on('end', async (collected, reason) => {
if (!collected.size) {
message.reactions.removeAll();
return message.channel.send('Oops, there were no reactions');
}
// you can use the collected reactions any way you want
console.log(collected);
// remove reactions
message.reactions.removeAll();
});
}

Categories