Discord JS : Display several modals with one interaction - javascript

I create a discord bot that when a user enters the /play command displays a modal (thanks to discord-modals).
On this one there is a question with necessarily an input to answer it.
If the answer is wrong the interaction returns a message (it works perfectly).
If the answer is correct, a second modal is displayed directly with another question.
However, it is impossible to display the second modal if the answer to the first question is correct.
So here is my code:
// Importing Modals and Text Inputs
const { Modal, showModal, TextInputComponent } = require("discord-modals");
module.exports = {
command: "play",
name: "Play",
description: "Start the game",
category: "event",
slash: true,
callback: async ({ user, interaction, client }) => {
// The list of questions and answers that will be displayed in the modals
questions = { [0]: "What is the name of the event ?", [1]: "How are you", [2]: "Who am I ?", [3]: "What's your country ?", [4]: "What's our group's name ?", [5]: "5 + 5 = ?", [6]: "3 + 3 = ?" };
answers = { [0]: "joker event", [1]: "fine", [2]: "joker", [3]: "france", [4]: "partouche", [5]: "10", [6]: "6" };
// A simple function that will display the questions randomly
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
var rand1 = getRandomInt(5);
var rand2 = getRandomInt(5);
// My two modals
const modal = new Modal()
.setCustomId(`modal_1`)
.setTitle(`Question 1`)
.addComponents(new TextInputComponent().setCustomId(`input_1`).setLabel(`${questions[rand1]}`).setStyle("SHORT").setPlaceholder("Write your answer").setRequired(true));
const modal2 = new Modal()
.setCustomId(`modal_2`)
.setTitle(`Question 2`)
.addComponents(new TextInputComponent().setCustomId(`input_2`).setLabel(`${questions[rand2]}`).setStyle("SHORT").setPlaceholder("Write your answer").setRequired(true));
// A function that displays the modal and when answering, compares the answer entered by the user with the answer to the question
function showModals(actualModal) {
showModal(actualModal, {
client: client,
interaction: interaction,
});
client.on("modalSubmit", async (modal_answered) => {
if (modal_answered.customId === `modal_1`) {
var answer = modal_answered.getTextInputValue(`input_1`);
if (answer.toLowerCase() == answers[rand1].toLowerCase()) {
await modal_answered.reply({
content: showModals(modal2),
ephemeral: true,
});
} else {
return await modal_answered.reply({
content: `Sorry ${interaction.user}, you have answered incorrectly.`,
ephemeral: true,
});
}
}
if (modal_answered.customId === `modal_2`) {
var answer = modal_answered.getTextInputValue(`input_2`);
if (answer.toLowerCase() == answers[rand2].toLowerCase()) {
await modal_answered.reply({
content: 'You won !',
ephemeral: true,
});
} else {
return await modal_answered.reply({
content: `Sorry ${interaction.user}, you have answered incorrectly.`,
ephemeral: true,
});
}
}
});
}
showModals(modal);
},
};
I then have two errors that I can't correct:
An error occurred when showing a modal. DiscordAPIError: Interaction has already been acknowledged.
and
Uncaught DiscordAPIError DiscordAPIError: Cannot send an empty message
Thank you for your help, if you have any questions don't hesitate

Related

verify button discord.js v14

I am trying to create a verify button but i cant get it to work, im very new to discord.js so the code is very messy. i am using discord.js v14 btw
I want the verify button to give the person who clicks it the #verified role (1009826361248055438) and to send a message afterwards so they see they have been verified.
const {
EmbedBuilder,
ApplicationCommandType,
ActionRowBuilder,
ButtonBuilder,
Message,
Emoji,
InviteTargetType,
} = require("discord.js");
module.exports = {
name: "sendverify",
description: "send the verify",
cooldown: 3000,
type: ApplicationCommandType.ChatInput,
default_member_permissions: "ManageRoles", // permission required
options: [
{
name: "channel",
description: "the channel to send it to.",
type: 7,
required: true,
},
],
run: async (client, interaction) => {
/** Get the buttons
* #param {Boolean} toggle
* #param {string}[choice = null] choice
*/
const getButtons = (toggle = false, choice) => {
const row3 = new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setLabel("Verify")
.setCustomId("verify")
.setStyle(
toggle == true && choice == "green" ? "Secondary" : "Success"
)
.setDisabled(toggle)
.setEmoji("1009822176830034010")
);
return row3;
};
const channel = interaction.options.get("channel").channel;
const embed1 = new EmbedBuilder()
.setTitle("Accepting the rules")
.setDescription(
"**Please read the rules before clicking the verify button!**"
)
.setColor(0x39f077)
.setFooter({
text: "By clicking verify you have agreed to the rules, and face the consequences if you don't follow them.",
});
return (
channel.send({ embeds: [embed1], components: [getButtons()] }),
interaction.reply({ content: "msg sent.", ephemeral: true })
);
},
};

