Variable undefined when using .get() - node.js

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!

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

Discord.js Bot rate limit [duplicate]

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!

How to Increment Value Atomically with Redis

I have below code in nodejs:
const decrease = async (userId, points) => {
const user = await redisClient.hgetall(userId);
if(user.points - points >= 0) {
await redisClient.hset(userId, userId, user.points - points);
}
}
since async/await is not blocking the execution, if there are multiple requests for the same userId, the code is not running as atomically. That means the user points may be decreased multiple times even there is not enough point left on users account. How can I make the method run atomically?
I have checked redis multi command and it works for multiple redis statements. But in my case, I need to calculate the user points which is not part of redis command. So how to make them run as an atomic function.
I also read the INCR pattern: https://redis.io/commands/incr
But it doesn't seem to fix my issue. The patterns listed there need to work with expire which I don't have such requirement to give a specific timeout value.
Use the power of (Redis) server-side Lua scripts by calling EVAL. It should probably look something like this:
const lua = `
local p = redis.call('HGET',KEYS[1],'points')
local d = p - ARGV[1]
if d >= 0 then
redis.call('HSET', KEYS[1], 'points', d)
end`
const decrease = async (userId, points) => {
await redisClient.eval(lua, 1, userId, points);
}

How should I avoid out of memory using nodejs?

var pass = require('./pass.js');
var fs = require('fs');
var path = "password.txt";
var name ="admin";
var
remaining = "",
lineFeed = "\r\n",
lineNr = 0;
var log =
fs.createReadStream(path, { encoding: 'utf-8' })
.on('data', function (chunk) {
// store the actual chunk into the remaining
remaining = remaining.concat(chunk);
// look that we have a linefeed
var lastLineFeed = remaining.lastIndexOf(lineFeed);
// if we don't have any we can continue the reading
if (lastLineFeed === -1) return;
var
current = remaining.substring(0, lastLineFeed),
lines = current.split(lineFeed);
// store from the last linefeed or empty it out
remaining = (lastLineFeed > remaining.length)
? remaining.substring(lastLineFeed + 1, remaining.length)
: "";
for (var i = 0, length = lines.length; i < length; i++) {
// process the actual line
var account={
username:name,
password:lines[i],
};
pass.test(account);
}
})
.on('end', function (close) {
// TODO I'm not sure this is needed, it depends on your data
// process the reamining data if needed
if (remaining.length > 0) {
var account={
username:name,
password:remaining,
};
pass.test(account);
};
});
I tried to do something like test password of account "admin", pass.test is a function to test the password, I download a weak password dictionary with a large number of lines,so I search for way to read that many lines of weak password,but with code above, the lines array became too large ,and run out of memory,what should I do?
Insofar as my limited understanding goes, you need to watch a 1GB limit, which I believe is imposed by the V8 engine, actually. (Here's a link, actually saying the limit is 1.4 GB, currently, and lists the different params used to change this manually.) Depending on where you host your node app(s), you can increase this limit, by a param set on the command line when node is started. Again, see the linked article for a few ways to do this.
Also, you might want to make sure that, whenever possible, you use buffers, instead of converting things like data streams (from a DB or other things, for instance) to arrays/whatever, as this will then load the entire dataset into memory. As long as it lives in a buffer, it doesn't contribute to the total memory footprint of your app.
And actually, one thing that doesn't make sense, and that seems to be very inefficient in your app, is that, on reading each chunk of data in, you then check your username against EVERY username you've amassed so far, in your lines array, instead of the LAST one. What your app should do is keep track of the last username and password combo you've read in, and then delete all data before this user, in your remaining variable, so you keep your memory down. And since it's not a hold all repository for every line of your password file anymore, you should probably retitle it something like buffer or something. This means that you'd remove your for loop, since you're already "looping" through the data in your password file, by reading it in, chunk by chunk.

Resources