How to create/join chat room using ws (Websocket) package in node js
You can create user and room in this way using a socket or without socket
const users = [];//It can be collection(noSQL) or table(SQL)
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => user.room === room && user.name === name);
if(!name || !room) return { error: 'Username and room are required.' };
if(existingUser) return { error: 'Username is taken.' };
const user = { id, name, room };
users.push(user);
return { user };
}
const port = process.env.PORT || 8000
const WebSocket = require('ws');
function noop() {}
function heartbeat() {
this.isAlive = true;
}
const wss = new WebSocket.Server({ port });
var rooms = {};
const paramsExist = (data) =>{
try {
if('meta' in data && 'roomID' in data && 'clientID' in data && 'message' in data){
return true;
}else{
return false;
}
} catch (error) {
return false;
}
}
const roomExist = (roomID) =>{
// check for room is already exist or not
if(roomID in rooms){
return true;
}else{
return false;
}
}
const insideRoomdataExist = (arr,data) =>{
var status = false;
for(var i =0; i<arr.length;i++){
if(data in arr[i]){
status= true;
break;
}
}
return status;
}
const clientExistInRoom = (roomID,ws,clientID) =>{
var status = false;
const data = rooms[roomID];
for(var i =0; i< data.length ;i++){
var temp = data[i];
// if(roomID in temp){
// status=true;
// console.log("hello world");
// }
for(const obj in temp){
// if(ws == temp[obj]){
if(clientID == obj){
status = true;
break;
}
}
}return status;
}
// create room
const createRoom =(data,ws)=>{
try {
var {roomID,clientID} = data;
const status = roomExist(roomID);
if(status){
ws.send(JSON.stringify({
'message':'room already exist',
'status':0
}));
}else{
rooms[roomID] = [];
var obj = {};
obj[clientID] = ws;
rooms[roomID].push(obj);
ws['roomID']=roomID;
ws['clientID']=clientID;
ws['admin']=true;
ws.send(JSON.stringify({
'message':'room created succesfully',
'status':1
}));
}
} catch (error) {
ws.send(JSON.stringify({
'message':'there was some problem in creating a room',
'status':0
}));
}
}
// join room
const joinRoom = (data,ws) => {
try {
var {roomID,clientID} = data;
// check if room exist or not
const roomExist = roomID in rooms;
if(!roomExist){
ws.send(JSON.stringify({
'message':'Check room id',
'status':0
}));
return;
}
// const inRoom = insideRoomdataExist(rooms[roomID],clientID);
const inRoom = clientExistInRoom(roomID,ws,clientID)
if(inRoom){
ws.send(JSON.stringify({
"message":"you are already in a room",
"status":0
}));
}else{
var obj = {};
obj[clientID] = ws;
rooms[roomID].push(obj);
ws['roomID']=roomID
ws['clientID']=clientID;
ws.send(JSON.stringify({
"message":"Joined succesfully",
"status":1
}));
}
} catch (error) {
ws.send(JSON.stringify({
'message':'there was some problem in joining a room',
'status':0
}));
}
}
// send message
const sendMessage = (data,ws,Status=null) => {
try {
var {roomID, message,clientID} = data;
//check whether room exist or not
const roomExist = roomID in rooms;
if(!roomExist){
ws.send(JSON.stringify({
'message':'Check room id',
'status':0
}));
return;
}
// check whether client is in room or not
const clientExist = clientExistInRoom(roomID,ws,clientID);
if(!clientExist){
ws.send(JSON.stringify({
'message':"You are not allowed to send message",
'status':0
}));
return;
}
const obj = rooms[roomID];
for(i=0;i<obj.length;i++){
var temp = obj[i];
for(var innerObject in temp){
var wsClientID = temp[innerObject];
if(ws!==wsClientID){
wsClientID.send(JSON.stringify({
'message':message,
'status':Status?Status:1
}));
}
}
}
} catch (error) {
ws.send(JSON.stringify({
'message':'There was some problem in sending message',
'status':0
}));
}
}
const leaveRoom = (ws,data) => {
try {
const {roomID} = data;
// manual code started------------------------------------------------------------
const roomExist = roomID in rooms;
if(!roomExist){
ws.send(JSON.stringify({
'message':'Check room id',
'status':0
}));
return;
}
if('admin' in ws){
data['message']="Admin left the room.";
sendMessage(data,ws,Status=2);
delete rooms[ws.roomID]
return;
}
else{
// find the index of object
lst_obj = rooms[roomID];
var index = null;
for(let i=0;i<lst_obj.length;i++){
var temp_obj = lst_obj[i];
for(var key in temp_obj){
var temp_inside = temp_obj[key]
if('admin' in temp_inside){
temp_inside.send(JSON.stringify({
'message':'Somebody leave the room',
'status':3
}));
}
if(ws==temp_inside){
index =i;
}
}
}
if(index!=null){
rooms[roomID].splice(index,1);
console.log((rooms[roomID].length));
}
}
} catch (error) {
ws.send(JSON.stringify({
'message':'There was some problem----------------------',
'status':0
}))
}
}
const available_room = (ws) =>{
try {
var available_room_id=[];
for(var i in rooms){
available_room_id.push(parseInt(i));
}
ws.send(JSON.stringify({
"rooms":available_room_id,
"status":4
}))
} catch (error) {
ws.send(JSON.stringify({
'message':'There was some problem----------------------',
'status':0
}))
}
}
wss.on('connection', function connection(ws) {
try {
ws.on('message',(recieveData)=>{
var data = JSON.parse(recieveData);
const error = paramsExist(data);
if(!error){
ws.send(JSON.stringify({
'message':'check params',
'status':0
}));
return;
}
var {roomID,meta} = data;
switch (meta) {
case "create_room":
createRoom(data,ws);
console.log(rooms);
break;
case "join_room":
joinRoom(data,ws);
console.log(rooms);
break;
case "send_message":
sendMessage(data,ws);
console.log(rooms);
break;
case "show_all_rooms":
ws.send(JSON.stringify({
"rooms":[rooms]
}))
break;
default:
ws.send(JSON.stringify({
"message":"Unsupported meta data provided provide valid data",
"status":0
}));
break;
}
})
ws.on('close', function(data) {
leaveRoom(ws,{roomID:ws.roomID,clientID:ws.clientID,message:"Leave request"})
ws.terminate();
});
ws.on('pong', heartbeat);
} catch (error) {
ws.send(JSON.stringify({
"message":"there was some problem",
"status":0
}))
}
});
const interval = setInterval(function ping() {
var a = wss.clients;
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) {
leaveRoom(ws,{roomID:ws.roomID,clientID:ws.clientID});
ws.terminate();
}
ws.isAlive = false;
ws.ping(noop);
});
}, 50000);
const serverFree = setInterval(()=>{
var removeKey = [];
for(const obj in rooms){
if(rooms[obj].length<1){
removeKey.push(obj);
}
}
for(var i =0; i<removeKey.length;i++){
delete rooms[removeKey[i]];
}
},30000)
Hello there above is the code for socket connection using was. All you need to do is provide JSON from the frontend in the meta key. There are 4 params that you need to send from the frontend in JSON using the socket library.
Required keys -meta:"create_room"/"join_room"/"send_message"/"show_all_rooms" -message:"anything" -roomID:"roomid to create or join" -clientID:"unique client id"
package.json file dependies
{
"name": "nodesocket",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node src/app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1",
"ws": "^7.4.6"
}
}
You could try something like:
const rooms = {};
wss.on("connection", socket => {
const uuid = ...; // create here a uuid for this connection
const leave = room => {
// not present: do nothing
if(! rooms[room][uuid]) return;
// if the one exiting is the last one, destroy the room
if(Object.keys(rooms[room]).length === 1) delete rooms[room];
// otherwise simply leave the room
else delete rooms[room][uuid];
};
socket.on("message", data => {
const { message, meta, room } = data;
if(meta === "join") {
if(! rooms[room]) rooms[room] = {}; // create the room
if(! rooms[room][uuid]) rooms[room][uuid] = socket; // join the room
}
else if(meta === "leave") {
leave(room);
}
else if(! meta) {
// send the message to all in the room
Object.entries(rooms[room]).forEach(([, sock]) => sock.send({ message }));
}
});
socket.on("close", () => {
// for each room, remove the closed socket
Object.keys(rooms).forEach(room => leave(room));
});
});
This is only a sketch: you need to handle leave room, disconnection from client (leave all rooms) and delete room when nobody is longer in.