Discord.js Bot rate limit [duplicate] - node.js

This question already has answers here:
Discord.js - Cooldown for a command for each user not all users
(3 answers)
Closed 2 years ago.
So i want to upload my discord bot online but i want to prevent users from spamming it's commands.
So let the delay be 5 seconds, if user runs !help the bot answers, but if user run !help before the delay is expired bot says: wait some time before using this command again.
Also i want the delay to work only for message author and not affect other users.
I'm using command handler so is it possible to make something like module.exports.delay?

to do that you can use timestamps like:
let msgTimestamp = [];
if(message.content === "?" + "help") {
if(typeof msgTimestamp[0] !== "undefined") {
if(msgTimestamp[0] + 5000 < Date.now()) {
message.channel.send("Here embed of command help.");
msgTimestamp = [];
} else {
message.channel.send("wait some time before using this command again.");
}
} else {
msgTimestamp.push(Date.now());
message.channel.send("Here embed of command help.");
}
}
In this coode you use timestamps, you check if the content of the message is eualt to the command you want to use, if the user had already sent a message, the variable which containing the message timestamp is full, you check then if the the typeof of the variable is not undefined and then you check id the timestamp of when the user sent the message is lower than the actual timestamp + how many delay you want!

Related

NodeJS - While loop until certain condition is met or certain time is passed

I've seen some questions/answers very similar but none exactly describing what I would like to achieve. Some background, this is a multi step provision flow. In pretty short words this is the goal.
1. POST an action.
2. GET status based in one variable submitted above. If response == "done" then proceed. Returns an ID.
3. POST an action. Returns an ID.
4. GET status based on ID returned above. If response == "done" then proceed. Returns an ID.
5. (..)
I think there are 6/7 steps in total.
The first question is, are there any modules that could help me somehow achieve this? The only requirement is that each attempt to get status should have an X amount of delay and should expire, marking the flow as failed after an X amount of time.
Nevertheless, the best I could get to, is this, assuming for example step 2:
GetNewDeviceId : function(req, res) {
const delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
var ip = req;
async function main() {
let response;
while (true) {
try {
response = await service.GetNewDeviceId(ip);
console.log("Running again for: " + ip + " - " + response)
if (response["value"] != null) {
break;
}
} catch {
// In case it fails
}
console.log("Delaying for: " + ip)
await delay(30000);
}
//Call next step
console.log("Moving on for: "+ ip)
}
main();
}
This brings couple of questions,
I'm not sure this is indeed the best/clean way.
How can I set a global timeout, let's say 30 minutes, forcing it to step out of the loop and call a "failure" function.
The other thing I'm not sure (NodeJS newbie here) is that, assuming this get's called let's say 4 times, with different IP before any of those 4 are finished, NodeJS will run each call in each own context right? I quickly tested this and it seems like so.
I'm not sure this is indeed the best/clean way.
It am unsure whether your function GetNewDeviceId involves a recursion, that is, whether it invokes itself as service.GetNewDeviceId. That would not make sense, service.GetNewDeviceId should perform a GET request, right? If that is the case, your function seems clean to me.
How can I set a global timeout, let's say 30 minutes, forcing it to step out of the loop and call a "failure" function.
let response;
let failAt = new Date().getTime() + 30 * 60 * 1000; // 30 minutes after now
while (true) {
if (new Date().getTime() >= failAt)
return res.status(500).send("Failure");
try {...}
...
await delay(30000);
}
The other thing I'm not sure (NodeJS newbie here) is that, assuming this get's called let's say 4 times, with different IP before any of those 4 are finished, NodeJS will run each call in each own context right?
Yes. Each invocation of the function GetNewDeviceId establishes a new execution context (called a "closure"), with its own copies of the parameters req and res and the variables response and failAt.

im making a purge command for my discord bot but it aint working i intend it to

im trying to make a purge command for my discord bot in node.js but everytime it removes one less than it should do(cus the bot thinks the message i send is also one of the messages i want 2 delete) but when i try to do some simple math to just make the number one higher i get something like this:
10 + 1 = 101 at least that is what the code does this is the code for the purge command i currently use:
const args = msg.content
.slice(prefix.length)
.trim()
.split(" ");
const Args2 = args[2];
const DeleteAmount = Args2;
if (!args[2]) return msg.channel.send("Please Specify a amount");
if (args[2] > 100) return msg.channel.send("You cant delete a amount higher than 100")
if (args[2] < 1) return msg.channel.send("You cant delete less than one message")
msg.channel.bulkDelete(DeleteAmount);
msg.channel.send(`${DeleteAmount} messages have been deleted`);
} else if(msg.content.startsWith(`${prefix}PurgeTest`)) {
msg.channel.send("You Do not have Permission to delete messages");
msg.channel.send("Only people with the role CustomBotAdmin can delete messages");
}
sorry for the messy text...
edit: ah i got it fixed

