Search code examples
javascriptnode.jsdiscorddiscord.js

How can you create a pop-up window in Discord that accepts an input from the user?


It's my first time seeing this feature from a Discord bot. I tried looking everywhere but it seems that I have failed. There's this feature from Captcha.bot Discord bot where you can accept input from a pop-up window inside Discord.

There's a button in an embedded message made by Captcha.bot where you will have to answer a Captcha test. After pressing the button, it creates a pop-up window like this.

enter image description here

After placing the right answer on the captcha bot, here's the aftermath of the experience.

enter image description here

All I want to learn is how to summon that pop-up window using Discord.js if it's even possible or at least learn how they did it.


Solution

  • Those are called modals, and they will be available in the next major discord.js version, v14. There is already a pull request for this.

    Update 2022-07-19: Modals in v14.0.0

    There are some changes in v14. If you try to update your code from v13.7, there are a few differences:

    • Modal is now ModalBuilder
    • MessageActionRow is now ActionRowBuilder
    • TextInputComponent is now TextInputBuilder
    • you need to use enums like TextInputStyle.Short, ButtonStyle.Primary, etc. instead of strings (i.e. "SHORT", or "PRIMARY")
    • interaction no longer has a isModalSubmit type guard. You'll need to check its type against the InteractionType.ModalSubmit enum

    You can find the full code below that you can use with discord.js v14:

    // discord.js version v14.0.0+
    const {
      ActionRowBuilder,
      ButtonBuilder,
      ButtonStyle,
      Client,
      Events,
      GatewayIntentBits,
      InteractionType,
      ModalBuilder,
      TextInputBuilder,
      TextInputStyle,
    } = require('discord.js');
    
    const TOKEN = 'YOUR TOKEN HERE';
    const client = new Client({
      intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
      ],
    });
    
    client.on(Events.MessageCreate, (message) => {
      if (message.author.bot) return;
    
      let button = new ActionRowBuilder();
      button.addComponents(
        new ButtonBuilder()
          .setCustomId('verification-button')
          .setStyle(ButtonStyle.Primary)
          .setLabel('Open modal dialog'),
      );
      message.reply({
        components: [button],
      });
    });
    
    client.on(Events.InteractionCreate, async (interaction) => {
      if (interaction.isButton()) {
        if (interaction.customId === 'verification-button') {
          const modal = new ModalBuilder()
            .setCustomId('verification-modal')
            .setTitle('Verify yourself')
            .addComponents([
              new ActionRowBuilder().addComponents(
                new TextInputBuilder()
                  .setCustomId('verification-input')
                  .setLabel('Answer')
                  .setStyle(TextInputStyle.Short)
                  .setMinLength(4)
                  .setMaxLength(12)
                  .setPlaceholder('ABCDEF')
                  .setRequired(true),
              ),
            ]);
    
          await interaction.showModal(modal);
        }
      }
    
      if (interaction.type === InteractionType.ModalSubmit) {
        if (interaction.customId === 'verification-modal') {
          const response =
            interaction.fields.getTextInputValue('verification-input');
          interaction.reply(`Yay, your answer is submitted: "${response}"`);
        }
      }
    });
    
    client.once('ready', () => {
      console.log('Bot v14 is connected...');
    });
    
    client.login(TOKEN);
    

    enter image description here

    Previous answer 2022-06-28: Modals in v13.7.0

    Modals are available since v13.7.0. If you try to update your code from discord-modals, there are a few differences:

    • you'll need to import Modal and TextInputComponent from discord.js
    • TextInputComponents must be inside a MessageActionRows
    • the interaction has a showModal() method that opens the modal
    • the interaction has an isModalSubmit() method that checks if it's a ModalSubmitInteraction
    • there is no modalSubmit event
    • to get the response you need to use interaction.fields.getTextInputValue()

    You can find the full code below that you can use in v13.7:

    // discord.js version v13.7.0+
    const {
      Client,
      Intents,
      MessageActionRow,
      MessageButton,
      Modal,
      TextInputComponent,
    } = require('discord.js');
    
    const TOKEN = 'YOUR TOKEN HERE';
    const client = new Client({
      intents: [
        Intents.FLAGS.GUILDS,
        Intents.FLAGS.GUILD_MESSAGES],
    });
    
    client.on('messageCreate', (message) => {
      if (message.author.bot) return;
    
      let button = new MessageActionRow();
      button.addComponents(
        new MessageButton()
          .setCustomId('verification-button')
          .setStyle('PRIMARY')
          .setLabel('Open modal dialog'),
      );
      message.reply({
        components: [button],
      });
    });
    
    client.on('interactionCreate', async (interaction) => {
      if (interaction.isButton()) {
        if (interaction.customId === 'verification-button') {
          const modal = new Modal()
            .setCustomId('verification-modal')
            .setTitle('Verify yourself')
            .addComponents([
              new MessageActionRow().addComponents(
                new TextInputComponent()
                  .setCustomId('verification-input')
                  .setLabel('Answer')
                  .setStyle('SHORT')
                  .setMinLength(4)
                  .setMaxLength(12)
                  .setPlaceholder('ABCDEF')
                  .setRequired(true),
              ),
            ]);
    
          await interaction.showModal(modal);
        }
      }
    
      if (interaction.isModalSubmit()) {
        if (interaction.customId === 'verification-modal') {
          const response =
            interaction.fields.getTextInputValue('verification-input');
          interaction.reply(`Yay, your answer is submitted: "${response}"`);
        }
      }
    });
    
    client.once('ready', () => {
      console.log('Bot v13 is connected...');
    });
    
    client.login(TOKEN);
    

    First answer 2022-03-30: Using the discord-modals package

    In the meantime, you can use an npm package like discord-modals or discordjs-modal.

    You can find a working example with the discord-modals package below. Don't forget to install it first using npm i discord-modals.

    // discord-modals
    const {
      Client,
      Intents,
      MessageActionRow,
      MessageButton,
    } = require('discord.js');
    const discordModals = require('discord-modals');
    const { Modal, TextInputComponent, showModal } = discordModals;
    
    const TOKEN = 'YOUR TOKEN HERE';
    const client = new Client({
      intents: [
        Intents.FLAGS.GUILDS,
        Intents.FLAGS.GUILD_MESSAGES],
    });
    discordModals(client);
    
    client.on('messageCreate', (message) => {
      if (message.author.bot) return;
    
      let button = new MessageActionRow();
      button.addComponents(
        new MessageButton()
          .setCustomId('verification-button')
          .setStyle('PRIMARY')
          .setLabel('Open modal dialog'),
      );
      message.reply({
        components: [button],
      });
    });
    
    client.on('interactionCreate', async (interaction) => {
      if (interaction.isButton()) {
        if (interaction.customId === 'verification-button') {
          const modal = new Modal() // We create a Modal
            .setCustomId('verification-modal')
            .setTitle('Verify yourself')
            .addComponents([
              new TextInputComponent()
                .setCustomId('verification-input')
                .setLabel('Answer')
                .setStyle('SHORT')
                .setMinLength(4)
                .setMaxLength(12)
                .setPlaceholder('ABCDEF')
                .setRequired(true),
            ]);
    
          showModal(modal, {
            client,
            interaction,
          });
        }
      }
    });
    
    client.on('modalSubmit', async (modal) => {
      if (modal.customId === 'verification-modal') {
        const response = modal.getTextInputValue('verification-input');
        modal.reply(`Yay, your answer is submitted: "${response}"`);
      }
    });
    
    client.once('ready', () => {
      console.log('Bot v13 is connected...');
    });
    
    client.login(TOKEN);