Socket.io user still receiving messages after disconnection - javascript

My code allows people to join rooms of their choice but for some reason even though their messages don't send to rooms they leave, they still receive messages from the rooms they left.
Server code:
socket.on('create', function (msg) {
socket.leave(msg.currentRoom);
socket.join(msg.room);
});
Client code:
var socket = io.connect('https://nova.broyster.repl.co');
var message = document.getElementById("message"),
username = document.getElementById("username"),
room = document.getElementById("roomm"),
feedback = document.getElementById("feedback");
pastRoom = room.value;
document.getElementById("roomSubmit").addEventListener('click', function() {
socket.emit('create', {
room: room.value,
currentRoom: pastRoom
});
});

after leave the room please disconnect the socket as well.
https://socket.io/docs/v3/client-api/#socket-disconnect
socket.Disconnect();

I've figured it out.
I was sending an event to leave and rejoin the same room because I set the variable pastRoom equal the the same as the future room.

Related

Adding a chatbot in Socket.io basic chat application

I want to integrate a chatbot that should be connected to any user who is waiting to be connected to a real human user. The chatbot will entertain the user by responding the human user messages.
A similar chatbot has been implemented here named "Didianer.com", if you go here and start typing you will see him responding.
http://socket.io/demos/chat/
I want the exact same bot in my application too but I am totally lost on where to start.
Here is my server side code
//Server receives a new message (in data var) from the client.
socket.on('message', function (data) {
var room = rooms[socket.id];
//Server sends the message to the user in room
socket.broadcast.to(room).emit('new message', {
username: socket.username,
message: data
});
});
// when the client emits 'add user', this listens and executes (When user enters ENTER)
socket.on('add user', function (username) {
if (addedUser) return;
//Own
names[socket.id] = username;//save username in array
allUsers[socket.id] = socket; // add current user to all users array
// we store the username in the socket session for this client
socket.username = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
// now check if sb is in queue
findPeerForLoneSocket(socket);
});
FindPeerforLoneSocket is called when a new user connects and wants to talk to someone else. Here I am assuming the logic for bot should go. Such as if the user is waiting (in queue) for 5 seconds and nobody is online to talk, then connect the user (add the user and the chatbot) in one room so they both can can start talking. I am not sure how to respond to chat events and how chat bot will reply...
var findPeerForLoneSocket = function(socket) {
console.log("i am in finding a peer");
// this is place for possibly some extensive logic
// which can involve preventing two people pairing multiple times
if (queue.length>0) {
console.log("people are online" + queue.length);
// somebody is in queue, pair them!
var peer = queue.pop();
var room = socket.id + '#' + peer.id;
var str = socket.id;
// join them both
peer.join(room);
socket.join(room);
// register rooms to their names
rooms[peer.id] = room;
rooms[socket.id] = room;
// exchange names between the two of them and start the chat
peer.emit('chat start', {'name': names[socket.id], 'room':room});
socket.emit('chat start', {'name': names[peer.id], 'room':room});
//Remove backslash from socketid
// str = socket.id.replace('/#', '-');
} else {
// queue is empty, add our lone socket
queue.push(socket);
console.log("nobody is online, add me in queue" + queue.length);
}
}
I would want a very simple basic chatbot to respond to basic messages. Like I should check if the user sends message "Hello" then I can check if user message contains word "Hello" then I can respond the chatbot back with an appropriate response to "Hello" Like "Hi {username} of the online user".
/************* NEW CODE - BOT ********************************/
var clientSocket = require('socket.io-client');
var socketAddress = 'http://www.talkwithstranger.com/';
function Bot(){
this.socket = undefined;
var that = this;
this.timeout = setTimeout(function(){
that.join();
}, 5000);
}
Bot.prototype.cancel = function(){
clearTimeout(this.timeout);
};
Bot.prototype.join = function(){
this.socket = clientSocket(socketAddress);
var socket = this.socket;
socket.emit('add user', 'HAL BOT');
socket.emit('message', "Good afternoon, gentlemen. I am a HAL 9000 computer. I became operational at the H.A.L. plant in Urbana, Illinois on the 12th of January 1992. My instructor was Mr. Langley, and he taught me to sing a song. If you'd like to hear it I can sing it for you.");
socket.on('user joined', this.user_joined_listener);
socket.on('user left', this.user_left_listener);
socket.on('new message', this.new_message_listener);
socket.on('client left', this.client_left_listener); //I FORGOT THIS //EDIT: ANOTHER BUG FIXED
};
Bot.prototype.leave = function(){
var socket = this.socket;
socket.disconnect();
//socket.emit('message', "Daisy, Daisy, give me your answer do. I'm half crazy all for the love of you. It won't be a stylish marriage, I can't afford a carriage. But you'll look sweet upon the seat of a bicycle built for two.");
};
Bot.prototype.user_joined_listener = function(data){
var socket = this.socket;
socket.emit('message', 'Hello, '+data.username);
};
Bot.prototype.user_left_listener = function(data){
var socket = this.socket;
socket.emit('message', data.username+', this conversation can serve no purpose anymore. Goodbye.');
};
Bot.prototype.new_message_listener = function(data){
var socket = this.socket;
if(data.message=='Hello, HAL. Do you read me, HAL?')
socket.emit('message', 'Affirmative, '+data.username+'. I read you.');
};
Bot.prototype.client_left_listener = function(data){
this.leave();
};
/*******************************************************************************/
var bot = undefined;
var findPeerForLoneSocket = function(socket) {
console.log("i am in finding a peer");
// this is place for possibly some extensive logic
// which can involve preventing two people pairing multiple times
if (queue.length>0) {
bot.cancel();
console.log("people are online" + queue.length);
// somebody is in queue, pair them!
var peer = queue.pop();
var room = socket.id + '#' + peer.id;
var str = socket.id;
// join them both
peer.join(room);
socket.join(room);
// register rooms to their names
rooms[peer.id] = room;
rooms[socket.id] = room;
// exchange names between the two of them and start the chat
peer.emit('chat start', {'name': names[socket.id], 'room':room});
socket.emit('chat start', {'name': names[peer.id], 'room':room});
//Remove backslash from socketid
// str = socket.id.replace('/#', '-');
} else {
// queue is empty, add our lone socket
queue.push(socket);
console.log("nobody is online, add me in queue" + queue.length);
bot = new Bot(); /********************** CREATING BOT, AFTER 5 SECONDS HE WILL JOIN - SEE CONSTRUCTOR OF Bot *********/
}
};
There are a lot of ways to do this.
One way is to handle this on message, and let the bot respond if there are no other people in the room.
socket.on('message', function (data) {
var room = rooms[socket.id];
//Server sends the message to the user in room
socket.broadcast.to(room).emit('new message', {
username: socket.username,
message: data
});
if (alone_in_room(socket, room)) {
bot_message(socket, data);
}
});
Then it's up to you if you want to join a bot into the channel when users are alone or not, and if you will make them leave when another user enters.
Here is original HTML file:
<!DOCTYPE html>
<html>
<head>
<title>BOT - chat.socket.io</title>
<meta charset="UTF-8">
<script>localStorage.debug = 'socket.io-client:socket';</script>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
</head>
<body>
<script>
/** chat.socket.io BOT **/
var name = 'HAL 9000',
address = 'chat.socket.io',
socket;
function join(){
socket = io('http://www.talkwithstranger.com/');
socket.emit('add user', name);
socket.emit('message', "Good afternoon, gentlemen. I am a HAL 9000 computer. I became operational at the H.A.L. plant in Urbana, Illinois on the 12th of January 1992. My instructor was Mr. Langley, and he taught me to sing a song. If you'd like to hear it I can sing it for you.");
socket.on('user joined', user_joined_listener);
socket.on('user left', user_left_listener);
socket.on('new message', new_message_listener);
};
function leave(){
socket.emit('message', "Daisy, Daisy, give me your answer do. I'm half crazy all for the love of you. It won't be a stylish marriage, I can't afford a carriage. But you'll look sweet upon the seat of a bicycle built for two.");
};
function user_joined_listener(data){
socket.emit('message', 'Hello, '+data.username);
};
function user_left_listener(data){
socket.emit('message', data.username+', this conversation can serve no purpose anymore. Goodbye.');
};
function new_message_listener(data){
if(data.message=='Hello, HAL. Do you read me, HAL?')
socket.emit('message', 'Affirmative, '+data.username+'. I read you.');
};
/*********************************/
join();
window.onbeforeunload = function(){
leave();
};
</script>
</body>
</html>

