Add rate limits and note quota

This commit is contained in:
Hri7566 2023-09-11 00:01:49 -04:00
parent 78fb73a25a
commit ef86aaddb0
61 changed files with 346 additions and 22376 deletions

26
config/ratelimits.yml Normal file
View File

@ -0,0 +1,26 @@
user:
normal:
a: 1500
m: 50
chains:
userset:
interval: 1800000,
num: 1000
crown:
normal:
a: 600
m: 50
chains:
userset:
interval: 1800000,
num: 1000
admin:
normal:
a: 120
m: 16.6666666667
chains:
userset:
interval: 1800000,
num: 1000

View File

@ -1,35 +0,0 @@
module.exports = Object.seal({
chat: {
lobby: {
amount: 4,
time: 4000
},
normal: {
amount: 4,
time: 4000
},
insane: {
amount: 10,
time: 4000
}
},
chown: {
amount: 10,
time: 5000
},
userset: {
amount: 30,
time: 30 * 60000
},
room: {
time: 500
},
cursor: {
time: 16,
amount: 10
},
kickban: {
amount: 2,
time: 1000
}
});

View File

@ -1,37 +0,0 @@
[
"68.106.138.210",
"154.28.188.216",
"154.28.188.214",
"154.28.188.217",
"154.28.188.215",
"154.28.188.213",
"154.28.188.212",
"154.28.188.220",
"154.28.188.219",
"154.28.188.218",
"154.28.188.222",
"154.28.188.223",
"154.28.188.221",
"154.28.188.224",
"154.28.188.226",
"154.28.188.225",
"177.54.145.61",
"213.6.141.146",
"103.23.101.30",
"210.245.51.15",
"91.237.161.211",
"213.136.89.190",
"177.137.168.132",
"92.86.33.126",
"138.97.236.2",
"119.243.95.62",
"49.51.74.19",
"31.128.248.1",
"185.220.102.245",
"61.41.9.213",
"103.25.120.138",
"46.8.247.3",
"45.70.84.46",
"46.0.205.175",
"36.94.126.50"
]

View File

@ -1,3 +0,0 @@
const ColorEncoder = require("./src/ColorEncoder");
// console.log(ColorEncoder.getTimeColor(new Date("Dec 30 1970")).toHexa());

View File

@ -1,28 +0,0 @@
module.exports = Object.seal({
port: 8443,
motd: "humongous clement",
_id_PrivateKey: process.env.SALT,
// defaultLobbyColor: "#19b4b9",
// defaultLobbyColor2: "#801014",
defaultLobbyColor: "#76b0db",
defaultLobbyColor2: "#276491",
// defaultLobbyColor: "#9900ff",
// defaultLobbyColor2: "#5900af",
defaultUsername: "Anonymous",
adminpass: process.env.ADMINPASS,
ssl: process.env.SSL,
serveFiles: true,
defaultRoomSettings: {
// color: "#3b5054",
// color2: "#001014",
color: "#480505",
crownsolo: false,
visible: true
},
hostDevFiles: true,
enableMPPCloneBot: false
});

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1656215259556" clover="3.2.0">
<project timestamp="1656215259556" name="All files">
<metrics statements="7" coveredstatements="7" conditionals="0" coveredconditionals="0" methods="2" coveredmethods="2" elements="9" coveredelements="9" complexity="0" loc="7" ncloc="7" packages="1" files="1" classes="1"/>
<file name="Crown.js" path="Z:\mpp-server\src\Crown.js">
<metrics statements="7" coveredstatements="7" conditionals="0" coveredconditionals="0" methods="2" coveredmethods="2"/>
<line num="3" count="3" type="stmt"/>
<line num="4" count="3" type="stmt"/>
<line num="5" count="3" type="stmt"/>
<line num="6" count="3" type="stmt"/>
<line num="10" count="3" type="stmt"/>
<line num="17" count="6" type="stmt"/>
<line num="21" count="1" type="stmt"/>
</file>
</project>
</coverage>

View File

@ -1,2 +0,0 @@
{"Z:\\mpp-server\\src\\Crown.js": {"path":"Z:\\mpp-server\\src\\Crown.js","statementMap":{"0":{"start":{"line":3,"column":8},"end":{"line":3,"column":32}},"1":{"start":{"line":4,"column":8},"end":{"line":4,"column":26}},"2":{"start":{"line":5,"column":8},"end":{"line":5,"column":31}},"3":{"start":{"line":6,"column":8},"end":{"line":9,"column":9}},"4":{"start":{"line":10,"column":8},"end":{"line":13,"column":9}},"5":{"start":{"line":17,"column":8},"end":{"line":17,"column":55}},"6":{"start":{"line":21,"column":0},"end":{"line":21,"column":23}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":2,"column":4},"end":{"line":2,"column":5}},"loc":{"start":{"line":2,"column":26},"end":{"line":14,"column":5}},"line":2},"1":{"name":"(anonymous_1)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":5}},"loc":{"start":{"line":16,"column":31},"end":{"line":18,"column":5}},"line":16}},"branchMap":{},"s":{"0":3,"1":3,"2":3,"3":3,"4":3,"5":6,"6":1},"f":{"0":3,"1":6},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"40c7ee2fa87e854156e60f42bacf48fffd8b89df"}
}

View File

@ -1,151 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for Crown.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="index.html">All files</a> Crown.js</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>7/7</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>2/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>7/7</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input oninput="onInput()" type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">6x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">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()
}
}
&nbsp;
static generateRandomPos() {
return Math.floor(Math.random() * 10000) / 100;
}
}
&nbsp;
module.exports = Crown;
&nbsp;
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at Sat Jun 25 2022 23:47:39 GMT-0400 (Eastern Daylight Time)
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
</html>

View File

