forked from Hri7566/mpp-server-dev2
Merge branch 'master' of gitlab.com:Hri7566/mpp-server into dev
This commit is contained in:
commit
be121478b3
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "mpp.hri7566.info"]
|
||||||
|
path = mpp.hri7566.info
|
||||||
|
url = git@gitlab.com:Hri7566/mpp.hri7566.info.git
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = Object.seal({
|
module.exports = Object.seal({
|
||||||
port: 8443,
|
port: 8443,
|
||||||
motd: "big th0nk",
|
motd: "humongous clement",
|
||||||
_id_PrivateKey: process.env.SALT,
|
_id_PrivateKey: process.env.SALT,
|
||||||
|
|
||||||
// defaultLobbyColor: "#19b4b9",
|
// defaultLobbyColor: "#19b4b9",
|
||||||
|
@ -13,6 +13,7 @@ module.exports = Object.seal({
|
||||||
defaultUsername: "Anonymous",
|
defaultUsername: "Anonymous",
|
||||||
adminpass: process.env.ADMINPASS,
|
adminpass: process.env.ADMINPASS,
|
||||||
ssl: process.env.SSL,
|
ssl: process.env.SSL,
|
||||||
|
serveFiles: true,
|
||||||
defaultRoomSettings: {
|
defaultRoomSettings: {
|
||||||
// color: "#3b5054",
|
// color: "#3b5054",
|
||||||
// color2: "#001014",
|
// color2: "#001014",
|
||||||
|
|
3
index.js
3
index.js
|
@ -21,7 +21,8 @@ global.isObj = function(a) {
|
||||||
|
|
||||||
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);
|
Server.start(config);
|
||||||
|
global.SERVER = Server;
|
||||||
|
|
||||||
// doesn't work with pm2
|
// doesn't work with pm2
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ class Bot extends EventEmitter {
|
||||||
|
|
||||||
this.on('online', () => {
|
this.on('online', () => {
|
||||||
this.logger.log('Connected');
|
this.logger.log('Connected');
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatBufferCycle() {
|
async chatBufferCycle() {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -28,6 +28,7 @@
|
||||||
"@types/jest": "^28.1.3",
|
"@types/jest": "^28.1.3",
|
||||||
"asyncconsole": "^1.3.9",
|
"asyncconsole": "^1.3.9",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
|
"date-holidays": "^3.16.4",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"events": "^3.1.0",
|
"events": "^3.1.0",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
|
@ -36,8 +37,8 @@
|
||||||
"level": "^7.0.0",
|
"level": "^7.0.0",
|
||||||
"mongoose": "^5.12.7",
|
"mongoose": "^5.12.7",
|
||||||
"mppclone-client": "^1.0.0",
|
"mppclone-client": "^1.0.0",
|
||||||
"node-json-color-stringify": "^1.1.0",
|
|
||||||
"nodemon": "^2.0.15",
|
"nodemon": "^2.0.15",
|
||||||
|
"unique-names-generator": "^4.7.1",
|
||||||
"ws": "^7.2.3"
|
"ws": "^7.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
295
src/Channel.js
295
src/Channel.js
|
@ -7,9 +7,29 @@ const RoomSettings = require('./RoomSettings.js');
|
||||||
const ftc = require('fancy-text-converter');
|
const ftc = require('fancy-text-converter');
|
||||||
const Notification = require('./Notification');
|
const Notification = require('./Notification');
|
||||||
const Color = require('./Color');
|
const Color = require('./Color');
|
||||||
|
const { getTimeColor } = require('./ColorEncoder.js');
|
||||||
|
const { InternalBot } = require('./InternalBot');
|
||||||
|
|
||||||
|
function ansiRegex({onlyFirst = false} = {}) {
|
||||||
|
const pattern = [
|
||||||
|
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||||
|
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
return new RegExp(pattern, onlyFirst ? undefined : 'g');
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOGGER_PARTICIPANT = {
|
||||||
|
name: 'Logger',
|
||||||
|
color: '#72f1b8',
|
||||||
|
_id: 'logger',
|
||||||
|
id: 'logger'
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOGGING_CHANNEL = 'lolwutsecretloggingchannel';
|
||||||
|
|
||||||
class Channel extends EventEmitter {
|
class Channel extends EventEmitter {
|
||||||
constructor(server, _id, settings) {
|
constructor(server, _id, settings, cl) {
|
||||||
super();
|
super();
|
||||||
this.logger = new Logger(`Room - ${_id}`);
|
this.logger = new Logger(`Room - ${_id}`);
|
||||||
this._id = _id;
|
this._id = _id;
|
||||||
|
@ -37,23 +57,62 @@ class Channel extends EventEmitter {
|
||||||
|
|
||||||
this.logger.log('Created');
|
this.logger.log('Created');
|
||||||
|
|
||||||
|
if (this._id == LOGGING_CHANNEL) {
|
||||||
|
if (cl.user.hasFlag('admin')) {
|
||||||
|
delete this.crown;
|
||||||
|
|
||||||
|
Logger.buffer.forEach(str => {
|
||||||
|
this.chatmsgs.push({
|
||||||
|
m: 'a',
|
||||||
|
p: LOGGER_PARTICIPANT,
|
||||||
|
a: str.replace(ansiRegex(), '')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Logger.on('buffer update', (str) => {
|
||||||
|
this.chatmsgs.push({
|
||||||
|
m: 'a',
|
||||||
|
p: LOGGER_PARTICIPANT,
|
||||||
|
a: str.replace(ansiRegex(), '')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sendChatArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emit('update');
|
||||||
|
let c = new Color(LOGGER_PARTICIPANT.color);
|
||||||
|
c.add(-0x40, -0x40, -0x40);
|
||||||
|
this.settings = RoomSettings.changeSettings({
|
||||||
|
color: c.toHexa(),
|
||||||
|
chat: true,
|
||||||
|
crownsolo: true,
|
||||||
|
lobby: false,
|
||||||
|
owner_id: LOGGER_PARTICIPANT._id
|
||||||
|
}, true);
|
||||||
|
} else {
|
||||||
|
cl.setChannel('test/awkward');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Database.getRoomSettings(this._id, (err, set) => {
|
Database.getRoomSettings(this._id, (err, set) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.settings = set.settings;
|
this.settings = RoomSettings.changeSettings(this.settings, true);
|
||||||
this.chatmsgs = set.chat;
|
this.chatmsgs = set.chat;
|
||||||
this.connections.forEach(cl => {
|
this.sendChatArray();
|
||||||
cl.sendArray([{
|
|
||||||
m: 'c',
|
|
||||||
c: this.chatmsgs.slice(-1 * 32)
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
this.setData();
|
this.setData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isLobby(this._id)) {
|
||||||
|
this.colorInterval = setInterval(() => {
|
||||||
|
this.setDefaultLobbyColorBasedOnDate();
|
||||||
|
}, 1000 * 60 * 5);
|
||||||
|
this.setDefaultLobbyColorBasedOnDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setChatArray(arr) {
|
setChatArray(arr) {
|
||||||
this.chatmsgs = arr || [];
|
this.chatmsgs = arr || [];
|
||||||
this.sendArray([{
|
this.sendArray([{
|
||||||
|
@ -63,17 +122,45 @@ class Channel extends EventEmitter {
|
||||||
this.setData();
|
this.setData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendChatArray() {
|
||||||
|
this.connections.forEach(cl => {
|
||||||
|
cl.sendArray([{
|
||||||
|
m: 'c',
|
||||||
|
c: this.chatmsgs.slice(-1 * 32)
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultLobbyColorBasedOnDate() {
|
||||||
|
let col = getTimeColor();
|
||||||
|
let col2 = new Color(col.r - 0x40, col.g - 0x40, col.b - 0x40);
|
||||||
|
|
||||||
|
this.settings.color = col.toHexa();
|
||||||
|
this.settings.color2 = col.toHexa();
|
||||||
|
|
||||||
|
for (let key in this.settings) {
|
||||||
|
this.server.lobbySettings[key] = this.settings[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('update');
|
||||||
|
}
|
||||||
|
|
||||||
join(cl, set) { //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) {
|
||||||
|
// we don't exist yet
|
||||||
|
// create id hash
|
||||||
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);
|
||||||
|
|
||||||
|
// set id
|
||||||
cl.user.id = participantId;
|
cl.user.id = participantId;
|
||||||
cl.participantId = participantId;
|
cl.participantId = participantId;
|
||||||
|
|
||||||
|
// init quotas (TODO pass type of room in?)
|
||||||
cl.initParticipantQuotas();
|
cl.initParticipantQuotas();
|
||||||
|
|
||||||
// if there are no users or the user with the crown entered the room, crown the user
|
// no users / already had crown? give 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)) {
|
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 || this.settings['owner_id'] == cl.user._id)) {
|
||||||
// user owns the room
|
// user owns the room
|
||||||
// we need to switch the crown to them
|
// we need to switch the crown to them
|
||||||
//cl.quotas.a.setParams(Quota.PARAMS_A_CROWNED);
|
//cl.quotas.a.setParams(Quota.PARAMS_A_CROWNED);
|
||||||
|
@ -85,7 +172,8 @@ class Channel extends EventEmitter {
|
||||||
//cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL);
|
//cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL);
|
||||||
|
|
||||||
if (this.isLobby(this._id) && this.settings.lobby !== true) {
|
if (this.isLobby(this._id) && this.settings.lobby !== true) {
|
||||||
this.settings.changeSettings(this.server.lobbySettings, 'user');
|
// fix lobby setting
|
||||||
|
this.settings.changeSettings({lobby: true});
|
||||||
// this.settings.visible = true;
|
// this.settings.visible = true;
|
||||||
// this.settings.crownsolo = false;
|
// this.settings.crownsolo = false;
|
||||||
// this.settings.lobby = true;
|
// this.settings.lobby = true;
|
||||||
|
@ -110,21 +198,25 @@ class Channel extends EventEmitter {
|
||||||
|
|
||||||
this.connections.push(cl);
|
this.connections.push(cl);
|
||||||
|
|
||||||
if (cl.hidden !== true) {
|
|
||||||
this.sendArray([{
|
|
||||||
color: this.ppl.get(cl.participantId).user.color,
|
|
||||||
id: this.ppl.get(cl.participantId).participantId,
|
|
||||||
m: "p",
|
|
||||||
name: this.ppl.get(cl.participantId).user.name,
|
|
||||||
x: this.ppl.get(cl.participantId).x || 200,
|
|
||||||
y: this.ppl.get(cl.participantId).y || 100,
|
|
||||||
_id: cl.user._id
|
|
||||||
}], cl, false)
|
|
||||||
}
|
|
||||||
cl.sendArray([{
|
cl.sendArray([{
|
||||||
m: "c",
|
m: "c",
|
||||||
c: this.chatmsgs.slice(-1 * 32)
|
c: this.chatmsgs.slice(-1 * 32)
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
// this.updateCh(cl, this.settings);
|
||||||
|
|
||||||
|
if (!cl.user.hasFlag("hidden", true)) {
|
||||||
|
this.sendArray([{
|
||||||
|
m: 'p',
|
||||||
|
_id: cl.user._id,
|
||||||
|
name: cl.user.name,
|
||||||
|
color: cl.user.color,
|
||||||
|
id: cl.participantId,
|
||||||
|
x: this.ppl.get(cl.participantId).x || 200,
|
||||||
|
y: this.ppl.get(cl.participantId).y || 100
|
||||||
|
}], cl, false);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateCh(cl, this.settings);
|
this.updateCh(cl, this.settings);
|
||||||
} else {
|
} else {
|
||||||
cl.user.id = otheruser.participantId;
|
cl.user.id = otheruser.participantId;
|
||||||
|
@ -190,11 +282,11 @@ class Channel extends EventEmitter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCh(cl) { //update channel for all people in channel
|
updateCh(cl, set) { //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();
|
||||||
}, 1000);
|
}, 13000);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connections.forEach((usr) => {
|
this.connections.forEach((usr) => {
|
||||||
|
@ -240,9 +332,10 @@ class Channel extends EventEmitter {
|
||||||
destroy() { //destroy room
|
destroy() { //destroy room
|
||||||
if (this.destroyed) return;
|
if (this.destroyed) return;
|
||||||
if (this.ppl.size > 0) return;
|
if (this.ppl.size > 0) return;
|
||||||
|
if (this._id == "lobby") return;
|
||||||
this.destroyed = true;
|
this.destroyed = true;
|
||||||
this._id;
|
this._id;
|
||||||
console.log(`Deleted room ${this._id}`);
|
this.logger.log(`Deleted room ${this._id}`);
|
||||||
this.settings = undefined;
|
this.settings = undefined;
|
||||||
this.ppl;
|
this.ppl;
|
||||||
this.connnections;
|
this.connnections;
|
||||||
|
@ -270,6 +363,7 @@ class Channel extends EventEmitter {
|
||||||
[...this.ppl.values()].forEach(c => {
|
[...this.ppl.values()].forEach(c => {
|
||||||
if (cl) {
|
if (cl) {
|
||||||
if (c.hidden == true && c.user._id !== cl.user._id) {
|
if (c.hidden == true && c.user._id !== cl.user._id) {
|
||||||
|
// client is hidden and we are that client
|
||||||
return;
|
return;
|
||||||
} else if (c.user._id == cl.user._id) {
|
} else if (c.user._id == cl.user._id) {
|
||||||
// let u = {
|
// let u = {
|
||||||
|
@ -364,9 +458,11 @@ class Channel extends EventEmitter {
|
||||||
this.crown = new Crown(id, prsn.user._id);
|
this.crown = new Crown(id, prsn.user._id);
|
||||||
this.crowndropped = false;
|
this.crowndropped = false;
|
||||||
} else {
|
} else {
|
||||||
|
if (this.crown) {
|
||||||
this.crown = new Crown(id, this.crown.userId);
|
this.crown = new Crown(id, this.crown.userId);
|
||||||
this.crowndropped = true;
|
this.crowndropped = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.updateCh();
|
this.updateCh();
|
||||||
}
|
}
|
||||||
|
@ -396,7 +492,7 @@ class Channel extends EventEmitter {
|
||||||
|
|
||||||
message.m = "a";
|
message.m = "a";
|
||||||
message.t = Date.now();
|
message.t = Date.now();
|
||||||
message.a = msg.message.test;
|
message.a = msg.message;
|
||||||
|
|
||||||
message.p = {
|
message.p = {
|
||||||
color: "#ffffff",
|
color: "#ffffff",
|
||||||
|
@ -430,77 +526,14 @@ class Channel extends EventEmitter {
|
||||||
name: p.user.name,
|
name: p.user.name,
|
||||||
_id: p.user._id
|
_id: p.user._id
|
||||||
};
|
};
|
||||||
|
|
||||||
message.t = Date.now();
|
message.t = Date.now();
|
||||||
|
|
||||||
this.sendArray([message]);
|
this.sendArray([message]);
|
||||||
this.chatmsgs.push(message);
|
this.chatmsgs.push(message);
|
||||||
this.setData();
|
this.setData();
|
||||||
|
|
||||||
let isAdmin = false;
|
InternalBot.emit('receive message', message, prsn, this);
|
||||||
if (prsn.user.hasFlag('admin')) {
|
|
||||||
isAdmin = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let args = message.a.split(' ');
|
|
||||||
let cmd = args[0].toLowerCase();
|
|
||||||
let argcat = message.a.substring(args[0].length).trim();
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case "!ping":
|
|
||||||
this.adminChat("pong");
|
|
||||||
break;
|
|
||||||
case "!setcolor":
|
|
||||||
if (!isAdmin) {
|
|
||||||
this.adminChat("You do not have permission to use this command.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let color = this.verifyColor(args[1]);
|
|
||||||
if (color) {
|
|
||||||
let c = new Color(color);
|
|
||||||
if (!args[2]) {
|
|
||||||
p.emit("color", {
|
|
||||||
color: c.toHexa(),
|
|
||||||
_id: p.user._id
|
|
||||||
}, true);
|
|
||||||
this.adminChat(`Your color is now ${c.getName().replace('A', 'a')} [${c.toHexa()}]`);
|
|
||||||
} else {
|
|
||||||
let winner = this.server.getAllClientsByUserID(args[2])[0];
|
|
||||||
if (winner) {
|
|
||||||
p.emit("color", {
|
|
||||||
color: c.toHexa(),
|
|
||||||
_id: winner.user._id
|
|
||||||
}, true);
|
|
||||||
this.adminChat(`Friend ${winner.user.name}'s color is now ${c.getName().replace('A', 'a')}.`);
|
|
||||||
} else {
|
|
||||||
this.adminChat("The friend you are looking for (" + args[2] + ") is not around.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.adminChat("Invalid color.");
|
|
||||||
}
|
|
||||||
this.updateCh();
|
|
||||||
break;
|
|
||||||
case "!users":
|
|
||||||
this.adminChat(`There are ${this.server.connections.size} users online.`);
|
|
||||||
break;
|
|
||||||
case "!chown":
|
|
||||||
if (!isAdmin) return;
|
|
||||||
let id = p.participantId;
|
|
||||||
if (args[1]) {
|
|
||||||
id = args[1];
|
|
||||||
}
|
|
||||||
if (this.hasUser(id)) {
|
|
||||||
this.chown(id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "!chlist":
|
|
||||||
case "!channellist":
|
|
||||||
if (!isAdmin) return;
|
|
||||||
this.adminChat("Channels:");
|
|
||||||
for (let ch of this.server.rooms) {
|
|
||||||
this.adminChat(`- ${ch._id}`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adminChat(str) {
|
adminChat(str) {
|
||||||
|
@ -516,11 +549,32 @@ class Channel extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
playNote(cl, note) {
|
playNote(cl, note) {
|
||||||
let vel = Math.round(cl.user.flags["volume"])/100 || undefined;
|
if (cl.user.hasFlag('mute', true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (vel) {
|
if (cl.user.hasFlag('mute')) {
|
||||||
|
if (Array.isArray(cl.user.flags['mute'])) {
|
||||||
|
if (cl.user.flags['mute'].includes(this._id)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let vol;
|
||||||
|
|
||||||
|
if (cl.user.hasFlag('volume')) {
|
||||||
|
vol = Math.round(cl.user.flags["volume"]) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof vol == 'number') {
|
||||||
for (let no of note.n) {
|
for (let no of note.n) {
|
||||||
no.v /= vel;
|
if (no.v) {
|
||||||
|
if (vol == 0) {
|
||||||
|
no.v = vol;
|
||||||
|
} else {
|
||||||
|
no.v *= vol;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,6 +641,20 @@ class Channel extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unban(_id) {
|
||||||
|
this.connections.filter((usr) => usr.participantId == user.participantId).forEach(u => {
|
||||||
|
if (user.bantime) {
|
||||||
|
delete user.bantime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.bannedtime) {
|
||||||
|
delete user.bannedtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bans.delete(user.user._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Notification(who, title, text, html, duration, target, klass, id) {
|
Notification(who, title, text, html, duration, target, klass, id) {
|
||||||
new Notification({
|
new Notification({
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -604,20 +672,20 @@ class Channel extends EventEmitter {
|
||||||
bindEventListeners() {
|
bindEventListeners() {
|
||||||
this.on("bye", participant => {
|
this.on("bye", participant => {
|
||||||
this.remove(participant);
|
this.remove(participant);
|
||||||
})
|
});
|
||||||
|
|
||||||
this.on("m", msg => {
|
this.on("m", msg => {
|
||||||
let p = this.ppl.get(msg.p);
|
let p = this.ppl.get(msg.p);
|
||||||
if (!p) return;
|
if (!p) return;
|
||||||
this.setCoords(p, msg.x, msg.y);
|
this.setCoords(p, msg.x, msg.y);
|
||||||
})
|
});
|
||||||
|
|
||||||
this.on("a", (participant, msg) => {
|
this.on("a", (participant, msg) => {
|
||||||
this.chat(participant, msg);
|
this.chat(participant, msg);
|
||||||
})
|
});
|
||||||
|
|
||||||
this.on("update", (cl) => {
|
this.on("update", (cl, set) => {
|
||||||
this.updateCh(cl);
|
this.updateCh(cl, set);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on("remove crown", () => {
|
this.on("remove crown", () => {
|
||||||
|
@ -637,6 +705,14 @@ class Channel extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.on("flag among us", amongus => {
|
||||||
|
if (amongus) {
|
||||||
|
this.startAmongUs();
|
||||||
|
} else {
|
||||||
|
this.stopAmongUs();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
verifySet(_id, msg) {
|
verifySet(_id, msg) {
|
||||||
|
@ -669,6 +745,25 @@ class Channel extends EventEmitter {
|
||||||
if (!val) return this.flags.hasOwnProperty(flag);
|
if (!val) return this.flags.hasOwnProperty(flag);
|
||||||
return this.flags.hasOwnProperty(flag) && this.flags[flag] == val;
|
return this.flags.hasOwnProperty(flag) && this.flags[flag] == val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async startAmongUs() {
|
||||||
|
if (!this.amongus) {
|
||||||
|
this.amongus = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.amongus.started) return;
|
||||||
|
|
||||||
|
if (!this.amongus.started) {
|
||||||
|
this.amongus.started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let imposter = this.connections[Math.floor(Math.random() * this.connections.length)];
|
||||||
|
imposter.user.setFlag("freeze_name", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAmongUs() {
|
||||||
|
this.amongus.started = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Channel;
|
module.exports = Channel;
|
||||||
|
|
110
src/Client.js
110
src/Client.js
|
@ -1,13 +1,18 @@
|
||||||
const Channel = require("./Channel.js");
|
const Channel = require("./Channel.js");
|
||||||
const Quota = require ("./Quota.js");
|
const Quota = require ("./Quota.js");
|
||||||
const quotas = require('../Quotas');
|
const quotas = require('../Quotas');
|
||||||
const RateLimit = require('./Ratelimit.js').RateLimit;
|
const { RateLimit, RateLimitChain } = require('./Ratelimit.js');
|
||||||
const RateLimitChain = require('./Ratelimit.js').RateLimitChain;
|
|
||||||
const User = require("./User.js");
|
const User = require("./User.js");
|
||||||
const Database = require("./Database.js");
|
const Database = require("./Database.js");
|
||||||
require('node-json-color-stringify');
|
const { EventEmitter } = require('events');
|
||||||
|
|
||||||
class Client extends EventEmitter {
|
class Client extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* Server-side client representation
|
||||||
|
* @param {*} ws WebSocket object
|
||||||
|
* @param {*} req WebSocket request
|
||||||
|
* @param {*} server Server
|
||||||
|
*/
|
||||||
constructor(ws, req, server) {
|
constructor(ws, req, server) {
|
||||||
super();
|
super();
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
@ -36,14 +41,28 @@ class Client extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user is connected
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
isConnected() {
|
isConnected() {
|
||||||
return this.ws && this.ws.readyState === WebSocket.OPEN;
|
return this.ws && this.ws.readyState === WebSocket.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user is connecting
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
isConnecting() {
|
isConnecting() {
|
||||||
return this.ws && this.ws.readyState === WebSocket.CONNECTING;
|
return this.ws && this.ws.readyState === WebSocket.CONNECTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move user to channel
|
||||||
|
* @param {string} _id User ID
|
||||||
|
* @param {*} settings Settings object
|
||||||
|
* @returns undefined
|
||||||
|
*/
|
||||||
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)) {
|
||||||
|
@ -75,7 +94,7 @@ class Client extends EventEmitter {
|
||||||
this.channel = this.server.rooms.get(_id);
|
this.channel = this.server.rooms.get(_id);
|
||||||
this.channel.join(this);
|
this.channel.join(this);
|
||||||
} else {
|
} else {
|
||||||
let room = new Channel(this.server, _id, settings);
|
let room = new Channel(this.server, _id, settings, this);
|
||||||
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);
|
||||||
|
@ -83,6 +102,10 @@ class Client extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data to client
|
||||||
|
* @param {any[]} arr Array of messages
|
||||||
|
*/
|
||||||
sendArray(arr) {
|
sendArray(arr) {
|
||||||
if (this.isConnected()) {
|
if (this.isConnected()) {
|
||||||
//console.log(`SEND: `, JSON.colorStringify(arr));
|
//console.log(`SEND: `, JSON.colorStringify(arr));
|
||||||
|
@ -90,6 +113,36 @@ class Client extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set username in database
|
||||||
|
* @param {string} name Username
|
||||||
|
* @param {boolean} admin Is admin?
|
||||||
|
* @returns undefined
|
||||||
|
*/
|
||||||
|
userset(name, admin) {
|
||||||
|
if (name.length > 40 && !admin) return;
|
||||||
|
if (this.quotas.userset) {
|
||||||
|
if (!this.quotas.userset.attempt()) return;
|
||||||
|
}
|
||||||
|
if (!this.user.hasFlag('freeze_name', true) || admin) {
|
||||||
|
this.user.name = name;
|
||||||
|
if (!this.user.hasFlag('freeze_name', true)) {
|
||||||
|
Database.getUserData(this, this.server).then((usr) => {
|
||||||
|
Database.updateUser(this.user._id, this.user);
|
||||||
|
|
||||||
|
this.server.rooms.forEach((room) => {
|
||||||
|
room.updateParticipant(this.user._id, {
|
||||||
|
name: name
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set rate limits
|
||||||
|
*/
|
||||||
initParticipantQuotas() {
|
initParticipantQuotas() {
|
||||||
this.quotas = {
|
this.quotas = {
|
||||||
//"chat": new Quota(Quota.PARAMS_A_NORMAL),
|
//"chat": new Quota(Quota.PARAMS_A_NORMAL),
|
||||||
|
@ -109,6 +162,9 @@ class Client extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the client
|
||||||
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
this.user.stopFlagEvents();
|
this.user.stopFlagEvents();
|
||||||
this.ws.close();
|
this.ws.close();
|
||||||
|
@ -124,6 +180,9 @@ class Client extends EventEmitter {
|
||||||
this.destroied = true;
|
this.destroied = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal
|
||||||
|
*/
|
||||||
bindEventListeners() {
|
bindEventListeners() {
|
||||||
this.ws.on("message", (evt, admin) => {
|
this.ws.on("message", (evt, admin) => {
|
||||||
try {
|
try {
|
||||||
|
@ -154,21 +213,62 @@ class Client extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send admin data bus message
|
||||||
|
*/
|
||||||
sendAdminData() {
|
sendAdminData() {
|
||||||
let data = {};
|
let data = {};
|
||||||
data.m = "data";
|
data.m = "data";
|
||||||
|
|
||||||
let channels = [];
|
let channels = [];
|
||||||
this.server.rooms.forEach(ch => {
|
this.server.rooms.forEach(ch => {
|
||||||
channels.push(ch.fetchChannelData());
|
let ppl = [];
|
||||||
|
for (let p of ch.fetchChannelData().ppl) {
|
||||||
|
ppl.push({
|
||||||
|
user: p
|
||||||
|
});
|
||||||
|
}
|
||||||
|
channels.push({
|
||||||
|
participants: ppl
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let users = [];
|
||||||
|
this.server.connections.forEach(cl => {
|
||||||
|
let u = {
|
||||||
|
p: {
|
||||||
|
_id: cl.user._id,
|
||||||
|
name: cl.user.name,
|
||||||
|
color: cl.user.color,
|
||||||
|
flags: cl.user.flags,
|
||||||
|
inventory: cl.user.inventory
|
||||||
|
},
|
||||||
|
id: cl.participantId,
|
||||||
|
}
|
||||||
|
|
||||||
|
users.push(u);
|
||||||
});
|
});
|
||||||
|
|
||||||
data.channelManager = {
|
data.channelManager = {
|
||||||
channels
|
channels
|
||||||
};
|
};
|
||||||
|
|
||||||
|
data.clientManager = {
|
||||||
|
users
|
||||||
|
}
|
||||||
|
|
||||||
this.sendArray([data]);
|
this.sendArray([data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Channel} ch
|
||||||
|
* @param {Client} cl If this is present, only this client's user data will be sent(?)
|
||||||
|
*/
|
||||||
|
sendChannelUpdate(ch, cl) {
|
||||||
|
let msg = ch.fetchChannelData(this, cl);
|
||||||
|
this.sendArray([msg]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Client;
|
module.exports = Client;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const Color = require("./Color");
|
||||||
|
|
||||||
function hashCode(str) { // java String#hashCode
|
function hashCode(str) { // java String#hashCode
|
||||||
var hash = 0;
|
var hash = 0;
|
||||||
for (var i = 0; i < str.length; i++) {
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
@ -13,4 +15,73 @@ function intToRGB(i){
|
||||||
|
|
||||||
return "00000".substring(0, 6 - c.length) + c;
|
return "00000".substring(0, 6 - c.length) + c;
|
||||||
}
|
}
|
||||||
module.exports = {hashCode, intToRGB};
|
|
||||||
|
/**
|
||||||
|
* Converts an HSL color value to RGB. Conversion formula
|
||||||
|
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||||
|
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||||
|
* returns r, g, and b in the set [0, 255].
|
||||||
|
*
|
||||||
|
* @param {number} h The hue
|
||||||
|
* @param {number} s The saturation
|
||||||
|
* @param {number} l The lightness
|
||||||
|
* @return {Array} The RGB representation
|
||||||
|
*/
|
||||||
|
function hslToRgb(h, s, l){
|
||||||
|
var r, g, b;
|
||||||
|
|
||||||
|
if(s == 0){
|
||||||
|
r = g = b = l; // achromatic
|
||||||
|
}else{
|
||||||
|
var hue2rgb = function hue2rgb(p, q, t){
|
||||||
|
if(t < 0) t += 1;
|
||||||
|
if(t > 1) t -= 1;
|
||||||
|
if(t < 1/6) return p + (q - p) * 6 * t;
|
||||||
|
if(t < 1/2) return q;
|
||||||
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||||
|
var p = 2 * l - q;
|
||||||
|
r = hue2rgb(p, q, h + 1/3);
|
||||||
|
g = hue2rgb(p, q, h);
|
||||||
|
b = hue2rgb(p, q, h - 1/3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeColor(currentDate = new Date()) {
|
||||||
|
// get day of year as a number from 1-365
|
||||||
|
let newYearsDay = new Date(currentDate.getFullYear());
|
||||||
|
let differenceInTime = (currentDate - newYearsDay) + ((newYearsDay.getTimezoneOffset() - currentDate.getTimezoneOffset()) * 60 * 1000);
|
||||||
|
let oneDayInMS = 1000 * 60 * 60 * 24;
|
||||||
|
let dayOfYear = Math.ceil(differenceInTime / oneDayInMS);
|
||||||
|
|
||||||
|
// get hour
|
||||||
|
let hours = currentDate.getHours();
|
||||||
|
let seconds = currentDate.getSeconds();
|
||||||
|
|
||||||
|
// get a hue based on time of day and day of year
|
||||||
|
let h = Math.floor((dayOfYear / 365) * 100) / 10000;
|
||||||
|
let s = (hours + 1) / (24 / 3);
|
||||||
|
// let s = 1;
|
||||||
|
let l = 0.25 + Math.floor(((hours / 60)) * 1000) / 1000;
|
||||||
|
|
||||||
|
if (l > 0.5) l = 0.5;
|
||||||
|
if (s > 1) s = 1;
|
||||||
|
|
||||||
|
// convert to rgb
|
||||||
|
let [r, g, b] = hslToRgb(h, s, l);
|
||||||
|
|
||||||
|
let col = new Color(r, g, b);
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
hashCode,
|
||||||
|
intToRGB,
|
||||||
|
getTimeColor,
|
||||||
|
hslToRgb
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
const ung = require('unique-names-generator');
|
||||||
|
|
||||||
|
const ung_config = {
|
||||||
|
dictionaries: [ung.adjectives, ung.colors],
|
||||||
|
separator: ' ',
|
||||||
|
length: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class Cow {
|
||||||
|
static generateRandomName() {
|
||||||
|
return ung.uniqueNamesGenerator(ung_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this['🐄'] = Cow.generateRandomName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Cow
|
||||||
|
}
|
|
@ -12,11 +12,13 @@ var logger = new Logger("Database");
|
||||||
|
|
||||||
mongoose.connect(process.env.MONGO_URL, {
|
mongoose.connect(process.env.MONGO_URL, {
|
||||||
useNewUrlParser: true,
|
useNewUrlParser: true,
|
||||||
useUnifiedTopology: true
|
useUnifiedTopology: true,
|
||||||
|
connectTimeoutMS: 1000
|
||||||
}, err => {
|
}, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return;
|
logger.error("Unable to connect to database service");
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
logger.log("Connected");
|
logger.log("Connected");
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
class Command {
|
||||||
|
constructor(id, args, desc, usage, func, permLevel) {
|
||||||
|
this.id = id;
|
||||||
|
this.args = args || [id];
|
||||||
|
this.desc = desc || 'no description'; // brandon-like words
|
||||||
|
this.usage = usage || 'no usage';
|
||||||
|
this.func = func;
|
||||||
|
this.permLevel = permLevel || 'admin'; // user / admin?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Command
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
const { EventEmitter } = require('events');
|
||||||
|
const { Command } = require('./Command');
|
||||||
|
const Color = require('../Color');
|
||||||
|
|
||||||
|
class InternalBot {
|
||||||
|
static on = EventEmitter.prototype.on;
|
||||||
|
static off = EventEmitter.prototype.off;
|
||||||
|
static emit = EventEmitter.prototype.emit;
|
||||||
|
static once = EventEmitter.prototype.once;
|
||||||
|
|
||||||
|
static prefix = '!';
|
||||||
|
|
||||||
|
static bindEventListeners() {
|
||||||
|
if (this.alreadyBound) return;
|
||||||
|
this.alreadyBound = true;
|
||||||
|
|
||||||
|
this.on('receive message', (msg, cl, ch) => {
|
||||||
|
/**
|
||||||
|
* msg.a - chat message
|
||||||
|
* msg.p - participant
|
||||||
|
* msg.t - timestamp
|
||||||
|
*/
|
||||||
|
|
||||||
|
let isAdmin = false;
|
||||||
|
if (cl.user.hasFlag('admin')) {
|
||||||
|
isAdmin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = msg.a.split(' ');
|
||||||
|
let cmd = args[0].toLowerCase().substring(this.prefix.length);
|
||||||
|
let argcat = msg.a.substring(args[0].length).trim();
|
||||||
|
let p = cl;
|
||||||
|
|
||||||
|
if (!args[0].startsWith(this.prefix)) return;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case "ping":
|
||||||
|
ch.adminChat('pong');
|
||||||
|
break;
|
||||||
|
case "setcolor":
|
||||||
|
case "color":
|
||||||
|
if (!isAdmin) {
|
||||||
|
ch.adminChat("You do not have permission to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let color = ch.verifyColor(args[1]);
|
||||||
|
if (color) {
|
||||||
|
let c = new Color(color);
|
||||||
|
if (!args[2]) {
|
||||||
|
p.emit("color", {
|
||||||
|
color: c.toHexa(),
|
||||||
|
_id: p.user._id
|
||||||
|
}, true);
|
||||||
|
ch.adminChat(`Your color is now ${c.getName().replace('A', 'a')} [${c.toHexa()}]`);
|
||||||
|
} else {
|
||||||
|
let winner = ch.server.getAllClientsByUserID(args[2])[0];
|
||||||
|
if (winner) {
|
||||||
|
p.emit("color", {
|
||||||
|
color: c.toHexa(),
|
||||||
|
_id: winner.user._id
|
||||||
|
}, true);
|
||||||
|
ch.adminChat(`Friend ${winner.user.name}'s color is now ${c.getName().replace('A', 'a')}.`);
|
||||||
|
} else {
|
||||||
|
ch.adminChat("The friend you are looking for (" + args[2] + ") is not around.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ch.adminChat("Invalid color.");
|
||||||
|
}
|
||||||
|
ch.updateCh();
|
||||||
|
break;
|
||||||
|
case "users":
|
||||||
|
ch.adminChat(`There are ${ch.server.connections.size} users online.`);
|
||||||
|
break;
|
||||||
|
case "chown":
|
||||||
|
if (!isAdmin) return;
|
||||||
|
let id = p.participantId;
|
||||||
|
if (args[1]) {
|
||||||
|
id = args[1];
|
||||||
|
}
|
||||||
|
if (ch.hasUser(id)) {
|
||||||
|
ch.chown(id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "chlist":
|
||||||
|
case "channellist":
|
||||||
|
if (!isAdmin) return;
|
||||||
|
ch.adminChat("Channels:");
|
||||||
|
for (let [_id] of ch.server.rooms) {
|
||||||
|
ch.adminChat(`- ${_id}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "restart":
|
||||||
|
if (!isAdmin) return;
|
||||||
|
cl.server.restart();
|
||||||
|
break;
|
||||||
|
case "eval":
|
||||||
|
if (!isAdmin) return;
|
||||||
|
cl.server.ev(argcat);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalBot.bindEventListeners();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
InternalBot
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
const { InternalBot } = require("./InternalBot");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
InternalBot
|
||||||
|
}
|
|
@ -1,28 +1,48 @@
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
|
const { EventEmitter } = require('events');
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
static buffer = [];
|
||||||
|
|
||||||
|
static on = EventEmitter.prototype.on;
|
||||||
|
static off = EventEmitter.prototype.off;
|
||||||
|
static once = EventEmitter.prototype.once;
|
||||||
|
static emit = EventEmitter.prototype.emit;
|
||||||
|
|
||||||
constructor (context) {
|
constructor (context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(args) {
|
log(args) {
|
||||||
console.log(chalk.green(`[`) + chalk.green(`${this.context}`) + chalk.green(`]`), args);
|
let str = chalk.green(`[`) + chalk.green(`${this.context}`) + chalk.green(`]`) + ' ' + args
|
||||||
|
console.log(str);
|
||||||
|
this.buffer(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
warn(args) {
|
warn(args) {
|
||||||
console.warn(chalk.yellow(`[WARN] [`) + chalk.yellow(`${this.context}`) + chalk.yellow(`]`), args);
|
let str = chalk.yellow(`[WARN] [`) + chalk.yellow(`${this.context}`) + chalk.yellow(`]`) + ' ' + args;
|
||||||
|
console.warn(str);
|
||||||
|
this.buffer(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
error(args) {
|
error(args) {
|
||||||
console.error(chalk.red(`[ERR] [`) + chalk.red(`${this.context}`) + chalk.red(`]`), args);
|
let str = chalk.red(`[ERR] [`) + chalk.red(`${this.context}`) + chalk.red(`]`) + ' ' + args;
|
||||||
|
console.error(str);
|
||||||
|
this.buffer(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(args) {
|
debug(args) {
|
||||||
if (process.env.DEBUG_ENABLED) {
|
if (process.env.DEBUG_ENABLED) {
|
||||||
console.log(chalk.blue(`[DEBUG] [`) + chalk.blue(`${this.context}`) + chalk.blue(`]`), args);
|
let str = chalk.blue(`[DEBUG] [`) + chalk.blue(`${this.context}`) + chalk.blue(`]`) + ' ' + args;
|
||||||
|
console.debug(str);
|
||||||
|
this.buffer(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer(str) {
|
||||||
|
Logger.buffer.push(str);
|
||||||
|
Logger.emit('buffer update', str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Logger;
|
module.exports = Logger;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
const Holidays = require('date-holidays');
|
||||||
|
|
||||||
|
let hd = new Holidays();
|
||||||
|
|
||||||
|
hd.init('US');
|
||||||
|
|
||||||
|
class MOTDGenerator {
|
||||||
|
static getDay() {
|
||||||
|
let now = new Date();
|
||||||
|
let start = new Date(now.getFullYear(), 0, 0);
|
||||||
|
let diff = (now - start) + ((start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000);
|
||||||
|
let oneDay = 1000 * 60 * 60 * 24;
|
||||||
|
let day = Math.floor(diff / oneDay);
|
||||||
|
return day;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCurrentMOTD() {
|
||||||
|
let h = hd.isHoliday(Date.now());
|
||||||
|
if (h) {
|
||||||
|
// maybe holiday
|
||||||
|
return `Happy ${h[0].name}`;
|
||||||
|
} else {
|
||||||
|
// no holiday
|
||||||
|
return 'cotton-headed ninnymuggins'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
MOTDGenerator
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ const User = require("./User.js");
|
||||||
const Channel = require("./Channel.js");
|
const Channel = require("./Channel.js");
|
||||||
const RoomSettings = require('./RoomSettings');
|
const RoomSettings = require('./RoomSettings');
|
||||||
const Database = require('./Database');
|
const Database = require('./Database');
|
||||||
|
const { MOTDGenerator } = require('./MOTDGenerator');
|
||||||
|
|
||||||
module.exports = (cl) => {
|
module.exports = (cl) => {
|
||||||
cl.once("hi", (msg, admin) => {
|
cl.once("hi", (msg, admin) => {
|
||||||
|
@ -14,7 +15,7 @@ module.exports = (cl) => {
|
||||||
|
|
||||||
let m = {};
|
let m = {};
|
||||||
m.m = "hi";
|
m.m = "hi";
|
||||||
m.motd = cl.server.welcome_motd;
|
m.motd = MOTDGenerator.getCurrentMOTD();
|
||||||
m.t = Date.now();
|
m.t = Date.now();
|
||||||
m.u = {
|
m.u = {
|
||||||
name: cl.user.name,
|
name: cl.user.name,
|
||||||
|
@ -97,7 +98,9 @@ module.exports = (cl) => {
|
||||||
//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 && !admin) {
|
||||||
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)
|
||||||
|
@ -143,10 +146,11 @@ module.exports = (cl) => {
|
||||||
}
|
}
|
||||||
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = new RoomSettings(cl.channel.settings, 'user');
|
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = new RoomSettings(cl.channel.settings, 'user');
|
||||||
cl.channel.settings.changeSettings(msg.set, admin);
|
cl.channel.settings.changeSettings(msg.set, admin);
|
||||||
cl.channel.updateCh();
|
// cl.channel.updateCh();
|
||||||
|
cl.channel.emit('update');
|
||||||
});
|
});
|
||||||
|
|
||||||
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;
|
||||||
if (typeof(msg.message) !== 'string') return;
|
if (typeof(msg.message) !== 'string') return;
|
||||||
|
@ -200,34 +204,21 @@ module.exports = (cl) => {
|
||||||
cl.server.roomlisteners.delete(cl.connectionid);
|
cl.server.roomlisteners.delete(cl.connectionid);
|
||||||
});
|
});
|
||||||
|
|
||||||
cl.on("userset", msg => {
|
cl.on("userset", (msg, admin) => {
|
||||||
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 = {};
|
||||||
if (msg.set.hasOwnProperty('name') && typeof msg.set.name == "string") {
|
if (msg.set.hasOwnProperty('name') && typeof msg.set.name == "string") {
|
||||||
if (msg.set.name.length > 40) return;
|
cl.userset(msg.set.name, admin);
|
||||||
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();
|
|
||||||
Database.updateUser(cl.user._id, cl.user);
|
|
||||||
cl.server.rooms.forEach((room) => {
|
|
||||||
room.updateParticipant(cl.user._id, {
|
|
||||||
name: msg.set.name
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cl.on('kickban', msg => {
|
cl.on('kickban', msg => {
|
||||||
|
if (!admin) {
|
||||||
if (cl.channel.crown == null) return;
|
if (cl.channel.crown == null) return;
|
||||||
if (!(cl.channel && cl.participantId)) return;
|
if (!(cl.channel && cl.participantId)) return;
|
||||||
if (!cl.channel.crown.userId) return;
|
if (!cl.channel.crown.userId) return;
|
||||||
if (!(cl.user._id == cl.channel.crown.userId)) return;
|
if (!(cl.user._id == cl.channel.crown.userId)) return;
|
||||||
|
}
|
||||||
if (msg.hasOwnProperty('_id') && typeof msg._id == "string") {
|
if (msg.hasOwnProperty('_id') && typeof msg._id == "string") {
|
||||||
if (!cl.quotas.kickban.attempt() && !admin) return;
|
if (!cl.quotas.kickban.attempt() && !admin) return;
|
||||||
let _id = msg._id;
|
let _id = msg._id;
|
||||||
|
@ -236,13 +227,27 @@ module.exports = (cl) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cl.on('unban', (msg, admin) => {
|
||||||
|
if (!admin) {
|
||||||
|
if (cl.channel.crown == null) return;
|
||||||
|
if (!(cl.channel && cl.participantId)) return;
|
||||||
|
if (!cl.channel.crown.userId) return;
|
||||||
|
if (!(cl.user._id == cl.channel.crown.userId)) return;
|
||||||
|
}
|
||||||
|
if (msg.hasOwnProperty('_id') && typeof msg._id == "string") {
|
||||||
|
if (!cl.quotas.kickban.attempt() && !admin) return;
|
||||||
|
let _id = msg._id;
|
||||||
|
cl.channel.unban(_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
cl.on("bye", msg => {
|
cl.on("bye", msg => {
|
||||||
cl.user.stopFlagEvents();
|
cl.user.stopFlagEvents();
|
||||||
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;
|
||||||
|
@ -250,9 +255,11 @@ module.exports = (cl) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
//admin only stuff
|
//admin only stuff
|
||||||
|
// TODO move all admin messages to their own stream
|
||||||
cl.on('color', (msg, admin) => {
|
cl.on('color', (msg, admin) => {
|
||||||
if (!admin) return;
|
if (!admin) return;
|
||||||
if (typeof cl.channel.verifyColor(msg.color) != 'string') return;
|
if (!msg.color) return;
|
||||||
|
// if (typeof cl.channel.verifyColor(msg.color) != 'string') return;
|
||||||
if (!msg.hasOwnProperty('id') && !msg.hasOwnProperty('_id')) return;
|
if (!msg.hasOwnProperty('id') && !msg.hasOwnProperty('_id')) return;
|
||||||
cl.server.connections.forEach(c => {
|
cl.server.connections.forEach(c => {
|
||||||
if (c.destroied) return;
|
if (c.destroied) return;
|
||||||
|
@ -360,6 +367,7 @@ module.exports = (cl) => {
|
||||||
cl.isSubscribedToAdminStream = true;
|
cl.isSubscribedToAdminStream = true;
|
||||||
let interval = 8000;
|
let interval = 8000;
|
||||||
if ('interval_ms' in msg) interval = msg['interval_ms'];
|
if ('interval_ms' in msg) interval = msg['interval_ms'];
|
||||||
|
cl.sendAdminData();
|
||||||
cl.adminStreamInterval = setInterval(() => {
|
cl.adminStreamInterval = setInterval(() => {
|
||||||
if (cl.isSubscribedToAdminStream == true) cl.sendAdminData();
|
if (cl.isSubscribedToAdminStream == true) cl.sendAdminData();
|
||||||
}, interval);
|
}, interval);
|
||||||
|
@ -384,12 +392,33 @@ module.exports = (cl) => {
|
||||||
|
|
||||||
if (!msg.hasOwnProperty('msg')) return;
|
if (!msg.hasOwnProperty('msg')) return;
|
||||||
if (typeof msg.msg != 'object') return;
|
if (typeof msg.msg != 'object') return;
|
||||||
|
if (typeof msg.msg.m != 'string') return;
|
||||||
|
|
||||||
if (!cl.channel) return;
|
if (!cl.channel) return;
|
||||||
if (!msg.hasOwnProperty('_id')) msg._id = cl.channel._id;
|
if (!msg.hasOwnProperty('_id')) msg._id = cl.channel._id;
|
||||||
|
|
||||||
let ch = cl.server.rooms.get(msg._id);
|
let ch = cl.server.rooms.get(msg._id);
|
||||||
if (!ch) return;
|
if (!ch) return;
|
||||||
ch.emit(msg.m, msg);
|
ch.emit(msg.msg.m, msg.msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
cl.on('name', (msg, admin) => {
|
||||||
|
if (!admin) return;
|
||||||
|
|
||||||
|
if (!msg.hasOwnProperty('_id')) return;
|
||||||
|
if (!msg.hasOwnProperty('name')) return;
|
||||||
|
|
||||||
|
for (const [mapID, conn] of cl.server.connections) {
|
||||||
|
if (!conn.user) return;
|
||||||
|
if (conn.user._id == msg._id) {
|
||||||
|
let c = conn;
|
||||||
|
c.userset(msg.name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cl.on('restart', (msg, admin) => {
|
||||||
|
if (!admin) return;
|
||||||
|
cl.server.restart(msg.notification);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,28 +13,27 @@ module.exports = class Notification {
|
||||||
}
|
}
|
||||||
|
|
||||||
send(_id, room) {
|
send(_id, room) {
|
||||||
let obj = {};
|
let msg = {};
|
||||||
Object.assign(obj, this);
|
Object.assign(msg, this);
|
||||||
obj.m = "notification";
|
msg.m = "notification";
|
||||||
|
|
||||||
switch (_id) {
|
switch (_id) {
|
||||||
case "all": {
|
case "all":
|
||||||
for (let con of Array.from(room.server.connections.values())) {
|
for (let con of Array.from(room.server.connections.values())) {
|
||||||
con.sendArray([obj]);
|
con.sendArray([msg]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case "room":
|
||||||
case "room": {
|
case "channel":
|
||||||
for (let con of room.connections) {
|
for (let con of room.connections) {
|
||||||
con.sendArray([obj]);
|
con.sendArray([msg]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
default: {
|
|
||||||
Array.from(room.server.connections.values()).filter((usr) => typeof(usr.user) !== 'undefined' ? usr.user._id == _id : null).forEach((p) => {
|
Array.from(room.server.connections.values()).filter((usr) => typeof(usr.user) !== 'undefined' ? usr.user._id == _id : null).forEach((p) => {
|
||||||
p.sendArray([obj]);
|
p.sendArray([msg]);
|
||||||
});
|
});
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,17 @@ const http = require("http");
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const RoomSettings = require('./RoomSettings');
|
const RoomSettings = require('./RoomSettings');
|
||||||
const Logger = require("./Logger.js");
|
const Logger = require("./Logger.js");
|
||||||
|
const Notification = require('./Notification');
|
||||||
|
|
||||||
class Server extends EventEmitter {
|
class Server {
|
||||||
constructor(config) {
|
static on = EventEmitter.prototype.on;
|
||||||
super();
|
static off = EventEmitter.prototype.off;
|
||||||
EventEmitter.call(this);
|
static emit = EventEmitter.prototype.emit;
|
||||||
|
static once = EventEmitter.prototype.once;
|
||||||
|
|
||||||
|
static start(config) {
|
||||||
|
// super();
|
||||||
|
// EventEmitter.call(this);
|
||||||
|
|
||||||
this.logger = new Logger("Server");
|
this.logger = new Logger("Server");
|
||||||
|
|
||||||
|
@ -24,7 +30,8 @@ class Server extends EventEmitter {
|
||||||
server: this.https_server,
|
server: this.https_server,
|
||||||
backlog: 100,
|
backlog: 100,
|
||||||
verifyClient: (info) => {
|
verifyClient: (info) => {
|
||||||
if (banned.includes((info.req.connection.remoteAddress).replace("::ffff:", ""))) return false;
|
const ip = (info.req.connection.remoteAddress).replace("::ffff:", "");
|
||||||
|
if (banned.includes(ip)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -35,7 +42,8 @@ class Server extends EventEmitter {
|
||||||
port: config.port,
|
port: config.port,
|
||||||
backlog: 100,
|
backlog: 100,
|
||||||
verifyClient: (info) => {
|
verifyClient: (info) => {
|
||||||
if (banned.includes((info.req.connection.remoteAddress).replace("::ffff:", ""))) return false;
|
const ip = (info.req.connection.remoteAddress).replace("::ffff:", "");
|
||||||
|
if (banned.includes(ip)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -88,17 +96,19 @@ class Server extends EventEmitter {
|
||||||
"unsubscribe from admin stream",
|
"unsubscribe from admin stream",
|
||||||
"data",
|
"data",
|
||||||
"channel message",
|
"channel message",
|
||||||
"channel_flag"
|
"channel_flag",
|
||||||
|
"name",
|
||||||
|
"restart"
|
||||||
];
|
];
|
||||||
|
|
||||||
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 || "amogus";
|
this._id_Private_Key = config._id_PrivateKey || "amogus";
|
||||||
|
|
||||||
this.adminpass = config.adminpass || "123123sucks";
|
this.adminpass = config.adminpass || "123123sucks";
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRoom(data) {
|
static 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())) {
|
||||||
|
@ -114,7 +124,7 @@ class Server extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ev(str) {
|
static ev(str) {
|
||||||
let out = "";
|
let out = "";
|
||||||
try {
|
try {
|
||||||
out = eval(str);
|
out = eval(str);
|
||||||
|
@ -124,24 +134,41 @@ class Server extends EventEmitter {
|
||||||
console.log(out);
|
console.log(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient(id) {
|
static getClient(id) {
|
||||||
return this.connections.get(id);
|
return this.connections.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClientByParticipantID(id) {
|
static getClientByParticipantID(id) {
|
||||||
for (let cl of Array.from(this.connections.values())) {
|
for (let cl of Array.from(this.connections.values())) {
|
||||||
if (cl.participantID == id) return cl;
|
if (cl.participantID == id) return cl;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllClientsByUserID(_id) {
|
static getAllClientsByUserID(_id) {
|
||||||
let out = [];
|
let out = [];
|
||||||
for (let cl of Array.from(this.connections.values())) {
|
for (let cl of Array.from(this.connections.values())) {
|
||||||
if (cl.user._id == _id) out.push(cl);
|
if (cl.user._id == _id) out.push(cl);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static restart(notif = {
|
||||||
|
m: "notification",
|
||||||
|
id: "server-restart",
|
||||||
|
title: "Notice",
|
||||||
|
text: "The server will restart in a few moments.",
|
||||||
|
target: "#piano",
|
||||||
|
duration: 20000,
|
||||||
|
class: "classic",
|
||||||
|
}) {
|
||||||
|
let n = new Notification(notif);
|
||||||
|
n.send("all", this.rooms.get('lobby'));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
process.exit();
|
||||||
|
}, n.duration || 20000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Server;
|
module.exports = Server;
|
||||||
|
|
10
src/User.js
10
src/User.js
|
@ -19,8 +19,16 @@ class User {
|
||||||
this.color = data.color;
|
this.color = data.color;
|
||||||
this.flags = typeof data.flags == "object" ? data.flags : {
|
this.flags = typeof data.flags == "object" ? data.flags : {
|
||||||
volume: 100,
|
volume: 100,
|
||||||
"no chat rate limit": false
|
"no chat rate limit": false,
|
||||||
|
freeze_name: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.inventory = {
|
||||||
|
'test': {
|
||||||
|
display_name: 'Test',
|
||||||
|
count: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getPublicUser() {
|
getPublicUser() {
|
||||||
|
|
103
yarn.lock
103
yarn.lock
|
@ -118,11 +118,21 @@ anymatch@~3.1.2:
|
||||||
normalize-path "^3.0.0"
|
normalize-path "^3.0.0"
|
||||||
picomatch "^2.0.4"
|
picomatch "^2.0.4"
|
||||||
|
|
||||||
|
argparse@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||||
|
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||||
|
|
||||||
array-flatten@1.1.1:
|
array-flatten@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||||
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
|
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
|
||||||
|
|
||||||
|
astronomia@^4.1.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/astronomia/-/astronomia-4.1.1.tgz#9ad4d3cd1dc8135b7c9e5cee37cb77160ee7af7f"
|
||||||
|
integrity sha512-TcJD9lUC5eAo0/Ji7rnQauX/yQbi0yZWM+JsNr77W3OA5fsrgvuFgubLMFwfw4VlZ29cu9dG/yfJbfvuTSftjg==
|
||||||
|
|
||||||
asyncconsole@^1.3.9:
|
asyncconsole@^1.3.9:
|
||||||
version "1.3.9"
|
version "1.3.9"
|
||||||
resolved "https://registry.yarnpkg.com/asyncconsole/-/asyncconsole-1.3.9.tgz#f98a46cf86f58b1d08e3782b60c68b8422cbb606"
|
resolved "https://registry.yarnpkg.com/asyncconsole/-/asyncconsole-1.3.9.tgz#f98a46cf86f58b1d08e3782b60c68b8422cbb606"
|
||||||
|
@ -256,6 +266,13 @@ cacheable-request@^6.0.0:
|
||||||
normalize-url "^4.1.0"
|
normalize-url "^4.1.0"
|
||||||
responselike "^1.0.2"
|
responselike "^1.0.2"
|
||||||
|
|
||||||
|
caldate@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/caldate/-/caldate-2.0.3.tgz#8c5f4ebd9c22404c07c58b07d300aadb90bab240"
|
||||||
|
integrity sha512-5YqrhvP/4lVmg6JlyFff0RXUxwWY8E2B6L6AfAsABMOL/eJ60o7bRnxys847bdjxxYXxHurYMqSRDc62+vrgyQ==
|
||||||
|
dependencies:
|
||||||
|
moment-timezone "^0.5.34"
|
||||||
|
|
||||||
call-bind@^1.0.0:
|
call-bind@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||||
|
@ -326,11 +343,6 @@ color-name@~1.1.4:
|
||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
colors@^1.1.2:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
|
||||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
|
@ -380,6 +392,48 @@ crypto-random-string@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
||||||
|
|
||||||
|
date-bengali-revised@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-bengali-revised/-/date-bengali-revised-2.0.2.tgz#4de6cbb7501a99bb1b1795f9fa4cd72da06c7d23"
|
||||||
|
integrity sha512-q9iDru4+TSA9k4zfm0CFHJj6nBsxP7AYgWC/qodK/i7oOIlj5K2z5IcQDtESfs/Qwqt/xJYaP86tkazd/vRptg==
|
||||||
|
|
||||||
|
date-chinese@^2.1.4:
|
||||||
|
version "2.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-chinese/-/date-chinese-2.1.4.tgz#f471af3b72525b6005a16ec4e544a53b28c8e656"
|
||||||
|
integrity sha512-WY+6+Qw92ZGWFvGtStmNQHEYpNa87b8IAQ5T8VKt4wqrn24lBXyyBnWI5jAIyy7h/KVwJZ06bD8l/b7yss82Ww==
|
||||||
|
dependencies:
|
||||||
|
astronomia "^4.1.0"
|
||||||
|
|
||||||
|
date-easter@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-easter/-/date-easter-1.0.2.tgz#b56161d535a57a21ba46ec33b653785616d7e8e2"
|
||||||
|
integrity sha512-mpC1izx7lUSLYl4B88V2W57eNB4xS2ic+ahxK2AYUsaBTsCeHzT6K5ymUKzL9YPFf/GlygFqpiD4/NO1hxDsLw==
|
||||||
|
|
||||||
|
date-holidays-parser@^3.4.1:
|
||||||
|
version "3.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-holidays-parser/-/date-holidays-parser-3.4.1.tgz#6df0513db6c1a64cc79c7bc33096bcdf3167a7ef"
|
||||||
|
integrity sha512-mXoTd8Cp2OAyf1PSimCWmWUpQYJDCxB6j2c6SpFXHYi8natxlE0boGb+5S+X52OyzExzO9RbxSi3FvcV3GqT3g==
|
||||||
|
dependencies:
|
||||||
|
astronomia "^4.1.0"
|
||||||
|
caldate "^2.0.3"
|
||||||
|
date-bengali-revised "^2.0.2"
|
||||||
|
date-chinese "^2.1.4"
|
||||||
|
date-easter "^1.0.2"
|
||||||
|
deepmerge "^4.2.2"
|
||||||
|
jalaali-js "^1.2.6"
|
||||||
|
moment-timezone "^0.5.34"
|
||||||
|
|
||||||
|
date-holidays@^3.16.4:
|
||||||
|
version "3.16.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-holidays/-/date-holidays-3.16.4.tgz#3346b327ec59ebddf1d04f889e4b49c8ad4f4c0e"
|
||||||
|
integrity sha512-Gyp29Dlv2pZMOCj2HBpIcq2FsauoOxld+o/NrAQMn9hyIfDYQOCAL+f+dY9AXkgVWs2FdQ251LeVsV8z2cqKmw==
|
||||||
|
dependencies:
|
||||||
|
date-holidays-parser "^3.4.1"
|
||||||
|
js-yaml "^4.1.0"
|
||||||
|
lodash.omit "^4.5.0"
|
||||||
|
lodash.pick "^4.4.0"
|
||||||
|
prepin "^1.0.3"
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
@ -413,6 +467,11 @@ deep-extend@^0.6.0:
|
||||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
|
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
|
||||||
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
|
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
|
||||||
|
|
||||||
|
deepmerge@^4.2.2:
|
||||||
|
version "4.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||||
|
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||||
|
|
||||||
defer-to-connect@^1.0.1:
|
defer-to-connect@^1.0.1:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
|
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
|
||||||
|
@ -970,6 +1029,16 @@ levelup@^5.1.1:
|
||||||
level-supports "^2.0.1"
|
level-supports "^2.0.1"
|
||||||
queue-microtask "^1.2.3"
|
queue-microtask "^1.2.3"
|
||||||
|
|
||||||
|
lodash.omit@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60"
|
||||||
|
integrity sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==
|
||||||
|
|
||||||
|
lodash.pick@^4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
|
||||||
|
integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
|
||||||
|
|
||||||
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||||
|
@ -1053,6 +1122,18 @@ minimist@^1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||||
|
|
||||||
|
moment-timezone@^0.5.34:
|
||||||
|
version "0.5.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
|
||||||
|
integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
|
||||||
|
dependencies:
|
||||||
|
moment ">= 2.9.0"
|
||||||
|
|
||||||
|
"moment@>= 2.9.0":
|
||||||
|
version "2.29.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||||
|
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||||
|
|
||||||
mongodb@3.7.3:
|
mongodb@3.7.3:
|
||||||
version "3.7.3"
|
version "3.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.7.3.tgz#b7949cfd0adc4cc7d32d3f2034214d4475f175a5"
|
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.7.3.tgz#b7949cfd0adc4cc7d32d3f2034214d4475f175a5"
|
||||||
|
@ -1149,13 +1230,6 @@ node-gyp-build@^4.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4"
|
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4"
|
||||||
integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==
|
integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==
|
||||||
|
|
||||||
node-json-color-stringify@^1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/node-json-color-stringify/-/node-json-color-stringify-1.1.0.tgz#8bb124f913859591058026513121d6609d6ef5b7"
|
|
||||||
integrity sha512-EfCyON2e1RVY9zECodKxwnrE2c858xj1GlueR5w3s5d3sdHALFkyouaii+34Ga3+nt8cF99mtkXau7VTTJaJXg==
|
|
||||||
dependencies:
|
|
||||||
colors "^1.1.2"
|
|
||||||
|
|
||||||
nodemon@^2.0.15:
|
nodemon@^2.0.15:
|
||||||
version "2.0.16"
|
version "2.0.16"
|
||||||
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef"
|
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef"
|
||||||
|
@ -1621,6 +1695,11 @@ undefsafe@^2.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
||||||
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
||||||
|
|
||||||
|
unique-names-generator@^4.7.1:
|
||||||
|
version "4.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unique-names-generator/-/unique-names-generator-4.7.1.tgz#966407b12ba97f618928f77322cfac8c80df5597"
|
||||||
|
integrity sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==
|
||||||
|
|
||||||
unique-string@^2.0.0:
|
unique-string@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
|
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
|
||||||
|
|
Loading…
Reference in New Issue