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
Related
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.
I'm working on a discord bot that does a variety of things. It keeps randomly crashing and I tracked down the source of the error. What it does is it downloads images sent to it in a discord private message, hashes them and sets their name to the hash to make sure no duplicates exist. This is not the most efficient way to do so but it works.
Here's my code (it's a bit messy)
message.attachments.forEach(url => {
if (!message.author.bot) {
function getDate() {
let d = new Date().getTime();
return d;
}
tempdate = new getDate();
ext = path.extname(url.url);
request.get(url).on(`error`, console.error).pipe(fs.createWriteStream(`./temp/Img-${tempdate}${ext}`));
hash = sha256(`./temp/Img-${tempdate}${ext}`);
if (fs.existsSync(`./attachments/Img-${hash}${ext}`)) {
request.get(url).on(`error`, console.error).pipe(fs.createWriteStream(`./attachments/Img-${hash}${ext}`));
console.log(`Error: Duplicate File`)
}
fs.createWriteStream(`./attachments/Img-${hash}${ext}`);
console.log(`Wrote Hashed File.`)
fs.unlinkSync(`./temp/Img-${tempdate}${ext}`)
}
})
Every once in a while, however it returns this:
fs.js:1061
return binding.unlink(pathModule._makeLong(path));
^
Error: ENOENT: no such file or directory, unlink 'C:\Users\xxxxx\Desktop\testbot\temp\Img-[object Object].png'
at Object.fs.unlinkSync (fs.js:1061:18)
at Client.client.on.message (C:\Users\xxxxx\Desktop\testbot\yes.js:48:20)
at emitOne (events.js:116:13)
at Client.emit (events.js:211:7)
at MessageCreateHandler.handle (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\packets\handlers\MessageCreate.js:9:34)
at WebSocketPacketManager.handle (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\packets\WebSocketPacketManager.js:103:65)
at WebSocketConnection.onPacket (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:333:35)
at WebSocketConnection.onMessage (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:296:17)
at WebSocket.onMessage (C:\Users\xxxxx\node_modules\ws\lib\event-target.js:120:16)
at emitOne (events.js:116:13)
I assume I did something wrong here. I can't find any answers online
You are mixing synchronous and asynchronous code.
The .pipe() is absolutely asynchronous, yet you're trying to assume that it's done immediately. You can't reliably program this way.
You need to listen for an event to know when the .pipe() is done before you can proceed with the rest of the processing.
And, as soon as you make it event driven, your .forEach() will run ahead with multiple iterations trying to run at the same time and then the fact that your variables aren't declared locally inside the loop will cause variable conflicts.
Also, what is this line of code doing all by itself:
fs.createWriteStream(`./attachments/Img-${hash}${ext}`);
You create a write stream, but don't even keep track of it. That will just leak a file handle.
As for solutions, it really requires a significant rewrite. I've answered a similar question here Issue with request and .pipe(). In that answer, I created a promise wrapper for .pipe() which would then let you use await to sequence a pipe operation. I'd probably use that solution in rewriting this code.
I'm somewhat new to javascript, but I wanted to try to understand how stack-tracing works in nodejs. I was a little lazy to look at the source code, so I just consulted the language reference https://www.ecma-international.org/ecma-262/10.0/index.html#sec-runtime-semantics-catchclauseevaluation and tried to see what works. So, here is the mystery: in the repl I get some odd results when catching and throwing.
When I run:
try { throw Error('foo'); } catch (e) { throw e; }
I get as output:
Error: foo
But when I run:
try { throw Error('foo'); } catch (e) { console.log(e); throw e; }
I get as output:
Error: foo
at repl:1:13
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at REPLServer.defaultEval (repl.js:240:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:468:10)
at emitOne (events.js:121:20)
at REPLServer.emit (events.js:211:7)
at REPLServer.Interface._onLine (readline.js:282:10)
at REPLServer.Interface._line (readline.js:631:8)
Error: foo
at repl:1:13
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at REPLServer.defaultEval (repl.js:240:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:468:10)
at emitOne (events.js:121:20)
at REPLServer.emit (events.js:211:7)
at REPLServer.Interface._onLine (readline.js:282:10)
at REPLServer.Interface._line (readline.js:631:8)
It seems as though calling console.log(e) causes the error object to trace the execution context stack (I hope I'm using this term correctly), but if it passes through to the repl without the context.log(e) it only knows about its error message.
I've tried different permutations of this and also nesting in different lexical contexts (functions and another try-catch block), and they all seem to give this "funny" behavior. Also, when executing these as scripts I always get an expected error message and stack-trace, so I'm led to believe that the implementation of the repl is at fault here. However, before diving into the source, I wanted to probe some experts to see if there is some good reason for this behavior, or if it is just a stupid corner case that I've come up with.
Well, I don't know if it's kosher to answer my own question, but I dug into the nodejs source and found out what the deal is. I tracked it down to repl.js: line 404. Here there is a call to script.runInThisContext. At this point, the line throw new Error() "escapes" the repl and ends up getting caught a few lines below. Here, there is check for process.domain, which apparently succeeds in the repl, but not in a script context. This then emits a domain error, which doesn't do much. If you go to self._domain.on('error',... you will see that all the stack tracing stuff is taken care of here.
I actually added a line self._domain.emit('error'... to the source right before the process.domain.emit('error',... call, and it then printed out a nice stack trace for me, so I'll write the guys over at nodejs and see if it is something they would like to fix. There may be a very good reason not to do so, I didn't look into it in that great of detail.
At this point I'm reminded of a scene from Burn after Reading. Watch it if you have a dry sense of humor: https://www.youtube.com/watch?v=46h7oP9eiBk
I'm probably misunderstanding exactly what's being returned by the members object here, but I'm attempting to test a deafen command within my bot - the documentation for discord.js states that the Message object contains a Mentions property that holds all the valid mentions within a sent message, from there, I can drill down a bit further and get to the Members property from within Mentions - which returns a collection of GuildMembers that have been mentioned in the message - which is what I want. The problems arises when I attempt to grab those GuildMember objects from within the collection that I get back - I'm not quite sure what I'm doing wrong, but I get back errors. The code I have is as follows:
module.exports = {
name: 'deafen',
cooldown: 5,
description: 'Deafens mentioned user or users.',
args: true,
execute(message) {
const taggedMembers = message.mentions.members;
for(const member of taggedMembers) {
member.setDeaf(true)
.then(() => console.log(`Deafened ${member.displayName}`))
.catch(console.error);
}
},
};
and I get back this error in my terminal window:
TypeError: member.setDeaf is not a function
at Object.execute (/home/tai/dev/FutabaBot/commands/admin/deafen.js:10:20)
at Client.<anonymous> (/home/tai/dev/FutabaBot/FutabaBot.js:80:17)
at Client.emit (events.js:210:5)
at MessageCreateHandler.handle (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js:9:34)
at WebSocketPacketManager.handle (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js:105:65)
at WebSocketConnection.onPacket (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/WebSocketConnection.js:333:35)
at WebSocketConnection.onMessage (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/WebSocketConnection.js:296:17)
at WebSocket.onMessage (/home/tai/dev/FutabaBot/node_modules/ws/lib/event-target.js:120:16)
at WebSocket.emit (events.js:210:5)
at Receiver.receiverOnMessage (/home/tai/dev/FutabaBot/node_modules/ws/lib/websocket.js:789:20)
I'm not sure if it's necessary, but I can post the other pieces of code that relate to this - and the documentation for discord.js can be found here
The message.mentions.members returns a Collection of GuildMembers and a Snowflake (some unique identifier. A Collection is an extension of the Map class, where a map is an array with each item having a key and a value attribute.
When looping over a map, like you're doing with for(const member of taggedMembers), each item (or member in your case) is a key and a value pair. Then you're trying to call the setDeaf method on that pair, which as you've seen doesn't work.
What you want is to only loop over the values in the collection. This can easily be done with a .forEach. I'll add some example code down below:
taggedMembers.forEach((member) => {
member.setDeaf(true)
.then(() => console.log(`Deafened ${member.displayName}`))
.catch(console.error);
});
Give it a go and let me know if you run into any more issues.
EDIT: As slothiful pointed out in the comments, using a .forEach won't catch any Promise rejections even after attaching a .catch(). To fix this you can use the for ... of loop but destructuring the pair. This can be seen in the example below:
for (let [, member] of taggedMembers) {
member.setDeaf(true)
.then(() => console.log(`Deafened ${member.displayName}`))
.catch(console.error);
}
here's a snippet of my code
console.log("IS RESTCLIENT UNDEFINED?");
console.log(restClient === undefined);
console.log(restClient);
restClient.get(options, function(resterr, restreq, restres, restobj) {
assert.ifError(resterr);
//not important
return next();
});
The problem is that when it gets executed (it's basically most of the handler for a route, simple stuff, nothing esoteric) the output is as following:
IS RESTCLIENT UNDEFINED?
false
JsonClient {<a lot of stuff describing the client>}
assert.js:327
assert.ifError = function(err) { if (err) throw err; };
^
InternalError: restClient is not defined
at parseResponse (/srv/oms-profiles-module/node_modules/restify/lib/clients/json_client.js:67:23)
at IncomingMessage.done (/srv/oms-profiles-module/node_modules/restify/lib/clients/string_client.js:151:17)
at IncomingMessage.g (events.js:260:16)
at emitNone (events.js:72:20)
at IncomingMessage.emit (events.js:166:7)
at endReadableNT (_stream_readable.js:893:12)
at doNTCallback2 (node.js:430:9)
at process._tickCallback (node.js:344:17)
The client is defined at the beginning of the file "as usual" (worked until now), and it uses the npm module restify. I don't even understand if the problem could be the scoping or what else, has anybody gotten any good advice on where I could bang my head on?
Thanks
UPDATE: this is not a problem with scopes and such, I presume; I suspect it is something with restify because otherwise the error would be something like "ReferenceError: restClient is not defined". Still, I am at a loss since it was working before and I did not introduce any major change for it to break. Restify version is 3.0.3
Alright, the answer is simple and I feel stupid (as I should),
the assert was being fired as there is a resterr. It comes from the API handler that is being called with restClient.get()
However I was expecting the whole (other) program to do something like crash (on another assert) or to tell me ReferenceError: restClient is not defined, but analysing the logs it did NOT happen. Thus I thought that the problem was in the snippet up here and not in the remote API that I was calling (still coded by me).