Underscore _.contains method always failed inside mongoose promise query - node.js

Hi im using mongoose query inside socket.io event, so far so good. I want to check if a user is a member to a private room then fetch messages. So i use underscore method _.contains() but is always return false. I don't know what wrong with the code cuz logging both list and checked item it should return true
socket.on('add user', function (data) {
var username = data.username;
var userId = data.userId;
var currentroom = data.room ? data.room : '559c02cfd2ad52cc276b7491';
// we store the username in the socket session for this client
socket.username = username;
socket.userid = userId;
// add the client's username to the global list
usernames[username] = username;
++numUsers;
socket.emit('login', {
numUsers: numUsers
});
var room = new roomModel();
return room.getRoomParticipants(currentroom)
.then(function(res) {
console.log('checking room');
console.log(res);
if (!res) {
throw new Error('Couldn\'t get room participants!');
}
res = res[0];
var participants = res.participants;
return participants;
})
.then(function(participants) {
if (!_.contains(participants, socket.userid)) {
console.log('have failed');
return null;
}
_.each(participants, function(item) {
var user = new userModel();
user.getById(item)
.then(function(res) {
if (res) {
rommates[res._id] = res;
}
})
});
return roommates;
})
.then(function(participants) {
var message = new messageModel();
message.roomMessages(currentroom, msgLimit)
.then(function(res) {
_.each(res, function(item) {
var data = {};
var user = rommates[res._id];
data.username = user.username;
data.message = item.msg;
data.time = item.created_at;
socket.emit('new message', data);
});
});
})
.then(null, function(err) {
logger.debug(err);
});
});

Solved it by replacing
if (!_.contains(participants, socket.userid)) {
return null;
}
with
var participants = _.object(res.participants);
var allowed = _.find(res.participants, function(item) {
return item == socket.userid;
});
if (!allowed) {
throw new Error('User ' + socket.userid + ' is not allowed to room ' + currentroom);
}

Related

How do I pass socket.io room name from one client to another?

