user18793506
user18793506

Reputation:

DiscordJS Listing all server channels in select menu

I am trying to list out all the channels from the server and put them all into a Select Menu but I get the following error:

data.components[0].components[0].options: Must be between 1 and 25 in length.

Code:

    run: async (interaction) => {
        const embed = new MessageEmbed()
            .setColor('#4ccdea')
            .setTitle('Select a channel below.')
            .setTimestamp()

        let selectmenu = new MessageActionRow()
            .addComponents(
                new MessageSelectMenu()
                    .setCustomId('channel')
                    .setPlaceholder('Nothing selected')
                    .addOptions([{
                        label: `Cancel`,
                        description: 'Cancel the channel selection',
                        value: 'cancel',
                    }])
            )

        interaction.guild.channels.cache.forEach(channel => {
            selectmenu.components[0].addOptions([{
                label: `${channel.name}`,
                description: `${channel.name}`,
                value: `${channel.id}`,
            }]);
        })
        await interaction.reply({ embeds: [embed], components: [selectmenu] });
    }

Upvotes: 1

Views: 2049

Answers (2)

Zsolt Meszaros
Zsolt Meszaros

Reputation: 23189

The error means that you can't have more than 25 options in a select menu. As you have already an option (cancel), you can only add 24 channels at a time.

A quick and dirty solution is to just send the first 24 channels, like this:

let selectmenu = new MessageActionRow().addComponents(
  new MessageSelectMenu()
    .setCustomId('channel')
    .setPlaceholder('Nothing selected')
    .addOptions([
      {
        label: `Cancel`,
        description: 'Cancel the channel selection',
        value: 'cancel',
      },
    ]),
);

interaction.guild.channels.cache.first(24).forEach((channel) => {
  selectmenu.components[0].addOptions([
    {
      label: `${channel.name}`,
      description: `${channel.name}`,
      value: `${channel.id}`,
    },
  ]);
});
await interaction.reply({ embeds: [embed], components: [selectmenu] });

You could also send more than one select menu. You will need to create an options array using map(), create an action row for every 24 channels, and add the menu with the 24 options.

You can also create a helper function to chunkify your array:

// helper function to slice arrays
function chunkify(arr, len) {
  let chunks = [];
  let i = 0;
  let n = arr.length;

  while (i < n)
    chunks.push(arr.slice(i, (i += len)));

  return chunks;
}

And then you can use it like this:

async (interaction) => {
  const embed = new MessageEmbed()
    .setColor('#4ccdea')
    .setTitle('Select a channel below.')
    .setTimestamp();

  let selectMenus = [
    new MessageActionRow().addComponents(
      new MessageSelectMenu()
        .setCustomId('channel')
        .setPlaceholder('Nothing selected')
        .addOptions([
          {
            label: `Cancel`,
            description: 'Cancel the channel selection',
            value: 'cancel',
          },
        ]),
    ),
  ];
  await interaction.guild.channels.fetch();

  // iterate over the channels and create menu option objects
  let options = interaction.guild.channels.cache.map((channel) => ({
    label: channel.name,
    description: channel.name,
    value: channel.id,
  }));

  // if there is less than 24 fields, you can safely send the menu
  // in a single message
  if (options.length <= 24) {
    selectMenus[0].components[0].addOptions(options);
    await interaction.reply({ embeds: [embed], components: selectMenus });
  }

  // if there are more, you need to create chunks w/ max 24 fields
  // you can use the helper function created above
  const chunks = chunkify(options, 24);

  chunks.forEach((options, i) => {
    // if this is the first row, append the options
    if (i === 0)
      selectMenus[0].components[0].addOptions(options);

    // else just create a new action row with a new menu for each 24 fields
    else
      selectMenus.push(
        new MessageActionRow().addComponents(
          new MessageSelectMenu()
            .setCustomId(`channel-${i}`)
            .setPlaceholder('Nothing selected')
            .addOptions(options),
        ),
      );
  });

  await interaction.reply({ embeds: [embed], components: selectmenus });
}

One drawback of this is that the customId is not channel, but channel, channel-1, channel-2, etc. It means, you will need to make sure that when you handle the interaction, you check if the interaction.customId.startsWith('channel').

enter image description here

Upvotes: 2

Xen0o2
Xen0o2

Reputation: 14

You can use .slice(0,25) to only keep 25 first channels and put them in the select.

Upvotes: -1

Related Questions