major update

This commit is contained in:
Hri7566 2021-04-12 17:29:02 -04:00
parent 50e85e65bc
commit 13ae21fcbe
11 changed files with 476 additions and 157 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.env .env
node_modules node_modules
ssl/ ssl/
src/db/users.json

View File

@ -3,10 +3,10 @@ module.exports = Object.seal({
motd: "big th0nk", motd: "big th0nk",
_id_PrivateKey: process.env.SALT, _id_PrivateKey: process.env.SALT,
defaultUsername: "Anonymous", defaultUsername: "Anonymous",
//defaultRoomColor: "#3b5054", // defaultRoomColor: "#3b5054",
defaultRoomColor: "#9900ff", defaultRoomColor: "#000000",
// defaultLobbyColor: "#19b4b9", // defaultLobbyColor: "#19b4b9",
defaultLobbyColor: "#5e32a8", defaultLobbyColor: "#9900ff",
// defaultLobbyColor2: "#801014", // defaultLobbyColor2: "#801014",
defaultLobbyColor2: "#801014", defaultLobbyColor2: "#801014",
adminpass: process.env.ADMINPASS, adminpass: process.env.ADMINPASS,

View File

@ -1,26 +1,31 @@
// dotenv to keep secret variables in (they won't show on github) // dotenv
require('dotenv').config(); require('dotenv').config();
//call new Server // call new Server
global.WebSocket = require('ws'); global.WebSocket = require('ws');
global.EventEmitter = require('events').EventEmitter; global.EventEmitter = require('events').EventEmitter;
global.fs = require('fs'); global.fs = require('fs');
global.createKeccakHash = require('keccak'); const AsyncConsole = require('asyncconsole');
const AsyncConsole = require('asyncconsole')
global.isString = function(a){ global.isString = function(a) {
return typeof a === 'string'; return typeof a === 'string';
} }
global.isBool = function(a){
global.isBool = function(a) {
return typeof a === 'boolean'; return typeof a === 'boolean';
} }
global.isObj = function(a){
global.isObj = function(a) {
return typeof a === "object" && !Array.isArray(a) && a !== null; return typeof a === "object" && !Array.isArray(a) && a !== null;
} }
let Server = require("./src/Server.js"); let Server = require("./src/Server.js");
let config = require('./config'); let config = require('./config');
global.SERVER = new Server(config); global.SERVER = new Server(config);
// below commented because it doesn't work with pm2
/*
let console = process.platform == 'win32' ? new AsyncConsole("", input => { let console = process.platform == 'win32' ? new AsyncConsole("", input => {
try { try {
console.log(JSON.stringify(eval(input))); console.log(JSON.stringify(eval(input)));
@ -28,3 +33,4 @@ let console = process.platform == 'win32' ? new AsyncConsole("", input => {
console.log(e.toString()); console.log(e.toString());
} }
}) : {}; }) : {};
*/

5
nodemon.json Normal file
View File

@ -0,0 +1,5 @@
{
"ignore": [
"**/*.json"
]
}

View File

@ -3,42 +3,55 @@ const Quota = require ("./Quota.js");
const quotas = require('../Quotas'); const quotas = require('../Quotas');
const RateLimit = require('./Ratelimit.js').RateLimit; const RateLimit = require('./Ratelimit.js').RateLimit;
const RateLimitChain = require('./Ratelimit.js').RateLimitChain; const RateLimitChain = require('./Ratelimit.js').RateLimitChain;
const User = require("./User.js");
const Database = require("./Database.js");
require('node-json-color-stringify'); require('node-json-color-stringify');
class Client extends EventEmitter { class Client extends EventEmitter {
constructor(ws, req, server) { constructor(ws, req, server) {
super(); super();
EventEmitter.call(this); EventEmitter.call(this);
this.user;
this.connectionid = server.connectionid; this.connectionid = server.connectionid;
this.server = server; this.server = server;
this.participantId; this.participantId;
this.channel; this.channel;
this.staticQuotas = { this.staticQuotas = {
room: new RateLimit(quotas.room.time) room: new RateLimit(quotas.room.time)
}; };
this.quotas = {}; this.quotas = {};
this.ws = ws; this.ws = ws;
this.req = req; this.req = req;
this.ip = (req.connection.remoteAddress).replace("::ffff:", ""); this.ip = (req.connection.remoteAddress).replace("::ffff:", "");
Database.getUserData(this, server).then(data => {
this.user = new User(this, data);
this.destroied = false; this.destroied = false;
this.bindEventListeners(); this.bindEventListeners();
require('./Message.js')(this); require('./Message.js')(this);
});
} }
isConnected() { isConnected() {
return this.ws && this.ws.readyState === WebSocket.OPEN; return this.ws && this.ws.readyState === WebSocket.OPEN;
} }
isConnecting() { isConnecting() {
return this.ws && this.ws.readyState === WebSocket.CONNECTING; return this.ws && this.ws.readyState === WebSocket.CONNECTING;
} }
setChannel(_id, settings) { setChannel(_id, settings) {
if (this.channel && this.channel._id == _id) return; if (this.channel && this.channel._id == _id) return;
if (this.server.rooms.get(_id)) { if (this.server.rooms.get(_id)) {
let room = this.server.rooms.get(_id, settings); let room = this.server.rooms.get(_id, settings);
let userbanned = room.bans.get(this.user._id); let userbanned = room.bans.get(this.user._id);
if (userbanned && (Date.now() - userbanned.bannedtime >= userbanned.msbanned)) { if (userbanned && (Date.now() - userbanned.bannedtime >= userbanned.msbanned)) {
room.bans.delete(userbanned.user._id); room.bans.delete(userbanned.user._id);
userbanned = undefined; userbanned = undefined;
} }
if (userbanned) { if (userbanned) {
room.Notification(this.user._id, room.Notification(this.user._id,
"Notice", "Notice",
@ -51,9 +64,11 @@ class Client extends EventEmitter {
this.setChannel("test/awkward", settings); this.setChannel("test/awkward", settings);
return; return;
} }
let channel = this.channel; let channel = this.channel;
if (channel) this.channel.emit("bye", this); if (channel) this.channel.emit("bye", this);
if (channel) this.channel.updateCh(); if (channel) this.channel.updateCh(this);
this.channel = this.server.rooms.get(_id); this.channel = this.server.rooms.get(_id);
this.channel.join(this); this.channel.join(this);
} else { } else {
@ -61,7 +76,7 @@ class Client extends EventEmitter {
this.server.rooms.set(_id, room); this.server.rooms.set(_id, room);
if (this.channel) this.channel.emit("bye", this); if (this.channel) this.channel.emit("bye", this);
this.channel = this.server.rooms.get(_id); this.channel = this.server.rooms.get(_id);
this.channel.join(this); this.channel.join(this, settings);
} }
} }
sendArray(arr) { sendArray(arr) {
@ -70,6 +85,7 @@ class Client extends EventEmitter {
this.ws.send(JSON.stringify(arr)); this.ws.send(JSON.stringify(arr));
} }
} }
initParticipantQuotas() { initParticipantQuotas() {
this.quotas = { this.quotas = {
//"chat": new Quota(Quota.PARAMS_A_NORMAL), //"chat": new Quota(Quota.PARAMS_A_NORMAL),
@ -88,6 +104,7 @@ class Client extends EventEmitter {
"-ls": new Quota(Quota.PARAMS_USED_A_LOT) "-ls": new Quota(Quota.PARAMS_USED_A_LOT)
} }
} }
destroy() { destroy() {
this.ws.close(); this.ws.close();
if (this.channel) { if (this.channel) {
@ -102,6 +119,7 @@ class Client extends EventEmitter {
this.destroied = true; this.destroied = true;
console.log(`Removed Connection ${this.connectionid}.`); console.log(`Removed Connection ${this.connectionid}.`);
} }
bindEventListeners() { bindEventListeners() {
this.ws.on("message", (evt, admin) => { this.ws.on("message", (evt, admin) => {
try { try {
@ -120,14 +138,17 @@ class Client extends EventEmitter {
} }
}); });
this.ws.on("close", () => { this.ws.on("close", () => {
if (!this.destroied) if (!this.destroied) {
this.destroy(); this.destroy();
}
}); });
this.ws.addEventListener("error", (err) => { this.ws.addEventListener("error", (err) => {
console.error(err); console.error(err);
if (!this.destroied) if (!this.destroied) {
this.destroy(); this.destroy();
}
}); });
} }
} }
module.exports = Client; module.exports = Client;

58
src/Database.js Normal file
View File

@ -0,0 +1,58 @@
const fs = require('fs');
const { promisify } = require('util');
const createKeccakHash = require('keccak');
const ColorEncoder = require('./ColorEncoder');
class Database {
static userdb;
static async load() {
const writeFile = promisify(fs.writeFile);
const readdir = promisify(fs.readdir);
let files = await readdir("src/db/");
if (!files.includes("users.json")) {
await writeFile('src/db/users.json', JSON.stringify(this.default_db, null, 2))
this.userdb = new Map(Object.entries(require("./db/users.json")));
} else {
this.userdb = new Map(Object.entries(require("./db/users.json")));
}
}
static async getUserData(cl, server) {
if (!this.userdb || (this.userdb instanceof Map && [...this.userdb.entries()] == [])) {
await this.load();
}
let _id = createKeccakHash('keccak256').update((cl.server._id_Private_Key + cl.ip)).digest('hex').substr(0, 24);
let usertofind = this.userdb.get(_id);
if (!usertofind) {
if (typeof usertofind == 'object' && (usertofind.hasOwnProperty('name') && usertofind.name != this.server.defaultUsername)) return;
this.userdb.set(_id, {
"color": `#${ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)).toLowerCase()}`,
"name": server.defaultUsername,
"_id": _id,
"ip": cl.ip
});
this.update();
}
let user = this.userdb.get(_id);
return user;
}
static async update() {
const writeFile = promisify(fs.writeFile);
await writeFile('src/db/users.json', JSON.stringify(this.strMapToObj(this.userdb), null, 2));
}
static strMapToObj(strMap) {
return [...strMap.entries()].reduce((obj, [key, value]) => (obj[key] = value, obj), {});
}
}
module.exports = Database;

View File

@ -1,20 +1,27 @@
const Quota = require('./Quota'); const Quota = require('./Quota');
const User = require("./User.js"); const User = require("./User.js");
const Room = require("./Room.js"); const Room = require("./Room.js");
const RoomSettings = require('./RoomSettings');
module.exports = (cl) => { module.exports = (cl) => {
cl.once("hi", () => { cl.once("hi", msg => {
let user = new User(cl); let m = {};
user.getUserData().then((data) => {
let msg = {}; m.m = "hi";
msg.m = "hi"; m.motd = cl.server.welcome_motd;
msg.motd = cl.server.welcome_motd; m.t = Date.now();
msg.t = Date.now(); m.u = {
msg.u = data; name: cl.user.name,
msg.v = "Beta"; _id: cl.user._id,
cl.sendArray([msg]) id: cl.participantId,
cl.user = data; color: cl.user.color
}) };
})
m.v = "https://gitlab.com/hri7566/mpp-server";
cl.sendArray([m]);
});
cl.on("t", msg => { cl.on("t", msg => {
if (msg.hasOwnProperty("e") && !isNaN(msg.e)) if (msg.hasOwnProperty("e") && !isNaN(msg.e))
cl.sendArray([{ cl.sendArray([{
@ -22,13 +29,17 @@ module.exports = (cl) => {
t: Date.now(), t: Date.now(),
e: msg.e e: msg.e
}]) }])
}) });
cl.on("ch", msg => { cl.on("ch", msg => {
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {}; if (typeof(msg.set) !== 'object') msg.set = {};
if (msg.hasOwnProperty("_id") && typeof msg._id == "string") { if (msg.hasOwnProperty("_id") && typeof msg._id == "string") {
if (msg._id.length > 512) return; if (msg._id.length > 512) return;
if (!cl.staticQuotas.room.attempt()) return; if (!cl.staticQuotas.room.attempt()) return;
cl.setChannel(msg._id, msg.set); cl.setChannel(msg._id, msg.set);
let param; let param;
if (cl.channel.isLobby(cl.channel._id)) { if (cl.channel.isLobby(cl.channel._id)) {
param = Quota.N_PARAMS_LOBBY; param = Quota.N_PARAMS_LOBBY;
@ -39,10 +50,12 @@ module.exports = (cl) => {
param = Quota.N_PARAMS_RIDICULOUS; param = Quota.N_PARAMS_RIDICULOUS;
} }
} }
param.m = "nq"; param.m = "nq";
cl.sendArray([param]) cl.sendArray([param]);
} }
}) });
cl.on("m", (msg, admin) => { cl.on("m", (msg, admin) => {
// if (!cl.quotas.cursor.attempt() && !admin) return; // if (!cl.quotas.cursor.attempt() && !admin) return;
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
@ -50,43 +63,63 @@ module.exports = (cl) => {
if (!msg.hasOwnProperty("y")) msg.y = null; if (!msg.hasOwnProperty("y")) msg.y = null;
if (parseInt(msg.x) == NaN) msg.x = null; if (parseInt(msg.x) == NaN) msg.x = null;
if (parseInt(msg.y) == NaN) msg.y = null; if (parseInt(msg.y) == NaN) msg.y = null;
cl.channel.emit("m", cl, msg.x, msg.y) cl.channel.emit("m", cl, msg.x, msg.y);
});
})
cl.on("chown", (msg, admin) => { cl.on("chown", (msg, admin) => {
if (!cl.quotas.chown.attempt() && !admin) return; if (!cl.quotas.chown.attempt() && !admin) return;
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
//console.log((Date.now() - cl.channel.crown.time)) //console.log((Date.now() - cl.channel.crown.time))
//console.log(!(cl.channel.crown.userId != cl.user._id), !((Date.now() - cl.channel.crown.time) > 15000)); //console.log(!(cl.channel.crown.userId != cl.user._id), !((Date.now() - cl.channel.crown.time) > 15000));
if (!(cl.channel.crown.userId == cl.user._id) && !((Date.now() - cl.channel.crown.time) > 15000)) return; if (!(cl.channel.crown.userId == cl.user._id) && !((Date.now() - cl.channel.crown.time) > 15000)) return;
if (msg.hasOwnProperty("id")) { if (msg.hasOwnProperty("id")) {
// console.log(cl.channel.crown) // console.log(cl.channel.crown)
if (cl.user._id == cl.channel.crown.userId || cl.channel.crowndropped) if (!admin) {
if (cl.user._id == cl.channel.crown.userId || cl.channel.crowndropped) {
cl.channel.chown(msg.id); cl.channel.chown(msg.id);
if (msg.id == cl.user.id) { if (msg.id == cl.user.id) {
param = Quota.N_PARAMS_RIDICULOUS; param = Quota.N_PARAMS_RIDICULOUS;
param.m = "nq"; param.m = "nq";
cl.sendArray([param]) cl.sendArray([param])
} }
}
} else { } else {
if (cl.user._id == cl.channel.crown.userId || cl.channel.crowndropped) cl.channel.chown(msg.id);
if (msg.id == cl.user.id) {
param = Quota.N_PARAMS_RIDICULOUS;
param.m = "nq";
cl.sendArray([param])
}
}
} else {
if (!admin) {
if (cl.user._id == cl.channel.crown.userId || cl.channel.crowndropped) {
cl.channel.chown(); cl.channel.chown();
param = Quota.N_PARAMS_NORMAL; param = Quota.N_PARAMS_NORMAL;
param.m = "nq"; param.m = "nq";
cl.sendArray([param]) cl.sendArray([param])
} }
}) } else {
cl.on("chset", msg => { cl.channel.chown();
if (!(cl.channel && cl.participantId)) return; param = Quota.N_PARAMS_NORMAL;
if (!(cl.user._id == cl.channel.crown.userId)) return; param.m = "nq";
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = cl.channel.verifySet(cl.channel._id,{}); cl.sendArray([param]);
Object.keys(cl.channel.settings).forEach(key => { }
if (cl.channel.settings[key] !== msg.set[key]) {
cl.channel.settings[key] = msg.set[key];
} }
}); });
cl.on("chset", msg => {
if (!(cl.channel && cl.participantId)) return;
if (!cl.channel.crown) return;
if (!(cl.user._id == cl.channel.crown.userId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = new RoomSettings(cl.channel.settings, 'user');
cl.channel.settings.changeSettings(msg.set);
cl.channel.updateCh(); cl.channel.updateCh();
}) });
cl.on("a", (msg, admin) => { cl.on("a", (msg, admin) => {
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('message')) return; if (!msg.hasOwnProperty('message')) return;
@ -102,7 +135,8 @@ module.exports = (cl) => {
} }
cl.channel.emit('a', cl, msg); cl.channel.emit('a', cl, msg);
} }
}) });
cl.on('n', msg => { cl.on('n', msg => {
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('t') || !msg.hasOwnProperty('n')) return; if (!msg.hasOwnProperty('t') || !msg.hasOwnProperty('n')) return;
@ -114,7 +148,8 @@ module.exports = (cl) => {
} else { } else {
cl.channel.playNote(cl, msg); cl.channel.playNote(cl, msg);
} }
}) });
cl.on('+ls', msg => { cl.on('+ls', msg => {
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.set(cl.connectionid, cl); cl.server.roomlisteners.set(cl.connectionid, cl);
@ -131,11 +166,13 @@ module.exports = (cl) => {
"c": true, "c": true,
"u": rooms "u": rooms
}]) }])
}) });
cl.on('-ls', msg => { cl.on('-ls', msg => {
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.delete(cl.connectionid); cl.server.roomlisteners.delete(cl.connectionid);
}) });
cl.on("userset", msg => { cl.on("userset", msg => {
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {}; if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {};
@ -157,7 +194,8 @@ module.exports = (cl) => {
}) })
} }
}) });
cl.on('kickban', msg => { cl.on('kickban', msg => {
if (cl.channel.crown == null) return; if (cl.channel.crown == null) return;
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
@ -169,17 +207,20 @@ module.exports = (cl) => {
let ms = msg.ms || 3600000; let ms = msg.ms || 3600000;
cl.channel.kickban(_id, ms); cl.channel.kickban(_id, ms);
} }
}) });
cl.on("bye", msg => { cl.on("bye", msg => {
cl.destroy(); cl.destroy();
}) });
cl.on("admin message", msg => { cl.on("admin message", msg => {
if (!(cl.channel && cl.participantId)) return; if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('password') || !msg.hasOwnProperty('msg')) return; if (!msg.hasOwnProperty('password') || !msg.hasOwnProperty('msg')) return;
if (typeof msg.msg != 'object') return; if (typeof msg.msg != 'object') return;
if (msg.password !== cl.server.adminpass) return; if (msg.password !== cl.server.adminpass) return;
cl.ws.emit("message", JSON.stringify([msg.msg]), true); cl.ws.emit("message", JSON.stringify([msg.msg]), true);
}) });
//admin only stuff //admin only stuff
cl.on('color', (msg, admin) => { cl.on('color', (msg, admin) => {
if (!admin) return; if (!admin) return;

View File

@ -2,7 +2,11 @@
//room class //room class
//room deleter //room deleter
//databases in Map //databases in Map
const createKeccakHash = require('keccak');
const Quota = require("./Quota.js"); const Quota = require("./Quota.js");
const RoomSettings = require('./RoomSettings.js');
class Room extends EventEmitter { class Room extends EventEmitter {
constructor(server, _id, settings) { constructor(server, _id, settings) {
super(); super();
@ -11,9 +15,7 @@ class Room extends EventEmitter {
this.server = server; this.server = server;
this.crown = null; this.crown = null;
this.crowndropped = false; this.crowndropped = false;
this.settings = this.verifySet(this._id, { this.settings = settings;
set: settings
});
this.chatmsgs = []; this.chatmsgs = [];
this.ppl = new Map(); this.ppl = new Map();
this.connections = []; this.connections = [];
@ -21,14 +23,15 @@ class Room extends EventEmitter {
this.server.rooms.set(_id, this); this.server.rooms.set(_id, this);
this.bans = new Map(); this.bans = new Map();
} }
join(cl) { //this stuff is complicated
join(cl, set) { //this stuff is complicated
let otheruser = this.connections.find((a) => a.user._id == cl.user._id) let otheruser = this.connections.find((a) => a.user._id == cl.user._id)
if (!otheruser) { if (!otheruser) {
let participantId = createKeccakHash('keccak256').update((Math.random().toString() + cl.ip)).digest('hex').substr(0, 24); let participantId = createKeccakHash('keccak256').update((Math.random().toString() + cl.ip)).digest('hex').substr(0, 24);
cl.user.id = participantId; cl.user.id = participantId;
cl.participantId = participantId; cl.participantId = participantId;
cl.initParticipantQuotas(); cl.initParticipantQuotas();
if (((this.connections.length == 0 && Array.from(this.ppl.values()).length == 0) && !this.isLobby(this._id)) || this.crown && (this.crown.userId == cl.user._id)) { //user that created the room, give them the crown. if (((this.connections.length == 0 && Array.from(this.ppl.values()).length == 0) && this.isLobby(this._id) == false) || this.crown && (this.crown.userId == cl.user._id)) { //user that created the room, give them the crown.
//cl.quotas.a.setParams(Quota.PARAMS_A_CROWNED); //cl.quotas.a.setParams(Quota.PARAMS_A_CROWNED);
this.crown = { this.crown = {
participantId: cl.participantId, participantId: cl.participantId,
@ -43,15 +46,29 @@ class Room extends EventEmitter {
y: this.getCrownY() y: this.getCrownY()
} }
} }
this.crowndropped = false; this.crowndropped = false;
this.settings = {visible:true,color:this.server.defaultRoomColor,chat:true,crownsolo:false}; this.settings = new RoomSettings(set, 'user');
} else { } else {
//cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL); //cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL);
this.settings = {visible:true,color:this.server.defaultRoomColor,chat:true,crownsolo:false,lobby:true}; console.log(this.isLobby(this._id));
if (this.isLobby(this._id)) {
this.settings = new RoomSettings(this.server.lobbySettings, 'user');
this.settings.visible = true;
this.settings.crownsolo = false;
this.settings.color = this.server.lobbySettings.color;
this.settings.color2 = this.server.lobbySettings.color2;
this.settings.lobby = true;
} else {
this.settings = new RoomSettings(set, 'user');
} }
}
this.ppl.set(participantId, cl); this.ppl.set(participantId, cl);
this.connections.push(cl); this.connections.push(cl);
this.sendArray([{ this.sendArray([{
color: this.ppl.get(cl.participantId).user.color, color: this.ppl.get(cl.participantId).user.color,
id: this.ppl.get(cl.participantId).participantId, id: this.ppl.get(cl.participantId).participantId,
@ -79,6 +96,7 @@ class Room extends EventEmitter {
} }
} }
remove(p) { //this is complicated too remove(p) { //this is complicated too
let otheruser = this.connections.filter((a) => a.user._id == p.user._id); let otheruser = this.connections.filter((a) => a.user._id == p.user._id);
if (!(otheruser.length > 1)) { if (!(otheruser.length > 1)) {
@ -99,31 +117,40 @@ class Room extends EventEmitter {
} }
} }
updateCh(cl) { //update channel for all people in channel updateCh(cl) { //update channel for all people in channel
if (Array.from(this.ppl.values()).length <= 0) { if (Array.from(this.ppl.values()).length <= 0) {
setTimeout(() => { setTimeout(() => {
this.destroy(); this.destroy();
}, 10000); }, 10000);
} }
this.connections.forEach((usr) => { this.connections.forEach((usr) => {
this.server.connections.get(usr.connectionid).sendArray([this.fetchData(usr, cl)]) this.server.connections.get(usr.connectionid).sendArray([this.fetchData(usr, cl)])
}) });
this.server.updateRoom(this.fetchData()); this.server.updateRoom(this.fetchData());
} }
updateParticipant(pid, options) { updateParticipant(pid, options) {
let p = null; let p;
Array.from(this.ppl).map(rpg => { Array.from(this.ppl).map(rpg => {
if(rpg[1].user._id == pid) p = rpg[1]; if (rpg[1].user._id == pid) p = rpg[1];
}); });
if (p == null) return;
if (typeof(p) == 'undefined') return;
options.name ? p.user.name = options.name : {}; options.name ? p.user.name = options.name : {};
options._id ? p.user._id = options._id : {}; options._id ? p.user._id = options._id : {};
options.color ? p.user.color = options.color : {}; options.color ? p.user.color = options.color : {};
this.connections.filter((ofo) => ofo.participantId == p.participantId).forEach((usr) => { this.connections.filter((ofo) => ofo.participantId == p.participantId).forEach((usr) => {
options.name ? usr.user.name = options.name : {}; options.name ? usr.user.name = options.name : {};
options._id ? usr.user._id = options._id : {}; options._id ? usr.user._id = options._id : {};
options.color ? usr.user.color = options.color : {}; options.color ? usr.user.color = options.color : {};
}) });
this.sendArray([{ this.sendArray([{
color: p.user.color, color: p.user.color,
id: p.participantId, id: p.participantId,
@ -132,17 +159,20 @@ class Room extends EventEmitter {
x: p.x || 200, x: p.x || 200,
y: p.y || 100, y: p.y || 100,
_id: p.user._id _id: p.user._id
}]) }]);
} }
destroy() { //destroy room destroy() { //destroy room
if (this.ppl.size > 0) return;
this._id; this._id;
console.log(`Deleted room ${this._id}`); console.log(`Deleted room ${this._id}`);
this.settings = {}; this.settings = undefined;
this.ppl; this.ppl;
this.connnections; this.connnections;
this.chatmsgs; this.chatmsgs;
this.server.rooms.delete(this._id); this.server.rooms.delete(this._id);
} }
sendArray(arr, not, onlythisparticipant) { sendArray(arr, not, onlythisparticipant) {
this.connections.forEach((usr) => { this.connections.forEach((usr) => {
if (!not || (usr.participantId != not.participantId && !onlythisparticipant) || (usr.connectionid != not.connectionid && onlythisparticipant)) { if (!not || (usr.participantId != not.participantId && !onlythisparticipant) || (usr.connectionid != not.connectionid && onlythisparticipant)) {
@ -152,13 +182,16 @@ class Room extends EventEmitter {
console.log(e); console.log(e);
} }
} }
}) });
} }
fetchData(usr, cl) { fetchData(usr, cl) {
let chppl = []; let chppl = [];
[...this.ppl.values()].forEach((a) => { [...this.ppl.values()].forEach((a) => {
chppl.push(a.user); chppl.push(a.user);
}) });
let data = { let data = {
m: "ch", m: "ch",
p: "ofo", p: "ofo",
@ -170,6 +203,7 @@ class Room extends EventEmitter {
}, },
ppl: chppl ppl: chppl
} }
if (cl) { if (cl) {
if (usr.connectionid == cl.connectionid) { if (usr.connectionid == cl.connectionid) {
data.p = cl.participantId; data.p = cl.participantId;
@ -179,13 +213,16 @@ class Room extends EventEmitter {
} else { } else {
delete data.p; delete data.p;
} }
if (data.ch.crown == null) { if (data.ch.crown == null) {
delete data.ch.crown; delete data.ch.crown;
} else { } else {
} }
return data; return data;
} }
verifyColor(strColor) { verifyColor(strColor) {
var test2 = /^#[0-9A-F]{6}$/i.test(strColor); var test2 = /^#[0-9A-F]{6}$/i.test(strColor);
if (test2 == true) { if (test2 == true) {
@ -194,6 +231,7 @@ class Room extends EventEmitter {
return false; return false;
} }
} }
isLobby(_id) { isLobby(_id) {
if (_id.startsWith("lobby")) { if (_id.startsWith("lobby")) {
if (_id == "lobby") { if (_id == "lobby") {
@ -221,12 +259,15 @@ class Room extends EventEmitter {
} }
} }
getCrownY() { getCrownY() {
return 50 - 30; return Math.floor(Math.random() * 10000) / 100;
} }
getCrownX() { getCrownX() {
return 50; return Math.floor(Math.random() * 10000) / 100;
} }
chown(id) { chown(id) {
let prsn = this.ppl.get(id); let prsn = this.ppl.get(id);
if (prsn) { if (prsn) {
@ -259,9 +300,11 @@ class Room extends EventEmitter {
} }
this.crowndropped = true; this.crowndropped = true;
} }
this.updateCh(); this.updateCh();
} }
setCords(p, x, y) {
setCoords(p, x, y) {
if (p.participantId && this.ppl.get(p.participantId)) { if (p.participantId && this.ppl.get(p.participantId)) {
x ? this.ppl.get(p.participantId).x = x : {}; x ? this.ppl.get(p.participantId).x = x : {};
y ? this.ppl.get(p.participantId).y = y : {}; y ? this.ppl.get(p.participantId).y = y : {};
@ -273,6 +316,7 @@ class Room extends EventEmitter {
}], p, false); }], p, false);
} }
} }
chat(p, msg) { chat(p, msg) {
if (msg.message.length > 512) return; if (msg.message.length > 512) return;
let filter = ["AMIGHTYWIND"]; let filter = ["AMIGHTYWIND"];
@ -309,6 +353,7 @@ class Room extends EventEmitter {
this.chatmsgs.push(message); this.chatmsgs.push(message);
} }
} }
playNote(cl, note) { playNote(cl, note) {
this.sendArray([{ this.sendArray([{
m: "n", m: "n",
@ -317,6 +362,7 @@ class Room extends EventEmitter {
t: note.t t: note.t
}], cl, true); }], cl, true);
} }
kickban(_id, ms) { kickban(_id, ms) {
ms = parseInt(ms); ms = parseInt(ms);
if (ms >= (1000 * 60 * 60)) return; if (ms >= (1000 * 60 * 60)) return;
@ -365,6 +411,7 @@ class Room extends EventEmitter {
}) })
} }
Notification(who, title, text, html, duration, target, klass, id) { Notification(who, title, text, html, duration, target, klass, id) {
let obj = { let obj = {
m: "notification", m: "notification",
@ -410,7 +457,7 @@ class Room extends EventEmitter {
}) })
this.on("m", (participant, x, y) => { this.on("m", (participant, x, y) => {
this.setCords(participant, x, y); this.setCoords(participant, x, y);
}) })
this.on("a", (participant, msg) => { this.on("a", (participant, msg) => {
@ -418,37 +465,27 @@ class Room extends EventEmitter {
}) })
} }
verifySet(_id,msg){ verifySet(_id, msg){
if(!isObj(msg.set)) msg.set = {visible:true,color:this.server.defaultRoomColor,chat:true,crownsolo:false}; if(typeof(msg.set) !== 'object') {
if(isBool(msg.set.lobby)){ msg.set = {
if(!this.isLobby(_id)) delete msg.set.lobby; // keep it nice and clean visible: true,
}else{ color: this.server.defaultSettings.color, chat:true,
if(this.isLobby(_id)) msg.set = {visible:true,color:this.server.defaultLobbyColor,color2:this.server.defaultLobbyColor2,chat:true,crownsolo:false,lobby:true}; crownsolo:false
} }
if(!isBool(msg.set.visible)){ }
if(msg.set.visible == undefined) msg.set.visible = (!isObj(this.settings) ? true : this.settings.visible);
else msg.set.visible = true; msg.set = RoomSettings.changeSettings(msg.set);
};
if(!isBool(msg.set.chat)){ if (typeof(msg.set.lobby) !== 'undefined') {
if(msg.set.chat == undefined) msg.set.chat = (!isObj(this.settings) ? true : this.settings.chat); if (msg.set.lobby == true) {
else msg.set.chat = true; if (!this.isLobby(_id)) delete msg.set.lobby; // keep it nice and clean
};
if(!isBool(msg.set.crownsolo)){
if(msg.set.crownsolo == undefined) msg.set.crownsolo = (!isObj(this.settings) ? false : this.settings.crownsolo);
else msg.set.crownsolo = false;
};
if(!isString(msg.set.color) || !/^#[0-9a-f]{6}$/i.test(msg.set.color)) msg.set.color = (!isObj(this.settings) ? this.server.defaultRoomColor : this.settings.color);
if(isString(msg.set.color2)){
if(!/^#[0-9a-f]{6}$/i.test(msg.set.color2)){
if(this.settings){
if(this.settings.color2) msg.set.color2 = this.settings.color2;
else delete msg.set.color2; // keep it nice and clean
} else { } else {
delete msg.set.color2; if (this.isLobby(_id)) {
msg.set = this.server.lobbySettings;
}
} }
} }
};
return msg.set;
} }
} }
module.exports = Room; module.exports = Room;

166
src/RoomSettings.js Normal file
View File

@ -0,0 +1,166 @@
class RoomSettings {
static allowedProperties = {
color: {
type: 'color',
default: "#9900ff",
allowedChange: true,
required: true
},
color2: {
type: 'color2',
default: "#5900bf",
allowedChange: true,
required: false
},
lobby: {
type: 'boolean',
allowedChange: false,
required: false
},
visible: {
type: 'boolean',
default: true,
allowedChange: true,
required: true
},
chat: {
type: 'boolean',
default: true,
allowedChange: true,
required: true
},
owner_id: {
type: 'string',
allowedChange: false,
required: false
},
crownsolo: {
type: 'boolean',
default: false,
allowedChange: true,
required: true
}
}
constructor (set, cont) {
Object.keys(RoomSettings.allowedProperties).forEach(key => {
if (typeof(RoomSettings.allowedProperties[key].default) !== 'undefined') {
if (this[key] !== RoomSettings.allowedProperties[key].default) {
this[key] = RoomSettings.allowedProperties[key].default;
}
}
});
Object.keys(RoomSettings.allowedProperties).forEach(key => {
if (RoomSettings.allowedProperties[key].required == true) {
if (typeof(this[key]) == 'undefined') {
this[key] = RoomSettings.allowedProperties[key].default;
}
}
});
if (typeof(set) !== 'undefined') {
Object.keys(set).forEach(key => {
if (typeof(set[key]) == 'undefined') return;
if (Object.keys(RoomSettings.allowedProperties).indexOf(key) !== -1) {
if (typeof(cont) == 'undefined') {
this[key] = this.verifyPropertyType(key, set[key], RoomSettings.allowedProperties[key].type);
} else {
if (cont == 'user') {
if (RoomSettings.allowedProperties[key].allowedChange) {
this[key] = this.verifyPropertyType(key, set[key], RoomSettings.allowedProperties[key].type);
}
}
}
}
});
}
}
verifyPropertyType(key, pr, type) {
let ret;
if (typeof(RoomSettings.allowedProperties[key]) !== 'object') return;
switch (type) {
case 'color':
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
case 'color2':
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
default:
if (typeof(pr) == type) {
ret = pr;
} else if (typeof(RoomSettings.allowedProperties[key].default) !== 'undefined') {
ret = RoomSettings.allowedProperties[key].default;
} else {
ret = undefined;
}
break;
}
return ret;
}
changeSettings(set) {
Object.keys(set).forEach(key => {
if (RoomSettings.allowedProperties[key].allowedChange) {
this[key] = this.verifyPropertyType(key, set[key], RoomSettings.allowedProperties[key].type);
}
});
}
static changeSettings(set) {
Object.keys(set).forEach(key => {
if (RoomSettings.allowedProperties[key].allowedChange) {
set[key] = RoomSettings.verifyPropertyType(key, set[key], RoomSettings.allowedProperties[key].type);
}
});
return set;
}
static verifyPropertyType(key, pr, type) {
let ret;
if (typeof(RoomSettings.allowedProperties[key]) !== 'object') return;
switch (type) {
case 'color':
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
case 'color2':
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
default:
if (typeof(pr) == type) {
ret = pr;
} else if (typeof(RoomSettings.allowedProperties[key].default) !== 'undefined') {
ret = RoomSettings.allowedProperties[key].default;
} else {
ret = undefined;
}
break;
}
return ret;
}
}
module.exports = RoomSettings;

View File

@ -3,11 +3,13 @@ const banned = require('../banned.json');
const https = require("https"); const https = require("https");
const http = require("http"); const http = require("http");
const fs = require('fs'); const fs = require('fs');
const RoomSettings = require('./RoomSettings');
class Server extends EventEmitter { class Server extends EventEmitter {
constructor(config) { constructor(config) {
super(); super();
EventEmitter.call(this); EventEmitter.call(this);
if (config.ssl == true) { if (config.ssl == true) {
this.https_server = https.createServer({ this.https_server = https.createServer({
key: fs.readFileSync('ssl/privkey.pem', 'utf8'), key: fs.readFileSync('ssl/privkey.pem', 'utf8'),
@ -35,31 +37,42 @@ class Server extends EventEmitter {
} }
}); });
} }
this.defaultUsername = "Anonymous";
this.defaultRoomSettings = new RoomSettings(config.defaultRoomSettings);
this.lobbySettings = new RoomSettings(config.defaultRoomSettings);
this.lobbySettings.lobby = true;
this.lobbySettings.color = config.defaultLobbyColor || "#9900ff";
this.lobbySettings.color2 = config.defaultLobbyColor2 || "#9900ff";
console.log(`Server started on port ${config.port}`); console.log(`Server started on port ${config.port}`);
this.connectionid = 0; this.connectionid = 0;
this.connections = new Map(); this.connections = new Map();
this.roomlisteners = new Map(); this.roomlisteners = new Map();
this.rooms = new Map(); this.rooms = new Map();
this.wss.on('connection', (ws, req) => { this.wss.on('connection', (ws, req) => {
this.connections.set(++this.connectionid, new Client(ws, req, this)); this.connections.set(++this.connectionid, new Client(ws, req, this));
}); });
this.legit_m = ["a", "bye", "hi", "ch", "+ls", "-ls", "m", "n", "devices", "t", "chset", "userset", "chown", "kickban", "admin message", "color", "eval", "notification"] this.legit_m = ["a", "bye", "hi", "ch", "+ls", "-ls", "m", "n", "devices", "t", "chset", "userset", "chown", "kickban", "admin message", "color", "eval", "notification"]
this.welcome_motd = config.motd || "You agree to read this message."; this.welcome_motd = config.motd || "You agree to read this message.";
this._id_Private_Key = config._id_PrivateKey || "boppity"; this._id_Private_Key = config._id_PrivateKey || "boppity";
this.defaultUsername = config.defaultUsername || "Anonymous";
this.defaultRoomColor = config.defaultRoomColor || "#3b5054";
this.defaultLobbyColor = config.defaultLobbyColor || "#19b4b9";
this.defaultLobbyColor2 = config.defaultLobbyColor2 || "#801014";
this.adminpass = config.adminpass || "Bop It"; this.adminpass = config.adminpass || "Bop It";
}; }
updateRoom(data) { updateRoom(data) {
if (!data.ch.settings.visible) return; if (!data.ch.settings.visible) return;
for (let cl of Array.from(this.roomlisteners.values())) { for (let cl of Array.from(this.roomlisteners.values())) {
cl.sendArray([{ cl.sendArray([{
"m": "ls", "m": "ls",
"c": false, "c": false,
"u": [data.ch] "u": [data.ch]
}]) }]);
} }
} }

View File

@ -1,53 +1,24 @@
const ColorEncoder = require("./ColorEncoder.js"); const Database = require("./Database");
const { promisify } = require('util');
let userdb;
class User { class User {
constructor(cl) { constructor(cl, data) {
this.cl = cl; this.server = cl.server;
this.server = this.cl.server; this.name = data.name;
this.userdb = userdb; this._id = data._id;
this.default_db = {}; this.color = data.color;
this.ip = data.ip;
} }
async getUserData() {
if (!userdb || (userdb instanceof Map && [...userdb.entries()] == [])) { static updateUserModel(cl, user) {
await this.setUpDb(); let u2 = new User(cl, user);
} if (typeof(u2) == 'undefined') return;
let _id = createKeccakHash('keccak256').update((this.cl.server._id_Private_Key + this.cl.ip)).digest('hex').substr(0, 24);
let usertofind = userdb.get(_id); for (let id in Object.keys(u2)) {
if (!usertofind) { if (!user.hasOwnProperty(id)) {
if (typeof usertofind == 'object' && (usertofind.hasOwnProperty('name') && usertofind.name != this.server.defaultUsername)) return; user[id] = u2[id];
userdb.set(_id, {
"color": `#${ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)).toLowerCase()}`,
"name": this.server.defaultUsername,
"_id": _id,
"ip": this.cl.ip
});
this.updatedb();
}
let user = userdb.get(_id);
return {
"color": user.color,
"name": user.name,
"_id": user._id,
} }
} }
async updatedb() {
const writeFile = promisify(fs.writeFile);
await writeFile('src/db/users.json', JSON.stringify(User.strMapToObj(userdb), null, 2));
}
async setUpDb() {
const writeFile = promisify(fs.writeFile);
const readdir = promisify(fs.readdir);
let files = await readdir("src/db/");
if (!files.includes("users.json")) {
await writeFile('src/db/users.json', JSON.stringify(this.default_db, null, 2))
userdb = new Map(Object.entries(require("./db/users.json")));
} else {
userdb = new Map(Object.entries(require("./db/users.json")));
}
}
static strMapToObj(strMap) {
return [...strMap.entries()].reduce((obj, [key, value]) => (obj[key] = value, obj), {});
} }
} }
module.exports = User; module.exports = User;