@ -1,224 +0,0 @@
body, html {
margin:0; padding: 0;
height: 100%;
}
body {
font-family: Helvetica Neue, Helvetica, Arial;
font-size: 14px;
color:#333;
}
.small { font-size: 12px; }
*, *:after, *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
h1 { font-size: 20px; margin: 0;}
h2 { font-size: 14px; }
pre {
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
margin: 0;
padding: 0;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
a { color:#0074D9; text-decoration:none; }
a:hover { text-decoration:underline; }
.strong { font-weight: bold; }
.space-top1 { padding: 10px 0 0 0; }
.pad2y { padding: 20px 0; }
.pad1y { padding: 10px 0; }
.pad2x { padding: 0 20px; }
.pad2 { padding: 20px; }
.pad1 { padding: 10px; }
.space-left2 { padding-left:55px; }
.space-right2 { padding-right:20px; }
.center { text-align:center; }
.clearfix { display:block; }
.clearfix:after {
content:'';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.fl { float: left; }
@media only screen and (max-width:640px) {
.col3 { width:100%; max-width:100%; }
.hide-mobile { display:none!important; }
}
.quiet {
color: #7f7f7f;
color: rgba(0,0,0,0.5);
}
.quiet a { opacity: 0.7; }
.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}
div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
padding: 0;
}
table.coverage td {
margin: 0;
padding: 0;
vertical-align: top;
}
table.coverage td.line-count {
text-align: right;
padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
text-align: right;
padding-right: 10px;
min-width:20px;
}
table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 100%;
}
.missing-if-branch {
display: inline-block;
margin-right: 5px;
border-radius: 3px;
position: relative;
padding: 0 4px;
background: #333;
color: yellow;
}
.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
.coverage-summary tr { border-bottom: 1px solid #bbb; }
.keyline-all { border: 1px solid #ddd; }
.coverage-summary td, .coverage-summary th { padding: 10px; }
.coverage-summary tbody { border: 1px solid #bbb; }
.coverage-summary td { border-right: 1px solid #bbb; }
.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
.coverage-summary th.file { border-right: none !important; }
.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
.coverage-summary td.abs { text-align: right; }
.coverage-summary td.file { white-space: nowrap; }
.coverage-summary td.pic { min-width: 120px !important; }
.coverage-summary tfoot td { }
.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.status-line { height: 10px; }
/* yellow */
.cbranch-no { background: yellow !important; color: #111; }
/* dark red */
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
.low .chart { border:1px solid #C21F39 }
.highlighted,
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
background: #C21F39 !important;
}
/* medium red */
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
.low, .cline-no { background:#FCE1E5 }
/* light green */
.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
.cstat-yes { background:rgb(161,215,106) }
/* dark green */
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
.high .chart { border:1px solid rgb(77,146,33) }
/* dark yellow (gold) */
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
.medium .chart { border:1px solid #f9cd0b; }
/* light yellow */
.medium { background: #fff4c2; }
.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }
span.cline-neutral { background: #eaeaea; }
.coverage-summary td.empty {
opacity: .5;
padding-top: 4px;
padding-bottom: 4px;
line-height: 1;
color: #888;
}
.cover-fill, .cover-empty {
display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
background: white;
}
.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -48px;
}
.footer, .push {
height: 48px;
}

View File

@ -1,87 +0,0 @@
/* eslint-disable */
var jumpToCode = (function init() {
// Classes of code we would like to highlight in the file view
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
// Elements to highlight in the file listing view
var fileListingElements = ['td.pct.low'];
// We don't want to select elements that are direct descendants of another match
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
// Selecter that finds elements on the page to which we can jump
var selector =
fileListingElements.join(', ') +
', ' +
notSelector +
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
// The NodeList of matching elements
var missingCoverageElements = document.querySelectorAll(selector);
var currentIndex;
function toggleClass(index) {
missingCoverageElements
.item(currentIndex)
.classList.remove('highlighted');
missingCoverageElements.item(index).classList.add('highlighted');
}
function makeCurrent(index) {
toggleClass(index);
currentIndex = index;
missingCoverageElements.item(index).scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
}
function goToPrevious() {
var nextIndex = 0;
if (typeof currentIndex !== 'number' || currentIndex === 0) {
nextIndex = missingCoverageElements.length - 1;
} else if (missingCoverageElements.length > 1) {
nextIndex = currentIndex - 1;
}
makeCurrent(nextIndex);
}
function goToNext() {
var nextIndex = 0;
if (
typeof currentIndex === 'number' &&
currentIndex < missingCoverageElements.length - 1
) {
nextIndex = currentIndex + 1;
}
makeCurrent(nextIndex);
}
return function jump(event) {
if (
document.getElementById('fileSearch') === document.activeElement &&
document.activeElement != null
) {
// if we're currently focused on the search input, we don't want to navigate
return;
}
switch (event.which) {
case 78: // n
case 74: // j
goToNext();
break;
case 66: // b
case 75: // k
case 80: // p
goToPrevious();
break;
}
};
})();
window.addEventListener('keydown', jumpToCode);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

View File

@ -1,116 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>All files</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>7/7</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>2/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>7/7</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input oninput="onInput()" type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="Crown.js"><a href="Crown.js.html">Crown.js</a></td>
<td data-value="100" class="pic high">
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="7" class="abs high">7/7</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="2" class="abs high">2/2</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="7" class="abs high">7/7</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at Sat Jun 25 2022 23:47:39 GMT-0400 (Eastern Daylight Time)
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
</html>

View File

@ -1 +0,0 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

View File

@ -1,196 +0,0 @@
/* eslint-disable */
var addSorting = (function() {
'use strict';
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() {
return document.querySelector('.coverage-summary');
}
// returns the thead element of the summary table
function getTableHeader() {
return getTable().querySelector('thead tr');
}
// returns the tbody element of the summary table
function getTableBody() {
return getTable().querySelector('tbody');
}
// returns the th element for nth column
function getNthColumn(n) {
return getTableHeader().querySelectorAll('th')[n];
}
function onFilterInput() {
const searchValue = document.getElementById('fileSearch').value;
const rows = document.getElementsByTagName('tbody')[0].children;
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
if (
row.textContent
.toLowerCase()
.includes(searchValue.toLowerCase())
) {
row.style.display = '';
} else {
row.style.display = 'none';
}
}
}
// loads the search box
function addSearchBox() {
var template = document.getElementById('filterTemplate');
var templateClone = template.content.cloneNode(true);
templateClone.getElementById('fileSearch').oninput = onFilterInput;
template.parentElement.appendChild(templateClone);
}
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML =
colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function(a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function(a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc
? ' sorted-desc'
: ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function() {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i = 0; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function() {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData();
addSearchBox();
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);

View File

@ -1,20 +0,0 @@
TN:
SF:src\Crown.js
FN:2,(anonymous_0)
FN:16,(anonymous_1)
FNF:2
FNH:2
FNDA:3,(anonymous_0)
FNDA:6,(anonymous_1)
DA:3,3
DA:4,3
DA:5,3
DA:6,3
DA:10,3
DA:17,6
DA:21,1
LF:7
LH:7
BRF:0
BRH:0
end_of_record

View File

@ -1,72 +0,0 @@
// dotenv
require('dotenv').config();
// call new Server
global.WebSocket = require('ws');
global.EventEmitter = require('events').EventEmitter;
global.fs = require('fs');
const AsyncConsole = require('asyncconsole');
global.isString = function(a) {
return typeof a === 'string';
}
global.isBool = function(a) {
return typeof a === 'boolean';
}
global.isObj = function(a) {
return typeof a === "object" && !Array.isArray(a) && a !== null;
}
let Server = require("./src/Server.js");
let config = require('./config');
Server.start(config);
global.SERVER = Server;
// doesn't work with pm2
/*
let console = process.platform == 'win32' ? new AsyncConsole("", input => {
try {
console.log(JSON.stringify(eval(input)));
} catch(e) {
console.log(e.toString());
}
}) : {};
*/
// dev environment
if (config.hostDevFiles) {
let express_logger = new (require("./src/Logger"))("Express Server");
const express = require('express');
const app = express();
const path = require('path');
var http = require('http');
let dir = path.join(__dirname, 'mpp.hri7566.info');
app.use(express.static(path.join(__dirname, dir)));
app.get('*', (req, res, next) => {
let file = path.join(dir, req.path);
if (fs.existsSync(file) && !file.endsWith('/') && !file.endsWith('\\')) {
res.sendFile(file);
} else {
res.sendFile(path.join(dir, 'index.html'));
}
});
const express_port = 8075;
http.createServer(app).listen(express_port);
}
if (config.enableMPPCloneBot) {
require('./mppclonebot');
}

View File

@ -1,231 +0,0 @@
const Client = require('mppclone-client');
const Logger = require('./src/Logger');
const { EventEmitter } = require('events');
const token = process.env.MPPCLONE_TOKEN;
class Command {
static commands = {};
static getUsage(us, prefix) {
return us.split('%PREFIX%').join(prefix);
}
constructor(cmd, desc, usage, minargs, func, minrank, hidden) {
this.cmd = typeof cmd == 'object' ? cmd : [cmd];
this.desc = desc || "No description";
this.usage = usage || "No usage";
this.minargs = minargs || 0;
this.func = func;
this.minrank = minrank || 0;
this.hidden = hidden || false;
Command.commands[this.cmd[0]] = this;
}
}
class Rank {
static ranks = {};
static user_ranks = {};
static setRank(_id, rank) {
Rank.user_ranks[_id] = rank;
}
static getRank(_id) {
return Rank.user_ranks[_id];
}
constructor(name, desc, minrank) {
this.name = name;
this.desc = desc;
this.minrank = minrank;
Rank.ranks[name] = this;
}
}
new Rank("User", "Default rank", 0);
new Rank("Mod", "Moderator rank", 1);
new Rank("Admin", "Administrator rank", 2);
new Rank("Owner", "Owner rank", 3);
class Prefix {
static prefixes = {};
static hasAnyPrefix(str) {
for (let i in Prefix.prefixes) {
if (str.startsWith(Prefix.prefixes[i].prefix)) {
return true;
}
}
}
static getPrefixFromString(str) {
for (let i in Prefix.prefixes) {
if (str.startsWith(Prefix.prefixes[i].prefix)) {
return Prefix.prefixes[i];
}
}
}
constructor(id, prefix) {
this.id = id;
this.prefix = prefix;
Prefix.prefixes[id] = this;
}
}
class Bot extends EventEmitter {
constructor(cl) {
super();
this.logger = new Logger('MPPClone Bot');
this.client = cl;
this.bindEventListeners();
this.userset = {
name: 'mpp.hri7566.info [indev]', // TODO change this name
color: '#76b0db'
};
this.chatBuffer = [];
this.chatBufferCycleCounter = 0;
this.chatBufferCycle();
this.client.start();
this.client.setChannel('✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧');
}
bindEventListeners() {
this.client.on('a', msg => {
this.emit('receiveChat', msg);
});
this.client.on('ch', msg => {
this.emit('resetName', msg);
})
this.client.on('hi', msg => {
this.emit('online', msg);
});
this.client.on('t', msg => {
this.emit('resetName', msg);
});
this.on('resetName', () => {
if (this.client.getOwnParticipant().name !== this.userset.name || this.client.getOwnParticipant().color !== this.userset.color) {
this.client.sendArray([{
m: 'userset',
set: this.userset
}]);
}
});
this.on('receiveChat', msg => {
if (Prefix.hasAnyPrefix(msg.a)) {
msg.prefix = Prefix.getPrefixFromString(msg.a);
this.emit('runCommand', msg);
}
});
this.on('addToChatBuffer', msg => {
this.chatBuffer.push(msg);
});
this.on('runCommand', msg => {
if (!msg.prefix) return;
msg.args = msg.a.split(' ');
msg.cmd = msg.args[0].substring(msg.prefix.prefix.length).trim();
msg.argcat = msg.a.substring(msg.args[0].length).trim();
let rank = Rank.getRank(msg.p._id);
if (!rank) {
rank = Rank.ranks['User'];
Rank.setRank(msg.p._id, rank);
}
msg.rank = rank;
for (let cmd of Object.values(Command.commands)) {
if (!cmd.cmd.includes(msg.cmd)) continue;
console.log(msg.cmd, cmd.cmd);
if (msg.args.length < cmd.minargs) return;
if (msg.rank.id < cmd.minrank) return;
try {
let out = cmd.func(msg);
if (!out) return;
out = out.split('\n').join(' ').split('\t').join(' ').split('\r').join(' ');
if (out !== '') {
this.emit('addToChatBuffer', {
m: 'a',
message: out,
p: msg.p._id
});
}
} catch (e) {
this.emit('addToChatBuffer', {
m: 'a',
message: 'An error has occurred.',
p: msg.p._id
});
}
}
});
this.on('online', () => {
this.logger.log('Connected');
});
}
async chatBufferCycle() {
if (this.chatBuffer.length <= 0) {
setTimeout(() => {
this.chatBufferCycle();
});
return;
}
this.chatBufferCycleCounter++;
let time = 0;
if (this.chatBufferCycleCounter > 4) {
time += 1000;
}
setTimeout(() => {
let nextMessage = this.chatBuffer.shift();
this.client.sendArray([nextMessage]);
this.chatBufferCycle();
this.chatBufferCycleCounter--;
}, time);
}
}
new Prefix('hmpp!', 'hmpp!');
new Prefix('h!', 'h!');
new Command(['help', 'cmds', 'h'], 'List all commands', '%PREFIX%help', 0, (msg) => {
let cmds = 'Commands: ';
for (let cmd of Object.values(Command.commands)) {
if (cmd.hidden) continue;
cmds += `${cmd.cmd[0]}, `;
}
cmds = cmds.substring(0, cmds.length - 2).trim();
return cmds;
}, 0, false);
new Command(['users'], 'See how many users are online', `%PREFIX%users`, 0, (msg) => {
console.log(SERVER.connections.size);
return `There are ${SERVER.connections.size} users on HMPP.`;
}, 0, false);
let cl = new Client("wss://mppclone.com:8443", token);
let bot = new Bot(cl);

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,848 +0,0 @@
const createKeccakHash = require("keccak");
const Crown = require("./Crown.js");
const Database = require("./Database.js");
const Logger = require("./Logger.js");
const Quota = require("./Quota.js");
const RoomSettings = require("./RoomSettings.js");
const ftc = require("fancy-text-converter");
const Notification = require("./Notification");
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 SERVER_PARTICIPANT = {
name: "mpp",
color: "#ffffff",
_id: "0",
id: "0"
};
const LOGGING_CHANNEL = "lolwutsecretloggingchannel";
const BAN_CHANNEL = "test/awkward";
class Channel extends EventEmitter {
static loggingChannel = LOGGING_CHANNEL;
static loggerParticipant = LOGGER_PARTICIPANT;
static serverParticipant = SERVER_PARTICIPANT;
static banChannel = BAN_CHANNEL;
constructor(server, _id, settings, cl) {
super();
this.logger = new Logger(`Room - ${_id}`);
this._id = _id;
this.server = server;
this.crown;
this.crowndropped = false;
if (this.isLobby(this._id)) {
this.settings = new RoomSettings(this.server.lobbySettings);
// this.settings.lobby = true;
// this.settings.color = this.server.lobbySettings.color;
// this.settings.color2 = this.server.lobbySettings.color2;
} else {
this.settings = new RoomSettings(settings, "user");
}
this.chatmsgs = [];
this.ppl = new Map();
this.connections = [];
this.bindEventListeners();
this.server.channels.set(_id, this);
this.bans = new Map();
this.flags = {};
this.destroyed = false;
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);
let c2 = new Color(c.toHexa());
c2.add(-0x40, -0x40, -0x40);
this.settings = RoomSettings.changeSettings(
{
color: c.toHexa(),
color2: c2.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) => {
if (err) {
return;
}
this.settings = RoomSettings.changeSettings(
this.settings,
true
);
this.chatmsgs = set.chat;
this.sendChatArray();
this.setData();
});
}
if (this.isLobby(this._id)) {
this.colorInterval = setInterval(() => {
this.setDefaultLobbyColorBasedOnDate();
}, 5000);
this.setDefaultLobbyColorBasedOnDate();
}
}
setChatArray(arr) {
this.chatmsgs = arr || [];
this.sendArray([
{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}
]);
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);
if (!this.settings) {
this.settings = new RoomSettings(this.server.lobbySettings);
}
this.settings.changeSettings({
color: col.toHexa(),
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
let otheruser = this.connections.find(a => a.user._id == cl.user._id);
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);
// set id
cl.user.id = participantId;
cl.participantId = participantId;
// init quotas (TODO pass type of room in?)
cl.initParticipantQuotas();
// 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 ||
this.settings["owner_id"] == cl.user._id))
) {
// user owns the room
// we need to switch the crown to them
//cl.quotas.a.setParams(Quota.PARAMS_A_CROWNED);
this.emit("add crown", {
participantId: cl.participantId,
userId: cl.user._id
});
this.crowndropped = false;
// this.settings = new RoomSettings(set, 'user');
} else {
//cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL);
if (this.isLobby(this._id) && this.settings.lobby !== true) {
// fix lobby setting
this.settings.changeSettings({ lobby: true });
// this.settings.visible = true;
// this.settings.crownsolo = false;
// this.settings.lobby = true;
// this.settings.color = this.server.lobbySettings.color;
// this.settings.color2 = this.server.lobbySettings.color2;
} else {
if (!this.isLobby) {
if (typeof set == "undefined") {
if (typeof this.settings == "undefined") {
this.settings = new RoomSettings(
this.server.defaultRoomSettings,
"user"
);
} else {
this.settings = new RoomSettings(
cl.channel.settings,
"user"
);
}
} else {
this.settings = new RoomSettings(set, "user");
}
}
}
}
this.ppl.set(participantId, cl);
this.connections.push(cl);
cl.sendArray([
{
m: "c",
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);
} else {
cl.user.id = otheruser.participantId;
cl.participantId = otheruser.participantId;
cl.quotas = otheruser.quotas;
this.connections.push(cl);
cl.sendArray([
{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}
]);
this.updateCh(cl, this.settings);
}
}
remove(p) {
// remove user
if (!p) return;
if (!p.user) return;
let otheruser = this.connections.filter(a => a.user._id == p.user._id);
if (!(otheruser.length > 1)) {
this.ppl.delete(p.participantId);
this.connections.splice(
this.connections.findIndex(
a => a.connectionid == p.connectionid
),
1
);
this.sendArray(
[
{
m: "bye",
p: p.participantId
}
],
p,
false
);
if (this.crown)
if (this.crown.userId == p.user._id && !this.crowndropped) {
this.chown();
}
this.updateCh();
} else {
this.connections.splice(
this.connections.findIndex(
a => a.connectionid == p.connectionid
),
1
);
}
}
updateCh(cl, set) {
//update channel for all people in channel
if (Array.from(this.ppl.values()).length <= 0) {
setTimeout(() => {
this.destroy();
}, 3000);
return;
}
this.connections.forEach(usr => {
let u = this.fetchChannelData(usr, cl);
this.server.connections.get(usr.connectionid).sendArray([u]);
});
this.server.updateChannelList([this.fetchChannelData()]);
}
updateParticipant(pid, options) {
let p;
Array.from(this.ppl).map(rpg => {
if (rpg[1].user._id == pid) p = rpg[1];
});
if (typeof p == "undefined") return;
options.name ? (p.user.name = options.name) : {};
options._id ? (p.user._id = options._id) : {};
options.color ? (p.user.color = options.color) : {};
this.connections
.filter(ofo => ofo.participantId == p.participantId)
.forEach(usr => {
options.name ? (usr.user.name = options.name) : {};
options._id ? (usr.user._id = options._id) : {};
options.color ? (usr.user.color = options.color) : {};
});
if (!p.hidden) {
this.sendArray([
{
color: p.user.color,
id: p.participantId,
m: "p",
name: p.user.name,
x: p.x || 200,
y: p.y || 100,
_id: p.user._id
}
]);
}
}
destroy() {
//destroy room
if (this.destroyed) return;
if (this.ppl.size > 0) return;
if (this._id == "lobby") return;
this.destroyed = true;
this._id;
this.logger.log(`Deleted room ${this._id}`);
this.settings = undefined;
this.ppl;
this.connnections;
this.chatmsgs;
this.server.channels.delete(this._id);
}
sendArray(arr, not, onlythisparticipant) {
this.connections.forEach(usr => {
if (
!not ||
(usr.participantId != not.participantId &&
!onlythisparticipant) ||
(usr.connectionid != not.connectionid && onlythisparticipant)
) {
try {
let cl = this.server.connections.get(usr.connectionid);
if (!cl) return;
this.server.connections
.get(usr.connectionid)
.sendArray(arr);
} catch (e) {
this.logger.error(e);
}
}
});
}
fetchChannelData(usr, cl) {
let chppl = [];
[...this.ppl.values()].forEach(c => {
if (cl) {
if (c.hidden == true && c.user._id !== cl.user._id) {
// client is hidden and we are that client
return;
} else if (c.user._id == cl.user._id) {
// let u = {
// _id: c.user._id,
// name: c.user.name + ' [HIDDEN]',
// color: c.user.color,
// id: c.participantId
// }
// chppl.push(u);
}
}
let u = {
_id: c.user._id,
name: c.user.name,
color: c.user.color,
id: c.participantId
};
chppl.push(u);
});
let data = {
m: "ch",
p: "ofo",
ch: {
count: chppl.length,
crown: this.crown,
settings: this.settings,
_id: this._id
},
ppl: chppl
};
if (cl) {
if (usr.connectionid == cl.connectionid) {
data.p = cl.participantId;
} else {
delete data.p;
}
} else {
delete data.p;
}
if (data.ch.crown == null) {
delete data.ch.crown;
} else {
}
return data;
}
verifyColor(strColor) {
var test2 = /^#[0-9A-F]{6}$/i.test(strColor);
if (test2 == true) {
return strColor;
} else {
return false;
}
}
isLobby(_id) {
if (_id.startsWith("lobby")) {
if (_id == "lobby") {
return true;
}
let lobbynum = _id.split("lobby")[1];
if (!(parseInt(lobbynum).toString() == lobbynum)) return false;
for (let i in lobbynum) {
if (parseInt(lobbynum[i]) >= 0) {
if (parseInt(i) + 1 == lobbynum.length) return true;
} else {
return false;
}
}
} else if (
_id.startsWith("test/") ||
_id.toLowerCase().includes("grant")
) {
if (_id == "test/") {
return false;
} else {
return true;
}
} else {
return false;
}
}
chown(id) {
let prsn = this.ppl.get(id);
if (prsn) {
this.crown = new Crown(id, prsn.user._id);
this.crowndropped = false;
} else {
if (this.crown) {
this.crown = new Crown(id, this.crown.userId);
this.crowndropped = true;
}
}
this.updateCh();
}
setCoords(p, x, y) {
if (p.participantId && this.ppl.get(p.participantId)) {
x ? (this.ppl.get(p.participantId).x = x) : {};
y ? (this.ppl.get(p.participantId).y = y) : {};
this.sendArray(
[
{
m: "m",
id: p.participantId,
x: this.ppl.get(p.participantId).x,
y: this.ppl.get(p.participantId).y
}
],
p,
false
);
}
}
chat(p, msg) {
if (msg.message.length > 512) return;
if (p.participantId == 0) {
let message = {};
message.m = "a";
message.t = Date.now();
message.a = msg.message;
if (message.a.length > 0 && message.a.length <= 512) {
message.p = {
color: "#ffffff",
id: "0",
name: "mpp",
_id: "0"
};
this.sendArray([message]);
this.chatmsgs.push(message);
this.setData();
return;
}
}
let filter = ["AMIGHTYWIND", "CHECKLYHQ"];
let regexp = new RegExp("\\b(" + filter.join("|") + ")\\b", "i");
if (regexp.test(msg.message.split(" ").join(""))) return;
let prsn = this.ppl.get(p.participantId);
if (!prsn) return;
let message = {};
message.m = "a";
message.a = msg.message;
if (prsn.user.hasFlag("chat_curse_1")) {
if (prsn.user.flags["chat_curse_1"] != false)
message.a = message.a
.split(/[aeiou]/)
.join("o")
.split(/[AEIOU]/)
.join("O");
}
if (prsn.user.hasFlag("chat_curse_2")) {
}
message.p = {
color: p.user.color,
id: p.participantId,
name: p.user.name,
_id: p.user._id
};
message.t = Date.now();
this.sendArray([message]);
this.chatmsgs.push(message);
this.setData();
InternalBot.emit("receive message", message, prsn, this);
}
adminChat(str) {
this.chat(
{
participantId: 0
},
{
message: str
}
);
}
hasUser(id) {
// return this.ppl.has(id);
for (const p of this.ppl.values()) {
if (p.id == id) return true;
}
}
playNote(cl, note) {
if (cl.user.hasFlag("mute", true)) {
return;
}
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) {
if (no.v) {
if (vol == 0) {
no.v = vol;
} else {
no.v *= vol;
}
}
}
}
this.sendArray(
[
{
m: "n",
n: note.n,
p: cl.participantId,
t: note.t
}
],
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)) {
u.setChannel(Channel.banChannel, {});
if (asd)
new Notification(this.server, {
id: "",
title: "Notice",
text: `Banned from "${this._id}" for ${Math.floor(
Math.floor(ms / 1000) / 60
)} minutes.`,
duration: 7000,
target: "#room",
class: "short",
targetUser: user.participantId,
targetChannel: "all",
cl: user
}).send();
new Notification(this.server, {
id: "",
title: "Notice",
text: `Banned from "${this._id}" for ${Math.floor(
Math.floor(ms / 1000) / 60
)} minutes.`,
duration: 7000,
target: "#room",
class: "short",
targetUser: user.participantId,
targetChannel: "all",
cl: user
}).send();
if (asd)
new Notification(this.server, {
id: "",
class: "short",
target: "#room",
title: "Notice",
text: `${pthatbanned.user.name} banned ${
user.user.name
} from the channel for ${Math.floor(
Math.floor(ms / 1000) / 60
)} minutes.`,
duration: 7000,
targetChannel: "room",
cl: pthatbanned
}).send();
if (this.crown && this.crown.userId == _id) {
new Notification(this.server, {
id: "",
class: "short",
target: "#room",
title: "Certificate of Award",
text: `Let it be known that ${user.user.name} kickbanned him/her self.`,
targetChannel: "room",
duration: 7000,
cl: pthatbanned
}).send();
}
//}
});
}
unban(_id) {
let ban = this.bans.get(_id);
if (!ban) return;
if (ban.bantime) {
delete ban.bantime;
}
if (ban.bannedtime) {
delete ban.bannedtime;
}
this.bans.delete(ban.user._id);
}
bindEventListeners() {
this.on("bye", participant => {
this.remove(participant);
});
this.on("m", msg => {
let p = this.ppl.get(msg.p);
if (!p) return;
this.setCoords(p, msg.x, msg.y);
});
this.on("a", (participant, msg) => {
this.chat(participant, msg);
});
this.on("update", (cl, set) => {
this.updateCh(cl, set);
});
this.on("remove crown", () => {
this.crown = undefined;
delete this.crown;
this.emit("update");
});
this.on("add crown", msg => {
this.crown = new Crown(msg.participantId, msg.userId);
this.emit("update");
});
this.on("kickban", msg => {
if (!msg._id) return;
if (!msg.ms) msg.ms = 30 * 60 * 1000;
this.kickban(msg._id, msg.ms);
});
}
verifySet(_id, msg) {
if (typeof msg.set !== "object") {
msg.set = {
visible: true,
color: this.server.defaultSettings.color,
chat: true,
crownsolo: false
};
}
msg.set = RoomSettings.changeSettings(msg.set);
if (typeof msg.set.lobby !== "undefined") {
if (msg.set.lobby == true) {
if (!this.isLobby(_id)) delete msg.set.lobby;
} else {
if (this.isLobby(_id)) {
msg.set = this.server.lobbySettings;
}
}
}
}
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 = Channel;

View File

@ -1,353 +0,0 @@
const Channel = require("./Channel.js");
const Quota = require("./Quota.js");
const quotas = require("../Quotas");
const { RateLimit, RateLimitChain } = require("./Ratelimit.js");
const User = require("./User.js");
const Database = require("./Database.js");
const { EventEmitter } = require("events");
class Client extends EventEmitter {
/**
* Server-side client representation
* @param {*} ws WebSocket object
* @param {*} req WebSocket request
* @param {*} server Server
*/
constructor(ws, req, server) {
super();
EventEmitter.call(this);
this.connectionid = server.connectionid;
this.server = server;
this.participantId;
this.channel;
this.isSubscribedToAdminStream = false;
this.adminStreamInterval;
this.staticQuotas = {
room: new RateLimit(quotas.room.time)
};
this.quotas = {};
this.ws = ws;
this.req = req;
this.ip = req.connection.remoteAddress.replace("::ffff:", "");
this.hidden = false;
Database.getUserData(this, server).then(data => {
this.user = new User(this, data);
this.destroied = false;
this.bindEventListeners();
require("./Message.js")(this);
});
}
/**
* Check if user is connected
* @returns boolean
*/
isConnected() {
return this.ws && this.ws.readyState === WebSocket.OPEN;
}
/**
* Check if user is connecting
* @returns boolean
*/
isConnecting() {
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) {
if (this.channel && this.channel._id == _id) return;
if (this.server.channels.get(_id)) {
let ch = this.server.channels.get(_id, settings);
let userbanned = ch.bans.get(this.user._id);
if (
userbanned &&
Date.now() - userbanned.bannedtime >= userbanned.msbanned
) {
ch.bans.delete(userbanned.user._id);
userbanned = undefined;
}
if (userbanned) {
new Notification(this.server, {
targetUser: this.participantId,
targetChannel: all,
title: "Notice",
text: `Currently banned from "${_id}" for ${Math.ceil(
Math.floor(
(userbanned.msbanned -
(Date.now() - userbanned.bannedtime)) /
1000
) / 60
)} minutes.`,
duration: 7000,
id: "",
target: "#room",
class: "short",
cl: this
}).send();
this.setChannel(Channel.banChannel, settings);
return;
}
let channel = this.channel;
if (channel) this.channel.emit("bye", this);
if (channel) this.channel.updateCh(this);
this.channel = this.server.channels.get(_id);
this.channel.join(this);
} else {
let room = new Channel(this.server, _id, settings, this);
this.server.channels.set(_id, room);
if (this.channel) this.channel.emit("bye", this);
this.channel = this.server.channels.get(_id);
this.channel.join(this, settings);
}
}
/**
* Send data to client
* @param {any[]} arr Array of messages
*/
sendArray(arr) {
if (this.isConnected()) {
//console.log(`SEND: `, JSON.colorStringify(arr));
this.ws.send(JSON.stringify(arr));
}
}
/**
* 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.channels.forEach(channel => {
channel.updateParticipant(this.user._id, {
name: name
});
});
});
}
}
}
/**
* Set rate limits
*/
initParticipantQuotas() {
this.quotas = {
//"chat": new Quota(Quota.PARAMS_A_NORMAL),
chat: {
lobby: new RateLimitChain(
quotas.chat.lobby.amount,
quotas.chat.lobby.time
),
normal: new RateLimitChain(
quotas.chat.normal.amount,
quotas.chat.normal.time
),
insane: new RateLimitChain(
quotas.chat.insane.amount,
quotas.chat.insane.time
)
},
cursor: new RateLimitChain(
quotas.cursor.amount,
quotas.cursor.time
),
chown: new RateLimitChain(quotas.chown.amount, quotas.chown.time),
userset: new RateLimitChain(
quotas.userset.amount,
quotas.userset.time
),
kickban: new RateLimitChain(
quotas.kickban.amount,
quotas.kickban.time
),
// note: new Quota(Quota.PARAMS_LOBBY),
note: new RateLimitChain(5, 5000),
chset: new Quota(Quota.PARAMS_USED_A_LOT),
"+ls": new Quota(Quota.PARAMS_USED_A_LOT),
"-ls": new Quota(Quota.PARAMS_USED_A_LOT)
};
}
/**
* Stop the client
*/
destroy() {
if (this.user) {
let lastClient = true;
for (const cl of this.server.connections.values()) {
if (cl.user) {
if (cl.user._id == this.user._id) {
lastClient = false;
break;
}
}
}
if (lastClient) this.user.stopFlagEvents();
}
this.ws.close();
if (this.channel) {
this.channel.emit("bye", this);
}
this.user;
this.participantId;
this.channel;
this.server.roomlisteners.delete(this.connectionid);
this.connectionid;
this.server.connections.delete(this.connectionid);
this.destroied = true;
}
/**
* Internal
*/
bindEventListeners() {
this.ws.on("message", (evt, admin) => {
try {
if (typeof evt !== "string") evt = evt.toJSON();
let transmission = JSON.parse(evt);
for (let msg of transmission) {
if (typeof msg !== "object" || msg == null || msg == NaN)
return;
if (!msg.hasOwnProperty("m")) return;
if (!this.server.legit_m.includes(msg.m)) return;
this.emit(msg.m, msg, !!admin);
//console.log(`RECIEVE: `, JSON.colorStringify(msg));
}
} catch (e) {
console.log(e);
// this.destroy();
}
});
this.ws.on("close", () => {
if (!this.destroied) {
this.destroy();
}
});
this.ws.addEventListener("error", err => {
console.error(err);
if (!this.destroied) {
this.destroy();
}
});
}
/**
* Send admin data bus message
*/
sendAdminData() {
let data = {};
data.m = "data";
let channels = [];
this.server.channels.forEach(ch => {
let ppl = [];
const chdata = ch.fetchChannelData();
for (let p of chdata.ppl) {
ppl.push({
user: p
});
}
channels.push({
chat: ch.chatmsgs.slice(-1 * 32),
participants: ppl
});
});
let users = [];
let clients = [];
this.server.connections.forEach(cl => {
if (!cl.user) return;
let c = {
ip: cl.ip,
participantId: cl.participantId,
userId: cl.user._id
};
clients.push(c);
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 = {
loggingChannel: Channel.loggingChannel,
loggerParticipant: Channel.loggerParticipant,
channels
};
data.clientManager = {
users,
clients
};
data.uptime = Date.now() - this.server.startTime;
data.config = this.server.config;
if (this.user) {
data.p = {
_id: this.user._id,
name: this.user.name,
color: this.user.color,
flags: this.user.flags
// inventory: this.user.inventory
};
}
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;

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +0,0 @@
const Color = require("./Color");
function hashCode(str) {
// java String#hashCode
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash;
}
function intToRGB(i) {
var c = (i & 0x00ffffff).toString(16).toUpperCase();
return "00000".substring(0, 6 - c.length) + c;
}
/**
* 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);
dayOfYear %= 365;
// console.log(dayOfYear);
// get hour
let hours = currentDate.getHours();
let seconds = currentDate.getSeconds();
// get a hue based on time of day and day of year
let h = dayOfYear / 365;
let s = (hours + 1) / (24 / 3);
// let s = 1;
let l = 0.15 + Math.floor(((hours + 12) / 60) * 1000) / 1000;
if (l > 1 / 3) l = 1 / 3;
if (s > 0.75) s = 0.75;
// const h = (Date.now() % 360) / 360;
// const s = 1;
// const l = 0.5;
// 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
};

View File

@ -1,29 +0,0 @@
const ung = require("unique-names-generator");
const ung_config = {
dictionaries: [ung.names],
separator: " ",
length: 1
};
class Cow {
static generateRandomName() {
return ung.uniqueNamesGenerator(ung_config);
}
constructor() {
this["display_name"] = Cow.generateRandomName();
this["emoji"] = "🐄";
this["count"] = 1;
}
toString() {
return `${this.emoji}${this.display_name}${
this.count > 1 ? `(x${this.count})` : ""
}`;
}
}
module.exports = {
Cow
};

View File

@ -1,22 +0,0 @@
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;
}
}
module.exports = Crown;

View File

@ -1,215 +0,0 @@
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");
const Logger = require("./Logger");
const logger = new Logger("Database");
mongoose.connect(
process.env.MONGO_URL,
{
useNewUrlParser: true,
useUnifiedTopology: true,
connectTimeoutMS: 1000
},
err => {
if (err) {
console.error(err);
logger.error("Unable to connect to database service");
process.exit(1);
}
logger.log("Connected to Mongo");
}
);
// TODO implement this with an if statement instead
fs.mkdirSync("db/", {
recursive: true
});
class Database {
static userdb;
static roomdb;
static bandb;
static utildb;
static async load() {
logger.log("Initializing level stores...");
this.userdb = mongoose.connection;
this.roomdb = level("db/rooms.db");
this.bandb = level("db/ban.db");
this.utildb = level("db/util.db");
logger.log("Level stores initialized");
// const writeFile = promisify(fs.writeFile);
// const readdir = promisify(fs.readdir);
// let files = await readdir("src/db/");
// if (!files.includes("users.json")) {
// await writeFile('src/db/users.json', JSON.stringify(this.default_db, null, 2))
// this.userdb = new Map(Object.entries(require("./db/users.json")));
// } else {
// this.userdb = new Map(Object.entries(require("./db/users.json")));
// }
}
static async getUserData(cl, server) {
if (!this.userdb) {
await this.load();
}
let _id = createKeccakHash("keccak256")
.update(cl.server._id_Private_Key + cl.ip)
.digest("hex")
.substr(0, 24);
let user = await UserModel.findById(_id).exec();
if (user == null) {
user = await this.createUser(_id);
}
return user;
}
static async createUser(_id) {
if (!this.userdb) {
await this.load();
}
let user = new UserModel({
name: "Anonymous",
_id: _id,
color: "#" + ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)),
flags: {}
});
user.save();
return user;
}
static async updateUser(_id, data) {
if (!this.userdb) {
await this.load();
}
let user = await UserModel.findOne({ _id: _id }).exec();
user.name = data.name;
user._id = data._id;
user.flags = data.flags;
user.color = data.color;
await user.save();
}
static async wipe() {
if (!this.userdb) {
await this.load();
}
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) {
if (!this.bandb) return this.load();
this.roomdb.del("room~" + _id);
}
static addIPBan(ip) {
if (!this.bandb) return this.load();
this.bandb.put("ipban~" + ip, true);
}
static removeIPBan(ip) {
if (!this.bandb) return this.load();
this.bandb.del("ipban~" + ip);
}
static isIPBanned(ip, cb) {
if (!this.bandb) {
// FIXME this was causing a crash :/ maybe it should be async instead of return false?
this.load();
return false;
}
this.roomdb.get("ipban~" + ip, (err, value) => {
if (err) {
return false;
}
console.log("ban:", value);
if (value == true) return true;
});
}
static utilSet(key, value) {
return this.utildb.put(key, value);
}
static utilGet(key) {
return this.utildb.get(key);
}
static utilDel(key) {
return this.utildb.del(key);
}
}
class RoomDataModel {
constructor(settings, chat) {
this.settings = settings;
this.chat = chat;
}
}
module.exports = Database;

View File

@ -1,301 +0,0 @@
const Logger = require("../Logger");
const Color = require("../Color");
const { Cow } = require("../Cow");
const Database = require("../Database");
class Command {
static commands = [];
static logger = new Logger("Command Handler");
static handleCommand(cl, ch, c, usedPrefix, args, argcat, p, isAdmin) {
for (let cmd of this.commands) {
let aliasCheck = false;
aliasLoop: for (let alias of cmd.aliases) {
if (c.toLowerCase() == alias.toLowerCase()) {
aliasCheck = true;
break aliasLoop;
}
}
if (!aliasCheck) continue;
if (!isAdmin && cmd.permLevel == "admin")
return ch.adminChat(
`You don't have permission to use this command.`
);
if (args.length - 1 < cmd.minargs)
return ch.adminChat(
`Not enough arguments. Usage: ${this.getUsage(
cmd.usage,
usedPrefix
)}`
);
try {
const out = cmd.func(cl, ch, {
c,
args,
argcat,
p,
isAdmin,
a: args.join(" ")
});
// console.log(out);
if (!out) return;
if (out !== "") {
ch.adminChat(out);
}
} catch (err) {
this.logger.error(err);
ch.adminChat(
`An error has occurred whilst performing this command.`
);
}
}
}
static addCommand(cmd) {
this.commands.push(cmd);
}
static getUsage(usa, pre) {
return usa.split("%P").join(pre);
}
constructor(id, aliases, desc, usage, minargs, func, permLevel) {
this.id = id;
this.aliases = aliases || [id];
this.desc = desc || "no description"; // brandon-like words
this.usage = usage || "no usage";
this.minargs = minargs;
this.func = func;
this.permLevel = permLevel || "admin"; // user / admin?
}
}
Command.addCommand(
new Command(
"ping",
["ping"],
undefined,
`%Pping`,
0,
(cl, ch, msg) => {
return `pong`;
},
"user"
)
);
Command.addCommand(
new Command(
"color",
["color", "setcolor", "colorset"],
undefined,
`%Pcolor [color] [userid]`,
0,
(cl, ch, msg) => {
if (!msg.isAdmin) {
ch.adminChat("You do not have permission to use this command.");
return;
}
let color = ch.verifyColor(msg.args[1]);
if (color) {
let c = new Color(color);
if (!msg.args[2]) {
cl.emit(
"color",
{ color: c.toHexa(), _id: cl.user._id },
true
);
ch.adminChat(
`Your color is now ${c
.getName()
.replace("A", "a")} [${c.toHexa()}]`
);
} else {
let winner = ch.server.getAllClientsByUserID(
msg.args[2]
)[0];
if (winner) {
cl.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 (" +
msg.args[2] +
") is not around."
);
}
}
} else {
ch.adminChat("Invalid color.");
}
ch.updateCh();
},
"user"
)
);
Command.addCommand(
new Command(
"chlist",
["chlist"],
undefined,
`%Pchlist`,
0,
(cl, ch, msg) => {
if (!msg.isAdmin) {
ch.adminChat("You do not have permission to use this command.");
return;
}
ch.adminChat("Channels:");
for (const cc of cl.server.channels.values()) {
ch.adminChat(`- ${cc._id}\n`);
}
},
"admin"
)
);
Command.addCommand(
new Command(
"cow",
["cow"],
undefined,
`%Pcow`,
0,
(cl, ch, msg) => {
const cow = new Cow();
return `Cow: ${cow.emoji}${cow.display_name}`;
},
"user"
)
);
Command.addCommand(
new Command(
"inventory",
["inventory", "inv"],
undefined,
`%Pinventory`,
0,
(cl, ch, msg) => {
if (cl.user.inventory) {
const items = Object.values(cl.user.inventory)
.map(
it =>
`${it.emoji ? it.emoji : ""}${it.display_name} (x${
it.count
})`
)
.join(", ")
.trim();
ch.adminChat(`Inventory: ${items == "" ? "(none)" : items}`);
}
},
"user"
)
);
Command.addCommand(
new Command(
"js",
["js"],
undefined,
`%Pjs`,
0,
(cl, ch, msg) => {
return cl.server.ev(msg.argcat);
},
"admin"
)
);
/*
Command.addCommand(
new Command(
"ip",
["ip"],
undefined,
"%Pip",
0,
(cl, ch, msg) => {
if (msg.args[1]) {
const winner = new Array(cl.server.connections.values()).find(
cl => {
if (!cl.user) return false;
console.log(cl.user._id);
return cl.user ? cl.user._id == msg.args[1] : false;
}
);
if (winner) {
cl.sendArray([
{
m: "a",
a: "IP: " + winner.ip,
p: {
name: "mpp",
color: "#ffffff",
_id: "0",
id: "0"
}
}
]);
} else {
cl.sendArray([
{
m: "a",
a: "No IP found.",
p: {
name: "mpp",
color: "#ffffff",
_id: "0",
id: "0"
}
}
]);
}
} else {
cl.sendArray([
{
m: "a",
a: "ip: " + cl.ip,
p: { name: "mpp", color: "#ffffff", _id: "0", id: "0" }
}
]);
}
},
"admin"
)
);
*/
Command.addCommand(
new Command(
"time",
["time"],
undefined,
`%Ptime`,
0,
(cl, ch, msg) => {
return `It is ${cl.server.cycle.getCurrentGenericTime()}.`;
},
"admin"
)
);
module.exports = { Command };

View File

@ -1,132 +0,0 @@
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 commands = [];
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;
let prefix = this.prefix;
Command.handleCommand(
cl,
ch,
cmd,
prefix,
args,
argcat,
p,
isAdmin
);
// 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.channels) {
// ch.adminChat(`- ${_id}`);
// }
// break;
// case "restart":
// if (!isAdmin) return;
// cl.server.restart();
// break;
// case "eval":
// case "javascript":
// case "js":
// if (!isAdmin) return;
// cl.server.ev(argcat);
// break;
// case "inventory":
// case "inv":
// if (cl.user.inventory) {
// ch.adminChat(`Inventory: ${Object.values(cl.user.inventory).map(it => `${it.display_name} (x${it.count})`)}`);
// } else {
// ch.adminChat(`Inventory: (empty)`);
// }
// break;
// }
});
}
}
InternalBot.bindEventListeners();
module.exports = {
InternalBot
};

View File

@ -1,5 +0,0 @@
const { InternalBot } = require("./InternalBot");
module.exports = {
InternalBot
}

View File

@ -1,6 +0,0 @@
const mongoose = require('mongoose');
module.exports = mongoose.model('Inventory', {
user_id: String,
items: Array
});

View File

@ -1,48 +0,0 @@
const chalk = require('chalk');
const { EventEmitter } = require('events');
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) {
this.context = context;
}
log(args) {
let str = chalk.green(`[`) + chalk.green(`${this.context}`) + chalk.green(`]`) + ' ' + args
console.log(str);
this.buffer(str);
}
warn(args) {
let str = chalk.yellow(`[WARN] [`) + chalk.yellow(`${this.context}`) + chalk.yellow(`]`) + ' ' + args;
console.warn(str);
this.buffer(str);
}
error(args) {
let str = chalk.red(`[ERR] [`) + chalk.red(`${this.context}`) + chalk.red(`]`) + ' ' + args;
console.error(str);
this.buffer(str);
}
debug(args) {
if (process.env.DEBUG_ENABLED) {
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;

View File

@ -1,103 +0,0 @@
const Holidays = require("date-holidays");
let hd = new Holidays();
hd.init("US");
let dayOfTheWeekMOTD = [
"Happy Sunday! (this message no longer only appears on Sunday)",
"You can chat with that thing.",
"I'm tired...",
"Don't forget to bring a towel!",
"Never lick a pole in winter.",
"Everyone loves a potato monkey!",
"Dear Mario: Please come to the castle. I've baked a cake for you. Yours truly-- Princess Toadstool",
"The MotD generator is broken. Come back tomorrow.",
"There's an earthbound battle background script in my frontend files.",
"Lapis doesn't own this!",
"https://youtube.com/hri7566",
"The most Brandon-like MPP",
'"penis" - Lapis',
'"I have an OnlyFans" - Nitsua',
"Sola's favorite site",
"This site has the most admin features out of any MPP.",
"This site is older than MPPClone.",
"Brandon used to manually IP ban proxy lists from Google.",
"Who else thinks Frackin Universe is overrated?",
"Where is Fennece?",
"Cosmic lives on the Chromatic Ribbon.",
"Watch out for raining crowns!",
"Tuesday is fishing day. (Monday if the fishing bot is in Germany)",
"At the time of writing, Chris Pratt is overrated.",
"MYHOUSE.WAD",
"Ling Ling 40 Hours",
"Do your job and watch new Mental Outlaw videos",
"Amazon's AWS is way too overpriced and too time consuming",
"This server has Brandon's official chat regex, courtesy of chacha.",
"Neovim is better than vscode.",
"All hail Cosmic",
"You can chat with this thing.",
"Don't let Foonix fall asleep in call again.",
"Firebase is trash.",
"distant cat sounds",
"NOBODY EXPECTS THE SPANISH INQUISITION!",
"Who will Billy Mitchell sue next?",
"ayy lmao",
"Khorne Bot",
"The greatest fantasy epic of our time.",
"Look out for a deranged hillbilly.",
"motd",
"Have you done today's wordle?",
"Give me the Nintendo.",
"Nutrition Facts",
"Staying safe online is an ever growing difficulty and you could be exploited by hackers. NordVPN allows you to change your IP address, making you harder to track, securing your privacy. Check out the link in the description to get 20% off for the first two months and thank you to NordVPN for sponsering this video.",
"MPPClone's profanity filter is a text file.",
"apple emoji",
"In a hole in the ground there lived a hobbit.",
"PROTEINS",
"Here are your bill details",
"Pentium FDIV bug",
""
];
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, get day
// let day = new Date().getDay();
let newYearsDay = new Date(new Date().getFullYear());
let differenceInTime =
new Date() -
newYearsDay +
(newYearsDay.getTimezoneOffset() -
new Date().getTimezoneOffset()) *
60 *
1000;
let oneDayInMS = 1000 * 60 * 60 * 24;
let dayOfYear = Math.ceil(differenceInTime / oneDayInMS);
dayOfYear %= 365;
return dayOfTheWeekMOTD[dayOfYear % dayOfTheWeekMOTD.length];
}
}
}
module.exports = {
MOTDGenerator
};

View File

@ -1,516 +0,0 @@
const Quota = require("./Quota");
const User = require("./User.js");
const Channel = require("./Channel.js");
const RoomSettings = require("./RoomSettings");
const Database = require("./Database");
const { MOTDGenerator } = require("./MOTDGenerator");
const Notification = require("./Notification");
module.exports = cl => {
cl.once("hi", (msg, admin) => {
if (msg.hasOwnProperty("password")) {
if (msg.password == "hideme") {
cl.hidden = true;
}
}
let m = {};
m.m = "hi";
m.motd = MOTDGenerator.getCurrentMOTD();
m.t = Date.now();
m.u = {
name: cl.user.name,
_id: cl.user._id,
id: cl.participantId,
color: cl.user.color
};
m.v = "2.0";
cl.sendArray([m]);
});
cl.on("t", msg => {
if (msg.hasOwnProperty("e") && !isNaN(msg.e))
cl.sendArray([
{
m: "t",
t: Date.now(),
e: msg.e
}
]);
});
cl.on("ch", msg => {
if (typeof msg.set !== "object") msg.set = {};
if (typeof msg._id == "string") {
if (msg._id.length > 512) return;
if (!cl.staticQuotas.room.attempt()) return;
cl.user.checkFlags();
cl.setChannel(msg._id, msg.set);
let param;
if (cl.channel.isLobby(cl.channel._id)) {
param = Quota.N_PARAMS_LOBBY;
} else {
if (!(cl.user._id == cl.channel.crown.userId)) {
param = Quota.N_PARAMS_NORMAL;
} else {
param = Quota.N_PARAMS_RIDICULOUS;
}
}
param.m = "nq";
setTimeout(() => {
cl.sendArray([param]);
}, 1000);
}
});
cl.on("m", (msg, admin) => {
// nobody will see our cursor if we're not somewhere
if (!("channel" in cl)) return;
// check against cursor rate limit
if (!cl.quotas.cursor.attempt() && !admin) return;
// if we are nobody, we don't have a cursor
if (!cl.participantId) return;
// no values? null, not undefined
if (!msg.hasOwnProperty("x")) msg.x = null;
if (!msg.hasOwnProperty("y")) msg.y = null;
if (isNaN(parseFloat(msg.x))) msg.x = null;
if (isNaN(parseFloat(msg.y))) msg.y = null;
let m = {
p: cl.participantId,
x: msg.x,
y: msg.y
};
cl.channel.emit("m", m);
});
cl.on("chown", (msg, admin) => {
if (!cl.quotas.chown.attempt() && !admin) return;
if (!(cl.channel && cl.participantId)) return;
//console.log((Date.now() - cl.channel.crown.time))
//console.log(!(cl.channel.crown.userId != cl.user._id), !((Date.now() - cl.channel.crown.time) > 15000));
if (!cl.channel.crown && !admin) {
if (
!(cl.channel.crown.userId == cl.user._id) &&
!(Date.now() - cl.channel.crown.time > 15000)
)
return;
}
if (msg.hasOwnProperty("id")) {
// console.log(cl.channel.crown)
if (!admin) {
if (
cl.user._id == cl.channel.crown.userId ||
cl.channel.crowndropped
) {
cl.channel.chown(msg.id);
if (msg.id == cl.user.id) {
param = Quota.N_PARAMS_RIDICULOUS;
param.m = "nq";
cl.sendArray([param]);
}
}
} else {
cl.channel.chown(msg.id);
if (msg.id == cl.user.id) {
param = Quota.N_PARAMS_RIDICULOUS;
param.m = "nq";
cl.sendArray([param]);
}
}
} else {
if (!admin) {
if (
cl.user._id == cl.channel.crown.userId ||
cl.channel.crowndropped
) {
cl.channel.chown();
param = Quota.N_PARAMS_NORMAL;
param.m = "nq";
cl.sendArray([param]);
}
} else {
cl.channel.chown();
param = Quota.N_PARAMS_RIDICULOUS;
param.m = "nq";
cl.sendArray([param]);
}
}
});
cl.on("chset", (msg, admin) => {
if (!(cl.channel && cl.participantId)) return;
if (!cl.channel.crown && !admin) return;
if (!admin) {
if (!(cl.user._id == cl.channel.crown.userId)) return;
}
if (!msg.hasOwnProperty("set") || !msg.set)
msg.set = new RoomSettings(cl.channel.settings, "user");
cl.channel.settings.changeSettings(msg.set, admin);
// cl.channel.updateCh();
cl.channel.emit("update");
});
cl.on("a", (msg, admin) => {
if (!(cl.channel && cl.participantId)) return;
if (cl.user.flags["cant_chat"]) return;
if (!msg.hasOwnProperty("message")) return;
if (typeof msg.message !== "string") return;
if (cl.channel.settings.chat) {
if (admin && msg.admin == true) {
cl.channel.adminChat(msg.message);
} else {
if (cl.channel.isLobby(cl.channel._id)) {
if (
!cl.quotas.chat.lobby.attempt() &&
!admin &&
!cl.user.hasFlag("no chat rate limit", true)
)
return;
} else {
if (!(cl.user._id == cl.channel.crown.userId)) {
if (
!cl.quotas.chat.normal.attempt() &&
!admin &&
!cl.user.hasFlag("no chat rate limit", true)
)
return;
} else {
if (
!cl.quotas.chat.insane.attempt() &&
!admin &&
!cl.user.hasFlag("no chat rate limit", true)
)
return;
}
}
cl.channel.emit("a", cl, msg);
}
}
});
cl.on("n", (msg, admin) => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("t") || !msg.hasOwnProperty("n")) return;
if (typeof msg.t != "number" || typeof msg.n != "object") return;
// if (cl.quotas.note && !admin) {
// if (!cl.quotas.note.attempt()) return;
// }
if (cl.channel.settings.crownsolo) {
if (
cl.channel.crown.userId == cl.user._id &&
!cl.channel.crowndropped
) {
cl.channel.playNote(cl, msg);
}
} else {
cl.channel.playNote(cl, msg);
}
});
cl.on("+ls", msg => {
if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.set(cl.connectionid, cl);
let rooms = [];
for (let room of Array.from(cl.server.channels.values())) {
let data = room.fetchChannelData().ch;
if (room.bans.get(cl.user._id)) {
data.banned = true;
}
if (room.settings.visible) rooms.push(data);
}
cl.sendArray([
{
m: "ls",
c: true,
u: rooms
}
]);
});
cl.on("-ls", msg => {
if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.delete(cl.connectionid);
});
cl.on("userset", (msg, admin) => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {};
if (msg.set.hasOwnProperty("name") && typeof msg.set.name == "string") {
cl.userset(msg.set.name, admin);
}
});
cl.on("kickban", (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;
let ms = msg.ms || 36e5;
cl.channel.kickban(_id, ms);
}
});
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.user.stopFlagEvents();
cl.destroy();
});
cl.on("admin message", msg => {
// if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("password") || !msg.hasOwnProperty("msg"))
return;
if (typeof msg.msg != "object") return;
if (msg.password !== cl.server.adminpass) return;
cl.ws.emit("message", JSON.stringify([msg.msg]), true);
});
//admin only stuff
// TODO move all admin messages to their own stream
cl.on("color", (msg, admin) => {
if (!admin) return;
if (!msg.color) return;
// if (typeof cl.channel.verifyColor(msg.color) != 'string') return;
if (!msg.hasOwnProperty("id") && !msg.hasOwnProperty("_id")) return;
cl.server.connections.forEach(c => {
if (c.destroied) return;
if (c.user._id !== msg._id && c.participantId !== msg.id) return;
c.user.color = msg.color;
require("./Database").updateUser(c.user._id, c.user);
cl.channel.updateParticipant(c.user._id, c.user);
});
});
cl.on("eval", (msg, admin) => {
if (!admin) return;
if (!msg.hasOwnProperty("str")) return;
cl.server.ev(msg.str);
});
cl.on("notification", (msg, admin) => {
if (!admin) return;
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;
let chat = msg.chat;
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;
}
new Notification(cl.server, {
cl,
id,
targetChannel,
targetUser,
target,
class: klass,
title,
text,
html,
chat,
duration
}).send();
});
cl.on("user_flag", (msg, admin) => {
if (!admin) return;
if (
!msg.hasOwnProperty("_id") ||
!msg.hasOwnProperty("key") ||
!msg.hasOwnProperty("value")
)
return;
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;
if (msg.key == "remove") {
delete usr.user.flags[msg.key];
usr.user.flags[msg.key] = undefined;
return;
}
usr.user.flags[msg.key] = msg.value;
Database.updateUser(usr.user._id, usr.user);
usr.user.checkFlags();
}
});
});
cl.on("channel_flag", (msg, admin) => {
if (!admin) return;
if (
!msg.hasOwnProperty("_id") ||
!msg.hasOwnProperty("key") ||
!msg.hasOwnProperty("value")
)
return;
try {
let ch = cl.server.channels.get(msg._id);
ch.flags[msg.key] = msg.value;
ch.emit("flag " + msg.key, msg.value);
} catch (err) {
console.error(err);
}
});
cl.on("room_flag", (msg, admin) => {
if (!admin) return;
cl.emit("channel_flag", msg, admin);
});
cl.on("clear_chat", (msg, admin) => {
if (!admin) return;
cl.channel.setChatArray([]);
});
cl.on("sudo", (msg, admin) => {
if (!admin) return;
if (typeof msg._id !== "string") return;
if (typeof msg.msg !== "object") return;
if (!msg.msg.m) return;
cl.server.connections.forEach(c => {
if (c.user._id !== msg._id) return;
c.emit(msg.msg.m, msg.msg);
});
});
cl.on("subscribe to admin stream", (msg, admin) => {
// if (!admin) return;
if (!("password" in msg)) return;
if (msg.password !== cl.server.adminpass) return;
cl.isSubscribedToAdminStream = true;
let interval = 8000;
if ("interval_ms" in msg) interval = msg["interval_ms"];
cl.sendAdminData();
cl.adminStreamInterval = setInterval(() => {
if (cl.isSubscribedToAdminStream == true) cl.sendAdminData();
}, interval);
});
cl.on("unsubscribe from admin stream", (msg, admin) => {
// if (!admin) return;
if (!("password" in msg)) return;
if (msg.password !== cl.server.adminpass) return;
cl.isSubscribedToAdminStream = false;
if (cl.adminStreamInterval) {
clearInterval(cl.adminStreamInterval);
cl.adminStreamInterval = undefined;
while (cl.adminStreamInterval !== undefined) {
cl.adminStreamInterval = undefined;
}
}
});
cl.on("channel message", (msg, admin) => {
if (!admin) return;
if (!msg.hasOwnProperty("msg")) return;
if (typeof msg.msg != "object") return;
if (typeof msg.msg.m != "string") return;
if (!cl.channel) return;
if (!msg.hasOwnProperty("_id")) msg._id = cl.channel._id;
let ch = cl.server.channels.get(msg._id);
if (!ch) return;
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);
});
cl.on("ipban", (msg, admin) => {
if (!admin) return;
if (!msg.ip) return;
cl.server.banIP(msg.ip);
});
cl.on("ipunban", (msg, admin) => {
if (!admin) return;
if (!msg.ip) return;
cl.server.unbanIP(msg.ip);
});
};