I'm using socket.io v4 with NodeJS (Express).
So, the situation that I have is that I have a chat window for client and the client can enter their email and message. Now, I want to create a room based on that email and have another client join that room. My current implementation doesn't work. It creates the room but the other client is unable to join it.
Server Side Code:
io.of(/^\/dynamic-[a-zA-Z0-9]+$/).on("connection", (socket) => {
let email;
const namespace = socket.nsp.name;
let namespaceToCheck = namespace.split('-');
//console.log(namespaceToCheck[1])
User.findOne({apiKey: namespaceToCheck[1]})
.then((doc)=> {
if(namespaceToCheck[1] == doc.apiKey) {
socket.once("pass-email", (data) => {
io.of(namespace).emit("pass-email", data);
email = data;
})
console.log("Valid Connection");
socket.on("chat-message", (msg) => {
socket.join(email, function(){
//console.log(`Socket now in ${socket.rooms}`);
});
//console.log(msg);
console.log(socket.rooms);
Message.findOne({namespace: namespace})
.then((doc) => {
// console.log(doc);
doc.messages.push(msg);
doc.save().then((saved) => { return Promise.resolve(saved) });
})
// console.log(socket.handshake);
//io.of(namespace).sockets.in(data).emit("chat-message", msg);
console.log(email);
io.of(namespace).to(email).emit("chat-message", msg);
})
}
})
.catch((err)=> {
console.log(err);
})
});
Client Socket Code (from which I'm passing the email)
var chatSocket = io("http://localhost:3000/dynamic-8171d2a713d65c5edf81e45af4d14558a2c62275df05c73ca198a94d422e5948");
var chatBtn = document.querySelector('.chat-btn');
var input = document.querySelector('.chat-input');
var messages = document.querySelector(".messages");
var emailInputTag = document.querySelector(".email-input");
input.addEventListener("keypress", function(event) {
if (event.key === "Enter") {
event.preventDefault();
if (input.value && emailInputTag.value) {
chatSocket.emit('create-room', emailInputTag.value);
//chatSocket.join(emailInputTag.value)
chatSocket.emit('chat-message', emailInputTag.value + ':' + input.value);
input.value = '';
}
}
});
chatBtn.addEventListener('click', function(e) {
e.preventDefault();
if (input.value && emailInputTag.value) {
chatSocket.emit('chat-message', emailInputTag.value + ':' + input.value);
input.value = '';
}
});
chatSocket.on('chat-message', function(msg) {
var item = document.createElement('div');
item.classList.add('msg');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
This is the rooms that this socket is in:
Client chat socket code (from which I want to receive room event and join the received room)
var chatSocket = io(`http://localhost:3000/dynamic-${ApiKey}`);
chatSocket.on("connect", function () {
console.log("Connected-test-succeeded");
// Connected, let's sign-up for to receive messages for this room
});
chatSocket.on("pass-email", (val) => {
console.log(val);
console.log("Listening");
});
var chatBtn = document.querySelector(".chat-btn");
var input = document.querySelector(".chat-input");
var messages = document.querySelector(".messages");
input.addEventListener("keypress", function (event) {
if (event.key === "Enter") {
event.preventDefault();
if (input.value) {
chatSocket.emit();
chatSocket.emit(
"chat-message",
"Owner" + ":" + input.value
);
input.value = "";
}
}
});
chatBtn.addEventListener("click", function (e) {
e.preventDefault();
if (input.value) {
chatSocket.emit(
"chat-message",
"Owner" + ":" + input.value
);
input.value = "";
}
});
chatSocket.on("chat-message", function (msg) {
console.log("Message received: " + msg);
var item = document.createElement("div");
item.classList.add("msg");
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
P.S: I know that the code is a bit messy but what I'm trying to do here is making a tawk.to clone so different namespace for each website url and the rooms are different in the said namespace for different users.

Completing loops inside of node.js promises

I am trying to complete a few loops over firebase objects using .forEach and I am also using promises. This isn't working out how I had planned it. My basic problem is that the loops inside of my promises complete well after the promise chain itself completes. Here is my function:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "";
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
console.log('one');
progress(20);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
console.log('two');
progress(40);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
console.log('three');
progress(60);
});
}).then(function() {
posts.once('value', function(postSnapshot) {
// use Promise.all and Array#map to wait for all these queries to finish
var allPosts = postSnapshot.val()
var postKeys = Object.keys(allPosts)
return Promise.all(postKeys.map(function(postKey) {
var postID = postKey;
return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {
return itemSnapshot.forEach(function(itemSnapshot) {
var itemData = itemSnapshot.val()
console.log('post snapshot'+ itemData);
updateObject['posts/'+postID+'/items/'+oldProductID] = null
updateObject['posts/'+postID+'/items/'+newProductID] = itemData
});
});
}));
});
}).then(function() {
// Move to next item
return console.log('hey look here'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
}).then(function() {
// Move to next item
return console.log('second one'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
And here is the output:
one
two
three
hey look hereundefined
second oneundefined
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
I think I might be using promises incorrectly but I don't know.
I figured out that I should be using Promise.all() in order to wait for my forEach loops to complete. Here is the code I used to solve my problem, enjoy:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "-KZOO0UII67uOmYo6DJh";
var postKeys = [];
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
return clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
progress(10);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
progress(25);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
progress(40);
});
}).then(function() {
progress(55);
return posts.orderByChild('receipt').equalTo(receiptID).once('value');
}).then(function(postSnapshot) {
return postSnapshot.forEach(function(post) {
progress(70);
postKeys.push(post.key)
});
}).then(function() {
return Promise.all(postKeys.map(function(postKey) {
return posts.child(postKey).child('items').child(oldProductID).once('value', function(itemSnapshot) {
var itemData = itemSnapshot.val()
updateObject['posts/'+postKey+'/items/'+oldProductID] = null;
updateObject['posts/'+postKey+'/items/'+newProductID] = itemData;
});
})).then(function(results) {
progress(85);
return results;
});
}).then(function() {
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
resolve();
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});

wit.ai : sessionid undefined in my runActions function

I am writing my first apps with wit.ai using a node.js backend. I found some posts here similar to my question, but not really the answer :
I use a socket.io to communicate with my node script.
The two relevant parts of my node are :
io.sockets.on('connection', function (socket) {
socket.on('message',
function(data) {
var json = JSON.parse(data);
var sid = json['sessionid'];
console.log("Received on sid " + sid);
if (_sessions[sid] == undefined) {
_sessions[sid] = {};
_sessions[sid].context = {};
}
_sessions[sid].socket = socket;
client.runActions(sid, json['text'], _sessions[sid].context, 30)
.then((context) => {
_sessions[sid].context = context;
}
)
.catch((err) =>
{
console.error('Oops! Got an error from Wit: ', err.stack || err);
}
);
}
);
}
);
========
const actions = {
send(request, response) {
const {sessionId, context, entities} = request;
const {text, quickreplies} = response;
return new Promise(function(resolve, reject) {
var session = _sessions[sessionId];
console.log("-------------------------------");
console.dir(context);
console.log("-------------------------------");
session.socket.emit("message", JSON.stringify(response));
return resolve();
});
},
gettaxi ({sessionid, context, text, entities}) {
return new Promise(function(resolve, reject) {
console.log(`Session ${sessionid} received ${text}`);
var quand = firstEntityValue(entities, "quand");
if (!quand && context['quand'] != undefined) quand = context['quand'];
var depart = firstEntityValue(entities, "depart");
var arrivee = firstEntityValue(entities, "arrivee");
if (depart) {
console.log("Found depart");
context.depart = depart;
delete context.missing_depart;
}
else {
context.missing_depart = true;
}
if (arrivee) {
console.log("Found arrivee");
context.arrivee = arrivee;
delete context.missing_arrivee;
}
else {
context.missing_arrivee = true;
}
console.dir(context);
if (quand) {
console.log("Found quand");
context.quand = quand;
delete context.missing_quand;
}
else {
context.missing_quand = true;
}
return resolve(context);
}
);
},
};
All is working rather good, except than my gettaxi receives a undefined sessionid.
It's not the case for the send function that receives the correct sessionid.
What am I doing wrong ?