How can i add Error Message in Discord.js?

I Create a Truth and dare Bot. My Prefix is + Now I want to add an error message to it. I have two variables "t" "d" If anyone types +something which does not match my variable to send a Message that "Invalid Command +help for Help" Can you guys help me?
const Discord = require('discord.js');
const client = new Discord.Client();
const keepAlive = require("./server")
const prefix = "+";
// ======== Ready Log ========
client.on ("ready", () => {
console.log('The Bot Is Ready!');
client.user.setPresence({
status: 'ONLINE', // Can Be ONLINE, DND, IDLE, INVISBLE
activity: {
name: 'Truth Or Dare | +help',
type: 'PLAYING', // Can Be WHATCHING, LISTENING
}
})
});
// ======== Code ========
client.on('message', message => {
const help = new Discord.MessageEmbed()
.setColor('#72dfa3')
.setTitle(`Truth Or Dare`)
.addFields(
{ name: '``+help``', value: 'For help'},
{ name: '``+t``', value: 'For Truth'},
{ name: '``+d``', value: 'For Your Dare'},
{ name: '``Created By``', value: 'AlpHa Coder [Labib Khan]'},
)
.setTimestamp()
.setFooter(`${message.author.username}`, message.author.displayAvatarURL());
if (message.content === `${prefix}help`) {
message.channel.send(help);
}
});
// ========= Truth =========
client.on('message', message => {
const t = [
"If you could be invisible, what is the first thing you would do?",
"What's the strangest dream you've ever had?",
"What are the top three things you look for in a boyfriend/girlfriend?",
"What is your worst habit?",
"How many stuffed animals do you own?",
"What is your biggest insecurity?"
]
const truth = t[Math.floor(Math.random() * t.length)];
if (message.content === `${prefix}t`) {
message.channel.send(truth);
}
});
// ========= Dare =========
client.on('message', message => {
const d = [
"Do a free-style rap for the next minute.",
"Let another person post a status on your behalf.",
"Hand over your phone to another player who can send a single text saying anything they want to anyone they want.",
"Let the other players go through your phone for one minute.",
"Smell another player's armpit",
"Smell another player's barefoot.",
"Tell everyone your honest opinion of the person who sent this command."
]
const dare = d[Math.floor(Math.random() * d.length)];
if (message.content === `${prefix}d`) {
message.channel.send(dare);
}
});
const token = process.env.TOKEN;
keepAlive()
client.login(token);
Please explain clearly so that I can understand. Advance Thank you
Don't use separate message event handlers, use one. You can take advantage of that by using if else chain. You are trying to match the command through the chain, if no match was found, in else (meaning every previous check in the chain failed) you reply to the user by saying:
"Invalid command, type +help for help.".
Also check for the prefix at the beginning. If there is no prefix, return from the function. That way you don't have to write it to the if statements when matching the message content.
// Array of possible truth replies
const t = [
"If you could be invisible, what is the first thing you would do?",
"What's the strangest dream you've ever had?",
"What are the top three things you look for in a boyfriend/girlfriend?",
"What is your worst habit?",
"How many stuffed animals do you own?",
"What is your biggest insecurity?"
];
// Array of possible dare replies
const d = [
"Do a free-style rap for the next minute.",
"Let another person post a status on your behalf.",
"Hand over your phone to another player who can send a single text saying anything they want to anyone they want.",
"Let the other players go through your phone for one minute.",
"Smell another player's armpit",
"Smell another player's barefoot.",
"Tell everyone your honest opinion of the person who sent this command."
];
// Handle all commands here
client.on('message', message => {
// Don't reply to itself
if (message.author.id === client.user.id) return;
// If there is no + (prefix) at the beginning of the message, exit function
if (!message.content.startsWith(prefix)) return;
// Remove the prefix from the message -> our command
const command = message.content.substring(prefix.length);
// Match the command
if (command === "t") { // Truth
const truth = t[Math.floor(Math.random() * t.length)];
message.channel.send(truth);
} else if (command === "d") { // Dare
const dare = d[Math.floor(Math.random() * d.length)];
message.channel.send(dare);
} else if (command === "help") { // Help
const help = new Discord.MessageEmbed()
.setColor('#72dfa3')
.setTitle(`Truth Or Dare`)
.addFields(
{ name: '``+help``', value: 'For help' },
{ name: '``+t``', value: 'For Truth' },
{ name: '``+d``', value: 'For Your Dare' },
{ name: '``Created By``', value: 'AlpHa Coder [Labib Khan]' },
)
.setTimestamp()
.setFooter(`${message.author.username}`, message.author.displayAvatarURL());
message.channel.send(help);
} else { // No match found, invalid command
message.channel.send("Invalid command, type `+help` for help.");
}
});