View File

@ -1,85 +0,0 @@
const Server = require("./Server");
module.exports = class Notification {
constructor(server, data) {
this.server = server;
this.cl = data.cl;
this.id = data.id;
this.title = data.title;
this.text = data.text;
this.html = data.html;
this.target = data.target;
this.duration = data.duration;
this.class = data.class;
this.targetChannel = data.targetChannel;
this.targetUser = data.targetUser;
this.chat = data.chat;
}
send() {
let msg = {
m: "notification",
id: this.id,
title: this.title,
text: this.text,
html: this.html,
target: this.target,
duration: this.duration,
class: this.class
};
// Object.assign(msg, this);
const targets = [];
if (this.targetChannel) {
switch (this.targetChannel) {
case "all":
// every channel
for (const cl of this.server.connections.values()) {
targets.push(cl);
}
break;
case "room":
case "channel":
// current channel
if (!this.cl) break;
if (!this.cl.channel) break;
for (const cl of this.server.connections.values()) {
if (!cl.channel) continue;
if (cl.channel._id == this.cl.channel._id) {
targets.push(cl);
}
}
break;
default:
// specific channel
for (const cl of this.server.connections.values()) {
if (!cl.channel) continue;
if (cl.channel._id == this.targetChannel) {
targets.push(cl);
}
}
break;
}
}
if (!this.chat) {
for (const cl of targets) {
if (this.targetUser) {
if (!cl.user) continue;
if (
cl.user._id == this.targetUser ||
cl.participantId == this.targetUser
)
cl.sendArray([msg]);
} else {
cl.sendArray([msg]);
}
}
} else {
this.cl.sendChat(this.text);
}
}
};

