Discord JS : How to "iterate" a stringified JSON? - javascript

I'm learning JS (And discord JS to be accurate) since a few days. All goes well but now I'm before an issue I don't really know how to wrap my head around.
I have a sequelize database, all working well, aka an "User Card Collection", filling up as intended.
Now, I want to ask for a showing up of all "user_id" card collection.
I used a JSON.stringify (is it already the right way to do it ?). I can call it in console.log easily, showing up as a JSON. But how could I insert the list into an embed to PM it to the user ?
I've try to iterate it, it says me that it's, of course, not iterable.
Here is my concerned code :
} else if (command === 'Collection' || command === 'collec' || command === 'col') {
const target = message.author.id;
const cards = await UserCollec.findAll({where: {user_id: target}, attributes: ['card_name', 'card_lvl', 'amount']});
console.log(cards.every(card => card instanceof UserCollec)); // true if ok. DEBUG : working as intended.
const JSONcardlist = console.log(`${message.author}, all your cards :`, JSON.stringify(cards, null, 2)) // returns the JSON list. DEBUG : Working as intended.
When I try to call ${JSONcardlist} in a message or embed, it returns undefined. What am I missing ?
Thx for your help :) Probably something easy but, you know... Beginner here, not found the answer on internet in a few hours so better to ask.

That's ok, thx for help.
I found my solution with this :
function GetCards(JSONcardlist) { return Object.values(cards).map(card => `• ${card.card_name} (lvl ${card.card_lvl}), ${card.amount} x`).join('\n')
}
message.channel.send(GetCards(JSONcardlist))

Related

Firestore startAfter() returning the same data in infinite scrolling when ordered by descending timestamp