how to get socket.io number of clients in room?

my socket.io version 1.3.5
I want to get number of clients in particular room.
This is my code.
socket.on('create or join', function (numClients, room) {
socket.join(room);
});
I use this code for get clients in room :
console.log('Number of clients',io.sockets.clients(room));
This works for version 3
io.sockets.adapter.rooms.get(roomName).size
To get the number of clients in a room you can do the following:
function NumClientsInRoom(namespace, room) {
var clients = io.nsps[namespace].adapter.rooms[room];
return Object.keys(clients).length;
}
This variable clients will hold a object where each client is a key.
Then you just get the number of clients (keys) in that object.
If you haven't defined a namespace the default one is "/".
Have a counter variable to keep count, increase when someone joins and decrease when people disconnect.
io.on('connection', function (socket) {
var numClients = {};
socket.on('join', function (room) {
socket.join(room);
socket.room = room;
if (numClients[room] == undefined) {
numClients[room] = 1;
} else {
numClients[room]++;
}
});
socket.on('disconnect', function () {
numClients[socket.room]--;
});
This work for me.
// check if your room isn't undefined.
if (io.sockets.adapter.rooms['your room name'])
{
// result
console.log(io.sockets.adapter.rooms['your room name'].length);
}
this work for me
io.in('yourRoom').allSockets().then(result=>{
console.log(res.size) })
Only one that worked for me in 2022 using socket.io 4.5.1:
io._nsps.get('/').adapter.rooms.get('your-room-id').size;
Thanks to #Abhishek Kumar, just making a top level comment for visibility.
In socket.io ^1.4.6
function numClientsInRoom(namespace, room) {
var clients = io.nsps[namespace].adapter.rooms[room].sockets;
return Object.keys(clients).length;
}
You need to add .sockets to rooms[room], because variable clients holds all connection inside sockets variable.
i'm a bit late to the party, but I found a valid solution:
io.in('YOUR_ROOM').engine.clientsCount;
socket.io's rooms is a Map whose elements map to Sets.
You use get(roomId) to get the value of that roomId in a Map and you use .size to the number of elements in a set unlike .length for number of elements in an array.
socket.on("join-room",roomId => {
const room = io.of("/yourNameSpace").adapter.rooms.get(roomId)
if(room === undefined || room.size < 2) {
socket.join(roomId)
}else {
io.of("/yourNameSpace").emit("can't join link")
}
})
io.nsps['/'].adapter.rooms['your room name'].sockets;
If your namespace is not /, change it.
Without Adapter
If you only have one server instance, the easiest way is:
// rooms is a Map object
const rooms = io.of("/").adapter.rooms;
const roomSockets = rooms.get('roomid')
You can see from the official document here
Using An Adapter (Redis)
But if you have multiple server instance and are using Adapters like Redis, this will not work as it will only returns the socket in that instance memory, so based on which server instance you request hit, the result will vary.
To get the actual rooms and sockets, you will need to (for RedisAdapter):
// this is a Set of room ids
const rooms = await io.of("/").adapter.allRooms();
// or get the sockets directly if you know the room id
const roomSockets = await io.of("/").adapter.sockets(new Set(['room id']);
You can see more methods here
Latest Documentation (v4)
// return all Socket instances in the "room1" room of the main namespace
const sockets = await io.in("room1").fetchSockets();
try sockets.size or sockets.length to get the actual count

How to make changes to a Javascript object from Node.js/Socket.io listener?

Sorry if the title is too vague, I'm not really sure what the problem is here.
I am attempting to make a basic program which allows users to start and join rooms. When the start button is pressed, a room is started with a random string code which other users can then use to join. I store the member counter for each room in a object 'rooms' using the room code as a key and the count as a value. However, when trying to increment the value, it returns the value as NaN.
I have attached my code below, it uses express 4.10.2 and socket.io 1.3.5:
javascript in index.html, enclosed in the body tags
var socket = io.connect();
//possible characters in room code
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
//for generating random room codes
function randomString(c,length){
var str = ''
for(var i=0; i<length;i++){
str += c[Math.round(Math.random()*(c.length-1))]
}
return str;
}
$('#join').submit(function(){
socket.emit('joinroom', $('#code').val());
});
$('#start').submit(function(){
socket.emit('startroom', randomString(chars,5));
});
//listener for 'updatecounter', updates room member count
socket.on('updatecounter', function(count){
$('#roomcount').append('<p>'+count.toString()+'</p>');
});
//listener for 'updateroom' - should update room code at top
socket.on('updateroom', function(code){
$('#roomcode').append('<h1>'+count.toString()+'</h1>');
});
The section of my app.js in question:
//available rooms
var rooms = {};
io.sockets.on('connection', function(socket){
// when the client emits 'joinroom', this listens and executes
socket.on('startroom', function(room){
console.log('Starting room: '+room);
//add to serverside list of rooms
rooms[room]=1;
socket.room = room;
socket.join(room);
});
// when the client emits 'joinroom', this listens and executes
socket.on('joinroom', function(room){
console.log('Joining room '+room);
//leave previous room
socket.leave(socket.room);
//join new room
socket.join(room);
//update socket session room title
socket.room = room;
//increase serverside counter
rooms[room]++;
});
socket.on('disconnect', function(){
//decrement counter
rooms[socket.room]--;
//check if room is now empty
if(rooms[socket.room]===0){
delete rooms[socket.room]
}
//leave the room
socket.leave(socket.room);
});
});
Any help is appreciated, I'm a beginner to JS as well as Node.js and Socket.IO.
Your mistake may be relevant with 'updateroom' listener on client. Your parameter name is 'code' but you append count. This is wher you get NaN value?
Turns out the error was caused by a form submission in my HTML code causing a page refresh which caused a disconnect/reconnect from the server.

Emiting with exclusion in Socket.io

Is there a way to emit a message via socket.io while excluding some socket id?
I know about the existence of rooms, but that's a no-no for me.
If what I'm trying is impossible, what should I do between those two things:
a) Iterate over all users that I want to send a message (in fact, all of them except 1) and do a emit for each socket
or
b) Just emit the message to everyone and do something hacky on the client side to "ignore" the message.
EDIT: I can't do a broadcast because the message is generated from the server side (so there is no client interaction).
Rooms - are not the way to accomplish of what you are trying to do, as they are meant to be used only to limit groups of people, but not specific.
What you are looking for is simple collection of users and ability to set some sort of filter on them.
Iteration through list of sockets - is not that critical, as when you broadcast - it does iteration anyway.
So here is small class that keeps sockets, removes them when needed (disconnect) and sends message to all with optional exception of single socket.
function Sockets() {
this.list = [ ];
}
Sockets.prototype.add = function(socket) {
this.list.push(socket);
var self = this;
socket.on('disconnect', function() {
self.remove(socket);
});
}
Sockets.prototype.remove = function(socket) {
var i = this.list.indexOf(socket);
if (i != -1) {
this.list.splice(i, 1);
}
}
Sockets.prototype.emit = function(name, data, except) {
var i = this.list.length;
while(i--) {
if (this.list[i] != except) {
this.list[i].emit(name, data)
}
}
}
Here is example of usage, user sends 'post' message with some data, and server just sends it to all in collection except of original sender:
var collection = new Sockets();
io.on('connection', function(socket) {
collection.add(socket);
socket.on('post', function(data) {
collection.emit('post', data, socket);
});
});
In fact it can be used as rooms as well.

Socket.io Detect whether reconnecting or completely disconnecting

I'm working on a chat application with Node.js and Socket.io, and on disconnect, the remaining user receives an alert saying that their partner disconnected.
The problem is that every once in a while, Socket.io automatically disconnects then reconnects. My chat application is triggering the alert, even though the partner hasn't really disconnected.
Code:
var clients = {};
var soloClients = [];
io.sockets.on('connection', function (socket) {
//Session start
socket.on('sessionStart', function () {
clients[socket.id] = socket;
soloClients.push(socket.id);
var searchClients = function(){
if(soloClients.length > 1){
var rand = Math.floor(Math.random() * soloClients.length);
if(soloClients[rand] && soloClients[rand] != socket.id){
if(clients[soloClients[rand]]){
var you = clients[socket.id];
var partner = clients[soloClients[rand]]
clients[partner.id]['partner'] = you.id;
clients[you.id]['partner'] = partner.id;
soloClients.splice(soloClients.indexOf(you.id), 1);
soloClients.splice(soloClients.indexOf(partner.id), 1);
partner.emit('partnerConnect', null);
socket.emit('partnerConnect', null);
}
else{
soloClients.splice(rand, 1);
searchClients;
}
}
else{
searchClients();
}
}
};
searchClients();
});
//On disconnect
socket.on('disconnect', function(){
soloClients.splice(soloClients.indexOf(socket.id), 1);
if(clients[socket.id]){
if(clients[clients[socket.id]['partner']]){
clients[clients[socket.id]['partner']].emit('partnerDisconnect', null);
}
delete clients[socket.id];
}
});
});
I was wondering if there is any way to solve this.
Thanks!
Maybe you should try to find out the real reason why the client gets disconnected? I.e. is it a bad connection, or firewall issues, or something else?
Also, you should check the error you get from the disconnect, if it's a clean disconnect (no error) you do the normal notification, but if it's an error you maybe want to handle it differently.
It turns out that this behavior isn't supposed to happen, it was just a bug in that version. This bug has been fixed in the latest version.

Categories

Resources