socketry
Version:
A socket library to help with your websocket needs!
188 lines (168 loc) • 6.67 kB
JavaScript
const WebSocket = require('ws');
const { EventEmitter } = require('events');
// ------ Server ------
/**
* Create a new server
* @constructor
* @param {number} port - Choose the port for the server to listen to.
*/
class Server extends EventEmitter {
constructor(port) {
super();
if (typeof port !== 'number') throw Error('Expected type: number');
this.clients = [];
this.rooms = [];
this.port = port;
this.server = new WebSocket.Server({ port });
this.server.on('connection', ws => {
ws.id = `0${Math.round(Math.random() * 10000000000000000)}`;
ws.user = new User(ws);
this.clients.push(ws.user);
this.emit('connection', ws.user);
ws.on('message', msg => {
if (!JSON.parse(msg).socketry) {
let msgB = JSON.parse(msg);
delete msgB.sendRoomName;
ws.user.emit('message', JSON.stringify(msgB), this.rooms.filter(r => r.name == JSON.parse(msg).sendRoomName)[0]);
} else if (JSON.parse(msg).type === 'joinRoom') {
if (!this.rooms.length) ws.send(JSON.stringify({socketry: true, type: 'Error', details: {error: 'No rooms available'}}));
if (!this.rooms.filter(r => r.name == JSON.parse(msg).details.name)) ws.send(JSON.stringify({socketry: true, type: 'Error', details: {error: 'No rooms with that name'}}));
let name = JSON.parse(msg).details.name;
this.rooms.filter(r => r.name == name)[0].clients.push(ws);
ws.send(JSON.stringify({socketry: true, type: 'roomJoined', details: {room: {id: this.rooms.filter(r => r.name == name)[0].id, name: this.rooms.filter(r => r.name == name)[0].name}}}));
} else if (JSON.parse(msg).type === 'leaveRoom') {
let name = JSON.parse(msg).details.name;
this.rooms.filter(r => r.name == name)[0].clients = this.rooms.filter(r => r.name == name)[0].clients.filter(c => c.id !== ws.id);
ws.send(JSON.stringify({socketry: true, type: 'roomLeft', details: {name: JSON.parse(msg).details.name, clients: this.rooms.filter(r => r.name == name)[0].clients.length}}));
}
});
ws.on('close', () => {
ws.user.emit('end', this.clients.filter(c => c.server.readyState !== 1)[0]);
this.clients = this.clients.filter(c => c.server.readyState === 1);
this.rooms.forEach(r => {
r.clients = r.clients.filter(c => c.readyState === 1);
});
});
});
}
/**
* Create a new room
* @param {string} room - Name of the room you would like to connect to
*/
get Room() {
return room.bind(null, this);
}
}
/**
* The room class. New rooms can be created with the Server class
*/
class room extends EventEmitter {
constructor (server, name) {
super();
this.id = `1${Math.round(Math.random() * 10000000000000000)}`;
this.oname = name;
if(!server.rooms.filter(r => r.oname === name).length) {
this.name = name;
} else {
this.name = `${name}-${server.rooms.filter(r => r.oname === name).length}`;
}
this.clients = [];
this.server = {
port: server.port,
socket: server.server
};
server.rooms.push(this);
}
}
/**
* The user class which is passed down to the server connection event (ids start with a '0')
* @constructor
*/
class User extends EventEmitter {
constructor(ws) {
super();
this.server = ws;
}
/**
* Send data to a client
* @param {string} msg - The message content
*/
send(msg) {
this.server.send(msg);
}
}
// ------ Client ------
/**
* The Client class
* @constructor
* @param {string} url - Connect to a socketry server (ids start with a '0')
*/
class Client extends EventEmitter {
constructor(url) {
super();
if (typeof url !== 'string') throw Error('Expected type: number');
this.url = url;
this.client = new WebSocket(url);
this.rooms = [];
this.client.on('open', () => {
this.emit('open');
this.client.on('message', data => {
try {
if (JSON.parse(data).type == 'Error') {
throw Error(JSON.parse(data).details.error);
} else if (JSON.parse(data).type == 'roomJoined') {
this.rooms.push(JSON.parse(data).details.room);
this.emit('join', new clientRoom(JSON.parse(data).details.room, this));
} else if (JSON.parse(data).type == 'roomLeft') {
this.emit('leave', JSON.stringify({name: JSON.parse(data).name, clients: JSON.parse(data).clients}));
}
} catch (err) {}
});
});
}
/**
* End the client connection
*/
end() {
this.client.close();
}
/**
* Join a room
* @param {string} room - Name of the room you would like to join
*/
joinRoom(room) {
if (typeof room !== 'string') throw Error('Expected type: string');
this.client.send(JSON.stringify({socketry: true, type: 'joinRoom', details: {name: room}}));
// {socketry: true, type: 'joinRoom', details: {name: room}}
}
}
class clientRoom extends EventEmitter {
constructor (room, main) {
super();
this.room = room;
this.main = main;
this.main.client.on('message', data => {
try {
JSON.parse(data);
} catch (err) {
this.emit('message', data);
}
});
}
/**
* Send data to a room
* @param {object} msg - The message content
*/
send(msg) {
if (typeof msg !== 'object') throw Error('Expected type: object');
msg.sendRoomName = this.room.name;
this.main.client.send(JSON.stringify(msg));
}
/** Leave a room
*/
leave() {
this.main.rooms = this.main.rooms.filter(r => r.name !== this.room.name);
this.main.client.send(JSON.stringify({socketry: true, type: 'leaveRoom', details: this.room}));
}
}
module.exports = { Server, Client };