diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 185f76c..02f1694 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,19 +9,28 @@
"version": "0.0.1",
"dependencies": {
"axios": "^0.24.0",
+ "chance": "^1.1.8",
+ "crypto-random-string": "^4.0.0",
"detritus-client": "^0.16.3",
"detritus-client-rest": "^0.10.5",
"discord-api-types": "^0.25.2",
"dotenv": "^10.0.0",
"fastify": "^3.24.1",
+ "fastify-cookie": "^5.4.0",
+ "pino": "^7.5.1",
+ "pino-discord": "^1.0.2",
"pug": "^3.0.2",
+ "relateurl": "^0.2.7",
"simple-oauth2": "^4.2.0",
"slash-create": "^4.4.0"
},
"devDependencies": {
+ "@types/chance": "^1.1.3",
"@types/node": "^16.11.12",
"@types/pug": "^2.0.5",
+ "@types/relateurl": "^0.2.29",
"@types/simple-oauth2": "^4.1.1",
+ "pino-pretty": "^7.3.0",
"typescript": "^4.5.3"
}
},
@@ -127,6 +136,12 @@
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
},
+ "node_modules/@types/chance": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.3.tgz",
+ "integrity": "sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==",
+ "dev": true
+ },
"node_modules/@types/node": {
"version": "16.11.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
@@ -147,6 +162,12 @@
"integrity": "sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==",
"dev": true
},
+ "node_modules/@types/relateurl": {
+ "version": "0.2.29",
+ "resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.29.tgz",
+ "integrity": "sha512-QSvevZ+IRww2ldtfv1QskYsqVVVwCKQf1XbwtcyyoRvLIQzfyPhj/C+3+PKzSDRdiyejaiLgnq//XTkleorpLg==",
+ "dev": true
+ },
"node_modules/@types/simple-oauth2": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/simple-oauth2/-/simple-oauth2-4.1.1.tgz",
@@ -184,11 +205,38 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/archy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
"integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
},
+ "node_modules/args": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
+ "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "5.0.0",
+ "chalk": "2.4.2",
+ "leven": "2.1.0",
+ "mri": "1.1.4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -254,6 +302,34 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chance": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.8.tgz",
+ "integrity": "sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg=="
+ },
"node_modules/character-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
@@ -262,6 +338,27 @@
"is-regex": "^1.0.3"
}
},
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "node_modules/colorette": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+ "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
+ "dev": true
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -273,6 +370,14 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/constantinople": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
@@ -290,6 +395,37 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookie-signature": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.1.0.tgz",
+ "integrity": "sha512-Alvs19Vgq07eunykd3Xy2jF0/qSNv2u7KDbAek9H5liV1UMijbqFs5cycZvv5dVsvseT/U4H8/7/w8Koh35C4A==",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/crypto-random-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+ "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+ "dependencies": {
+ "type-fest": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dateformat": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
@@ -450,6 +586,34 @@
"node": ">=10"
}
},
+ "node_modules/duplexify": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz",
+ "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==",
+ "dependencies": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@@ -519,16 +683,61 @@
"tiny-lru": "^7.0.0"
}
},
+ "node_modules/fastify-cookie": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/fastify-cookie/-/fastify-cookie-5.4.0.tgz",
+ "integrity": "sha512-uKTbOLx6pSyKqA9oD2G9hpMuRTVtKRm98bRwJVg4ga7GCm+RR6771stmfhbblXxHHcQQHuHvwdOdPeHJjr2sgg==",
+ "dependencies": {
+ "cookie-signature": "^1.1.0",
+ "fastify-plugin": "^3.0.0"
+ }
+ },
"node_modules/fastify-error": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.1.tgz",
"integrity": "sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ=="
},
+ "node_modules/fastify-plugin": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.0.tgz",
+ "integrity": "sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w=="
+ },
"node_modules/fastify-warning": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz",
"integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw=="
},
+ "node_modules/fastify/node_modules/pino": {
+ "version": "6.13.3",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-6.13.3.tgz",
+ "integrity": "sha512-tJy6qVgkh9MwNgqX1/oYi3ehfl2Y9H0uHyEEMsBe74KinESIjdMrMQDWpcZPpPicg3VV35d/GLQZmo4QgU2Xkg==",
+ "dependencies": {
+ "fast-redact": "^3.0.0",
+ "fast-safe-stringify": "^2.0.8",
+ "fastify-warning": "^0.2.0",
+ "flatstr": "^1.0.12",
+ "pino-std-serializers": "^3.1.0",
+ "quick-format-unescaped": "^4.0.3",
+ "sonic-boom": "^1.0.2"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/fastify/node_modules/pino-std-serializers": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
+ "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
+ },
+ "node_modules/fastify/node_modules/sonic-boom": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
+ "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0",
+ "flatstr": "^1.0.12"
+ }
+ },
"node_modules/fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
@@ -601,6 +810,14 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
@@ -625,6 +842,15 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
@@ -650,6 +876,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -710,6 +941,15 @@
"@sideway/pinpoint": "^2.0.0"
}
},
+ "node_modules/joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/js-stringify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
@@ -729,6 +969,15 @@
"promise": "^7.0.1"
}
},
+ "node_modules/leven": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+ "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/light-my-request": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-4.7.0.tgz",
@@ -800,6 +1049,15 @@
"node": ">= 0.6"
}
},
+ "node_modules/mri": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
+ "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -824,32 +1082,103 @@
"node": ">=0.10.0"
}
},
+ "node_modules/on-exit-leak-free": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz",
+ "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg=="
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/pino": {
- "version": "6.13.3",
- "resolved": "https://registry.npmjs.org/pino/-/pino-6.13.3.tgz",
- "integrity": "sha512-tJy6qVgkh9MwNgqX1/oYi3ehfl2Y9H0uHyEEMsBe74KinESIjdMrMQDWpcZPpPicg3VV35d/GLQZmo4QgU2Xkg==",
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-7.5.1.tgz",
+ "integrity": "sha512-Wzo2G7CLaRHKOz3+Ex006LC5Xi0xEUm+mwm/h0NKzuKZONdekcbmjXg7vWDoO8hVTGX+1RuUy2fwlzvZ24EI5A==",
"dependencies": {
"fast-redact": "^3.0.0",
- "fast-safe-stringify": "^2.0.8",
"fastify-warning": "^0.2.0",
- "flatstr": "^1.0.12",
- "pino-std-serializers": "^3.1.0",
+ "get-caller-file": "^2.0.5",
+ "on-exit-leak-free": "^0.2.0",
+ "pino-abstract-transport": "v0.5.0",
+ "pino-std-serializers": "^4.0.0",
"quick-format-unescaped": "^4.0.3",
- "sonic-boom": "^1.0.2"
+ "real-require": "^0.1.0",
+ "safe-stable-stringify": "^2.1.0",
+ "sonic-boom": "^2.2.1",
+ "thread-stream": "^0.13.0"
},
"bin": {
"pino": "bin.js"
}
},
+ "node_modules/pino-abstract-transport": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz",
+ "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==",
+ "dependencies": {
+ "duplexify": "^4.1.2",
+ "split2": "^4.0.0"
+ }
+ },
+ "node_modules/pino-discord": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pino-discord/-/pino-discord-1.0.2.tgz",
+ "integrity": "sha512-BpFTPwnNrqNB9YvXaMrylq6264KZ/OswflpNGeAOBGme8sdKR94uxlg+mADTDvoUW+s98r+XN3NPeDjKGTwSLg==",
+ "dependencies": {
+ "commander": "^6.1.0",
+ "pump": "^3.0.0",
+ "split2": "^3.2.2",
+ "through2": "^4.0.2"
+ },
+ "bin": {
+ "pino-discord": "index.js"
+ }
+ },
+ "node_modules/pino-discord/node_modules/split2": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+ "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+ "dependencies": {
+ "readable-stream": "^3.0.0"
+ }
+ },
+ "node_modules/pino-pretty": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-7.3.0.tgz",
+ "integrity": "sha512-HAhShJ2z2QzxXhYAn6XfwYpF13o1PQbjzSNA9q+30FAvhjOmeACit9lprhV/mCOw/8YFWSyyNk0YCq2EDYGYpw==",
+ "dev": true,
+ "dependencies": {
+ "args": "^5.0.1",
+ "colorette": "^2.0.7",
+ "dateformat": "^4.6.3",
+ "fast-safe-stringify": "^2.0.7",
+ "joycon": "^3.1.1",
+ "pino-abstract-transport": "^0.5.0",
+ "pump": "^3.0.0",
+ "readable-stream": "^3.6.0",
+ "rfdc": "^1.3.0",
+ "secure-json-parse": "^2.4.0",
+ "sonic-boom": "^2.2.0",
+ "strip-json-comments": "^3.1.1"
+ },
+ "bin": {
+ "pino-pretty": "bin.js"
+ }
+ },
"node_modules/pino-std-serializers": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
- "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz",
+ "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q=="
},
"node_modules/promise": {
"version": "7.3.1",
@@ -983,6 +1312,15 @@
"resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz",
"integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ=="
},
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -1015,6 +1353,35 @@
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
},
+ "node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/real-require": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz",
+ "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
"node_modules/require-all": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/require-all/-/require-all-3.0.0.tgz",
@@ -1065,6 +1432,25 @@
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
},
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/safe-regex2": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz",
@@ -1073,6 +1459,14 @@
"ret": "~0.2.0"
}
},
+ "node_modules/safe-stable-stringify": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
+ "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/secure-json-parse": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz",
@@ -1142,12 +1536,32 @@
}
},
"node_modules/sonic-boom": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
- "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.4.1.tgz",
+ "integrity": "sha512-WgtVLfGl347/zS1oTuLaOAvVD5zijgjphAJHgbbnBJGgexnr+C1ULSj0j7ftoGxpuxR4PaV635NkwFemG8m/5w==",
"dependencies": {
- "atomic-sleep": "^1.0.0",
- "flatstr": "^1.0.12"
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
+ "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
}
},
"node_modules/string-similarity": {
@@ -1155,6 +1569,46 @@
"resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz",
"integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ=="
},
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/thread-stream": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.13.0.tgz",
+ "integrity": "sha512-kTMZeX4Dzlb1zZ00/01aerGaTw2i8NE4sWF0TvF1uXewRhCiUjCvatQkvxIvFqauWG2ADFS2Wpd3qBeYL9i3dg==",
+ "dependencies": {
+ "real-require": "^0.1.0"
+ }
+ },
+ "node_modules/through2": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+ "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+ "dependencies": {
+ "readable-stream": "3"
+ }
+ },
"node_modules/tiny-lru": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz",
@@ -1186,6 +1640,17 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
+ "node_modules/type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/typescript": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz",
@@ -1207,6 +1672,11 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
"node_modules/void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
@@ -1243,6 +1713,11 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
"node_modules/ws": {
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
@@ -1356,6 +1831,12 @@
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
},
+ "@types/chance": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.3.tgz",
+ "integrity": "sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==",
+ "dev": true
+ },
"@types/node": {
"version": "16.11.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
@@ -1376,6 +1857,12 @@
"integrity": "sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==",
"dev": true
},
+ "@types/relateurl": {
+ "version": "0.2.29",
+ "resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.29.tgz",
+ "integrity": "sha512-QSvevZ+IRww2ldtfv1QskYsqVVVwCKQf1XbwtcyyoRvLIQzfyPhj/C+3+PKzSDRdiyejaiLgnq//XTkleorpLg==",
+ "dev": true
+ },
"@types/simple-oauth2": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/simple-oauth2/-/simple-oauth2-4.1.1.tgz",
@@ -1403,11 +1890,32 @@
"uri-js": "^4.2.2"
}
},
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
"archy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
"integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
},
+ "args": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
+ "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "5.0.0",
+ "chalk": "2.4.2",
+ "leven": "2.1.0",
+ "mri": "1.1.4"
+ }
+ },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -1464,6 +1972,28 @@
"get-intrinsic": "^1.0.2"
}
},
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chance": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.8.tgz",
+ "integrity": "sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg=="
+ },
"character-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
@@ -1472,6 +2002,27 @@
"is-regex": "^1.0.3"
}
},
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colorette": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+ "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
+ "dev": true
+ },
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1480,6 +2031,11 @@
"delayed-stream": "~1.0.0"
}
},
+ "commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="
+ },
"constantinople": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
@@ -1494,6 +2050,25 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
+ "cookie-signature": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.1.0.tgz",
+ "integrity": "sha512-Alvs19Vgq07eunykd3Xy2jF0/qSNv2u7KDbAek9H5liV1UMijbqFs5cycZvv5dVsvseT/U4H8/7/w8Koh35C4A=="
+ },
+ "crypto-random-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+ "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+ "requires": {
+ "type-fest": "^1.0.1"
+ }
+ },
+ "dateformat": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+ "dev": true
+ },
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
@@ -1589,6 +2164,31 @@
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
},
+ "duplexify": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz",
+ "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==",
+ "requires": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@@ -1650,6 +2250,45 @@
"secure-json-parse": "^2.0.0",
"semver": "^7.3.2",
"tiny-lru": "^7.0.0"
+ },
+ "dependencies": {
+ "pino": {
+ "version": "6.13.3",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-6.13.3.tgz",
+ "integrity": "sha512-tJy6qVgkh9MwNgqX1/oYi3ehfl2Y9H0uHyEEMsBe74KinESIjdMrMQDWpcZPpPicg3VV35d/GLQZmo4QgU2Xkg==",
+ "requires": {
+ "fast-redact": "^3.0.0",
+ "fast-safe-stringify": "^2.0.8",
+ "fastify-warning": "^0.2.0",
+ "flatstr": "^1.0.12",
+ "pino-std-serializers": "^3.1.0",
+ "quick-format-unescaped": "^4.0.3",
+ "sonic-boom": "^1.0.2"
+ }
+ },
+ "pino-std-serializers": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
+ "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
+ },
+ "sonic-boom": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
+ "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
+ "requires": {
+ "atomic-sleep": "^1.0.0",
+ "flatstr": "^1.0.12"
+ }
+ }
+ }
+ },
+ "fastify-cookie": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/fastify-cookie/-/fastify-cookie-5.4.0.tgz",
+ "integrity": "sha512-uKTbOLx6pSyKqA9oD2G9hpMuRTVtKRm98bRwJVg4ga7GCm+RR6771stmfhbblXxHHcQQHuHvwdOdPeHJjr2sgg==",
+ "requires": {
+ "cookie-signature": "^1.1.0",
+ "fastify-plugin": "^3.0.0"
}
},
"fastify-error": {
@@ -1657,6 +2296,11 @@
"resolved": "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.1.tgz",
"integrity": "sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ=="
},
+ "fastify-plugin": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.0.tgz",
+ "integrity": "sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w=="
+ },
"fastify-warning": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz",
@@ -1711,6 +2355,11 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
"get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
@@ -1729,6 +2378,12 @@
"function-bind": "^1.1.1"
}
},
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
@@ -1742,6 +2397,11 @@
"has-symbols": "^1.0.2"
}
},
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -1790,6 +2450,12 @@
"@sideway/pinpoint": "^2.0.0"
}
},
+ "joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "dev": true
+ },
"js-stringify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
@@ -1809,6 +2475,12 @@
"promise": "^7.0.1"
}
},
+ "leven": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+ "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+ "dev": true
+ },
"light-my-request": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-4.7.0.tgz",
@@ -1869,6 +2541,12 @@
"mime-db": "1.51.0"
}
},
+ "mri": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
+ "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==",
+ "dev": true
+ },
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -1887,29 +2565,96 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
+ "on-exit-leak-free": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz",
+ "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg=="
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"pino": {
- "version": "6.13.3",
- "resolved": "https://registry.npmjs.org/pino/-/pino-6.13.3.tgz",
- "integrity": "sha512-tJy6qVgkh9MwNgqX1/oYi3ehfl2Y9H0uHyEEMsBe74KinESIjdMrMQDWpcZPpPicg3VV35d/GLQZmo4QgU2Xkg==",
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-7.5.1.tgz",
+ "integrity": "sha512-Wzo2G7CLaRHKOz3+Ex006LC5Xi0xEUm+mwm/h0NKzuKZONdekcbmjXg7vWDoO8hVTGX+1RuUy2fwlzvZ24EI5A==",
"requires": {
"fast-redact": "^3.0.0",
- "fast-safe-stringify": "^2.0.8",
"fastify-warning": "^0.2.0",
- "flatstr": "^1.0.12",
- "pino-std-serializers": "^3.1.0",
+ "get-caller-file": "^2.0.5",
+ "on-exit-leak-free": "^0.2.0",
+ "pino-abstract-transport": "v0.5.0",
+ "pino-std-serializers": "^4.0.0",
"quick-format-unescaped": "^4.0.3",
- "sonic-boom": "^1.0.2"
+ "real-require": "^0.1.0",
+ "safe-stable-stringify": "^2.1.0",
+ "sonic-boom": "^2.2.1",
+ "thread-stream": "^0.13.0"
+ }
+ },
+ "pino-abstract-transport": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz",
+ "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==",
+ "requires": {
+ "duplexify": "^4.1.2",
+ "split2": "^4.0.0"
+ }
+ },
+ "pino-discord": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pino-discord/-/pino-discord-1.0.2.tgz",
+ "integrity": "sha512-BpFTPwnNrqNB9YvXaMrylq6264KZ/OswflpNGeAOBGme8sdKR94uxlg+mADTDvoUW+s98r+XN3NPeDjKGTwSLg==",
+ "requires": {
+ "commander": "^6.1.0",
+ "pump": "^3.0.0",
+ "split2": "^3.2.2",
+ "through2": "^4.0.2"
+ },
+ "dependencies": {
+ "split2": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+ "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+ "requires": {
+ "readable-stream": "^3.0.0"
+ }
+ }
+ }
+ },
+ "pino-pretty": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-7.3.0.tgz",
+ "integrity": "sha512-HAhShJ2z2QzxXhYAn6XfwYpF13o1PQbjzSNA9q+30FAvhjOmeACit9lprhV/mCOw/8YFWSyyNk0YCq2EDYGYpw==",
+ "dev": true,
+ "requires": {
+ "args": "^5.0.1",
+ "colorette": "^2.0.7",
+ "dateformat": "^4.6.3",
+ "fast-safe-stringify": "^2.0.7",
+ "joycon": "^3.1.1",
+ "pino-abstract-transport": "^0.5.0",
+ "pump": "^3.0.0",
+ "readable-stream": "^3.6.0",
+ "rfdc": "^1.3.0",
+ "secure-json-parse": "^2.4.0",
+ "sonic-boom": "^2.2.0",
+ "strip-json-comments": "^3.1.1"
}
},
"pino-std-serializers": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
- "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz",
+ "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q=="
},
"promise": {
"version": "7.3.1",
@@ -2040,6 +2785,15 @@
"resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz",
"integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ=="
},
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -2055,6 +2809,26 @@
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
},
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "real-require": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz",
+ "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg=="
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
+ },
"require-all": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/require-all/-/require-all-3.0.0.tgz",
@@ -2089,6 +2863,11 @@
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
},
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
"safe-regex2": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz",
@@ -2097,6 +2876,11 @@
"ret": "~0.2.0"
}
},
+ "safe-stable-stringify": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
+ "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg=="
+ },
"secure-json-parse": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz",
@@ -2145,12 +2929,29 @@
}
},
"sonic-boom": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
- "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.4.1.tgz",
+ "integrity": "sha512-WgtVLfGl347/zS1oTuLaOAvVD5zijgjphAJHgbbnBJGgexnr+C1ULSj0j7ftoGxpuxR4PaV635NkwFemG8m/5w==",
"requires": {
- "atomic-sleep": "^1.0.0",
- "flatstr": "^1.0.12"
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "split2": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
+ "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ=="
+ },
+ "stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "requires": {
+ "safe-buffer": "~5.2.0"
}
},
"string-similarity": {
@@ -2158,6 +2959,37 @@
"resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz",
"integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ=="
},
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "thread-stream": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.13.0.tgz",
+ "integrity": "sha512-kTMZeX4Dzlb1zZ00/01aerGaTw2i8NE4sWF0TvF1uXewRhCiUjCvatQkvxIvFqauWG2ADFS2Wpd3qBeYL9i3dg==",
+ "requires": {
+ "real-require": "^0.1.0"
+ }
+ },
+ "through2": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+ "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+ "requires": {
+ "readable-stream": "3"
+ }
+ },
"tiny-lru": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz",
@@ -2183,6 +3015,11 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
+ "type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="
+ },
"typescript": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz",
@@ -2197,6 +3034,11 @@
"punycode": "^2.1.0"
}
},
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
"void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
@@ -2227,6 +3069,11 @@
"babel-walk": "3.0.0-canary-5"
}
},
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
"ws": {
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
diff --git a/package.json b/package.json
index 9994500..8e99f6e 100644
--- a/package.json
+++ b/package.json
@@ -3,19 +3,28 @@
"version": "0.0.1",
"dependencies": {
"axios": "^0.24.0",
+ "chance": "^1.1.8",
+ "crypto-random-string": "^4.0.0",
"detritus-client": "^0.16.3",
"detritus-client-rest": "^0.10.5",
"discord-api-types": "^0.25.2",
"dotenv": "^10.0.0",
"fastify": "^3.24.1",
+ "fastify-cookie": "^5.4.0",
+ "pino": "^7.5.1",
+ "pino-discord": "^1.0.2",
"pug": "^3.0.2",
+ "relateurl": "^0.2.7",
"simple-oauth2": "^4.2.0",
"slash-create": "^4.4.0"
},
"devDependencies": {
+ "@types/chance": "^1.1.3",
"@types/node": "^16.11.12",
"@types/pug": "^2.0.5",
+ "@types/relateurl": "^0.2.29",
"@types/simple-oauth2": "^4.1.1",
+ "pino-pretty": "^7.3.0",
"typescript": "^4.5.3"
},
"type": "module",
diff --git a/src/BaseServer.ts b/src/BaseServer.ts
new file mode 100644
index 0000000..130cf72
--- /dev/null
+++ b/src/BaseServer.ts
@@ -0,0 +1,92 @@
+import {SavedWebhook} from "./SavedWebhook.js";
+import fastify, {FastifyInstance} from "fastify";
+import {SlashCreator, SlashCreatorOptions} from "slash-create";
+import fastifyCookie from "fastify-cookie";
+import {FastifyServerButItWorksUnlikeTheRealOne} from "./FastifyHelpers.js";
+
+export interface BaseServerDeps {
+ appId: string
+ botToken: string
+ clientSecret: string
+ publicKey: string
+ listenPort: number
+ listenAddress: string
+ cookieSecret: string
+ gameWebhook: SavedWebhook
+ adminWebhook: SavedWebhook
+}
+
+export class BaseServer {
+ readonly server: FastifyInstance
+ readonly gameWebhook: SavedWebhook
+ readonly adminWebhook: SavedWebhook
+ readonly appId: string
+ readonly clientSecret: string
+ readonly listenPort: number
+ readonly listenAddress: string
+ readonly slashcmd: SlashCreator
+
+ constructor({
+ cookieSecret,
+ appId,
+ clientSecret,
+ listenPort,
+ listenAddress,
+ gameWebhook,
+ adminWebhook,
+ publicKey,
+ botToken,
+ slashCreatorOptions = {}
+ }: BaseServerDeps & { slashCreatorOptions?: Partial }) {
+ this.slashcmd = new SlashCreator({
+ allowedMentions: {everyone: false, roles: false, users: false},
+ applicationID: appId,
+ defaultImageFormat: "webp",
+ handleCommandsManually: false,
+ maxSignatureTimestamp: 3000,
+ publicKey,
+ requestTimeout: 10000,
+ token: botToken,
+ unknownCommandResponse: true,
+ endpointPath: "/interactions",
+ ...slashCreatorOptions,
+ })
+ this.server = fastify({logger: true})
+ this.slashcmd.withServer(new FastifyServerButItWorksUnlikeTheRealOne(this.server, {alreadyListening: true}))
+ this.server.register(fastifyCookie, {
+ secret: cookieSecret,
+ })
+ this.appId = appId
+ this.clientSecret = clientSecret
+ this.listenPort = listenPort
+ this.listenAddress = listenAddress
+ this.gameWebhook = gameWebhook
+ this.adminWebhook = adminWebhook
+
+ }
+
+ async _initInternal(): Promise {
+ return
+ }
+
+ async initialize(): Promise {
+ await this._initInternal()
+ await this.slashcmd.syncCommandsAsync({
+ syncGuilds: true,
+ syncPermissions: true,
+ skipGuildErrors: false,
+ deleteCommands: true,
+ })
+ await this.slashcmd.startServer()
+ await this.server.listen(this.listenPort, this.listenAddress)
+ }
+
+ async _shutdownInternal(): Promise {
+ return
+ }
+
+ async shutdown(): Promise {
+ await this._shutdownInternal()
+ return this.server.close()
+ }
+}
\ No newline at end of file
diff --git a/src/CookieHelpers.ts b/src/CookieHelpers.ts
new file mode 100644
index 0000000..d2d62ff
--- /dev/null
+++ b/src/CookieHelpers.ts
@@ -0,0 +1,32 @@
+import {FastifyReply, FastifyRequest} from "fastify";
+import {RouteGenericInterface} from "fastify/types/route";
+import cryptoRandomString from "crypto-random-string";
+
+export interface XSRFRoute extends RouteGenericInterface {
+ Querystring: { [key in typeof XSRFParameter]: string | string[] | undefined }
+}
+
+export const XSRFCookie = "__Host-XSRF-Cookie";
+export const XSRFParameter = "state" as const;
+
+export function generateXSRFCookie(res: FastifyReply): string {
+ const newState = cryptoRandomString({
+ length: 32,
+ type: 'url-safe'
+ })
+ res.setCookie(XSRFCookie, newState, {
+ path: "/",
+ sameSite: "strict",
+ httpOnly: true,
+ signed: true,
+ secure: true,
+ })
+ return newState
+}
+
+export function checkAndClearXSRFCookie(req: FastifyRequest, res: FastifyReply): boolean {
+ const queryState = req.query[XSRFParameter] ?? null
+ const cookieState = req.cookies[XSRFCookie] ?? null
+ res.clearCookie(XSRFCookie)
+ return cookieState !== null && queryState !== null && cookieState === queryState
+}
\ No newline at end of file
diff --git a/src/DiscordWebhookHandler.ts b/src/DiscordWebhookHandler.ts
index 569cb0d..ec804fc 100644
--- a/src/DiscordWebhookHandler.ts
+++ b/src/DiscordWebhookHandler.ts
@@ -4,11 +4,13 @@ import {AccessToken, AuthorizationCode, ModuleOptions} from "simple-oauth2";
import pug from "pug";
import {renderError} from "./PugRenderer.js";
import {APIWebhook} from "discord-api-types";
-import {getBaseUrl, getFirstValue, RouteWithQuerystring} from "./FastifyHelpers.js";
-import { join } from "path";
+import {getBaseUrl, getFirstValue} from "./FastifyHelpers.js";
+import {join, relative} from "path";
+import {RouteGenericInterface} from "fastify/types/route";
+import {checkAndClearXSRFCookie, generateXSRFCookie} from "./CookieHelpers.js";
interface DiscordWebhookToken extends AccessToken {
- token: {webhook?: APIWebhook}
+ token: { webhook?: APIWebhook }
}
const AuthConfig: ModuleOptions["auth"] = {
@@ -18,12 +20,29 @@ const AuthConfig: ModuleOptions["auth"] = {
revokePath: "/api/oauth2/token/revoke",
}
+export interface OAuthRoute extends RouteGenericInterface {
+ Querystring: {
+ code: string | string[] | undefined,
+ state: string | string[] | undefined,
+ error: string | string[] | undefined,
+ error_description: string | string[] | undefined,
+ error_uri: string | string[] | undefined,
+ }
+}
+
export class DiscordWebhookHandler {
readonly webhook: SavedWebhook
readonly templateFilename: string
readonly config: ModuleOptions
readonly destinationFunc: () => string
- constructor({webhook, templateFilename, appId, secret, destinationFunc}: {webhook: SavedWebhook, templateFilename: string, appId: string, secret: string, destinationFunc: () => string}) {
+
+ constructor({
+ webhook,
+ templateFilename,
+ appId,
+ secret,
+ destinationFunc
+ }: { webhook: SavedWebhook, templateFilename: string, appId: string, secret: string, destinationFunc: () => string }) {
this.templateFilename = templateFilename
this.webhook = webhook
this.config = {
@@ -35,20 +54,56 @@ export class DiscordWebhookHandler {
}
this.destinationFunc = destinationFunc
}
- async handleRequest(req: FastifyRequest, res: FastifyReply): Promise {
+
+ async handleRequest(req: FastifyRequest, res: FastifyReply): Promise {
const baseUrl = getBaseUrl(req)
- const code = getFirstValue(req.query["code"]) ?? null
+ const withoutParams = new URL(req.url)
+ withoutParams.search = ""
+ const code = getFirstValue(req.query.code) ?? null
+ const errorCode = getFirstValue(req.query.error) ?? null
const client = new AuthorizationCode(this.config)
- if (code === null) {
+ if (code === null && errorCode === null) {
+ const state = generateXSRFCookie(res)
const authUrl = client.authorizeURL({
scope: ["applications.commands", "webhook.incoming"],
- redirect_uri: req.url,
+ redirect_uri: withoutParams.toString(),
+ state,
})
res.code(200)
res.type("text/html")
- res.send(pug.renderFile(join("static/pages", this.templateFilename), {authUrl, baseUrl, isReset: this.webhook.isPresent}))
+ res.send(pug.renderFile(join("static/pages", this.templateFilename), {
+ authUrl,
+ baseUrl,
+ isReset: this.webhook.isPresent
+ }))
return
}
+ const validXSRF = checkAndClearXSRFCookie(req, res)
+ if (errorCode !== null || code === null) {
+ const errorDescription = getFirstValue(req.query.error_description) ?? null
+ const errorUrl = getFirstValue(req.query.error_uri)
+ return renderError({
+ baseUrl,
+ res,
+ code: 400,
+ error: errorDescription ?? errorCode ?? "There was no code or error present.",
+ context: "authorizing the application",
+ errorUrl,
+ buttonText: "Try Again",
+ buttonUrl: relative("/", new URL(req.url).pathname),
+ })
+ }
+ if (!validXSRF) {
+ return renderError({
+ baseUrl,
+ res,
+ code: 409,
+ error: "The state was incorrectly set - try restarting the authentication process.",
+ context: "processing the new access code",
+ buttonText: "Try Again",
+ buttonUrl: relative("/", new URL(req.url).pathname),
+ })
+ }
let token: DiscordWebhookToken
try {
token = await client.getToken({
@@ -61,15 +116,21 @@ export class DiscordWebhookHandler {
baseUrl,
res: res,
error: e,
- context: "exchanging the code you gave me for a token"
+ code: 400,
+ context: "exchanging the code you gave me for a token",
+ buttonText: "Try Again",
+ buttonUrl: relative("/", new URL(req.url).pathname),
})
}
if (!token.token.webhook) {
return renderError({
baseUrl,
res: res,
- error: "token did not contain webhook",
- context: "processing the token I received"
+ code: 400,
+ error: "The token did not contain a webhook.",
+ context: "processing the token I received",
+ buttonText: "Try Again",
+ buttonUrl: relative("/", new URL(req.url).pathname),
})
}
const wasSet = this.webhook.isPresent
@@ -79,8 +140,11 @@ export class DiscordWebhookHandler {
return renderError({
baseUrl,
res: res,
+ code: 500,
error: e,
- context: `saving the new webhook${wasSet ? " and deleting the old one" : ""}`
+ context: `saving the new webhook${wasSet ? " and deleting the old one" : ""}`,
+ buttonText: "Try Again",
+ buttonUrl: relative("/", new URL(req.url).pathname),
})
}
res.redirect(this.destinationFunc())
diff --git a/src/FastifyHelpers.ts b/src/FastifyHelpers.ts
index 48c531d..c25072d 100644
--- a/src/FastifyHelpers.ts
+++ b/src/FastifyHelpers.ts
@@ -1,10 +1,6 @@
/** Constructs the base URL from the headers from the given request. */
-import {FastifyRequest} from "fastify";
-import {RouteGenericInterface} from "fastify/types/route";
-
-export interface RouteWithQuerystring extends RouteGenericInterface {
- Querystring: {[key: string]: string|string[]|undefined}
-}
+import fastify, {FastifyInstance, FastifyRequest} from "fastify";
+import {Server, ServerOptions, ServerRequestHandler} from "slash-create";
export function getBaseUrl(request: FastifyRequest): string {
const hostHeader = request.hostname ?? "localhost"
@@ -14,6 +10,69 @@ export function getBaseUrl(request: FastifyRequest): string {
}
/** Translates a zero-to-many set of strings to one or zero strings. */
-export function getFirstValue(value: string|string[]|undefined): string|undefined {
+export function getFirstValue(value: string | string[] | undefined): string | undefined {
return typeof value === "string" ? value : Array.isArray(value) ? value[0] : undefined
+}
+
+/**
+ * A server for Fastify applications.
+ * @see https://fastify.io
+ */
+export class FastifyServerButItWorksUnlikeTheRealOne extends Server {
+ readonly app: FastifyInstance;
+
+ /**
+ * @param app The fastify application
+ * @param opts The server options
+ */
+ constructor(app?: FastifyInstance, opts?: ServerOptions) {
+ super(opts);
+ this.app = app || fastify();
+ }
+
+ /**
+ * Adds middleware to the Fastify server.
+ * This requires you to have the 'middie' module registered to the server before using.
+ * @param middleware The middleware to add.
+ * @see https://www.fastify.io/docs/latest/Middleware/
+ */
+ addMiddleware(middleware: Function) {
+ // @ts-ignore
+ if ('use' in this.app) this.app.use(middleware);
+ else
+ throw new Error(
+ "In order to use Express-like middleware, you must initialize the server and register the 'middie' module."
+ );
+ return this;
+ }
+
+ /** Alias for {@link FastifyServerButItWorksUnlikeTheRealOne#addMiddleware} */
+ use(middleware: Function) {
+ return this.addMiddleware(middleware);
+ }
+
+ /** @private */
+ createEndpoint(path: string, handler: ServerRequestHandler) {
+ this.app.post(path, (req: any, res: any) =>
+ handler(
+ {
+ headers: req.headers,
+ body: req.body,
+ request: req,
+ response: res
+ },
+ async (response) => {
+ res.status(response.status || 200);
+ if (response.headers) res.headers(response.headers);
+ res.send(response.body);
+ }
+ )
+ );
+ }
+
+ /** @private */
+ async listen(port = 8030, host = 'localhost') {
+ if (this.alreadyListening) return;
+ await this.app.listen(port, host);
+ }
}
\ No newline at end of file
diff --git a/src/GameServer.ts b/src/GameServer.ts
index bc2d811..feccd17 100644
--- a/src/GameServer.ts
+++ b/src/GameServer.ts
@@ -1,100 +1,109 @@
-import {SavedWebhook} from "./SavedWebhook.js";
-import fastify, {FastifyInstance} from "fastify";
-import {DiscordWebhookHandler} from "./DiscordWebhookHandler.js";
-import {getBaseUrl, RouteWithQuerystring} from "./FastifyHelpers.js";
-import pug from "pug";
+import {SetupServer} from "./SetupServer.js";
+import {checkAndClearXSRFCookie, generateXSRFCookie, XSRFRoute} from "./CookieHelpers.js";
import {renderError} from "./PugRenderer.js";
+import {getBaseUrl} from "./FastifyHelpers.js";
+import pug from "pug";
+import {BaseServer, BaseServerDeps} from "./BaseServer.js";
+import {PullCommand} from "./commands/game/PullCommand.js";
-export class GameServer {
- readonly server: FastifyInstance
- readonly gameWebhook: SavedWebhook
- readonly adminWebhook: SavedWebhook
- readonly appId: string
- readonly secret: string
- readonly port: number
- constructor({appId, secret, port}: {appId: string, secret: string, port: number}) {
- this.server = fastify({
- logger: true
- })
- this.appId = appId
- this.secret = secret
- this.port = port
- this.gameWebhook = new SavedWebhook("broadcasthook.json", {logger: this.server.log})
- this.adminWebhook = new SavedWebhook("logginghook.json", {logger: this.server.log})
+export class GameServer extends BaseServer {
+ readonly setupFactory: () => SetupServer
+
+ constructor(deps: BaseServerDeps & { setupFactory: () => SetupServer }) {
+ super(deps)
+ this.setupFactory = deps.setupFactory
}
- async initialize(): Promise {
- const gameHandler = new DiscordWebhookHandler({
- webhook: this.gameWebhook,
- templateFilename: "setupGame.pug",
- appId: this.appId,
- secret: this.secret,
- destinationFunc: () => {
- if (this.adminWebhook.isPresent) {
- return "clear"
- } else {
- return "adminChannel"
- }
- },
+ async _initInternal(): Promise {
+ this.slashcmd.registerCommand(new PullCommand(this.slashcmd, this.gameWebhook))
+ this.server.get("/game/started", async (req, res) => {
+ const token = generateXSRFCookie(res)
+ res.code(200)
+ res.type("text/html")
+ res.send(pug.renderFile("static/pages/game/running.pug", {
+ baseUrl: getBaseUrl(req),
+ setupModeUrl: `game/stop?token=${token}`,
+ shutdownUrl: `shutdown?token=${token}`,
+ }))
})
- const adminHandler = new DiscordWebhookHandler({
- webhook: this.adminWebhook,
- templateFilename: "setupAdmin.pug",
- appId: this.appId,
- secret: this.secret,
- destinationFunc: () => {
- if (this.gameWebhook.isPresent) {
- return "clear"
- } else {
- return "gameChannel"
- }
- },
+ this.server.get("/game", async (req, res) => {
+ res.redirect("game/started")
})
- this.server.get("/setup", async (req, res) => {
- if (!this.gameWebhook.isPresent) {
- res.redirect(`setup/gameChannel`)
- } else if (!this.adminWebhook.isPresent) {
- res.redirect(`setup/adminChannel`)
- } else {
- res.redirect(`setup/done`)
- }
+ this.server.get("/game/start", async (req, res) => {
+ res.redirect("started")
+ })
+ this.server.get("/setup", async (req, res) => {
+ res.redirect("game/started")
+ })
+ this.server.get("/setup/gameChannel", async (req, res) => {
+ res.redirect("../game/started")
})
- this.server.get("/setup/gameChannel", async (req, res) => {
- return await gameHandler.handleRequest(req, res)
+ this.server.get("/setup/adminChannel", async (req, res) => {
+ res.redirect("../game/started")
})
- this.server.get("/setup/adminChannel", async (req, res) => {
- return await adminHandler.handleRequest(req, res)
+ this.server.get("/setup/clear", async (req, res) => {
+ res.redirect("../game/started")
})
- this.server.get("/setup/clear", async (req, res) => {
- try {
- await Promise.all([this.gameWebhook, this.adminWebhook]
- .filter((item) => item.isPresent)
- .map((item) => item.clearHook()))
- } catch (e) {
+ this.server.get("/setup/done", async (req, res) => {
+ res.redirect("../game/started")
+ })
+ this.server.get("/game/stop", async (req, res) => {
+ if (!checkAndClearXSRFCookie(req, res)) {
return renderError({
baseUrl: getBaseUrl(req),
res,
- error: e,
- context: "clearing the broadcast webhook"
+ code: 400,
+ error: "Token was incorrect or not set.",
+ context: "stopping the game",
+ buttonText: "Return to Game",
+ buttonUrl: "game/started"
})
}
- res.redirect(".")
+ res.code(200)
+ res.type("text/html")
+ res.send(pug.renderFile("static/pages/game/stop.pug", {
+ startedUrl: "setup"
+ }))
+ setImmediate(async () => {
+ this.server.log.info("Shutting down the game server and switching to the setup server.")
+ try {
+ await this.server.close()
+ } catch (e) {
+ this.server.log.error(e, "Failed to shut down the game server")
+ }
+ try {
+ await this.setupFactory().initialize()
+ } catch (e) {
+ this.server.log.error(e, "Failed to start up the setup server")
+ }
+ this.server.log.info("Successfully switched from the game server to the setup server.")
+ })
+
})
- this.server.get("/setup/done", async (req, res) => {
- if (!this.gameWebhook.isPresent) {
- res.redirect("gameChannel")
- } else if (!this.adminWebhook.isPresent) {
- res.redirect("adminChannel")
- } else {
- res.code(200)
- res.type("text/html")
- res.send(pug.renderFile("static/pages/setupDone.pug", {
+ this.server.get("/shutdown", async (req, res) => {
+ if (!checkAndClearXSRFCookie(req, res)) {
+ return renderError({
baseUrl: getBaseUrl(req),
- clearUrl: "setup/clear",
- gameSetupUrl: "setup/gameChannel",
- adminSetupUrl: "setup/adminChannel"}))
+ res,
+ code: 400,
+ error: "Token was incorrect or not set.",
+ context: "shutting down the game",
+ buttonText: "Return to Game",
+ buttonUrl: "game/started"
+ })
}
+ res.code(200)
+ res.type("text/html")
+ res.send(pug.renderFile("static/pages/shutdown.pug"))
+ setImmediate(async () => {
+ this.server.log.info("Shutting down the game server.")
+ try {
+ await this.server.close()
+ } catch (e) {
+ this.server.log.error(e, "Failed to shut down the game server")
+ }
+ this.server.log.info("Shut down. Good night...")
+ })
})
- await Promise.all([this.gameWebhook.load(), this.adminWebhook.load(), this.server.listen(this.port, "127.0.0.1")])
}
}
\ No newline at end of file
diff --git a/src/PugRenderer.ts b/src/PugRenderer.ts
index b783a96..00b46d8 100644
--- a/src/PugRenderer.ts
+++ b/src/PugRenderer.ts
@@ -2,12 +2,24 @@ import {FastifyReply} from "fastify";
import {renderFile} from "pug";
/** Renders the error page into the given reply. */
-export function renderError({baseUrl, res, error, context}: {baseUrl: string, res: FastifyReply, error: unknown, context: string}): void {
- res.code(500)
+export function renderError({
+ baseUrl,
+ code = 500,
+ res,
+ error,
+ context,
+ errorUrl,
+ buttonText,
+ buttonUrl
+ }: { baseUrl: string, code?: number, res: FastifyReply, error: unknown, context: string, errorUrl?: string, buttonText?: string, buttonUrl?: string }): void {
+ res.code(code)
res.type("text/html")
- res.send(renderFile("static/pages/error.pug", {
+ res.send(renderFile("static/pages/setup/error.pug", {
baseUrl,
error,
context,
+ errorUrl,
+ buttonText: buttonText ?? "Return to Setup",
+ buttonUrl: buttonUrl ?? "setup"
}))
}
\ No newline at end of file
diff --git a/src/SavedWebhook.ts b/src/SavedWebhook.ts
index dfa9012..aa28bec 100644
--- a/src/SavedWebhook.ts
+++ b/src/SavedWebhook.ts
@@ -1,7 +1,7 @@
/** */
import {APIWebhook} from "discord-api-types";
import {FastifyLoggerInstance} from "fastify";
-import {readFile as fsReadFile, writeFile as fsWriteFile, mkdir as fsMkdir, rm as fsRm} from "fs/promises";
+import {mkdir as fsMkdir, readFile as fsReadFile, rm as fsRm, writeFile as fsWriteFile} from "fs/promises";
import axios from "axios";
import {join} from "path";
import {isENOENT} from "./NodeErrorHelpers.js";
@@ -11,8 +11,7 @@ const WebhookPath = "runtime/webhooks"
/** File-based storage for webhook instances. */
export class SavedWebhook {
readonly filename: string
- private _state: APIWebhook|null|undefined
- private readonly logger: FastifyLoggerInstance|null
+ private readonly logger: FastifyLoggerInstance | null
private readonly readFile: typeof fsReadFile
private readonly writeFile: typeof fsWriteFile
private readonly mkdir: typeof fsMkdir
@@ -20,7 +19,15 @@ export class SavedWebhook {
private readonly delRequest: typeof axios["delete"]
/** Initializes a SavedWebhook pointing at the given location. */
- constructor(filename: string, {logger, state, readFile, writeFile, mkdir, rm, delRequest}: {logger?: FastifyLoggerInstance|null, state?: APIWebhook|null, readFile?: typeof fsReadFile, writeFile?: typeof fsWriteFile, mkdir?: typeof fsMkdir, rm?: typeof fsRm, delRequest?: typeof axios["delete"]}) {
+ constructor(filename: string, {
+ logger,
+ state,
+ readFile,
+ writeFile,
+ mkdir,
+ rm,
+ delRequest
+ }: { logger?: FastifyLoggerInstance | null, state?: APIWebhook | null, readFile?: typeof fsReadFile, writeFile?: typeof fsWriteFile, mkdir?: typeof fsMkdir, rm?: typeof fsRm, delRequest?: typeof axios["delete"] }) {
this.filename = filename
this._state = state
this.logger = logger?.child({webhookFile: filename}) ?? null
@@ -31,6 +38,17 @@ export class SavedWebhook {
this.delRequest = delRequest ?? axios.delete.bind(axios)
}
+ private _state: APIWebhook | null | undefined
+
+ /** Gets the current state of the webhook, either a webhook instance or null. */
+ get state(): APIWebhook | null {
+ if (this._state === undefined) {
+ this.logger?.warn("SavedWebhook was not initialized before having its state checked")
+ return null
+ }
+ return this._state
+ }
+
/** The path of the webhook, including WebhookPath. */
get path(): string {
return join(WebhookPath, this.filename)
@@ -41,17 +59,8 @@ export class SavedWebhook {
return this.state !== null
}
- /** Gets the current state of the webhook, either a webhook instance or null. */
- get state(): APIWebhook|null {
- if (this._state === undefined) {
- this.logger?.warn("SavedWebhook was not initialized before having its state checked")
- return null
- }
- return this._state
- }
-
/** Loads the current state from the disk, including a null state if not present. */
- async load(): Promise {
+ async load(): Promise {
if (this._state !== undefined) {
this.logger?.warn(`SavedWebhook was double-initialized`)
}
@@ -138,6 +147,6 @@ export class SavedWebhook {
return
}
await this.delRequest(`https://discord.com/api/webhooks/${this._state.id}/${this._state.token}`,
- {validateStatus: (s) => s === 204 || s === 404 })
+ {validateStatus: (s) => s === 204 || s === 404})
}
}
\ No newline at end of file
diff --git a/src/SetupServer.ts b/src/SetupServer.ts
new file mode 100644
index 0000000..ca1497e
--- /dev/null
+++ b/src/SetupServer.ts
@@ -0,0 +1,192 @@
+import {DiscordWebhookHandler, OAuthRoute} from "./DiscordWebhookHandler.js";
+import {getBaseUrl} from "./FastifyHelpers.js";
+import pug from "pug";
+import {renderError} from "./PugRenderer.js";
+import {GameServer} from "./GameServer.js";
+import {checkAndClearXSRFCookie, generateXSRFCookie, XSRFRoute} from "./CookieHelpers.js";
+import {SetupCommand, setupComponentInteractionHandler} from "./commands/SetupCommand.js";
+import {BaseServer, BaseServerDeps} from "./BaseServer.js";
+
+export class SetupServer extends BaseServer {
+ readonly gameFactory: () => GameServer
+
+ constructor(deps: BaseServerDeps & { gameFactory: () => GameServer }) {
+ super(deps)
+ this.gameFactory = deps.gameFactory
+ }
+
+ async _initInternal(): Promise {
+ this.slashcmd.registerCommand(new SetupCommand(this.slashcmd))
+ this.slashcmd.on("componentInteraction", setupComponentInteractionHandler)
+ const gameHandler = new DiscordWebhookHandler({
+ webhook: this.gameWebhook,
+ templateFilename: "setup/game.pug",
+ appId: this.appId,
+ secret: this.clientSecret,
+ destinationFunc: () => {
+ if (this.adminWebhook.isPresent) {
+ return "clear"
+ } else {
+ return "adminChannel"
+ }
+ },
+ })
+ const adminHandler = new DiscordWebhookHandler({
+ webhook: this.adminWebhook,
+ templateFilename: "setup/admin.pug",
+ appId: this.appId,
+ secret: this.clientSecret,
+ destinationFunc: () => {
+ if (this.gameWebhook.isPresent) {
+ return "clear"
+ } else {
+ return "gameChannel"
+ }
+ },
+ })
+ this.server.get("/setup", async (req, res) => {
+ if (!this.gameWebhook.isPresent) {
+ res.redirect(`setup/gameChannel`)
+ } else if (!this.adminWebhook.isPresent) {
+ res.redirect(`setup/adminChannel`)
+ } else {
+ res.redirect(`setup/done`)
+ }
+ })
+ this.server.get("/setup/start", async (req, res) => {
+ res.redirect(".")
+ })
+ this.server.get("/game", async (req, res) => {
+ res.redirect("setup")
+ })
+ this.server.get("/game/started", async (req, res) => {
+ res.redirect("../setup")
+ })
+ this.server.get("/game/stop", async (req, res) => {
+ res.redirect("../setup")
+ })
+ this.server.get("/game/start", async (req, res) => {
+ if (!checkAndClearXSRFCookie(req, res)) {
+ return renderError({
+ baseUrl: getBaseUrl(req),
+ res,
+ code: 400,
+ error: "Token was incorrect or not set.",
+ context: "starting the game",
+ buttonText: "Return to Setup",
+ buttonUrl: "setup"
+ })
+ }
+ if (!this.gameWebhook.isPresent || !this.adminWebhook.isPresent) {
+ return renderError({
+ baseUrl: getBaseUrl(req),
+ res,
+ code: 409,
+ error: "You can't start the game while one of the channels is not set!",
+ context: "starting the game",
+ buttonText: "Finish Setup",
+ buttonUrl: "setup"
+ })
+ }
+ res.code(200)
+ res.type("text/html")
+ res.send(pug.renderFile("static/pages/setup/start.pug", {
+ startedUrl: "game/start"
+ }))
+ setImmediate(async () => {
+ this.server.log.info("Shutting down the setup server and switching to the game server.")
+ try {
+ await this.server.close()
+ } catch (e) {
+ this.server.log.error(e, "Failed to shut down the setup server")
+ }
+ try {
+ await this.gameFactory().initialize()
+ } catch (e) {
+ this.server.log.error(e, "Failed to start up the game server")
+ }
+ this.server.log.info("Successfully switched from the setup server to the game server.")
+ })
+
+ })
+ this.server.get("/setup/gameChannel", async (req, res) => {
+ return await gameHandler.handleRequest(req, res)
+ })
+ this.server.get("/setup/adminChannel", async (req, res) => {
+ return await adminHandler.handleRequest(req, res)
+ })
+ this.server.get("/setup/clear", async (req, res) => {
+ if (!checkAndClearXSRFCookie(req, res)) {
+ return renderError({
+ baseUrl: getBaseUrl(req),
+ res,
+ code: 400,
+ error: "Token was incorrect or not set.",
+ context: "clearing the channel setup",
+ buttonText: "Return to Setup",
+ buttonUrl: "setup"
+ })
+ }
+ try {
+ await Promise.all([this.gameWebhook, this.adminWebhook]
+ .filter((item) => item.isPresent)
+ .map((item) => item.clearHook()))
+ } catch (e) {
+ return renderError({
+ baseUrl: getBaseUrl(req),
+ res,
+ code: 500,
+ error: e,
+ context: "clearing and deleting the webhooks",
+ buttonUrl: "setup/clear",
+ buttonText: "Try Again"
+ })
+ }
+ res.redirect(".")
+ })
+ this.server.get("/setup/done", async (req, res) => {
+ if (!this.gameWebhook.isPresent) {
+ res.redirect("gameChannel")
+ } else if (!this.adminWebhook.isPresent) {
+ res.redirect("adminChannel")
+ } else {
+ const token = generateXSRFCookie(res)
+ res.code(200)
+ res.type("text/html")
+ res.send(pug.renderFile("static/pages/setup/done.pug", {
+ baseUrl: getBaseUrl(req),
+ gameModeUrl: `game/start?token=${token}`,
+ clearUrl: `setup/clear?token=${token}`,
+ gameSetupUrl: `setup/gameChannel`,
+ adminSetupUrl: `setup/adminChannel`,
+ shutdownUrl: `shutdown?token=${token}`
+ }))
+ }
+ })
+ this.server.get("/shutdown", async (req, res) => {
+ if (!checkAndClearXSRFCookie(req, res)) {
+ return renderError({
+ baseUrl: getBaseUrl(req),
+ res,
+ code: 400,
+ error: "Token was incorrect or not set.",
+ context: "shutting the server down",
+ buttonText: "Return to Setup",
+ buttonUrl: "setup"
+ })
+ }
+ res.code(200)
+ res.type("text/html")
+ res.send(pug.renderFile("static/pages/shutdown.pug"))
+ setImmediate(async () => {
+ this.server.log.info("Shutting down the setup server.")
+ try {
+ await this.server.close()
+ } catch (e) {
+ this.server.log.error(e, "Failed to shut down the setup server")
+ }
+ this.server.log.info("Shut down. Good night...")
+ })
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/app.ts b/src/app.ts
index 35e3145..5eb5cf4 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -1,5 +1,11 @@
import dotenv from "dotenv";
+import {SetupServer} from "./SetupServer.js";
import {GameServer} from "./GameServer.js";
+import cryptoRandomString from "crypto-random-string";
+import {SavedWebhook} from "./SavedWebhook.js";
+import pino from "pino"
+
+const log = pino()
async function main(): Promise {
const {parsed, error} = dotenv.config()
@@ -8,14 +14,44 @@ async function main(): Promise {
}
const clientSecret = parsed["DISCORD_CLIENT_SECRET"]
const appId = parsed["DISCORD_APP_ID"]
- const port = parseInt(parsed["HTTP_PORT"] ?? "5244")
- const server = new GameServer({
- appId, port, secret: clientSecret
- })
- return await server.initialize()
+ const botToken = parsed["DISCORD_BOT_TOKEN"]
+ const publicKey = parsed["DISCORD_PUBLIC_KEY"]
+ const listenPort = parseInt(parsed["HTTP_PORT"] ?? "5244")
+ const listenAddress = parsed["HTTP_ADDRESS"] ?? "127.0.0.1"
+ const cookieSecret = parsed["COOKIE_SECRET"] ?? cryptoRandomString({length: 32, type: "base64"})
+ const gameWebhook = new SavedWebhook("game.json", {logger: log})
+ const adminWebhook = new SavedWebhook("admin.json", {logger: log})
+ const factory: { game: () => GameServer, setup: () => SetupServer } = {
+ game(): never {
+ throw Error("game factory not set up yet")
+ },
+ setup(): never {
+ throw Error("setup factory not set up yet")
+ },
+ }
+ const deps = {
+ appId,
+ listenAddress,
+ listenPort,
+ clientSecret,
+ cookieSecret,
+ gameFactory: () => factory.game(),
+ setupFactory: () => factory.setup(),
+ gameWebhook,
+ adminWebhook,
+ botToken,
+ publicKey,
+ }
+ factory.setup = () => new SetupServer(deps)
+ factory.game = () => new GameServer(deps)
+ await Promise.all([gameWebhook.load(), adminWebhook.load()])
+ if (gameWebhook.isPresent && adminWebhook.isPresent) {
+ await factory.game().initialize()
+ } else {
+ await factory.setup().initialize()
+ }
}
main().catch((err) => {
- console.log("Main crashed!")
- console.log(err)
+ log.fatal(err, "Startup failed!")
})
diff --git a/src/commands/SetupCommand.ts b/src/commands/SetupCommand.ts
new file mode 100644
index 0000000..8703f0c
--- /dev/null
+++ b/src/commands/SetupCommand.ts
@@ -0,0 +1,28 @@
+import {AutocompleteContext, CommandContext, ComponentContext, Message, SlashCommand, SlashCreator} from "slash-create";
+
+export class SetupCommand extends SlashCommand {
+ constructor(creator: SlashCreator) {
+ super(creator, {
+ name: "setup-commands-disabled",
+ unknown: true
+ });
+ }
+
+ autocomplete(ctx: AutocompleteContext): Promise {
+ return ctx.sendResults([])
+ }
+
+ run(ctx: CommandContext): Promise {
+ return ctx.send({
+ ephemeral: true,
+ content: "The server is currently in setup mode! You can't run commands right now..."
+ })
+ }
+}
+
+export function setupComponentInteractionHandler(ctx: ComponentContext): Promise {
+ return ctx.send({
+ ephemeral: true,
+ content: "The server is currently in setup mode! You can't interact with its messages right now..."
+ })
+}
\ No newline at end of file
diff --git a/src/commands/game/PullCommand.ts b/src/commands/game/PullCommand.ts
new file mode 100644
index 0000000..a1fda32
--- /dev/null
+++ b/src/commands/game/PullCommand.ts
@@ -0,0 +1,56 @@
+import {CommandContext, CommandOptionType, SlashCommand, SlashCreator} from "slash-create";
+import {SavedWebhook} from "../../SavedWebhook.js";
+import {Chance} from "chance";
+
+const rand = Chance()
+
+export class PullCommand extends SlashCommand {
+ readonly gameWebhook: SavedWebhook
+
+ constructor(creator: SlashCreator, gameWebhook: SavedWebhook) {
+ super(creator, {
+ name: "pull",
+ guildIDs: gameWebhook.state?.guild_id,
+ description: "Pulls one or more new heroines from the ether.",
+ options: [
+ {
+ name: "count",
+ description: "The number of heroines to pull.",
+ required: false,
+ max_value: 10,
+ min_value: 1,
+ type: CommandOptionType.NUMBER,
+ }
+ ]
+ });
+ this.gameWebhook = gameWebhook
+ }
+
+ run(ctx: CommandContext): Promise {
+ if (ctx.guildID !== this.gameWebhook.state?.guild_id) {
+ return ctx.send({
+ content: "Sorry, you can't do that in this guild.",
+ ephemeral: true,
+ })
+ }
+ if (ctx.channelID !== this.gameWebhook.state?.channel_id) {
+ return ctx.send({
+ content: `Sorry, you can't do that here. You have to do it in <#${this.gameWebhook.state?.channel_id}>.`,
+ ephemeral: true,
+ })
+ }
+ const count: number = ctx.options.count ?? 1
+ const results: string[] = []
+ for (let x = 0; x < count; x += 1) {
+ results.push(rand.weighted(["**Nicole**: D tier Predator Podcaster",
+ "**Herja**: C tier Viking Warrior",
+ "**Sharla**: B tier Skark Girl",
+ "**Melpomene**: A tier Muse of Tragedy",
+ "**Lady Bootstrap**: S tier Time Traveler"], [20, 15, 10, 5, 1]))
+ }
+ return ctx.send({
+ content: `_${ctx.user.mention}_, you pulled...\n \\* ${results.join("\n \\* ")}`,
+ ephemeral: false,
+ })
+ }
+}
\ No newline at end of file
diff --git a/static/pages/error.pug b/static/pages/error.pug
deleted file mode 100644
index 8a95d15..0000000
--- a/static/pages/error.pug
+++ /dev/null
@@ -1,10 +0,0 @@
-extends ../template/template
-
-block title
- | Setup Error
-block content
- | Something went wrong while #{context}. What did you do?
- br
- | The error was: #{error}
-block link
- a(href="setup") Return to Setup Page
\ No newline at end of file
diff --git a/static/pages/game/running.pug b/static/pages/game/running.pug
new file mode 100644
index 0000000..a324be5
--- /dev/null
+++ b/static/pages/game/running.pug
@@ -0,0 +1,11 @@
+extends ../../template/template
+
+block title
+ | Game Running
+block content
+ | The game is up and running.
+ br
+ | To make changes to its configuration setup, click Return to Setup.
+block link
+ a(href=setupModeUrl, rel="nofollow") Return to Setup
+ a(href=shutdownUrl, rel="nofollow") Shut Down Server
\ No newline at end of file
diff --git a/static/pages/game/stop.pug b/static/pages/game/stop.pug
new file mode 100644
index 0000000..5e477c2
--- /dev/null
+++ b/static/pages/game/stop.pug
@@ -0,0 +1,13 @@
+extends ../../template/template
+
+block title
+ | Game Stopping
+block content
+ | You got it! The game is being stopped and returned to the setup phase now...
+ | If you aren't automatically redirected after a few seconds, click the button below.
+ script.
+ setTimeout(() => {
+ document.getElementById("stopButton").click()
+ }, 3000)
+block link
+ a(href=stoppedUrl, id="stopButton", rel="nofollow") Verify Game Stopped
\ No newline at end of file
diff --git a/static/pages/setupAdmin.pug b/static/pages/setup/admin.pug
similarity index 91%
rename from static/pages/setupAdmin.pug
rename to static/pages/setup/admin.pug
index 716b06e..9bf0028 100644
--- a/static/pages/setupAdmin.pug
+++ b/static/pages/setup/admin.pug
@@ -1,4 +1,4 @@
-extends ../template/template
+extends ../../template/template
block title
| Admin Setup
diff --git a/static/pages/setup/done.pug b/static/pages/setup/done.pug
new file mode 100644
index 0000000..8328fba
--- /dev/null
+++ b/static/pages/setup/done.pug
@@ -0,0 +1,14 @@
+extends ../../template/template
+
+block title
+ | Setup Complete!
+block content
+ | You're all done! Time to just play the game.
+ br
+ | If you'd like to reconfigure the game's channels, click one of the buttons below.
+block link
+ a(href=gameModeUrl, rel="nofollow") Start the Game!
+ a(href=gameSetupUrl, rel="nofollow") Reconfigure Game Channel
+ a(href=adminSetupUrl, rel="nofollow") Reconfigure Admin Channel
+ a(href=clearUrl, rel="nofollow") Clear All Channels
+ a(href=shutdownUrl, rel="nofollow") Shut Down Server
\ No newline at end of file
diff --git a/static/pages/setup/error.pug b/static/pages/setup/error.pug
new file mode 100644
index 0000000..50074b9
--- /dev/null
+++ b/static/pages/setup/error.pug
@@ -0,0 +1,13 @@
+extends ../../template/template
+
+block title
+ | Setup Error
+block content
+ | Something went wrong while #{context}. What did you do?
+ br
+ if errorUrl
+ | The error was: #[a(href=errorUrl) #{error}]
+ else
+ | The error was: #{error}
+block link
+ a(href=buttonUrl, rel="nofollow") #{buttonText}
\ No newline at end of file
diff --git a/static/pages/setupGame.pug b/static/pages/setup/game.pug
similarity index 87%
rename from static/pages/setupGame.pug
rename to static/pages/setup/game.pug
index e057c19..5769964 100644
--- a/static/pages/setupGame.pug
+++ b/static/pages/setup/game.pug
@@ -1,4 +1,4 @@
-extends ../template/template
+extends ../../template/template
block title
| Game Setup
@@ -15,4 +15,4 @@ block content
br
| Click the link to choose a game channel.
block link
- a(href=authUrl) Connect Game Channel
\ No newline at end of file
+ a(href=authUrl, rel="nofollow") Connect Game Channel
\ No newline at end of file
diff --git a/static/pages/setup/start.pug b/static/pages/setup/start.pug
new file mode 100644
index 0000000..dff3970
--- /dev/null
+++ b/static/pages/setup/start.pug
@@ -0,0 +1,13 @@
+extends ../../template/template
+
+block title
+ | Game Starting
+block content
+ | You got it! The game is being started up now...
+ | If you aren't automatically redirected after a few seconds, click the button below.
+ script.
+ setTimeout(() => {
+ document.getElementById("startButton").click()
+ }, 3000)
+block link
+ a(href=startedUrl, id="startButton", rel="nofollow") Verify Game Started
\ No newline at end of file
diff --git a/static/pages/setupDone.pug b/static/pages/setupDone.pug
deleted file mode 100644
index 05339e1..0000000
--- a/static/pages/setupDone.pug
+++ /dev/null
@@ -1,12 +0,0 @@
-extends ../template/template
-
-block title
- | Setup Complete!
-block content
- | You're all done! Time to just play the game.
- br
- | If you'd like to reconfigure the game's channels, click one of the buttons below.
-block link
- a(href=gameSetupUrl) Reconfigure Game Channel
- a(href=adminSetupUrl) Reconfigure Admin Channel
- a(href=clearUrl) Clear All Channels
\ No newline at end of file
diff --git a/static/pages/shutdown.pug b/static/pages/shutdown.pug
new file mode 100644
index 0000000..c48c4ca
--- /dev/null
+++ b/static/pages/shutdown.pug
@@ -0,0 +1,6 @@
+extends ../template/template
+
+block title
+ | Server Shutdown
+block content
+ | You got it. The server has been shut down. Good night...
\ No newline at end of file
diff --git a/static/template/template.pug b/static/template/template.pug
index b7046c5..1f09fda 100644
--- a/static/template/template.pug
+++ b/static/template/template.pug
@@ -4,13 +4,51 @@ html
block title
base(href=baseUrl)
style.
- body { display: flex; flex-flow: column; justify-content: center; align-items: center; min-height: 100vh; font-family: sans-serif; background-color: darkblue; color: aliceblue }
- body * { max-width: 40em; }
- p.content { text-align: center; }
- p.link { display: flex; flex-flow: row; justify-content: center; align-items: center; }
- p.link a { display: inline-block; border-radius: 5px; background-color: cadetblue; color: black; border: 2px solid darkslateblue; padding: 0.5em; box-shadow: 2px 4px 0 rgba(255,255,255,0.55); }
- a:visited, a:active, a:link { color: inherit; text-decoration: none; }
- p.link a:active { transform: translate(2px, 4px); box-shadow: 0 0 transparent; }
+ body {
+ display: flex;
+ flex-flow: column;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ font-family: sans-serif;
+ background-color: darkblue;
+ color: aliceblue
+ }
+
+ body * {
+ max-width: 40em;
+ }
+
+ p.content {
+ text-align: center;
+ }
+
+ p.link {
+ display: flex;
+ flex-flow: row;
+ justify-content: center;
+ align-items: center;
+ }
+
+ p.link a {
+ display: inline-block;
+ border-radius: 5px;
+ background-color: cadetblue;
+ color: black;
+ border: 2px solid darkslateblue;
+ padding: 0.5em;
+ box-shadow: 2px 4px 0 rgba(255, 255, 255, 0.55);
+ }
+
+ p.link a:visited, p.link a:active, p.link a:link {
+ color: inherit;
+ text-decoration: none;
+ }
+
+ p.link a:active {
+ transform: translate(2px, 4px);
+ box-shadow: 0 0 transparent;
+ }
body
h1
block title
diff --git a/tsconfig.json b/tsconfig.json
index f00bfbd..011d0f7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
{
"compilerOptions": {
- "target": "es5",
+ "target": "ES2018",
"lib": [
"dom",
"dom.iterable",