'How do I check if the user is interacting with select menu and disable it in discord.js v13

I want that when the user is interacting with the select menu it should work and then it should disable it after a few seconds when the user stops interacting with it.

This is just the idea how the Dank Memer bot Help command work.

What my command is doing: The command works as a help command that is getting all the files in the sub-folder of the command folder and giving the usage, name and description of the command. It edits the embed when the user selects an option in select menu and the option are the sub-folders of the command folder.

So I have done this till now:

const { MessageEmbed, Message, MessageActionRow, MessageSelectMenu, MessageButton } = require("discord.js");
const fs = require("fs");
const prefix = require("../../config.json").PREFIX;

module.exports = {
  name: "help",
  description: "Get All The Commands!",
  usage: "help",
  cooldown: 1000 * 5,
  category: "Help Commands!",
  /**
   * @param {Message} message
   * 
   */
  run: async (client, message, args) => {
    const sizeOfCat = fs.readdirSync("./Commands - Staff/");

    const OldEmbed = new MessageEmbed()
      .setAuthor({
        name: "Commands! =>",
        iconURL: `${message.guild.iconURL({ dynamic: true })}`
      })
      .setThumbnail(`${message.guild.iconURL({ dynamic: true })}`)
      .setTitle("📬 Need help? Select The Category You Want To =>")
      .setDescription(
        `
        **\`Hello Dear ${message.member.user.username}, Please Select Your Category According To Your Choice!\`**

        **How To Use =>**
        \`1) Click The Select Menu Down Below.\`
        \`2) You Will See Many Categories, Click Your Category You Want To View.\`
        \`3) The Embed Will Be Edited And You Can See Your Commands According To Category.\`

        *Note => The Select Menu And The Button Will Be Disabled Automatically After 6 Seconds!*

        **Total Categories Are: ${sizeOfCat.length}**
        `
      )
      .setColor("BLURPLE")
      .setTimestamp()
      .setFooter({
        text: `Requested by ${message.author.tag}`, 
        iconURL: message.author.displayAvatarURL({ dynamic: true })
      })

      const homeButton = new MessageActionRow()
      .addComponents(
        new MessageButton()
        .setCustomId("Home")
        .setLabel("Back To Home!")
        .setStyle("PRIMARY")
        .setEmoji("🏘️")
      )

      const EmojisCat = {
        "Other Commands!": "🤖",
        "Help Commands!": "🤝",
        "Moderation Commands!": "⚒️",
        "Owner Commands!": "👑"
      };

      const selectMenu = new MessageActionRow()
      .addComponents(
        new MessageSelectMenu()
        .setCustomId("Help-Menu")
        .setPlaceholder(`Click To View The Categories Of The Commands!`)
        .addOptions([
          client.categoriesCommands.map((cat) => {
            return {
              label: `${cat[0].toUpperCase() + cat.slice(1)}`,
              value: cat,
              emoji: EmojisCat[cat],
              description: `Click To View The Commands Of This Categories!`,
            }
          })
        ])
      );

    await message.reply({
      content: "**There You Go, Check The List Of Categories!**",
      embeds: [OldEmbed],
      components: [selectMenu, homeButton]
    })
    .then(async (msg) => {

      let filter = i => i.member.id === message.member.id;
      let colletor = msg.createMessageComponentCollector({ filter: filter });

      let timeout = null;
      colletor.on("collect", async (i) => {
        if (!i.member.id === message.member.id) {
          await msg.reply({
            content: `**Its Not Your Turn Of Using The Command Menu Or The Command (\`${prefix}help\`) Is Not Runned By You! Dum Dum.**`,
            ephemeral: true,
          });
        } else {

          if (i.isButton()) {
            await i.deferUpdate();
            if (i.customId === "Home") {
              msg.edit({ embeds: [OldEmbed] })
            }
          }

          if (i.isSelectMenu()) {
            if (i.customId === "Help-Menu") {
              await i.deferUpdate();
              let [ directory ] = i.values;
              let totalCdms = client.categoriesCommands.filter(cmd => cmd.category === directory).map(cmd => cmd.size);
              let command = client.categoriesCommands.filter(cmd => cmd.category === directory).map(cmd => cmd.length) + 1;

              const embed = new MessageEmbed()
                .setAuthor({
                  name: "AwesomeSMP Commands! =>",
                  iconURL: `${message.guild.iconURL({ dynamic: true })}`
                })
                .setThumbnail(`${message.guild.iconURL({ dynamic: true })}`)
                .setTitle(`📬 Need help? Here Are All Of My ${directory} Commands:`)
                .setDescription(
                  `
                  **\`Here Are One Of My [${directory} Category] Commands =>\`**

                  **Total Commands In ${directory} Are: ${totalCdms}**
                  `
                )
                .setColor("BLURPLE")
                .setTimestamp()
                .setFooter({
                  text: `Requested by ${message.author.tag}`, 
                  iconURL: message.author.displayAvatarURL({ dynamic: true })
                })
                
              client.commands.filter((cmd) => cmd.category === directory)
              .map((cmd) => {
                embed.addField(
                  `\n**\nCommmand-${command++}** =>\n\`${prefix}${cmd.name}\``,
                  `   **Description:** *${cmd.description.toUpperCase() || "*None*"}*\n   **Usage:** *${prefix} ${cmd.usage || "None"}*\n`
                  ,true
                );
              })

              await msg.edit({
                embeds: [embed]
              });
              
              if (!i.message.member.id === i.isSelectMenu()) {
                if (timeout) clearTimeout(timeout);
                timeout = setTimeout(async () => {
                  selectMenu.components[0].setDisabled(true);
                  homeButton.components[0].setDisabled(true);
          
                  await msg.edit({
                    embeds: [OldEmbed],
                    components: [selectMenu, homeButton]
                  });
                }, 6000);
              }

            }
          }
        }
      });
    });
  }
}

