Merge pull request #9 from ledlamp/development

Development
This commit is contained in:
Lamp 2018-07-14 22:40:03 -07:00 committed by GitHub
commit a01a2933df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1152 additions and 597 deletions

5
.gitignore vendored
View File

@ -1,2 +1,5 @@
/local/
node_modules/
node_modules/
.DS_Store
test.sh
.vscode

View File

@ -1 +1 @@
worker: node index.js
worker: node src/main.js

View File

@ -1,10 +0,0 @@
(async function(){
global.dbClient = new (require('pg').Client)({
connectionString: process.env.DATABASE_URL,
ssl: true,
});
await dbClient.connect();
require('./src/main');
})().catch(error => {console.error(error.stack); process.exit(1);});

544
package-lock.json generated Normal file
View File

@ -0,0 +1,544 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"agent-base": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
"requires": {
"es6-promisify": "^5.0.0"
}
},
"async-exit-hook": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="
},
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"bson": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz",
"integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg=="
},
"buffer-from": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ=="
},
"buffer-writer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz",
"integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg="
},
"combined-stream": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^2.2.2",
"typedarray": "^0.0.6"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"discord.js": {
"version": "github:hydrabolt/discord.js#7d2744be89f90fa16b77b6fbad95902e26f4564a",
"from": "github:hydrabolt/discord.js",
"requires": {
"form-data": "^2.3.2",
"node-fetch": "^2.1.2",
"pako": "^1.0.0",
"prism-media": "github:hydrabolt/prism-media#ead016c69b1f341898cb264746c355b982502c62",
"tweetnacl": "^1.0.0",
"ws": "^4.0.0"
},
"dependencies": {
"ws": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
"requires": {
"async-limiter": "~1.0.0",
"safe-buffer": "~5.1.0"
}
}
}
},
"es6-promise": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
"integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ=="
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"requires": {
"es6-promise": "^4.0.3"
}
},
"extract-zip": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
"integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
"requires": {
"concat-stream": "1.6.2",
"debug": "2.6.9",
"mkdirp": "0.5.1",
"yauzl": "2.4.1"
}
},
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
"requires": {
"pend": "~1.2.0"
}
},
"form-data": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "1.0.6",
"mime-types": "^2.1.12"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"https-proxy-agent": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
"integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
"requires": {
"agent-base": "^4.1.0",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
}
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
},
"mime-types": {
"version": "2.1.18",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
"requires": {
"mime-db": "~1.33.0"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"mongodb": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.1.tgz",
"integrity": "sha512-GU9oWK4pi8PC7NyGiwjFMwZyMqwGWoMEMvM0LZh7UKW/FFAqgmZKjjriD+5MEOCDUJE2dtHX93/K5UtDxO0otg==",
"requires": {
"mongodb-core": "3.1.0"
}
},
"mongodb-core": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.0.tgz",
"integrity": "sha512-qRjG62Fu//CZhkgn0jA/k8jh5MhACIq8cOJUryH6sck87pgt+C222MSD02tsCq5zNo/B6ZFHtNodZ2qpf8E86g==",
"requires": {
"bson": "~1.0.4",
"require_optional": "^1.0.1",
"saslprep": "^1.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node-fetch": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
"integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"packet-reader": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz",
"integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc="
},
"pako": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"pg": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-7.4.3.tgz",
"integrity": "sha1-97b5P1NA7MJZavu5ShPj1rYJg0s=",
"requires": {
"buffer-writer": "1.0.1",
"packet-reader": "0.3.1",
"pg-connection-string": "0.1.3",
"pg-pool": "~2.0.3",
"pg-types": "~1.12.1",
"pgpass": "1.x",
"semver": "4.3.2"
},
"dependencies": {
"semver": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz",
"integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c="
}
}
},
"pg-connection-string": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz",
"integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc="
},
"pg-pool": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.3.tgz",
"integrity": "sha1-wCIDLIlJ8xKk+R+2QJzgQHa+Mlc="
},
"pg-types": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz",
"integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=",
"requires": {
"postgres-array": "~1.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.0",
"postgres-interval": "^1.1.0"
}
},
"pgpass": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz",
"integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=",
"requires": {
"split": "^1.0.0"
}
},
"postgres-array": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz",
"integrity": "sha1-jgsy6wO/d6XAp4UeBEHBaaJWojg="
},
"postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
},
"postgres-date": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz",
"integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g="
},
"postgres-interval": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.2.tgz",
"integrity": "sha512-fC3xNHeTskCxL1dC8KOtxXt7YeFmlbTYtn7ul8MkVERuTmf7pI4DrkAxcw3kh1fQ9uz4wQmd03a1mRiXUZChfQ==",
"requires": {
"xtend": "^4.0.0"
}
},
"prism-media": {
"version": "github:hydrabolt/prism-media#ead016c69b1f341898cb264746c355b982502c62",
"from": "github:hydrabolt/prism-media"
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"progress": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8="
},
"proxy-from-env": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
"integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4="
},
"puppeteer": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-0.13.0.tgz",
"integrity": "sha512-M52SA/WmW54YMLzFtCLGslhr9tntzfTgJIZnx3QnaDXn9F5q2BlTosywSBEKj8aVVd6al0WNfiu14MUQW3wjaw==",
"requires": {
"debug": "^2.6.8",
"extract-zip": "^1.6.5",
"https-proxy-agent": "^2.1.0",
"mime": "^1.3.4",
"progress": "^2.0.0",
"proxy-from-env": "^1.0.0",
"rimraf": "^2.6.1",
"ws": "^3.0.0"
}
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"require_optional": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
"requires": {
"resolve-from": "^2.0.0",
"semver": "^5.1.0"
}
},
"resolve-from": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
"integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"requires": {
"glob": "^7.0.5"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"saslprep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.0.tgz",
"integrity": "sha512-5lvKUEQ7lAN5/vPl5d3k8FQeDbEamu9kizfATfLLWV5h6Mkh1xcieR1FSsJkcSRUk49lF2tAW8gzXWVwtwZVhw==",
"optional": true
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
},
"split": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
"integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
"requires": {
"through": "2"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"tweetnacl": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz",
"integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins="
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "~1.0.0",
"safe-buffer": "~5.1.0",
"ultron": "~1.1.0"
}
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"yauzl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
"integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
"requires": {
"fd-slicer": "~1.0.1"
}
}
}
}

View File

@ -7,7 +7,6 @@ global.colorRoles = { // TODO clean up this, adsfhaiusdhgaisuhg
name:"[]",
permissions:[],
color:"RANDOM",
//position: member.guild.roles.get('346754988023873546').position
}});
return role;
},

