What's wrong with this logic? Node JS - javascript

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.

Related

Hiding messages from a discord user through javascript - Soft blocking

Blocking a user is a good feature of discord, but often enough I find myself wanting to block someone temporarily: not see their messages, but only for a certain period of time and without them knowing that I did so. Theoretically such a thing should be possible to do. I found a userscript that you are supposed to paste into the discord console once you start discord that serves the purpose of hiding messages from blocked users completely. Using the same principle, if instead of searching for blocked messages I could search for messages from a specific user, and then hide them, I could soft-block them for the duration of my current session. So the idea of this is to search all messages in a channel, check if a message was posted by a certain user that I want blocked, and if so, hide it.
To do so I have to figure out how to tie messages to their authors. There appears to be a different class for each new message that a user sends. But when a user sends multiple messages in a row, the classes are different. The first message contains the link to the user's avatar, which in turn contains the user's unique discord ID that he could be identified with. But his follow-up messages don't contain his avatar. And I can't find anything else that I could use to identify a user with except for that.
In the image above the classes that have groupStart contain the avatar links, whereas the classes without it are the follow-up messages from the same user that do not. Like so:
What kind of a loop would be able to include both the messages with some specified avatar, and the follow up messages without it?
As I see it, here's how you'll have to do it:
first identify the groupStart message, and then keep looking at the next messages until you reach a new groupStart message.
Luckily JavaScript keeps elements in the order that they're displayed when you use document.getElementsByClassName so you should just be able to set a variable to true when you hit the target, and then back to false when you hit a new person. This is my code:
function block(userid) {
return setInterval(() => {
let deleteMessages = false;
[...document.getElementsByClassName('message-2qnXI6')].forEach(i => {
if(i) {
if(i.className.match('groupStart-23k01U')) {
if(([...i.children].find(i => i.className.match('contents-2mQqc9')).children[0].src || '/1/').split('/').find(i => i.match(/^\d+$/)) === userid) {
i.style.display = 'none'
deleteMessages = true
} else deleteMessages = false
}
else if(deleteMessages) i.style.display = 'none'
}
})
deleteMessages = false
}, 300)
}
The function block sets an interval for 300 seconds to:
First, get all the messages: [...document.getElementsByClassName('message-2qnXI6')]
Then for each message check to see if it's a groupStart element: if(i.className.match('groupStart-23k01U'))
If it is, it'll check to see if it's a message from the blocked user:
if(([...i.children].find(i => i.className.match('contents-2mQqc9')).children[0].src || '/1/').split('/').find(i => i.match(/^\d+$/)) === userid)
If it is, it'll delete that message and set deleteMessages to true:
i.style.display = 'none'
deleteMessages = true
If it isn't, it'll set deleteMessages to false, so that the next messages don't get deleted.
Going back, if the message isn't a groupStart message, it'll check to see if deleteMessages is true (which would be the first message in the group was from a blocked user, and therefore this message is from a blocked user). If it is, it'll delete it.
Everything should be reset with a simple ctrl+r
Just copy and paste this (the minified version of my code):
function block(e){return setInterval(()=>{let t=!1;[...document.getElementsByClassName("message-2qnXI6")].forEach(n=>{n&&(n.className.match("groupStart-23k01U")?([...n.children].find(e=>e.className.match("contents-2mQqc9")).children[0].src||"/1/").split("/").find(e=>e.match(/^\d+$/))===e?(n.style.display="none",t=!0):t=!1:t&&(n.style.display="none"))}),t=!1},300)}
into the console, and then to block someone type block('<userid>') (make sure the user id is a string and not a number), and it should start working. Also, when you do, it should return a number, just type clearInterval(number) to stop it from blocking new messages.
If you're wondering about this line:
if(([...i.children].find(i => i.className.match('contents-2mQqc9')).children[0].src || '/1/').split('/').find(i => i.match(/^\d+$/)) === userid)
There's a few hackey things I did here:
I start by getting an array of all the messages children, then I find the child with the userID in it (i.className.match('contents-2mQqc9')). If I don't I just use a placeholder to avoid errors.
Then I split via /s because the user id is one of the paths. Then I find which part of the path is the user id by checking to see if it's only numbers (i.match(/^\d+$/)). Finally once I've done all that I check to see if it's the same as the userid.
Here's a quick example:
https://cdn.discordapp.com/avatars/847295719724247388/c9af8fff1110025e6aae862492b4aa29.webp?size=256 (this isn't a real link) => ['https:', '', 'cdn.discordapp.com', 'avatars', '847295719724247388', 'c9af8fff1110025e6aae862492b4aa29.webp?size=256'] => '847295719724247388' is made completely of numbers, so: '847295719724247388'
Unfortunately, if you use this on too many messages by the same person in a row discord will really glitch out, so you'll have to watch for that (note this was testing it on 1000+ messages in a row by the same person). It seems like problems start to happen at around 100, but don't become an issue until it's up to 200 - 300, and even then it'll sometimes work if you wait for a second.
Hopefully if this doesn't work for you you'll at least have an idea of what to do.

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

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))

Check if a mention is a user - discord

