best update ever

This commit is contained in:
Hri7566 2021-05-09 04:52:41 -04:00
parent 35c52b5a12
commit 2f33925be5
17 changed files with 1426 additions and 2110 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
node_modules
ssl/
src/db/users.json
.history

View File

@ -31,4 +31,4 @@ module.exports = Object.seal({
amount: 2,
time: 1000
}
})
});

View File

@ -4,15 +4,15 @@ module.exports = Object.seal({
_id_PrivateKey: process.env.SALT,
defaultUsername: "Anonymous",
// defaultRoomColor: "#3b5054",
defaultRoomColor: "#000000",
// defaultLobbyColor: "#19b4b9",
defaultLobbyColor: "#9900ff",
defaultLobbyColor: "#76b0db",
// defaultLobbyColor2: "#801014",
defaultLobbyColor2: "#801014",
defaultLobbyColor2: "#276491",
adminpass: process.env.ADMINPASS,
ssl: true,
ssl: false,
defaultRoomSettings: {
color: "#000000",
color: "#3b5054",
color2: "#001014",
crownsolo: false,
visible: true
}

1026
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,8 @@
"dotenv": "^8.2.0",
"events": "^3.1.0",
"keccak": "^2.1.0",
"level": "^7.0.0",
"mongoose": "^5.12.7",
"node-json-color-stringify": "^1.1.0",
"ws": "^7.2.3"
}

View File

@ -71,7 +71,9 @@ class Client extends EventEmitter {
if (channel) this.channel.updateCh(this);
this.channel = this.server.rooms.get(_id);
if (!this.user.hasFlag("hidden", true)) {
this.channel.join(this);
}
} else {
let room = new Room(this.server, _id, settings);
this.server.rooms.set(_id, room);
@ -109,7 +111,7 @@ class Client extends EventEmitter {
destroy() {
this.ws.close();
if (this.channel) {
this.channel.emit("bye", this)
this.channel.emit("bye", this);
}
this.user;
this.participantId;
@ -118,7 +120,7 @@ class Client extends EventEmitter {
this.connectionid;
this.server.connections.delete(this.connectionid);
this.destroied = true;
console.log(`Removed Connection ${this.connectionid}.`);
console.log(`Removed Connection ${this.connectionid}. user is ${this.user}`);
}
bindEventListeners() {

19
src/Crown.js Normal file
View File

@ -0,0 +1,19 @@
module.exports = class Crown {
constructor (id, _id) {
this.participantId = id;
this.userId = _id;
this.time = Date.now();
this.startPos = {
x: 50,
y: 50
}
this.endPos = {
x: Crown.generateRandomPos(),
y: Crown.generateRandomPos()
}
}
static generateRandomPos() {
return Math.floor(Math.random() * 10000) / 100;
}
}

View File

@ -2,57 +2,143 @@ const fs = require('fs');
const { promisify } = require('util');
const createKeccakHash = require('keccak');
const ColorEncoder = require('./ColorEncoder');
const UserModel = require('./UserModel');
const mongoose = require('mongoose');
const level = require('level');
const { db } = require('./UserModel');
mongoose.connect(process.env.MONGO_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
}, err => {
if (err) {
console.error(err);
return;
}
console.log("Connected to Database");
});
class Database {
static userdb;
static roomdb;
static async load() {
const writeFile = promisify(fs.writeFile);
const readdir = promisify(fs.readdir);
this.userdb = mongoose.connection;
this.roomdb = level('src/db/rooms.db');
// 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")));
}
// 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()] == [])) {
if (!this.userdb) {
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);
let user = await UserModel.findOne({ _id: _id }).exec();
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();
if (user == null) {
user = this.createUser(_id);
}
let user = this.userdb.get(_id);
console.log('user flags:');
console.log(user.flags);
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 async createUser(_id) {
let user = new UserModel({
name: "Anonymous",
_id: _id,
color: "#" + ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)),
flags: {
}
});
user.save();
return user;
}
static async updateUser(_id, data) {
UserModel.findById(_id, (err, doc) => {
if (err) {
console.error(err);
return err;
}
if (!doc) {
return console.warn('Could not find user to save.');
}
doc.set(data);
doc.save();
});
}
static async wipe() {
await UserModel.find({}, (err, docs) => {
docs.forEach(doc => {
doc.remove();
});
}).exec();
}
static strMapToObj(strMap) {
return [...strMap.entries()].reduce((obj, [key, value]) => (obj[key] = value, obj), {});
}
static getRoomSettings(_id, cb) {
let key = "room~"+_id;
roomSettings
this.roomdb.get(key, (err, value) => {
if (err || !value || value == "") {
cb(err, value);
return;
}
cb(undefined, value);
});
}
static setRoomSettings(_id, roomSettings, chat) {
let roomData = new RoomDataModel(roomSettings, chat);
let key = "room~"+_id;
this.roomdb.put(key, JSON.stringify(roomData));
}
static getRoomSettings(_id, cb) {
let key = "room~"+_id;
this.roomdb.get(key, (err, value) => {
if (err) {
cb(err);
return;
}
let settings = JSON.parse(value);
cb(err, settings);
});
}
static deleteRoomSettings(_id) {
this.roomdb.del("room~"+_id);
}
}
class RoomDataModel {
constructor (settings, chat) {
this.settings = settings;
this.chat = chat;
}
}
module.exports = Database;

View File

@ -6,6 +6,7 @@ const Database = require('./Database');
module.exports = (cl) => {
cl.once("hi", (msg, admin) => {
console.log('hi on')
if (msg.hasOwnProperty("password")) {
if (msg.password == "hideme") {
cl.hidden = true;
@ -23,8 +24,7 @@ module.exports = (cl) => {
color: cl.user.color
};
m.v = "https://gitlab.com/hri7566/mpp-server";
m.v = "2.0";
cl.sendArray([m]);
});
@ -40,7 +40,7 @@ module.exports = (cl) => {
cl.on("ch", msg => {
if (typeof(msg.set) !== 'object') msg.set = {};
if (msg.hasOwnProperty("_id") && typeof msg._id == "string") {
if (typeof(msg._id) == "string") {
if (msg._id.length > 512) return;
if (!cl.staticQuotas.room.attempt()) return;
@ -57,8 +57,12 @@ module.exports = (cl) => {
}
}
param.m = "nq";
setTimeout(() => {
cl.user.checkFlags();
cl.sendArray([param]);
}, 1000);
}
});
@ -164,7 +168,7 @@ module.exports = (cl) => {
cl.server.roomlisteners.set(cl.connectionid, cl);
let rooms = [];
for (let room of Array.from(cl.server.rooms.values())) {
let data = room.fetchData().ch;
let data = room.fetchChannelData().ch;
if (room.bans.get(cl.user._id)) {
data.banned = true;
}
@ -190,10 +194,11 @@ module.exports = (cl) => {
if(!cl.quotas.userset.attempt()) return;
cl.user.name = msg.set.name;
Database.getUserData(cl, cl.server).then((usr) => {
let dbentry = Database.userdb.get(cl.user._id);
if (!dbentry) return;
dbentry.name = msg.set.name;
Database.update();
// let dbentry = Database.userdb.get(cl.user._id);
// if (!dbentry) return;
// dbentry.name = msg.set.name;
// Database.update();
Database.updateUser(cl.user._id, cl.user);
cl.server.rooms.forEach((room) => {
room.updateParticipant(cl.user._id, {
name: msg.set.name
@ -218,6 +223,7 @@ module.exports = (cl) => {
});
cl.on("bye", msg => {
clearInterval(cl.user.rainbow);
cl.destroy();
});
@ -264,7 +270,52 @@ module.exports = (cl) => {
cl.on('notification', (msg, admin) => {
if (!admin) return;
if (!msg.hasOwnProperty("id") || (!msg.hasOwnProperty("targetChannel") && !msg.hasOwnProperty("targetUser")) || !msg.hasOwnProperty("target") || !msg.hasOwnProperty("duration") || !msg.hasOwnProperty("class") || !msg.hasOwnProperty("html")) return;
cl.channel.Notification(msg.targetUser || msg.targetChannel, null, null, msg.html, msg.duration, msg.target, msg.class);
if (!msg.hasOwnProperty("id") || (!msg.hasOwnProperty("targetChannel") && !msg.hasOwnProperty("targetUser"))
|| !msg.hasOwnProperty("target") || !msg.hasOwnProperty("duration")) return;
let id = msg.id;
let targetChannel = msg.targetChannel;
let targetUser = msg.targetUser;
let target = msg.target;
let duration = msg.duration;
let klass;
let title;
let text;
let html;
if (msg.hasOwnProperty("class")) {
klass = msg.class;
}
if (!msg.hasOwnProperty("html")) {
if (!msg.hasOwnProperty("title") || !msg.hasOwnProperty("text")) return;
title = msg.title;
text = msg.text;
} else {
html = msg.html;
}
cl.channel.Notification(targetUser || targetChannel, title, text, html, duration, target, klass, id);
});
cl.on('user_flag', (msg, admin) => {
if (!admin) return;
if (!msg.hasOwnProperty('_id') || !msg.hasOwnProperty('key') || !msg.hasOwnProperty('value')) return;
console.log("stuff");
cl.server.connections.forEach((usr) => {
if ((usr.channel && usr.participantId && usr.user) && (usr.user._id == msg._id || (usr.participantId == msg.id))) {
if (!usr.hasOwnProperty('user')) return;
usr.user.flags[msg.key] = msg.value;
usr.user.checkFlags();
}
});
console.log(`set user flag: ${msg.key} - ${msg.value}`);
});
cl.on('clear_chat', (msg, admin) => {
if (!admin) return;
});
}

View File

@ -45,61 +45,72 @@ class Quota {
this.setParams(params);
this.resetPoints();
this.interval;
};
}
static N_PARAMS_LOBBY = {
allowance: 200,
max: 600,
interval: 2000
};
}
static N_PARAMS_NORMAL = {
allowance: 400,
max: 1200,
interval: 2000
};
}
static N_PARAMS_RIDICULOUS = {
allowance: 600,
max: 1800,
interval: 2000
};
}
static PARAMS_OFFLINE = {
allowance: 8000,
max: 24000,
maxHistLen: 3,
interval: 2000
};
}
static PARAMS_A_NORMAL = {
allowance: 4,
max: 4,
interval: 6000
};
}
static PARAMS_A_CROWNED = {
allowance:10,
max:10,
interval: 2000
}
static PARAMS_CH = {
allowance: 1,
max: 2,
interval: 1000
}
static PARAMS_USED_A_LOT = {
allowance:1,
max:1,
interval: 2000
}
static PARAMS_M = {
allowance:15000,
max:500000,
interval: 2000
}
getParams() {
return {
m: "nq",
allowance: this.allowance,
max: this.max,
maxHistLen: this.maxHistLen
};
};
}
}
setParams(params) {
params = params || Quota.PARAMS_OFFLINE;
var allowance = params.allowance || this.allowance || Quota.PARAMS_OFFLINE.allowance;
@ -118,14 +129,16 @@ class Quota {
return true;
}
return false;
};
}
resetPoints() {
this.points = this.max;
this.history = [];
for (var i = 0; i < this.maxHistLen; i++)
this.history.unshift(this.points);
if (this.cb) this.cb(this.points);
};
}
tick() {
// keep a brief history
this.history.unshift(this.points);
@ -137,7 +150,8 @@ class Quota {
// fire callback
if (this.cb) this.cb(this.points);
}
};
}
spend(needed) {
// check whether aggressive limitation is needed
var sum = 0;
@ -153,7 +167,7 @@ class Quota {
if (this.cb) this.cb(this.points); // fire callback
return true;
}
};
}
}
module.exports = Quota

6
src/Rank.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = class Rank {
constructor (name, _id) {
this.name = name;
this._id = _id;
}
}

View File

@ -1,14 +1,15 @@
const createKeccakHash = require('keccak');
const Crown = require('./Crown.js');
const Database = require('./Database.js');
const Quota = require("./Quota.js");
const RoomSettings = require('./RoomSettings.js');
class Room extends EventEmitter {
constructor(server, _id, settings) {
super();
EventEmitter.call(this);
this._id = _id;
this.server = server;
this.crown = null;
this.crown;
this.crowndropped = false;
this.settings = settings;
this.chatmsgs = [];
@ -17,30 +18,31 @@ class Room extends EventEmitter {
this.bindEventListeners();
this.server.rooms.set(_id, this);
this.bans = new Map();
this.flags = {}
Database.getRoomSettings(this._id, (err, set) => {
if (err) {
return;
}
this.settings = set.settings;
this.chatmsgs = set.chat;
this.setData();
});
}
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) == 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,
userId: cl.user._id,
time: Date.now(),
startPos: {
x: 50,
y: 50
},
endPos: {
x: this.getCrownX(),
y: this.getCrownY()
}
}
this.crown = new Crown(cl.participantId, cl.user._id);
this.crowndropped = false;
this.settings = new RoomSettings(set, 'user');
@ -105,7 +107,6 @@ class Room extends EventEmitter {
if (!(otheruser.length > 1)) {
this.ppl.delete(p.participantId);
this.connections.splice(this.connections.findIndex((a) => a.connectionid == p.connectionid), 1);
console.log(`Deleted client ${p.user.id}`);
this.sendArray([{
m: "bye",
p: p.participantId
@ -125,19 +126,15 @@ class Room extends EventEmitter {
if (Array.from(this.ppl.values()).length <= 0) {
setTimeout(() => {
this.destroy();
}, 10000);
}, 5000);
}
this.connections.forEach((usr) => {
let u = this.fetchData(usr, cl);
u.ppl.forEach(c => {
c.ip = undefined;
c.server = undefined;
});
this.server.connections.get(usr.connectionid).sendArray([u])
let u = this.fetchChannelData(usr, cl);
this.server.connections.get(usr.connectionid).sendArray([u]);
});
this.server.updateRoom(this.fetchData());
this.server.updateRoom(this.fetchChannelData());
}
updateParticipant(pid, options) {
@ -195,11 +192,17 @@ class Room extends EventEmitter {
});
}
fetchData(usr, cl) {
fetchChannelData(usr, cl) {
let chppl = [];
[...this.ppl.values()].forEach((a) => {
chppl.push(a.user);
[...this.ppl.values()].forEach(c => {
let u = {
_id: c.user._id,
name: c.user.name,
color: c.user.color,
id: c.participantId
}
chppl.push(u);
});
let data = {
@ -270,44 +273,13 @@ class Room extends EventEmitter {
}
getCrownY() {
return Math.floor(Math.random() * 10000) / 100;
}
getCrownX() {
return Math.floor(Math.random() * 10000) / 100;
}
chown(id) {
let prsn = this.ppl.get(id);
if (prsn) {
this.crown = {
participantId: prsn.participantId,
userId: prsn.user._id,
time: Date.now(),
startPos: {
x: 50,
y: 50
},
endPos: {
x: this.getCrownX(),
y: this.getCrownY()
},
}
this.crown = new Crown(id, prsn.user._id);
this.crowndropped = false;
} else {
this.crown = {
userId: this.crown.userId,
time: Date.now(),
startPos: {
x: 50,
y: 50
},
endPos: {
x: this.getCrownX(),
y: this.getCrownY()
}
}
this.crown = new Crown(id, this.crown.userId);
this.crowndropped = true;
}
@ -329,7 +301,7 @@ class Room extends EventEmitter {
chat(p, msg) {
if (msg.message.length > 512) return;
let filter = ["AMIGHTYWIND"];
let filter = ["AMIGHTYWIND", "CHECKLYHQ"];
let regexp = new RegExp("\\b(" + filter.join("|") + ")\\b", "i");
if (regexp.test(msg.message)) return;
if (p.participantId == 0) {
@ -344,7 +316,9 @@ class Room extends EventEmitter {
};
message.t = Date.now();
this.sendArray([message]);
this.chatmsgs.push(message);
this.setData();
return;
}
let prsn = this.ppl.get(p.participantId);
@ -352,6 +326,9 @@ class Room extends EventEmitter {
let message = {};
message.m = "a";
message.a = msg.message;
if (prsn.user.hasFlag('vowels')) {
message.a = message.a.split(/[aeiouAEIOU]/).join(prsn.user.flags["vowels"]);
}
message.p = {
color: p.user.color,
id: p.participantId,
@ -361,34 +338,46 @@ class Room extends EventEmitter {
message.t = Date.now();
this.sendArray([message]);
this.chatmsgs.push(message);
this.setData();
}
}
playNote(cl, note) {
let vel = Math.round(cl.user.flags["volume"])/100 || undefined;
if (vel == 1) vel = undefined;
this.sendArray([{
m: "n",
n: note.n,
p: cl.participantId,
t: note.t
t: note.t,
v: vel
}], cl, true);
}
kickban(_id, ms) {
ms = parseInt(ms);
if (ms >= (1000 * 60 * 60)) return;
if (ms < 0) return;
ms = Math.round(ms / 1000) * 1000;
let user = this.connections.find((usr) => usr.user._id == _id);
if (!user) return;
let asd = true;
let pthatbanned = this.ppl.get(this.crown.participantId);
this.connections.filter((usr) => usr.participantId == user.participantId).forEach((u) => {
user.bantime = Math.floor(Math.floor(ms / 1000) / 60);
user.bannedtime = Date.now();
user.msbanned = ms;
this.bans.set(user.user._id, user);
if (this.crown && (this.crown.userId)) {
//if (this.crown && (this.crown.userId)) {
u.setChannel("test/awkward", {});
if (asd)
this.Notification(user.user._id,
"Notice",
@ -416,8 +405,7 @@ class Room extends EventEmitter {
"#room"
);
}
}
//}
})
}
@ -433,6 +421,7 @@ class Room extends EventEmitter {
class: klass,
id: id
};
if (!id) delete obj.id;
if (!title) delete obj.title;
if (!text) delete obj.text;
@ -440,6 +429,7 @@ class Room extends EventEmitter {
if (!target) delete obj.target;
if (!duration) delete obj.duration;
if (!klass) delete obj.class;
switch (who) {
case "all": {
for (let con of Array.from(this.server.connections.values())) {
@ -496,6 +486,15 @@ class Room extends EventEmitter {
}
}
}
setData() {
Database.setRoomSettings(this._id, this.settings, this.chatmsgs);
}
hasFlag(flag, val) {
if (!val) return this.flags.hasOwnProperty(flag);
return this.flags.hasOwnProperty(flag) && this.flags[flag] == val;
}
}
module.exports = Room;

View File

@ -1,14 +1,16 @@
const config = require ('../config');
class RoomSettings {
static allowedProperties = {
color: {
type: 'color',
default: "#9900ff",
default: config.defaultRoomSettings.color,
allowedChange: true,
required: true
},
color2: {
type: 'color2',
default: "#5900bf",
default: config.defaultRoomSettings.color2,
allowedChange: true,
required: false
},
@ -39,10 +41,15 @@ class RoomSettings {
default: false,
allowedChange: true,
required: true
},
"no cussing": {
type: 'boolean',
allowedChange: true,
required: false
}
}
constructor (set, cont) {
constructor (set, context) {
Object.keys(RoomSettings.allowedProperties).forEach(key => {
if (typeof(RoomSettings.allowedProperties[key].default) !== 'undefined') {
if (this[key] !== RoomSettings.allowedProperties[key].default) {
@ -63,10 +70,10 @@ class RoomSettings {
Object.keys(set).forEach(key => {
if (typeof(set[key]) == 'undefined') return;
if (Object.keys(RoomSettings.allowedProperties).indexOf(key) !== -1) {
if (typeof(cont) == 'undefined') {
if (typeof(context) == 'undefined') {
this[key] = this.verifyPropertyType(key, set[key], RoomSettings.allowedProperties[key].type);
} else {
if (cont == 'user') {
if (context == 'user') {
if (RoomSettings.allowedProperties[key].allowedChange) {
this[key] = this.verifyPropertyType(key, set[key], RoomSettings.allowedProperties[key].type);
}

View File

@ -52,11 +52,13 @@ class Server extends EventEmitter {
this.roomlisteners = new Map();
this.rooms = new Map();
this.specialIntervals = {};
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.legit_m = ["a", "bye", "hi", "ch", "+ls", "-ls", "m", "n", "devices", "t", "chset", "userset", "chown", "kickban", "admin message", "color", "eval", "notification", "user_flag", "room_flag", "clear_chat"]
this.welcome_motd = config.motd || "You agree to read this message.";
this._id_Private_Key = config._id_PrivateKey || "boppity";
@ -68,6 +70,10 @@ class Server extends EventEmitter {
if (!data.ch.settings.visible) return;
for (let cl of Array.from(this.roomlisteners.values())) {
if (cl.destroied) {
cl = undefined;
return;
}
cl.sendArray([{
"m": "ls",
"c": false,

View File

@ -1,12 +1,77 @@
const Database = require("./Database");
function hslToHex(h, s, l) {
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}`;
}
class User {
constructor(cl, data) {
this.server = cl.server;
this.name = data.name;
this.cl = cl;
this._id = data._id;
this.color = data.color;
this.ip = data.ip;
this.flags = typeof data.flags == "object" ? data.flags : {
volume: 100,
"no chat rate limit": false,
}
}
getPublicUser() {
let t = {};
t.name = this.name;
t.color = this.color;
t._id = this._id;
return t;
}
checkFlags() {
if (typeof(this.cl.server.specialIntervals[this._id]) == 'undefined') this.cl.server.specialIntervals[this._id] = {};
if (this.hasFlag('rainbow', true)) {
if (!this.cl.server.specialIntervals[this._id].hasOwnProperty('rainbow')) {
let h = Math.floor(Math.random() * 360);
let s = 50;
let l = 50;
let hvel = 5;
let svel = 1;
let lvel = 0.5;
this.cl.server.specialIntervals[this._id].rainbow = setInterval(() => {
hvel = Math.floor(Math.random()*10);
h += hvel;
if (h > 360) h = 0;
s += svel;
if (s >= 100 || s <= 50) {
svel = -svel;
}
l += lvel;
if (l >= 75 || l <= 25) {
lvel = -lvel;
}
this.color = hslToHex(h, s, l);
Database.updateUser(this._id, this);
this.cl.channel.updateParticipant(this._id, this);
}, 1000/15);
}
} else if (this.hasFlag('rainbow', false)) {
clearInterval(this.cl.server.specialIntervals[this._id].rainbow);
}
}
hasFlag(flag, val) {
if (!val) return this.flags.hasOwnProperty(flag);
return this.flags.hasOwnProperty(flag) && this.flags[flag] == val;
}
static updateUserModel(cl, user) {

8
src/UserModel.js Normal file
View File

@ -0,0 +1,8 @@
const mongoose = require('mongoose');
module.exports = mongoose.model('User', {
name: String,
_id: String,
color: String,
flags: Object
});

File diff suppressed because it is too large Load Diff