Search code examples
javascriptdiscord.jsnode-schedule

I'm making a schedule Discord.js bot, but it's not sending a message


I'm making a discord.js scheduling bot. I am using node-schedule for this. It's not throwing any errors but it's still not sending a message. What am I doing wrong, and how do I get rid of this issue? (thank you in advance)

My code is:

const Discord = require('discord.js');
const client = new Discord.Client();
const schedule = require('node-schedule');


client.once('ready', () => {
  console.log('Ready!');
});

client.login('TOKEN IS HERE');

const rule = new schedule.RecurrenceRule();
rule.tz = 'EDT';


client.on('message', message => {
  if (message.content === '!schedule 9pm meeting') {
    message.channel.send('Alright. I will announce it for you, friend! :smiley:');
    const job = schedule.scheduleJob('00 21 * * *', function () {
      client.channels.cache.get("channel id is here").send("This is a test message. Does it work?");
    });
  }
});

Solution

  • You can't run the schedule.scheduleJob from inside the client.on function and expect the message to still exist. Discord API expects a response to a webhook within a specific time before it times out.

    Also, if the bot runs on a cloud service, the node it runs on might be restarting once in a while, which messes up in-memory data like attaching cron jobs in node-schedule.

    Persisting the data

    You should probably get scheduled time by the user and persist the data in some sort of database. You should use database read\writes in order to save the data between your cloud provider restarts (unless you have a paid subscription).

    Have a global cron job or interval

    Since you can potentialy have thousands of scheduled meetings, it's better in your case to check for meetings withing a certain interval and send all the reminders at the same time.

    Let's say a user can't give us a time more specific than a certain minute. Then we can check for reminders every minute, knowing we'll inform users before the meeting starts.

    // Run checkForReminders every 60 seconds to scan DB for outstanding reminders
    setInterval(checkForReminders, 60000);
    
    // Parse reminder request, save to DB, DM confirmation to user
    client.on('message', (msg) => {
        createNewReminder(msg);
    });
    

    New reminders handling

    const createNewReminder = (msg) => {
        const formattedMessage = formatMessage(msg)
    
        // If message isn't a remindme command, cease function execution
        if (!formattedMessage.startsWith('!remindme')) {
            return
        }
    
        // Determine if message contains a number to assign to intervalInteger
        checkForNumbersInMessage(formattedMessage)
    
        // Final format for message to be sent at reminderTime
        const messageToDeliverToUser = formattedMessage.replace('!remindme', '')
                
        // Set integer and verb values for moment.add() parameters
        const intervalInteger = parseInt(checkForNumbersInMessage(formattedMessage))
        const intervalVerb = setIntervalVerb(formattedMessage)
    
        // Format time to send reminder to UTC timezone    
        const reminderTime = moment().utc().add(intervalInteger, intervalVerb).format('YYYY-MM-DD HH:mm:ss')
    
        // Save reminder to DB
        saveNewReminder(msg.author.id, msg.author.username, messageToDeliverToUser, reminderTime)
    
        // Send embedded message to author & notify author in channel of remindertime request
        const embed = createEmbed(messageToDeliverToUser, reminderTime)
        msg.author.send(embed)
        msg.channel.send(`${msg.author} - A reminder confirmation has been sent to your DMs. I will DM you again at the requested reminder time`)
    }
    

    Send a message to a guild or user later

    In order to send a message later, save either the userId or guildId to the database, then, retrieve the user or guild from the discord client, and send the message.

    const checkForReminders = () => {
        db.serialize(() => {
            // Select all messages older than the current dateTime
            db.each("SELECT id, reminderTime, userID, message FROM reminders WHERE reminderTime < DATETIME()", (error, row) => {
                if (error || !row) {
                    return console.log('Error or no row found')
                }
    
                // If reminders are found, fetch userIDs, then send the requested reminder through DM
                client.users.fetch(row.userID).then((user) => {
                    user.send(`Hi, you asked to be reminded "${row.message}" - That's right now!`).catch(console.error)
                    console.log(`Message delivered: ${row.message}`)
                    console.log(`Message deleted successfully`)
    
                // Delete message after DMing to user 
                db.run("DELETE FROM reminders WHERE id = ?", [row.id])
                    console.log('Message sent and removed successfully')
                })
            })    
        })
    }
    

    Code examples where taken from NathanDennis/discord-reminder-bot. check out the repository for a complete example. He comments on his code so it's easy to understand.