There are no errors till now.



Solution 1:[1]

As I mentioned in my comment, it looks like all you want to do is declare an empty variable (for example, named timeout) before your collector, and each time the collector collects something you do clearTimeout(timeout) if timeout is defined, and then afterwards you set timeout equal to your current setTimeout() statement. That is, if I'm understanding your question correctly. Ignore my initial comment about moving the timeout entirely outside the collector, as that was probably not what you were looking for with your question (I believe I initially misunderstood your question -- however, if this answer is still not what you are looking for, my initial comment may instead be exactly what you are looking for).

Now for the answer to your question. Here is an example of what I am talking about with the process I described above and in the comments. I'm just going to show the relevant parts of the code so that you know where everything is happening, without having to sift through dozens of lines to find my changes (and note in the future, please only include the parts of your code that are relevant to your question -- i.e. just your collector and the collector.on("collect") handler in this case).

async function disableAll() {
    selectMenu.components[0].setDisabled(true);
    homeButton.components[0].setDisabled(true);
  
    await msg.edit({
        embeds: [OldEmbed],
        components: [selectMenu, homeButton]
    });
}

let timeout = setTimeout(disableAll, 6000);
colletor.on("collect", async (i) => {
               
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(disableAll, 6000);

});

Here is what this code does, step by step. Every time a user interacts with the select menu:

  1. Check if a timeout has already been started. If so, clear it. (If no timeout has been created yet, no clearing of timeouts needs to occur).
  2. Create a new timeout using setTimeout(). The new timeout will disable the select menu after 6 seconds.
  3. If the user interacts with the select menu again before the 6 seconds is over, the process starts over from step 1. The timeout created in the previous step will be cleared and another one will be created in its place, with the time reset to 6 seconds.
  4. If no user interacts with the select menu again before the 6 seconds is over, the select menu simply gets disabled once the time runs out. This means the menu will only disable 6 seconds after the user last interacted with the menu.

In response to your comment expressing confusion over what I was trying to tell you in the comments, I hope this step-by-step explanation clears up what I meant.

Edit

Removed the if statement that checks the case !i.message.member.id === i.isSelectMenu(). As I explained in the comments, !i.message.member.id is always false here, and i.isSelectMenu() is always true here, so the removed if statement was checking if false === true (which is obviously a statement that is always false).

Edit 2

As I mentioned in my very first comment on this question, i.e. the initial comment that the OP was confused about, the original timeout functionality was inside the collector. This meant that all of the code to disable the select menu and button would only execute after a value in the menu was initially selected. In other words, the menu would not disable itself after 6 seconds of no interaction; it would only disable itself 6 seconds after the last interaction. If the user did not use the select menu at all, no disabling would occur after 6 seconds. This is what I meant with my original comment. I have modified the answer to move the initial timeout outside the collector. Now, it will disable after 6 seconds if the user either: a) has not interacted at all; or b) last interacted over 6 seconds ago.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 halfer