View File

@ -1,173 +0,0 @@
//Adaptation of https://gist.github.com/brandon-lockaby/7339587 into modern javascript.
/*
class RateLimit {
constructor(interval_ms) {
this._interval_ms = interval_ms || 0; // (0 means no limit)
this._after = 0;
}
attempt(time) {
var time = time || Date.now();
if(time < this._after) return false;
this._after = time + this._interval_ms;
return true;
};
interval(interval_ms) {
this._after += interval_ms - this._interval_ms;
this._interval_ms = interval_ms;
};
}
class RateLimitChain(num, interval_ms) {
constructor(num, interval_ms) {
this.setNumAndInterval(num, interval_ms);
}
attempt(time) {
var time = time || Date.now();
for(var i = 0; i < this._chain.length; i++) {
if(this._chain[i].attempt(time)) return true;
}
return false;
};
setNumAndInterval(num, interval_ms) {
this._chain = [];
for(var i = 0; i < num; i++) {
this._chain.push(new RateLimit(interval_ms));
}
};
}*/
class Quota {
constructor(params, cb) {
this.cb = cb;
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;
var max = params.max || this.max || Quota.PARAMS_OFFLINE.max;
var maxHistLen = params.maxHistLen || this.maxHistLen || Quota.PARAMS_OFFLINE.maxHistLen;
let interval = params.interval || 0;
clearInterval(this.interval);
this.interval = setInterval(() => {
this.tick();
}, params.interval)
if (allowance !== this.allowance || max !== this.max || maxHistLen !== this.maxHistLen) {
this.allowance = allowance;
this.max = max;
this.maxHistLen = maxHistLen;
this.resetPoints();
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);
this.history.length = this.maxHistLen;
// hook a brother up with some more quota
if (this.points < this.max) {
this.points += this.allowance;
if (this.points > this.max) this.points = this.max;
// fire callback
if (this.cb) this.cb(this.points);
}
}
spend(needed) {
// check whether aggressive limitation is needed
var sum = 0;
for (var i in this.history) {
sum += this.history[i];
}
if (sum <= 0) needed *= this.allowance;
// can they afford it? spend
if (this.points < needed) {
return false;
} else {
this.points -= needed;
if (this.cb) this.cb(this.points); // fire callback
return true;
}
}
}
module.exports = Quota