Keep repeating the prompter questions with Inquirer.js based on answer?

I'm using Inquirer.js to create a CLI's prompter which allows users to enter/reply to some input/questions. In the last question, I want to add a feature that if the user replies no to Are you done? question, then the prompter will restart asking the questions until the user replies yes. I'm almost there with the functionality.
It's working, but only on the first time when I enter no. The second time I enter no, the prompter stops.
How can I run this on a loop to accomplish the desired behavior? What I'm doing wrong?
This is what I have some far:
import inquirer from 'inquirer';
inquirer
.prompt([
// { bunch of other questions previously },
{
type: 'confirm',
name: 'repeat_questions',
message: 'Are you done?',
},
])
.then((answers) => {
if (answers.repeat_questions) {
return inquirer.prompt([
// { bunch of other questions previously },
{
type: 'confirm',
name: 'repeat_questions',
message: 'Are you done?',
},
]);
}
})
.catch((error) => {
if (error.isTtyError) {
throw new Error(`Prompt couldn't be render in current environment`);
}
});
One way is a recursive function:
import inquirer from "inquirer";
const questions = [
{
type: "number",
name: "children_count",
message: "How many children do you have?",
},
{
type: "input",
name: "first_child_name",
message: "What is the eldest child's name?",
},
{
type: "confirm",
name: "is_finished",
message: "Are you done?",
},
];
function getAnswers() {
return inquirer.prompt(questions).then((answers) => {
if (answers.is_finished) {
return answers;
} else {
return getAnswers();
}
});
}
getAnswers()
.then(console.log)
.catch((error) => {});
The variable repeat_questions doesn't make sense, if the user says no to if they are done, repeat_questions is also no. So instead I renamed it to is_finished.

Is there a way of making "embed pages" with Object-based embeds? (Discord.js)