View File

@ -38,11 +38,11 @@ global.commands = {
usage: "<name>",
description: "Creates a generic text channel in this server and gives you full permissions for it.",
exec: async function (msg) {
if (!msg.args[0]) return false;
if (!msg.args[0]) return "EBADUSG";
//var name = msg.txt(1).replace(/[^a-zA-Z0-9]/g, '-').substr(0,100).toLowerCase();
var name = msg.txt(1);
msg.guild.channels.create(name, {
parent: '399735134061985792',
parent: config.channels.user_channels,
overwrites: [
{
id: msg,
@ -79,7 +79,7 @@ global.commands = {
var channel = msg.channel;
}
if (!channel.permissionsFor(msg.member).has('MANAGE_CHANNELS')) return msg.react('🚫');
await channel.setParent('425054198699261953');
await channel.setParent(config.channels.deleted_channels);
await channel.lockPermissions();
msg.react('🆗');
}
@ -106,6 +106,24 @@ global.commands = {
}
}
},
"exec": {
op: true,
usage: "<command>",
aliases: ["$"],
exec: async function (msg) {
require("child_process").exec(msg.txt(1), function(error, stdout, stderr){
if (error) msg.channel.send(error, {split:{char:''}});
else {
var str = ""
if (stdout) msg.channel.send(stdout, {split:{char:''}});
if (stderr) msg.channel.send(stderr, {split:{char:''}});
}
})
}
},
"query": {
description: "Queries the Heroku PostgreSQL database",
usage: "<query>",
@ -145,35 +163,26 @@ dClient.on('message', message => {
if (!message.guild) message.guild = dClient.guilds.get(config.guildID);
if (!message.member) message.member = dClient.guilds.get(config.guildID).members.get(message.author.id);
/*if (commands.hasOwnProperty(cmd)) {
var command = commands[cmd];
if (command.op && message.author.id !== op) return message.react('🚫');
try {
command.exec(message, args, txt);
} catch(e) {
message.reply(`:warning: An error occured while processing your command.`);
console.error(e.stack);
}
}*/
Object.keys(commands).forEach(commandName => {
var command = commands[commandName];
if (!(commandName === cmd || (command.aliases && command.aliases.includes(cmd)))) return;
if (command.op && message.author.id !== config.opID) return message.react('🚫');
/*try {
var d = command.exec(message, args, txt);
if (d === false) message.channel.send(`**Usage:** \`!${commandName} ${command.usage}\``);
} catch(e) {
message.reply(`:warning: An error occured while processing your command.`);
console.error(e.stack);
}*/
command.exec(message, args, txt).then(
(res) => {
if (res === false) message.channel.send(`**Usage:** \`!${commandName} ${command.usage}\``);
switch (res) {
case "ENOTBRIDGE":
message.channel.send([
`This is not a bridged channel.`,
`You can only use this command in a bridged channel.`
].random());
break;
case "EBADUSG":
message.channel.send(`**Usage:** \`!${commandName} ${command.usage}\``);
break;
}
},
(err) => {
message.reply(`:warning: An error occured: \`\`\`\n${err.stack}\n\`\`\``);
message.reply(`:warning: An error occured: \`\`\`\n${err.stack}\n\`\`\`<@281134216115257344>`);
console.error(err.stack);
}
)

View File

@ -1,25 +1,42 @@
global.testmode = process.env.TEST ? true : false;
module.exports = {
"testmode": false,
"token": process.env.DISCORD_TOKEN,
"DISCORD_TOKEN": process.env.DISCORD_TOKEN,
"DATABASE_URL": testmode ? "postgres://localhost/k4t" : process.env.DATABASE_URL,
"MONGODB_URI": testmode ? "mongodb://localhost/k4t" : process.env.MONGODB_URI,
"webhooks": {
"console": process.env.TOKEN_WEBHOOK_CONSOLE,
"welcome": process.env.TOKEN_WEBHOOK_WELCOME,
"welcome": process.env.WEBHOOK_WELCOME.split("/"),
},
"opID": "281134216115257344",
"guildID": "321819041348190249",
"channels": {
"main": "321819041348190249"
"guildID": testmode ? "467473467634089985" : "321819041348190249",
"channels": { // includes voice channels & category channels
"main": testmode ? "467473467634089987" : "321819041348190249",
"voice": testmode ? "467473467634089989" : "425060452129701889",
"name_collection": testmode ? "467481952728121345" : '379738469511069698',
"mpp_bridges": testmode ? "467481904707534850" : '360557444952227851',
"user_channels": testmode ? "467482031157149709" : '399735134061985792',
"deleted_channels": testmode ? "467482085657935872" : '425054198699261953',
"deleted_bridges": testmode ? "467482121657778176" : '451838300068511745',
"mpp_screenshot": testmode ? "467482164611514388" : '383773548810076163',
"owop_screenshot": testmode ? "467482202217906226" : '399079481161023492'
},
"roles": {
"viewing_deleted_channels": testmode ? "467473718353068042" : "425060792455397376",
},
"mppname": "[discord.gg/k44Eqha]",
"disabledRooms": [
"RP Room",
"Legends of Alorgon {RP Room}",
"Legends of Alorgon",
"Breastmilk ♥ 7:45 AM"
]
],
}

View File

@ -17,10 +17,9 @@ function mixin(obj1, obj2) {
};
function Client(uri, arrayBuffer) {
function Client(uri) {
EventEmitter.call(this);
this.uri = uri;
this.arrayBuffer = arrayBuffer;
this.ws = undefined;
this.serverTimeOffset = 0;
this.user = undefined;
@ -85,7 +84,7 @@ Client.prototype.connect = function() {
// browseroni
this.ws = new WebSocket(this.uri);
}
if (this.arrayBuffer) this.ws.binaryType = "arraybuffer";
this.ws.binaryType = "arraybuffer";
this.emit("ws created");
var self = this;
this.ws.addEventListener("close", function(evt) {

View File

@ -1,51 +1,33 @@
require('./util');
global.config = require('./config');
if (config.testmode) console.log('TEST MODE');
global.exitHook = require('async-exit-hook');
global.Discord = require('discord.js');
global.fs = require('fs');
global.config = require('./config');
global.dClient = new Discord.Client({ disableEveryone: true });
console._log = console.log;
console.log = function(){
console._log.apply(console, arguments);
log2discord(arguments);
}
console._error = console.error;
console.error = function(){
console._error.apply(console, arguments);
log2discord(arguments);
}
console.warn = console.error;
console.info = console.log;
var webhook = new Discord.WebhookClient('405445543536623627', config.webhooks.console);
function log2discord(str){
str = Array.from(str);
str = str.map(require('util').inspect);
str = str.join(' ');
webhook.send(str, {split:{char:''}});
}
process.on('unhandledRejection', (reason, promise) => {
console.error(promise);
global.dbClient = new (require('pg').Client)({
connectionString: process.env.DATABASE_URL,
ssl: !testmode,
});
process.on('uncaughtException', error => {
console.error(error.stack);
});
(require('mongodb').MongoClient).connect(process.env.MONGODB_URI).then(client=>{
global.mdbClient = client;
dClient.login(config.token);
console.log("Connecting to Postgres…")
dbClient.connect().then(function(){
console.log("Connecting to MongoDB…");
(require('mongodb').MongoClient).connect(config.MONGODB_URI).then(client=>{
global.mdbClient = client;
console.log("Connecting to Discord…");
dClient.login(config.DISCORD_TOKEN);
});
});
dClient.once('ready', () => {
console.log('Discord Client Ready');
require('./commands.js');
require('./colorroles.js');
require('./mppbridger.js');
require('./screenshotter.js');
require('./misc.js');
require('./commands');
require('./colorroles');
require('./mppbridger');
require('./screenshotter');
require('./misc');
});
dClient.on('error', console.error);

View File

@ -1,22 +1,22 @@
// join/leave
(function(){
var webhook = new Discord.WebhookClient('404736784354770958', config.webhooks.welcome);
dClient.on('guildMemberAdd', member => {
(async function(){
var webhook = new Discord.WebhookClient(config.webhooks.welcome[0], config.webhooks.welcome[1]);
dClient.on('guildMemberAdd', async member => {
webhook.send(`${member} joined.`, {username: member.user.username, avatarURL: member.user.displayAvatarURL(), disableEveryone:true});
});
dClient.on('guildMemberRemove', member => {
dClient.on('guildMemberRemove', async member => {
webhook.send(`${member.user.tag} left.`, {username: member.user.username, avatarURL: member.user.displayAvatarURL(), disableEveryone:true});
});
})();
// view deleted channels
(function(){
var vcid = '425060452129701889';
var rid = '425060792455397376';
dClient.on('voiceStateUpdate', (oldMember, newMember) => {
(async function(){
var vcid = config.channels.voice;
var rid = config.roles.viewing_deleted_channels;
dClient.on('voiceStateUpdate', async (oldMember, newMember) => {
if (oldMember.voiceChannelID != vcid && newMember.voiceChannelID == vcid) {
// member joined the channel
newMember.roles.add(newMember.guild.roles.get(rid));

View File

@ -1,500 +0,0 @@
var Client = require('./lib/Client.js');
global.clients = {};
/*function reconnectClients() {
for (let site in clients) {
site = clients[site];
let i = 0;
for (let client in site) {
client = site[client];
if (client.reconnectTimeout) clearTimeout(client.reconnectTimeout);
client.reconnectTimeout = setTimeout(()=>{
client.connect();
client.reconnectTimeout = undefined;
}, i += 2000);
}
}
} //TODO BETTAH*/
global.clientConnector = {
queue: [],
enqueue: function(client) {
if (this.queue.includes(client)) return;
this.queue.push(client);
},
interval: setInterval(function(){
var client = clientConnector.queue.shift();
if (client) client.connect();
}, 2000)
}
global.createMPPbridge = function (room, DiscordChannelID, site = 'MPP', webhookID, webhookToken) {
var DiscordChannel = dClient.channels.get(DiscordChannelID);
if (!DiscordChannel) return console.error(`Couldn't bridge ${site} ${room} because Discord Channel ${DiscordChannelID} is missing!`);
if (webhookID && webhookToken) var webhook = new Discord.WebhookClient(webhookID, webhookToken, {disableEveryone:true});
var msgBuffer = [];
function _dSend(msg, embed) {
if (webhook && !config.testmode) {
let username = gClient.channel && gClient.channel._id || room;
if (username.length > 32) username = username.substr(0,31) + '…';
else if (username.length < 2) username = undefined;
webhook.send(msg, {username, embed, split:{char:''}}).catch(e => {
console.error(e);
DiscordChannel.send(msg, {embed, split:{char:''}}).catch(console.error);
});
}
else DiscordChannel.send(msg, {embed, split:{char:''}}).catch(console.error);
}
function dSend(msg) {
msgBuffer.push(msg);
}
setInterval(()=>{
if (msgBuffer.length == 0) return;
_dSend(msgBuffer.join('\n'));
msgBuffer = [];
}, 2000); //TODO make changeable
const gClient = site == "MPP" ? new Client("ws://www.multiplayerpiano.com:443") : site == "WOPP" ? new Client("ws://ourworldofpixels.com:1234", true) : site == "MPT" ? new Client("ws://ts.terrium.net:8080", true) : site == "VFDP" ? new Client("ws://www.visualfiredev.com:8080") : undefined;
if (!gClient) return console.error(`Invalid site ${site}`);
gClient.setChannel(/*(site == "MPP" && room == "lobby") ? "lolwutsecretlobbybackdoor" : */room);
gClient.canConnect = true;
clientConnector.enqueue(gClient);
var isConnected = false;
gClient.on('connect', () => {
console.log(`Connected to room ${room} of ${site} server`);
dSend(`**Connected**`); // TODO say what room it actually connected to ?
gClient.sendArray([{m: "userset", set: {name: config.mppname}}])
isConnected = true;
});
gClient.on('disconnect', () => {
if (isConnected) {
console.log(`Disconnected from room ${room} of ${site} server`);
dSend(`**Disconnected**`);
isConnected = false;
}
clientConnector.enqueue(gClient);
});
/*gClient.on('status', status => {
console.log(`[${site}] [${room}] ${status}`);
});*/
let lastCh = room;
gClient.on('ch', msg => {
if (lastCh && msg.ch._id !== lastCh) {
dSend(`**Channel changed from \`${lastCh}\` to \`${msg.ch._id}\`**`);
console.log(`[${site}][${room}] Channel changed from ${lastCh} to ${msg.ch._id}`);
lastCh = msg.ch._id;
}
(async function(){
// catch dropped crown
if (msg.ch.crown && !msg.ch.crown.hasOwnProperty('participantId')) {
gClient.sendArray([{m:'chown', id: gClient.getOwnParticipant().id}]); // if possible
var avail_time = msg.ch.crown.time + 15000 - gClient.serverTimeOffset;
var ms = avail_time - Date.now();
setTimeout(()=> gClient.sendArray([{m:'chown', id: gClient.getOwnParticipant().id}]) , ms);
}
// transfer crown to owner
if (msg.ppl && msg.ch.crown && msg.ch.crown.participantId == gClient.getOwnParticipant().id) {
var res = await dbClient.query("SELECT owner_mpp__id FROM bridges WHERE mpp_room = $1 AND site = $2;", [room, site]);
if (res.rows.length == 0) return;
var owner = res.rows[0].owner_mpp__id;
if (!owner) return;
msg.ppl.some(part => {
if (part._id == owner) {
gClient.sendArray([{m:'chown', id: part.id}]);
return true;
} else return false;
});
}
})();
});
// MPP to Discord
gClient.on('a', msg => {
if (msg.p._id == gClient.getOwnParticipant()._id) return;
var id = msg.p._id.substr(0,6);
var name = msg.p.name.replace(/discord.gg\//g, 'discord.gg\\/');
var str = `\`${id}\` **${name}:** ${msg.a}`;
str = str.replace(/<@/g, "<\\@");
dSend(str);
});
// Discord to MPP
dClient.on('message', message => {
if (message.channel.id !== DiscordChannelID || message.author.bot || message.content.startsWith('!')) return;
var str = message.cleanContent;
var arr = [];
if (str.startsWith('/') || str.startsWith('\\')) {
arr.push({m:"a", message:
`${message.member.displayName}`
});
} else str = message.member.displayName + ': ' + str;
if (str.startsWith('\\')) str = str.slice(1);
if (message.attachments.first()) str += ' '+message.attachments.first().url;
if (str.length > 512) str = str.substr(0,511) + '…';
arr.push({m:"a", message:str});
gClient.sendArray(arr);
});
// announce join/leave
gClient.on('participant added', participant => { //TODO universal way of filtering names
dSend(`**\`${participant._id.substr(0,6)}\` ${participant.name.replace(/<@/g, "<\\@")} entered the room.**`);
});
gClient.on('participant removed', participant => {
dSend(`**\`${participant._id.substr(0,6)}\` ${participant.name.replace(/<@/g, "<\\@")} left the room.**`);
});
/*// autoban banned participants
gClient.on('participant added', part => {
if (PS.banned_ppl.hasOwnProperty(part._id) && gClient.isOwner())
gClient.sendArray([{m: "kickban", _id: part._id, ms: 60*60*1000}]);
});*/
gClient.on('notification', async msg => {
// show notification
_dSend(undefined, {
title: msg.title,
description: msg.text || msg.html
});
// ban handling
if (msg.text && (msg.text.startsWith('Banned from') || msg.text.startsWith('Currently banned from'))) {
// Banned from "{room}" for {n} minutes.
// Currently banned from "{room}" for {n} minutes.
let arr = msg.text.split(' ');
arr.pop();
let minutes = arr.pop();
gClient.stop();
setTimeout(()=>{
gClient.setChannel(room);
gClient.start();
}, minutes*60*1000+3000);
dSend(`**Attempting to rejoin in ${minutes} minutes.**`);
}
});
gClient.on("ch", function(msg){
if (gClient.isOwner()) {
if (gClient.countParticipants() <= 1) {
gClient.sendArray([{m:'chset', set: { visible: false }}])
} else {
gClient.sendArray([{m:'chset', set: { visible: true }}])
}
}
});
// addons
gClient.on('participant update', function(participant){
nameCollector.collect(participant);
});
//if (site == 'MPP' && room == 'lobby') MPPrecorder.start(gClient);//TODO too much memory
// collect data
(async function(){
var filename = `${site} ${room} .txt`.replace(/\//g, ':');
var size = 0;
var startDate = new Date();
gClient.on('ws created', function(){
gClient.ws.addEventListener('message', msg => {
var data = msg.data;
if (data instanceof ArrayBuffer) data = Buffer.from(data).toString('base64');
var line = `${Date.now()} ${data}\n`;
size += line.length;
fs.appendFile(filename, line, ()=>{});
if (size > 8000000) {save(); size = 0;}
});
});
async function save(callback){
console.log(`saving data recording`, filename)
fs.readFile(filename, (err, file) => {
if (err) return console.error(err);
require('zlib').gzip(file, async function(err, gzip){
if (err) return console.error(err);
var attachmentName = `${site} ${room} raw data recording from ${startDate.toISOString()} to ${new Date().toISOString()} .txt.gz`;
await DiscordChannel.send(new Discord.MessageAttachment(gzip, attachmentName));
fs.writeFileSync(filename, '');
size = 0;
startDate = new Date();
console.log(`saved raw data recording`, attachmentName);
if (callback) callback();
});
});
}
exitHook(callback => {
save(()=>callback());
});
gClient.dataCollectorSave = function(){save()}; // test
})();
if (!clients[site]) clients[site] = {};
clients[site][room] = gClient;
/*// EXPERIMENTAL
gClient.on('ls', msg => {
msg.u.forEach(async r => {
if (clients[site][r._id]) return;
for (let client of clientConnector.queue) {if (client.desiredChannelId == r._id) return} // ugg
var discordChannelName = r._id.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
var categoryID = '409079939501916160';
var channel = await dClient.guilds.get(config.guildID).createChannel(discordChannelName, {parent: categoryID});
channel.setTopic(`Bridged to http://www.multiplayerpiano.com/${encodeURIComponent(r._id)}`);
var webhook = await channel.createWebhook('Webhook');
createMPPbridge(r._id, channel.id, site, webhook.id, webhook.token);
})
})*/
}
global.nameCollector = {
collection: mdbClient.db('heroku_jrtfvpd9').collection('ppl'),
collect: async function (participant) {
if (config.testmode) return;
if (participant.name == "Anonymous" || participant.name == "Anonymoose") return;
var newMsg = function(continued){
var str = `__**${participant._id}**__${continued ? ' (continued)' : ''}\n${participant.name}`;
return dClient.channels.get('379738469511069698').send(str);
}
var document = await this.collection.findOne({_id: participant._id});
if (document) {
// update person
if (document.names.includes(participant.name)) return;
document.names.push(participant.name);
this.collection.updateOne({_id: participant._id}, {$set:{names: document.names}});
let message = await dClient.channels.get('379738469511069698').messages.fetch(document.discord_msg_id);
try {
await message.edit(message.content + ', ' + participant.name);
} catch(e) {
let message = await newMsg(true);
this.collection.updateOne({_id: participant._id}, {$set:{discord_msg_id: message.id}});
}
} else {
// add new person
let message = await newMsg();
nameCollector.collection.insertOne({
_id: participant._id,
discord_msg_id: message.id,
names: [participant.name]
});
}
}
};
global.MPPrecorder = {
start: function(client) {
var recorder = (require('./lib/mpprecorder-module.js'))(undefined, client);
this.save = async function () {
var startDate = new Date(recorder.startTime), endDate = new Date();
var filename = `www.multiplayerpiano.com lobby recording from ${startDate.toISOString()} to ${endDate.toISOString()} .mid.gz`;
var file = recorder.save();
file = Buffer.from(file, 'binary');
file = require('zlib').gzipSync(file);
var attachment = new Discord.MessageAttachment(file, filename);
await dClient.channels.get('394967426133000193').send(attachment);
}
this.interval = setInterval(() => {
this.save();
}, 10*60*1000);
exitHook(callback => {
this.save().then(callback);
});
}
};
// start
(async function () {
var res = await dbClient.query('SELECT * FROM bridges;');
var sites = {};
res.rows.forEach(row => {
if (row.disabled) return;
if (!sites[row.site]) sites[row.site] = [];
sites[row.site].push(row);
});
for (let site in sites) {
let arr = sites[site];
arr.sort((a, b) => {return a.position - b.position});
let i = 0;
arr.forEach(bridge => {
setTimeout(function(){
createMPPbridge(bridge.mpp_room, bridge.discord_channel_id, bridge.site, bridge.webhook_id, bridge.webhook_token, bridge.owner_mpp__id);
}, i);
i = i + 2000;
});
}
})();
// commands
commands.bridge = {
usage: "<MPP room>",
description: "Creates a bridge to the specified MPP room.",
exec: async function (msg) {
var site = 'MPP';
var room = msg.txt(1);
if (!room) return false;
var existingBridge = (await dbClient.query('SELECT * FROM bridges WHERE mpp_room = $1;', [room])).rows[0];
if (existingBridge) {
if (!existingBridge.disabled) {
return msg.reply(`${site} room ${room} is already bridged.`);
} else {
if (config.disabledRooms.includes(room)) {
return msg.reply(`You cannot bridge this room.`);
} else /* rebridge */ {
let channel = dClient.guilds.get(config.guildID).channels.get(existingBridge.discord_channel_id);
await dbClient.query("UPDATE bridges SET disabled = false WHERE mpp_room = $1", [room]);
await channel.setParent('360557444952227851');
await channel.lockPermissions();
createMPPbridge(room, existingBridge.mpp_room, existingBridge.site, existingBridge.webhook_id, existingBridge.webhook_token);
await msg.reply(`${site} room ${room} has been re-bridged.`);
return;
}
}
}
/* new bridge */
var discordChannelName = room.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
var categoryID = '360557444952227851';
var channel = await dClient.guilds.get(config.guildID).channels.create(discordChannelName, {parent: categoryID});
channel.setTopic(`http://www.multiplayerpiano.com/${encodeURIComponent(room)}`);
var webhook = await channel.createWebhook('Webhook');
createMPPbridge(room, channel.id, site, webhook.id, webhook.token);
dbClient.query('INSERT INTO bridges (site, mpp_room, discord_channel_id, webhook_id, webhook_token, owner_discord_user_id) VALUES ($1, $2, $3, $4, $5, $6)', [
site, room, channel.id, webhook.id, webhook.token, msg.author.id,
]);
msg.reply(`${site} room ${room} is now bridged to ${channel}.`);
}
};
commands.unbridge = {
usage: "[MPP Room]",
description: "Deletes a bridge to the specified MPP room.",
exec: async function (msg) {
var bridge = (await dbClient.query("SELECT * FROM bridges WHERE mpp_room = $1 OR discord_channel_id = $2", [msg.txt(1), msg.channel.id])).rows[0];
if (!bridge) {
//msg.react('⚠️');
msg.reply(`That room is not bridged. Make sure you type the MPP room name correctly.`);
return;
}
if (bridge.disabled) {
msg.reply(`That room has already been unbridged.`);
return;
}
if (!(bridge.owner_discord_user_id == msg.author.id || msg.author.id == config.opID)) {
//msg.react('🚫');
msg.reply(`You do not own that bridge.`);
return;
}
await dbClient.query("UPDATE bridges SET disabled = 'true' WHERE mpp_room = $1", [bridge.mpp_room]);
clients.MPP[bridge.mpp_room].stop();
var channel = dClient.channels.get(bridge.discord_channel_id)
await channel.setParent('451838300068511745');
await channel.lockPermissions();
msg.reply(`${bridge.mpp_room} has been unbridged.`);
}
}
commands.chown = {
usage: "<'mpp'/'discord'> <Discord User ID or mention, or MPP _id>",
description: "Changes the MPP or Discord owner of a private bridge. The first argument must be either `mpp` or `discord`.",
aliases: ['changeowner', 'setowner'],
exec: async function (msg) {
if (msg.args.length < 3 || !['mpp','discord'].includes(msg.args[1])) return false;
var res = await dbClient.query('SELECT * FROM bridges WHERE discord_channel_id = $1;', [msg.channel.id]);
if (res.rows.length == 0) return msg.react('🚫');
var bridge = res.rows[0];
if (!(bridge.owner_discord_user_id == msg.author.id || msg.author.id == config.opID)) return msg.react('🚫');
if (msg.args[1] == 'discord') {
let selectedUser = dClient.users.get(msg.args[2]) || msg.mentions.users.first();
if (!selectedUser) return msg.react('⚠️');
msg.channel.overwritePermissions(selectedUser, {
MANAGE_CHANNELS: true,
MANAGE_ROLES: true,
MANAGE_WEBHOOKS: true,
MANAGE_MESSAGES: true
});
let po = msg.channel.permissionOverwrites.find(x => x.id == msg.author.id);
if (po) po.delete();
await dbClient.query('UPDATE bridges SET owner_discord_user_id = $1 WHERE discord_channel_id = $2;', [selectedUser.id, msg.channel.id]);
msg.channel.send(`Ownership of ${msg.channel} has been transferred to ${selectedUser}`);
} else if (msg.args[1] == 'mpp') {
let _id = msg.args[2];
await dbClient.query('UPDATE bridges SET owner_mpp__id = $1 WHERE discord_channel_id = $2;', [_id, msg.channel.id]);
msg.channel.send(`MPP user \`${_id}\` has been assigned as owner of the MPP room, and the crown will be transferred to them whenever possible.`);
//todo give crown if owner there
}
}
};
commands.list = {
description: "Lists online participants",
aliases: ['ppl', 'online'],
exec: async function (message) {
var row = (await dbClient.query("SELECT mpp_room, site FROM bridges WHERE discord_channel_id = $1;", [message.channel.id])).rows[0];
if (!row) {
//message.react('🚫');
message.reply(`Use this in a bridged room to see who is at the other side.`);
return;
}
var ppl = clients[row.site][row.mpp_room].ppl;
var numberOfPpl = Object.keys(ppl).length;
var str = `__**Participants Online (${numberOfPpl})**__\n`;
var names = [];
for (let person in ppl) {
person = ppl[person];
names.push(`\`${person._id.substr(0,6)}\` ${person.name.replace(/<@/g, "<\\@")}`);
}
str += names.join(', ');
message.channel.send(str, {split:{char:''}});
}
};

View File

@ -0,0 +1,25 @@
module.exports = {
usage: "<MPP user id>",
description: "Adds a perma-ban on the user",
aliases: ["permaban"],
exec: async function (msg) {
if (!msg.args[1]) return "EBADUSG";
var res = await dbClient.query('SELECT * FROM bridges WHERE discord_channel_id = $1;', [msg.channel.id]);
if (!res.rows.length) return "ENOTBRIDGE"
var bridge = res.rows[0];
if (bridge.owner_discord_user_id != msg.author.id) return msg.reply(`You are not the owner of this bridge.`);
var _id = msg.txt(1);
await dbClient.query("UPDATE bridges SET bans = array_append(bans, $1) WHERE discord_channel_id = $2", [_id, msg.channel.id]);
await msg.reply(`OK, I'll ban anyone whose user ID equals or starts with \`${_id}\` from this room, whenever possible.`);
var client = clients.MPP[bridge.mpp_room]
for (let p in client.ppl) {
p = client.ppl[p]
if (p._id.startsWith(_id))
client.sendArray([{m:'kickban', _id, ms: 60*60*1000}])
}
if (_id.length != 24) await msg.reply(":warning: The ID you gave me does not look like a full user ID (it is not 24 chars long). If it's a truncated ID it will still work, however it could possibly ban someone else whose user ID starts with those chars.")
},
}

View File

@ -0,0 +1,38 @@
module.exports = {
usage: "<MPP room>",
description: "Creates a bridge to the specified MPP room.",
exec: async function (msg) {
var site = 'MPP';
var room = msg.txt(1);
if (!room) return "EBADUSG";
var existingBridge = (await dbClient.query('SELECT * FROM bridges WHERE mpp_room = $1;', [room])).rows[0];
if (existingBridge) {
if (!existingBridge.disabled) {
return msg.reply(`${site} room ${room} is already bridged.`);
} else {
if (config.disabledRooms.includes(room)) {
return msg.reply(`You cannot bridge this room.`);
} else /* rebridge */ {
let channel = dClient.guilds.get(config.guildID).channels.get(existingBridge.discord_channel_id);
await dbClient.query("UPDATE bridges SET disabled = false WHERE mpp_room = $1", [room]);
await channel.setParent();
await channel.lockPermissions();
createMPPbridge(room, existingBridge.mpp_room, existingBridge.site, existingBridge.webhook_id, existingBridge.webhook_token);
await msg.reply(`${site} room ${room} has been re-bridged.`);
return;
}
}
}
/* new bridge */
var discordChannelName = room.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
var categoryID = config.channels.mpp_bridges;
var channel = await dClient.guilds.get(config.guildID).channels.create(discordChannelName, {parent: categoryID});
channel.setTopic(`http://www.multiplayerpiano.com/${encodeURIComponent(room)}`);
var webhook = await channel.createWebhook('Webhook');
createMPPbridge(room, channel.id, site, webhook.id, webhook.token);
dbClient.query('INSERT INTO bridges (site, mpp_room, discord_channel_id, webhook_id, webhook_token, owner_discord_user_id) VALUES ($1, $2, $3, $4, $5, $6)', [
site, room, channel.id, webhook.id, webhook.token, msg.author.id,
]);
msg.reply(`${site} room ${room} is now bridged to ${channel}.`);
}
};

View File

@ -0,0 +1,32 @@
module.exports = {
usage: "<'mpp'/'discord'> <Discord User ID or mention, or MPP _id>",
description: "Changes the MPP or Discord owner of a private bridge. The first argument must be either `mpp` or `discord`.",
aliases: ['changeowner', 'setowner'],
exec: async function (msg) {
if (msg.args.length < 3 || !['mpp','discord'].includes(msg.args[1])) return "EBADUSG";
var res = await dbClient.query('SELECT * FROM bridges WHERE discord_channel_id = $1;', [msg.channel.id]);
if (res.rows.length == 0) return msg.react('🚫');
var bridge = res.rows[0];
if (!(bridge.owner_discord_user_id == msg.author.id || msg.author.id == config.opID)) return msg.react('🚫');
if (msg.args[1] == 'discord') {
let selectedUser = dClient.users.get(msg.args[2]) || msg.mentions.users.first();
if (!selectedUser) return msg.react('⚠️');
msg.channel.overwritePermissions(selectedUser, {
MANAGE_CHANNELS: true,
MANAGE_ROLES: true,
MANAGE_WEBHOOKS: true,
MANAGE_MESSAGES: true
});
let po = msg.channel.permissionOverwrites.find(x => x.id == msg.author.id);
if (po) po.delete();
await dbClient.query('UPDATE bridges SET owner_discord_user_id = $1 WHERE discord_channel_id = $2;', [selectedUser.id, msg.channel.id]);
msg.channel.send(`Ownership of ${msg.channel} has been transferred to ${selectedUser}`);
} else if (msg.args[1] == 'mpp') {
let _id = msg.args[2];
await dbClient.query('UPDATE bridges SET owner_mpp__id = $1 WHERE discord_channel_id = $2;', [_id, msg.channel.id]);
msg.channel.send(`MPP user \`${_id}\` has been assigned as owner of the MPP room, and the crown will be transferred to them whenever possible.`);
//todo give crown if owner there
}
}
};

View File

@ -0,0 +1,23 @@
module.exports = {
description: "Lists online participants",
aliases: ['ppl', 'online'],
exec: async function (message) {
var row = (await dbClient.query("SELECT mpp_room, site FROM bridges WHERE discord_channel_id = $1;", [message.channel.id])).rows[0];
if (!row) {
//message.react('🚫');
message.reply(`Use this in a bridged room to see who is at the other side.`);
return;
}
var ppl = clients[row.site][row.mpp_room].ppl;
var numberOfPpl = Object.keys(ppl).length;
var str = `__**Participants Online (${numberOfPpl})**__\n`;
var names = [];
for (let person in ppl) {
person = ppl[person];
names.push(`\`${person._id.substr(0,6)}\` ${person.name.replace(/<@/g, "<\\@")}`);
}
str += names.join(', ');
message.channel.send(str, {split:{char:''}});
}
};

View File

@ -0,0 +1,27 @@
module.exports = {
usage: "[MPP Room]",
description: "Deletes a bridge to the specified MPP room.",
exec: async function (msg) {
var bridge = (await dbClient.query("SELECT * FROM bridges WHERE mpp_room = $1 OR discord_channel_id = $2", [msg.txt(1), msg.channel.id])).rows[0];
if (!bridge) {
//msg.react('⚠️');
msg.reply(`That room is not bridged. Make sure you type the MPP room name correctly.`);
return;
}
if (bridge.disabled) {
msg.reply(`That room has already been unbridged.`);
return;
}
if (!(bridge.owner_discord_user_id == msg.author.id || msg.author.id == config.opID)) {
//msg.react('🚫');
msg.reply(`You do not own that bridge.`);
return;
}
await dbClient.query("UPDATE bridges SET disabled = 'true' WHERE mpp_room = $1", [bridge.mpp_room]);
clients.MPP[bridge.mpp_room].stop();
var channel = dClient.channels.get(bridge.discord_channel_id)
await channel.setParent(config.channels.deleted_bridges);
await channel.lockPermissions();
msg.reply(`${bridge.mpp_room} has been unbridged.`);
}
};

View File

@ -0,0 +1,37 @@
module.exports = async function(gClient, site, room, DiscordChannel) {
var path = require('os').tmpdir();
var filename = `${site} ${room} .txt`.replace(/\//g, ':');
var filepath = path + "/" + filename;
var size = 0;
var startDate = new Date();
gClient.on('ws created', function(){
gClient.ws.addEventListener('message', msg => {
var data = msg.data;
if (data instanceof ArrayBuffer) data = Buffer.from(data).toString('base64');
var line = `${Date.now()} ${data}\n`;
size += line.length;
fs.appendFile(filepath, line, ()=>{});
if (size > 8000000) {save(); size = 0;}
});
});
async function save(callback){
console.log(`saving data recording`, filename)
fs.readFile(filepath, (err, file) => {
if (err) return console.error(err);
require('zlib').gzip(file, async function(err, gzip){
if (err) return console.error(err);
var attachmentName = `${site} ${room} raw data recording from ${startDate.toISOString()} to ${new Date().toISOString()} .txt.gz`;
await DiscordChannel.send(new Discord.MessageAttachment(gzip, attachmentName));
fs.writeFileSync(filepath, '');
size = 0;
startDate = new Date();
console.log(`saved raw data recording`, attachmentName);
if (callback) callback();
});
});
}
exitHook(callback => {
save(()=>callback());
});
gClient.dataCollectorSave = function(){save()}; // test
}

283
src/mppbridger/index.js Executable file
View File

@ -0,0 +1,283 @@
var Client = require('../lib/Client.js');
global.clients = {};
global.clientConnector = {
queue: [],
enqueue: function(client) {
if (this.queue.includes(client)) return;
this.queue.push(client);
},
interval: setInterval(function(){
var client = clientConnector.queue.shift();
if (client) client.connect();
}, 2000)
}
global.createMPPbridge = function createMPPbridge(room, DiscordChannelID, site = 'MPP', webhookID, webhookToken) {
var DiscordChannel = dClient.channels.get(DiscordChannelID);
if (!DiscordChannel) return console.error(`Couldn't bridge ${site} ${room} because Discord Channel ${DiscordChannelID} is missing!`);
if (webhookID && webhookToken) var webhook = new Discord.WebhookClient(webhookID, webhookToken, {disableEveryone:true});
// discord message sending
var msgBuffer = [];
function _dSend(msg, embed) {
if (webhook && !config.testmode) {
let username = gClient.channel && gClient.channel._id || room;
if (username.length > 32) username = username.substr(0,31) + '…';
else if (username.length < 2) username = undefined;
webhook.send(msg, {username, embed, split:{char:''}}).catch(e => {
console.error(e);
DiscordChannel.send(msg, {embed, split:{char:''}}).catch(console.error);
});
}
else DiscordChannel.send(msg, {embed, split:{char:''}}).catch(console.error);
}
function dSend(msg) {
msgBuffer.push(msg);
}
setInterval(()=>{
if (msgBuffer.length == 0) return;
_dSend(msgBuffer.join('\n'));
msgBuffer = [];
}, 2000); //TODO make changeable
const gClient =
site == "MPP" ? new Client("ws://www.multiplayerpiano.com:443") :
site == "WOPP" ? new Client("ws://ourworldofpixels.com:1234") :
site == "MPT" ? new Client("ws://ts.terrium.net:8080") :
site == "VFDP" ? new Client("ws://www.visualfiredev.com:8080") :
undefined;
if (!gClient) return console.error(`Invalid site ${site}`);
gClient.setChannel(/*(site == "MPP" && room == "lobby") ? "lolwutsecretlobbybackdoor" : */room);
gClient.canConnect = true;
clientConnector.enqueue(gClient);
var isConnected = false;
gClient.on('connect', () => {
console.log(`Connected to room ${room} of ${site} server`);
dSend(`**Connected**`); // TODO say what room it actually connected to ?
isConnected = true;
});
gClient.on('hi', ()=>{
if (!testmode) gClient.sendArray([{m: "userset", set: {name: config.mppname}}])
});
gClient.on('disconnect', () => {
if (isConnected) {
console.log(`Disconnected from room ${room} of ${site} server`);
dSend(`**Disconnected**`);
isConnected = false;
}
clientConnector.enqueue(gClient);
});
/*gClient.on('status', status => {
console.log(`[${site}] [${room}] ${status}`);
});*/
let lastCh = room;
gClient.on('ch', msg => {
// announce channel change
if (lastCh && msg.ch._id !== lastCh) {
dSend(`**Channel changed from \`${lastCh}\` to \`${msg.ch._id}\`**`);
console.log(`[${site}][${room}] Channel changed from ${lastCh} to ${msg.ch._id}`);
lastCh = msg.ch._id;
}
(async function(){
// catch dropped crown
if (msg.ch.crown && !msg.ch.crown.hasOwnProperty('participantId')) {
gClient.sendArray([{m:'chown', id: gClient.getOwnParticipant().id}]); // if possible
var avail_time = msg.ch.crown.time + 15000 - gClient.serverTimeOffset;
var ms = avail_time - Date.now();
setTimeout(()=> gClient.sendArray([{m:'chown', id: gClient.getOwnParticipant().id}]) , ms);
}
// transfer crown to owner
if (msg.ppl && msg.ch.crown && msg.ch.crown.participantId == gClient.getOwnParticipant().id) {
var res = await dbClient.query("SELECT owner_mpp__id FROM bridges WHERE mpp_room = $1 AND site = $2;", [room, site]);
if (res.rows.length == 0) return;
var owner = res.rows[0].owner_mpp__id;
if (!owner) return;
msg.ppl.some(part => {
if (part._id == owner) {
gClient.sendArray([{m:'chown', id: part.id}]);
return true;
} else return false;
});
}
})();
});
// MPP to Discord
gClient.on('a', msg => {
if (msg.p._id == gClient.getOwnParticipant()._id) return;
var id = msg.p._id.substr(0,6);
var name = msg.p.name.replace(/discord.gg\//g, 'discord.gg\\/');
var str = `\`${id}\` **${name}:** ${msg.a}`;
str = str.replace(/<@/g, "<\\@");
dSend(str);
});
// Discord to MPP
dClient.on('message', message => {
if (message.channel.id !== DiscordChannelID || message.author.bot || message.content.startsWith('!')) return;
var str = message.cleanContent;
var arr = [];
if (str.startsWith('/') || str.startsWith('\\')) {
arr.push({m:"a", message:
`${message.member.displayName}`
});
} else str = message.member.displayName + ': ' + str;
if (str.startsWith('\\')) str = str.slice(1);
if (message.attachments.first()) str += ' '+message.attachments.first().url;
if (str.length > 512) str = str.substr(0,511) + '…';
arr.push({m:"a", message:str});
gClient.sendArray(arr);
});
// announce join/leave
gClient.on('participant added', participant => {
dSend(`**\`${participant._id.substr(0,6)}\` ${participant.name.replace(/<@/g, "<\\@")} entered the room.**`);
});
gClient.on('participant removed', participant => {
dSend(`**\`${participant._id.substr(0,6)}\` ${participant.name.replace(/<@/g, "<\\@")} left the room.**`);
});
gClient.on('notification', async msg => {
// show notification
_dSend(undefined, {
title: msg.title,
description: msg.text || msg.html
});
// handle bans
if (msg.text && (msg.text.startsWith('Banned from') || msg.text.startsWith('Currently banned from'))) {
// Banned from "{room}" for {n} minutes.
// Currently banned from "{room}" for {n} minutes.
let arr = msg.text.split(' ');
arr.pop();
let minutes = arr.pop();
gClient.stop();
setTimeout(()=>{
gClient.setChannel(room);
gClient.start();
}, minutes*60*1000+3000);
dSend(`**Attempting to rejoin in ${minutes} minutes.**`);
}
});
// autoban perma-banned users
gClient.on("participant added", async part => {
var bans = (await dbClient.query("SELECT bans FROM bridges WHERE discord_channel_id = $1", [DiscordChannelID])).rows[0].bans;
if (!bans) return;
for (let b of bans)
if (part._id.startsWith(b))
gClient.sendArray([{m: "kickban", _id: part._id, ms: 60*60*1000}]);
})
// make room invisible when nobody else is in it
gClient.on("ch", function(msg){
if (gClient.isOwner()) {
if (gClient.countParticipants() <= 1) {
gClient.sendArray([{m:'chset', set: { visible: false }}])
} else {
gClient.sendArray([{m:'chset', set: { visible: true }}])
}
}
});
// addons
{
// collect names
gClient.on('participant update', function(participant){
require('./namecollector').collect(participant);
});
// record raw data
require('./datacollector')(gClient, site, room, DiscordChannel);
}
if (!clients[site]) clients[site] = {};
clients[site][room] = gClient;
};
// start
(async function () {
var res = await dbClient.query('SELECT * FROM bridges;');
var sites = {};
res.rows.forEach(row => {
if (row.disabled) return;
if (!sites[row.site]) sites[row.site] = [];
sites[row.site].push(row);
});
for (let site in sites) {
let arr = sites[site];
arr.sort((a, b) => {return a.position - b.position});
let i = 0;
arr.forEach(bridge => {
setTimeout(function(){
createMPPbridge(bridge.mpp_room, bridge.discord_channel_id, bridge.site, bridge.webhook_id, bridge.webhook_token, bridge.owner_mpp__id);
}, i);
i = i + 2000;
});
}
})();
// commands
commands.bridge = require('./commands/bridge');
commands.unbridge = require('./commands/unbridge');
commands.chown = require('./commands/chown');
commands.list = require('./commands/list');
commands.ban = require('./commands/ban');

View File

@ -0,0 +1,37 @@
var nameCollector = module.exports = {
collection: mdbClient.db('heroku_jrtfvpd9').collection('ppl'),
collect: async function (participant) {
if (config.testmode) return;
if (participant.name == "Anonymous" || participant.name == "Anonymoose") return;
var newMsg = function(continued){
var str = `__**${participant._id}**__${continued ? ' (continued)' : ''}\n${participant.name}`;
return dClient.channels.get(config.channels.name_collection).send(str);
}
var document = await this.collection.findOne({_id: participant._id});
if (document) {
// update person
if (document.names.includes(participant.name)) return;
document.names.push(participant.name);
this.collection.updateOne({_id: participant._id}, {$set:{names: document.names}});
let message = await dClient.channels.get(config.channels.name_collection).messages.fetch(document.discord_msg_id);
try {
await message.edit(message.content + ', ' + participant.name);
} catch(e) {
let message = await newMsg(true);
this.collection.updateOne({_id: participant._id}, {$set:{discord_msg_id: message.id}});
}
} else {
// add new person
let message = await newMsg();
nameCollector.collection.insertOne({
_id: participant._id,
discord_msg_id: message.id,
names: [participant.name]
});
}
}
}

View File

@ -11,14 +11,14 @@ global.screenshotter = {
var screenshot = await page.screenshot({type: 'png'});
var filename = `Screenshot of www.multiplayerpiano.com/lobby @ ${new Date().toISOString()}.png`;
var attachment = new Discord.MessageAttachment(screenshot, filename);
await dClient.channels.get('383773548810076163').send(attachment);
await dClient.channels.get(config.channels.mpp_screenshot).send(attachment);
await page.goto('http://ourworldofpixels.com');
await page.evaluate(function(){OWOP.camera.zoom = 1;});
await new Promise(resolve => setTimeout(resolve, 5000));
var screenshot = await page.screenshot({type: 'png'});
var filename = `Screenshot of ourworldofpixels.com/main @ ${new Date().toISOString()}.png`;
var attachment = new Discord.MessageAttachment(screenshot, filename);
await dClient.channels.get('399079481161023492').send(attachment);
await dClient.channels.get(config.channels.owop_screenshot).send(attachment);
await browser.close();
console.log('Finished screen captures');
},

10
src/util.js Normal file
View File

@ -0,0 +1,10 @@
process.on('unhandledRejection', error => {
console.error(error.stack);
});
process.on('uncaughtException', error => {
console.error(error.stack);
});
Array.prototype.random = function () {
return this[Math.floor(Math.random() * this.length)]
}