View File

@ -1,40 +0,0 @@
var RateLimit = function(interval_ms) {
this._interval_ms = interval_ms || 0; // (0 means no limit)
this._after = 0;
};
RateLimit.prototype.attempt = function(time) {
var time = time || Date.now();
if(time < this._after) return false;
this._after = time + this._interval_ms;
return true;
};
RateLimit.prototype.setInterval = function(interval_ms) {
this._after += interval_ms - this._interval_ms;
this._interval_ms = interval_ms;
};
var RateLimitChain = function(num, interval_ms) {
this.setNumAndInterval(num, interval_ms);
};
RateLimitChain.prototype.attempt = function(time) {
var time = time || Date.now();
for(var i = 0; i < this._chain.length; i++) {
if(this._chain[i].attempt(time)) return true;
}
return false;
};
RateLimitChain.prototype.setNumAndInterval = function(num, interval_ms) {
this._chain = [];
for(var i = 0; i < num; i++) {
this._chain.push(new RateLimit(interval_ms));
}
};
var exports = typeof module !== "undefined" ? module.exports : this;
exports.RateLimit = RateLimit;
exports.RateLimitChain = RateLimitChain;

View File

@ -1,212 +0,0 @@
const config = require("../config");
class RoomSettings {
static allowedProperties = {
color: {
type: "color",
default: config.defaultRoomSettings.color,
allowedChange: true,
required: true
},
color2: {
type: "color2",
default: config.defaultRoomSettings.color2,
allowedChange: true,
required: false
},
lobby: {
type: "boolean",
allowedChange: false,
required: false
},
visible: {
type: "boolean",
default: true,
allowedChange: true,
required: true
},
chat: {
type: "boolean",
default: true,
allowedChange: true,
required: true
},
owner_id: {
type: "string",
allowedChange: false,
required: false
},
crownsolo: {
type: "boolean",
default: false,
allowedChange: true,
required: true
},
"no cussing": {
type: "boolean",
allowedChange: true,
required: false
},
"lyrical notes": {
type: "boolean",
allowedChange: false,
required: false
}
};
constructor(set, context) {
Object.keys(RoomSettings.allowedProperties).forEach(key => {
if (
typeof RoomSettings.allowedProperties[key].default !==
"undefined"
) {
if (this[key] !== RoomSettings.allowedProperties[key].default) {
this[key] = RoomSettings.allowedProperties[key].default;
}
}
});
Object.keys(RoomSettings.allowedProperties).forEach(key => {
if (RoomSettings.allowedProperties[key].required == true) {
if (typeof this[key] == "undefined") {
this[key] = RoomSettings.allowedProperties[key].default;
}
}
});
if (typeof set !== "undefined") {
Object.keys(set).forEach(key => {
if (typeof set[key] == "undefined") return;
if (
Object.keys(RoomSettings.allowedProperties).indexOf(key) !==
-1
) {
if (typeof context == "undefined") {
this[key] = this.verifyPropertyType(
key,
set[key],
RoomSettings.allowedProperties[key].type
);
} else {
if (context == "user") {
if (
RoomSettings.allowedProperties[key]
.allowedChange
) {
this[key] = this.verifyPropertyType(
key,
set[key],
RoomSettings.allowedProperties[key].type
);
}
}
}
}
});
}
}
verifyPropertyType(key, pr, type) {
let ret;
if (typeof RoomSettings.allowedProperties[key] !== "object") return;
switch (type) {
case "color":
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
case "color2":
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
default:
if (typeof pr == type) {
ret = pr;
} else if (
typeof RoomSettings.allowedProperties[key].default !==
"undefined"
) {
ret = RoomSettings.allowedProperties[key].default;
} else {
ret = undefined;
}
break;
}
return ret;
}
changeSettings(set) {
Object.keys(set).forEach(key => {
if (RoomSettings.allowedProperties[key].allowedChange) {
this[key] = this.verifyPropertyType(
key,
set[key],
RoomSettings.allowedProperties[key].type
);
}
});
}
static changeSettings(set, admin) {
Object.keys(set).forEach(key => {
if (
RoomSettings.allowedProperties[key].allowedChange ||
admin == true
) {
set[key] = RoomSettings.verifyPropertyType(
key,
set[key],
RoomSettings.allowedProperties[key].type
);
}
});
return set;
}
static verifyPropertyType(key, pr, type) {
let ret;
if (typeof RoomSettings.allowedProperties[key] !== "object") return;
switch (type) {
case "color":
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
case "color2":
if (/^#[0-9a-f]{6}$/i.test(pr)) {
ret = pr;
} else {
ret = RoomSettings.allowedProperties[key].default;
}
break;
default:
if (typeof pr == type) {
ret = pr;
} else if (
typeof RoomSettings.allowedProperties[key].default !==
"undefined"
) {
ret = RoomSettings.allowedProperties[key].default;
} else {
ret = undefined;
}
break;
}
return ret;
}
}
module.exports = RoomSettings;

