Search code examples
javascriptnode.jsdiscorddiscord.jsdiscord-buttons

Discord.js | Buttons doesn't work after bot restarts


The buttons work after doing the command but after restarting the bot and pressing the button, it says interaction failed Here's my ticket.js

const { MessageButton } = require('discord-buttons');

module.exports = {
    name: 'ticket-setup',
    aliases: ['close'],
    category: 'Miscellaneous',
    description: 'Makes a ticket embed',
    async execute(message, args, cmd, client, Discord){

        if(!message.member.permissions.has("MANAGE_CHANNELS")) return message.reply("Normies can't do this command")

        if (cmd == 'close') {
            if (!message.channel.name.includes('ticket-')) return message.channel.send('You cannot use that here!');
            message.channel.delete();
           }
       
        let title;
        let desc;
        let ticketMsg;

        const filter = msg => msg.author.id == message.author.id;
        let options = {
            max: 1
        };
    
        message.channel.send("What will the ticket title be?\nSay cancel to cancel")
        let col = await message.channel.awaitMessages(filter, options)
        if(col.first().content == 'cancel') return message.channel.send("Cancelled");
        title = col.first().content
    
        message.channel.send('What will the description be?\nSay cancel to cancel')
        let col2 = await message.channel.awaitMessages(filter, options)
        if(col2.first().content == 'cancel') return message.channel.send("Cancelled");
        desc = col2.first().content
        
        message.channel.send('What is the message that the user will see when they make a ticket?\nSay cancel to cancel')
        let col3 = await message.channel.awaitMessages(filter, options)
        if(col3.first().content == 'cancel') return message.channel.send("Cancelled");
        ticketMsg = col3.first().content

        const setupEmbed =  new Discord.MessageEmbed()
        .setTitle(title)
        .setDescription(desc)
        .setFooter(message.guild.name, message.guild.iconURL({ dynamic: true }))
        .setColor('00f8ff')

        const hiEmbed = new Discord.MessageEmbed()
        .addField(ticketMsg, "\n\nDo a.close or press the button to close the ticket")
        .setColor("RANDOM")
        .setTimestamp()
        
        const createTicketButton = new MessageButton()
        .setID("ticketCreate")
        .setStyle("blurple")
        .setLabel("📨");

        const closeTicketButton = new MessageButton()
        .setID("ticketClose")
        .setLabel("Close ticket")
        .setStyle("red");



        if(cmd == 'ticket-setup'){ 
        message.channel.send({embed: setupEmbed, button: createTicketButton })      
        }

           client.on('clickButton', async (button) => {
            await button.clicker.fetch();
            await button.reply.defer();
               const user = button.clicker.user
            if (button.id === 'ticketCreate') {
              button.guild.channels.create(`ticket-${user.id}`,  {
                permissionOverwrites: [
                 {
                  id: user.id,
                  allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
                 },
                 {
                  id: button.message.guild.roles.everyone,
                  deny: ['VIEW_CHANNEL'],
                 },
                ],
                type: 'text',
               }).then(async (channel) =>{
                   channel.send({embed: hiEmbed, button: closeTicketButton })
               })
            } else if(button.id == 'ticketClose'){
                button.message.channel.delete()
            }
          });
    }
}

I use the package discord-buttons Docs link

I tried putting the clickButton event in my event handler but it didn't work as I get a lot of errors. How do I still make the buttons work even after restart?


Solution

  • The Problem

    The reason why your buttons aren't working after your bot restarts is because your client.on("clickButton") event handler is inside your "ticket-setup" command's code. That means your event is only setup once the ticket-setup command is used after the bot restarts, or in other words once execute() is called on this file after the bot has started up.

    Think about this: your client.on("clickButton") code is not reached until your ticket-setup command's execute() function is called. This will cause multiple problems for you. First of all, as mentioned above, the clickButton event is not even being handled until you've used ticket-setup at least once after the bot has started up. Secondly, this will create an additional event handler every time the command is used. In other words, if you were to use the ticket-setup command twice or more, the code within your clickButton handler would execute more than once every time your buttons are clicked (in your specific scenario, it would create more than one ticket per button click).

    The Solution

    The problem you are facing has a pretty simple solve. You simply need to move the entirety of your clickButton event handler out of the execute() method. Perhaps move it into your main server.js or bot.js file, alongside your client.on("ready") and client.on("message") event handlers. This will ensure that the clickButton event handler is setup only once, and immediately when the bot starts up.

    Note, however, that you do need to make some slight additions to your clickButton event handler to ensure this works properly. You need to move the code for your hiEmbed and closeTicketButton into your client.on("clickButton") handler.

    Here's how that might look, in server.js, based on the code in your question:

    client.on('clickButton', async (button) => {        
        await button.clicker.fetch();
        await button.reply.defer();
        const user = button.clicker.user;
    
        const hiEmbed = new Discord.MessageEmbed()
        .addField(ticketMsg, "\n\nDo a.close or press the button to close the ticket")
        .setColor("RANDOM")
        .setTimestamp();
    
        const closeTicketButton = new MessageButton()
        .setID("ticketClose")
        .setLabel("Close ticket")
        .setStyle("red");
    
        if (button.id === 'ticketCreate') {
            button.guild.channels.create(`ticket-${user.id}`,  {
                permissionOverwrites: [
                {
                    id: user.id,
                    allow: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
                },
                {
                    id: button.message.guild.roles.everyone,
                    deny: ['VIEW_CHANNEL'],
                },
                ],
                type: 'text',
            }).then(async (channel) =>{
                channel.send({embed: hiEmbed, button: closeTicketButton })
            })
        } else if(button.id == 'ticketClose'){
            button.message.channel.delete()
        }
    });
    

    You may have noticed another problem with this: the ticketMsg variable will not be defined. You will need to make changes to solve that issue as well. I would recommend saving the value of ticketMsg to a JSON file or database, and accessing that information within your client.on("clickButton"). If this code represents a proper ticketing system, you will need to do this regardless of whether you use this solution or not, as otherwise your users will need to use ticket-setup to setup the ticketing system again every time your bot restarts.