I'm new to the discordjs package and I've been digging around but for the last 2 days I'm stuck at this.
What I'm trying to achieve is "questionnaire" that will guide the user through some questions. An example would be:
User types: !askOrder
Q: Do you want red wine or white wine?
A: white wine
Q: You want white wine. Do you prefer a verdejo or a rueda?
A: rueda
Q: A rueda it's. Do you want it at room temperature or chill?
A: room temperature.
What I've is:
msg.author.send('Do you want red wine or white wine?');
msg.author.send(WineText)
.then((systemMsg) => {
systemMsg.channel.awaitMessages(response => response.content, {
max: 1,
time: 5000,
errors: ['time'],
}).then((collected) => {
systemMsg.channel.send(`You want ${collected.first().content}. Do you prefer a verdejo or a rueda?`);
collected.channel.awaitMessages(response => response.content, {
max: 1,
time: 10000
});
})then((collected2) => {
systemMsg.channel.send(`A ${collected2.first().content} it's. Do you want it at room temperature or chill? `);
}).catch((err) => {
return;
});
But it simply stops after it asks the second questions, not capturing/collecting the second answer. What is it that Im doing wrong?
Thanks a lot for your answers!
What I'd do is put all of the questions (and answers) in an array, wrap those statements in a function, and loop them over an iteration like this:
let i = 0; let questions = ['Q1', 'Q2'];
function askQuestion() {
const filter = m => m.author.id == message.author.id;
message.channel.send(questions[i]).then(() => {
message.channel.awaitMessages(filter, { max: 1, time: 120000, errors: ['time'] })
.then(async collected => {
//do stuff
message.channel.send(`answer was ${collected.first().content}`);
i++; //increase iteration
if (i === questions.length) return;
else askQuestion();
})
.catch(collected => {
//timed out
});
});
}
askQuestion();
It helps clean your code a little bit. You can change them as you see fit.
I made a bot that ask questions just like this, you can view the code here
You structured your awaitMessages events wrong:
msg.author.send('Do you want red wine or white wine?');
msg.author.send(WineText)
.then((systemMsg) => {
systemMsg.channel.awaitMessages(response => response.content, {
max: 1,
time: 5000,
errors: ['time']
}).then((collected) => {
systemMsg.channel.send(`You want ${collected.first().content}. Do you prefer a verdejo or a rueda?`);
collected.first().channel.awaitMessages(response => response.content, {
max: 1,
time: 10000
}).then((collected2) => {
systemMsg.channel.send(`A ${collected2.first().content} it's. Do you want it at room temperature or chill? `);
}).catch((err) => {
return;
});
});
});
There were 2 crucial errors you made:
(1) You sent a message to collected.channel. However, collected is a collection of messages, and doesn't have a channel property. To send it to the channel, just do collected.first().channel, since collected.first() is a message.
(2) You were missing two })s at the end of your code, because of the thens that you included and never closed.
Related
I'm making a suggest system for my bot which starts of with the user pressing a button in a channel which then prompts them to say what they want to suggest and then their suggestion gets sent to a different channel where others can vote on it. So what I want to make it do is I want it to create a createMessageComponentCollector and then register all the good/bad votes under a a period of 24h, if the suggestion got 10 good votes it's approved, otherwise it's not. Here is my code for it so far but what I'm having issue with is to start the ComponentCollector one time and then make it register the votes for each suggestion. this is the part of my code that I'm having trouble with
client.channels.cache.get("909420357285474334").send({ embeds:[embed], components:[row] })
const filter1 = m => m.customId === "Yes" && i.messageId === interaction.messageId
collector1 = interaction.channel.createMessageComponentCollector({filter: filter1, time: 86400000});
const filter2 = m => m.customId === "No" && i.messageId === interaction.messageId
collector2 = interaction.channel.createMessageComponentCollector({filter: filter2, time: 86400000});
if (interaction.customId === "Yes") {
collector1.on('collect', async i => {
client.logger("collector1 collected")
});
collector1.on('end', collected => {
client.logger(collected.size)
// if (collected.size < 10) {
// }
});
}
if (interaction.customId === "No") {
collector2.on('collect', async i => {
client.logger("collector2 collected")
});
collector2.on('end', collected => {
client.logger(collected.size)
// if (collected.size < 10) {
// }
});
}
your question is a little bit unclear but I think I get what your looking for, if you want to check if a user already has pressed a button you could add them to a Set() constructor, now you need to set a new Set() which you can define in your startup file like this:
client.alreadyPressed = new Set()
and when you've done that one possible way to do it in your suggestion code could be like this:
collector1.on('collect', async i => {
client.logger("collector1 collected")
if (client.alreadyPressed.has(interaction.user.id)) {
i.reply({ content: `You've already voted ${i.user.username}!`, ephemeral: true })
return;
} else {
i.reply({ content: `Thank you for your vote ${i.user.username}!`, ephemeral: true });
}
client.alreadyPressed.add(i.user.id)
});
collector1.on('end', collected => {
client.logger(collected.size)
// if (collected.size < 10) {
// }
});
collector2.on('collect', async i => {
client.logger("collector2 collected")
if (client.alreadyPressed.has(interaction.user.id)) {
i.reply({ content: `You've already voted ${i.user.username}!`, ephemeral: true })
return;
} else {
i.reply({ content: `Thank you for your vote ${i.user.username}!`, ephemeral: true });
}
client.alreadyPressed.add(i.user.id)
});
collector2.on('end', collected => {
client.logger(collected.size)
// if (collected.size < 10) {
// }
});
one problem with this is that they only can vote once in the whole channel which im not sure if that's what you wanted
Similar to these questions:
How to use remove event with ReactionCollector?
DiscordJS reactionCollector 'remove' is never emitted
But none of the solutions listed in either solve my problem:
//Message filter and collector
const reactionFilter = (reaction, user) => {
return ['✅', '▶️'].includes(reaction.emoji.name);
}
//Create reaction collector
const reactionCollector = sent.createReactionCollector(reactionFilter, { dispose: true, max: 1, time: 100000 });
//Message filter and collector
const messageFilter = m => m.member.id == this.player.id && m.content;
const messageCollector = sent.channel.createMessageCollector(messageFilter, { dispose: true, time: 100000 });
//Await message collector collect
messageCollector.on('collect', m => {
//Emits fine
});
//Await reaction
reactionCollector.on('collect', (reaction, user) => {
//Emits fine
});
//Await remove
reactionCollector.on('remove', (reaction, user) => {
//Never emits
});
//Await end
reactionCollector.on('end', r => {
//Emits fine
});
No matter what I do, the remove event is never emitted.
I am using Discord.js V12.5.3
I have dispose: true and it still doesn't work
The collector ends on the first reaction
The collector ends on the first reaction because you set the max to 1. For the remove event to be fired, the max must be 2 or above.
sent.createReactionCollector(reactionFilter, { dispose: true, max: 2, time: 100000 })
This can emit the remove event because the max is more than 1.
I'm making an application with Discord.js. Everything's working fine, however the bot does not collect the messages as soon as all the questions have been answered.
It does when the collector runs out of time.
How can I trigger collector.on('end' ...) once all of the questions have been answered?
const questions =[
'First Name:',
'Last Name:',
'How old are you:',
'Your email:',
'What is your time zone:',
'Where are you from:',
'What languages are you speaking:',
'Have your ever been punished on our server:',
'On which server your most active on:',
'Have you ever been in a staff team:',
'Tell us about your self:'
]
let counter = 0
const filter = m => m.author.id === message.author.id
const collector = message.author.dmChannel.createMessageCollector(filter, {
max: questions.Lenght,
time: 60000
});
message.author.send(questions[counter++]);
console.log(`1st question has been sent to ${message.author.tag}`);
collector.on('collect', (m) => {
if(counter < questions.length) {
console.log(`sending more questions to ${message.author.tag} until answer them all`);
message.author.send(questions[counter++])
}
})
collector.on('end', (collected) => {
console.log(`collected ${collected.size} messages`);
message.author.send(`Your application has been successfully sent`);
let counter = 0
collected.forEach((value) => {
console.log(questions[counter++], value.content)
})
})
You wrote max: questions.Lenght instead of max: questions.length with a lower L and th
I'm trying to make a start command for a currency bot. I'm trying to make a message collector, which asks for collecting for this certain user. And depending on the user's response, it would have different responses.
If the user replies with "yes", the bot will send "cool".
If the user replies with "no", the bot will send "not cool".
If the user doesn't reply in 15 seconds or replies something else than "yes" and "no", the bot will send "bruh".
message.channel.send("Yes or no?")
.then(function (message) {
const filter = m => m.author.id === message.author.id,;
const collector = message.channel.createMessageCollector(filter, { max: 1, time: 15000, errors: ['time'] })
collector.on('collect', m => {
if (m == "yes") {
message.channel.send("cool")
} else if (m == "no") {
message.channel.send("not cool")
} else {
message.channel.send("bruh")
}
})
})
}
But whatever I say, the bot doesn't respond at all, even in the console. Thanks for any help!
The message parameter of the MessageCollector returns a message object. What you are currently attempting to do is comparing an object (The parameter m that represents the object) with a string - Something that can obviously not be done.
If you would like to compare the collected message to a certain string, you would have to compare its content property, that obviously returns the content of the collected message.
Final Code:
message.channel.send("Yes or no?")
.then(function (message) {
const filter = m => m.author.id === message.author.id,;
const collector = message.channel.createMessageCollector(filter, { max: 1, time: 15000, errors: ['time'] })
collector.on('collect', m => {
if (m.content == "yes") {
message.channel.send("cool")
} else if (m.content == "no") {
message.channel.send("not cool")
} else {
message.channel.send("bruh")
}
})
})
}
I am trying to make a poll command for a discord bot in which the user chooses a number of options in the first command (ie '!poll 4') and then chooses the questions and the options. I am having some issues getting the bot to wait for a response before it moves on to the next option in the loop. When I try and use await in the loop it says I cannot use await because it's not an async function, but it is an async function I think. I am very inexperienced with this so I am sure it is a simple error or probably multiple. If anyone can give me advice on a way to make the loop work as intended and ask for each option I would appreciate it. Also is there a way to add if statements to do addFields to an embed? Here is my code:
const Discord = module.require('discord.js');
module.exports = {
name: 'poll',
async execute(message, args) {
function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0) }
if(isNumber(args[1])){
if(args[1]<2) return message.channel.send('Please choose a higher number of options for the poll :)');
if(args[1]>10) return message.channel.send('Please choose a lower number of options for the poll :)');
const filter = response => {
if(!response.author.bot) return response;
};
var question;
var options;
message.channel.send('What question would you like to ask?').then(() => {
message.channel.awaitMessages(filter, { max: 1, time: 15000})
.then(collected => {
question = `${collected.first()}?`;
message.channel.send('Question: ' + question);
for (var i = 0; i < args[1]; i++) {
message.channel.send('What is option ' + (i + 1) + '?').then(() => {
message.channel.awaitMessages(filter, { max: 1, time: 15000})
.then(collected => {
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
})
.catch(collected => {
message.channel.send('Poll has timed out.');
});
})
}
})
.catch(collected => {
message.channel.send('Poll has timed out.');
});
const embed = new Discord.MessageEmbed()
.setColor(3447003)
.setTitle(question)
.setDescription('choose an option')
/*
if (options[0]) .addField('1️⃣:' + option[0])
if (options[1]) .addField('2️⃣:' + option[1])
if (options[2]) .addField('3️⃣:' + option[2])
if (options[3]) .addField('4️⃣:' + option[3])
if (options[4]) .addField('5️⃣:' + option[4])
if (options[5]) .addField('6️⃣:' + option[5])
if (options[6]) .addField('7️⃣:' + option[6])
if (options[7]) .addField('8️⃣:' + option[7])
if (options[8]) .addField('9️⃣:' + option[8])
if (options[9]) .addField('🔟:' + option[9])
*/
message.channel.send(embed).then(embedMessage => {
if (options[0]) embedMessage.react('1️⃣');
if (options[1]) embedMessage.react('2️⃣');
if (options[2]) embedMessage.react('3️⃣');
if (options[3]) embedMessage.react('4️⃣');
if (options[4]) embedMessage.react('5️⃣');
if (options[5]) embedMessage.react('6️⃣');
if (options[6]) embedMessage.react('7️⃣');
if (options[7]) embedMessage.react('8️⃣');
if (options[8]) embedMessage.react('9️⃣');
if (options[9]) embedMessage.react('🔟');
});
});
}
}
}
Since you say you are trying to use await in your loop, let me take the function it is contained in out from your snippet, format it a little, and try to do some explaining. Disclaimer: I am no expert, so I am learning as well.
.then(collected => {
question = `${collected.first()}?`;
message.channel.send(`Question: ${question}`);
for (var i = 0; i < args[1]; i++) {
message.channel.send(
`What is option ${i + 1}?`
).then(() => {
message.channel.awaitMessages(filter, {
"max": 1,
"time": 15000,
}).then(collected => {
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
}).catch(collected => {
message.channel.send("Poll has timed out.");
});
});
}
});
But before that, as the first inner .then() still returns a Promise, you can chain the second inner .then() in the outer scope to avoid nesting them too deep, and leave a single .catch() at the end. On that note, it would probably be more accurate to call the catch's parameter something like error. So here's the new snippet:
.then(collected => {
question = `${collected.first()}?`;
message.channel.send('Question: ' + question);
for (var i = 0; i < args[1]; i++) {
message.channel.send(
`What is option ${i + 1}?`
).then(() => {
message.channel.awaitMessages(filter, {
"max": 1,
"time": 15000,
});
}).then(collected => { // Change .then() chaining
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
}).catch(error => { // Change parameter name
message.channel.send("Poll has timed out.");
});
}
})
What's happening now is that each iteration is running one after the other immediately. You .send() a whole bunch of messages which each return a Promise, and off that Promise, you pass a callback function to .then() which runs once each Promise resolves into a Message. That callback implicitly returns the result of .awaitMessages(), which is also a promise, and once that resolves, the next callback in the next .then() runs with the value of whatever the previous promise resolved to passed in as an argument, and so on.
Alright, so you want to the entire Promise chain to finish processing and resolve before proceeding to the next iteration, right? You can use the await keyword to suspend progress in the relevant anonymous function, until its associated promise-based operation either resolves or rejects. The catch is that that function has to be marked with the async keyword, and in your code, that is not actually the case, you are just making use of Promises and callback functions (regarding "but it is an async function I think"). So, let's add both the aforementioned keywords:
.then(async collected => { // Mark as async
question = `${collected.first()}?`;
message.channel.send('Question: ' + question);
for (var i = 0; i < args[1]; i++) {
await message.channel.send( // Wait for this entire Promise chain to resolve before proceeding
`What is option ${i + 1}?`
).then(() => {
message.channel.awaitMessages(filter, {
"max": 1,
"time": 15000,
});
}).then(collected => {
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
}).catch(error => {
message.channel.send("Poll has timed out.");
});
}
})
That should cause your desired behaviour, though my edits may have syntax errors as I have not ran it myself. If I got something wrong, please do comment.
You can read more here:
Using async-await
Using Promises