View File

@ -1,230 +0,0 @@
const Client = require("./Client.js");
const banned = require("../banned.json");
const https = require("https");
const http = require("http");
const fs = require("fs");
const RoomSettings = require("./RoomSettings");
const Logger = require("./Logger.js");
const Notification = require("./Notification");
const Database = require("./Database.js");
class Server {
static on = EventEmitter.prototype.on;
static off = EventEmitter.prototype.off;
static emit = EventEmitter.prototype.emit;
static once = EventEmitter.prototype.once;
static startTime = Date.now();
static start(config) {
// super();
// EventEmitter.call(this);
this.logger = new Logger("Server");
if (config.ssl == "true") {
this.https_server = https.createServer({
key: fs.readFileSync("ssl/privkey.pem", "utf8"),
cert: fs.readFileSync("ssl/cert.pem"),
ca: fs.readFileSync("ssl/chain.pem")
});
this.wss = new WebSocket.Server({
server: this.https_server,
backlog: 100,
verifyClient: info => {
const ip = info.req.connection.remoteAddress.replace(
"::ffff:",
""
);
if (
!ip.match(
/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/gi
)
)
return false;
if (banned.includes(ip)) return false;
return true;
}
});
this.https_server.listen(config.port, "0.0.0.0");
} else {
this.wss = new WebSocket.Server({
port: config.port,
backlog: 100,
verifyClient: info => {
const ip = info.req.connection.remoteAddress.replace(
"::ffff:",
""
);
if (banned.includes(ip)) return false;
if (Database.isIPBanned(ip)) return false;
return true;
}
});
}
this.defaultUsername = config.defaultUsername;
this.defaultRoomSettings = new RoomSettings(config.defaultRoomSettings);
this.lobbySettings = new RoomSettings(config.defaultRoomSettings);
this.lobbySettings.lobby = true;
this.lobbySettings.color = config.defaultLobbyColor || "#9900ff";
this.lobbySettings.color2 = config.defaultLobbyColor2 || "#9900ff";
this.logger.log(`Server started on port ${config.port}`);
this.connectionid = 0;
this.connections = new Map();
this.roomlisteners = new Map();
this.channels = new Map();
this.cycle = require("./cycle");
this.specialIntervals = {};
this.wss.on("connection", (ws, req) => {
// console.log("socket connected");
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",
"unban",
"admin message",
"color",
"eval",
"notification",
"user_flag",
"room_flag",
"clear_chat",
"sudo",
"subscribe to admin stream",
"unsubscribe from admin stream",
"data",
"channel message",
"channel_flag",
"name",
"restart",
"ipban",
"ipunban"
];
// this.welcome_motd = config.motd || "You agree to read this message.";
this._id_Private_Key = config._id_PrivateKey || "amogus";
this.adminpass = config.adminpass || "123123sucks";
}
static updateChannelList(channelDataArray) {
const listData = [];
for (let chm of Object.values(channelDataArray)) {
if (!chm.ch.settings.visible) return;
listData.push(chm.ch);
}
for (let cl of Array.from(this.roomlisteners.values())) {
if (cl.destroied) {
cl = undefined;
return;
}
for (const ch of Object.values(listData)) {
const c = this.channels.get(ch._id);
if (!c) continue;
ch.banned = typeof c.bans.get(cl.user._id) !== "undefined";
}
cl.sendArray([
{
m: "ls",
c: false,
u: listData
}
]);
}
}
static ev(str) {
let out = "";
try {
out = eval(str);
} catch (err) {
out = err;
}
// console.log(out);
return `(${typeof out}) ${out}`;
}
static getClient(id) {
return this.connections.get(id);
}
static getClientByParticipantID(id) {
for (let cl of Array.from(this.connections.values())) {
if (cl.participantID == id) return cl;
}
return null;
}
static getAllClientsByUserID(_id) {
let out = [];
for (let cl of Array.from(this.connections.values())) {
if (cl.user._id == _id) out.push(cl);
}
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",
targetChannel: "all"
}
) {
let n = new Notification(this, notif);
n.send();
setTimeout(() => {
process.exit();
}, n.duration || 20000);
}
static banIP(ip) {
Database.addIPBan(ip);
for (const cl of this.connections.values()) {
if (cl.ip == ip) {
cl.destroy();
}
}
}
static unbanIP(ip) {
Database.removeIPBan(ip);
}
}
module.exports = Server;

