Getting the values from a JSON into a discord.js embed - javascript

I'm making a discord.js bot where all the command data is stored in a JSON file and when the help command is run, I want it to iterate through the JSON, at the level the user entered.
So far, I've managed to check if the value entered is a command or a category, and I have been able to console.log all the information and it shows the correct data I want to display on the embed.
For example, in my JSON file I have:
{
"category1": {
"command1": {
"Aliases": [],
"Description": "Runs a command"
}
}
}
When I do the console.log() when the user runs g!help category1 it outputs:
command1
Runs a command
and when it sends the embed I get:
[object Object]
undefined
Here is the part of the code which iterates through the JSON and adds the data to the embed:
for (ctg in help_data) {
if (args[0] === ctg) {
embed.setTitle(ctg)
for (command in help_data[ctg]) {
var cmd = command
var desc = help_data[ctg][command].Description
console.log(cmd)
console.log(desc)
embed.addField({
"name": cmd,
"Value": desc
})
}
}
}
I don't understand why it changes between the console.log and the embed.addField, if someone could please explain why this happens and how I can fix it, that would be amazing!

embed.addField takes three parameters, the first is the name of the field, the second is its value. Both of these are strings. In your example, your first argument is an object that gets converted to a string (and hence becomes [object Object]) and the second one (as you don't provide anything) is undefined.
To fix this, you can either add the two strings like this:
embed.addField(cmd, desc);
Or use the addFields method instead, that accepts an object like this:
embed.addFields({
name: cmd,
value: desc,
});

Related

Extract variables from json string

Context :
I'm making a Discord bot using the discord.js library and MongoDB (with mongoose). I created a script that can construct messages from data stored in my MongoDB database. For exemple, if an user on my Discord server type "!hello", it will query my database to construct a reply like "Hello to you!".
I store my reply message data in an document like this :
{
"command": "!hello",
"content": "Hello to you!"
}
(I voluntary omitted some properties here that are useless in the context of my question)
And retrieve it in my code with something like :
let retrievedReply;
await mongo().then(async mongoose => {
try {
let query = {command: message.content};
retrievedReply = await replySchema.findOne(query);
} finally {
mongoose.connection.close();
}
});
message.reply(retrievedReply.content);
My issue :
Now, imagine I want it to respond "Hello username!".
If I didn't construct my reply using data from mongo I could simply do something like :
message.reply(`Hello ${message.member.nickname}!`);
or
message.reply("Hello " + message.member.nickname + "!");
So, in other words, using backticks and ${} syntax or (but I like it less), splitting my string and concatenate my property value like the second example.
The thing is, we can't store a string inside backticks in json format (used by MongoDB). So even if I build my document like so :
{
"command": "hello",
"content": "Hello ${message.member.nickname}!"
}
${message.member.nickname} would not be interpreted like a property value but just like a simple string.
The 1000$ question :
How can I use this property inside my code? I thought about extracting it using regex but it does not change the fact that it will still be a string. Is there a way to "convert" from a string to an usable variable?
EDIT
Since Brettski asked me (rightfully), here is an example of what I want :
message.member.send(`Hello ${message.member}!`);
This message.member property here represent a member object of a Discord guild. It contains a lot of things like its permissions on the guild, its roles, etc.
When we use a member object into a message, Discord will make a mention of the member like bellow.
It works the same with other properties like channels for example. Users can then click on those to see informations about the member or to go directly to the channel, etc. I want to be able to use this.
That is correct you will not be able to store a string literal in Mongo or other way.
One though I had is to put a token in your string and then do a replace on it in message.reply() or before it.
example:
const helloCommand = {
"command": "hello",
"content": "Hello ~~membernickname~~!"
};
In this example the token to replace is ~~membernickname~~
You can use something like this to do the replace:
const command = JSON.parse(helloCommand)
message.member.send(command.content.replace('~~membernickname~~', message.member));
The resulting string being sent to the send() command is the same. So if the nickname is #brettski, the string 'Hello #brettski' will be sent to the send() command.
In your example:
message.reply(`Hello ${message.member.nickname}!`);
the part:
`Hello ${message.member.nickname}!`
is a string literal (template literals). What the function message.reply() ends up getting is 'Hello Nickname' a string. So the replace should work. As a test you can hard code the string used in message.reply() to a known nickname and it should provide the results you expect.
Ok so, #Brettski put me on the right track with the fact that I can use users' or channels' IDs directly as strings within the message and between specific tags.
To reuse the example in my question, I can use this syntax to mention an user :
"<#idOfTheUser>"
What I did is if I want to mention the author of the command in my reply, I put the text in my mongoDB document as so :
const helloCommand = {
"command": "hello",
"content": "Hello $member$!"
};
Then, in my code, I can simply use :
message.reply((retrieveReply.content).replace(/\$member\$/g, `<#${message.member.id}>`))
Of course, I wanted to use more properties so I did a function like :
const messageContentReplace = function(content) {
const replaceableStrings = {
"$member$": `<#${message.member.id}>`,
"$memberName$": message.member.displayName,
"$guild$": message.guild.name
}
return content.replace(/(\$member\$)|(\$memberName\$)|(\$guild\$)/g, match => {
return replaceableStrings[match];
});
(in reality, I did something more complexe because I use an embed message as my response with multiple fields etc... See here about embeds)

Firebase: Convert array of objects into a true array on the front end - weird behavior

I am using onSnapshot to get data from Firebase to my front end. I'm trying to convert one field on the document, which is an array of objects, into a true array on the front end.
I already know that Firebase stores this data as an array of objects with each object's key as the index of the would-be array: 0, 1, 2, ...
However, on the front end, for display purposes, I was hoping I could actually convert this into an array I can map over. The data is a userChats document for a chat app. See below:
{
"conversationid": "someConvoId",
"lastMessageCreatedAt": {
"nanoseconds": 0,
"seconds": 1598882400,
},
"lastMessageFromId": "someId",
"lastMessageFromName": "Guy",
"lastMessageText": "Hey, what up?",
"readByUser": false,
"usersArr": [
{
"userAvatar": "https://randomuser.me/api/portraits/men/65.jpg",
"userId": "someId",
"userName": "Guy",
},
{
"userAvatar": "https://randomuser.me/api/portraits/men/60.jpg",
"userId": "someOtherId",
"userName": "Gal",
},
],
}
The chat app uses group chat, so I'm using the usersArray to determine how many people are in the chat as well as display a user avatar, user name, and a "+1" (or +2, etc.) on the main Messages screen. In trying to convert the usersArray into a true array on the front end, I am getting unusual behavior. No matter what I try, methods that would work on either Objects or Arrays are rejected. Object.keys() only works for a console.log, but not when I try to assign the value to a variable.
const actuallyAnObject = chat.usersArray;
let realArray = actuallyAnObject.map((item, i) => item); // throws an error
console.log(Object.keys(actuallyAnObject); // displays in the console correctly
let keys = Object.keys(actuallyAnObject); // throws an error
let array = [];
Object.keys(actuallyAnObject).forEach((key) => { // throws an error
array.push({[key]: actuallyAnObject[key]});
});
console.log("actuallyAnObject['0'] : ", actuallyAnObject["0"]); // displays correctly
console.log("typeof actuallyAnObject : ", typeof actuallyAnObject); // object
How can I actually convert this into a useable array?
Posting this as Community Wiki, as it's based in the comments.
It seems that due to the fact that the data was manually added into Firebase, there was a difference between the name in the fields. While one was usersArray the other one was usersArr. This mistake was causing an inconsisten behavior while trying to load the data and convert it into a real usable array.

JavaScript / JSON - Access array element using a key name as index

I want to be able to access an array element by using a unique key name instead of a numeric index. In my situation, I'm making a Discord bot and each servers have their own settings. When someone sends a message on a server, I want to access some of this server's settings (such as a message prefix). IMPORTANT: Right now, the only way I can do this is looping through all the servers that the bot is in, which in long-term, could slow it down if there's hundreds of active servers sending messages. So looping through all the servers is already being done right now, but I want a direct way without having to do this.
conf.json:
{
"Settings": [
"358262452343013386" {
"prefix": "$",
"Admins": [
"155444308395294720"
],
"NotificationChannel": "358772856282284033",
"robotpieces": []
}
]
}
What I want to be able to do in my bot.js:
console.log(conf.Settings[message.guild.id].prefix); // outputs the prefix
// message.guild.id is the id of the server, which in this case, would translate to this:
console.log(conf.Settings["358262452343013386"].prefix) // outputs '$'
Any ideas of how I can achieve a similar goal WITHOUT having to loop through all of the array?
EDIT:
I know the following JSON is invalid, but I want a solution which would give the same result.
Aside from the fact that the JSON you posted is invalid, you could store the server settings as an object rather than an array, and access is just like you are trying to:
{
"Settings": {
"358262452343013386": {
"prefix": "$",
"Admins": [
"155444308395294720"
],
"NotificationChannel": "358772856282284033",
"robotpieces": []
}
}
}

Reading from JSON returns undefined

I'm trying to read from a JSON that contains my error messages, so that in case I'd ever want to change what my error messages say, I'd just change the JSON instead of diving into my source code. Everything seems to be working fine...
var fs = require('fs')
console.log("Now reading from error messages configuration file...");
var errorMsgs = JSON.parse(fs.readFileSync('config/error_msgs.JSON'));
console.log(errorMsgs)
This is what's in errorMsgs.json:
{
"bad_password" : [
{
"error" : "Incorrect login credentials.",
"details" : "This happens if you either have an incorrect username or password. Please try again with different credentials."
}
],
"missing_fields" : [
{
"error" : "Credentials failed to parse.",
"details" : "This happens when one or more fields are missing (or have illegal characters in them), please try again with different credentials."
}
]
}
When I log to the console, errorMsgs displays fine. When I log one of the items that errorMsgs has (like bad_password), it also works fine, as in it displays the items that are nested inside. However, when I attempt to retrieve a specific value like errorMsgs.bad_password['error'], it returns undefined. I can't seem to figure it out. I tried dot notation (errorMsgs.bad_password.error), which returns undefined. I tried the method above (errorMsgs.bad_password['error']) which also returns undefined. Asking for the typeof of errorMsgs, it returns object, which I assume makes it not a string. Passing the value to a variable first and then logging the variable doesn't do anything either. Is node converting it to a string on-the-fly, causing it to return undefined, or am I just doing something wrong?
bad_password" : [
{
"error" : "Incorrect login credentials.",
"details" : "This happens if you either have an incorrect username or password. Please try again with different credentials."
}
],
Your nested object is contained in an array.
errorMsgs.bad_password[0]['error']
This is what you're looking for. Just grab the first value of the array

JavaScript JSON Parse Issue ($.getJSON)

I can't seem to get the following code to output anything to the screen. JavaScript is not my strong suit!
This is the JavaScript code calling a local file that outputs a list of rooms
$.getJSON('roomupdate.php?level=1&div=1&callback=json',function(res){
console.log(res.rooms.name[0]);
});
In the above I'm merely trying to see the name of the first room in the console. And this is the JSON output (Which I can confirm the script can see and load)
json(
{
"rooms":
[
{ "name" : "Bedroom" },
{ "name" : "Living Room" },
{ "name" : "Lounge" },
{ "name" : "Kitchen" }
]
})
Could anyone suggest what I am doing wrong? Even to view in the console?
Lastly, can you loop through the array?
Your JSON data contains an object rooms and this object actually contains an array [], So to access the data inside your array you need to put the index on rooms :
console.log(res.rooms[0].name);
Use callback=? rather than callback=json so that jQuery knows you are using JSONp and can choose it's own name for the callback function.
$.getJSON('roomupdate.php?level=1&div=1&callback=?',function(res){
//alert('Your name is '+res.rooms);
console.log(res.rooms.name[0]);
});
See http://api.jquery.com/jQuery.getJSON/#jsonp for details.
Edit:
Looking again, you will need to change the way you are accessing the data around. res.rooms.name[0] should be res.rooms[0].name because rooms is a list, and each room has a name property.
This will loop through the array of rooms and log the name of each one:
$.each( res.rooms, function( i, room ) {
console.log( room.name );
});
If that doesn't work, then add this statement at the beginning of your callback (where you have the console.log() call now):
debugger;
Load/run your page with the developer tools open, and it will stop in the debugger where you have that statement. Now you can look at all your variables in detail, try out expressions to see what they do, single step through your code, etc.

Categories

Resources