Merge branch 'typescript' into music

This commit is contained in:
Keanu Timmermans 2020-10-22 16:44:49 +02:00 committed by GitHub
commit 9348bda210
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1420 additions and 1254 deletions

3
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Specific to this repository # Specific to this repository
dist/ dist/
data/ data/*
!data/endpoints.json
tmp/ tmp/
test* test*
!test/ !test/

31
data/endpoints.json Normal file
View File

@ -0,0 +1,31 @@
{
"sfw": {
"tickle": "/img/tickle",
"slap": "/img/slap",
"poke": "/img/poke",
"pat": "/img/pat",
"neko": "/img/neko",
"meow": "/img/meow",
"lizard": "/img/lizard",
"kiss": "/img/kiss",
"hug": "/img/hug",
"foxGirl": "/img/fox_girl",
"feed": "/img/feed",
"cuddle": "/img/cuddle",
"why": "/why",
"catText": "/cat",
"fact": "/fact",
"nekoGif": "/img/ngif",
"kemonomimi": "/img/kemonomimi",
"holo": "/img/holo",
"smug": "/img/smug",
"baka": "/img/baka",
"woof": "/img/woof",
"spoiler": "/spoiler",
"wallpaper": "/img/wallpaper",
"goose": "/img/goose",
"gecg": "/img/gecg",
"avatar": "/img/avatar",
"waifu": "/img/waifu"
}
}

521
package-lock.json generated
View File

@ -59,10 +59,16 @@
"integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
"dev": true "dev": true
}, },
"@types/ms": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==",
"dev": true
},
"@types/node": { "@types/node": {
"version": "14.0.22", "version": "14.14.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.22.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.2.tgz",
"integrity": "sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g==", "integrity": "sha512-jeYJU2kl7hL9U5xuI/BhKPZ4vqGM/OmK6whiFAXVhlstzZhVamWhDSmHyGLIp+RVyuF9/d0dqr2P85aFj4BvJg==",
"dev": true "dev": true
}, },
"@types/through": { "@types/through": {
@ -75,14 +81,20 @@
} }
}, },
"@types/ws": { "@types/ws": {
"version": "7.2.6", "version": "7.2.7",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.6.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.7.tgz",
"integrity": "sha512-Q07IrQUSNpr+cXU4E4LtkSIBPie5GLZyyMC1QtQYRLWz701+XcoVygGUZgvLqElq1nU4ICldMYPnexlBsg3dqQ==", "integrity": "sha512-UUFC/xxqFLP17hTva8/lVT0SybLUrfSD9c+iapKb0fEiC8uoDbA+xuZ3pAN603eW+bY8ebSMLm9jXdIPnD0ZgA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
"abort-controller": { "abort-controller": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@ -144,18 +156,6 @@
"sprintf-js": "~1.0.2" "sprintf-js": "~1.0.2"
} }
}, },
"array.prototype.map": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
"integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1",
"es-array-method-boxes-properly": "^1.0.0",
"is-string": "^1.0.4"
}
},
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -233,9 +233,9 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
}, },
"chokidar": { "chokidar": {
"version": "3.4.2", "version": "3.4.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
"integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"anymatch": "~3.1.1", "anymatch": "~3.1.1",
@ -245,7 +245,7 @@
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
"normalize-path": "~3.0.0", "normalize-path": "~3.0.0",
"readdirp": "~3.4.0" "readdirp": "~3.5.0"
} }
}, },
"cli-cursor": { "cli-cursor": {
@ -351,12 +351,12 @@
} }
}, },
"debug": { "debug": {
"version": "4.1.1", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"dev": true, "dev": true,
"requires": { "requires": {
"ms": "^2.1.1" "ms": "2.1.2"
} }
}, },
"decamelize": { "decamelize": {
@ -365,15 +365,6 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true "dev": true
}, },
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
},
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -422,57 +413,6 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
}, },
"es-abstract": {
"version": "1.17.6",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.0",
"is-regex": "^1.1.0",
"object-inspect": "^1.7.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
},
"es-array-method-boxes-properly": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
"integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
"dev": true
},
"es-get-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
"integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
"dev": true,
"requires": {
"es-abstract": "^1.17.4",
"has-symbols": "^1.0.1",
"is-arguments": "^1.0.4",
"is-map": "^2.0.1",
"is-set": "^2.0.1",
"is-string": "^1.0.5",
"isarray": "^2.0.5"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -542,13 +482,10 @@
} }
}, },
"flat": { "flat": {
"version": "4.1.0", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
"dev": true, "dev": true
"requires": {
"is-buffer": "~2.0.3"
}
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.5.10", "version": "1.5.10",
@ -592,12 +529,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"get-caller-file": { "get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -633,26 +564,11 @@
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true "dev": true
}, },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": { "has-flag": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
}, },
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true
},
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -684,9 +600,9 @@
"dev": true "dev": true
}, },
"inquirer": { "inquirer": {
"version": "7.3.1", "version": "7.3.3",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.1.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
"integrity": "sha512-/+vOpHQHhoh90Znev8BXiuw1TDQ7IDxWsQnFafUEoK5+4uN5Eoz1p+3GqOj/NtzEi9VzWKQcV9Bm+i8moxedsA==", "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
"requires": { "requires": {
"ansi-escapes": "^4.2.1", "ansi-escapes": "^4.2.1",
"chalk": "^4.1.0", "chalk": "^4.1.0",
@ -694,7 +610,7 @@
"cli-width": "^3.0.0", "cli-width": "^3.0.0",
"external-editor": "^3.0.3", "external-editor": "^3.0.3",
"figures": "^3.0.0", "figures": "^3.0.0",
"lodash": "^4.17.16", "lodash": "^4.17.19",
"mute-stream": "0.0.8", "mute-stream": "0.0.8",
"run-async": "^2.4.0", "run-async": "^2.4.0",
"rxjs": "^6.6.0", "rxjs": "^6.6.0",
@ -703,12 +619,6 @@
"through": "^2.3.6" "through": "^2.3.6"
} }
}, },
"is-arguments": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
"integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
"dev": true
},
"is-binary-path": { "is-binary-path": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -718,24 +628,6 @@
"binary-extensions": "^2.0.0" "binary-extensions": "^2.0.0"
} }
}, },
"is-buffer": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
"dev": true
},
"is-callable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
"integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
"dev": true
},
"is-date-object": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
"dev": true
},
"is-extglob": { "is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -756,12 +648,6 @@
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
} }
}, },
"is-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
"integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
"dev": true
},
"is-number": { "is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -769,45 +655,9 @@
"dev": true "dev": true
}, },
"is-plain-obj": { "is-plain-obj": {
"version": "1.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"dev": true
},
"is-regex": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
"integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
},
"is-set": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
"integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
"dev": true
},
"is-string": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
"dev": true
},
"is-symbol": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
},
"isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true "dev": true
}, },
"isexe": { "isexe": {
@ -816,22 +666,6 @@
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true "dev": true
}, },
"iterate-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
"integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
"dev": true
},
"iterate-value": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
"integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
"dev": true,
"requires": {
"es-get-iterator": "^1.0.2",
"iterate-iterator": "^1.0.1"
}
},
"js-yaml": { "js-yaml": {
"version": "3.14.0", "version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
@ -861,9 +695,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.19", "version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
}, },
"log-symbols": { "log-symbols": {
"version": "4.0.0", "version": "4.0.0",
@ -914,15 +748,16 @@
} }
}, },
"mocha": { "mocha": {
"version": "8.1.2", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.2.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.0.tgz",
"integrity": "sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw==", "integrity": "sha512-lEWEMq2LMfNJMKeuEwb5UELi+OgFDollXaytR5ggQcHpzG3NP/R7rvixAvF+9/lLsTWhWG+4yD2M70GsM06nxw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1", "ansi-colors": "4.1.1",
"browser-stdout": "1.3.1", "browser-stdout": "1.3.1",
"chokidar": "3.4.2", "chokidar": "3.4.3",
"debug": "4.1.1", "debug": "4.2.0",
"diff": "4.0.2", "diff": "4.0.2",
"escape-string-regexp": "4.0.0", "escape-string-regexp": "4.0.0",
"find-up": "5.0.0", "find-up": "5.0.0",
@ -933,17 +768,16 @@
"log-symbols": "4.0.0", "log-symbols": "4.0.0",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"ms": "2.1.2", "ms": "2.1.2",
"object.assign": "4.1.0", "nanoid": "3.1.12",
"promise.allsettled": "1.0.2", "serialize-javascript": "5.0.1",
"serialize-javascript": "4.0.0", "strip-json-comments": "3.1.1",
"strip-json-comments": "3.0.1", "supports-color": "7.2.0",
"supports-color": "7.1.0",
"which": "2.0.2", "which": "2.0.2",
"wide-align": "1.1.3", "wide-align": "1.1.3",
"workerpool": "6.0.0", "workerpool": "6.0.2",
"yargs": "13.3.2", "yargs": "13.3.2",
"yargs-parser": "13.1.2", "yargs-parser": "13.1.2",
"yargs-unparser": "1.6.1" "yargs-unparser": "2.0.0"
}, },
"dependencies": { "dependencies": {
"escape-string-regexp": { "escape-string-regexp": {
@ -951,25 +785,39 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true "dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
} }
} }
}, },
"moment": { "moment": {
"version": "2.27.0", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
}, },
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"mute-stream": { "mute-stream": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
}, },
"nanoid": {
"version": "3.1.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz",
"integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==",
"dev": true
},
"node-cleanup": { "node-cleanup": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
@ -987,30 +835,6 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true "dev": true
}, },
"object-inspect": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
"dev": true
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
},
"object.assign": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.0",
"object-keys": "^1.0.11"
}
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -1021,13 +845,18 @@
} }
}, },
"onetime": { "onetime": {
"version": "5.1.0", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"requires": { "requires": {
"mimic-fn": "^2.1.0" "mimic-fn": "^2.1.0"
} }
}, },
"os": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz",
"integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M="
},
"os-tmpdir": { "os-tmpdir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@ -1101,19 +930,6 @@
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz",
"integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw==" "integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw=="
}, },
"promise.allsettled": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
"integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
"dev": true,
"requires": {
"array.prototype.map": "^1.0.1",
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1",
"function-bind": "^1.1.1",
"iterate-value": "^1.0.0"
}
},
"ps-tree": { "ps-tree": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
@ -1133,9 +949,9 @@
} }
}, },
"readdirp": { "readdirp": {
"version": "3.4.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
"integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"picomatch": "^2.2.1" "picomatch": "^2.2.1"
@ -1187,9 +1003,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"serialize-javascript": { "serialize-javascript": {
"version": "4.0.0", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
"dev": true, "dev": true,
"requires": { "requires": {
"randombytes": "^2.1.0" "randombytes": "^2.1.0"
@ -1282,26 +1098,6 @@
"strip-ansi": "^6.0.0" "strip-ansi": "^6.0.0"
} }
}, },
"string.prototype.trimend": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
"integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"string.prototype.trimstart": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
"integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"strip-ansi": { "strip-ansi": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
@ -1311,9 +1107,9 @@
} }
}, },
"strip-json-comments": { "strip-json-comments": {
"version": "3.0.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true "dev": true
}, },
"supports-color": { "supports-color": {
@ -1388,9 +1184,9 @@
"integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ=="
}, },
"typescript": { "typescript": {
"version": "3.9.6", "version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"dev": true "dev": true
}, },
"which": { "which": {
@ -1451,9 +1247,9 @@
} }
}, },
"workerpool": { "workerpool": {
"version": "6.0.0", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz",
"integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==",
"dev": true "dev": true
}, },
"wrap-ansi": { "wrap-ansi": {
@ -1660,127 +1456,28 @@
} }
}, },
"yargs-unparser": { "yargs-unparser": {
"version": "1.6.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
"integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^5.3.1", "camelcase": "^6.0.0",
"decamelize": "^1.2.0", "decamelize": "^4.0.0",
"flat": "^4.1.0", "flat": "^5.0.2",
"is-plain-obj": "^1.1.0", "is-plain-obj": "^2.1.0"
"yargs": "^14.2.3"
}, },
"dependencies": { "dependencies": {
"ansi-regex": { "camelcase": {
"version": "4.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "integrity": "sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==",
"dev": true "dev": true
}, },
"emoji-regex": { "decamelize": {
"version": "7.0.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
"dev": true "dev": true
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
},
"yargs": {
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
"integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^15.0.1"
}
},
"yargs-parser": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
"integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
} }
} }
}, },

View File

@ -8,19 +8,22 @@
"chalk": "^4.1.0", "chalk": "^4.1.0",
"discord.js": "^12.4.0", "discord.js": "^12.4.0",
"discord.js-lavalink-lib": "^0.1.7", "discord.js-lavalink-lib": "^0.1.7",
"inquirer": "^7.3.1", "inquirer": "^7.3.3",
"moment": "^2.27.0" "moment": "^2.29.1",
"ms": "^2.1.2",
"os": "^0.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/inquirer": "^6.5.0", "@types/inquirer": "^6.5.0",
"@types/mocha": "^8.0.3", "@types/mocha": "^8.0.3",
"@types/node": "^14.0.22", "@types/ms": "^0.7.31",
"@types/ws": "^7.2.6", "@types/node": "^14.14.2",
"mocha": "^8.1.2", "@types/ws": "^7.2.7",
"mocha": "^8.2.0",
"prettier": "2.1.2", "prettier": "2.1.2",
"ts-node": "^9.0.0", "ts-node": "^9.0.0",
"tsc-watch": "^4.2.9", "tsc-watch": "^4.2.9",
"typescript": "^3.9.6" "typescript": "^3.9.7"
}, },
"scripts": { "scripts": {
"build": "tsc && npm prune --production", "build": "tsc && npm prune --production",

View File

@ -1,192 +1,228 @@
import Command from '../core/command'; import Command from '../core/command';
import { CommonLibrary, logs, botHasPermission } from '../core/lib'; import { CommonLibrary, logs, botHasPermission, clean } from '../core/lib';
import { Config, Storage } from '../core/structures'; import { Config, Storage } from '../core/structures';
import { PermissionNames, getPermissionLevel } from '../core/permissions'; import { PermissionNames, getPermissionLevel } from '../core/permissions';
import { Permissions } from 'discord.js'; import { Permissions } from 'discord.js';
import * as discord from 'discord.js'; import * as discord from 'discord.js';
function getLogBuffer(type: string) { function getLogBuffer(type: string) {
return { return {
files: [ files: [
{ {
attachment: Buffer.alloc(logs[type].length, logs[type]), attachment: Buffer.alloc(logs[type].length, logs[type]),
name: `${Date.now()}.${type}.log`, name: `${Date.now()}.${type}.log`,
}, },
], ],
}; };
} }
const activities = ['playing', 'listening', 'streaming', 'watching']; const activities = ['playing', 'listening', 'streaming', 'watching'];
const statuses = ['online', 'idle', 'dnd', 'invisible'];
export default new Command({
description: export default new Command({
"An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", description:
async run($: CommonLibrary): Promise<any> { "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.",
if (!$.member) async run($: CommonLibrary): Promise<any> {
return $.channel.send( if (!$.member)
"Couldn't find a member object for you! Did you make sure you used this in a server?", return $.channel.send(
); "Couldn't find a member object for you! Did you make sure you used this in a server?",
const permLevel = getPermissionLevel($.member); );
$.channel.send( const permLevel = getPermissionLevel($.member);
`${$.author.toString()}, your permission level is \`${ $.channel.send(
PermissionNames[permLevel] `${$.author.toString()}, your permission level is \`${
}\` (${permLevel}).`, PermissionNames[permLevel]
); }\` (${permLevel}).`,
}, );
subcommands: { },
set: new Command({ subcommands: {
description: 'Set different per-guild settings for the bot.', set: new Command({
run: 'You have to specify the option you want to set.', description: 'Set different per-guild settings for the bot.',
permission: Command.PERMISSIONS.ADMIN, run: 'You have to specify the option you want to set.',
subcommands: { permission: Command.PERMISSIONS.ADMIN,
prefix: new Command({ subcommands: {
description: prefix: new Command({
'Set a custom prefix for your guild. Removes your custom prefix if none is provided.', description:
usage: '(<prefix>)', 'Set a custom prefix for your guild. Removes your custom prefix if none is provided.',
async run($: CommonLibrary): Promise<any> { usage: '(<prefix>)',
Storage.getGuild($.guild?.id || 'N/A').prefix = null; async run($: CommonLibrary): Promise<any> {
Storage.save(); Storage.getGuild($.guild?.id || 'N/A').prefix = null;
$.channel.send( Storage.save();
`The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.`, $.channel.send(
); `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.`,
}, );
any: new Command({ },
async run($: CommonLibrary): Promise<any> { any: new Command({
Storage.getGuild($.guild?.id || 'N/A').prefix = $.args[0]; async run($: CommonLibrary): Promise<any> {
Storage.save(); Storage.getGuild($.guild?.id || 'N/A').prefix = $.args[0];
$.channel.send( Storage.save();
`The custom prefix for this guild is now \`${$.args[0]}\`.`, $.channel.send(
); `The custom prefix for this guild is now \`${$.args[0]}\`.`,
}, );
}), },
}), }),
}, }),
}), },
diag: new Command({ }),
description: 'Requests a debug log with the "info" verbosity level.', diag: new Command({
permission: Command.PERMISSIONS.BOT_SUPPORT, description: 'Requests a debug log with the "info" verbosity level.',
async run($: CommonLibrary): Promise<any> { permission: Command.PERMISSIONS.BOT_SUPPORT,
$.channel.send(getLogBuffer('info')); async run($: CommonLibrary): Promise<any> {
}, $.channel.send(getLogBuffer('info'));
any: new Command({ },
description: `Select a verbosity to listen to. Available levels: \`[${Object.keys( any: new Command({
logs, description: `Select a verbosity to listen to. Available levels: \`[${Object.keys(
).join(', ')}]\``, logs,
async run($: CommonLibrary): Promise<any> { ).join(', ')}]\``,
const type = $.args[0]; async run($: CommonLibrary): Promise<any> {
const type = $.args[0];
if (type in logs) $.channel.send(getLogBuffer(type));
else if (type in logs) $.channel.send(getLogBuffer(type));
$.channel.send( else
`Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys( $.channel.send(
logs, `Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys(
).join(', ')}]\`.`, logs,
); ).join(', ')}]\`.`,
}, );
}), },
}), }),
status: new Command({ }),
description: "Changes the bot's status.", status: new Command({
permission: Command.PERMISSIONS.BOT_SUPPORT, description: "Changes the bot's status.",
async run($: CommonLibrary): Promise<any> { permission: Command.PERMISSIONS.BOT_SUPPORT,
$.channel.send('Setting status to `online`...'); async run($: CommonLibrary): Promise<any> {
}, $.channel.send('Setting status to `online`...');
any: new Command({ },
description: `Select a status to set to. Available statuses: \`online\`, \`idle\`, \`dnd\`, \`invisible\``, any: new Command({
async run($: CommonLibrary): Promise<any> { description: `Select a status to set to. Available statuses: \`[${statuses.join(
let statuses = ['online', 'idle', 'dnd', 'invisible']; ', ',
if (!statuses.includes($.args[0])) )}]\`.`,
return $.channel.send("That status doesn't exist!"); async run($: CommonLibrary): Promise<any> {
else { if (!statuses.includes($.args[0]))
$.client.user?.setStatus($.args[0]); return $.channel.send("That status doesn't exist!");
$.channel.send(`Setting status to \`${$.args[0]}\`...`); else {
} $.client.user?.setStatus($.args[0]);
}, $.channel.send(`Setting status to \`${$.args[0]}\`...`);
}), }
}), },
purge: new Command({ }),
description: 'Purges bot messages.', }),
permission: Command.PERMISSIONS.BOT_SUPPORT, purge: new Command({
async run($: CommonLibrary): Promise<any> { description: 'Purges bot messages.',
if ($.message.channel instanceof discord.DMChannel) { permission: Command.PERMISSIONS.BOT_SUPPORT,
return; async run($: CommonLibrary): Promise<any> {
} if ($.message.channel instanceof discord.DMChannel) {
$.message.delete(); return;
const msgs = await $.channel.messages.fetch({ }
limit: 100, $.message.delete();
}); const msgs = await $.channel.messages.fetch({
const travMessages = msgs.filter( limit: 100,
(m) => m.author.id === $.client.user?.id, });
); const travMessages = msgs.filter(
(m) => m.author.id === $.client.user?.id,
await $.message.channel );
.send(`Found ${travMessages.size} messages to delete.`)
.then((m) => await $.message.channel
m.delete({ .send(`Found ${travMessages.size} messages to delete.`)
timeout: 5000, .then((m) =>
}), m.delete({
); timeout: 5000,
await $.message.channel.bulkDelete(travMessages); }),
}, );
}), await $.message.channel.bulkDelete(travMessages);
nick: new Command({ },
description: "Change the bot's nickname.", }),
permission: Command.PERMISSIONS.BOT_SUPPORT, clear: new Command({
async run($: CommonLibrary): Promise<any> { description: "Clears a given amount of messages.",
const nickName = $.args.join(' '); usage: "<amount>",
const trav = $.guild?.members.cache.find( run: "A number was not provided.",
(member) => member.id === $.client.user?.id, number: new Command({
); description: "Amount of messages to delete.",
await trav?.setNickname(nickName); async run($: CommonLibrary): Promise<any> {
if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES)) $.message.delete();
$.message.delete({ timeout: 5000 }).catch($.handler.bind($)); const fetched = await $.channel.messages.fetch({
$.channel limit: $.args[0],
.send(`Nickname set to \`${nickName}\``) });
.then((m) => m.delete({ timeout: 5000 })); /// @ts-ignore
}, $.channel.bulkDelete(fetched)
}), .catch((error: any) => $.channel.send(`Error: ${error}`));
guilds: new Command({ }
description: 'Shows a list of all guilds the bot is a member of.', })
permission: Command.PERMISSIONS.BOT_SUPPORT, }),
async run($: CommonLibrary): Promise<any> { eval: new Command({
const guildList = $.client.guilds.cache.array().map((e) => e.name); description: 'Evaluate code.',
$.channel.send(guildList); usage: '<code>',
}, permission: Command.PERMISSIONS.BOT_OWNER,
}), async run($: CommonLibrary): Promise<any> {
activity: new Command({ try {
description: 'Set the activity of the bot.', const code = $.args.join(' ');
permission: Command.PERMISSIONS.BOT_SUPPORT, let evaled = eval(code);
usage: '<type> <string>',
async run($: CommonLibrary): Promise<any> { if (typeof evaled !== 'string')
$.client.user?.setActivity('.help', { evaled = require('util').inspect(evaled);
type: 'LISTENING', $.channel.send(clean(evaled), { code: 'x1' });
}); } catch (err) {
$.channel.send('Activity set to default.'); $.channel.send(`\`ERROR\` \`\`\`x1\n${clean(err)}\n\`\`\``);
}, }
any: new Command({ },
description: `Select an activity type to set. Available levels: \`[${activities.join( }),
', ', nick: new Command({
)}]\``, description: "Change the bot's nickname.",
async run($: CommonLibrary): Promise<any> { permission: Command.PERMISSIONS.BOT_SUPPORT,
const type = $.args[0]; async run($: CommonLibrary): Promise<any> {
const nickName = $.args.join(' ');
if (activities.includes(type)) { const trav = $.guild?.members.cache.find(
$.client.user?.setActivity($.args.slice(1).join(' '), { (member) => member.id === $.client.user?.id,
type: $.args[0].toUpperCase(), );
}); await trav?.setNickname(nickName);
$.channel.send( if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES))
`Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args $.message.delete({ timeout: 5000 }).catch($.handler.bind($));
.slice(1) $.channel
.join(' ')}\`.`, .send(`Nickname set to \`${nickName}\``)
); .then((m) => m.delete({ timeout: 5000 }));
} else },
$.channel.send( }),
`Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join( guilds: new Command({
', ', description: 'Shows a list of all guilds the bot is a member of.',
)}]\`.`, permission: Command.PERMISSIONS.BOT_SUPPORT,
); async run($: CommonLibrary): Promise<any> {
}, const guildList = $.client.guilds.cache.array().map((e) => e.name);
}), $.channel.send(guildList);
}), },
}, }),
}); activity: new Command({
description: 'Set the activity of the bot.',
permission: Command.PERMISSIONS.BOT_SUPPORT,
usage: '<type> <string>',
async run($: CommonLibrary): Promise<any> {
$.client.user?.setActivity('.help', {
type: 'LISTENING',
});
$.channel.send('Activity set to default.');
},
any: new Command({
description: `Select an activity type to set. Available levels: \`[${activities.join(
', ',
)}]\``,
async run($: CommonLibrary): Promise<any> {
const type = $.args[0];
if (activities.includes(type)) {
$.client.user?.setActivity($.args.slice(1).join(' '), {
type: $.args[0].toUpperCase(),
});
$.channel.send(
`Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args
.slice(1)
.join(' ')}\`.`,
);
} else
$.channel.send(
`Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join(
', ',
)}]\`.`,
);
},
}),
}),
},
});

View File

@ -0,0 +1,50 @@
import Command from '../../core/command';
import { CommonLibrary } from '../../core/lib';
export default new Command({
description: "Gives specified user a cookie.",
usage: "['all'/@user]",
run: ":cookie: Here's a cookie!",
any: new Command({
async run($: CommonLibrary): Promise<any> {
if ($.args[0] == "all") return $.channel.send(`${$.author} gave everybody a cookie!`)
}
}),
user: new Command({
description: "User to give cookie to.",
async run($: CommonLibrary): Promise<any> {
const sender = $.author;
const mention = $.message.mentions.users.first();
if (!mention) return;
const cookies = [
`has given <@${mention.id}> a chocolate chip cookie!`,
`has given <@${mention.id}> a soft homemade oatmeal cookie!`,
`has given <@${mention.id}> a plain, dry, old cookie. It was the last one in the bag. Gross.`,
`gives <@${mention.id}> a sugar cookie. What, no frosting and sprinkles? 0/10 would not touch.`,
`gives <@${mention.id}> a chocolate chip cookie. Oh wait, those are raisins. Bleck!`,
`gives <@${mention.id}> an enormous cookie. Poking it gives you more cookies. Weird.`,
`gives <@${mention.id}> a fortune cookie. It reads "Why aren't you working on any projects?"`,
`gives <@${mention.id}> a fortune cookie. It reads "Give that special someone a compliment"`,
`gives <@${mention.id}> a fortune cookie. It reads "Take a risk!"`,
`gives <@${mention.id}> a fortune cookie. It reads "Go outside."`,
`gives <@${mention.id}> a fortune cookie. It reads "Don't forget to eat your veggies!"`,
`gives <@${mention.id}> a fortune cookie. It reads "Do you even lift?"`,
`gives <@${mention.id}> a fortune cookie. It reads "m808 pls"`,
`gives <@${mention.id}> a fortune cookie. It reads "If you move your hips, you'll get all the ladies."`,
`gives <@${mention.id}> a fortune cookie. It reads "I love you."`,
`gives <@${mention.id}> a Golden Cookie. You can't eat it because it is made of gold. Dammit.`,
`gives <@${mention.id}> an Oreo cookie with a glass of milk!`,
`gives <@${mention.id}> a rainbow cookie made with love :heart:`,
`gives <@${mention.id}> an old cookie that was left out in the rain, it's moldy.`,
`bakes <@${mention.id}> fresh cookies, it smells amazing.`,
];
if (mention.id == sender.id)
return $.channel.send("You can't give yourself cookies!");
$.channel.send(
`:cookie: <@${sender.id}> ` +
cookies[Math.floor(Math.random() * cookies.length)],
);
}
})
})

26
src/commands/fun/neko.ts Normal file
View File

@ -0,0 +1,26 @@
/// @ts-nocheck
import { URL } from 'url'
import FileManager from '../../core/storage';
import Command from '../../core/command';
import { CommonLibrary, getContent } from '../../core/lib';
const endpoints = FileManager.read('endpoints');
export default new Command({
description: 'Provides you with a random image with the selected argument.',
async run($: CommonLibrary): Promise<any> {
console.log(endpoints.sfw)
$.channel.send(`Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(', ')}]\`.`)
},
any: new Command({
description: "Image type to send.",
async run($: CommonLibrary): Promise<any> {
if (!$.args[0] in endpoints.sfw)
return $.channel.send("Couldn't find that endpoint!");
let baseURL = 'https://nekos.life/api/v2';
let url = new URL(`${baseURL}${endpoints.sfw[$.args[0]]}`);
const content = await getContent(url.toString())
$.channel.send(content.url)
},
})
});

68
src/commands/fun/ok.ts Normal file
View File

@ -0,0 +1,68 @@
import Command from '../../core/command';
import { CommonLibrary } from '../../core/lib';
export default new Command({
description: "Sends random ok message.",
async run($: CommonLibrary): Promise<any> {
const responses = [
'boomer',
'zoomer',
'the last generationer',
'the last airbender',
'fire nation',
'fire lord',
'guy fieri',
'guy from final fight',
'haggar',
'Max Thunder from Streets of Rage 2',
'police guy who fires bazookas',
'Mr. X',
'Leon Its Wrong If Its Not Ada Wong S. Kennedy.',
'Jill',
'JFK',
'george bush',
'obama',
'the world',
'copy of scott pilgrim vs the world',
'ok',
'ko',
'Hot Daddy Venomous',
'big daddy',
'John Cena',
'BubbleSpurJarJarBinks',
'T-Series',
'pewdiepie',
'markiplier',
'jacksepticeye',
'vanossgaming',
'miniladd',
'Traves',
'Wilbur Soot',
'sootrhianna',
'person with tiny ears',
'anti-rabbit',
'homo sapiens',
'homo',
'cute kitty',
'ugly kitty',
'sadness',
'doomer',
'gloomer',
'bloomer',
'edgelord',
'weeb',
"m'lady",
'Mr. Crabs',
'hand',
'lahoma',
'big man',
'fox',
'pear',
'cat',
'large man',
];
$.channel.send(
'ok ' + responses[Math.floor(Math.random() * responses.length)],
);
}
})

View File

@ -0,0 +1,12 @@
/// @ts-nocheck
import Command from '../../core/command';
import { CommonLibrary, getContent } from '../../core/lib';
export default new Command({
description: 'OwO-ifies the input.',
async run($: CommonLibrary): Promise<any> {
let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(' ')}`);
const content = await getContent(url.toString());
$.channel.send(content.owo);
},
});

View File

@ -1,197 +1,243 @@
import { Guild, MessageEmbed } from 'discord.js'; import { MessageEmbed, version as djsversion } from 'discord.js';
import moment from 'moment'; /// @ts-ignore
import Command from '../core/command'; import { version } from '../../package.json';
import { CommonLibrary } from '../core/lib'; import ms from 'ms';
import { verificationLevels, filterLevels, regions, flags } from '../defs/info'; import os from 'os';
import Command from '../core/command';
export default new Command({ import { CommonLibrary, formatBytes, trimArray } from '../core/lib';
description: import { verificationLevels, filterLevels, regions, flags } from '../defs/info';
'Command to provide all sorts of info about the current server, a user, etc.', import moment from 'moment';
run: 'Please provide an argument.\nFor help, run `%prefix%help info`.', import utc from 'moment';
subcommands: {
avatar: new Command({ export default new Command({
description: "Shows your own, or another user's avatar.", description:
usage: '(<user>)', 'Command to provide all sorts of info about the current server, a user, etc.',
async run($: CommonLibrary): Promise<any> { run: 'Please provide an argument.\nFor help, run `%prefix%help info`.',
$.channel.send( subcommands: {
$.author.displayAvatarURL({ dynamic: true, size: 2048 }), avatar: new Command({
); description: "Shows your own, or another user's avatar.",
}, usage: '(<user>)',
user: new Command({ async run($: CommonLibrary): Promise<any> {
description: "Shows your own, or another user's avatar.", $.channel.send(
async run($: CommonLibrary): Promise<any> { $.author.displayAvatarURL({ dynamic: true, size: 2048 }),
$.channel.send( );
$.args[0].displayAvatarURL({ dynamic: true, size: 2048 }), },
); user: new Command({
}, description: "Shows your own, or another user's avatar.",
}), async run($: CommonLibrary): Promise<any> {
}), $.channel.send(
$.args[0].displayAvatarURL({ dynamic: true, size: 2048 }),
guild: new Command({ );
description: 'Displays info about the current guild.', },
async run($: CommonLibrary): Promise<any> { }),
if ($.guild) { }),
const roles = $.guild.roles.cache
.sort((a, b) => b.position - a.position) bot: new Command({
.map((role) => role.toString()); description: 'Displays info about the bot.',
const members = $.guild.members.cache; async run($: CommonLibrary): Promise<any> {
const channels = $.guild.channels.cache; const core = os.cpus()[0];
const emojis = $.guild.emojis.cache; const embed = new MessageEmbed()
.setThumbnail(
const iconURL = $.guild.iconURL({ dynamic: true }); /// @ts-ignore
const embed = new MessageEmbed() $.client.user?.displayAvatarURL({ dynamic: true, size: 2048 }),
.setDescription(`**Guild information for __${$.guild.name}__**`) )
.setColor('BLUE'); .setColor($.guild?.me?.displayHexColor || 'BLUE')
if (iconURL) .addField('General', [
embed `** Client:** ${$.client.user?.tag} (${$.client.user?.id})`,
.setThumbnail(iconURL) `** Servers:** ${$.client.guilds.cache.size.toLocaleString()}`,
.addField('General', [ `** Users:** ${$.client.guilds.cache
`** Name:** ${$.guild.name}`, .reduce((a: any, b: { memberCount: any }) => a + b.memberCount, 0)
`** ID:** ${$.guild.id}`, .toLocaleString()}`,
`** Owner:** ${$.guild.owner?.user.tag} (${$.guild.ownerID})`, `** Channels:** ${$.client.channels.cache.size.toLocaleString()}`,
`** Region:** ${regions[$.guild.region]}`, `** Creation Date:** ${utc($.client.user?.createdTimestamp).format(
`** Boost Tier:** ${ 'Do MMMM YYYY HH:mm:ss',
$.guild.premiumTier ? `Tier ${$.guild.premiumTier}` : 'None' )}`,
}`, `** Node.JS:** ${process.version}`,
`** Explicit Filter:** ${ `** Version:** v${version}`,
filterLevels[$.guild.explicitContentFilter] `** Discord.JS:** ${djsversion}`,
}`, '\u200b',
`** Verification Level:** ${ ])
verificationLevels[$.guild.verificationLevel] .addField('System', [
}`, `** Platform:** ${process.platform}`,
`** Time Created:** ${moment($.guild.createdTimestamp).format( `** Uptime:** ${ms(os.uptime() * 1000, { long: true })}`,
'LT', `** CPU:**`,
)} ${moment($.guild.createdTimestamp).format('LL')} ${moment( `\u3000 • Cores: ${os.cpus().length}`,
$.guild.createdTimestamp, `\u3000 • Model: ${core.model}`,
).fromNow()})`, `\u3000 • Speed: ${core.speed}MHz`,
'\u200b', `** Memory:**`,
]) `\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`,
.addField('Statistics', [ `\u3000 • Used: ${formatBytes(process.memoryUsage().heapTotal)}`,
`** Role Count:** ${roles.length}`, ])
`** Emoji Count:** ${emojis.size}`, .setTimestamp();
`** Regular Emoji Count:** ${ $.channel.send(embed);
emojis.filter((emoji) => !emoji.animated).size },
}`, }),
`** Animated Emoji Count:** ${
emojis.filter((emoji) => emoji.animated).size guild: new Command({
}`, description: 'Displays info about the current guild.',
`** Member Count:** ${$.guild.memberCount}`, async run($: CommonLibrary): Promise<any> {
`** Humans:** ${ if ($.guild) {
members.filter((member) => !member.user.bot).size const roles = $.guild.roles.cache
}`, .sort((a, b) => b.position - a.position)
`** Bots:** ${ .map((role) => role.toString());
members.filter((member) => member.user.bot).size const members = $.guild.members.cache;
}`, const channels = $.guild.channels.cache;
`** Text Channels:** ${ const emojis = $.guild.emojis.cache;
channels.filter((channel) => channel.type === 'text').size
}`, const iconURL = $.guild.iconURL({ dynamic: true });
`** Voice Channels:** ${ const embed = new MessageEmbed()
channels.filter((channel) => channel.type === 'voice').size .setDescription(`**Guild information for __${$.guild.name}__**`)
}`, .setColor('BLUE');
`** Boost Count:** ${$.guild.premiumSubscriptionCount || '0'}`, if (iconURL)
`\u200b`, embed
]) .setThumbnail(iconURL)
.addField('Presence', [ .addField('General', [
`** Online:** ${ `** Name:** ${$.guild.name}`,
members.filter( `** ID:** ${$.guild.id}`,
(member) => member.presence.status === 'online', `** Owner:** ${$.guild.owner?.user.tag} (${$.guild.ownerID})`,
).size `** Region:** ${regions[$.guild.region]}`,
}`, `** Boost Tier:** ${
`** Idle:** ${ $.guild.premiumTier ? `Tier ${$.guild.premiumTier}` : 'None'
members.filter((member) => member.presence.status === 'idle') }`,
.size `** Explicit Filter:** ${
}`, filterLevels[$.guild.explicitContentFilter]
`** Do Not Disturb:** ${ }`,
members.filter((member) => member.presence.status === 'dnd') `** Verification Level:** ${
.size verificationLevels[$.guild.verificationLevel]
}`, }`,
`** Offline:** ${ `** Time Created:** ${moment($.guild.createdTimestamp).format(
members.filter( 'LT',
(member) => member.presence.status === 'offline', )} ${moment($.guild.createdTimestamp).format('LL')} ${moment(
).size $.guild.createdTimestamp,
}`, ).fromNow()})`,
'\u200b', '\u200b',
]) ])
.addField( .addField('Statistics', [
`Roles [${roles.length - 1}]`, `** Role Count:** ${roles.length}`,
roles.length < 10 `** Emoji Count:** ${emojis.size}`,
? roles.join(', ') `** Regular Emoji Count:** ${
: roles.length > 10 emojis.filter((emoji) => !emoji.animated).size
? this.client.utils.trimArray(roles) }`,
: 'None', `** Animated Emoji Count:** ${
) emojis.filter((emoji) => emoji.animated).size
.setTimestamp(); }`,
`** Member Count:** ${$.guild.memberCount}`,
$.channel.send(embed); `** Humans:** ${
} else { members.filter((member) => !member.user.bot).size
$.channel.send('Please execute this command in a guild.'); }`,
} `** Bots:** ${
}, members.filter((member) => member.user.bot).size
}), }`,
}, `** Text Channels:** ${
user: new Command({ channels.filter((channel) => channel.type === 'text').size
description: 'Displays info about mentioned user.', }`,
async run($: CommonLibrary): Promise<any> { `** Voice Channels:** ${
// Transforms the User object into a GuildMember object of the current guild. channels.filter((channel) => channel.type === 'voice').size
const member = $.guild?.members.resolve($.args[0]); }`,
`** Boost Count:** ${$.guild.premiumSubscriptionCount || '0'}`,
if (!member) `\u200b`,
return $.channel.send( ])
'No member object was found by that user! Are you sure you used this command in a server?', .addField('Presence', [
); `** Online:** ${
members.filter(
const roles = member.roles.cache (member) => member.presence.status === 'online',
.sort( ).size
(a: { position: number }, b: { position: number }) => }`,
b.position - a.position, `** Idle:** ${
) members.filter((member) => member.presence.status === 'idle')
.map((role: { toString: () => any }) => role.toString()) .size
.slice(0, -1); }`,
// @ts-ignore - Discord.js' typings seem to be outdated here. According to their v12 docs, it's User.fetchFlags() instead of User.flags. `** Do Not Disturb:** ${
const userFlags = ((await member.user.fetchFlags()) as UserFlags).toArray(); members.filter((member) => member.presence.status === 'dnd')
.size
const embed = new MessageEmbed() }`,
.setThumbnail( `** Offline:** ${
member.user.displayAvatarURL({ dynamic: true, size: 512 }), members.filter(
) (member) => member.presence.status === 'offline',
.setColor(member.displayHexColor || 'BLUE') ).size
.addField('User', [ }`,
`** Username:** ${member.user.username}`, '\u200b',
`** Discriminator:** ${member.user.discriminator}`, ])
`** ID:** ${member.id}`, .addField(
`** Flags:** ${userFlags.length ? userFlags.join(', ') : 'None'}`, `Roles [${roles.length - 1}]`,
`** Avatar:** [Link to avatar](${member.user.displayAvatarURL({ roles.length < 10
dynamic: true, ? roles.join(', ')
})})`, : roles.length > 10
`** Time Created:** ${moment(member.user.createdTimestamp).format( ? trimArray(roles)
'LT', : 'None',
)} ${moment(member.user.createdTimestamp).format('LL')} ${moment( )
member.user.createdTimestamp, .setTimestamp();
).fromNow()}`,
`** Status:** ${member.user.presence.status}`, $.channel.send(embed);
`** Game:** ${ } else {
member.user.presence.activities || 'Not playing a game.' $.channel.send('Please execute this command in a guild.');
}`, }
]) },
.addField('Member', [ }),
`** Highest Role:** ${ },
member.roles.highest.id === $.guild?.id user: new Command({
? 'None' description: 'Displays info about mentioned user.',
: member.roles.highest.name async run($: CommonLibrary): Promise<any> {
}`, // Transforms the User object into a GuildMember object of the current guild.
`** Server Join Date:** ${moment(member.joinedAt).format('LL LTS')}`, const member = $.guild?.members.resolve($.args[0]);
`** Hoist Role:** ${
member.roles.hoist ? member.roles.hoist.name : 'None' if (!member)
}`, return $.channel.send(
`** Roles:** [${roles.length}]: ${ 'No member object was found by that user! Are you sure you used this command in a server?',
roles.length < 10 );
? roles.join(', ')
: roles.length > 10 const roles = member.roles.cache
? this.client.utils.trimArray(roles) .sort(
: 'None' (a: { position: number }, b: { position: number }) =>
}`, b.position - a.position,
]); )
$.channel.send(embed); .map((role: { toString: () => any }) => role.toString())
}, .slice(0, -1);
}), // @ts-ignore - Discord.js' typings seem to be outdated here. According to their v12 docs, it's User.fetchFlags() instead of User.flags.
}); const userFlags = ((await member.user.fetchFlags()) as UserFlags).toArray();
const embed = new MessageEmbed()
.setThumbnail(
member.user.displayAvatarURL({ dynamic: true, size: 512 }),
)
.setColor(member.displayHexColor || 'BLUE')
.addField('User', [
`** Username:** ${member.user.username}`,
`** Discriminator:** ${member.user.discriminator}`,
`** ID:** ${member.id}`,
`** Flags:** ${userFlags.length ? userFlags.join(', ') : 'None'}`,
`** Avatar:** [Link to avatar](${member.user.displayAvatarURL({
dynamic: true,
})})`,
`** Time Created:** ${moment(member.user.createdTimestamp).format(
'LT',
)} ${moment(member.user.createdTimestamp).format('LL')} ${moment(
member.user.createdTimestamp,
).fromNow()}`,
`** Status:** ${member.user.presence.status}`,
`** Game:** ${
member.user.presence.activities || 'Not playing a game.'
}`,
])
.addField('Member', [
`** Highest Role:** ${
member.roles.highest.id === $.guild?.id
? 'None'
: member.roles.highest.name
}`,
`** Server Join Date:** ${moment(member.joinedAt).format('LL LTS')}`,
`** Hoist Role:** ${
member.roles.hoist ? member.roles.hoist.name : 'None'
}`,
`** Roles:** [${roles.length}]: ${
roles.length < 10
? roles.join(', ')
: roles.length > 10
? this.client.utils.trimArray(roles)
: 'None'
}`,
]);
$.channel.send(embed);
},
}),
});

View File

@ -0,0 +1,29 @@
import Command from '../../core/command';
import { CommonLibrary } from '../../core/lib';
export default new Command({
description: "Renames current voice channel.",
usage: "<name>",
async run($: CommonLibrary): Promise<any> {
const voiceChannel = $.message.member?.voice.channel;
if (!voiceChannel)
return $.channel.send('You are not in a voice channel.');
if (!$.guild?.me?.hasPermission('MANAGE_CHANNELS'))
return $.channel.send(
'I am lacking the required permissions to perform this action.',
);
if ($.args.length === 0)
return $.channel.send(
'Please provide a new voice channel name.',
);
const changeVC = $.guild.channels.resolve(voiceChannel.id);
$.channel
.send(
`Changed channel name from "${voiceChannel}" to "${$.args.join(
' ',
)}".`,
)
/// @ts-ignore
.then(changeVC?.setName($.args.join(' ')));
}
})

View File

@ -0,0 +1,68 @@
import Command from '../../core/command';
import { CommonLibrary } from '../../core/lib';
export default new Command({
description:
'Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.',
usage: 'react <emote name> (<message ID / distance>)',
async run($: CommonLibrary): Promise<any> {
let target;
let distance = 1;
if ($.args.length >= 2) {
const last = $.args[$.args.length - 1];
if (/\d{17,19}/g.test(last)) {
try {
target = await $.channel.messages.fetch(last);
} catch {
return $.channel.send(
`No valid message found by the ID \`${last}\`!`,
);
}
$.args.pop();
}
// The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this.
else if (/^\d+$/g.test(last)) {
distance = parseInt(last);
if (distance >= 0 && distance <= 99) $.args.pop();
else return $.channel.send('Your distance must be between 0 and 99!');
}
}
if (!target) {
// Messages are ordered from latest to earliest.
// You also have to add 1 as well because fetchMessages includes your own message.
target = (
await $.message.channel.messages.fetch({
limit: distance + 1,
})
).last();
}
let anyEmoteIsValid = false;
for (const search of $.args) {
const emoji = $.client.emojis.cache.find(
(emoji) => emoji.name === search,
);
if (emoji) {
// Call the delete function only once to avoid unnecessary errors.
if (!anyEmoteIsValid && distance !== 0) $.message.delete();
anyEmoteIsValid = true;
const reaction = await target?.react(emoji);
// This part is called with a promise because you don't want to wait 5 seconds between each reaction.
setTimeout(() => {
/// @ts-ignore
reaction.users.remove($.client.user);
}, 5000);
}
}
if (!anyEmoteIsValid && !$.message.deleted) $.message.react('❓');
},
});

View File

@ -0,0 +1,14 @@
import Command from '../../core/command';
import { CommonLibrary } from '../../core/lib';
export default new Command({
description: "Repeats your message.",
usage: "<message>",
run: "Please provide a message for me to say!",
any: new Command({
description: "Message to repeat.",
async run($: CommonLibrary): Promise<any> {
$.channel.send(`*${$.author} says:*\n${$.args.join(' ')}`);
}
})
})

View File

@ -0,0 +1,25 @@
import Command from '../../core/command';
import { CommonLibrary } from '../../core/lib';
import * as https from 'https';
export default new Command({
description: 'Shortens a given URL.',
run: 'Please provide a URL.',
any: new Command({
async run($: CommonLibrary): Promise<any> {
https.get(
'https://is.gd/create.php?format=simple&url=' +
encodeURIComponent($.args[0]),
function (res) {
var body = '';
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
$.channel.send(`<${body}>`);
});
},
);
},
}),
});

View File

@ -1,446 +1,506 @@
import { import {
GenericWrapper, GenericWrapper,
NumberWrapper, NumberWrapper,
StringWrapper, StringWrapper,
ArrayWrapper, ArrayWrapper,
} from './wrappers'; } from './wrappers';
import { import {
Client, Client,
Message, Message,
TextChannel, TextChannel,
DMChannel, DMChannel,
NewsChannel, NewsChannel,
Guild, Guild,
User, User,
GuildMember, GuildMember,
Permissions, Permissions,
} from 'discord.js'; } from 'discord.js';
import chalk from 'chalk'; import chalk from 'chalk';
import FileManager from './storage'; import { get } from 'https';
import { eventListeners } from '../events/messageReactionRemove'; import FileManager from './storage';
import { client } from '../index'; import { eventListeners } from '../events/messageReactionRemove';
import { client } from '../index';
/** A type that describes what the library module does. */
export interface CommonLibrary { /** A type that describes what the library module does. */
// Wrapper Object // export interface CommonLibrary {
/** Wraps the value you enter with an object that provides extra functionality and provides common utility functions. */ // Wrapper Object //
(value: number): NumberWrapper; /** Wraps the value you enter with an object that provides extra functionality and provides common utility functions. */
(value: string): StringWrapper; (value: number): NumberWrapper;
<T>(value: T[]): ArrayWrapper<T>; (value: string): StringWrapper;
<T>(value: T): GenericWrapper<T>; <T>(value: T[]): ArrayWrapper<T>;
<T>(value: T): GenericWrapper<T>;
// Common Library Functions //
/** <Promise>.catch($.handler.bind($)) or <Promise>.catch(error => $.handler(error)) */ // Common Library Functions //
handler: (error: Error) => void; /** <Promise>.catch($.handler.bind($)) or <Promise>.catch(error => $.handler(error)) */
log: (...args: any[]) => void; handler: (error: Error) => void;
warn: (...args: any[]) => void; log: (...args: any[]) => void;
error: (...args: any[]) => void; warn: (...args: any[]) => void;
debug: (...args: any[]) => void; error: (...args: any[]) => void;
ready: (...args: any[]) => void; debug: (...args: any[]) => void;
paginate: ( ready: (...args: any[]) => void;
message: Message, paginate: (
senderID: string, message: Message,
total: number, senderID: string,
callback: (page: number) => void, total: number,
duration?: number, callback: (page: number) => void,
) => void; duration?: number,
prompt: ( ) => void;
message: Message, prompt: (
senderID: string, message: Message,
onConfirm: () => void, senderID: string,
duration?: number, onConfirm: () => void,
) => void; duration?: number,
getMemberByUsername: ( ) => void;
guild: Guild, getMemberByUsername: (
username: string, guild: Guild,
) => Promise<GuildMember | undefined>; username: string,
callMemberByUsername: ( ) => Promise<GuildMember | undefined>;
message: Message, callMemberByUsername: (
username: string, message: Message,
onSuccess: (member: GuildMember) => void, username: string,
) => Promise<void>; onSuccess: (member: GuildMember) => void,
) => Promise<void>;
// Dynamic Properties //
args: any[]; // Dynamic Properties //
client: Client; args: any[];
message: Message; client: Client;
channel: TextChannel | DMChannel | NewsChannel; message: Message;
guild: Guild | null; channel: TextChannel | DMChannel | NewsChannel;
author: User; guild: Guild | null;
member: GuildMember | null; author: User;
} member: GuildMember | null;
}
export default function $(value: number): NumberWrapper;
export default function $(value: string): StringWrapper; export default function $(value: number): NumberWrapper;
export default function $<T>(value: T[]): ArrayWrapper<T>; export default function $(value: string): StringWrapper;
export default function $<T>(value: T): GenericWrapper<T>; export default function $<T>(value: T[]): ArrayWrapper<T>;
export default function $(value: any) { export default function $<T>(value: T): GenericWrapper<T>;
if (isType(value, Number)) return new NumberWrapper(value); export default function $(value: any) {
else if (isType(value, String)) return new StringWrapper(value); if (isType(value, Number)) return new NumberWrapper(value);
else if (isType(value, Array)) return new ArrayWrapper(value); else if (isType(value, String)) return new StringWrapper(value);
else return new GenericWrapper(value); else if (isType(value, Array)) return new ArrayWrapper(value);
} else return new GenericWrapper(value);
}
// If you use promises, use this function to display the error in chat.
// Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). // If you use promises, use this function to display the error in chat.
// Case #2: $.channel.send("").catch($.handler.bind($)); --> Manually caught by the user. // Case #1: await $.channel.send(""); --> Automatically caught by Command.execute().
$.handler = function (this: CommonLibrary, error: Error) { // Case #2: $.channel.send("").catch($.handler.bind($)); --> Manually caught by the user.
if (this) $.handler = function (this: CommonLibrary, error: Error) {
this.channel.send( if (this)
`There was an error while trying to execute that command!\`\`\`${ this.channel.send(
error.stack ?? error `There was an error while trying to execute that command!\`\`\`${
}\`\`\``, error.stack ?? error
); }\`\`\``,
else );
$.warn( else
'No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!', $.warn(
); 'No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!',
);
$.error(error);
}; $.error(error);
};
// Logs with different levels of verbosity.
export const logs: { [type: string]: string } = { // Logs with different levels of verbosity.
error: '', export const logs: { [type: string]: string } = {
warn: '', error: '',
info: '', warn: '',
verbose: '', info: '',
}; verbose: '',
};
let enabled = true;
export function setConsoleActivated(activated: boolean) { let enabled = true;
enabled = activated; export function setConsoleActivated(activated: boolean) {
} enabled = activated;
}
// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log.
// General Purpose Logger // The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log.
$.log = (...args: any[]) => { // General Purpose Logger
if (enabled) $.log = (...args: any[]) => {
console.log( if (enabled)
chalk.white.bgGray(formatTimestamp()), console.log(
chalk.black.bgWhite('INFO'), chalk.white.bgGray(formatTimestamp()),
...args, chalk.black.bgWhite('INFO'),
); ...args,
const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(' ')}\n`; );
logs.info += text; const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(' ')}\n`;
logs.verbose += text; logs.info += text;
}; logs.verbose += text;
// "It'll still work, but you should really check up on this." };
$.warn = (...args: any[]) => { // "It'll still work, but you should really check up on this."
if (enabled) $.warn = (...args: any[]) => {
console.warn( if (enabled)
chalk.white.bgGray(formatTimestamp()), console.warn(
chalk.black.bgYellow('WARN'), chalk.white.bgGray(formatTimestamp()),
...args, chalk.black.bgYellow('WARN'),
); ...args,
const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(' ')}\n`; );
logs.warn += text; const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(' ')}\n`;
logs.info += text; logs.warn += text;
logs.verbose += text; logs.info += text;
}; logs.verbose += text;
// Used for anything which prevents the program from actually running. };
$.error = (...args: any[]) => { // Used for anything which prevents the program from actually running.
if (enabled) $.error = (...args: any[]) => {
console.error( if (enabled)
chalk.white.bgGray(formatTimestamp()), console.error(
chalk.white.bgRed('ERROR'), chalk.white.bgGray(formatTimestamp()),
...args, chalk.white.bgRed('ERROR'),
); ...args,
const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(' ')}\n`; );
logs.error += text; const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(' ')}\n`;
logs.warn += text; logs.error += text;
logs.info += text; logs.warn += text;
logs.verbose += text; logs.info += text;
}; logs.verbose += text;
// Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose". };
// $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> <path>/::(<object>.)<function>(<args>) = <value> // Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose".
// Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests. // $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> <path>/::(<object>.)<function>(<args>) = <value>
$.debug = (...args: any[]) => { // Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests.
if (process.argv[2] === 'dev' && enabled) $.debug = (...args: any[]) => {
console.debug( if (process.argv[2] === 'dev' && enabled)
chalk.white.bgGray(formatTimestamp()), console.debug(
chalk.white.bgBlue('DEBUG'), chalk.white.bgGray(formatTimestamp()),
...args, chalk.white.bgBlue('DEBUG'),
); ...args,
const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(' ')}\n`; );
logs.verbose += text; const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(' ')}\n`;
}; logs.verbose += text;
// Used once at the start of the program when the bot loads. };
$.ready = (...args: any[]) => { // Used once at the start of the program when the bot loads.
if (enabled) $.ready = (...args: any[]) => {
console.log( if (enabled)
chalk.white.bgGray(formatTimestamp()), console.log(
chalk.black.bgGreen('READY'), chalk.white.bgGray(formatTimestamp()),
...args, chalk.black.bgGreen('READY'),
); ...args,
const text = `[${formatUTCTimestamp()}] [READY] ${args.join(' ')}\n`; );
logs.info += text; const text = `[${formatUTCTimestamp()}] [READY] ${args.join(' ')}\n`;
logs.verbose += text; logs.info += text;
}; logs.verbose += text;
};
export function formatTimestamp(now = new Date()) {
const year = now.getFullYear(); export function formatTimestamp(now = new Date()) {
const month = (now.getMonth() + 1).toString().padStart(2, '0'); const year = now.getFullYear();
const day = now.getDate().toString().padStart(2, '0'); const month = (now.getMonth() + 1).toString().padStart(2, '0');
const hour = now.getHours().toString().padStart(2, '0'); const day = now.getDate().toString().padStart(2, '0');
const minute = now.getMinutes().toString().padStart(2, '0'); const hour = now.getHours().toString().padStart(2, '0');
const second = now.getSeconds().toString().padStart(2, '0'); const minute = now.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}:${second}`; const second = now.getSeconds().toString().padStart(2, '0');
} return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
export function formatUTCTimestamp(now = new Date()) {
const year = now.getUTCFullYear(); export function formatUTCTimestamp(now = new Date()) {
const month = (now.getUTCMonth() + 1).toString().padStart(2, '0'); const year = now.getUTCFullYear();
const day = now.getUTCDate().toString().padStart(2, '0'); const month = (now.getUTCMonth() + 1).toString().padStart(2, '0');
const hour = now.getUTCHours().toString().padStart(2, '0'); const day = now.getUTCDate().toString().padStart(2, '0');
const minute = now.getUTCMinutes().toString().padStart(2, '0'); const hour = now.getUTCHours().toString().padStart(2, '0');
const second = now.getUTCSeconds().toString().padStart(2, '0'); const minute = now.getUTCMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}:${second}`; const second = now.getUTCSeconds().toString().padStart(2, '0');
} return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
export function botHasPermission(
guild: Guild | null, export function botHasPermission(
permission: number, guild: Guild | null,
): boolean { permission: number,
return !!( ): boolean {
client.user && return !!(
guild?.members.resolve(client.user)?.hasPermission(permission) client.user &&
); guild?.members.resolve(client.user)?.hasPermission(permission)
} );
}
// Pagination function that allows for customization via a callback.
// Define your own pages outside the function because this only manages the actual turning of pages. // Pagination function that allows for customization via a callback.
$.paginate = async ( // Define your own pages outside the function because this only manages the actual turning of pages.
message: Message, $.paginate = async (
senderID: string, message: Message,
total: number, senderID: string,
callback: (page: number) => void, total: number,
duration = 60000, callback: (page: number) => void,
) => { duration = 60000,
let page = 0; ) => {
const turn = (amount: number) => { let page = 0;
page += amount; const turn = (amount: number) => {
page += amount;
if (page < 0) page += total;
else if (page >= total) page -= total; if (page < 0) page += total;
else if (page >= total) page -= total;
callback(page);
}; callback(page);
const handle = (emote: string, reacterID: string) => { };
switch (emote) { const handle = (emote: string, reacterID: string) => {
case '⬅️': switch (emote) {
turn(-1); case '⬅️':
break; turn(-1);
case '➡️': break;
turn(1); case '➡️':
break; turn(1);
} break;
}; }
};
// Listen for reactions and call the handler.
await message.react('⬅️'); // Listen for reactions and call the handler.
await message.react('➡️'); await message.react('⬅️');
eventListeners.set(message.id, handle); await message.react('➡️');
await message.awaitReactions( eventListeners.set(message.id, handle);
(reaction, user) => { await message.awaitReactions(
if (user.id === senderID) { (reaction, user) => {
// The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. if (user.id === senderID) {
// This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error.
const canDeleteEmotes = botHasPermission( // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission.
message.guild, const canDeleteEmotes = botHasPermission(
Permissions.FLAGS.MANAGE_MESSAGES, message.guild,
); Permissions.FLAGS.MANAGE_MESSAGES,
handle(reaction.emoji.name, user.id); );
handle(reaction.emoji.name, user.id);
if (canDeleteEmotes) reaction.users.remove(user);
} if (canDeleteEmotes) reaction.users.remove(user);
return false; }
}, return false;
{ time: duration }, },
); { time: duration },
// When time's up, remove the bot's own reactions. );
eventListeners.delete(message.id); // When time's up, remove the bot's own reactions.
message.reactions.cache.get('⬅️')?.users.remove(message.author); eventListeners.delete(message.id);
message.reactions.cache.get('➡️')?.users.remove(message.author); message.reactions.cache.get('⬅️')?.users.remove(message.author);
}; message.reactions.cache.get('➡️')?.users.remove(message.author);
};
// Waits for the sender to either confirm an action or let it pass (and delete the message).
$.prompt = async ( // Waits for the sender to either confirm an action or let it pass (and delete the message).
message: Message, $.prompt = async (
senderID: string, message: Message,
onConfirm: () => void, senderID: string,
duration = 10000, onConfirm: () => void,
) => { duration = 10000,
let isDeleted = false; ) => {
let isDeleted = false;
message.react('✅');
await message.awaitReactions( message.react('✅');
(reaction, user) => { await message.awaitReactions(
if (user.id === senderID) { (reaction, user) => {
if (reaction.emoji.name === '✅') onConfirm(); if (user.id === senderID) {
isDeleted = true; if (reaction.emoji.name === '✅') onConfirm();
message.delete(); isDeleted = true;
} message.delete();
}
// CollectorFilter requires a boolean to be returned.
// My guess is that the return value of awaitReactions can be altered by making a boolean filter. // CollectorFilter requires a boolean to be returned.
// However, because that's not my concern with this command, I don't have to worry about it. // My guess is that the return value of awaitReactions can be altered by making a boolean filter.
// May as well just set it to false because I'm not concerned with collecting any reactions. // However, because that's not my concern with this command, I don't have to worry about it.
return false; // May as well just set it to false because I'm not concerned with collecting any reactions.
}, return false;
{ time: duration }, },
); { time: duration },
);
if (!isDeleted) message.delete();
}; if (!isDeleted) message.delete();
};
$.getMemberByUsername = async (guild: Guild, username: string) => {
return ( $.getMemberByUsername = async (guild: Guild, username: string) => {
await guild.members.fetch({ return (
query: username, await guild.members.fetch({
limit: 1, query: username,
}) limit: 1,
).first(); })
}; ).first();
};
/** Convenience function to handle false cases automatically. */
$.callMemberByUsername = async ( /** Convenience function to handle false cases automatically. */
message: Message, $.callMemberByUsername = async (
username: string, message: Message,
onSuccess: (member: GuildMember) => void, username: string,
) => { onSuccess: (member: GuildMember) => void,
const guild = message.guild; ) => {
const send = message.channel.send; const guild = message.guild;
const send = message.channel.send;
if (guild) {
const member = await $.getMemberByUsername(guild, username); if (guild) {
const member = await $.getMemberByUsername(guild, username);
if (member) onSuccess(member);
else send(`Couldn't find a user by the name of \`${username}\`!`); if (member) onSuccess(member);
} else send('You must execute this command in a server!'); else send(`Couldn't find a user by the name of \`${username}\`!`);
}; } else send('You must execute this command in a server!');
};
/**
* Splits a command by spaces while accounting for quotes which capture string arguments. /**
* - `\"` = `"` * Splits a command by spaces while accounting for quotes which capture string arguments.
* - `\\` = `\` * - `\"` = `"`
*/ * - `\\` = `\`
export function parseArgs(line: string): string[] { */
let result = []; export function parseArgs(line: string): string[] {
let selection = ''; let result = [];
let inString = false; let selection = '';
let isEscaped = false; let inString = false;
let isEscaped = false;
for (let c of line) {
if (isEscaped) { for (let c of line) {
if (['"', '\\'].includes(c)) selection += c; if (isEscaped) {
else selection += '\\' + c; if (['"', '\\'].includes(c)) selection += c;
else selection += '\\' + c;
isEscaped = false;
} else if (c === '\\') isEscaped = true; isEscaped = false;
else if (c === '"') inString = !inString; } else if (c === '\\') isEscaped = true;
else if (c === ' ' && !inString) { else if (c === '"') inString = !inString;
result.push(selection); else if (c === ' ' && !inString) {
selection = ''; result.push(selection);
} else selection += c; selection = '';
} } else selection += c;
}
if (selection.length > 0) result.push(selection);
if (selection.length > 0) result.push(selection);
return result;
} return result;
}
/**
* Allows you to store a template string with variable markers and parse it later. /**
* - Use `%name%` for variables * Allows you to store a template string with variable markers and parse it later.
* - `%%` = `%` * - Use `%name%` for variables
* - If the invalid token is null/undefined, nothing is changed. * - `%%` = `%`
*/ * - If the invalid token is null/undefined, nothing is changed.
export function parseVars( */
line: string, export function parseVars(
definitions: { [key: string]: string }, line: string,
invalid: string | null = '', definitions: { [key: string]: string },
): string { invalid: string | null = '',
let result = ''; ): string {
let inVariable = false; let result = '';
let token = ''; let inVariable = false;
let token = '';
for (const c of line) {
if (c === '%') { for (const c of line) {
if (inVariable) { if (c === '%') {
if (token === '') result += '%'; if (inVariable) {
else { if (token === '') result += '%';
if (token in definitions) result += definitions[token]; else {
else if (invalid === null) result += `%${token}%`; if (token in definitions) result += definitions[token];
else result += invalid; else if (invalid === null) result += `%${token}%`;
else result += invalid;
token = '';
} token = '';
} }
}
inVariable = !inVariable;
} else if (inVariable) token += c; inVariable = !inVariable;
else result += c; } else if (inVariable) token += c;
} else result += c;
}
return result;
} return result;
}
export function isType(value: any, type: any): boolean {
if (value === undefined && type === undefined) return true; export function isType(value: any, type: any): boolean {
else if (value === null && type === null) return true; if (value === undefined && type === undefined) return true;
else else if (value === null && type === null) return true;
return value !== undefined && value !== null && value.constructor === type; else
} return value !== undefined && value !== null && value.constructor === type;
}
/**
* Checks a value to see if it matches the fallback's type, otherwise returns the fallback. /**
* For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). * Checks a value to see if it matches the fallback's type, otherwise returns the fallback.
* If at any point the value doesn't match the data structure provided, the fallback is returned. * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object).
* Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! * If at any point the value doesn't match the data structure provided, the fallback is returned.
*/ * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this!
export function select<T>( */
value: any, export function select<T>(
fallback: T, value: any,
type: Function, fallback: T,
isArray = false, type: Function,
): T { isArray = false,
if (isArray && isType(value, Array)) { ): T {
for (let item of value) if (!isType(item, type)) return fallback; if (isArray && isType(value, Array)) {
return value; for (let item of value) if (!isType(item, type)) return fallback;
} else { return value;
if (isType(value, type)) return value; } else {
else return fallback; if (isType(value, type)) return value;
} else return fallback;
} }
}
export interface GenericJSON {
[key: string]: any; export function clean(text: any) {
} if (typeof text === 'string')
return text
export abstract class GenericStructure { .replace(/`/g, '`' + String.fromCharCode(8203))
private __meta__ = 'generic'; .replace(/@/g, '@' + String.fromCharCode(8203));
else return text;
constructor(tag?: string) { }
this.__meta__ = tag || this.__meta__;
} export function trimArray(arr: any, maxLen = 10) {
if (arr.length > maxLen) {
public save(asynchronous = true) { const len = arr.length - maxLen;
const tag = this.__meta__; arr = arr.slice(0, maxLen);
/// @ts-ignore arr.push(`${len} more...`);
delete this.__meta__; }
FileManager.write(tag, this, asynchronous); return arr;
this.__meta__ = tag; }
}
} export function formatBytes(bytes: any) {
if (bytes === 0) return '0 Bytes';
// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). const i = Math.floor(Math.log(bytes) / Math.log(1024));
export const Random = { return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`;
num: (min: number, max: number) => Math.random() * (max - min) + min, }
int: (min: number, max: number) => Math.floor(Random.num(min, max)),
chance: (decimal: number) => Math.random() < decimal, export function getContent(url: any) {
sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), return new Promise((resolve, reject) => {
deviation: (base: number, deviation: number) => get(
Random.num(base - deviation, base + deviation), url,
}; (res: {
resume?: any;
setEncoding?: any;
on?: any;
statusCode?: any;
}) => {
const { statusCode } = res;
if (statusCode !== 200) {
res.resume();
reject(`Request failed. Status code: ${statusCode}`);
}
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk: string) => {
rawData += chunk;
});
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
reject(`Error: ${e.message}`);
}
});
},
).on('error', (err: { message: any }) => {
reject(`Error: ${err.message}`);
});
});
}
export interface GenericJSON {
[key: string]: any;
}
export abstract class GenericStructure {
private __meta__ = 'generic';
constructor(tag?: string) {
this.__meta__ = tag || this.__meta__;
}
public save(asynchronous = true) {
const tag = this.__meta__;
/// @ts-ignore
delete this.__meta__;
FileManager.write(tag, this, asynchronous);
this.__meta__ = tag;
}
}
// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1).
// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance).
export const Random = {
num: (min: number, max: number) => Math.random() * (max - min) + min,
int: (min: number, max: number) => Math.floor(Random.num(min, max)),
chance: (decimal: number) => Math.random() < decimal,
sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1),
deviation: (base: number, deviation: number) =>
Random.num(base - deviation, base + deviation),
};