View File

@ -1,131 +0,0 @@
const Database = require("./Database");
const { Cow } = require("./Cow");
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.name = data.name;
this.cl = cl;
this._id = data._id;
this.color = data.color;
this.flags =
typeof data.flags == "object"
? data.flags
: {
volume: 100,
"no chat rate limit": false,
freeze_name: false
};
this.inventory = {};
}
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);
for (const ch of this.cl.server.channels.values()) {
if (ch.hasUser(this.cl.id)) {
ch.updateParticipant(this._id, this);
}
}
},
1000 / 15
);
}
} else if (this.hasFlag("rainbow", false)) {
this.stopFlagEvents();
}
}
stopFlagEvents() {
let ints = this.cl.server.specialIntervals[this._id];
if (!ints) {
this.cl.server.specialIntervals[this._id] = {};
ints = this.cl.server.specialIntervals[this._id];
}
if ("rainbow" in ints) {
clearInterval(this.cl.server.specialIntervals[this._id].rainbow);
delete 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;
}
setFlag(flag, val) {
if (typeof this.flags[flag] == "undefined") {
this.flags[flag] = val;
}
}
static updateUserModel(cl, user) {
let u2 = new User(cl, user);
if (typeof u2 == "undefined") return;
for (let id in Object.keys(u2)) {
if (!user.hasOwnProperty(id)) {
user[id] = u2[id];
}
}
}
}
module.exports = User;

View File

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

View File

@ -1,37 +0,0 @@
const Database = require("./Database");
module.exports = class Cycle {
static startOfDay = 0;
static time = this.startOfDay;
static endOfDay = 24;
static cycleInterval = setInterval(() => {
try {
this.time = Database.utilGet("time");
} catch (err) {
this.time = 0;
}
this.time++;
if (this.time > this.endOfDay) {
this.time = this.startOfDay;
}
Database.utilSet("time", this.time);
}, 60 * 1000);
static getCurrentTime() {
return this.time;
}
static getCurrentGenericTime() {
if (this.time < 2) return "late night";
if (this.time < 6) return "dawn";
if (this.time < 12) return "morning";
if (this.time < 14) return "day";
if (this.time < 18) return "evening";
if (this.time < 20) return "dusk";
return "night";
}
};

View File

@ -1,131 +0,0 @@
// var Client = require("../multiplayerpiano/static/Client.js");
const Client = require("../../mpp.hri7566.info/Client.js");
var level = require("level");
var fs = require("fs");
var crypto = require("crypto");
process.stdout.write(
"\n********************************START********************************\n"
);
// var client = new Client("wss://www.multiplayerpiano.com");
var client = new Client("wss://mpp.hri7566.info:8443");
// var client = new Client("ws://127.0.0.1:8443");
client.on("connect", function () {
console.log("connected");
});
client.on("hi", function () {
console.log("hi");
fs.readFile("./password.txt", function (err, data) {
if (err) throw err;
var password = new String(data).trim();
client.sendArray([
{
m: "subscribe to admin stream",
password: password,
interval_ms: 10000000
}
]);
var BATTLE_CHANNEL = "test/:)";
var BATTLE_DURATION = 7000;
function spoop_text(message) {
var old = message;
message = "";
for (var i = 0; i < old.length; i++) {
if (Math.random() < 0.9) {
message += String.fromCharCode(
old.charCodeAt(i) + Math.floor(Math.random() * 20 - 10)
);
//message[i] = String.fromCharCode(Math.floor(Math.random() * 255));
} else {
message += old[i];
}
}
return message;
}
client.on("data", function (msg) {
console.log("data");
for (var i = 0; i < msg.channelManager.channels.length; i++) {
var channel = msg.channelManager.channels[i];
if (channel._id == BATTLE_CHANNEL) {
console.log("sending messages");
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "notification",
id: "ebbattle",
targetChannel: client.channel._id,
duration: "7000",
class: "short",
html: `<p></p>`
}
}
]);
setTimeout(() => {
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "notification",
id: "ebbattle",
targetChannel: client.channel._id,
duration: "7000",
class: "short",
html:
`<script>` +
stop.toString() +
`</script>`
}
}
]);
}, BATTLE_DURATION);
}
}
});
});
});
function start() {
var ebcanv = `<canvas id="ebbattle" style="
padding: 0;
margin: 0;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
image-rendering: pixelated;
">`;
$("body").append(ebcanv);
/*
var canvas = document.getElementById("ebbattle");
var ctx = canvas.getContext("2d");
*/
globalThis.params = {
layer1: 182,
layer2: 181
};
var ebbattlescript = document.createElement("script");
ebbattlescript.src = "ebbattle/index.js";
ebbattlescript.type = "module";
ebbattlescript.module = true;
console.log(ebbattlescript);
$("head").append(ebbattlescript);
}
client.start();
function stop() {
window.location.reload();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,192 +0,0 @@
// var Client = require("../multiplayerpiano/static/Client.js");
const Client = require("../../mpp.hri7566.info/Client.js");
var level = require("level");
var fs = require("fs");
var crypto = require("crypto");
process.stdout.write(
"\n********************************START********************************\n"
);
// var client = new Client("wss://www.multiplayerpiano.com");
var client = new Client("wss://mpp.hri7566.info:8443");
client.start();
client.on("connect", function () {
console.log("connected");
});
client.on("hi", function () {
fs.readFile("./password.txt", function (err, data) {
if (err) throw err;
var password = new String(data).trim();
client.sendArray([
{
m: "subscribe to admin stream",
password: password,
interval_ms: 10000000
}
]);
var SPOOP_CHANNEL = "test/:)";
var SPOOP_DURATION = 7000;
function spoop_text(message) {
var old = message;
message = "";
for (var i = 0; i < old.length; i++) {
if (Math.random() < 0.9) {
message += String.fromCharCode(
old.charCodeAt(i) + Math.floor(Math.random() * 20 - 10)
);
//message[i] = String.fromCharCode(Math.floor(Math.random() * 255));
} else {
message += old[i];
}
}
return message;
}
client.on("data", function (msg) {
console.log("data");
for (var i = 0; i < msg.channelManager.channels.length; i++) {
var channel = msg.channelManager.channels[i];
if (1) {
//if(channel._id === SPOOP_CHANNEL) {
var participants = channel.participants;
var users = {};
for (var j = 0; j < participants.length; j++) {
var part = participants[j];
users[part.user._id] = part.user;
}
for (var j in users) {
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "name",
_id: users[j]._id,
name: spoop_text(users[j].name)
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "color",
_id: users[j]._id,
color: "#000000"
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "user_flag",
_id: users[j]._id,
key: "chat_curse_1",
value: 1
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "user_flag",
_id: users[j]._id,
key: "chat_curse_2",
value: 1
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "user_flag",
_id: users[j]._id,
key: "freeze_name",
value: 1
}
}
]);
/*client.sendArray([{m: "admin message", password: password,
msg: {"m": "notification", "class":"short","targetChannel":SPOOP_CHANNEL,"html":"<style>.cursor{width:100000px;height:100000px;margin-left:-50000px;margin-top:-50000px}</style>","duration":SPOOP_DURATION}}]);*/
}
setTimeout(function () {
for (var j in users) {
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "name",
_id: users[j]._id,
name: users[j].name
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "color",
_id: users[j]._id,
color: users[j].color
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "user_flag",
_id: users[j]._id,
key: "chat_curse_1",
value: 0
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "user_flag",
_id: users[j]._id,
key: "chat_curse_2",
value: 0
}
}
]);
client.sendArray([
{
m: "admin message",
password: password,
msg: {
m: "user_flag",
_id: users[j]._id,
key: "freeze_name",
value: 0
}
}
]);
}
setTimeout(function () {
process.exit();
}, 1000);
}, SPOOP_DURATION);
}
}
});
});
});

