is there a way to loop through a user's groups with now.js?
When a user disconnects, I want to run some functions on the groups that they were a part of.
something like answer 1 here (1), but for some reason, that code doesn't work.
Code:
nowjs.on('disconnect', function() {
var that = this;
this.getGroups(function(groups){
for (i=0;i<groups.length;i++){
nowjs.getGroup(groups[i]).removeUser(that.user.clientId);
console.log('user removed');
}
})
})
I forgot that I had already created an array called serverRoomsList. That held, oddly enough, a list of all the chat rooms I had already created. I just looped through that to get all of the group names.
However, I would still like to know if Now itself keeps track of groups in an array, or if I have to do it.
Related
I have a question for Discord.js.
How would I remove a specific user's reaction from a message?
I have tried to do so with this code:
// Now known as 'messages.fetch'.
message.channel.fetchMessage(MessageID).then(m => {
m.reactions.remove(UserID);
});
But it doesn't remove the user's reaction at all. Am I doing something wrong?
Any help would be appreciated.
Since this question is getting a lot of attraction, I decided to post what worked for me
Remove Specific User's Specific Reaction
// Channel = the channel object of the message's original channel
// MessageID = ID of the message, if hard coding, put around quotations eg: "1234"
const msg = await channel.messages.fetch(MessageID);
msg.reactions.resolve("REACTION EMOJI,
REACTION OBJECT OR REACTION ID").users.remove("ID OR OBJECT OF USER TO REMOVE");
Note: If using an older version of discord.js, simply replace channel.messages.fetch with channel.fetchMessage
The chosen option works, I would like to explain it. You need to grab the MessageReaction object from the message for the reaction you want to remove. How you do that depends on your code, in my case I was working with a reaction collector so on my collect call I already had the MessageReaction.
You then need to access the users property (a ReactionUserManager object). With this object, you can call .remove().
Code is below:
collector.on('collect', async (reaction, user) => {
...
// delete the reaciton
reaction.users.remove(user.id);
});
message.reactions is a collection of messageReactions. I think you need to loop through the collection and then remove the messageReaction required.
message.channel.fetchMessage(messageID).map(r => r).then(message => {
message.reactions.forEach(reaction => reaction.remove(UserID))
})
If you look at the documentation for reactions you can see it is a Collection, and it mentions that they are mapped by reaction ID's , and not the user ID's. The way you could remove them is get the reaction, filter the users and then maybe do something with that? I'm not sure how to remove those specifically, but that should get you the users, then filter that to the ID you want.
message.channel.fetchMessage(messageID).then(msg = m.reactions.get(reactionID).users); // Gets the users that reacted to a certain emote, I think.
I'd like to have my bot announce in our specific channel called family-talk, which I do have the channel ID of as well but not sure where to put it, but I'd want this to only happen when a role has been added to a member, is my below code correct or wrong? I don't have a lot of good ways of testing this so i'm hoping for some big help here. I also would like to know where the best place would be to place the code. Thank you!
if(!oldMember.roles.has('539208166563643407') && newMember.roles.has('561773668439687179'))
client.channels.get("550197572178935809").send("This member got the special role!");
Your code should work, BUT you have 2 diffrent IDs in the if, so to make this a bit cleaner just do:
const roleID = '539208166563643407';
const channelID = '550197572178935809';
client.on('guildMemberUpdate', (oldMember, newMember) => {
if(!oldMember.roles.has(roleID) && newMember.roles.has(roleID)) {
client.channels.get(channelID).send(newMember.displayName + ' got the special role!');
}
});
I am having difficulties looping over an object of constituency data, finding existing entries in a MongoDB and doing something with them. It always ends up being the same entry being passed to be found in the DB over and over again.
I am assuming this is a problem of scope and timing.
My code:
for (key in jsonObj) {
var newConstituent = new Constituent({
name : jsonObj[key]["Name"],
email : jsonObj[key]["Email"],
social : {
twitter: {
twitter_handle : jsonObj[key]["Twitter handle"],
twitter_id : jsonObj[key]["User id"],
timestamp : jsonObj[key]["Timestamp"]
}
}
});
console.log(jsonObj[key]["Email"]); // this is fine here!
Constituent.findOne({ email : jsonObj[key]["Email"] }, function(err, constitutents){
console.log(jsonObj[key]["Email"]); // here it's always the same record
if (err) {
console.log(err)
}
if (constitutents === 'null') {
console.log("Constituent not found. Create new entry .. ");
// console.log(newConstituent);
newConstituent.save(function (err) {
if (err) {
console.log('db save error');
}
});
} else {
console.log("Constituent already exists .. ");
}
});
}
I have a suspicion that the for loop finishes sooner than .findOne() is executing and therefor always and only gets the last item of the object passed to find.
Could someone point me into the right direction?
A couple of this.
Don't use for ... in, especially in node. You can use Object.keys() and any of the array methods at that point. for ... in can include values you don't wish to loop over unless you're using hasOwnProperty since it'll include values from the prototype chain.
The reason the email is the same is that you're just printing out your query again. jsonObj is included in the scope of your callback to findOne since you're not re-declaring it inside the findOne callback. So whatever the value of key happens to be (my guess is that it's the last one in your list) when the callback is invoked is the email you're getting. Since, in javascript, inner function scope always includes, implicitly, the scope of the surrounding context, you're just accessing the jsonObj from your enclosing scope.
To clarify about this point, your for ... in loop is synchronous -- that is the interpreter finishes running all the instructions in it before it will process any new instructions. findOne, how ever is asynchronous. Very simply, When you call it in this loop, it's not actually doing ANYTHING immediately -- the interpreter is still running your for ... in loop. It is, however, adding more tasks to the execution stack to run after it's finished your loop. So the loop is finished, AND THEN your callbacks will start to execute. Since the for ... in loop is totally finished, key is set to whatever the final value of it was. So, for example, if it's last value was foo that means EVERYTIME your callback is invoked, you will be printing out jsonObj.foo since the for ... in loop is already complete.
So it's like you asked your friend to say the letters from A to J, and you left the room to do 10 things. To do something. He totally finished going to J since that is much faster than doing 1 of the 10 things you're doing. Now every time you're done doing one of your things, you come back and say "what's the latest letter you said". The answer will ALWAYS be J. If you need to know what letter he is on for each task you either need to get him to stop counting while you're doing it or somehow get the information about what letter corresponds with the number of task that you're performing.
Having them wait is not a good idea -- it's a waste of their time. However, if you wrap your findOne in a new function where you pass in the value of key, this would work. See the updated code below.
I'm not sure about your data but findOne will return one record. You're putting it into a variable with a plural (constitutents). From reading your code I would expect back a single value here. (It might still be wrapped in an array however.)
Since you're calling findOne and assigning the results of the find operation to constituent, you should be examining that object in the console.log.
e.g.
console.log(constitutents.email); // or console.log(constitutents[0].email)
rather than
console.log(jsonObj[key]["Email"]);
(Assuming email is a property on constituants).
You might just try logging the constituants entirely to verify what you're looking for.
The reason this following code will work is that you're passing the current value of key to the function for each invocation. This means there is a local copy of that variable created for each time you call findConstituent rather than using the closure value of the variable.
var newConstituent;
function findConstituent(key){
Constituent.findOne({ email : jsonObj[key]["Email"] }, function(err, constitutents){
console.log(jsonObj[key]["Email"]); // here it's always the same record
if (err) {
console.log(err)
}
if (constitutents === 'null') {
console.log("Constituent not found. Create new entry .. ");
// console.log(newConstituent);
newConstituent.save(function (err) {
if (err) {
console.log('db save error');
}
});
} else {
console.log("Constituent already exists .. ");
}
});
}
for (key in jsonObj) {
newConstituent = new Constituent({
name : jsonObj[key]["Name"],
email : jsonObj[key]["Email"],
social : {
twitter: {
twitter_handle : jsonObj[key]["Twitter handle"],
twitter_id : jsonObj[key]["User id"],
timestamp : jsonObj[key]["Timestamp"]
}
}
});
findConstituent(key);
}
This may be a vary bad idea, or a possible solution that we have to a database concurrency problem.
We have a method that is called to do an update of a mongo record. We are seeing some concurrency problems - process A reads the record, process B reads the record, process A makes mods and saves the record, process makes B mods and saves the record. Because B reads after A, before A writes, it doesn't know about the changes A made, and we lose the data from A.
I'm wondering if we could not use a database semaphore, basically a field on the collection, that is a boolean. If we read the record at the start of the method, and the field is true, it's being edited. At that point, re-call the method using process.nexttick(), with the same data. Otherwise, set the semaphore, and carry on.
There would still be a bit of time between the read and the save, but it should be/could be faster than what we are doing now.
Be something like this. Any thoughts, anyone done anything like this? Will it even work?
function remove_source(service_id,session, next)
{
var User = Mongoose.model("User");
/* get the user, based on the session user id */
User.findById(session.me,function(err,user_info)
{
if (user_info.semaphore === true)
{
process.nextTick(remove_source(service_id,session,next));
}
else
{
user_info.semaphore = true;
user_info.save(function(err,user_new)
{
if (err) next(err,user_new);
else continue_on(null,user_new);
});
}
function continue_on(user_new)
{
etc.......
}
Edit: New Code:
The function now looks as follows. I'm doing individual updates to the arrays. This of course means that I now have the possibility, if the transaction fails between the first and second transactions, of having data out of sync. I'm thinking that I could simply resave the user object that I retrieved on entry into the function, overwriting my changes. I don't know if Mongoose/Mongo will not do the save if I have not changed that object, will have to try and see. Any more thoughts?
var User = Mongoose.model("User");
/* get the user, based on the session user id */
User.findById(session.me,function(err,user_info)
{
if (err)
{
next(err,user_info,null);
return;
}
if (!user_info || user_info.length === 0)
{
next(_e("ACCOUNT_NOT_FOUND"),"user_id: " + session.me);
return;
}
var source_service_info = _.where(user_info.credentials, {"source_service_id": service_id});
var source_service = source_service_info.source_service;
User.findByIdAndUpdate(session.me,{$pull: {"credentials": {"source_service_id": service_id}}},{},function(err,user_credential_removed)
{
if (err)
{
next(err,user_info,null);
return;
}
User.findByIdAndUpdate(session.me,{$pull: {"criteria": {"source_service": source_service}}},{},function(err,user_criteria_removed)
{
if (err)
{
next(err,user_info,null);
return;
}
else
{
next(null,user_criteria_removed);
}
});
});
});
};
The problem with your approach is that it just shortens the time during which the data could be read by a second process, it doesn't eliminate the problem.
The solution to this would be to set your semaphore in the same action as the read. I haven't used Mongoose, but in MongoDB you can use findAndModify to only return a User record if the semaphore is false, and if it is false, in one atomic operation, set the semaphore to true.
If you don't want to use findAndModify, you could first do an update that sets the semaphore true (or to some specific ID value so you know that it is YOUR semaphore) only if the semaphore is not set. Then, if that process succeeds, you could do the find (perhaps passing your semaphore ID as a criterion in the find). However, findAndModify, if it is available in Mongoose, would do that in one step.
A variation of that is described here: http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/ where you do a form of optimistic locking that checks that the old values are unchanged before changing them to the new values.
There is a variation on this that uses a separate table to simulate a two-phase commit: http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/
Edited: Upon interchange below, this seems to be a schema and updating issue. Question may become something like: I have some entries in an array, and the ordinal index to those entries relates to some other arrays as well. How do I perform deletes without having mismatches?
Three off the top possibilities occur, depending on frequency in the real world vs QA test scenarios.
Consider adding a deleted flag but keeping the records in the same order. If someone toggles, reuse the same record, but fix however you want.
Use an associative array (JS object) for each element (not a feature from relational world.) If you need an order, add an array that lists the keys in order. Both have syntax to update without touching anything other that what has changed, and will not overwrite changes to different fields.
Use an associative array where the keys are numbers. Actual deletion won't hurt retrieval.
stuff = {}
stuff[1] = {some:'details'}
stuff[2] = {some:'details2'}
Was
1) Are you making changes to the same field? Make that into an array, and push changes, and pop the latest to read the current value.
2) Are you changing different fields, but data is getting trounced? Then there is better syntax to use for the updating. you can update field by field.
$set: { 'fielda': 'valuea' }
won't lose edits on previous fields
3) change your schema
4) change the timing on the processes so they don't overlap. Or so they do so in smaller subsets, that you can manage to prevent from overlapping.
I'd like to know, just out of interest, what multiple processes are needed to make updates on the same record? I don't work with anything that looks like that.
I am using socket.io to make a chat application where I need to show the current user list.
Till now it is working fine with the current user list being displayed
I am using this code for maintaining the user list
usernames[socket.id] = name;//where name is the name of the user
Then on disconnect I am again deleting that user from the list
delete usernames[socket.id];
But, my problem with this method is that suppose there are three users a,b and c
now the userlist will be populated on the connection event,something like this
io.sockets.on('connection', function(socket) {
...
socket.on('register', function(name) {
usernames[socket.id] = name;
And the user list will be something like "a,b and c"
But, what I want is that for user "a" the list should show "b and c" and for "b" it should be "a and c" and for "c" it should be "a and b".
I also thought about removing it on the client side based on some variable which stores the name of the user who is viewing it but for this chat multiple users can have the same name.
So if suppose all the three have name "a" then it will remove all three instead of removing only one name from the list.
....I guess I am out of ideas on this one. Plz help me to implement it in socket.io
Your question has nothing to do with Socket.IO really. When displaying a list of users, simply don't display the current user!
for (userIndex in users) {
if (users[userIndex].id !== currentUser.id) {
// Add user to displayed user list
}
}