diff --git a/index.js b/index.js index 09098f7..4507ce5 100644 --- a/index.js +++ b/index.js @@ -21,7 +21,8 @@ global.isObj = function(a) { let Server = require("./src/Server.js"); let config = require('./config'); -global.SERVER = new Server(config); +Server.start(config); +global.SERVER = Server; // doesn't work with pm2 diff --git a/package.json b/package.json index 677de7c..d639528 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Attempt at making a MPP Server.", "main": "index.js", "scripts": { + "start": "node .", "test": "nodemon" }, "repository": { @@ -24,6 +25,7 @@ "dependencies": { "asyncconsole": "^1.3.9", "chalk": "^4.1.1", + "date-holidays": "^3.16.4", "dotenv": "^8.2.0", "events": "^3.1.0", "express": "^4.18.1", diff --git a/src/Channel.js b/src/Channel.js index 4963254..89c00e0 100644 --- a/src/Channel.js +++ b/src/Channel.js @@ -8,6 +8,7 @@ const ftc = require('fancy-text-converter'); const Notification = require('./Notification'); const Color = require('./Color'); const { getTimeColor } = require('./ColorEncoder.js'); +const { InternalBot } = require('./InternalBot'); class Channel extends EventEmitter { constructor(server, _id, settings) { @@ -468,78 +469,14 @@ class Channel extends EventEmitter { name: p.user.name, _id: p.user._id }; + message.t = Date.now(); + this.sendArray([message]); this.chatmsgs.push(message); this.setData(); - let isAdmin = false; - 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": - case "!color": - 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 [_id] of this.server.rooms) { - this.adminChat(`- ${_id}`); - } - break; - } + InternalBot.emit('receive message', message, prsn, this); } adminChat(str) { @@ -711,6 +648,14 @@ class Channel extends EventEmitter { } } }); + + this.on("flag among us", amongus => { + if (amongus) { + this.startAmongUs(); + } else { + this.stopAmongUs(); + } + }); } verifySet(_id, msg) { @@ -743,6 +688,25 @@ class Channel extends EventEmitter { if (!val) return this.flags.hasOwnProperty(flag); 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; diff --git a/src/InternalBot/Command.js b/src/InternalBot/Command.js new file mode 100644 index 0000000..31c7fa4 --- /dev/null +++ b/src/InternalBot/Command.js @@ -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 +} diff --git a/src/InternalBot/InternalBot.js b/src/InternalBot/InternalBot.js new file mode 100644 index 0000000..e0de30f --- /dev/null +++ b/src/InternalBot/InternalBot.js @@ -0,0 +1,98 @@ +const { EventEmitter } = require('events'); +const { Command } = require('./Command'); + +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(); + let argcat = msg.a.substring(args[0].length).trim(); + + 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; + } + }); + } +} + +InternalBot.bindEventListeners(); + +module.exports = { + InternalBot +} diff --git a/src/InternalBot/index.js b/src/InternalBot/index.js new file mode 100644 index 0000000..fc30291 --- /dev/null +++ b/src/InternalBot/index.js @@ -0,0 +1,5 @@ +const { InternalBot } = require("./InternalBot"); + +module.exports = { + InternalBot +} diff --git a/src/MOTDGenerator.js b/src/MOTDGenerator.js new file mode 100644 index 0000000..b3268df --- /dev/null +++ b/src/MOTDGenerator.js @@ -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 +} diff --git a/src/Message.js b/src/Message.js index cc3e667..d79d248 100644 --- a/src/Message.js +++ b/src/Message.js @@ -3,6 +3,7 @@ const User = require("./User.js"); const Channel = require("./Channel.js"); const RoomSettings = require('./RoomSettings'); const Database = require('./Database'); +const { MOTDGenerator } = require('./MOTDGenerator'); module.exports = (cl) => { cl.once("hi", (msg, admin) => { @@ -14,7 +15,7 @@ module.exports = (cl) => { let m = {}; m.m = "hi"; - m.motd = cl.server.welcome_motd; + m.motd = MOTDGenerator.getCurrentMOTD(); m.t = Date.now(); m.u = { name: cl.user.name, @@ -254,6 +255,7 @@ module.exports = (cl) => { }); //admin only stuff + // TODO move all admin messages to their own stream cl.on('color', (msg, admin) => { if (!admin) return; if (typeof cl.channel.verifyColor(msg.color) != 'string') return; diff --git a/src/Server.js b/src/Server.js index eabcfe8..319bb05 100644 --- a/src/Server.js +++ b/src/Server.js @@ -7,10 +7,15 @@ const RoomSettings = require('./RoomSettings'); const Logger = require("./Logger.js"); const Notification = require('./Notification'); -class Server extends EventEmitter { - constructor(config) { - super(); - EventEmitter.call(this); +class Server { + static on = EventEmitter.prototype.on; + static off = EventEmitter.prototype.off; + static emit = EventEmitter.prototype.emit; + static once = EventEmitter.prototype.once; + + static start(config) { + // super(); + // EventEmitter.call(this); this.logger = new Logger("Server"); @@ -38,7 +43,7 @@ class Server extends EventEmitter { backlog: 100, verifyClient: (info) => { const ip = (info.req.connection.remoteAddress).replace("::ffff:", ""); - if (ip) return false; + if (banned.includes(ip)) return false; return true; } }); @@ -96,14 +101,14 @@ class Server extends EventEmitter { "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.adminpass = config.adminpass || "123123sucks"; } - updateRoom(data) { + static updateRoom(data) { if (!data.ch.settings.visible) return; for (let cl of Array.from(this.roomlisteners.values())) { @@ -119,7 +124,7 @@ class Server extends EventEmitter { } } - ev(str) { + static ev(str) { let out = ""; try { out = eval(str); @@ -129,18 +134,18 @@ class Server extends EventEmitter { console.log(out); } - getClient(id) { + static getClient(id) { return this.connections.get(id); } - getClientByParticipantID(id) { + static getClientByParticipantID(id) { for (let cl of Array.from(this.connections.values())) { if (cl.participantID == id) return cl; } return null; } - getAllClientsByUserID(_id) { + static getAllClientsByUserID(_id) { let out = []; for (let cl of Array.from(this.connections.values())) { if (cl.user._id == _id) out.push(cl); @@ -148,7 +153,7 @@ class Server extends EventEmitter { return out; } - restart(notif = { + static restart(notif = { m: "notification", id: "server-restart", title: "Notice", diff --git a/yarn.lock b/yarn.lock index b7c2914..efd444d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -93,11 +93,21 @@ anymatch@~3.1.2: normalize-path "^3.0.0" 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: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 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: version "1.3.9" resolved "https://registry.yarnpkg.com/asyncconsole/-/asyncconsole-1.3.9.tgz#f98a46cf86f58b1d08e3782b60c68b8422cbb606" @@ -231,6 +241,13 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" 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: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -350,6 +367,48 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" 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: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -383,6 +442,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" 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: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -795,6 +859,18 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +jalaali-js@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/jalaali-js/-/jalaali-js-1.2.6.tgz#f4ee4bf686ed32bb9656f101225be4b7a1c3fd21" + integrity sha512-io974va+Qyu+UfuVX3UIAgJlxLhAMx9Y8VMfh+IG00Js7hXQo1qNQuwSiSa0xxco0SVgx5HWNkaiCcV+aZ8WPw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -910,6 +986,16 @@ levelup@^5.1.1: level-supports "^2.0.1" 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: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -993,6 +1079,18 @@ minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" 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: version "3.7.3" resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.7.3.tgz#b7949cfd0adc4cc7d32d3f2034214d4475f175a5" @@ -1188,6 +1286,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== +prepin@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/prepin/-/prepin-1.0.3.tgz#6996d34f1f8bf0de0bacf525e3037da79b7df91b" + integrity sha512-0XL2hreherEEvUy0fiaGEfN/ioXFV+JpImqIzQjxk6iBg4jQ2ARKqvC4+BmRD8w/pnpD+lbxvh0Ub+z7yBEjvA== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"