如何检查用户是否正在与选择菜单交互并在 Discord.js v13 中禁用它

发布于 2025-01-09 12:15:50 字数 959 浏览 0 评论 0原文

我希望当用户与选择菜单交互时它应该工作,然后当用户停止与其交互时几秒钟后它应该禁用它。

这就是 Dank Memer 机器人帮助命令的工作原理。

我的命令正在做什么: 该命令作为帮助命令,获取命令文件夹的子文件夹中的所有文件,并给出命令的用法、名称和描述。当用户在选择菜单中选择一个选项并且该选项是命令文件夹的子文件夹时,它会编辑嵌入。

所以到目前为止我已经这样做了:

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("
              

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时光是把杀猪刀 2025-01-16 12:15:50

正如我在评论中提到的,看起来您想要做的就是在收集器之前声明一个空变量(例如,名为 timeout),并且每次收集器收集您所做的事情时 如果定义了 timeout,则clearTimeout(timeout),然后将 timeout 设置为等于当前的 setTimeout() 语句。也就是说,如果我正确理解你的问题。忽略我关于将超时完全移到收集器之外的最初评论,因为这可能不是您在问题中寻找的内容(我相信我最初误解了您的问题 - 但是,如果这个答案仍然不是您想要的,我最初的评论可能正是您正在寻找的)。

现在回答你的问题。这是我在上面和评论中描述的过程的一个例子。我只是要显示代码的相关部分,以便您知道所有事情都发生在哪里,而不必筛选数十行来查找我的更改(并在将来注意,请仅包含代码中发生的部分)与您的问题相关——即在本例中只是您的收集器和collector.on("collect")处理程序)。

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);

});

以下是这段代码的作用,一步一步。每次用户与选择菜单交互时:

  1. 检查超时是否已启动。如果是这样,请将其清除。 (如果尚未创建超时,则无需清除超时)。
  2. 使用 setTimeout() 创建新的超时。新的超时将在 6 秒后禁用选择菜单。
  3. 如果用户在 6 秒结束之前再次与选择菜单交互,则过程从步骤 1 重新开始。上一步中创建的超时将被清除,并在其位置创建另一个超时,时间重置为 6秒。
  4. 如果在 6 秒结束之前没有用户再次与选择菜单交互,则一旦时间耗尽,选择菜单就会被禁用。这意味着菜单仅在用户最后一次与菜单交互后 6 秒才会禁用。

为了回应您对我在评论中试图告诉您的内容感到困惑的评论,我希望这个逐步的解释能够澄清我的意思。

编辑

删除了检查大小写 !i.message.member.id === i.isSelectMenu()if 语句。正如我在评论中所解释的,这里 !i.message.member.id 始终为 false,而 i.isSelectMenu() 始终为 < code>true 在这里,因此删除的 if 语句正在检查 if false === true (这显然是一个始终为 false 的语句)。

编辑2

正如我在关于这个问题的第一个评论中提到的,即OP感到困惑的最初评论,原始的超时功能位于收集器内部。这意味着禁用选择菜单和按钮的所有代码仅在最初选择菜单中的值后才会执行。换句话说,6秒无交互后菜单不会自行禁用;它只会在最后一次交互后 6 秒自行禁用。如果用户根本没有使用选择菜单,则 6 秒后不会发生禁用。这就是我原来的评论的意思。我修改了答案,将初始超时移到收集器之外。现在,如果用户出现以下情况,它将在 6 秒后禁用: a) 根本没有进行交互;或 b) 最后一次互动是在 6 秒前。

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.

嘿咻 2025-01-16 12:15:50

为此,您需要在收集器中添加 idle: amount,其中 amount 是您的时间(以毫秒为单位)。

let collector = msg.createMessageComponentCollector({ filter: filter, idle: 6000 });

这将在上次交互后 6 秒后或启动命令后 6 秒后停止收集器。

您还可以使用 time: amount 在经过特定时间后停止收集器。

To achieve that you need to add idle: amount, where amount is your time in milliseconds, in your collector.

let collector = msg.createMessageComponentCollector({ filter: filter, idle: 6000 });

This will stop the collector after 6 seconds from the last interaction, or 6 seconds after the command being launched.

You can also use time: amount to stop the collector after a specific amount of time passes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文