View File

@ -1,3 +1,4 @@
export class Gateway {
public hasProcessedHi: boolean = false;
public hasSentDevices: boolean = false;
}

View File

@ -1,5 +1,4 @@
import { createColor, createID, createUserID } from "../util/id";
import { decoder, encoder } from "../util/helpers";
import EventEmitter from "events";
import {
ChannelInfo,
@ -15,8 +14,12 @@ import { loadConfig } from "../util/config";
import { Gateway } from "./Gateway";
import { Channel, channelList } from "../channel/Channel";
import { ServerWebSocket } from "bun";
import { findSocketByUserID, socketsBySocketID } from "./server";
import { socketsBySocketID } from "./server";
import { Logger } from "../util/Logger";
import { RateLimitConstructorList, RateLimitList } from "./ratelimit/config";
import { adminLimits } from "./ratelimit/limits/admin";
import { userLimits } from "./ratelimit/limits/user";
import { NoteQuota } from "./ratelimit/NoteQuota";
interface UsersConfig {
defaultName: string;
@ -42,6 +45,9 @@ export class Socket extends EventEmitter {
public gateway = new Gateway();
public rateLimits: RateLimitList | undefined;
public noteQuota = new NoteQuota(this.onQuota);
public desiredChannel: {
_id: string | undefined;
set: Partial<ChannelSettings> | undefined;
@ -88,6 +94,12 @@ export class Socket extends EventEmitter {
}
this.loadUser();
// TODO Permissions
let isAdmin = false;
this.setRateLimits(isAdmin ? adminLimits : userLimits);
this.bindEventListeners();
}
@ -330,4 +342,31 @@ export class Socket extends EventEmitter {
]);
}
}
public setRateLimits(list: RateLimitConstructorList) {
this.rateLimits = {
normal: {},
chains: {}
} as RateLimitList;
for (const key of Object.keys(list.normal)) {
(this.rateLimits.normal as any)[key] = (list.normal as any)[key]();
}
for (const key of Object.keys(list.chains)) {
(this.rateLimits.chains as any)[key] = (list.chains as any)[key]();
}
// Send note quota
this.sendArray([
{
m: "nq",
allowance: this.noteQuota.allowance,
max: this.noteQuota.max,
maxHistLen: this.noteQuota.maxHistLen
}
]);
}
public onQuota(points: number) {}
}

View File

@ -3,7 +3,8 @@ import { ServerEventListener } from "../../../../util/types";
export const a: ServerEventListener<"a"> = {
id: "a",
callback: (msg, socket) => {
// Send chat message
// Chat message
if (!socket.rateLimits?.normal.a.attempt()) return;
const ch = socket.getCurrentChannel();
if (!ch) return;

View File

@ -3,6 +3,8 @@ import { ServerEventListener } from "../../../../util/types";
export const devices: ServerEventListener<"devices"> = {
id: "devices",
callback: (msg, socket) => {
// List of MIDI Devices
if (socket.gateway.hasSentDevices) return;
socket.gateway.hasSentDevices = true;
}
};

View File

@ -3,7 +3,9 @@ import { ServerEventListener } from "../../../../util/types";
export const hi: ServerEventListener<"hi"> = {
id: "hi",
callback: (msg, socket) => {
// Handshake message
// TODO Hi message tokens
if (socket.gateway.hasProcessedHi) return;
let part = socket.getParticipant();
if (!part) {
@ -28,5 +30,7 @@ export const hi: ServerEventListener<"hi"> = {
}
}
]);
socket.gateway.hasProcessedHi = true;
}
};

View File

@ -3,6 +3,8 @@ import { ServerEventListener } from "../../../../util/types";
export const m: ServerEventListener<"m"> = {
id: "m",
callback: (msg, socket) => {
// Cursor movement
if (!socket.rateLimits?.normal.m.attempt()) return;
if (!msg.x || !msg.y) return;
socket.setCursorPos(msg.x, msg.y);
}

View File

@ -4,6 +4,8 @@ export const userset: ServerEventListener<"userset"> = {
id: "userset",
callback: (msg, socket) => {
// Change username/color
if (!socket.rateLimits?.chains.userset.attempt()) return;
if (!msg.set.name && !msg.set.color) return;
socket.userset(msg.set.name, msg.set.color);
}
};

View File

@ -0,0 +1,105 @@
export class NoteQuota {
public allowance = 8000;
public max = 24000;
public maxHistLen = 3;
public points = 24000;
public history = new Array<number>();
public static PARAMS_LOBBY = { allowance: 200, max: 600 };
public static PARAMS_NORMAL = { allowance: 400, max: 1200 };
public static PARAMS_RIDICULOUS = { allowance: 600, max: 1800 };
public static PARAMS_OFFLINE = {
allowance: 8000,
max: 24000,
maxHistLen: 3
};
constructor(public cb: (points: number) => void) {
this.setParams();
this.resetPoints();
}
public getParams() {
return {
m: "nq",
allowance: this.allowance,
max: this.max,
maxHistLen: this.maxHistLen
};
}
public setParams(
params: {
allowance: number;
max: number;
maxHistLen: number;
} = NoteQuota.PARAMS_OFFLINE
) {
let allowance: number =
params.allowance ||
this.allowance ||
NoteQuota.PARAMS_OFFLINE.allowance;
let max = params.max || this.max || NoteQuota.PARAMS_OFFLINE.max;
let maxHistLen =
params.maxHistLen ||
this.maxHistLen ||
NoteQuota.PARAMS_OFFLINE.maxHistLen;
if (
allowance !== this.allowance ||
max !== this.max ||
maxHistLen !== this.maxHistLen
) {
this.allowance = allowance;
this.max = max;
this.maxHistLen = maxHistLen;
this.resetPoints();
return true;
}
return false;
}
public resetPoints() {
this.points = this.max;
this.history = [];
for (let i = 0; i < this.maxHistLen; i++) {
this.history.unshift(this.points);
}
if (this.cb) {
this.cb(this.points);
}
}
public tick() {
this.history.unshift(this.points);
this.history.length = this.maxHistLen;
if (this.points < this.max) {
this.points += this.allowance;
if (this.points > this.max) this.points = this.max;
if (this.cb) this.cb(this.points);
}
}
public spend(needed: number) {
let sum = 0;
for (const i in this.history) {
sum += this.history[i];
}
if (sum <= 0) needed *= this.allowance;
if (this.points < needed) {
return false;
} else {
this.points -= needed;
if (this.cb) this.cb(this.points);
return true;
}
}
}

View File

@ -0,0 +1,16 @@
export class RateLimit {
public after: number = 0;
constructor(private interval_ms: number = 0) {}
public attempt(time: number = Date.now()) {
if (time < this.after) return false;
this.after = time + this.interval_ms;
return true;
}
public setInterval(interval_ms: number) {
this.after += interval_ms - this.interval_ms;
this.interval_ms = interval_ms;
}
}

View File

@ -0,0 +1,25 @@
import { RateLimit } from "./RateLimit";
export class RateLimitChain {
public chain: RateLimit[] = [];
constructor(num: number, interval_ms: number) {
this.setNumAndInterval(num, interval_ms);
}
public attempt(time: number = Date.now()) {
for (let i = 0; i < this.chain.length; i++) {
if (this.chain[i].attempt(time)) return true;
}
return true;
}
public setNumAndInterval(num: number, interval_ms: number) {
this.chain = [];
for (let i = 0; i < num; i++) {
this.chain.push(new RateLimit(interval_ms));
}
}
}

View File

@ -0,0 +1,69 @@
import { loadConfig } from "../../util/config";
import { RateLimit } from "./RateLimit";
import { RateLimitChain } from "./RateLimitChain";
export interface RateLimitConfigList<
RL = number,
RLC = { num: number; interval: number }
> {
normal: {
m: RL;
a: RL;
};
chains: {
userset: RLC;
};
}
export type RateLimitConstructorList = RateLimitConfigList<
() => RateLimit,
() => RateLimitChain
>;
export type RateLimitList = RateLimitConfigList<RateLimit, RateLimitChain>;
export interface RateLimitsConfig {
user: RateLimitConfigList;
crown: RateLimitConfigList;
admin: RateLimitConfigList;
}
export const config = loadConfig<RateLimitsConfig>("config/ratelimits.yml", {
user: {
normal: {
a: 6000 / 4,
m: 1000 / 20
},
chains: {
userset: {
interval: 1000 * 60 * 30,
num: 1000
}
}
},
crown: {
normal: {
a: 6000 / 10,
m: 1000 / 20
},
chains: {
userset: {
interval: 1000 * 60 * 30,
num: 1000
}
}
},
admin: {
normal: {
a: 6000 / 50,
m: 1000 / 60
},
chains: {
userset: {
interval: 1000 * 60 * 30,
num: 1000
}
}
}
});

View File

@ -0,0 +1,17 @@
import { RateLimit } from "../RateLimit";
import { RateLimitChain } from "../RateLimitChain";
import { RateLimitConstructorList, config } from "../config";
export const adminLimits: RateLimitConstructorList = {
normal: {
a: () => new RateLimit(config.admin.normal.a),
m: () => new RateLimit(config.admin.normal.m)
},
chains: {
userset: () =>
new RateLimitChain(
config.admin.chains.userset.interval,
config.admin.chains.userset.num
)
}
};

View File

@ -0,0 +1,17 @@
import { RateLimit } from "../RateLimit";
import { RateLimitChain } from "../RateLimitChain";
import { RateLimitConstructorList, config } from "../config";
export const crownLimits: RateLimitConstructorList = {
normal: {
a: () => new RateLimit(config.crown.normal.a),
m: () => new RateLimit(config.crown.normal.m)
},
chains: {
userset: () =>
new RateLimitChain(
config.crown.chains.userset.interval,
config.crown.chains.userset.num
)
}
};

View File

@ -0,0 +1,17 @@
import { RateLimit } from "../RateLimit";
import { RateLimitChain } from "../RateLimitChain";
import { RateLimitConstructorList, config } from "../config";
export const userLimits: RateLimitConstructorList = {
normal: {
a: () => new RateLimit(config.user.normal.a),
m: () => new RateLimit(config.user.normal.m)
},
chains: {
userset: () =>
new RateLimitChain(
config.user.chains.userset.interval,
config.user.chains.userset.num
)
}
};