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
node_modules
ssl/
src/db/users.json

View File

@ -3,10 +3,10 @@ module.exports = Object.seal({
motd: "big th0nk",
_id_PrivateKey: process.env.SALT,
defaultUsername: "Anonymous",
//defaultRoomColor: "#3b5054",
defaultRoomColor: "#9900ff",
// defaultRoomColor: "#3b5054",
defaultRoomColor: "#000000",
// defaultLobbyColor: "#19b4b9",
defaultLobbyColor: "#5e32a8",
defaultLobbyColor: "#9900ff",
// defaultLobbyColor2: "#801014",
defaultLobbyColor2: "#801014",
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();
//call new Server
// call new Server
global.WebSocket = require('ws');
global.EventEmitter = require('events').EventEmitter;
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';
}
global.isBool = function(a){
global.isBool = function(a) {
return typeof a === 'boolean';
}
global.isObj = function(a){
global.isObj = function(a) {
return typeof a === "object" && !Array.isArray(a) && a !== null;
}
let Server = require("./src/Server.js");
let config = require('./config');
global.SERVER = new Server(config);
// below commented because it doesn't work with pm2
/*
let console = process.platform == 'win32' ? new AsyncConsole("", input => {
try {
console.log(JSON.stringify(eval(input)));
@ -28,3 +33,4 @@ let console = process.platform == 'win32' ? new AsyncConsole("", input => {
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 RateLimit = require('./Ratelimit.js').RateLimit;
const RateLimitChain = require('./Ratelimit.js').RateLimitChain;
const User = require("./User.js");
const Database = require("./Database.js");
require('node-json-color-stringify');
class Client extends EventEmitter {
constructor(ws, req, server) {
super();
EventEmitter.call(this);
this.user;
this.connectionid = server.connectionid;
this.server = server;
this.participantId;
this.channel;
this.staticQuotas = {
room: new RateLimit(quotas.room.time)
};
this.quotas = {};
this.ws = ws;
this.req = req;
this.ip = (req.connection.remoteAddress).replace("::ffff:", "");
Database.getUserData(this, server).then(data => {
this.user = new User(this, data);
this.destroied = false;
this.bindEventListeners();
require('./Message.js')(this);
});
}
isConnected() {
return this.ws && this.ws.readyState === WebSocket.OPEN;
}
isConnecting() {
return this.ws && this.ws.readyState === WebSocket.CONNECTING;
}
setChannel(_id, settings) {
if (this.channel && this.channel._id == _id) return;
if (this.server.rooms.get(_id)) {
let room = this.server.rooms.get(_id, settings);
let userbanned = room.bans.get(this.user._id);
if (userbanned && (Date.now() - userbanned.bannedtime >= userbanned.msbanned)) {
room.bans.delete(userbanned.user._id);
userbanned = undefined;
}
if (userbanned) {
room.Notification(this.user._id,
"Notice",
@ -51,9 +64,11 @@ class Client extends EventEmitter {
this.setChannel("test/awkward", settings);
return;
}
let channel = this.channel;
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.join(this);
} else {
@ -61,7 +76,7 @@ class Client extends EventEmitter {
this.server.rooms.set(_id, room);
if (this.channel) this.channel.emit("bye", this);
this.channel = this.server.rooms.get(_id);
this.channel.join(this);
this.channel.join(this, settings);
}
}
sendArray(arr) {
@ -70,6 +85,7 @@ class Client extends EventEmitter {
this.ws.send(JSON.stringify(arr));
}
}
initParticipantQuotas() {
this.quotas = {
//"chat": new Quota(Quota.PARAMS_A_NORMAL),
@ -88,6 +104,7 @@ class Client extends EventEmitter {
"-ls": new Quota(Quota.PARAMS_USED_A_LOT)
}
}
destroy() {
this.ws.close();
if (this.channel) {
@ -102,6 +119,7 @@ class Client extends EventEmitter {
this.destroied = true;
console.log(`Removed Connection ${this.connectionid}.`);
}
bindEventListeners() {
this.ws.on("message", (evt, admin) => {
try {
@ -120,14 +138,17 @@ class Client extends EventEmitter {
}
});
this.ws.on("close", () => {
if (!this.destroied)
if (!this.destroied) {
this.destroy();
}
});
this.ws.addEventListener("error", (err) => {
console.error(err);
if (!this.destroied)
if (!this.destroied) {
this.destroy();
}
});
}
}
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 User = require("./User.js");
const Room = require("./Room.js");
const RoomSettings = require('./RoomSettings');
module.exports = (cl) => {
cl.once("hi", () => {
let user = new User(cl);
user.getUserData().then((data) => {
let msg = {};
msg.m = "hi";
msg.motd = cl.server.welcome_motd;
msg.t = Date.now();
msg.u = data;
msg.v = "Beta";
cl.sendArray([msg])
cl.user = data;
})
})
cl.once("hi", msg => {
let m = {};
m.m = "hi";
m.motd = cl.server.welcome_motd;
m.t = Date.now();
m.u = {
name: cl.user.name,
_id: cl.user._id,
id: cl.participantId,
color: cl.user.color
};
m.v = "https://gitlab.com/hri7566/mpp-server";
cl.sendArray([m]);
});
cl.on("t", msg => {
if (msg.hasOwnProperty("e") && !isNaN(msg.e))
cl.sendArray([{
@ -22,13 +29,17 @@ module.exports = (cl) => {
t: Date.now(),
e: msg.e
}])
})
});
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._id.length > 512) return;
if (!cl.staticQuotas.room.attempt()) return;
cl.setChannel(msg._id, msg.set);
let param;
if (cl.channel.isLobby(cl.channel._id)) {
param = Quota.N_PARAMS_LOBBY;
@ -39,10 +50,12 @@ module.exports = (cl) => {
param = Quota.N_PARAMS_RIDICULOUS;
}
}
param.m = "nq";
cl.sendArray([param])
cl.sendArray([param]);
}
})
});
cl.on("m", (msg, admin) => {
// if (!cl.quotas.cursor.attempt() && !admin) return;
if (!(cl.channel && cl.participantId)) return;
@ -50,43 +63,63 @@ module.exports = (cl) => {
if (!msg.hasOwnProperty("y")) msg.y = null;
if (parseInt(msg.x) == NaN) msg.x = 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) => {
if (!cl.quotas.chown.attempt() && !admin) return;
if (!(cl.channel && cl.participantId)) return;
//console.log((Date.now() - cl.channel.crown.time))
//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 (msg.hasOwnProperty("id")) {
// 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);
if (msg.id == cl.user.id) {
param = Quota.N_PARAMS_RIDICULOUS;
param.m = "nq";
cl.sendArray([param])
}
}
} 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();
param = Quota.N_PARAMS_NORMAL;
param.m = "nq";
cl.sendArray([param])
}
})
cl.on("chset", msg => {
if (!(cl.channel && cl.participantId)) return;
if (!(cl.user._id == cl.channel.crown.userId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = cl.channel.verifySet(cl.channel._id,{});
Object.keys(cl.channel.settings).forEach(key => {
if (cl.channel.settings[key] !== msg.set[key]) {
cl.channel.settings[key] = msg.set[key];
} else {
cl.channel.chown();
param = Quota.N_PARAMS_NORMAL;
param.m = "nq";
cl.sendArray([param]);
}
}
});
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.on("a", (msg, admin) => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('message')) return;
@ -102,7 +135,8 @@ module.exports = (cl) => {
}
cl.channel.emit('a', cl, msg);
}
})
});
cl.on('n', msg => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('t') || !msg.hasOwnProperty('n')) return;
@ -114,7 +148,8 @@ module.exports = (cl) => {
} else {
cl.channel.playNote(cl, msg);
}
})
});
cl.on('+ls', msg => {
if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.set(cl.connectionid, cl);
@ -131,11 +166,13 @@ module.exports = (cl) => {
"c": true,
"u": rooms
}])
})
});
cl.on('-ls', msg => {
if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.delete(cl.connectionid);
})
});
cl.on("userset", msg => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {};
@ -157,7 +194,8 @@ module.exports = (cl) => {
})
}
})
});
cl.on('kickban', msg => {
if (cl.channel.crown == null) return;
if (!(cl.channel && cl.participantId)) return;
@ -169,17 +207,20 @@ module.exports = (cl) => {
let ms = msg.ms || 3600000;
cl.channel.kickban(_id, ms);
}
})
});
cl.on("bye", msg => {
cl.destroy();
})
});
cl.on("admin message", msg => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('password') || !msg.hasOwnProperty('msg')) return;
if (typeof msg.msg != 'object') return;
if (msg.password !== cl.server.adminpass) return;
cl.ws.emit("message", JSON.stringify([msg.msg]), true);
})
});
//admin only stuff
cl.on('color', (msg, admin) => {
if (!admin) return;

View File

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

View File

@ -1,53 +1,24 @@
const ColorEncoder = require("./ColorEncoder.js");
const { promisify } = require('util');
let userdb;
const Database = require("./Database");
class User {
constructor(cl) {
this.cl = cl;
this.server = this.cl.server;
this.userdb = userdb;
this.default_db = {};
constructor(cl, data) {
this.server = cl.server;
this.name = data.name;
this._id = data._id;
this.color = data.color;
this.ip = data.ip;
}
async getUserData() {
if (!userdb || (userdb instanceof Map && [...userdb.entries()] == [])) {
await this.setUpDb();
}
let _id = createKeccakHash('keccak256').update((this.cl.server._id_Private_Key + this.cl.ip)).digest('hex').substr(0, 24);
let usertofind = userdb.get(_id);
if (!usertofind) {
if (typeof usertofind == 'object' && (usertofind.hasOwnProperty('name') && usertofind.name != this.server.defaultUsername)) return;
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,
static updateUserModel(cl, user) {
let u2 = new User(cl, user);
if (typeof(u2) == 'undefined') return;
for (let id in Object.keys(u2)) {
if (!user.hasOwnProperty(id)) {
user[id] = u2[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;