So far I got the bot to add the message to a specific channel when the ⭐ reaction is triggered. I am stuck now... I'm trying to add a threshold where it takes a certain amount of ⭐ reactions to trigger the push the specific channel. Any help will be greatly apperciated.
const handleStarboard = async () => {
const starboard = bot.channels.cache.find(channel => channel.name.toLowerCase() === 'starboard');
const msgs = await starboard.messages.fetch({ limit: 100 });
const existingMsg = msgs.find(msg =>
msg.embeds.length === 1 ?
(msg.embeds[0].footer.text.startsWith(reaction.message.id) ? true : false) : false);
if(existingMsg) existingMsg.edit(`${reaction.count} - ⭐`);
else {
const embed = new MessageEmbed()
.setAuthor(reaction.message.author.tag, reaction.message.author.displayAvatarURL())
.addField('Url', reaction.message.url)
.setDescription(reaction.message.content)
.setFooter(reaction.message.id + ' - ' + new Date(reaction.message.createdTimestamp));
if(starboard)
starboard.send('1 - ⭐', embed);
}
}
if(reaction.emoji.name === '⭐') {
if(reaction.message.channel.name.toLowerCase() === 'starboard') return;
if(reaction.message.partial) {
await reaction.fetch();
await reaction.message.fetch();
handleStarboard();
}
else
handleStarboard();
}
});
bot.on('messageReactionRemove', async (reaction, user) => {
const handleStarboard = async () => {
const starboard = bot.channels.cache.find(channel => channel.name.toLowerCase() === 'starboard');
const msgs = await starboard.messages.fetch({ limit: 100 });
const existingMsg = msgs.find(msg =>
msg.embeds.length === 1 ?
(msg.embeds[0].footer.text.startsWith(reaction.message.id) ? true : false) : false);
if(existingMsg) {
if(reaction.count === 0)
existingMsg.delete({ timeout: 2500 });
else
existingMsg.edit(`${reaction.count} - ⭐`)
};
}
if(reaction.emoji.name === '⭐') {
if(reaction.message.channel.name.toLowerCase() === 'starboard') return;
if(reaction.message.partial) {
await reaction.fetch();
await reaction.message.fetch();
handleStarboard();
}
else
handleStarboard();
}
});
Related
Recently I decided to update my Discord Bot from V12 to V13 and I encountered a problem with my Reaction Roles.
It only works if the bot doesn't restart. It worked perfectly on discord.js 12.5.3 and now I'm using discord.js 13.5.0. Could you guys help me with the problem?
Here is my code:
const { MessageEmbed } = require("discord.js");
const Discord = require('discord.js');
const { Client } = require('discord.js');
const client = new Client({
intents: 32767,
partials: ['MESSAGE', 'CHANNEL', 'REACTION'],
});
const config2 = require("../config2");
module.exports = async (client, message, args) => {
client.on('messageCreate', async message => {
if (message.content.startsWith("22ar")) {
const embed = new MessageEmbed()
.setColor('#FFA500')
.setTitle(`Pick the roles`)
.setDescription(`🔓 **- You unlock yourself.**
✅ **- You get access to the server.**
💜 **- You get notified for X things.**
[🔓, ✅] - obligatory.
[💜] - optional.
\`Wait at least 30 seconds and try again if you don't get the roles.\``)
const msg = await message.channel.send({ embeds: [ embed ] })
msg.react('🔓')
msg.react('✅')
msg.react('💜')
}
})
const CHANNEL = config2.ROLECHANNEL;
const UROLE = config2.UNLOCKROLE;
const MROLE = config2.MEMBERROLE;
const NROLE = config2.NOTIFYROLE;
const unlockEmoji = '🔓';
const checkEmoji = '✅';
const notifyEmoji = '💜';
client.on('messageReactionAdd', async (reaction, user) => {
if (reaction.partial) {
try {
await reaction.fetch();
} catch (error) {
console.error('Fetching message failed: ', error);
return;
}
}
if (!user.bot) {
if(reaction.message.channel.id === CHANNEL) {
if (reaction.emoji.name == checkEmoji) {
const memberRole = reaction.message.guild.roles.cache.find(r => r.id === MROLE);
const { guild } = reaction.message
const member = guild.members.cache.find(member => member.id === user.id);
member.roles.add(MROLE);
}
if (reaction.emoji.name == unlockEmoji) {
const unlockRole = reaction.message.guild.roles.cache.find(r => r.id === UROLE);
const { guild } = reaction.message
const member = guild.members.cache.find(member => member.id === user.id);
member.roles.remove(UROLE)
}
if (reaction.emoji.name == notifyEmoji) {
const notifyRole = reaction.message.guild.roles.cache.find(r => r.id === NROLE);
const { guild } = reaction.message
const member = guild.members.cache.find(member => member.id === user.id);
member.roles.add(NROLE)
}
}
client.on('messageReactionRemove', async (reaction, user) => {
if (reaction.partial) {
try {
await reaction.fetch();
} catch (error) {
console.error('Fetching message failed: ', error);
return;
}
}
if (!user.bot) {
if(reaction.message.channel.id === CHANNEL) {
if (reaction.emoji.name == checkEmoji) {
const memberRole = reaction.message.guild.roles.cache.find(r => r.id === MROLE);
const { guild } = reaction.message
const member = guild.members.cache.find(member => member.id === user.id);
member.roles.remove(memberRole);
}
if (reaction.emoji.name == unlockEmoji) {
const unlockRole = reaction.message.guild.roles.cache.find(r => r.id === UROLE);
const { guild } = reaction.message
const member = guild.members.cache.find(member => member.id === user.id);
member.roles.remove(unlockRole)
}
if (reaction.emoji.name == notifyEmoji) {
const notifyRole = reaction.message.guild.roles.cache.find(r => r.id === NROLE);
const { guild } = reaction.message
const member = guild.members.cache.find(member => member.id === user.id);
member.roles.remove(notifyRole)
}
}
}
})
}
})
}
I am not getting any error in the console and the bot continues to work as usual.
Thank you for your attention.
Messages sent before your bot started are uncached unless you fetch them first. By default, the library does not emit client events if the data received and cached is not sufficient to build fully functional objects. Since version 12, you can change this behavior by activating partials.
Official guide about this here (Listening for reactions on old messages).
const optionsEmbed = new Discord.MessageEmbed()
.setTitle("What would you like to setup?")
.setDescription(":one: Prefix\n:two: Mod Log Channel")
.setColor('RANDOM');
message.channel.send(optionsEmbed)
.then(async (msg) => {
await msg.react('1️⃣');
await msg.react('2️⃣');
const filter = (reaction, user) => {
return reaction.emoji.name === '1️⃣' || reaction.emoji.name === '2️⃣' && user.id === message.author.id;
};
const collector = message.createReactionCollector(filter, {
time: 30000
});
let collected = 0;
collector.on('collect', (reaction, user) => {
collected++;
if (reaction.emoji.name === "one") return managePrefix();
if (reaction.emoji.name === "two") return manageModLogChannel();
msg.delete();
collector.end();
});
collector.on('end', collected => {
if (collected === 0) return message.channel.send("You took too long to react, cancelled setup.");
});
});
async function managePrefix() {
const getCurrentPrefix = await message.getServerDatabase(message.guild.id, "prefix")
const currentPrefixEmbed = new Discord.MessageEmbed()
.setTitle("Info")
.setDescription("Current Prefix: " + getCurrentPrefix + "\n\nPlease reply with the new prefix or *cancel* to cancel.")
.setColor('RED');
message.channel.send(currentPrefixEmbed)
.then(async (msg) => {
const filter = (msgg, user) => {
return user.id == message.author.id;
}
const collector = message.channel.createMessageCollector(filter, {
time: 60000
});
let collected = 0;
collector.on('collect', async (m) => {
collected++;
msg.delete()
if (m.content.toLowerCase() === "cancel") {
m.delete();
return collector.end()
}
await message.setServerDatabase(message.guild.id, "prefix", m.content)
await m.reply("Set prefix to `" + m.content + "`");
collector.end()
});
collector.on('end', collected => {
if (collected === 0) return message.channel.send("You took too long to respond, cancelled setup.")
});
});
}
async function manageModLogChannel() {
const getCurrentPrefix = await message.getServerDatabase(message.guild.id, "modLogChannel")
const currentPrefixEmbed = new Discord.MessageEmbed()
.setTitle("Info")
.setDescription("Current ModLogChannel: <#" + getCurrentPrefix + ">\n\nPlease reply with the new channel or *cancel* to cancel.")
.setColor('RED');
message.channel.send(currentPrefixEmbed)
.then(async (msg) => {
const filter = (msgg, user) => {
return user.id == message.author.id;
}
const collector = message.channel.createMessageCollector(filter, {
time: 60000
});
let collected = 0;
collector.on('collect', async (m) => {
collected++;
msg.delete()
if (m.content.toLowerCase() === "cancel") {
m.delete();
return collector.end()
}
if (!m.mentions.channels.first()) {
m.delete()
message.channel.send("Not a valid channel, cancelled setup.");
return collector.end();
}
const channel = m.mentions.channels.first();
await message.setServerDatabase(message.guild.id, "modLogChannel", channel.id)
await m.reply("Set channel to `<#" + m.content + ">`");
collector.end()
});
collector.on('end', collected => {
if (collected === 0) return message.channel.send("You took too long to respond, cancelled setup.")
});
});
}
I am trying to make a config command, upon reacting to the first embed.. nothing happens.
If that first listener is not working, then I doubt the message collectors are working either.
The user should be able to react to a main embed which asks them if they want to change the prefix or mod log channel, from there it will then ask them what they want to change it to with the cancel option, it then saves it in the database using the custom functions (message.getServerDatabase and message.setServerDatabase())
Does anyone know what I'm doing wrong?
The reason it is not working as you want it to is that you are calling the .createReactionCollector() method on the wrong Message object. You should create the reaction collector on msg instead of message.
Like this:
const collector = msg.createReactionCollector(filter, {
time: 30000
});
Also collector.end() is not a function, you probably want to use collector.stop() instead.
Here is the entirety of my puppeteer controller:
import { Readability } from '#mozilla/readability';
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const summarize = require('summarize');
const keyword_extractor = require('keyword-extractor');
const amex = require('../../csv/AMEX.json');
const nasdaq = require('../../csv/NASDAQ.json');
const nyse = require('../../csv/NYSE.json');
const cryptotickers = require('../../csv/cryptos.json');
puppeteer.use(StealthPlugin());
class Reader {
constructor() {
this.browser = null;
}
async getLink(link) {
this.browser = await puppeteer.launch({
devtools: false,
headless: true,
// product: 'firefox',
executablePath: '/usr/bin/chromium-browser',
args: [
'--proxy-server=' + process.env.PROXY_HOST,
'--no-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--single-process',
'--disable-setuid-sandbox',
'--no-zygote',
'--shm-size=4gb',
'--disable-infobars',
'--ignore-certifcate-errors',
'--ignore-certifcate-errors-spki-list',
// '--user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1"'
],
});
const { htm, title } = await this.spa(link);
if (!htm) {
await this.browser.close();
return;
}
const text = txt(htm, link);
const data = Object.assign({}, text);
const parts = new URL(link);
if (!data.title) {
data.title = title;
}
data.summary = summary(data.content, data.title);
data.tickers = tickers(data.content, data.textContent);
data.cryptos = cryptos(data.content, data.textContent);
data.meta = getMeta(htm);
if (!data.title && data.meta.title) {
data.title = data.meta.title;
}
data.url = link;
data.htm = htm;
data.host = parts.host;
data.text = data.textContent;
delete data.textContent;
console.log('data fetched: ' + link);
await this.browser.close();
// await this.browser.disconnect();
return data;
}
async spa(url) {
let htm;
let title;
try {
let page = await this.browser.newPage();
await page.setRequestInterception(true);
page.on('request', (req) => {
if (
req.resourceType() === 'stylesheet' ||
req.resourceType() === 'font' ||
req.resourceType() == 'image'
) {
req.abort();
} else {
req.continue();
}
});
await page.authenticate({
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS,
});
await page.setViewport({ width: 800, height: 600 });
// await page.goto(url, { waitUntil: 'networkidle2' });
await page.goto(url, { waitUntil: 'domcontentloaded' });
await this.autoScroll(page);
await page.evaluate(() => window.scrollTo(0, 50));
htm = await page.content();
title = await page.evaluate(() => document.title);
if (htm.indexOf('<title') === -1) {
htm = await page.evaluate(() => document.documentElement.outerHTML);
}
console.log(title, 'title');
} catch (err) {
console.error(err, url);
}
return { htm, title };
}
async autoScroll(page) {
await page.evaluate(async () => {
new Promise((resolve, reject) => {
try {
const maxScroll = Number.MAX_SAFE_INTEGER;
let lastScroll = 0;
const interval = setInterval(() => {
window.scrollBy(0, document.body.offsetHeight);
const { scrollTop } = document.documentElement;
if (scrollTop === maxScroll || scrollTop === lastScroll) {
clearInterval(interval);
resolve();
} else {
lastScroll = scrollTop;
}
}, 1000);
} catch (error) {
reject(error);
}
}).catch((error) => {
console.error(error); // add catch here
});
});
// await page.evaluate(async () => {
// await new Promise((resolve, reject) => {
// let totalHeight = 0;
// let distance = 300;
// let timer = setInterval(() => {
// const scrollHeight = document.body.scrollHeight;
// window.scrollBy(0, distance);
// totalHeight += distance;
// if(totalHeight >= scrollHeight){
// clearInterval(timer);
// resolve();
// }
// }, 100);
// });
// });
}
} // end Class Reader
async function summarization2(text) {
let res;
let data;
console.log(text, process.env.DEEPAI_KEY);
try {
const body = new FormData();
body.append('text', text);
res = await fetch(`https://api.deepai.org/api/summarization`, {
method: 'POST',
body,
headers: {
'api-key': process.env.DEEPAI_KEY,
},
});
data = await res.json();
} catch (err) {
console.error(err);
}
return data;
}
async function sentiment(text) {
return await deepai.callStandardApi('sentiment-analysis', { text });
}
async function summarization(text) {
return await deepai.callStandardApi('summarization', { text }).catch(console.error);
}
function summary(text, title) {
if (!text) return {};
const summary = summarize(`${title} - ${text}`);
summary.topics = keyword_extractor
.extract(`${title} - ${text}`, {
language: 'english',
remove_digits: true,
return_changed_case: true,
remove_duplicates: false,
})
.map(process);
const counts = summary.topics.reduce(
(acc, value) => ({
...acc,
[value]: (acc[value] || 0) + 1,
}),
{},
);
let topics = [];
for (let topic in counts) {
topics.push({ topic, count: counts[topic] });
}
topics = topics.filter((t) => t.topic);
topics = topics.sort((a, b) => {
return b.count - a.count;
});
topics = topics.slice(0, 10);
topics = topics.map((topic) => topic.topic);
summary.topics = topics;
function process(topic) {
topic = topic.toLowerCase().trim();
topic = topic.replace(/[\W_]+/g, '');
topic = topic.replace(/\s+/g, '-');
return topic;
}
console.log('summary: ', summary);
return summary;
}
function tickers(htm, text) {
if (!text) return {};
const tickers = [];
function findTicker(ticker, exchange) {
let name = ticker.Name;
if (name && name.indexOf('Twitter') === -1 && name.indexOf('Facebook') === -1) {
name = name.replace(/,? ?Inc\.?/gi, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const regex = new RegExp(`\\b${name}\\b`, 'gi');
if (text.match(regex)) {
console.log(name);
console.log(regex.toString());
tickers.push({ name: ticker.Name, symbol: ticker.Symbol, exchange });
}
}
amex.forEach((ticker) => {
findTicker(ticker, 'amex');
});
nasdaq.forEach((ticker) => {
findTicker(ticker, 'nasdaq');
});
nyse.forEach((ticker) => {
findTicker(ticker, 'nyse');
});
console.log(tickers);
return tickers;
}
function cryptos(htm, text) {
if (!text) return {};
const tickers = [];
function findTicker(ticker) {
const name = ticker.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`\\b${name}\\b`, 'g');
if (text.match(regex)) {
console.log(name);
console.log(regex.toString());
tickers.push({ name: ticker.name, symbol: ticker.symbol });
}
}
cryptotickers.forEach(findTicker);
console.log(tickers);
return tickers;
}
function getMeta(htm) {
const doc = new JSDOM(htm);
const meta = {};
const thumb =
doc.window.document.querySelector('meta[property="og:image"]') ||
doc.window.document.querySelector('meta[name="twitter:image"]');
const title = doc.window.document.title;
meta.title = title;
meta.thumb = thumb && thumb.getAttribute('content');
return meta;
}
function txt(htm, link) {
const url = new URL(link);
const doc = new JSDOM(htm);
doc.window.document
.querySelectorAll('img')
.forEach(
(el) =>
(el.src =
el.src.indexOf('http') === 0 || el.src.indexOf('//') === 0
? el.src.indexOf('http://')
? el.src.replace('http:', '')
: el.str
: '//' + url.host + el.src),
);
doc.window.document
.querySelectorAll('a[href]')
.forEach(
(el) =>
(el.href =
el.href && el.href.indexOf('/') === 0
? url.protocol + '//' + url.host + el.href
: el.href),
);
const reader = new Readability(doc.window.document);
return reader.parse();
}
export default Reader;
For some reason after a few days the docker container has too many puppeteer processes because for some reason when fetching urls the browser doesn't exit properly.
Eventually the container is out of resources and the entire app freezes and is inaccessible.
I had the same issue when using Puppeteer inside docker. The solution was to implement dumb-init within docker. The Dockerfile should somehow look like this then (I assume you are developing a node-project therefore we call npm start at the end:
RUN apt-get install dumb-init // ... plus your other packages
... your remaining docker things
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD [ "npm", "start" ]
i have code that creates a reaction menu then outputs whichever option the user chose
client.on('message', async message => {
if (message.author.bot) return;
if (message.content === 'y.Einek')
{
const sentMessage = await message.channel.send(Einek);
const reactions = ['🇳', '🇭', '🇽'];
for (const reaction of reactions) await sentMessage.react(reaction);
const filter = (reaction, user) => reactions.includes(reaction) && user.id === message.author.id;
const collected = await sentMessage.awaitReactions(filter, { max: 1, timeout: 5000, errors: ['time'] }).catch(() => null);
if (!collected) return message.channel.send('You either took too long or something went wrong selecting your difficulty.');
const { name } = collected.first().emoji;
const response = name === '🇳' ? 'Normal' : (name === '🇭' ? 'Hard' : 'Extreme');
return message.channel.send(response);
}
});
and everything works fine except it doesn't output "response"
This code should work. Using the catch method for any error instead.
client.on('message', async message => {
if (message.author.bot) return;
if (message.content === 'y.Einek') {
const sentMessage = await message.channel.send(Einek);
const reactions = ['🇳', '🇭', '🇽'];
for (const reaction of reactions) await sentMessage.react(reaction);
const filter = (reaction, user) => reactions.includes(reaction) && user.id === message.author.id;
sentMessage.awaitReactions(filter, { max: 1, timeout: 5000, errors: ['time'] }).then(collected => {
const { name } = collected.first().emoji;
const response = name === '🇳' ? 'Normal' : (name === '🇭' ? 'Hard' : 'Extreme');
sentMessage.channel.send(response);
}).catch(() => message.channel.send('You either took too long or something went wrong selecting your difficulty.'));
}
});
Trying to make a bot that when the users click on the reaction there discord id goes into an embed Field and if they un click or click another emoji they end up in the field. This is gonna be used for a voting bot that once a certain number of users click yes or no a decision will be made to accept a user or deny a user. Any help?
exports.run = async (client, message, args) => {
message.delete({ timeout: 100 });
if (!args[0]) return message.reply('You need to supply the question');
let embed = new Discord.MessageEmbed()
.setTitle(args.join(' '))
.setDescription('Poll created by ' + message.author.tag)
.addField('Status', 'Voting is currently open.')
.setColor('#ffd700')
.attachFiles(new Discord.MessageAttachment('https://i.imgur.com/QUmbq9o.png', 'thumbnail.png'))
.setThumbnail('attachment://thumbnail.png')
.setFooter('Bot created by James (Rock)₇₇₇');
message.channel.send(embed).then(async msg => {
await msg.react('👍');
await msg.react('👎');
await msg.react('🤷');
await msg.react('🗑️');
const threshold = 6;
async function stop(result) {
collector.stop();
const newEmbed = new Discord.MessageEmbed(msg.embeds[0]);
newEmbed.title = newEmbed.title + ' [CLOSED]';
newEmbed.fields[0] = { name: 'Status', value: 'Voting is now closed.\n' + result };
newEmbed.setThumbnail('attachment://thumbnail.png');
await msg.edit(newEmbed);
msg.reactions.removeAll();
}
async function update() {
const newEmbed = new Discord.MessageEmbed(embed);
const userYes = (votes['👍'].size === 0)? '-' : [...votes['👍']];
const userNo = (votes['👎'].size === 0)? '-' : [...votes['👎']];
const userUnsure = (votes['🤷'].size === 0)? '-' : [...votes['🤷']];
newEmbed.addFields(
{ name: `User Yes (${votes['👍'].size}/${threshold})`, value: userYes, inline: true },
{ name: `User No (${votes['👎'].size}/${threshold})`, value: userNo, inline: true },
{ name: 'User Unsure', value: userUnsure, inline: true }
);
await msg.edit(newEmbed);
if (votes['👍'].size >= threshold) {
await stop('This answer is good enough to get accepted and an upvote.');
// do something
} else if (votes['👎'].size >= threshold) {
await stop('This answer is not good enough to get accepted and an upvote.');
// do something
}
}
const votes = {
'👍': new Set(),
'👎': new Set(),
'🤷': new Set(),
'🗑️': new Set()
};
update();
const collector = msg.createReactionCollector((reaction, user) => !user.bot , { dispose: true });
collector.on('collect', async (reaction, user) => {
if (['👍', '👎', '🤷', '🗑️'].includes(reaction.emoji.name)) {
const userReactions = msg.reactions.cache.filter(reaction => reaction.users.cache.has(user.id));
for (const userReaction of userReactions.values()) {
if (userReaction.emoji.name !== reaction.emoji.name || reaction.emoji.name === '🗑️') {
userReaction.users.remove(user.id);
votes[userReaction.emoji.name].delete(user);
}
}
votes[reaction.emoji.name].add(user);
} else {
reaction.remove();
}
update();
});
collector.on('remove', (reaction, user) => {
votes[reaction.emoji.name].delete(user);
update();
});
});
};
module.exports.help = {
name: "poll"
}
You can read this page from the discord.js guide about reaction collectors, it tells you everything you need to know. You can read this for more info about the .createReactionCollector() method.
There are multiple ways to acheive what you want once you make the reaction collector but I beleive the easiest would look something like this:
message.channel.send('your_message_here')
.then(async function(message) {
await message.react('👍');
await message.react('👎');
await message.react('🤷');
const filter = (reaction, user) => {
return ['👍', '👎', '🤷'].includes(reaction.emoji.name) && user.id === ogauthor
}
const collector = message.createReactionCollector(filter)
collector.on('collect', (reaction, user) => {
async function collect() {
if (!user.bot) {
if (reaction.emoji.name === '👍') {
//code here
}
//repeat this for the rest of your reactions
reaction.users.remove(user.id) //you can remove the reaction once they react to it and their name is added.
}
}
collect()
});
})
One problem is that this will run forever so you should add a timer to it.
You can use Reaction Collectors for this case to listen to reactions and list them depending on it.
I've made the code below, it works as expected:
message.delete({ timeout: 100 });
if (!args[0]) return message.reply('You need to supply the question');
let embed = new Discord.MessageEmbed()
.setTitle(args.join(' '))
.setDescription('Poll created by ' + message.author.tag)
.setColor('#ffd700')
.setThumbnail("https://i.imgur.com/QUmbq9o.png")
.addFields({name: "User Yes", value: 'None'}, {name: "User No", value: 'None'}, {name: "User Hum", value: 'None'})
.setFooter("Bot created by James (Rock)₇₇₇");
message.channel.send(embed).then(async msg => {
await msg.react('👍');
await msg.react('👎');
await msg.react('🤷');
const filter = (reaction, user) => {
return ["👍", "👎", "🤷"].includes(reaction.emoji.name);
};
const collector = await msg.createReactionCollector(filter);
collector.on('collect', (reaction, user) => {
const reactionsList = ["👍", "👎", "🤷"];
const fieldTitle = ["User Yes", "User No", "User Hum"];
var reactions = reaction.message.reactions.cache.array();
for(var reactionID in reactions) {
for (var i = 0; i < reactionsList.length; i++) {
if(reactionsList[i] === reaction.emoji.name){
let fieldDescription = user.id + "\n";
var users = reactions[reactionID].users.cache.array();
for(var userID in users){
if(users[userID].id === client.user.id || users[userID].id === user.id) continue;
fieldDescription += users[userID].id + "\n";
}
embed.spliceFields(i, 1, {name: fieldTitle[i], value: fieldDescription})
}
}
}
msg.edit(embed);
});
})
I have slightly modified your code and added code to track votes, edit the embed and check when the votes reach the threshold.
Demonstration
Code
message.delete({ timeout: 100 });
if (!args[0]) return message.reply('You need to supply the question');
let embed = new Discord.MessageEmbed()
.setTitle(args.join(' '))
.setDescription('Poll created by ' + message.author.tag)
.addField('Status', 'Voting is currently open.')
.setColor('#ffd700')
.attachFiles(new Discord.MessageAttachment('https://i.imgur.com/QUmbq9o.png', 'thumbnail.png'))
.setThumbnail('attachment://thumbnail.png')
.setFooter('Bot created by James (Rock)₇₇₇');
message.channel.send(embed).then(async msg => {
await msg.react('👍');
await msg.react('👎');
await msg.react('🤷');
await msg.react('🗑️');
const threshold = 1;
async function stop(result) {
collector.stop();
const newEmbed = new Discord.MessageEmbed(msg.embeds[0]);
newEmbed.title = newEmbed.title + ' [CLOSED]';
newEmbed.fields[0] = { name: 'Status', value: 'Voting is now closed.\n' + result };
newEmbed.setThumbnail('attachment://thumbnail.png');
await msg.edit(newEmbed);
msg.reactions.removeAll();
}
async function update() {
const newEmbed = new Discord.MessageEmbed(embed);
const userYes = (votes['👍'].size === 0)? '-' : [...votes['👍']];
const userNo = (votes['👎'].size === 0)? '-' : [...votes['👎']];
const userUnsure = (votes['🤷'].size === 0)? '-' : [...votes['🤷']];
newEmbed.addFields(
{ name: `User Yes (${votes['👍'].size}/${threshold})`, value: userYes, inline: true },
{ name: `User No (${votes['👎'].size}/${threshold})`, value: userNo, inline: true },
{ name: 'User Unsure', value: userUnsure, inline: true }
);
await msg.edit(newEmbed);
if (votes['👍'].size >= threshold) {
await stop('This answer is good enough to get accepted and an upvote.');
// do something
} else if (votes['👎'].size >= threshold) {
await stop('This answer is not good enough to get accepted and an upvote.');
// do something
}
}
const votes = {
'👍': new Set(),
'👎': new Set(),
'🤷': new Set(),
'🗑️': new Set()
};
update();
const collector = msg.createReactionCollector((reaction, user) => !user.bot , { dispose: true });
collector.on('collect', async (reaction, user) => {
if (['👍', '👎', '🤷', '🗑️'].includes(reaction.emoji.name)) {
const userReactions = msg.reactions.cache.filter(reaction => reaction.users.cache.has(user.id));
for (const userReaction of userReactions.values()) {
if (userReaction.emoji.name !== reaction.emoji.name || reaction.emoji.name === '🗑️') {
userReaction.users.remove(user.id);
votes[userReaction.emoji.name].delete(user);
}
}
votes[reaction.emoji.name].add(user);
} else {
reaction.remove();
}
update();
});
collector.on('remove', (reaction, user) => {
votes[reaction.emoji.name].delete(user);
update();
});
});