Make a variable persist after a restart of NodeJS - javascript

Let's say I have a message object and it dissapears after nodejs restart. How can I make it persist over restarts?
I really don't want to delve into databases and have additional libraries in the project,
only to make it persist over restarts. I think about storing the object into a file, but after trials I'm unable to figure.
I've tried to save the message object variable to file by using fs and JSON.
newmsg = await message.channel.sendMessage("world");
const fs = require('fs')
const data = await JSON.stringify(newmsg)
// write JSON string to a file
fs.writeFile('msg.json', data, err => {
if (err) {
throw err
}
console.log('JSON data is saved.')
})
What I've got instead is an error of
(node:430541) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Client'
| property '_events' -> object with constructor 'Events'
| property 'packet' -> object with constructor 'EE'
--- property 'context' closes the circle
at JSON.stringify (<anonymous>)
at Client.<anonymous> (/root/bot.js:166:27)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
At this point I'm not sure how to approach saving this circular structure to a file and then restoring it on the next nodejs relaunch. After researching for quite a while this seems to be complex issue that I still do not understand.

Related

Variable throws initialization error after initializing it

I'm trying to make a Discord bot, one of its features is that it plays chess, at the start of the command, I read the JSON file, then check if it's undefined since I'll call a key from the object, so just in case it doesn't exist already, I'll create the data needed
Problem is, when I try to assign the data, it says I haven't initialized the variable, which is weird, because I did initialize it 3 lines before, var, let and const all throw the same error
Another thing is that outside the if statement, just above it, when console.logging it, it works perfectly, but when console.logging it inside the if statement, it throws that error
I honestly have no idea what's happening here, I can't think of anything that's causing this either, is this a NodeJS bug? I feel like it could be.
const games = JSON.parse(fs.readFileSync("./lib/database/games/chess/chess_games.json"))
console.log(games, 'outside if statement') // Here it outputs just fine
if (games[interaction.guild.id] == undefined) {
console.log(games, 'inside if statement') // Here is where it breaks
games[interaction.guild.id] = {
games: {},
players: {},
amount_of_games: 0
}
fs.writeFileSync("./lib/database/games/chess/chess_games.json", JSON.stringify(games, null, 2));
const games = JSON.parse(fs.readFileSync("./lib/database/games/chess/chess_games.json"))
}
This is the error:
ReferenceError: Cannot access 'games' before initialization
at Object.execute (C:\Users\aschr\Dropbox\Coding\Javascript\entropic_bot\lib\database\bot\commands\chess.js:40:4)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Client.<anonymous> (C:\Users\aschr\Dropbox\Coding\Javascript\entropic_bot\index.js:44:3)
You have two variables with the same name.
Change the name of one of them
The second games is tried to be accessed instead of the first one because of hoisting. That's why you are getting the error

can't seem to append to a .json array

