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} */ 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} */ 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;