Search code examples
node.jsdiscord.js

How to make a new embed when the first embed reaches the description limit (4096)


I have added the logs case for my warn slash command, but I have a problem.. that problem is that if the embed description reaches the limit, i get an error and thats not what I want.

So basically, I want the a new embed to be created as like a "second page", and I can use my pagination function to help with navigating between pages and so on. I just don't exactly know how to do that or how to get started.

I am asking for some assistance here because my goal is to have a functional "warning logs" embed with buttons to navigate through the pages if there are more than one like most users will have.

case "logs": {
          const buttonPages = require("../../functions/pagination");
          
          const user = interaction.options.getUser("user");
          const userWarnings = await warnSchema.find({ Guild: interaction.guild.id, User: user.id });

          if (!userWarnings?.length) return interaction.reply({ content: `\`${user.tag}\` does not have any warnings.`, ephemeral: true });

          const embedDescription = userWarnings.map((warn) => {
              const moderator = interaction.guild.members.cache.get(warn.Moderator);

              return [
                `<:CL_Shield:937188831227183135> Warn ID: ${warn.id}`,
                `<:CL_ReplyContinued:909444370221137930> Moderator: ${moderator || "unknown"}`,
                `<:CL_ReplyContinued:909444370221137930> User: ${user}`,
                `<:CL_ReplyContinued:909444370221137930> Reason: \`${warn.Reason}\``,
                `<:CL_Reply:909436090413363252> Date: ${warn.Date}`,
              ].join("\n");
            }).join("\n\n");

          const embed = new EmbedBuilder()
            .setTitle(`${user.tag}'s warnings`)
            .setDescription(embedDescription)
            .setColor("#2f3136");
          
          //const pages = [embed];
          //buttonPages(interaction, pages);
          await interaction.reply({ embeds: [embed] });
        }

Solution

  • What you're trying to do is called pagination. Code is below, but here is how it works:

    First what you've got to do is determine how many different embeds it'll be. We can do that with description.length/4096, but we need to round up so we'd use Math.ceil. The Array().keys() allows us to get a list of all the numbers up to it to iterate over. For example Array(3).keys() would give us [0, 1, 2] so we can iterate over it.

    We then need to select which part of the description we want to send. We want to start at (i*4096) since all previous embeds have 4096 characters, and then we want it to be 4096 characters long so we simply end it at (i*4096)+4096).

    You also need to consider that we cannot always use interaction.reply as interactions can only be replied to once, so we must use interaction.channel.send to send them all to the channel. But, we will want some sort of response to the interaction, so we send the first one as a response to the interaction, and all following ones to the channel.

    Here's the code:

    for (const i of Array(Math.ceil(embedDescription.length/4096)).keys()) {
        const embed = new EmbedBuilder().setDescription(embedDescription.substring((i*4096), (i*4096)+4096))
        if(i === 0) await interaction.reply({embeds: [embed]})
        else await interaction.channel.send({embeds: [embed]})
    }