Currently I'm trying to add to a blacklist array for my discord bot. Reading the array works fine, but whenever I try to append to the bot node exits with code 1.
I've tried a few ways, but none really seem to work.
const data = require('./data.json');
data.serverData.blackListedColors.append(randomRoleColor);
which returns:
data.serverData.blackListedColors.append(randomRoleColor);
^
TypeError: data.serverData.blackListedColors.append is not a function
at Client.<anonymous> (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\bot.js:363:59)
at Client.emit (events.js:209:13)
at MessageCreateHandler.handle (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\packets\handlers\MessageCreate.js:9:34)
at WebSocketPacketManager.handle (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\packets\WebSocketPacketManager.js:105:65)
at WebSocketConnection.onPacket (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:333:35)
at WebSocketConnection.onMessage (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:296:17)
at WebSocket.onMessage (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\node_modules\ws\lib\event-target.js:120:16)
at WebSocket.emit (events.js:209:13)
at Receiver.receiverOnMessage (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\node_modules\ws\lib\websocket.js:789:20)
at Receiver.emit (events.js:209:13)
Process finished with exit code 1
I've tried using .parse and .add but those also don't seem to work.
using .push doesn't seem to crash it, but also doesn't add the string to the .json file.
edit:
here's the .json file for reference
{
"serverData": {
"muffinID": "[disocrdID]",
"token": "[token]",
"blackListedColors": []
}
You can't use require() for data that changes during the lifetime of your bot process due to the Node require cache. In addition, just modifying data won't automagically persist it back into the file; you'll have to write it back into the file.
It's not rocket surgery to write yourself, but you may want to look into a library that does this for you – I haven't tried this, but it looks promising enough: https://www.npmjs.com/package/json-persistent-object

Can I re-create an error object with the original stack and stack frames?

I'm familiar with creating a custom Error object in JavaScript like this.
class CustomError extends Error {
constructor(args) {
super(...args);
Error.captureStackTrace(this, CustomError);
}
}
But given an exception/error that has already been thrown elsewhere I want to create a new error object that is a clone/copy of the original including the stack.
My context is that I'm using a log reporter, e.g. Winston, to capture events and I would like to post error messages to Sentry. Sentry provides a way to capture exceptions like this -
try {
aFunctionThatMightFail();
} catch (err) {
Sentry.captureException(err);
}
The problem though is that Sentry assumes that where the error is captured is where the error was thrown.
One of the benefits of Sentry is that it can report the line numbers of where the error occurred in an app but because I'm aggregating the logs the stack frame from the original error has been lost. I can save additional meta-data which I can send to Sentry but it still highlights the line with Sentry.captureException as the origin of the error and the stack frames from calling Winston.
The Sentry SDK assembles a JSON payload from the Error object you pass to captureException. I think you'll just want to assemble that payload directly and send it using captureEvent. See https://docs.sentry.io/development/sdk-dev/attributes/ for more information.

sqlite3 database: TypeError: Cannot read property of undefined

I have a Node.js script that used to work, but after I switched to another VM it does not work anymore. Can anyone see what the problem is? Here is the function, db is a database:
this.start = function() {
logger.debug('Starting up.');
db.serialize(() => {
db.run("DELETE FROM jobs WHERE status = 'failed'")
.run("UPDATE jobs SET status = 'queued'", (err) => {
if (err) {
logger.error(err.message);
} else {
logger.info('done');
}
});
});
}
Now I get the following error:
TypeError: Cannot read property 'run' of undefined
at Database.db.serialize ()
at TransactionDatabase.serialize
at module.exports.start
at Object.<anonymous>
...
The error is pointing at the second ".run".
My Node.js version is 10.4.1, sqlite3 version 3.8.2.
What am I missing? Some module?
I think I found the answer. Chaining run() runs the queries nearly at the same time. According to this answer, the function run() starts the query, but returns immediately.
However, if you serialize and chain, those two methods cannot be used at the same time. You are trying run queries sequentialy, but also at the same time.
Although, depending on your needs, you can nest serialize, parallelize or callbacks, as shown in the "control flow" doc.
I guess the method serialize() "locks" chaining by changing the return value of run() to undefined.

How can a yeoman sub-generator access variables defined on the main generator?

I am doing a sub-generator and I want to pass to this sub-generator variables defined on the main generator:
Some thing like that :
writing: function() {
console.log(this.appversion);
var email = this.email; // Variable defined on the main controller.
this.fs.copy(
this.templatePath('somefile.js'),
this.destinationPath('somefile.js')
);
I've tried to do something like the code below on the main generator:
this.composeWith('jstack1:controller', {options: {name: 'some-name'}});
and the code below on the sub-generator
this.option('name', {/* settings */});
But I am not sure if it is a good way to do this ,furthermore I always get the following error message:
Error: Did not provide required argument [1mname[22m!
at null. (C:\Users\Alexandre_\generator-jstack1\generator-generator-jstack1\node_modules\yeoman-generator\lib\base.js:359:33)
at Array.forEach (native)
at Base.checkRequiredArgs (C:\Users\Alexandre_\generator-jstack1\generator-generator-jstack1\node_modules\yeoman-generator\lib\base.js:355:19)
at argument (C:\Users\Alexandre_\generator-jstack1\generator-generator-jstack1\node_modules\yeoman-generator\lib\base.js:321:8)
at module.exports.yeoman.generators.Base.extend.initializing (C:\Users\Alexandre_\generator-jstack1\generator-generator-jstack1\generators\controller\index.js:6:10)
at C:\Users\Alexandre_\generator-jstack1\generator-generator-jstack1\node_modules\yeoman-generator\lib\base.js:421:16
at processImmediate [as _immediateCallback] (timers.js:383:17)
But I am not sure if it is a good way to do this
Yes, this is the correct way to do it. The only way generators communicate with each other is via options and arguments. (There's also some communication possible through cache/configurations and the file system, but these are not frequent channels.)
About the error, my guess is you're extending generators.NamedBase rather than generators.Base.
I have found a way to do that, and it is very simple.
First at the main generator I add the context variables at the storage:
var templateContext = {
appname: this.appname,
appdescription: this.appdescription,
appversion: this.appversion,
applicense: this.applicense,
appautor: this.appautor,
appemail: this.appemail
};
this.config.set('templateContext',templateContext);
then at the sub-generator I get the templateContext with:
var templateContext = this.config.get('templateContext');

Categories

Resources