MWSE/Source/Services/Room.js

300 lines
8.3 KiB
JavaScript

const { Client } = require("../Client.js");
let {randomUUID} = require("crypto");
const joi = require("joi");
let {addService,addListener} = require("../WebSocket.js");
let term = require("terminal-kit").terminal;
function Room()
{
/**
* @type {string}
*/
this.id = randomUUID();
/**
* @type {string}
*/
this.name = "";
/**
* @type {string}
*/
this.description = "";
/**
* @type {Client}
*/
this.owner = null;
/**
* @type {Date}
*/
this.createdAt = new Date();
/**
* @type {Map<string, Client>}
*/
this.clients = new Map();
/**
* @type {"public"|"private"}
*/
this.accessType = "";
/**
* @type {"free"|"invite"|"password"|"lock"}
*/
this.joinType = "invite";
/**
* @type {boolean}
*/
this.notifyActionInvite = false;
/**
* @type {boolean}
*/
this.notifyActionJoined = true;
/**
* @type {boolean}
*/
this.notifyActionEjected = true;
/**
* @type {string}
*/
this.password = null;
/**
* @type {string[]}
*/
this.waitingInvited = new Set();
}
/**
* @param {Room} room
*/
Room.prototype.publish = function(room){
Room.rooms.set(this.id, this);
term.green("Room Published ").white(this.name," in ").yellow(this.clients.size).white(" clients")('\n');
};
Room.prototype.toJSON = function(){
let obj = {};
obj.id = this.id;
obj.accessType = this.accessType;
obj.createdAt = this.createdAt;
obj.description = this.description;
obj.joinType = this.joinType;
obj.name = this.name;
obj.owner = this.owner.id;
obj.waitingInvited = [...this.waitingInvited];
return obj;
};
Room.prototype.send = function(obj){
for (const client of this.clients.values()) {
client.send(obj);
}
term.green("Room bulk message ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
};
/**
* @param {Client} client
*/
Room.prototype.join = function(client){
if(this.notifyActionJoined)
{
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/joined']);
};
client.rooms.add(this.id);
this.clients.set(client.id, client);
term.green("Client Room joined ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
};
Room.prototype.down = function(){
term.red("Room is downed ").red(this.name," in ").yellow(this.clients.size + "").red(" clients")('\n');
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/closed']);
Room.rooms.delete(this.id);
};
/**
* @param {Client} client
*/
Room.prototype.eject = function(client){
if(this.notifyActionEjected)
{
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/ejected']);
}
client.rooms.delete(this.id);
this.clients.delete(client.id);
term.red("Client Room ejected ").red(this.name," in ").yellow(this.clients.size + "").red(" clients")('\n');
};
/**
* @type {Map<string, Room>}
*/
Room.rooms = new Map();
addListener('connect',(global, client)=>{
let room = new Room();
room.accessType = "private";
room.joinType = "notify";
room.description = 'Yourself private room, you can invite friends';
room.id = client.id;
room.name = "Your Room | " + client.id;
room.owner = client;
room.join(client);
room.publish();
});
addListener('disconnect',(global, client)=>{
Room.rooms.get(client.id).eject(client);
for (const roomId of client.rooms) {
Room.rooms.get(roomId).eject(client);
}
});
let CreateRoomVerify = joi.object({
type: joi.any().required(),
accessType: joi.string().pattern(/^public$|private$/).required(),
notifyActionInvite: joi.boolean().required(),
notifyActionJoined: joi.boolean().required(),
notifyActionEjected: joi.boolean().required(),
joinType: joi.string().pattern(/^free$|^invite$|^password$|^lock$/).required(),
description: joi.string().required(),
name: joi.string().required()
});
addService(({
client,
end,
global,
message,
next,
response
})=>{
let {type} = message;
switch(type)
{
case 'myroom-info':{
let room = Room.rooms.get(client.id);
end(room.toJSON())
break;
}
case 'room-info':{
let {name} = message;
for (const [roomId,{name:RoomName}] of Room.rooms) {
if(name == RoomName)
{
return end({
status : "success",
room : Room.rooms.get(roomId).toJSON()
})
}
};
return end({
status : "fail",
message : "NOT-FOUND-ROOM"
})
break;
}
case 'joinedrooms':{
let data = [
...client.rooms
].map(e => {
return Room.rooms.get(e).toJSON()
});
end(data)
break;
}
case 'create-room':{
let {error} = CreateRoomVerify.validate(message);
if(error)
{
return end({
status: 'fail',
messages: error.message
})
}else{
let {name} = message;
for (const [,{name:RoomName}] of Room.rooms) {
if(name == RoomName)
{
return end({
status : "fail",
message : "ALREADY-EXISTS"
})
}
}
let room = new Room();
room.accessType = message.accessType;
room.notifyActionInvite = message.notifyActionInvite;
room.notifyActionJoined = message.notifyActionJoined;
room.notifyActionEjected = message.notifyActionEjected;
room.joinType = message.joinType;
room.description = message.description;
room.name = message.name;
room.owner = client;
room.join(client);
room.publish();
end({
status: "success",
room: room.toJSON()
});
}
break;
}
case 'joinroom':{
let {name} = message;
let roomId;
for (const [roomId,{name:RoomName}] of Room.rooms) {
if(name == RoomName)
{
roomId = roomId
}
}
let isRoom = Room.rooms.has(roomId);
if(isRoom)
{
let room = Room.rooms.get(roomId);
if(room.joinType == "lock")
{
return end({
status : "fail",
message : "LOCKED-ROOM"
})
}else if(room.joinType == "password")
{
if(room.password == message.credential)
{
room.join(client);
return end({status : "success"})
}else return end({
status : "fail",
message : "WRONG-PASSWORD"
})
}else if(room.joinType == "free"){
room.join(client);
}else if(room.joinType == "invite"){
room.waitingInvited.add(client.id);
if(room.notifyActionInvite)
{
room.send([{
id: client.id
},"room/invite"]);
}else{
room.owner.send([{
id: client.id
},"room/invite"]);
}
};
}else{
return end({
status : "fail",
message : "NOT-FOUND-ROOM"
})
}
break;
}
}
});
exports.Room = Room;