I'm writing a profile page with chronological user posts (latest post on top) using Firestore on Ionic by setting orderBy() to "timestamp" descending. I'm using Ionic's infinite loading to load more posts when the user reaches the bottom, but the result is that Firestore loads the exact same posts over and over again. Please help!
Hi!
Sorry if this is a beginner question, but I've been wrapping my head around this for sever hours to no avail. The pagination works properly when in ascending order, but loads the same posts when in descending order. I've looked at trying the following alternatives, but they would not be cost-efficient:
Change the limit when user reaches the bottom: this would lead to reading all the posts again and again
Do it in ascending order but reverse the array: would defeat the purpose of pagination
use a query (where) to grab the documents before x timestamp: works in theory, but is a bit hacky and I would really want to know how startAfter() works since it's already there.
.
'''
READ_posts__profile(uid,limit, start) : Promise<[]>
{
return new Promise((resolve, reject) =>
{
if (start == null)
{
resolve();
}
else if(start == 'head')
{
start = new Date();
}
console.log(start);
this._DB.collection(this.ref.posts + uid + '/userposts' )
.orderBy("timestamp", 'desc').startAfter(start).limit(limit)
.get()
.then((querySnapshot) =>
{
let obj : any = [];
console.log(obj);
querySnapshot
.forEach((doc : any) =>
{
obj.push({
id : doc.id,
content : doc.data().content,
likes : doc.data().likes,
timestamp : doc.data().timestamp,
comments : doc.data().comments
});
});
resolve(obj);
})
.catch((error : any) =>
{
reject(error);
});
});
}
'''
Expected result: Infinite scrolling of posts from latest to oldest
Result: Infinite scrolling of first x posts again and again (limit is x)
Thank you for taking the time to read. Hope to hear from you guys soon!
UPDATE FOR FUTURE REFERENCE:
Rather than using the doc itself in .startAfter(), it worked with using doc.timestamp like so:
this._DB.collection(this.ref.posts + uid + '/userposts' )
.orderBy("timestamp", "desc").startAfter(start.timestamp).limit(this.limit)
.get()
.then(querySnapshot =>
{
In my case I added the query like this
query.startAfter(lastId)
where it should have been
query = query.startAfter(lastId)
The problem is that startAfter() is expecting a query cursor for the parameter, not the timestamp that you are passing.
A query cursor identifies the document:
Paginate data with query cursors | Firebase
What you need to do is save the doc to a variable at the class level and then pass that in with the next one:
// in the class variables at the top
latestEntry: any;
// then save a reference to it
this.latestEntry = data[data.length - 1].doc;
// Now you can use the latestEntry to query with startAfter
.startAfter(this.latestEntry)
This is the general theory. When I got this working myself in an app I used the code in this answer.
It's going to need quite a lot of rewriting of your code to do this, which is beyond the scope of the time I have right now, but hopefully this will help you resolve it yourself.
The problem is, that you have to match your last query order:
The order of the field values must ""match the order of the order by clauses of the query""
so if you first make a query like this
collectionReference
.whereEqualTo("id", someId)
.orderBy("timestamp", Query.Direction.DESCENDING)
.limit(5)
.get()
.addOnSuccessListener { documentSnapshots ->
//do some stuff
}
Then your next query should be like:
collectionReference
.whereEqualTo("id", someId)
.orderBy("timestamp", Query.Direction.DESCENDING) //FIELD
.limit(5)
.startAfter(lastVisible.get("timestamp"))
.get()
.addOnSuccessListener { documentSnapshots ->
//do some stuff
}
So you see that startAfter({somecriteria}) has to match the orderBy({somecriteria}) of your first query where you got your last doc visible from, otherwise it will return your first query again.
The query should work if you make sure that the data type are the same between timestamp in firestore and variable start
I understand the OP uses Javascript SDK, I think I find a workaround for this issue. unfortunately currently I am using Flutter, but I think you can tweak it easily.
so the idea is to convert the Date or DateTime object from the language (Dart, Javascript, Kotlin etc) to Timestamp object from the Firestore library. and then pass that Timestamp object to startAfter
in Flutter, you can do it like this
import 'package:cloud_firestore/cloud_firestore.dart'; // to import Timestamp class from Firestore
var lastCreatedAtDateTime = yourObject.createdAt; // this is Date Time Object from your language
var firestoreTimestamp = Timestamp.fromDate(lastCreatedAtDateTime); // convert Date to be Firestore's timestamp
query = query.startAfter([firestoreTimestamp)]);

How to create a command that only who have one of the roles can use?

let staffrole = ['383874699941117952', '149622819158884353', '149622998180036608'];
How do you make a command that only people who have one of the roles can use it?
Thank you!
What you can do is that, on a message event, you run the command, and you can check the member's roles for one of the ones in the array.
Heres what that would look like:
client.on("message", msg => {
if(command === "whateverItIs") {
let staffrole = ['383874699941117952', '149622819158884353', '149622998180036608'];
for(i=0;i<staffrole.length;i++) {
if(msg.member.roles.filter((role) => role.id == staffrole[i]).size > 0) {
//run the code
return;
}
}
}
})
On a message event, with the determined command, the bot will check through each of the staff roles and if the message author 's roles includes one of the staffrole's then the command will run.
I would recommend doing something like this:
First, set your command name in the client's message listener:
// ... (in client message listener)
switch(command) {
case '<your command>':
runIfRoleIncluded(message);
break;
}
Next, get the role Id from the message that was sent and check if that message's role Id is in your staffrole array:
function runIfRoleIncluded(message) {
let rolesCollection = message.member.roles;
let staffrole = ['383874699941117952', '149622819158884353', '149622998180036608'];
// only 1 member can send a message, so get role collection's first key (id)
let messageRoleId = rolesCollection.firstKey();
// check if message's role id is in staff role array
if (staffrole.includes(messageRoleId)) {
// do stuff here
// ...
}
}
The .roles property of the .member object is a Collection, which is why you have to use the .firstKey() method. You can also turn the Collection into a normal js Array, but the way I outlined above is easier.
Started looking at this... Don't know the discord space very well but got an example bot and with a hello world ping, also found this pretty sweet Github gist that lays out fairly well how to build what amounts to a command switch statement. Making a lot of guesses here -- as a note for future questions it would be very helpful for you to add in some code on what you are trying to do -- a single variable set to an array isn't much to go on...
After Reading what #Raymond Zhang said, because, yeh that's what I was doing...
this is straight out of the Github gist I linked ->
...
if(command === "kick") {
if(!message.member.roles.some(r=>["Administrator","Moderator"].includes(r.name)) )
return message.reply("Sorry, you don't have permissions to use this!");
...
I have tested this and it works great, although it checks against the roles name not a number. It would help if you updated you answer to explain your process. More info = better answer. :)

What's wrong with this logic? Node JS

I built a mini cms app with Node JS. I allow users to edit their own profile and admins to edit all profiles. I have a weird problem with the logic - If I use this syntax, I get an error (401) when an admin tries to edit other user's profile:
if (!loggedUser.isAdmin || foundUser.id !== loggedUser.id) {
res.status(401).json();
} else {
// Save Updated User
foundUser.username = req.body.username;
foundUser.birthday = req.body.birthday;
foundUser.personalWeb = req.body.personalWeb;
foundUser.location = req.body.location;
foundUser.save().then(() => res.status(200).json(200));
}
But if I use this syntax, the permissions work just fine:
if (loggedUser.isAdmin || foundUser.id === loggedUser.id) {
// Save Updated User
foundUser.username = req.body.username;
foundUser.profileImg = req.body.profileImg;
foundUser.personalWeb = req.body.personalWeb;
foundUser.location = req.body.location;
foundUser.save().then(() => res.status(200).json(200));
} else {
res.status(401).json();
}
Can someone please explain what's the differnce between the two conditions?
!loggedUser.isAdmin || foundUser.id !== loggedUser.id and loggedUser.isAdmin || foundUser.id === loggedUser.id are not boolean inverses of each other.
The first is saying "if the user is not an admin or the found user's id does not match the logged in user's id." In the case of an admin you would expect their id to not match the found user's id.
I think that your second code block is easier to read and you should keep it, but if you wanted to do the negative condition first it would be:
!loggedUser.isAdmin && foundUser.id !== loggedUser.id
That is: "if the logged in user is not an admin and the found user's id does not match the logged in user's id."
This is also the boolean inverse:
!(loggedUser.isAdmin || foundUser.id === loggedUser.id)
// expands to
!loggedUser.isAdmin && foundUser.id !== loggedUser.id
It was surprisingly difficult for me to find good documentation or descriptions of boolean negation, but this article explains the concepts well I think: http://www.math.toronto.edu/preparing-for-calculus/3_logic/we_3_negation.html
Although you can simplify boolean expressions, I think it's best to write them in a way that makes the most sense to read back for you and your development team, so I suggest you use the first block since it's easy to read. Failing that, leave a comment about what the expression is trying to accomplish.
It's because your foundUser.id !== loggedUser.id is evaluating to true when editing any user that's not you.
To add to that, any non admin user will get a 401 due to the first condition evaluating to true.
With || as long as one condition is met, the body will execute and then it's done. It won't move on to the else body if only one condition is false. Both need to be false
Personally I would just use your second example. It's more readable.

Cannot get the full json with request json

When I am actually entering the XXXX YYYY, then I am getting the players json code in my html page (around 150 values).
But when I am trying to use a function on the players list it somewhy does not contain all the 150 values and the try throws me into the catch error part, where I can see that players json has only 100 players inside there.
Any idea what could be the problem?
if(yourID === "XXXX" && targetID === "YYYY"){
return players;
}
try{
if(isUserAlive(yourID)){
if(targetID === ""){
return userTargetInfo(yourID);
}
var checkForMatch = getUserTarget(yourID);
if(checkForMatch === targetID){
killTarget(targetID);
getUser(yourID).targetID = getTargetTarget(targetID);
addScore(yourID);
return userTargetInfo(yourID);
//return getTargetTargetStats(targetID);
}else{
return "INVALID";
}
}else{
return "DEAD"
}
}catch(err){
console.log("Error",console.log(players))
return "INVALID"
}
Edit: Since I had no time, I created 2 websites and divided the database into 2 different databases, so it would work under 100 people on each. Did not have time to fix the error at this point. So I won't be choosing the solution to that since I won't be trying that any time soon.
Thank you for all your help!
Check the link api that you are using , it might have pagination integrated with it . in that case i will return certain number of object 1st and then you can re-request to get next batch . Most likely they might have a option to change the no of object returned (sometimes with max value)
I'm pretty sure body is returned as a string. Try changing it to an object so you can work with it easier.
Change:
players = body;
to:
players = JSON.parse(body);
I'm not sure the rest of your code, but you may want to add var on your players variable declaration because this looks like the first time you are setting it.
Research: namespace collisions
If you are still having issues, edit your question to include the response you are getting from console.log(JSON.parse(body));. You will be able to get more helpful answers. Personally, I am curious to see the keys such as:
{ query:
{ count: 1,
created: '2017-04-23T22:03:31Z',
lang: 'en-US',
results: { channel: [Object] } } }
If it's paginated, you should see some kind of cursor key in there, or prev and next along with some kind of totalCount.
Hope this helps.

Can anyone explain how to work with conversationThreading-js?

I am trying to use this conversationThreading-js code to group emails into threads but documentation is scant and/or I am not able to understand it. Has anyone used this bit of code before or used the JWZ email conversation threading algorithm on which it is based?
This is where I am so far:
using Electron I load and parse a local mbox using node-mbox and node-mailparser
I build an array of javascript objects which have key value pairs of messageId, inReplyTo and references.
Using example code from the test file for this lib I try to build the threads but apparently I am not doing it right. I get no errors but I also get no threads (and my test mbox does contain threaded conversations).
Maybe I am misunderstanding what the result should be? Or I am just "doing it wrong"? My end goal here is to be able to display the resulting threads in some sort of directed graph using D3 – but that is not going to happen if I can't get the data set up correctly.
function makeThread(emails) {
var thread = jwz.messageThread().thread(emails.map(
function (message) {
return jwz.message(message.subject, message.messageId, message.references);
}
));
console.log('thread',thread);
}
It's pretty unclear how it works, but I managed to write some code that prints the "thread tree":
function recurse(node, level) {
level = level || 0;
let prefix = '\t'.repeat(level);
(node.children || []).forEach(function(child) {
child.children = child.children || [];
console.log(prefix, level ? '' : '-', child.message ? child.message.subject : '??', `[${ child.children.length }]`);
return recurse(child, level + 1);
});
}
recurse(jwz.messageThread().thread(messages));
(messages is an array of jwz.message() objects, similar to how you're creating it)

Categories