I've been looking around for a way to make embed pages with discord.js but all of the tutorials I've seen are for a MessageEmbed. I would like to have some way to react to the embed and have it switch to the next.
This is my original code:
client.on('message', (message) => {
if (message.content.toLowerCase() == prefix + "help") {
const Embed = {
color: 0x91A6A6,
title: 'Help Menu. Page 1.',
author: {
name: 'KLSB',
icon_url: bot.user.avatarURL()
},
description: '\n How to use KLSB. \n',
fields: [
{
name: '!-help',
value: 'Opens a help menu.',
inline: true,
},
{
name: '!-cat',
value: 'Grabs a random cat picture from the internet.',
inline: true,
},
{
name: '!-dog',
value: 'Grabs a random dog picture from the internet.',
inline: true,
},
{
name: '!-panda',
value: 'Grabs a random panda picture from the internet.',
inline: true,
},
{
name: '!-bird',
value: 'Grabs a random panda picture from the internet.',
inline: true,
},
{
name: '!-meme',
value: 'Finds a meme from Reddit.',
inline: true,
},
{
name: '!-animememe',
value: 'Finds a anime meme from Reddit.',
inline: true,
},
{
name: '!-animalmeme',
value: 'Finds a animal meme from Reddit.',
inline: true,
},
{
name: '!-hug',
value: "Sends a hug through the internet!",
inline: true,
},
{
name: '!-8ball',
value: "As they say, it sees the future!",
inline: true,
},
],
};
message.channel.send({ embed: Embed });
}})
If anyone has some examples, please share them with me (and it would help if you could explain what's happening :D)
Yes, it is possible. The one I'm going to show you uses reaction collectors to control which page to go to. You can modify it accordingly to fit with your pages needs, but this is the basic gist.
I first set up an array called Pages that would be what you wrote in the description, you can add additional arrays accordingly for your titles and whatnot. I also have page set to one since that's the default page and it will be the counter of which page the user is currently on.
There's multiple steps to show how this works:
Create initial embed + send embed (which I see you have already done, but you'll need to modify it accordingly to fit with the pages array.
Then add the reactions (I chose :rewind: and :fast_forward:. Note: Do not simply just copy them from the Discord channel, as I have learned here.)
Then create two separate filters, one for each ReactionCollector. I've labeled mine isBackwards and isForwards accordingly.
Create a ReactionCollector for both forwards and backwards.
I've labeled mine backwards and forwards
Then inside of each event handler, I just shifted the page and the descriptions and whatnot, and edited the embed accordingly.
Note: The second reaction takes a while to load. I haven't found a function where you can put both embeds at the same time... so it might take a while for the second embed to pop up.
Code:
//pages is very flexible, as long as you change the array at the top you're set
let pages = ["Page one", "Page two", "Page three", "Page four"];
let page = 1;
if (message.content.startsWith("!embed")) {
const embed = new Discord.MessageEmbed()
.setColor(0xffffff) //sets color here
.setFooter(`Page ${page} of ${pages.length}`)
.setDescription(pages[page - 1])
message.channel.send(embed).then(msg => {
msg.react('⏪').then(r => {
msg.react('⏩');
//filters
const isBackwards = (reaction, user) => reaction.emoji.name === '⏪' && user.id === message.author.id;
const isForwards = (reaction, user) => reaction.emoji.name === '⏩' && user.id === message.author.id;
const backwards = msg.createReactionCollector(isBackwards);
const forwards = msg.createReactionCollector(isForwards);
backwards.on("collect", r => {
if (page === 1) return;
page--;
embed.setDescription(pages[page - 1]);
embed.setFooter(`Page ${page} of ${pages.length}`);
msg.edit(embed)
});
forwards.on("collect", r => {
if (page === pages.length) return;
page++;
embed.setDescription(pages[page - 1]);
embed.setFooter(`Page ${page} of ${pages.length}`);
msg.edit(embed)
});
});
});
}

radio buttons in react, ignore null value

so i'm developing a personality quiz app using one of the tutorials i found on the internet //mitchgavan.com/react-quiz/, I have a quizQuestions.js file for api where i fetch the answer and the question from, like so
{
question: "I am task oriented in order to achieve certain goals",
answers: [
{
type: "Brown",
content: "Hell Ya!"
},
{
type: " ",
content: "Nah"
}
]
},
it has type and content, so this is the initial state of the app, every time the user click on Hell YA button it will increment that type +1, for example Brown: 1 etc.. but the problem is, when user select Nah it will give me this :null , I have a AnswerOption.js component like so
function AnswerOption(props) {
return (
<AnswerOptionLi>
<Input
checked={props.answerType === props.answer}
id={props.answerType}
value={props.answerType}
disabled={props.answer}
onChange={props.onAnswerSelected}
/>
<Label className="radioCustomLabel" htmlFor={props.answerType}>
{props.answerContent}
</Label>
</AnswerOptionLi>
);
}
AnswerOption.PropTypes = {
answerType: PropTypes.string.isRequired,
answerContent: PropTypes.string.isRequired,
answer: PropTypes.string.isRequired,
onAnswerSelected: PropTypes.func.isRequired
};
and my setUserAnswer function like so
setUserAnswer(answer) {
const updatedAnswersCount = update(this.state.answersCount, {
[answer]: {$apply: (currentValue) => currentValue + 1}
});
this.setState({
answersCount: updatedAnswersCount,
answer: answer
});
}
my question is, how can i let react ignore that white space, so when user click Nah it will not do anything with it, and if there is different approach to the problem i will be gladly accept it, thanks in advance.
Simple solution to your problem is to check if answer is empty :
if(answer.trim()) {
const updatedAnswersCount = update(this.state.answersCount, {
[answer]: {$apply: (currentValue) => currentValue + 1}
});
this.setState({
answersCount: updatedAnswersCount,
answer: answer
});
}

Categories