Variable undefined when using .get()

I am coding a Discord bot and one of my features is a Member counter. So far I can point it in the right direction and as long as I use the actual id number is numerical form everything works fine. The moment I try to create and store the id in a variable however, it starts to tell me that it is undefined.
let setchan = ""
let guildid = 0
client.on('message', async message => {
if (!message.content.startsWith(prefix2) || message.author.bot) return;
const args = message.content.slice(prefix2.length).trim().split(" ");
const command = args.shift().toLowerCase();
if (command === 'memchan'){
setchan = args
message.channel.send("Channel set to: " + setchan)
} else if (command === 'count') {
total = client.guilds.cache.get(message.guild.id).memberCount
guildID = client.guilds.cache.get(message.guild.id)
message.channel.send("Current member count: " + total)
message.channel.send("Your server id is: " + guildID)
message.channel.send("Your channel id is set to: " + setchan)
message.guild.channels.cache.get(setChan).setName("Members: " + total)
}
});
works fine
message.guild.channels.cache.get(setchan).setName("Members: " + total)
Does not though.
I have used .toString() and tried to set it as a number instead of string. Can someone please tell me what I am doing wrong?
Created the variable as both num and string.
the error I am getting is: Cannot read property 'setname' of undefined.
There are a few issues with your approach.
The declaration of setchan is inside your command handler. This means that every time the command handler is executed (once per message) it is re-set to "". If you change this value in one execution of the command handler, it will not persist to any other execution.
Even if this were working properly, you are setting setchan = args. args is the result of a .split(" ") - it is an array of arguments, not just one. You need to get args[0] if you want to respect only the first argument.
This is a poor way to implement any kind of configuration variables. When your bot restarts, the current memory state is lost. Since you're holding these only in memory, that means users will need to run memchan again every time the bot restarts. Additionally, if your bot ever joins more than just one guild, users in each guild will overwrite other guilds configuration.
My recommendation: Ditch the current approach. What you should do is either store configuration data in a map (object, Map, etc) and write that map to disk as JSON, loading it from disk on startup, so that the data can persist between restarts - or use a proper database, either a RDBMS/ORM/other. Look into SQL databases, Mongo, sequelize, etc.
To fix your current approach: the setchan variable needs to be declared once at the beginning of the file. You can store it on the bot if you like, it is quite convenient to do so.
//top level
bot.setchan = ""
...
//in memchan command
bot.setchan = args[0]
//in count command
message.guild.channels.cache.get(bot.setchan).setName("Members: " + total)
So this is how I eventually worked it out without needing to store the channel id in a DB ( Iam trying to avoid any extra weight if I can). So instead of creating a variable I just pass the desired channels id as an arg. A user can then use their choice of already running mod bots like mee6, dyno, carl etc to use an automessage to tell my bot when to work.
client.on('message', async message => {
if (!message.content.startsWith(prefix2) || message.author.bot) return;
const args = message.content.slice(prefix2.length).trim().split(" ");
const command = args.shift().toLowerCase();
if (command === 'run') {
let setchan = args[0]
total = client.guilds.cache.get(message.guild.id).memberCount
message.channel.send("Current member count: " + total)
message.guild.channels.cache.get(setchan).setName("Members: " + total)
message.delete()
}
});
Then deletes the command (as I havent added a role check yet)...
Thank you kuso and klay!

Counting game discord js

