bot doesn't collect the messages ASAP - discord.js

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

Related

Discord.js V.13 collect once with Collector

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

Adding buttons to an application sent to specified applicationChannelId

Yes I know TLDR but I would appreciate the help
Ok I have this wall of code below I use for applications
It uses a button to start the application (asking questions)
and after the application is filled in it sends that application to a specified applicationChannelId
is there any way I could add buttons to the application that was sent to the applicationChannelId to accept or deny members?
Accept would add a role by id and sends a message to the original applicant
//Not the code I tried using just an example for what the Accept button would do
//
let teamRole = message.guild.roles.cache.find(role => role.id == "761996603434598460")
member.roles.add(teamRole)
member.send("You have been accepted into the team")
Deny would send a message to the original applicant
I have tried doing this for the past few days but just can't get it to work it either does nothing or breaks everything
I have removed some info from the code below to make it shorter
Code for my button I already use to start the application for an applicant
client.on("message", async (message) => {
const reqEmbed = {
color: 0xed5181,
title: 'Blind Spot Team Requirements',
url: 'link',
author: {
name: '',
icon_url: '',
url: '',
},
description: '',
thumbnail: {
url: '',
},
fields: [{
"name": `Read through these Requirements`,
"value": `- requirments will go here`,
}, ],
image: {
url: '',
},
footer: {
text: 'Blind Spot est 2019',
icon_url: 'link',
},
};
//
// Don't reply to bots
let admins = ['741483726688747541', '741483726688747541'];
if (message.content.startsWith(`#blindspot`)) {
message.delete();
const amount = message.content.split(" ")[1];
if (!admins.includes(message.author.id)) {
message.reply("You do not have permission to do that!");
return;
}
// Perform raw API request and send a message with a button,
// since it isn't supported natively in discord.js v12
client.api.channels(message.channel.id).messages.post({
data: {
embeds: [reqEmbed],
components: [{
type: 1,
components: [{
type: 2,
style: 4,
label: "Apply",
// Our button id, we can use that later to identify,
// that the user has clicked this specific button
custom_id: "send_application"
}]
}]
}
});
}
});
Rest of code that handles questions and sends application to applicationChannelId when complete
// Channel id where the application will be sent
const applicationChannelId = "652099170835890177";
// Our questions the bot will ask the user
const questions = ["These are the questions but have deleted them to make this shorter",];
// Function that will ask a GuildMember a question and returns a reply
async function askQuestion(member, question) {
const message = await member.send(question);
const reply = await message.channel.awaitMessages((m) => {
return m.author.id === member.id;
}, {
time: 5 * 60000,
max: 1
});
return reply.first();
}
client.ws.on("INTERACTION_CREATE", async (interaction) => {
// If component type is a button
if (interaction.data.component_type === 2) {
const guildId = interaction.guild_id;
const userId = interaction.member.user.id;
const buttonId = interaction.data.custom_id;
const member = client.guilds.resolve(guildId).member(userId);
if (buttonId == "send_application") {
// Reply to an interaction, so we don't get "This interaction failed" error
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 4,
data: {
content: "I have started the application process in your DM's.",
flags: 64 // make the message ephemeral
}
}
});
try {
// Create our application, we will fill it later
const application = new MessageEmbed()
.setTitle("New Application")
.setDescription(`This application was submitted by ${member}/${member.user.tag}`)
.setFooter("If the #Username doesn't appear please go to general chat and scroll through the member list")
.setColor("#ED4245");
const cancel = () => member.send("Your application has been canceled.\n**If you would like to start your application again Click on the Apply button in** <#657393981851697153>");
// Ask the user if he wants to continue
const reply = await askQuestion(member, "Please fill in this form so we can proceed with your tryout.\n" + "**Type `yes` to continue or type `cancel` to cancel.**");
// If not cancel the process
if (reply.content.toLowerCase() != "yes") {
cancel();
return;
}
// Ask the user questions one by one and add them to application
for (const question of questions) {
const reply = await askQuestion(member, question);
// The user can cancel the process anytime he wants
if (reply.content.toLowerCase() == "cancel") {
cancel();
return;
}
application.addField(question, reply);
}
await askQuestion(member, "Would you like to submit your Application?\n" + "**Type `yes` to get your Application submitted and reviewed by Staff Members.**");
// If not cancel the process
if (reply.content.toLowerCase() != "yes") {
cancel();
return;
}
// Send the filled application to the application channel
client.channels.cache.get(applicationChannelId).send(application);
} catch {
// If the user took too long to respond an error will be thrown,
// we can handle that case here.
member.send("You took too long to respond or Something went wrong, Please contact a Staff member\n" + "The process was canceled.");
}
}
}
});
at this point I just feel like not even doing this and keep it as is because its driving me insane
You very well can! just send it as you send your normal buttons!
const {
MessageButton,
MessageActionRow
} = require("discord.js"),
const denybtn = new MessageButton()
.setStyle('DANGER')
.setEmoji('❌')
.setCustomId('deny')
const acceptbtn = new MessageButton()
.setStyle('SUCCESS')
.setEmoji('✔')
.setCustomId('accept')
client.channels.cache.get(applicationChannelId).send({
embeds: [application],
components: [new MessageActionRow().addComponents["acceptbtn", "denybtn"]]
});
const collector = msg.createMessageComponentCollector({
time: 3600000,
errors: ["time"],
});
await collector.on("collect", async (r) => {
if (r.user.id !== message.author.id)
return r.reply({
content: "You may not accept/ deny this application",
ephemeral: true,
});
if (r.customId === "acceptbtn") {
let teamRole = message.guild.roles.cache.find(role => role.id == "761996603434598460")
member.roles.add(teamRole)
member.send("You have been accepted into the team")
}
if (r.customId === "denybtn") {
member.send("You have been rejected");
}
});
NOTE: Please be mindful that since your question lacks the functions / method definitions you are using I have used discord.js v13's methods, you may update your discord.js version and the code will work as intended ALTHOUGH the component collector's functions have been directly copy pasted from your question and in some instances such as member#send member is not defined, so please take this as an example and I urge you to write the code instead of copy pasting directly!