I want to make a simple kick function that kicks a player with mention.
But I first want to check if a mention is a user. which I have no idea how.
let member = message.mentions.members.first();
if ("member.something") { // if member is a user in the server.
member.kick().then((member) => {
channel.send(`${member} has been kicked! :wave:`)
});
} else {
channel.send(`Error: ${member} can't be kicked!`)
}
Here are the options that I can think of:
User.bot Documentation
if (!member.user.bot)
To check if the user is not a bot
GuildMember.kickable Documentation
if (member.kickable)
To check if the member is kickable
To check if a member exists first, check out:
https://stackoverflow.com/a/53284678/11425141
You can do this in multiple ways.
You can either check for which permissions have in order to disallow other's from kicking them (For instance, KICK_MEMBERS). That would look something like this:
let member = message.mentions.members.first();
if (member.hasPermission("KICK_MEMBERS)) return message.channel.send("That member can also kick!")
You can also check if they got a certain role which disallows them to be kicked. (Could be moderator role, could be a protected role)
//Get role by ID (Allows you to later change its name, ID will remain the same)
let modRole = message.guild.roles.get("MODROLE_ID");
if (member.role.has(modRole.id)) return message.channel.send("This member is a moderator")
//Find role by name. Means if you change the name of this, you need to change code too.
let protectedRole = message.guild.roles.find(r => r.name === "Protected from kicking")
if (member.role.has(protectedRole.id)) return message.channel.send("This member is protected")
Lastly (that I know of), you can check if they're kickable. But all that does is, say if someone above them is trying to kick them, it will do it.
So if, say an admin, is testing or something, it will just kick the user if kickable = true
if (member.kickable) {
member.kick()
} else {
message.channel.send("This member is above you!)"
}
If you just want to check if they're an actual user, throw this line in at the top:
if (!member.bot) {
//If they're a user
} else {
//If they're a bot
}
There are obviously a lot of fun things you can do with this. But these are the basics.
Hope I helped a bit, and sorry for this late response, I was pretty much just scrolling through the forum and found this unanswered question.

Need Help Declining Steam Offers That Take Things From Me

Ok so I have a code from a steam bot the accepts and declines trades if the offer state is correct. But I would like it to accept trade offers that give me things but decline trade offers that are made by someone else asking for things.
if(body.response.trade_offers_received){
body.response.trade_offers_received.forEach(function(offer) {
if (offer.trade_offer_state == 2){
offers.acceptOffer({tradeOfferId: offer.tradeofferid});
}
else {
offers.declineOffer({tradeOfferId: offer.tradeofferid});
}
}
);
}
Not exactly sure what bot you're basing your code off, however after looking at the steam api for trade offers, there should be an array called "items_to_give" which you could check and see if it is empty before accepting.
if (offer.trade_offer_state === 2 && (!offer.hasOwnProperty("items_to_give") || offer.items_to_give.length === 0)){
So above we check if we do not have the "items_to_give" key, which doesn't exist if you are giving nothing. Then we check to make 100% sure that it has no items in it, just in case Steam decides to include empty keys with their API at a later date.
After looking at the steam api again, I believe your code could be improved if you also checked for TradeOfferStateCountered(4), which would let you accept counter offers as well. Here is the code for that
if ((offer.trade_offer_state === 2 || offer.trade_offer_state === 4) && (!offer.hasOwnProperty("items_to_give") || offer.items_to_give.length === 0)){

Implementing remove tweet and like/upvote functionality in Firebase

Continuing from this thread, on HN: https://news.ycombinator.com/item?id=5462769
Reading through the firefeed rules file answered a lot of questions for me, except for these two:
Editing an existing tweet isn't allowed (".write": "!data.exists()"). How can you make it not editable, but deletable by the author?
How would you securely handle liking/unliking or upvoting/downvoting? write if authenticated, validate for the increase/decrease by one, if the user hasn't modified this before? How would that work? Would there have to be a child list of people who edited this? I'm just really curious about this specific use case as it seems pretty common in many apps, yet seems to me, would be really complicated to implement in firebase?
1. Not editable but deletable by the author
".write": "!data.exists() || (!newData.exists() && data.child('author') === auth.id)"
2. Liking/Upvoting
On the client, use a transaction which allows you to increment the value safely:
ref.transaction(function(currentValue) {
return (currentValue||0)+1;
}, function(error) {
if( error ) /* failed too many times */
else /* it worked */
});
Security is also straightforward:
".validate": "newData.isNumber() && newData.val() === data.val()+1"
2.5 Ensuring Unique Votes
I'm not sure what this means; the records can't be edited and presumably if they could, only the author would be able to do so; so I don't really understand "modified" in this context: "if the user hasn't modified this before? How would that work?"
To ensure votes are unique, you just store them by user ID. The user can remove their vote by deleting the record.
I'd recommend storing these in a separate path than the sparks and still maintaining a simple increment (the messages that are getting voted up/down) as you don't want to have to retrieve the entire list of voters each time you fetch the spark.
The security rules would look like so:
"votes": {
"$spark_id": {
"$vote": {
".read": "$vote === auth.id",
".write": "$vote === auth.id",
// to allow downvoting in addition to up or delete, just add -1 here
".validate": "newData.val() === 1 || newData.val() === null"
}
}
}
And now add a check to the validate rule for the increment:
".validate": "!root.child('votes').child($spark_id).child(auth.id).exists() && newData.isNumber() && newData.val() === data.val()+1"
Now that Firebase Functions has been released (in beta) to the general public, it seems to be a good option: https://firebase.googleblog.com/2017/03/introducing-cloud-functions-for-firebase.html
The idea is to have each user be allowed to add their name, by key, to an "upvoters" collection for the tweet. They can create or delete their entry --
but there can only be one, since it's by-key and the security rule only allows control of their one key.
When finding of the "upvote count" is to take place, the client could get the full list of upvoters and tally the number. But instead, for performance's sake, we create a Firebase Function which is triggered whenever an upvote entry is added or removed.
All it does then is increase or decrease an "upvote count" property on the tweet. This is the same as before, except that we make a security rule that only lets the cloud-hosted Function modify this field. Thus, the modification is always trusted and safe, and removes the need for the client to receive the list of upvoters just to get the upvote-count.

Categories