Hewo everyone. I created my own bot
I have a lot of great things like games etc.
However, I want to do a couting game.
I have a channel called "Counting"
And i want to setup my bot do like:
User 1: 456
User 2: 457
Bot: 458
My question is, How can I make the bot count when no one else is counting? But just once. (Look the example^^)
If you can, can you give me an example code please? Thanks!!
Try this:
const {Client} = require('discord.js')
const client = new Client()
// Stores the current count.
let count = 0
// Stores the timeout used to make the bot count if nobody else counts for a set period of
// time.
let timeout
// Discord.js v12:
// client.on('message', ({channel, content, member}) => {
// Discord.js v13:
client.on('messageCreate', ({channel, content, member}) => {
// Only do this for the counting channel of course
// If you want to simply make this work for all channels called 'counting', you
// could use this line:
// if (client.channels.cache.filter(c => c.name === 'counting').has(channel.id))
if (channel.id === 'counting channel id') {
// You can ignore all bot messages like this
if (member.user.bot) return
// If the message is the current count + 1...
if (Number(content) === count + 1) {
// ...increase the count
count++
// Remove any existing timeout to count
if (timeout) clearTimeout(timeout)
// Add a new timeout
timeout = setTimeout(
// This will make the bot count and log all errors
() => channel.send(++count).catch(console.error),
// after 30 seconds
30000
)
// If the message wasn't sent by the bot...
} else if (member.id !== client.user.id) {
// ...send a message because the person stuffed up the counting (and log all errors)
channel.send(`${member} messed up!`).catch(console.error)
// Reset the count
count = 0
// Reset any existing timeout because the bot has counted so it doesn't need to
// count again
if (timeout) clearTimeout(timeout)
}
}
})
client.login('your token')
Explanation
When a user (that isn't the bot) sends a message in the counting channel, the bot checks if the user is counting correctly (if (Number(content) === count + 1)).
If they are, it increments count, removes the timeout if it exists (if (timeout) clearTimeout(timeout)), and schedules the bot to count after 30 seconds (setTimeout(() => channel.send(++count), 30000)).
If they aren't, the bot sends a message, resets count, and clears the timeout (if it exists).
When the bot sends a message, it won't trigger any of that. When the bot counts, Number(content) === count because it was already incremented.

Node JS : socket.io - handling multiple connection

I am a newbie in using socket.io in Node JS - however I have coded a client (web page) / server program to render some statistics data at client side.
The requirement is like – to render a box statistics (out of many). Since the user can open multiple browser windows - we have a scenario where one box data can be requested by many times:
http://www.tool.com?boxname=box1
As such I want to achieve spawning one job for multiple requests for same box. Below is the logic I have implemented to meet the requirement:
Client Side:
Establishes a connection to server creating a data channel:
socket.on(boxname.toLowerCase(), function (data){
}
So whenever there is a data in boxname event I receive, parse the data and render it.
Server Side
First call an expect script to copy a script to run within an infinite loop on a box.
On successful copying of the script in the box - invoke another expect script to start its execution:
exports.ServiceMonitor = function(boxName, res) {
Step 1.
I need to copy a script as many times request comes else I would not be able to enter ‘exit’ event of this spawned process where I start the script execution as mentioned below:
var child = spawn('./scripts/copyscriptonbox.sh' , [boxName.toLowerCase(), getURL(boxName)]);
In the later part of the code I keep adding box names to boxnames variable which is declared global. So on a new boxname request - I search for number of occurrences of boxname in boxnames variable. If the count is 1 or more it would mean to me that a script is already running on that box:
var noofbox = boxnames.split(boxName.toLowerCase()).length - 1;
child.on('exit', function (code) {
logger.info('child stream exit: ' + code);
if(code == 0)
{
boxNames += boxName.toLowerCase();
logger.info('Box name added: ' + boxNames);
res.render('boxpage', {}); //render a web page
io.sockets.on('connection', function (socket) {
logger.info('Connected to box :' + boxName);
if(noofbox <= 0)
schild = spawn('./scripts/boxmon.sh', [boxName.toLowerCase(), getURL(boxName)]);
schild.on('exit', function (code) {
logger.info('schild stream exit.');
});
schild.stderr.on('data', function (data) {
logger.info('stderr: ' + data);
});
schild.stdout.on('data', function (data) {
//generate response
});
socket.on('disconnect',function(){logger.info('Disconnect Killed');ServiceMonitorEnd(boxName.toLowerCase());});
});
}
});
}
The issue is that if in two browser window I access URL : www.tool.com?boxname=box1 - first time I get the log only once (Connected to box : box1) but second time I get the same logs 2 times where as I was expecting it to be one time - I mean as many request comes in after the first one the logs gets printed that many times – if 5 then log gets printed for 1(first time)+2 (second time)+3(third time)+4 (fourth time)+5 (fifth time)? I understand that when ‘connection’ event is called for x times then it enters that many times for each connection.
How can I make the 'connection' event on the socket.io once for each request only?
Use socket.once instead of socket.on, it is another EventEmitter like .on, that is emitted only once, the first time. Check the docs. But remember after that any such events will not be received by the client.

Resources