SocketIO Identify what user disconnected

I am making a simple Node.js game that uses Express, Socket.io, and an Http server. All of the users are stored in a multidimensional object on the server. This is how the server-side code works:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use(express.static(__dirname + '/'));
var playerList = {};
createPlayer = function(array,width,height,spdx,spdy,x,y,color,name,id) {
var player = {
width:width,
height:height,
spdx:spdx,
spdy:spdy,
x:x,
y:y,
wKeyDown:false,
aKeyDown:false,
sKeyDown:false,
dKeyDown:false,
color:color,
name:name,
id:id
}
array[id] = player;
}
io.on('connection', function(socket) {
socket.on('new player', function(id, name) {
id = parseInt(id);
if (!playerList[id]) {
createPlayer(playerList,25,25,4,4,Math.round(Math.random() * 800),Math.round(Math.random() * 600),randomColor(),name,id);
}
socket.on('pressW', function(id, keyDown) {
playerList[id].wKeyDown = keyDown;
});
socket.on('pressA', function(id, keyDown) {
playerList[id].aKeyDown = keyDown;
});
socket.on('pressS', function(id, keyDown) {
playerList[id].sKeyDown = keyDown;
});
socket.on('pressD', function(id, keyDown) {
playerList[id].dKeyDown = keyDown;
});
});
socket.on('disconnect', function() {
});
};
sendPlayerList = function() {
//newPlayerList is used to prevent client from seeing other users IDs
var newPlayerList = {};
var count = 0;
for (var q in playerList) {
player = {
x:playerList[q].x,
y:playerList[q].y,
width:playerList[q].width,
height:playerList[q].height,
color:playerList[q].color,
name:playerList[q].name,
}
newPlayerList[count] = player;
count++;
}
io.emit('edit playerlist', newPlayerList);
}
SPLInterval = setInterval(sendPlayerList, 1000);
Here is the client-side code for connection:
var id;
$('#playbutton').click(function() {
var name = document.getElementById('name').value;
id = Math.floor(Date.now() * Math.random());
socket.emit('new player', id, name);
});
On the client-side, in the update loop, when the game wants to tell the server your input, it emits your input like so:
update = function() {
ctx.clearRect(0,0,canvas.width,canvas.height);
if (document.hasFocus()) {
socket.emit('pressD', id, dKeyDown);
socket.emit('pressS', id, sKeyDown);
socket.emit('pressA', id, aKeyDown);
socket.emit('pressW', id, wKeyDown);
}else{
socket.emit('pressD', id, false);
socket.emit('pressS', id, false);
socket.emit('pressA', id, false);
socket.emit('pressW', id, false);
}
clientUpdatePlayer();
updatePlayers();
}
}
var updateInterval = setInterval(update, 31.25);
The function to update players just draws players based on the player list sent from the server.
My problem is that when a user disconnects, they stay in the player list.
I don't understand how I should go about fixing this. I identify users by getting the ID they send from the client, but I can't get the user's id when they disconnect.
There is a lot more code, but I tried to only include the code that I thought was necessary. I am willing to include more code if that is needed.
You could just store the id value in the parent scope, which the disconnect event handler would have access to:
io.on('connection', function(socket) {
var userId;
socket.on('new player', function(id, name) {
userId = id = parseInt(id);
// ...
});
socket.on('disconnect', function() {
delete playerList[userId];
});
};
Maybe I'm late to the party but I was stuck with something similar and found it the hard way and this may help someone.
The best way to detect if the user is disconnected is would be to first set the username in socket session.
Send the name from the client on emit
socket.emit("newUser", username);
and on server
socket.on('newUser',function (username) {
// we store the username in the socket session for this client
socket.username = username;
});
and when the user disconnects find that on the disconnect event
socket.on('disconnect', function () {
var connectionMessage = socket.username + " Disconnected from Socket " + socket.id;
console.log(connectionMessage);
});
and you can take it from there.
This worked for me:
On every new connection or user who comes online generate a socket Id, add it to the user object, and add it to the array of all the users online.
const users = [];
io.on('connection', (socket) => {
const socketId = socket.id;
socket.on('user online', (data) => {
users.push({ ...data, socketId });
io.emit('view user online', user);
});
Then in the disconnect, use forEach to loop through each object in the array, then use for to loop through and delete each key in the object:
socket.on('disconnect', () => {
users.forEach((user) => {
if (user.socketId === socket.id) {
for (const key in user) {
delete user[key];
}
}
});
logger(`A user has disconnected`);
});
});
});
Tweak to the way you want.
var users = [];
socket.on('newUser', (username) => {
users.push({
id: socket.id,
username: username
});
});
socket.on('disconnect', () => {
const presentUser = users.find(user => user.id == socket.id);
users = users.filter(user => user != presentUser);
});
We can use socket id for storing data as a refrence in playerList. whenever user will disconnect you can delete element from object according to socket id
var playerList = {};
io.on("connection", socket => {
if (!Object.values(playerList).includes(playername) && playername != null) {
var U_data = {
[socket.id]: playername
};
playerList = { ...playerList, ...U_data };
}
socket.on("disconnect", function(e, id) {
console.log(socket.id);
delete playerList[socket.id];
io.emit("broadcast", Object.values(playerList));
});
}

How to create online users list using webrtc and nodejs on the server end

I am using webrtc to make a audio, video and chat application where I need keep all the users in a user list in the serverside. Need help how to get this done.
Also, how can I remove users from the list when they logout from the system.
Need help to implement this.
webRTC.rtc.on('connect', function(rtc) {
//Client connected
});
webRTC.rtc.on('send answer', function(rtc) {
//answer sent
});
webRTC.rtc.on('disconnect', function(rtc) {
//Client disconnect
//console.log(webRTC);
});
webRTC.rtc.on('chat_msg', function(data, socket) {
var roomList = webRTC.rtc.rooms[data.room] || [];
for (var i = 0; i < roomList.length; i++) {
var socketId = roomList[i];
if (socketId !== socket.id) {
var soc = webRTC.rtc.getSocket(socketId);
if (soc) {
soc.send(JSON.stringify({
"eventName": "receive_chat_msg",
"data": {
"messages": data.messages,
"id": data.id,
"from": data.from,
"status": data.status,
"email": data.email
}
}), function(error) {
if (error) {
console.log(error);
}
});
}
}
}
});
As I was using webrtc.io module, so below are the methods that helped me to create the userlist and maintain the presence.
webRTC.rtc.on('join_room', function(data, socket) {
// Will get info who joined along with his socket id
}
And
webRTC.rtc.on('room_leave', function(room, socketid) {
// Will get info who left the room
}
Node.js code:
var users = {};
io.sockets.on('connection', function (socket) {
socket.emit('connect', true);
socket.on('message', function (data) {
socket.broadcast.emit('message', data);
});
socket.on('new-user', function (username) {
users[username] = username;
});
socket.on('check-presence', function (username) {
var isUserPresent = !! users[username];
socket.emit('presence', isUserPresent);
});
socket.on('remove-user', function (username) {
var user = users[username];
if (user) delete users[username];
});
});
This may also work (node.js):
var users = {};
io.sockets.on('connection', function (socket) {
var UserName;
socket.emit('connect', true);
socket.on('message', function (data) {
socket.broadcast.emit('message', data);
});
socket.on('new-user', function (username) {
users[username] = username;
UserName = username;
});
socket.on('check-presence', function (username) {
var isUserPresent = !! users[username];
socket.emit('presence', isUserPresent);
});
// removing user on "disconnect"
socket.on('disconnect', function () {
var user = users[UserName];
if (user) delete users[UserName];
});
});
For 1st case; client-side code:
var socket = io.connect();
socket.on('connect', function () {
socket.emit('new-user', 'username');
});
function removeUser() {
socket.emit('remove-user', 'username');
}
window.onbeforeunload = function () {
removeUser();
};
// if someone pressed "F5" key to refresh the page
window.onkeyup = function (e) {
if (e.keyCode == 116)
removeUser();
};
// if someone leaves via <a href>
var anchors = document.querySelectorAll('a'),
length = anchors.length;
for (var i = 0; i < length; i++) {
var a = anchors[i];
if (a.href.indexOf('#') !== 0 && a.getAttribute('target') != '_blank')
a.onclick = function () {
removeUser();
};
}
For 2nd case; client side code:
var socket = io.connect();
socket.on('connect', function () {
socket.emit('new-user', 'username');
});
You can check presence too:
socket.on('presence', isUserPresent) {
// boolean: user is present or not
});
socket.emit('check-presence', 'username');

Resources