Search code examples
javascriptembeddiscord.js

How do you make embed pages in discord.js



 message.channel.send(bot.guilds.cache.map(g=> "*Name:* **"+g.name +'** *ID:* **'+g.id+"** *Owner:* **"+g.owner.user.tag+"**"))

I have this code to send all guilds, the name id and owner name, however how can I make this so that the fisrt 10 are displayed on an embed page, then a reaction arrow takes you to the next page showing other guilds and then the back reaction takes you back a page


Solution

  • Discord.js v13

    You could do this using Discord's relatively new buttons:

    import {MessageActionRow, MessageButton, MessageEmbed} from 'discord.js'
    
    // Constants
    
    const backId = 'back'
    const forwardId = 'forward'
    const backButton = new MessageButton({
      style: 'SECONDARY',
      label: 'Back',
      emoji: '⬅️',
      customId: backId
    })
    const forwardButton = new MessageButton({
      style: 'SECONDARY',
      label: 'Forward',
      emoji: '➡️',
      customId: forwardId
    })
    
    // Put the following code wherever you want to send the embed pages:
    
    const {author, channel} = message
    const guilds = [...client.guilds.cache.values()]
    
    /**
     * Creates an embed with guilds starting from an index.
     * @param {number} start The index to start from.
     * @returns {Promise<MessageEmbed>}
     */
    const generateEmbed = async start => {
      const current = guilds.slice(start, start + 10)
    
      // You can of course customise this embed however you want
      return new MessageEmbed({
        title: `Showing guilds ${start + 1}-${start + current.length} out of ${
          guilds.length
        }`,
        fields: await Promise.all(
          current.map(async guild => ({
            name: guild.name,
            value: `**ID:** ${guild.id}\n**Owner:** ${(await guild.fetchOwner()).user.tag}`
          }))
        )
      })
    }
    
    // Send the embed with the first 10 guilds
    const canFitOnOnePage = guilds.length <= 10
    const embedMessage = await channel.send({
      embeds: [await generateEmbed(0)],
      components: canFitOnOnePage
        ? []
        : [new MessageActionRow({components: [forwardButton]})]
    })
    // Exit if there is only one page of guilds (no need for all of this)
    if (canFitOnOnePage) return
    
    // Collect button interactions (when a user clicks a button),
    // but only when the button as clicked by the original message author
    const collector = embedMessage.createMessageComponentCollector({
      filter: ({user}) => user.id === author.id
    })
    
    let currentIndex = 0
    collector.on('collect', async interaction => {
      // Increase/decrease index
      interaction.customId === backId ? (currentIndex -= 10) : (currentIndex += 10)
      // Respond to interaction by updating message with new embed
      await interaction.update({
        embeds: [await generateEmbed(currentIndex)],
        components: [
          new MessageActionRow({
            components: [
              // back button if it isn't the start
              ...(currentIndex ? [backButton] : []),
              // forward button if it isn't the end
              ...(currentIndex + 10 < guilds.length ? [forwardButton] : [])
            ]
          })
        ]
      })
    })
    

    Here's a preview (with rubbish fields to show the pagination):

    Preview of embed pages with buttons

    Discord.js v12

    This is the original version I posted using reactions. This code only works for Discord.js v12.

    const guilds = bot.guilds.cache.array()
    
    /**
     * Creates an embed with guilds starting from an index.
     * @param {number} start The index to start from.
     */
    const generateEmbed = start => {
      const current = guilds.slice(start, start + 10)
    
      // you can of course customise this embed however you want
      return new MessageEmbed({
        title: `Showing guilds ${start + 1}-${start + current.length} out of ${guilds.length}`,
        fields: current.map(guild => ({
          name: guild.name,
          value: `**ID:** ${guild.id}\n**Owner:** ${guild.owner.user.tag}`
        }))
      })
    }
    
    const {author, channel} = message.author
    
    // send the embed with the first 10 guilds
    channel.send(generateEmbed(0)).then(message => {
    
      // exit if there is only one page of guilds (no need for all of this)
      if (guilds.length <= 10) return
      // react with the right arrow (so that the user can click it) (left arrow isn't needed because it is the start)
      message.react('➡️')
      const collector = message.createReactionCollector(
        // only collect left and right arrow reactions from the message author
        (reaction, user) => ['⬅️', '➡️'].includes(reaction.emoji.name) && user.id === author.id,
        // time out after a minute
        {time: 60000}
      )
    
      let currentIndex = 0
      collector.on('collect', async reaction => {
        // remove the existing reactions
        await message.reactions.removeAll()
        // increase/decrease index
        reaction.emoji.name === '⬅️' ? currentIndex -= 10 : currentIndex += 10
        // edit message with new embed
        await message.edit(generateEmbed(currentIndex))
        // react with left arrow if it isn't the start
        if (currentIndex !== 0) await message.react('⬅️')
        // react with right arrow if it isn't the end
        if (currentIndex + 10 < guilds.length) await message.react('➡️')
      })
    })