Search code examples
javascriptnode.jsdiscord.js

Discord Bot JS: Editing the reply from a previous slash command interaction


I'm coding a multi-functions discord bot with music support and need some help with this single functionality.

There is a play command that sends an embed message containing music information and when the stop command is executed, it has to edit that embed sent by the play command.

This is a minimized version of my code (just as an example):

(...)
    //PLAY COMMAND
    if (options === 'play') {
      const PlayEmbed = new MessageEmbed()
      PlayEmbed.setColor('#007DD1')
      PlayEmbed.setDescription(`${track.title}`)
    
      //Send message
      interaction.editReply({embeds: [PlayEmbed]})   // <--- The original reply
    }
    
    //STOP COMMAND
    if (options === 'stop') {
      const EndEmbed = new MessageEmbed()
      EndEmbed.setColor('#007DD1')
      EndEmbed.setDescription(`The music stopped!`)
    
      //Edit message
      interaction.editReply({embeds: [EndEmbed]})   // <--- Edit the original reply
    }
(...)

In this case, ".editReply" just sends the new embed after the stop command. This may be a lot easier to do than I think it is and I know that I need to get the "play" interaction to edit the specific reply sent by that interaction, either via webhooks or another method, but I don't know-how. The subcommands "play" and "stop" are being handled this way: const options = interaction.options.getSubcommand();


Solution

  • Note: This answer is for Discord.js v13.3.0


    Each command is in a different if block. When you send the play command, the bot will edit the reply, as intended. Once it finishes that, it checks itself against the stop command. stop does not match play and the code moves on. The reason why it's not editing the reply you want it to is because you're using a different Interaction each time. The first Interaction is for the play command and as stated before, since play does not match stop, the code moves on, discarding the Interaction.

    A solution to this is to use a Set and map the member's ID to the channel ID and the reply ID. However, this only works if the message is not ephemeral ("Only you can see this"). The solution works like this:

    const plays = new Map();
    
    (...)
    
    //PLAY COMMAND
    if (options === 'play') {
     (...)
     interaction.editReply({embeds: [PlayEmbed]})
     plays.set(interaction.member.id, [interaction.channel.id, interaction.fetchReply().id])
    }
        
    //STOP COMMAND
    if (options === 'stop') {
     (...)
     let channel = client.channels.cache.get(plays[interaction.member.id].0);
     // Fetch the messages before we can access any of them
     channel.messages.fetch();
     channel.messages.cache.get(plays[interaction.member.id].1).edit({embeds: [EndEmbed]})
    }
    

    One caveat to this code is that if the user hasn't ran the play command, plays[interaction.member.id] will return undefined and likely result in an error.