How do i keep taking inputs from multiple users until a specific user says a particular keyword (discord.js)?

So I've been trying to make an auction bot on discord. I've figured out how to handle a single item, but I'm having issues regarding bundles.
My goal - Let the user info any/all items when auctioning a bundle till he says the keyword 'done'. The bot stores the url of the first message (embed sent by the bot) and then does nothing till the user says done. The two users are 1. the user himself, 2. the bot who's item(s) he wants to auction.
I tried using the message collector but the bot straight up ignores it. After searching around on the web and trying to adapt to my situation, the closest thing i found is using async/await with awaitMessages while inside a for loop. After that failed too, i tried to use a chain of then() with the awaitMessages. Here's my code snippet. Any help is appreciated.
let flg =0
if(arguments[3] === 'bundle'){
message.channel.send('Say `done` when you have info-ed all your pokemons')
await message.channel.awaitMessages(m => m.author.id === '666956518511345684' || m.author.id === message.author.id, {time : 30000, errors : ['time']}).then(col1 => {
if (col1.first().author.id === '666956518511345684')
details.poke = col1.first().url
}).then(col2 => {
if (col2.first().author.id === message.author.id)
while(flg = 0){
if (col2.first().content.toLowerCase() === 'done')
flg = 1
}
})
Have the collector ignore bot messages, take multiple inputs and check for message content. Once time runs out, or the max input has been reached, the collection of messages will be returned.
if(arguments[3] === 'bundle'){
message.channel.send('Say `done` when you have info-ed all your pokemons')
const filter = m => {
return !m.user.bot && ['666956518511345684', message.author.id].includes(m.author.id) && m.content.toLowerCase() === 'done';
}
message.channel.awaitMessages(filter, {
// Take a max of 5 inputs for example
max: 5
time: 30000,
errors: ['time']
}).then(collected => {
// Your code
})
}
Documentation on TextChannel#awaitMessages()
I tried message collector once again and i made it work somehow. Here's how :
let i = 0
let filter = m => {m.author.id === '<Bot's id>' || m.author.id === message.author.id}
let collector = message.channel.createMessageCollector(filter, {time : 30000})
collector.on('collect', m => {
if (m.author.id === '<bot's id>' && i === 0){
details.poke = m.url
i = 1
}
if (m.author.id === message.author.id && m.content.toLowerCase() === 'done'){
collector.stop()
}
})
collector.on('end', col => { //code here })
The variable i is a flag, since i only want the url of the first message sent by the bot. It can be removed/more flags can be added up to your needs/requirements.
Lemme know if you need anymore help regarding this.

How do I make a message collector in discord.js to have multiple responses according to the message the user sent?

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")
}
})
})
}

DiscordJs cascade of awaitMessages

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.

Resources