I have a command which allows me to go and query an API to retrieve images and then display them in discord.
Currently I come to search from my API 10 images, I want to set up a navigation system thanks to the discord reactions. Everything works but the problem is that once the user has clicked the reaction remains active. I need to delete the reaction to improve the user experience. Currently it is therefore necessary to double click which is not very practical. Here's the method that doesn't work for deletion :
const removeReaction = (m, msg, emoji) => {
try {
m.reactions.find(r => r.emoji.name == emoji).users.remove(msg.author.id);
} catch(err) {
console.log('err: ', err)
}
}
here is all my code
bot.on('message', async msg => {
if (msg.content.startsWith('!test')) {
const args = msg.content.slice('!test').split(' ')
console.log('args :', args[1])
axios(`https://json-api.example.com/posts?tags=${args[1]}&limit=10`, {
method: 'GET',
})
.then((response) => {
if (response.status === 500) {
msg.channel.send('error')
} else {
if (response.data.posts.length === 0) {
msg.channel.send(`No result for ${args[1]}`)
}
if (response.data.posts.length > 0) {
const resultsLength = response.data.posts.length
const options = {
limit: 60000,
min: 1,
max: resultsLength - 1,
page: 1
}
const pages = []
response.data.posts.map((i, index) => {
pages.push({
"title": `Result page number ${index} for ${args[1]}`,
"url": `${response.data.posts[index].sample_url}`,
"color": 43333,
"footer": {
"icon_url": "https://cdn.discordapp.com/app-icons/708760465999790244/228b2993e942a361518b557ee4511b26.png?size=32",
"text": "Cool footer"
},
"image": {
"url": `${response.data.posts[index].url}`
},
"fields": [
{
"name": "tags",
"value": `${response.data.posts[index].tags[0] || '/'}, ${response.data.posts[index].tags[1] || '/'}, ${response.data.posts[index].tags[2] || '/'}, ${response.data.posts[index].tags[3] || '/'}`
}
]
})
})
const m = msg.channel.send({ embed: pages[options.page] }).then((el) => {
el.react('⬅️')
el.react('➡️')
el.react('🗑️')
})
const filter = (reaction, user) => {
return ['⬅️', '➡️', '🗑️'].includes(reaction.emoji.name) && user.id == msg.author.id
}
const awaitReactions = (msg, m, options, filter) => {
const { min, max, page, limit } = options
m.awaitReactions(filter, { max: 1, time: limit, errors: ['time'] })
.then((collected) => {
const reaction = collected.first()
const removeReaction = (m, msg, emoji) => {
try { m.reactions.find(r => r.emoji.name == emoji).users.remove(msg.author.id); } catch(err) { console.log('err: ', err) }
}
if (reaction.emoji.name === '⬅️') {
removeReaction(m, msg, '⬅️')
if (page != min) {
page = page - 1
m.edit({ embed: pages[page] })
}
awaitReactions(msg, m, options, filter)
}
else if (reaction.emoji.name === '➡️') {
removeReaction(m, msg, '➡️');
if (page != max) {
page = page + 1
m.edit({ embed: pages[page] })
}
awaitReactions(msg, m, options, filter);
}
else if (reaction.emoji.name === '➡️') {
removeReaction(m, msg, '➡️');
if (page != max) {
page = page + 1
m.edit({ embed: pages[page] })
}
awaitReactions(msg, m, options, filter);
}
else if (reaction.emoji.name === '🗑️') {
return m.delete()
}
else {
awaitReactions(msg, m, options, filter)
}
}).catch((err) => { console.log('err: ', err) })
}
awaitReactions(msg, m, options, filter)
Thanks in advance for your help
so, after much research I finally found. I share the answer here.
With the 12+ version of discord.js package a lot has evolved including my problem:
m.reactions.find(r => r.emoji.name == emoji).users.remove(msg.author.id);
must become :
m.reactions.cache.find(r => r.emoji.name == emoji).users.remove(msg.author);
I have made this code a while ago.
It is for an older version of Discord.js but according to docs it should still work.
Basically what you want to do is to use Discord.js createReactionCollector function after you send the picture, then inside this.reactionCollector.on('collect', (reaction, reactionCollector) => {//do your work here}) you can change the picture then call reaction.remove(user); which immediately removes the reaction for that user.
Here's my code:
Note: I have made a class for this command so it might look a bit different for you.
Also in my version, only the user who initially called the command can change the picture. So you will need to modify that part if you want to let anyone change the picture. (It might be a pretty bad idea if your bot is in a large server.)
this.channel.send({embed}).then(msg => {
this.message = msg;
this.message.react('⬅️').then(x => {
this.message.react('➡️');
})
const filter = (reaction, user) => {
return (reaction.emoji.name === '➡️' || reaction.emoji.name === '⬅️') && user.id === this.user.id;
};
this.reactionCollector = this.message.createReactionCollector(filter, { time: 600000 });
this.reactionCollector.on('collect', (reaction, reactionCollector) => {
if(reaction.emoji.name === '➡️') {
if(this.index + 1 < result.length) {
this.index++;
var embed = this.makeEmbed(result[this.index].img);
this.message.edit({embed});
}
}
else if(reaction.emoji.name === '⬅️') {
if(this.index - 1 > -1) {
this.index--;
var embed = this.makeEmbed(result[this.index].img);
this.message.edit({embed});
}
}
reaction.remove(this.user);
});
});
Related
While we are create list and list columns using pnp/sp. Why some of the columns are not created in one go? We need to run again then it will created. Please help me to resolve this issue.
I have used SPFx solution with react and "pnp/sp". Please refer below configuration for the same.
node 14.15.4
#microsoft/generator-sharepoint#1.14.0
gulp#4.0.2
npm#6.14.10
yo#4.3.0
#pnp/sp#2.11.0
Please refer below code snippet for the same.
import { sp } from '#pnp/sp';
import "#pnp/sp/webs";
import "#pnp/sp/lists";
import "#pnp/sp/items";
import "#pnp/sp/fields";
import "#pnp/sp/views";
import { UrlFieldFormatType } from "#pnp/sp/fields";
export const trainingsList = async () => {
try {
const listEnsureResult = await sp.web.lists.ensure("Trainings", "Trainings", 100, false, { Hidden: false });
if (listEnsureResult.created) {
return listEnsureResult;
}
} catch (error) {
console.log("Error in Trainings List", error);
}
}
export const trainingsFields = async () => {
const courseList = await sp.web.lists.getByTitle("Course")();
try {
await sp.web.lists.getByTitle("Trainings").fields.select("*").get().then(async (res) => {
let listData = [];
res.forEach(ele => {
listData.push(ele.InternalName);
});
if (listData.indexOf("VideoUrl") == -1) {
setTimeout(() => {
sp.web.lists.getByTitle("Trainings").fields.addUrl("VideoUrl", UrlFieldFormatType.Hyperlink).then(() => {
sp.web.lists.getByTitle("Trainings").defaultView.fields.add("VideoUrl");
})
}, 5000);
}
if (listData.indexOf("Walkthrough") == -1) {
sp.web.lists.getByTitle("Trainings").fields.addMultilineText("Walkthrough").then(() => {
sp.web.lists.getByTitle("Trainings").defaultView.fields.add("Walkthrough");
});
}
if (listData.indexOf("Status") == -1) {
sp.web.lists.getByTitle("Trainings").fields.addBoolean("Status").then(() => {
sp.web.lists.getByTitle("Trainings").defaultView.fields.add("Status");
});
}
if (listData.indexOf("TrainingDuration") == -1) {
sp.web.lists.getByTitle("Trainings").fields.addNumber("TrainingDuration").then(() => {
sp.web.lists.getByTitle("Trainings").defaultView.fields.add("TrainingDuration");
});
}
if (listData.indexOf("Course") == -1) {
sp.web.lists.getByTitle("Trainings").fields.addLookup("Course", courseList.Id, "Title").then(() => {
sp.web.lists.getByTitle("Trainings").defaultView.fields.add("Course");
});
}
await sp.web.lists.getByTitle("Trainings").fields.select("*").get().then(async (resListData) => {
let listColumnData = [];
resListData.forEach(ele => {
listColumnData.push(ele.InternalName);
});
console.log("listColumnData...", listColumnData);
if (listColumnData.indexOf("VideoUrl") != -1 && listColumnData.indexOf("Walkthrough") != -1 && listColumnData.indexOf("Status") != -1 && listColumnData.indexOf("TrainingDuration") != -1 && listColumnData.indexOf("Course") != -1) {
console.log("Inside IF.....");
}
else {
console.log("Inside ELSE.....");
await trainingsFields();
}
}).catch((err) => {
console.log("Error in trainings fields creation", err);
});
console.log("Trainings fields created successfully!");
}).catch((err) => {
console.log("Error in Trainings fields", err);
});
} catch (error) {
console.log("Error in Trainings fields", error);
}
}
You need to use await with add functions, otherwise your function may terminate before all fields are created. In general, I would avoid mixing await with .then for simplicity. Consider something like:
if (listData.indexOf("Walkthrough") == -1) {
await sp.web.lists.getByTitle("Trainings").fields.addMultilineText("Walkthrough");
await sp.web.lists.getByTitle("Trainings").defaultView.fields.add("Walkthrough");
}
if (listData.indexOf("Status") == -1) {
await sp.web.lists.getByTitle("Trainings").fields.addBoolean("Status");
await sp.web.lists.getByTitle("Trainings").defaultView.fields.add("Status");
}
Normally, you should get a linter (or compiler) error or warning when you try to call an asynchronous function without waiting for it to terminate in some way (i.e. without await or .then or .catch), i.e. "shooting in the air", but this may depend on the linter settings, as far as I remember.
TLDR: replace all foo().then(result => ..) with const result = await foo() and you should be fine.
Another thing, there may be a much more efficient way to create fields not just in "one go" but in "one call", if you used batches, for example.
The flip camera doesn't work on devices where they have more than 2 video inputs. In the first load, the video appears but when the flip camera button is clicked the application throws an error.
Expected behavior:
The camera should be flipped (environment)
Actual behavior:
It throws the following error:
error : call to getusermedia failed domexception could not start video source
Software versions:
Browser(s): Chrome
Operating System: Android (devices that I'm checking & it's not working eq. Samsung M31, Redmi note 11 T, One Plus 7T)
twilio-video.js: 2.24.0
Third-party libraries (e.g., Angular, nodejs, etc.):
Code used to start twilio stream
async startTwilioStream(twilioToken: string, localVideo: ElementRef, remoteVideo: ElementRef): Promise<void> {
console.log('startTwilioStream');
this.localVideoElement = localVideo;
this.remoteVideoElement = remoteVideo;
await this.startLocalVideo(this.localVideoElement);
this.connectOptions = {
video: false,
audio: false,
tracks: [this.localAudioTrack, this.localVideoTrack],
audioConstraints: {
mandatory: {
googAutoGainControl: false,
},
},
region: 'in1',
preferredAudioCodecs: ['opus'],
preferredVideoCodecs: ['H264'],
};
connect(twilioToken, this.connectOptions).then((twilioRoom: any) => {
console.log('twilioRoom.localParticipant ================== ', twilioRoom.localParticipant);
setTimeout(() => {
if (this.remoteVideoElement?.nativeElement) {
this.remoteVideoElement.nativeElement.muted = false;
}
}, 5000);
this.twilioRoom = twilioRoom;
console.log('this.twilioRoom vvvv', this.twilioRoom);
twilioRoom.localParticipant.setNetworkQualityConfiguration({
local: 2,
remote: 1,
});
// flip.addEventListener('change', this.updateVideoDevice);
twilioRoom.on('participantConnected', participant => {
console.log('participant Connected===============', participant);
participant.tracks.forEach((publication) => {
console.log('publication', publication);
if (publication.isSubscribed) {
const track = publication.track;
this.attachTracks([track]);
}
});
this.twilioRoom = twilioRoom;
});
twilioRoom.on('participantDisconnected', participant => {
console.log('participantDisconnected', participant);
console.log('SOME PARTICIPANT DISCONNECTED');
if ((participant.identity === 'agent-screen-share' && this.serviceUserType !== 'agent') || (participant.identity === 'consumer-screen-share' && this.serviceUserType !== 'consumer')) {
this.changeDetectionEmitter.emit('remoteScreenShareStopped');
this.isRemoteScreenShareOn = false;
} else if (participant.identity !== 'agent-screen-share' && participant.identity !== 'consumer-screen-share') {
console.log('real participant dced');
this.remoteMediaStream = null;
this.detachTracks(participant);
this.isRemoteVideoOn = false;
}
this.twilioRoom = twilioRoom;
});
twilioRoom.participants.forEach((participant) => {
participant.tracks.forEach((publication) => {
if (publication.track) {
const track = publication.track;
this.attachTracks([track]);
}
});
participant.on('trackSubscribed', (track) => {
console.log('trackSubscribed', track);
this.attachTracks([track]);
});
this.twilioRoom = twilioRoom;
});
twilioRoom.on('trackAdded', (track, participant) => {
console.log('trackAdded', track, participant);
this.attachTracks([track]);
this.twilioRoom = twilioRoom;
});
// When a Participant adds a Track, attach it to the DOM.
twilioRoom.on('trackSubscribed', (track, err, participant) => {
console.log('trackSubscribed', track);
this.sendLoaderStatus('ringing');
if ((participant.identity === 'agent-screen-share' && this.serviceUserType !== 'agent') || (participant.identity === 'consumer-screen-share' && this.serviceUserType !== 'consumer')) {
this.attachScreenShareTrack([track]);
} else if (participant.identity === 'agent-screen-share' || participant.identity === 'consumer-screen-share') {
} else {
this.attachTracks([track]);
}
this.twilioRoom = twilioRoom;
});
// When a Participant removes a Track, detach it from the DOM.
twilioRoom.on('trackRemoved', (track, participant) => {
console.log('trackRemoved', track);
this.detachTracks([track]);
this.twilioRoom = twilioRoom;
});
}, err => {
});
}
Start local video and local audio track
async startLocalVideo(localVideo: ElementRef, deviceId = 'user'): Promise<void> {
this.localVideoElement = localVideo;
const localAudioTrack = await createLocalAudioTrack({
audio: true
});
const localVideoTrack = await createLocalVideoTrack({
facingMode: deviceId
});
this.localAudioTrack = localAudioTrack;
this.localVideoTrack = localVideoTrack;
if (!this.localAudioTrack) {
alert('Audio source not found, do you hava a mic connected ?');
}
if (!this.localVideoTrack) {
alert('Video source not found, do you hava a videocam connected ?');
}
console.log('this.localVideoTrack to check', this.localVideoTrack);
this.localDisplayMediaStream = new MediaStream();
console.log('this.localVideoTrack.mediaStreamTrack to check', this.localVideoTrack.mediaStreamTrack);
this.localDisplayMediaStream.addTrack(this.localVideoTrack.mediaStreamTrack);
console.log('this.localDisplayMediaStream to check', this.localDisplayMediaStream);
this.localVideoElement.nativeElement.srcObject = this.localDisplayMediaStream;
}
Flip event listener calls on the click of switch button
const flip = document.querySelector('#flip');
flip.addEventListener('click', (e) => {
if (this.facingMode == "user") {
this.facingMode = "environment";
this.twilioService.switch(this.facingMode)
} else {
this.facingMode = "user";
this.twilioService.switch(this.facingMode)
}
});
Switch camera function calls in flip event listener
async switch(facingMode) {
console.log(this.localDisplayMediaStream);
if (this.localDisplayMediaStream) {
this.localDisplayMediaStream.getTracks().forEach(track => {
track.stop();
});
if (this.twilioRoom) {
await this.twilioRoom.localParticipant.videoTracks.forEach((track: any) => {
console.log('track', track);
track.track.stop();
});
}
}
const localVideoTrack = await createLocalVideoTrack({
facingMode: facingMode
});
this.localVideoTrack = localVideoTrack;
this.localDisplayMediaStream = new MediaStream();
this.localDisplayMediaStream.addTrack(this.localVideoTrack.mediaStreamTrack);
this.localVideoElement.nativeElement.srcObject = this.localDisplayMediaStream;
}
So I made this poll command that people without admin could make polls. To prevent spam, there is a verify step for everyone without admin. But when you react to the poll to verify it, it only works for the person making the poll. And not the admin that's supposed to check if it's not spam.
So when the admin reacts nothing happens but when the person that made the poll reacts to it, it verifies the poll and sends it to the main channel.
Code is down below is someone could help! 'Appreciate it!
const {Client, Collection, GuildMember, User, MessageEmbed, Message} = require("discord.js");
const ms = require("ms");
const delay = (msec) => new Promise((resolve) => setTimeout(resolve, msec));
module.exports.run = async(client, message, args, user, reaction) => {
var woord = '!poll'
var question = args.slice(0).join(' ')
var poll = new MessageEmbed()
.setTitle(`${message.author.username} wil een poll maken.`)
.setDescription(question)
.setColor('#eb8dd8')
.setFooter(`Poll gemaakt door: `+ message.author.username)
var success = new MessageEmbed()
.setDescription(question)
.setColor('#eb8dd8')
.setFooter("Poll started door: "+ message.author.username)
if(message.content.includes(woord)) {
message.delete({timeout:0})
}
if(!message.member.roles.cache.some(r => r.name === 'Barman')) {
if(message.channel.name.includes("🙇-poll")) {
if(args[0]) {
message.delete()
message.guild.channels.create(message.author.username, { permissionOverwrites:[
{
deny: 'VIEW_CHANNEL',
id: message.guild.id
},
{
allow: 'VIEW_CHANNEL',
id: message.author.id
},
],
}).then(channel => {
channel.send(poll).then(poll => {
poll.react('✅')
.then(() => poll.react('❌'));
})
})
} else {
message.delete()
}
}
} else {
var pollChannel = client.channels.cache.get('876531134702448701')
pollChannel.send(success).then(success => {
success.react('✅')
.then(() => success.react('❌'))
})
}
client.on('messageReactionAdd', (reaction, user) => {
const deleteChannel = message.guild.channels.cache.find(channel => channel.name.toLowerCase() === user.username);
var pollChannel = client.channels.cache.get('876531134702448701')
if(reaction.emoji.name === '✅') {
if(message.guild.channels.cache.find(channel => channel.name.toLowerCase() === user.username)) {
deleteChannel.delete()
.then(channel => {
pollChannel.send(success).then(success =>{
success.react('✅')
.then(() => success.react('❌'))
})
})
}
} if(reaction.emoji.name === '❌') {
if(message.guild.channels.cache.find(channel => channel.name.toLowerCase() === user.username)) {
deleteChannel.delete()
}
}
})
}
module.exports.help = {
name: "poll"
}
At the start of your function you can do this:
const questions = new Collection();
questions.set("What is the color of healthy grass?", "green");
questions.set("How many vowels are there in these letters: apple", "2");
const chosen = questions.randomKey()
await message.channel.send(`Please answer the question. This is to prevent spam. Make sure your spelling is correct. You have 30 seconds —> ${chosen}`)
try {
await message.channel.awaitMessages((m) => m.author.id === message.author.id && m.content.toLowerCase() === questions.get(chosen), {
time: 30000,
max: 1
})
} catch(err) {
return message.channel.send("You ran out of time to answer correctly!")
}
//code to make poll
Your awaitMessages syntax may be different on v13. You can also replace my questions, and add as many as you want!
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();
});
});
my goal is to use a command that add reactions and you can react to change the text, so when I click and the reaction 1 that's edit the original text to : 1 and same for the others... that's pretty much working, but when i'm trying to remove and re-add the reaction, that's not working I think it's because of : { max: 1 } here's my code :
bot.on('message', msg => {
if(msg.content === PREFIX + "test") {
msg.channel.send(`${msg.author.username}, exemple`).then((sentMessage) => {
setTimeout(function() {
sentMessage.react("⏪");
}, 500);
setTimeout(function() {
sentMessage.react("⬅️");
}, 1000);
setTimeout(function() {
sentMessage.react("➡️");
}, 1500);
setTimeout(function() {
sentMessage.react("⏩");
}, 2000);
let reactionFilter = (reaction, user) => {
reaction.emoji.name === '⏪' && user.id !== "bot id" && user.id == msg.author.id
}
sentMessage.awaitReactions(reactionFilter, { max: 1 }).then(() => {
sentMessage.edit(`1`)
}
)
let reactionFilter2 = (reaction, user) => {
reaction.emoji.name === '⬅️' && user.id !== "bot id" && user.id === msg.author.id
}
sentMessage.awaitReactions(reactionFilter2, { max: 1 }).then(() => {
sentMessage.edit(`2`)
}
)
let reactionFilter3 = (reaction, user) => {
reaction.emoji.name === '➡️' && user.id !== "bot id" && user.id === msg.author.id
}
sentMessage.awaitReactions(reactionFilter3, { max: 1 }).then(() => {
sentMessage.edit(`3`)
}
)
let reactionFilter4 = (reaction, user) => {
reaction.emoji.name === '⏩' && user.id !== "bot id" && user.id === msg.author.id
}
sentMessage.awaitReactions(reactionFilter4, { max: 1 }).then(() => {
sentMessage.edit(`4`)
}
)
})
}
})
Thanks !
Yes it's because of the max limit, you should prob instead be using one reaction collector instead:
const emojis = ["⏪", "⬅️", "➡️", "⏩"];
//no need to check if the user id isnt the bot
const filter = (reaction, user) => emojis.includes(reaction.emoji.name) && user.id === msg.author.id;
// adding a time is probably a better option
const collector = sentMessage.createReactionCollector(filter, { time: 5000 });
collector.on("collect", reaction => {
// array is 0 indexed so just add 1 to the index and you get the number you wanted
// might need to convert the number into a string aswell
sentMessage.edit("" + (emojis.indexOf(reaction.emoji.name) + 1));
});
Should note this doesn't do anything if a reaction is removed, which I think is best because it would be hard to decide what the text should be after it's removed