javascriptnode.jsdiscorddiscord.js

Asynchronous message Cooldown / Antispam


I am making a Discord bot that informs moderators when a user joins a specific voice channel. The bot is supposed to also have spam protection such that it will only log a message once per minute per user.

This is what I have tried before:

const { Client } = require("discord.js");
const { config } = require("dotenv");
const fs = require('fs');

const client = new Client({
    partials: ['MESSAGE', 'CHANNEL', 'REACTION']
});

config({
    path: __dirname + "/.env"
})

var supportchannel = '827574015526567947'
var dutychannel = '847445933969113118'
var ondutyrole = '847447374925398016'

client.on("ready", () => {
    console.log(`Hi, ${client.user.username} is now online!`);
    global.timer = 0;
    client.user.setStatus('online');
    
    client.user.setActivity('me getting developed', { type: "WATCHING"})
        .then(presence => console.log('status set'))
        .catch(console.error); 
});

client.on('voiceStateUpdate', (oldMember, newMember) => {
    let newUserChannel = newMember.channelID;
    let oldUserChannel = oldMember.channelID;
   
    if (newUserChannel === supportchannel) {           
        if (timer == 0) {
            timer = 1
            setTimeout(() => {
                timer = 0
            }, 60000);
            const Userfm = client.users.cache.get(newMember.id);    
            if (Userfm) {
                const channelfx = client.channels.cache.get(dutychannel)
                let roleId = ondutyrole
                channelfx.send(`<@&${roleId}> **${Userfm.tag}** requires Support`);       
            }
        } else {
            return;
        }         
    }
    console.log("User joined vc with id "+newUserChannel)  
});
client.login(process.env.TOKEN);

This doesn't work the way I intended because the cooldown is not separate for every user; instead all users share a cooldown. This blocks every user from getting a moderator's attention for 60 seconds.

I thought that the code ran asynchronously for every user.

The same goes for this code in which I made use of the wait-sync npm library:

const { Client } = require("discord.js");
const { config } = require("dotenv");
const fs = require('fs');
const waitSync = require('wait-sync');

const client = new Client({
    partials: ['MESSAGE', 'CHANNEL', 'REACTION']
});

config({
    path: __dirname + "/.env"
})

var supportchannel = '827574015526567947'
var dutychannel = '847445933969113118'
var ondutyrole = '847447374925398016'

client.on("ready", () => {
    console.log(`Hi, ${client.user.username} is now online!`);
    global.timer = 0;
    client.user.setStatus('online');
    
    client.user.setActivity('me getting developed', { type: "WATCHING"})
        .then(presence => console.log('status set'))
        .catch(console.error); 
});

client.on('voiceStateUpdate', (oldMember, newMember) => {
    let newUserChannel = newMember.channelID;
    let oldUserChannel = oldMember.channelID;
   
    if (newUserChannel === supportchannel) {           
        const Userfm = client.users.cache.get(newMember.id);    
        if (Userfm) {
            const channelfx = client.channels.cache.get(dutychannel)
            let roleId = ondutyrole
            channelfx.send(`<@&${roleId}> **${Userfm.tag}** requires Support`);
            waitSync(60);       
        }
    }
    console.log("User joined vc with id "+newUserChannel)  
});
client.login(process.env.TOKEN);

If you know how to solve this problem please let me know.


Solution

  • What you can do is probably to have some sort of mapping that keeps track of the timer for each user and have the user IDs be the keys:

    const timers = {};
    
    client.on('voiceStateUpdate', (oldMember, newMember) => {
    ...
        // If we don't have any timer set for this user, go ahead and set it
        if (!timers[newMember.id]) {
            timers[newMember.id] = 1;
            setTimeout(() => {
                // Delete the timer from the mapping
                delete timers[newMember.id];
            }, 60000);
    ...