loads of new commands, updates and bug fixes
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..3e187f2
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2920 @@
+{
+  "name": "nucleus",
+  "version": "0.0.1",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "nucleus",
+      "version": "0.0.1",
+      "license": "SEE LICENSE IN LICENSE",
+      "dependencies": {
+        "@discordjs/builders": "^0.12.0",
+        "@ungap/structured-clone": "^1.0.1",
+        "body-parser": "^1.20.0",
+        "discord.js": "^13.8.0",
+        "express": "^4.18.1",
+        "humanize": "^0.0.9",
+        "humanize-duration": "^3.27.1",
+        "jshaiku": "file:../haiku",
+        "json-diff": "^0.7.1",
+        "mongodb": "^4.7.0",
+        "node-tesseract": "^0.2.7",
+        "structured-clone": "^0.2.2",
+        "tesseract.js": "^2.1.5",
+        "typescript": "^4.5.5",
+        "unscan": "^1.1.2"
+      }
+    },
+    "../haiku": {
+      "name": "jshaiku",
+      "version": "1.0.0",
+      "license": "AGPL-3.0",
+      "dependencies": {
+        "@discordjs/builders": "^0.11.0",
+        "@discordjs/rest": "^0.2.0-canary.0",
+        "@types/node-cron": "^3.0.1",
+        "ansi-styles": "^6.1.0",
+        "chalk": "^5.0.0",
+        "discord-api-types": "^0.26.1",
+        "discord.js": "^13.8.0",
+        "node-cron": "^3.0.0"
+      },
+      "devDependencies": {
+        "@babel/core": "^7.16.7",
+        "@babel/preset-env": "^7.16.8",
+        "@babel/preset-typescript": "^7.16.7",
+        "@types/jest": "^27.4.0",
+        "babel-jest": "^27.4.6",
+        "jest": "^27.4.7",
+        "ts-node": "^10.4.0",
+        "typescript": "4.5.4"
+      }
+    },
+    "node_modules/@discordjs/builders": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.12.0.tgz",
+      "integrity": "sha512-Vx2MjUZd6QVo1uS2uWt708Fd6cHWGFblAvbpL5EBO+kLl0BADmPwwvts+YJ/VfSywed6Vsk6K2cEooR/Ytjhjw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@sindresorhus/is": "^4.3.0",
+        "discord-api-types": "^0.26.1",
+        "ts-mixer": "^6.0.0",
+        "tslib": "^2.3.1",
+        "zod": "^3.11.6"
+      },
+      "engines": {
+        "node": ">=16.9.0"
+      }
+    },
+    "node_modules/@discordjs/collection": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz",
+      "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==",
+      "engines": {
+        "node": ">=16.9.0"
+      }
+    },
+    "node_modules/@sapphire/async-queue": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.1.tgz",
+      "integrity": "sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g==",
+      "engines": {
+        "node": ">=v14.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/@sapphire/shapeshift": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.2.0.tgz",
+      "integrity": "sha512-asNgE5Ooil2/oGIAj6vZMoUc2ZFED0TGYD7jwvZsjHPQZBEh9ITj94ca4bCgiCR1s2ER/UjzykH+5wE3ebVZnQ==",
+      "engines": {
+        "node": ">=v15.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "17.0.21",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
+      "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "form-data": "^3.0.0"
+      }
+    },
+    "node_modules/@types/node-fetch/node_modules/form-data": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+      "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/@types/webidl-conversions": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz",
+      "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==",
+      "license": "MIT"
+    },
+    "node_modules/@types/whatwg-url": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz",
+      "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/webidl-conversions": "*"
+      }
+    },
+    "node_modules/@types/ws": {
+      "version": "8.5.3",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+      "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@ungap/structured-clone": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.0.1.tgz",
+      "integrity": "sha512-zKVyTt6rELvPXYwcVPTJcPFtY0AckN5A7xWuc7owBqR0FdtuDYhE9MZZUi6IY1kZUQFSXV1B3UOOIyLkVHYd2w==",
+      "license": "ISC"
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "license": "MIT"
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/blueimp-load-image": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-3.0.0.tgz",
+      "integrity": "sha512-Q9rFbd4ZUNvzSFmRXx9MoG0RwWwJeMjjEUbG7WIOJgUg22Jgkow0wL5b35B6qwiBscxACW9OHdrP5s2vQ3x8DQ==",
+      "license": "MIT"
+    },
+    "node_modules/bmp-js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+      "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=",
+      "license": "MIT"
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
+      "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.10.3",
+        "raw-body": "2.5.1",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/bson": {
+      "version": "4.6.4",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.4.tgz",
+      "integrity": "sha512-TdQ3FzguAu5HKPPlr0kYQCyrYUYh8tFM+CMTpxjNzVzxeiJY00Rtuj3LXLHSgiGvmaWlZ8PE+4KyM2thqE38pQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "buffer": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/cli-color": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.1.tgz",
+      "integrity": "sha512-eBbxZF6fqPUNnf7CLAFOersUnyYzv83tHFLSlts+OAHsNendaqv2tHCq+/MO+b3Y+9JeoUlIvobyxG/Z8GNeOg==",
+      "license": "ISC",
+      "dependencies": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.53",
+        "es6-iterator": "^2.0.3",
+        "memoizee": "^0.4.15",
+        "timers-ext": "^0.1.7"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/colors": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+      "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "license": "MIT"
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+      "license": "MIT"
+    },
+    "node_modules/d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "license": "ISC",
+      "dependencies": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/denque": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
+      "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/difflib": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz",
+      "integrity": "sha1-teMDYabbAjF21WKJLbhZQKcY9H4=",
+      "dependencies": {
+        "heap": ">= 0.2.0"
+      }
+    },
+    "node_modules/discord-api-types": {
+      "version": "0.26.1",
+      "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.26.1.tgz",
+      "integrity": "sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/discord.js": {
+      "version": "13.8.0",
+      "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.8.0.tgz",
+      "integrity": "sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==",
+      "dependencies": {
+        "@discordjs/builders": "^0.14.0",
+        "@discordjs/collection": "^0.7.0",
+        "@sapphire/async-queue": "^1.3.1",
+        "@types/node-fetch": "^2.6.1",
+        "@types/ws": "^8.5.3",
+        "discord-api-types": "^0.33.3",
+        "form-data": "^4.0.0",
+        "node-fetch": "^2.6.1",
+        "ws": "^8.7.0"
+      },
+      "engines": {
+        "node": ">=16.6.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/discord.js/node_modules/@discordjs/builders": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.14.0.tgz",
+      "integrity": "sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==",
+      "dependencies": {
+        "@sapphire/shapeshift": "^3.1.0",
+        "@sindresorhus/is": "^4.6.0",
+        "discord-api-types": "^0.33.3",
+        "fast-deep-equal": "^3.1.3",
+        "ts-mixer": "^6.0.1",
+        "tslib": "^2.4.0"
+      },
+      "engines": {
+        "node": ">=16.9.0"
+      }
+    },
+    "node_modules/discord.js/node_modules/discord-api-types": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz",
+      "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg=="
+    },
+    "node_modules/dreamopt": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/dreamopt/-/dreamopt-0.8.0.tgz",
+      "integrity": "sha1-W8yAvnCX5F/EicNCQFq2gUCowdk=",
+      "dependencies": {
+        "wordwrap": ">=0.0.2"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/es5-ext": {
+      "version": "0.10.57",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.57.tgz",
+      "integrity": "sha512-L7cCNoPwTkAp7IBHxrKLsh7NKiVFkcdxlP9vbVw9QUvb7gF0Mz9bEBN0WY9xqdTjGF907EMT/iG013vnbqwu1Q==",
+      "hasInstallScript": true,
+      "license": "ISC",
+      "dependencies": {
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "next-tick": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "license": "MIT",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "license": "ISC",
+      "dependencies": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "node_modules/es6-weak-map": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+      "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+      "license": "ISC",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "^0.10.46",
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+      "license": "MIT"
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+      "license": "MIT",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
+    "node_modules/express": {
+      "version": "4.18.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
+      "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.0",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.5.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.2.0",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.10.3",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.18.0",
+        "serve-static": "1.15.0",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/ext": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
+      "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
+      "license": "ISC",
+      "dependencies": {
+        "type": "^2.5.0"
+      }
+    },
+    "node_modules/ext/node_modules/type": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
+      "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==",
+      "license": "ISC"
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+    },
+    "node_modules/file-type": {
+      "version": "12.4.2",
+      "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz",
+      "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+      "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "license": "ISC"
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "license": "MIT"
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/glob": {
+      "version": "5.0.15",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+      "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+      "license": "ISC",
+      "dependencies": {
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "2 || 3",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/heap": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
+      "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
+      "license": "MIT"
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/humanize": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/humanize/-/humanize-0.0.9.tgz",
+      "integrity": "sha1-GZT/rs3+nEQe0r2sdFK3u0yeQaQ=",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/humanize-duration": {
+      "version": "3.27.1",
+      "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz",
+      "integrity": "sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA==",
+      "license": "Unlicense"
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/idb-keyval": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-3.2.0.tgz",
+      "integrity": "sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/ip": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==",
+      "license": "MIT"
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-electron": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.1.tgz",
+      "integrity": "sha512-r8EEQQsqT+Gn0aXFx7lTFygYQhILLCB+wn0WCDL5LZRINeLH/Rvw1j2oKodELLXYNImQ3CRlVsY8wW4cGOsyuw==",
+      "license": "MIT"
+    },
+    "node_modules/is-promise": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
+      "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
+      "license": "MIT"
+    },
+    "node_modules/is-url": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+      "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+      "license": "MIT"
+    },
+    "node_modules/jpeg-autorotate": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/jpeg-autorotate/-/jpeg-autorotate-7.1.1.tgz",
+      "integrity": "sha512-ewTZTG/QWOM0D5h/yKcQ3QgyrnQYsr3qmcS+bqoAwgQAY1KBa31aJ+q+FlElaxo/rSYqfF1ixf+8EIgluBkgTg==",
+      "license": "MIT",
+      "dependencies": {
+        "colors": "^1.4.0",
+        "glob": "^7.1.6",
+        "jpeg-js": "^0.4.2",
+        "piexifjs": "^1.0.6",
+        "yargs-parser": "^20.2.1"
+      },
+      "bin": {
+        "jpeg-autorotate": "src/cli.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jpeg-autorotate/node_modules/glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/jpeg-js": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.3.tgz",
+      "integrity": "sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/jshaiku": {
+      "resolved": "../haiku",
+      "link": true
+    },
+    "node_modules/json-diff": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.7.3.tgz",
+      "integrity": "sha512-VBvNBt3cIrCBHa3gYbVsCFUEReqWZPf+Biq1ZtFdIiQ6rytRLDp3qvtrGv7z/iZDd1D4vXWpW7Nx1nP8muLzkg==",
+      "license": "MIT",
+      "dependencies": {
+        "cli-color": "^2.0.0",
+        "difflib": "~0.2.1",
+        "dreamopt": "~0.8.0"
+      },
+      "bin": {
+        "json-diff": "bin/json-diff.js"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/lru-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
+      "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=",
+      "license": "MIT",
+      "dependencies": {
+        "es5-ext": "~0.10.2"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/memoizee": {
+      "version": "0.4.15",
+      "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz",
+      "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==",
+      "license": "ISC",
+      "dependencies": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.53",
+        "es6-weak-map": "^2.0.3",
+        "event-emitter": "^0.3.5",
+        "is-promise": "^2.2.2",
+        "lru-queue": "^0.1.0",
+        "next-tick": "^1.1.0",
+        "timers-ext": "^0.1.7"
+      }
+    },
+    "node_modules/memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+      "license": "MIT"
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.51.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+      "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.34",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+      "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.51.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mongodb": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.7.0.tgz",
+      "integrity": "sha512-HhVar6hsUeMAVlIbwQwWtV36iyjKd9qdhY+s4wcU8K6TOj4Q331iiMy+FoPuxEntDIijTYWivwFJkLv8q/ZgvA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bson": "^4.6.3",
+        "denque": "^2.0.1",
+        "mongodb-connection-string-url": "^2.5.2",
+        "socks": "^2.6.2"
+      },
+      "engines": {
+        "node": ">=12.9.0"
+      },
+      "optionalDependencies": {
+        "saslprep": "^1.0.3"
+      }
+    },
+    "node_modules/mongodb-connection-string-url": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.2.tgz",
+      "integrity": "sha512-tWDyIG8cQlI5k3skB6ywaEA5F9f5OntrKKsT/Lteub2zgwSUlhqEN2inGgBTm8bpYJf8QYBdA/5naz65XDpczA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/whatwg-url": "^8.2.1",
+        "whatwg-url": "^11.0.0"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+      "license": "MIT"
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/next-tick": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
+      "license": "ISC"
+    },
+    "node_modules/node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-fetch/node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
+      "license": "MIT"
+    },
+    "node_modules/node-fetch/node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/node-tesseract": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/node-tesseract/-/node-tesseract-0.2.7.tgz",
+      "integrity": "sha1-yPAvuDUaQnByc1d4wFGYI/JgG4Q=",
+      "license": "MIT",
+      "dependencies": {
+        "glob": "^5.0.10",
+        "node-uuid": "^1.4.1"
+      }
+    },
+    "node_modules/node-uuid": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+      "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
+      "bin": {
+        "uuid": "bin/uuid"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/opencollective-postinstall": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+      "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
+      "license": "MIT",
+      "bin": {
+        "opencollective-postinstall": "index.js"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+      "license": "MIT"
+    },
+    "node_modules/piexifjs": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/piexifjs/-/piexifjs-1.0.6.tgz",
+      "integrity": "sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag==",
+      "license": "MIT"
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.10.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+      "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+      "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.9",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+      "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
+      "license": "MIT"
+    },
+    "node_modules/resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "license": "MIT"
+    },
+    "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"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/saslprep": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
+      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "sparse-bitfield": "^3.0.3"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/send": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+      "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/serve-static": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+      "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.18.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz",
+      "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==",
+      "license": "MIT",
+      "dependencies": {
+        "ip": "^1.1.5",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "memory-pager": "^1.0.2"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/structured-clone": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/structured-clone/-/structured-clone-0.2.2.tgz",
+      "integrity": "sha1-rJK2vjGVimQ9sw8TNavGobAt/cI=",
+      "license": "MIT"
+    },
+    "node_modules/tesseract.js": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-2.1.5.tgz",
+      "integrity": "sha512-7CIS3SWr7TXpeaH9+HS7iUtVbCfPFYOO3p6rkRAkdtsOtrbz6496x59na6SCbFAIaZulQxy8BjwSu3qL3AoDRg==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "blueimp-load-image": "^3.0.0",
+        "bmp-js": "^0.1.0",
+        "file-type": "^12.4.1",
+        "idb-keyval": "^3.2.0",
+        "is-electron": "^2.2.0",
+        "is-url": "^1.2.4",
+        "jpeg-autorotate": "^7.1.1",
+        "node-fetch": "^2.6.0",
+        "opencollective-postinstall": "^2.0.2",
+        "regenerator-runtime": "^0.13.3",
+        "resolve-url": "^0.2.1",
+        "tesseract.js-core": "^2.2.0",
+        "zlibjs": "^0.3.1"
+      }
+    },
+    "node_modules/tesseract.js-core": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz",
+      "integrity": "sha512-a8L+OJTbUipBsEDsJhDPlnLB0TY1MkTZqw5dqUwmiDSjUzwvU7HWLg/2+WDRulKUi4LE+7PnHlaBlW0k+V0U0w==",
+      "license": "Apache License 2.0"
+    },
+    "node_modules/timers-ext": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz",
+      "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==",
+      "license": "ISC",
+      "dependencies": {
+        "es5-ext": "~0.10.46",
+        "next-tick": "1"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+      "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+      "license": "MIT",
+      "dependencies": {
+        "punycode": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/ts-mixer": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz",
+      "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg=="
+    },
+    "node_modules/tslib": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+    },
+    "node_modules/type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+      "license": "ISC"
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+      "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=4.2.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/unscan": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/unscan/-/unscan-1.1.2.tgz",
+      "integrity": "sha512-a5RcGaBFMO9l78QWKffeWUo2cvfqUv05JCXuphE8MFOA92qyqp1Da7isnR+zjJspi45+yS8tTSuhd0vV3asWdA==",
+      "license": "MIT",
+      "dependencies": {
+        "commander": "^8.3.0",
+        "form-data": "^4.0.0",
+        "node-fetch": "^2.6.5"
+      },
+      "bin": {
+        "unscan": "bin/unscan.js"
+      }
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/whatwg-url": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+      "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "^3.0.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/whatwg-url/node_modules/webidl-conversions": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "license": "MIT"
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "8.8.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz",
+      "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/zlibjs": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
+      "integrity": "sha1-UBl+2yihxCymWcyLTmqd3W1ERVQ=",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/zod": {
+      "version": "3.13.4",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.13.4.tgz",
+      "integrity": "sha512-LZRucWt4j/ru5azOkJxCfpR87IyFDn8h2UODdqvXzZLb3K7bb9chUrUIGTy3BPsr8XnbQYfQ5Md5Hu2OYIo1mg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    }
+  },
+  "dependencies": {
+    "@discordjs/builders": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.12.0.tgz",
+      "integrity": "sha512-Vx2MjUZd6QVo1uS2uWt708Fd6cHWGFblAvbpL5EBO+kLl0BADmPwwvts+YJ/VfSywed6Vsk6K2cEooR/Ytjhjw==",
+      "requires": {
+        "@sindresorhus/is": "^4.3.0",
+        "discord-api-types": "^0.26.1",
+        "ts-mixer": "^6.0.0",
+        "tslib": "^2.3.1",
+        "zod": "^3.11.6"
+      }
+    },
+    "@discordjs/collection": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz",
+      "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA=="
+    },
+    "@sapphire/async-queue": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.1.tgz",
+      "integrity": "sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g=="
+    },
+    "@sapphire/shapeshift": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.2.0.tgz",
+      "integrity": "sha512-asNgE5Ooil2/oGIAj6vZMoUc2ZFED0TGYD7jwvZsjHPQZBEh9ITj94ca4bCgiCR1s2ER/UjzykH+5wE3ebVZnQ=="
+    },
+    "@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
+    },
+    "@types/node": {
+      "version": "17.0.21",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
+      "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ=="
+    },
+    "@types/node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==",
+      "requires": {
+        "@types/node": "*",
+        "form-data": "^3.0.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+          "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
+    },
+    "@types/webidl-conversions": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz",
+      "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q=="
+    },
+    "@types/whatwg-url": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz",
+      "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==",
+      "requires": {
+        "@types/node": "*",
+        "@types/webidl-conversions": "*"
+      }
+    },
+    "@types/ws": {
+      "version": "8.5.3",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+      "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@ungap/structured-clone": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.0.1.tgz",
+      "integrity": "sha512-zKVyTt6rELvPXYwcVPTJcPFtY0AckN5A7xWuc7owBqR0FdtuDYhE9MZZUi6IY1kZUQFSXV1B3UOOIyLkVHYd2w=="
+    },
+    "accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "requires": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      }
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+    },
+    "blueimp-load-image": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-3.0.0.tgz",
+      "integrity": "sha512-Q9rFbd4ZUNvzSFmRXx9MoG0RwWwJeMjjEUbG7WIOJgUg22Jgkow0wL5b35B6qwiBscxACW9OHdrP5s2vQ3x8DQ=="
+    },
+    "bmp-js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+      "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM="
+    },
+    "body-parser": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
+      "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
+      "requires": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.10.3",
+        "raw-body": "2.5.1",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "bson": {
+      "version": "4.6.4",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.4.tgz",
+      "integrity": "sha512-TdQ3FzguAu5HKPPlr0kYQCyrYUYh8tFM+CMTpxjNzVzxeiJY00Rtuj3LXLHSgiGvmaWlZ8PE+4KyM2thqE38pQ==",
+      "requires": {
+        "buffer": "^5.6.0"
+      }
+    },
+    "buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "requires": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "cli-color": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.1.tgz",
+      "integrity": "sha512-eBbxZF6fqPUNnf7CLAFOersUnyYzv83tHFLSlts+OAHsNendaqv2tHCq+/MO+b3Y+9JeoUlIvobyxG/Z8GNeOg==",
+      "requires": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.53",
+        "es6-iterator": "^2.0.3",
+        "memoizee": "^0.4.15",
+        "timers-ext": "^0.1.7"
+      }
+    },
+    "colors": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+      "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "requires": {
+        "safe-buffer": "5.2.1"
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "cookie": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+    },
+    "denque": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
+      "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
+    },
+    "depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+    },
+    "destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
+    },
+    "difflib": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz",
+      "integrity": "sha1-teMDYabbAjF21WKJLbhZQKcY9H4=",
+      "requires": {
+        "heap": ">= 0.2.0"
+      }
+    },
+    "discord-api-types": {
+      "version": "0.26.1",
+      "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.26.1.tgz",
+      "integrity": "sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ=="
+    },
+    "discord.js": {
+      "version": "13.8.0",
+      "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.8.0.tgz",
+      "integrity": "sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==",
+      "requires": {
+        "@discordjs/builders": "^0.14.0",
+        "@discordjs/collection": "^0.7.0",
+        "@sapphire/async-queue": "^1.3.1",
+        "@types/node-fetch": "^2.6.1",
+        "@types/ws": "^8.5.3",
+        "discord-api-types": "^0.33.3",
+        "form-data": "^4.0.0",
+        "node-fetch": "^2.6.1",
+        "ws": "^8.7.0"
+      },
+      "dependencies": {
+        "@discordjs/builders": {
+          "version": "0.14.0",
+          "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.14.0.tgz",
+          "integrity": "sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==",
+          "requires": {
+            "@sapphire/shapeshift": "^3.1.0",
+            "@sindresorhus/is": "^4.6.0",
+            "discord-api-types": "^0.33.3",
+            "fast-deep-equal": "^3.1.3",
+            "ts-mixer": "^6.0.1",
+            "tslib": "^2.4.0"
+          }
+        },
+        "discord-api-types": {
+          "version": "0.33.5",
+          "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz",
+          "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg=="
+        }
+      }
+    },
+    "dreamopt": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/dreamopt/-/dreamopt-0.8.0.tgz",
+      "integrity": "sha1-W8yAvnCX5F/EicNCQFq2gUCowdk=",
+      "requires": {
+        "wordwrap": ">=0.0.2"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "es5-ext": {
+      "version": "0.10.57",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.57.tgz",
+      "integrity": "sha512-L7cCNoPwTkAp7IBHxrKLsh7NKiVFkcdxlP9vbVw9QUvb7gF0Mz9bEBN0WY9xqdTjGF907EMT/iG013vnbqwu1Q==",
+      "requires": {
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "next-tick": "^1.1.0"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "es6-weak-map": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+      "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.46",
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
+    "express": {
+      "version": "4.18.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
+      "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
+      "requires": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.0",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.5.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.2.0",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.10.3",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.18.0",
+        "serve-static": "1.15.0",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      }
+    },
+    "ext": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
+      "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
+      "requires": {
+        "type": "^2.5.0"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
+          "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ=="
+        }
+      }
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+    },
+    "file-type": {
+      "version": "12.4.2",
+      "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz",
+      "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg=="
+    },
+    "finalhandler": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+      "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
+      }
+    },
+    "form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "glob": {
+      "version": "5.0.15",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+      "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+      "requires": {
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "2 || 3",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+    },
+    "heap": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
+      "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="
+    },
+    "http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "requires": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      }
+    },
+    "humanize": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/humanize/-/humanize-0.0.9.tgz",
+      "integrity": "sha1-GZT/rs3+nEQe0r2sdFK3u0yeQaQ="
+    },
+    "humanize-duration": {
+      "version": "3.27.1",
+      "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz",
+      "integrity": "sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA=="
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "idb-keyval": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-3.2.0.tgz",
+      "integrity": "sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ=="
+    },
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ip": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
+    },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+    },
+    "is-electron": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.1.tgz",
+      "integrity": "sha512-r8EEQQsqT+Gn0aXFx7lTFygYQhILLCB+wn0WCDL5LZRINeLH/Rvw1j2oKodELLXYNImQ3CRlVsY8wW4cGOsyuw=="
+    },
+    "is-promise": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
+      "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="
+    },
+    "is-url": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+      "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
+    },
+    "jpeg-autorotate": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/jpeg-autorotate/-/jpeg-autorotate-7.1.1.tgz",
+      "integrity": "sha512-ewTZTG/QWOM0D5h/yKcQ3QgyrnQYsr3qmcS+bqoAwgQAY1KBa31aJ+q+FlElaxo/rSYqfF1ixf+8EIgluBkgTg==",
+      "requires": {
+        "colors": "^1.4.0",
+        "glob": "^7.1.6",
+        "jpeg-js": "^0.4.2",
+        "piexifjs": "^1.0.6",
+        "yargs-parser": "^20.2.1"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+          "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        }
+      }
+    },
+    "jpeg-js": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.3.tgz",
+      "integrity": "sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q=="
+    },
+    "jshaiku": {
+      "version": "file:../haiku",
+      "requires": {
+        "@babel/core": "^7.16.7",
+        "@babel/preset-env": "^7.16.8",
+        "@babel/preset-typescript": "^7.16.7",
+        "@discordjs/builders": "^0.11.0",
+        "@discordjs/rest": "^0.2.0-canary.0",
+        "@types/jest": "^27.4.0",
+        "@types/node-cron": "^3.0.1",
+        "ansi-styles": "^6.1.0",
+        "babel-jest": "^27.4.6",
+        "chalk": "^5.0.0",
+        "discord-api-types": "^0.26.1",
+        "discord.js": "^13.8.0",
+        "jest": "^27.4.7",
+        "node-cron": "^3.0.0",
+        "ts-node": "^10.4.0",
+        "typescript": "4.5.4"
+      }
+    },
+    "json-diff": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.7.3.tgz",
+      "integrity": "sha512-VBvNBt3cIrCBHa3gYbVsCFUEReqWZPf+Biq1ZtFdIiQ6rytRLDp3qvtrGv7z/iZDd1D4vXWpW7Nx1nP8muLzkg==",
+      "requires": {
+        "cli-color": "^2.0.0",
+        "difflib": "~0.2.1",
+        "dreamopt": "~0.8.0"
+      }
+    },
+    "lru-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
+      "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=",
+      "requires": {
+        "es5-ext": "~0.10.2"
+      }
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "memoizee": {
+      "version": "0.4.15",
+      "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz",
+      "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==",
+      "requires": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.53",
+        "es6-weak-map": "^2.0.3",
+        "event-emitter": "^0.3.5",
+        "is-promise": "^2.2.2",
+        "lru-queue": "^0.1.0",
+        "next-tick": "^1.1.0",
+        "timers-ext": "^0.1.7"
+      }
+    },
+    "memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "optional": true
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+    },
+    "mime-db": {
+      "version": "1.51.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+      "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
+    },
+    "mime-types": {
+      "version": "2.1.34",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+      "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+      "requires": {
+        "mime-db": "1.51.0"
+      }
+    },
+    "minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "mongodb": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.7.0.tgz",
+      "integrity": "sha512-HhVar6hsUeMAVlIbwQwWtV36iyjKd9qdhY+s4wcU8K6TOj4Q331iiMy+FoPuxEntDIijTYWivwFJkLv8q/ZgvA==",
+      "requires": {
+        "bson": "^4.6.3",
+        "denque": "^2.0.1",
+        "mongodb-connection-string-url": "^2.5.2",
+        "saslprep": "^1.0.3",
+        "socks": "^2.6.2"
+      }
+    },
+    "mongodb-connection-string-url": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.2.tgz",
+      "integrity": "sha512-tWDyIG8cQlI5k3skB6ywaEA5F9f5OntrKKsT/Lteub2zgwSUlhqEN2inGgBTm8bpYJf8QYBdA/5naz65XDpczA==",
+      "requires": {
+        "@types/whatwg-url": "^8.2.1",
+        "whatwg-url": "^11.0.0"
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+    },
+    "next-tick": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
+    },
+    "node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "requires": {
+        "whatwg-url": "^5.0.0"
+      },
+      "dependencies": {
+        "tr46": {
+          "version": "0.0.3",
+          "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+          "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+        },
+        "whatwg-url": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+          "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+          "requires": {
+            "tr46": "~0.0.3",
+            "webidl-conversions": "^3.0.0"
+          }
+        }
+      }
+    },
+    "node-tesseract": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/node-tesseract/-/node-tesseract-0.2.7.tgz",
+      "integrity": "sha1-yPAvuDUaQnByc1d4wFGYI/JgG4Q=",
+      "requires": {
+        "glob": "^5.0.10",
+        "node-uuid": "^1.4.1"
+      }
+    },
+    "node-uuid": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+      "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
+    },
+    "object-inspect": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+    },
+    "on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "opencollective-postinstall": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+      "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q=="
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "piexifjs": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/piexifjs/-/piexifjs-1.0.6.tgz",
+      "integrity": "sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag=="
+    },
+    "proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "requires": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+    },
+    "qs": {
+      "version": "6.10.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+      "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+      "requires": {
+        "side-channel": "^1.0.4"
+      }
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "raw-body": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+      "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+      "requires": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      }
+    },
+    "regenerator-runtime": {
+      "version": "0.13.9",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+      "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
+    },
+    "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=="
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "saslprep": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
+      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
+      "optional": true,
+      "requires": {
+        "sparse-bitfield": "^3.0.3"
+      }
+    },
+    "send": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+      "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+      "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.18.0"
+      }
+    },
+    "setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+    },
+    "side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "requires": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      }
+    },
+    "smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
+    },
+    "socks": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz",
+      "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==",
+      "requires": {
+        "ip": "^1.1.5",
+        "smart-buffer": "^4.2.0"
+      }
+    },
+    "sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+      "optional": true,
+      "requires": {
+        "memory-pager": "^1.0.2"
+      }
+    },
+    "statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
+    },
+    "structured-clone": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/structured-clone/-/structured-clone-0.2.2.tgz",
+      "integrity": "sha1-rJK2vjGVimQ9sw8TNavGobAt/cI="
+    },
+    "tesseract.js": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-2.1.5.tgz",
+      "integrity": "sha512-7CIS3SWr7TXpeaH9+HS7iUtVbCfPFYOO3p6rkRAkdtsOtrbz6496x59na6SCbFAIaZulQxy8BjwSu3qL3AoDRg==",
+      "requires": {
+        "blueimp-load-image": "^3.0.0",
+        "bmp-js": "^0.1.0",
+        "file-type": "^12.4.1",
+        "idb-keyval": "^3.2.0",
+        "is-electron": "^2.2.0",
+        "is-url": "^1.2.4",
+        "jpeg-autorotate": "^7.1.1",
+        "node-fetch": "^2.6.0",
+        "opencollective-postinstall": "^2.0.2",
+        "regenerator-runtime": "^0.13.3",
+        "resolve-url": "^0.2.1",
+        "tesseract.js-core": "^2.2.0",
+        "zlibjs": "^0.3.1"
+      }
+    },
+    "tesseract.js-core": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz",
+      "integrity": "sha512-a8L+OJTbUipBsEDsJhDPlnLB0TY1MkTZqw5dqUwmiDSjUzwvU7HWLg/2+WDRulKUi4LE+7PnHlaBlW0k+V0U0w=="
+    },
+    "timers-ext": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz",
+      "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==",
+      "requires": {
+        "es5-ext": "~0.10.46",
+        "next-tick": "1"
+      }
+    },
+    "toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
+    },
+    "tr46": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+      "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+      "requires": {
+        "punycode": "^2.1.1"
+      }
+    },
+    "ts-mixer": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz",
+      "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg=="
+    },
+    "tslib": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+    },
+    "type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typescript": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+      "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg=="
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "unscan": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/unscan/-/unscan-1.1.2.tgz",
+      "integrity": "sha512-a5RcGaBFMO9l78QWKffeWUo2cvfqUv05JCXuphE8MFOA92qyqp1Da7isnR+zjJspi45+yS8tTSuhd0vV3asWdA==",
+      "requires": {
+        "commander": "^8.3.0",
+        "form-data": "^4.0.0",
+        "node-fetch": "^2.6.5"
+      }
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+    },
+    "whatwg-url": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+      "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+      "requires": {
+        "tr46": "^3.0.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "dependencies": {
+        "webidl-conversions": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+          "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+        }
+      }
+    },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "ws": {
+      "version": "8.8.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz",
+      "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==",
+      "requires": {}
+    },
+    "yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+    },
+    "zlibjs": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
+      "integrity": "sha1-UBl+2yihxCymWcyLTmqd3W1ERVQ="
+    },
+    "zod": {
+      "version": "3.13.4",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.13.4.tgz",
+      "integrity": "sha512-LZRucWt4j/ru5azOkJxCfpR87IyFDn8h2UODdqvXzZLb3K7bb9chUrUIGTy3BPsr8XnbQYfQ5Md5Hu2OYIo1mg=="
+    }
+  }
+}
diff --git a/package.json b/package.json
index 9706070..39257d9 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,9 @@
 {
   "dependencies": {
     "@discordjs/builders": "^0.12.0",
+    "@ungap/structured-clone": "^1.0.1",
     "body-parser": "^1.20.0",
-    "discord.js": "^13.6.0",
+    "discord.js": "^13.8.0",
     "express": "^4.18.1",
     "humanize": "^0.0.9",
     "humanize-duration": "^3.27.1",
diff --git a/src/api/index.ts b/src/api/index.ts
index 570ca5b..07131ff 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -2,6 +2,8 @@
 import express from 'express';
 import bodyParser from 'body-parser';
 import generateEmojiEmbed from "../utils/generateEmojiEmbed.js";
+import structuredClone from '@ungap/structured-clone';
+
 
 const jsonParser = bodyParser.json();
 const app = express();
@@ -9,7 +11,7 @@
 
 const runServer = (client: HaikuClient) => {
     app.get('/', (req, res) => {
-        res.send(client.ws.ping);
+        res.status(200).send(client.ws.ping);
     });
 
     app.post('/verify/:code', jsonParser, async function (req, res) {
@@ -32,9 +34,9 @@
                     .setEmoji("MEMBER.JOIN")
                 ], components: []});
             }
-            res.status(200).send();
+            res.sendStatus(200);
         } else {
-            res.status(403).send();
+            res.sendStatus(403);
         }
     });
 
@@ -51,17 +53,17 @@
                 ]});
             }
         } catch {}
-        res.status(200).send();
+        res.sendStatus(200);
     })
 
     app.get('/verify/:code', jsonParser, function (req, res) {
         const code = req.params.code;
         if (client.verify[code]) {
-            // let data = structuredClone(client.verify[code])
-            // delete data.interaction;
-            // return res.status(200).send(data);
+            let data = structuredClone(client.verify[code])
+            delete data.interaction;
+            return res.status(200).send(data);
         }
-        return res.status(404).send();
+        return res.sendStatus(404);
     })
 
     app.listen(port);
diff --git a/src/automations/createModActionTicket.ts b/src/automations/createModActionTicket.ts
index ef317b7..a9a5a27 100644
--- a/src/automations/createModActionTicket.ts
+++ b/src/automations/createModActionTicket.ts
@@ -1,10 +1,10 @@
 import Discord, { MessageActionRow, MessageButton } from 'discord.js';
-import readConfig from '../utils/readConfig.js'
 import generateEmojiEmbed from '../utils/generateEmojiEmbed.js';
 import getEmojiByName from "../utils/getEmojiByName.js";
+import client from "../utils/client.js";
 
-export async function create(guild: Discord.Guild, member: Discord.User, createdBy: Discord.User, client) {
-    let config = await readConfig(guild.id);
+export async function create(guild: Discord.Guild, member: Discord.User, createdBy: Discord.User, reason: string) {
+    let config = await client.database.read(guild.id);
     // @ts-ignore
     const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger
     let overwrites = [{
@@ -12,6 +12,11 @@
         allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
         type: "member"
     }] as Discord.OverwriteResolvable[];
+    overwrites.push({
+        id: guild.roles.everyone,
+        deny: ["VIEW_CHANNEL"],
+        type: "role"
+    })
     if (config.tickets.supportRole != null) {
         overwrites.push({
             id: guild.roles.cache.get(config.tickets.supportRole),
@@ -47,7 +52,7 @@
             .setTitle("New Ticket")
             .setDescription(
                 `Ticket created by a Moderator\n` +
-                `**Support type:** Appeal submission\n` +
+                `**Support type:** Appeal submission\n` + (reason != null ? `**Reason:**\n> ${reason}\n` : "") +
                 `**Ticket ID:** \`${c.id}\`\n` +
                 `Type \`/ticket close\` to archive this ticket.`,
             )
@@ -84,6 +89,6 @@
 }
 
 export async function areTicketsEnabled(guild: string) {
-    let config = await readConfig(guild);
+    let config = await client.database.read(guild);
     return config.tickets.enabled;
 }
\ No newline at end of file
diff --git a/src/automations/guide.ts b/src/automations/guide.ts
index 6a9b0b5..d443a42 100644
--- a/src/automations/guide.ts
+++ b/src/automations/guide.ts
@@ -96,7 +96,20 @@
                 )
                 .setEmoji("NUCLEUS.LOGO")
                 .setStatus("Danger")
-            ).setTitle("Tickets").setDescription("Ticket system").setPageId(5)
+            ).setTitle("Tickets").setDescription("Ticket system").setPageId(5),
+        new Embed()
+            .setEmbed(new generateEmojiEmbed()
+                .setTitle("Tags")
+                .setDescription(
+                    "Add a tag system to your server with the `/tag` and `/tags` commands.\n" +
+                    "To create a tag, type `/tags create <tag name> <tag content>`.\n" +
+                    "Tag names and content can be edited with `/tags edit`.\n" +
+                    "To delete a tag, type `/tags delete <tag name>`.\n" +
+                    "To view all tags, type `/tags list`.\n"
+                )
+                .setEmoji("NUCLEUS.LOGO")
+                .setStatus("Danger")
+            ).setTitle("Tags").setDescription("Tag system").setPageId(6)
     ]
     let m;
     if (interaction) {
@@ -144,30 +157,25 @@
                     .setPlaceholder("Choose a page...")
             ])]
         }
+        let components = selectPane.concat([new MessageActionRow().addComponents([
+            new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(page === 0),
+            new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(false),
+            new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(page === pages.length - 1),
+            new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER")
+        ])])
         if (interaction) {
             let em = new Discord.MessageEmbed(pages[page].embed)
             em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
             await interaction.editReply({
                 embeds: [em],
-                components: selectPane.concat([new MessageActionRow().addComponents([
-                    new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(page === 0),
-                    new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(false),
-                    new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(page === pages.length - 1),
-                    new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER")
-                ])]),
-                fetchReply: true
+                components: components
             });
         } else {
             let em = new Discord.MessageEmbed(pages[page].embed)
             em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
             await m.edit({
                 embeds: [em],
-                components: selectPane.concat([new MessageActionRow().addComponents([
-                    new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(page === 0),
-                    new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(false),
-                    new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(page === pages.length - 1),
-                    new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER")
-                ])]),
+                components: components,
                 fetchReply: true
             });
         }
diff --git a/src/automations/roleMenu.ts b/src/automations/roleMenu.ts
index c7073c5..cac15a5 100644
--- a/src/automations/roleMenu.ts
+++ b/src/automations/roleMenu.ts
@@ -1,18 +1,18 @@
 import { Message, MessageButton } from "discord.js";
-import readConfig from '../utils/readConfig.js'
 import generateEmojiEmbed from '../utils/generateEmojiEmbed.js'
 import { MessageActionRow, MessageSelectMenu } from 'discord.js';
 import getEmojiByName from "../utils/getEmojiByName.js";
+import client from "../utils/client.js";
 
 export async function callback(interaction) {
-    let config = await readConfig(interaction.guild.id);
-    if (!config.roleMenu.enabled) await interaction.reply({embeds: [new generateEmojiEmbed()
+    let config = await client.database.read(interaction.guild.id);
+    if (!config.roleMenu.enabled) return await interaction.reply({embeds: [new generateEmojiEmbed()
         .setTitle("Roles")
         .setDescription("Self roles are currently disabled. Please contact a staff member or try again later.")
         .setStatus("Danger")
         .setEmoji("CONTROL.BLOCKCROSS")
     ], ephemeral: true})
-    if (config.roleMenu.options.length === 0) await interaction.reply({embeds: [new generateEmojiEmbed()
+    if (config.roleMenu.options.length === 0) return await interaction.reply({embeds: [new generateEmojiEmbed()
         .setTitle("Roles")
         .setDescription("There are no roles available. Please contact a staff member or try again later.")
         .setStatus("Danger")
@@ -136,7 +136,7 @@
             .setDescription("Something went wrong and your roles were not added. Please contact a staff member or try again later.")
             .setStatus("Danger")
             .setEmoji("GUILD.RED")
-        ]})
+        ], components: []})
     }
     await interaction.editReply({embeds: [new generateEmojiEmbed()
         .setTitle("Roles")
diff --git a/src/automations/statsChannelAdd.ts b/src/automations/statsChannelAdd.ts
index 42ec580..01dfef1 100644
--- a/src/automations/statsChannelAdd.ts
+++ b/src/automations/statsChannelAdd.ts
@@ -1,19 +1,28 @@
-import log from '../utils/log.js'
-import readConfig from '../utils/readConfig.js'
 import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
+import singleNotify from '../utils/singleNotify.js';
+import client from '../utils/client.js';
 
 export async function callback(_, member) {
-    let config = await readConfig(member.guild.id);
+    let config = await client.database.read(member.guild.id);
 
     config.stats.forEach(async element => {
         if (element.enabled) {
             let string = element.text
             if (!string) return
             string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
-
-            let channel = await member.client.channels.fetch(element.channel)
+            let channel;
+            try {
+                channel = await member.client.channels.fetch(element.channel)
+            } catch (error) { channel = null }
+            if (!channel) {
+                return singleNotify(
+                    "statsChannelDeleted",
+                    member.guild.id,
+                    "One or more of your stats channels have been deleted. Please open the settings menu to change this.",
+                    "Critical"
+                )
+            }
             if (channel.guild.id !== member.guild.id) return
-            if (!channel) return // TODO: Notify mods
             try {
                 await channel.edit({ name: string })
             } catch (err) {
@@ -21,4 +30,4 @@
             }
         }
     });
-}
\ No newline at end of file
+}
diff --git a/src/automations/statsChannelRemove.ts b/src/automations/statsChannelRemove.ts
index 4b24768..fee0d2d 100644
--- a/src/automations/statsChannelRemove.ts
+++ b/src/automations/statsChannelRemove.ts
@@ -1,10 +1,9 @@
-import log from '../utils/log.js'
-import readConfig from '../utils/readConfig.js'
+import client from '../utils/client.js';
 import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
 import singleNotify from '../utils/singleNotify.js';
 
-export async function callback(interaction, member) {
-    let config = await readConfig(member.guild.id);
+export async function callback(_, member) {
+    let config = await client.database.read(member.guild.id);
 
     config.stats.forEach(async element => {
         if (element.enabled) {
@@ -14,10 +13,10 @@
 
             let channel = await member.client.channels.fetch(element.channel)
             if (channel.guild.id !== member.guild.id) return
-            if (!channel) return await singleNotify(interaction.client,
+            if (!channel) return singleNotify(
                 "statsChannelDeleted",
                 member.guild.id,
-                "The stats channel has been deleted. Please set a new channel to use this feature.",
+                "One or more of your stats channels have been deleted. Please open the settings menu to change this.",
                 "Critical"
             )
             try {
diff --git a/src/automations/tickets/create.ts b/src/automations/tickets/create.ts
index fd0ae57..9642089 100644
--- a/src/automations/tickets/create.ts
+++ b/src/automations/tickets/create.ts
@@ -1,6 +1,6 @@
 import Discord, { MessageActionRow, MessageButton } from "discord.js";
 import { tickets, toHexArray } from "../../utils/calculate.js";
-import readConfig from "../../utils/readConfig.js";
+import client from "../../utils/client.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../../utils/getEmojiByName.js";
 
@@ -13,7 +13,7 @@
     // @ts-ignore
     const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = interaction.client.logger
 
-    let config = await readConfig(interaction.guild.id);
+    let config = await client.database.read(interaction.guild.id);
     if (!config.tickets.enabled || !config.tickets.category) {
         return await interaction.reply({embeds: [new generateEmojiEmbed()
             .setTitle("Tickets are disabled")
@@ -63,8 +63,8 @@
                     .setEmoji(getEmojiByName(("TICKETS." + type.toString().toUpperCase()), "id"));
             }
         });
-        for (let i = 0; i < formattedTicketTypes.length; i += 4) {
-            splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 4)));
+        for (let i = 0; i < formattedTicketTypes.length; i += 5) {
+            splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 5)));
         }
         let m = await interaction.reply({embeds: [new generateEmojiEmbed()
             .setTitle("Create Ticket")
@@ -97,8 +97,8 @@
                     .setDisabled(true)
             }
         });
-        for (let i = 0; i < formattedTicketTypes.length; i += 4) {
-            splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 4)));
+        for (let i = 0; i < formattedTicketTypes.length; i += 5) {
+            splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 5)));
         }
         component.update({embeds: [new generateEmojiEmbed()
             .setTitle("Create Ticket")
diff --git a/src/automations/tickets/delete.ts b/src/automations/tickets/delete.ts
index 17d889a..1d577a4 100644
--- a/src/automations/tickets/delete.ts
+++ b/src/automations/tickets/delete.ts
@@ -1,18 +1,20 @@
 import Discord, { MessageButton, MessageActionRow } from "discord.js";
+import client from "../../utils/client.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../../utils/getEmojiByName.js";
-import readConfig from "../../utils/readConfig.js";
 
 export default async function (interaction) {
     // @ts-ignore
     const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = interaction.client.logger
 
-    let config = await readConfig(interaction.guild.id);
+    let config = await client.database.read(interaction.guild.id);
+    let thread = false; let threadChannel
+    if (interaction.channel instanceof Discord.ThreadChannel) thread = true; threadChannel = interaction.channel as Discord.ThreadChannel
     let channel = (interaction.channel as Discord.TextChannel)
-    if (!channel.parent || config.tickets.category != channel.parent.id) {
+    if (!channel.parent || config.tickets.category != channel.parent.id || (thread ? (threadChannel.parent.parent.id != config.tickets.category) : false)) {
         return interaction.reply({embeds: [new generateEmojiEmbed()
             .setTitle("Deleting Ticket...")
-            .setDescription("This ticket is not in your tickets category, so cannot be deleted. You cannot run close in a thread.") // TODO bridge to cross later!
+            .setDescription("This ticket is not in your tickets category, so cannot be deleted. You cannot run close in a thread.")
             .setStatus("Danger")
             .setEmoji("CONTROL.BLOCKCROSS")
         ], ephemeral: true});
@@ -110,7 +112,7 @@
 }
 
 async function purgeByUser(member, guild) {
-    let config = await readConfig(guild.id);
+    let config = await client.database.read(guild.id);
     if (!config.tickets.category) return;
     let tickets = guild.channels.cache.get(config.tickets.category);
     if (!tickets) return;
diff --git a/src/automations/verify.ts b/src/automations/verify.ts
index 3d4e658..bf26505 100644
--- a/src/automations/verify.ts
+++ b/src/automations/verify.ts
@@ -1,9 +1,9 @@
-import Discord, { CommandInteraction, GuildMember } from "discord.js";
+import Discord, { GuildMember } from "discord.js";
 import generateEmojiEmbed from "../utils/generateEmojiEmbed.js";
-import readConfig from "../utils/readConfig.js";
 import fetch from "node-fetch";
 import { TestString, NSFWCheck } from "../automations/unscan.js";
 import createPageIndicator from "../utils/createPageIndicator.js";
+import client from "../utils/client.js";
 
 function step(i) {
     return "\n\n" + createPageIndicator(5, i);
@@ -18,7 +18,13 @@
         .setStatus("Danger")
         .setEmoji("NUCLEUS.LOADING")
     ], ephemeral: true, fetchReply: true});
-    let config = await readConfig(interaction.guild.id);
+    let config = await client.database.read(interaction.guild.id);
+    if ((!config.verify.enabled ) || (!config.verify.role)) return interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Verify")
+        .setDescription(`Verify is not enabled on this server`)
+        .setStatus("Danger")
+        .setEmoji("CONTROL.BLOCKCROSS")
+    ], ephemeral: true, fetchReply: true});
     if ((interaction.member as GuildMember).roles.cache.has(config.verify.role)) {
         return await interaction.editReply({embeds: [new generateEmojiEmbed()
             .setTitle("Verify")
@@ -34,7 +40,7 @@
         .setEmoji("NUCLEUS.LOADING")
     ]});
     try {
-        let status = await fetch(`https://clicks.codes/`).then(res => res.status);
+        let status = await fetch(client.config.baseUrl).then(res => res.status);
         if (status != 200) {
             return await interaction.editReply({embeds: [new generateEmojiEmbed()
                 .setTitle("Verify")
@@ -53,7 +59,7 @@
             new Discord.MessageButton()
                 .setLabel("Check webpage")
                 .setStyle("LINK")
-                .setURL("https://clicks.codes/"),
+                .setURL(client.config.baseUrl),
             new Discord.MessageButton()
                 .setLabel("Support")
                 .setStyle("LINK")
@@ -132,7 +138,6 @@
     ], components: [new Discord.MessageActionRow().addComponents([new Discord.MessageButton()
         .setLabel("Verify")
         .setStyle("LINK")
-        // .setURL(`https://clicks.codes/nucleus/verify?code=${code}`)
-        .setURL(`https://insulation-coin-hoping-nevertheless.trycloudflare.com/nucleus/verify?code=${code}`)
+        .setURL(`${client.config.baseUrl}/nucleus/verify?code=${code}`)
     ])]});
 }
diff --git a/src/automations/welcome.ts b/src/automations/welcome.ts
index 84a87ec..11c4844 100644
--- a/src/automations/welcome.ts
+++ b/src/automations/welcome.ts
@@ -1,10 +1,10 @@
 import log from '../utils/log.js'
-import readConfig from '../utils/readConfig.js'
 import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
+import client from '../utils/client.js';
 
 export async function callback(_, member) {
     if (member.bot) return
-    let config = await readConfig(member.guild.id);
+    let config = await client.database.read(member.guild.id);
     if (!config.welcome.enabled) return
 
     if (!config.welcome.verificationRequired.role) {
diff --git a/src/commands/categorisationTest.ts b/src/commands/categorisationTest.ts
index 2852a2b..33f99e9 100644
--- a/src/commands/categorisationTest.ts
+++ b/src/commands/categorisationTest.ts
@@ -60,7 +60,6 @@
             .setCustomId("category")
             .setMinValues(0)
             .setMaxValues(1)
-            // .setMaxValues(Object.keys(types).length)
             .setOptions(Object.keys(types).map(type => {return {label: toCapitals(type), value: type}}))
         ])]});
     }
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index 1e17e53..8b17db3 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -4,8 +4,8 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from '../../utils/readConfig.js';
 import addPlurals from "../../utils/plurals.js";
+import client from "../../utils/client.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -31,13 +31,11 @@
         + `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
         + `Are you sure you want to ban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         let dmd = false
         let dm;
-        let config = await readConfig(interaction.guild.id);
+        let config = await client.database.read(interaction.guild.id);
         try {
             if (interaction.options.getString("notify") != "no") {
                 dm = await (interaction.options.getMember("user") as GuildMember).send({
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index 9dc447b..20fbc01 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -5,7 +5,7 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from '../../utils/readConfig.js'
+import client from "../../utils/client.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -29,13 +29,11 @@
         + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
         + `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         let dmd = false
         let dm;
-        let config = await readConfig(interaction.guild.id);
+        let config = await client.database.read(interaction.guild.id);
         try {
             if (interaction.options.getString("notify") != "no") {
                 dm = await (interaction.options.getMember("user") as GuildMember).send({
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index b2cc94e..854f38f 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -6,7 +6,7 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
 import humanizeDuration from "humanize-duration";
-import readConfig from "../../utils/readConfig.js";
+import client from "../../utils/client.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -21,7 +21,7 @@
     .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are muted | Default yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]]))
 
-const callback = async (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     // @ts-ignore
     const { log, NucleusColors, renderUser, entry } = interaction.client.logger
     const user = interaction.options.getMember("user") as GuildMember
@@ -32,7 +32,7 @@
         minutes: interaction.options.getInteger("minutes") || 0,
         seconds: interaction.options.getInteger("seconds") || 0
     }
-    let config = await readConfig(interaction.guild.id)
+    let config = await client.database.read(interaction.guild.id)
     let serverSettingsDescription = (config.moderation.mute.timeout ? "given a timeout" : "")
     if (config.moderation.mute.role) serverSettingsDescription += (serverSettingsDescription ? " and " : "") + `given the <@&${config.moderation.mute.role}> role`
 
@@ -132,13 +132,11 @@
         + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
         + `Are you sure you want to mute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send(true)
     if (confirmation.success) {
         let dmd = false
         let dm;
-        let config = await readConfig(interaction.guild.id);
+        let config = await client.database.read(interaction.guild.id);
         try {
             if (interaction.options.getString("notify") != "no") {
                 dm = await (interaction.options.getMember("user") as GuildMember).send({
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index 6a0410a..96738c6 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -4,7 +4,6 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from '../../utils/readConfig.js';
 import { create, areTicketsEnabled } from "../../automations/createModActionTicket.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -31,10 +30,8 @@
         .setColor("Danger")
         .addCustomBoolean(
             "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
-            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.client),
+            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.options.getString("reason")),
             "An appeal ticket will be created when Confirm is clicked")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         let dmd = false
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index 924e507..8e6762e 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -19,7 +19,7 @@
     .addUserOption(option => option.setName("user").setDescription("The user to purge messages from").setRequired(false))
     .addStringOption(option => option.setName("reason").setDescription("The reason for the purge").setRequired(false))
 
-const callback = async (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     let user = interaction.options.getMember("user") as GuildMember ?? null
     let channel = (interaction.channel as GuildChannel)
     if (!(["GUILD_TEXT", "GUILD_NEWS", "GUILD_NEWS_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_PRIVATE_THREAD"].includes(channel.type.toString()))) {
@@ -205,8 +205,6 @@
                 "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
             }))
             .setColor("Danger")
-    //        pluralize("day", interaction.options.getInteger("amount"))
-    //        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
         .send()
         if (confirmation.success) {
             let messages;
diff --git a/src/commands/mod/slowmode.ts b/src/commands/mod/slowmode.ts
index b91f065..2498746 100644
--- a/src/commands/mod/slowmode.ts
+++ b/src/commands/mod/slowmode.ts
@@ -29,8 +29,6 @@
         })
         + `Are you sure you want to set the slowmode in this channel?`)
         .setColor("Danger")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         try {
@@ -39,7 +37,7 @@
             await interaction.editReply({embeds: [new generateEmojiEmbed()
                 .setEmoji("CHANNEL.SLOWMODE.RED")
                 .setTitle(`Slowmode`)
-                .setDescription("An error occurred while setting the slowmode")
+                .setDescription("Something went wrong while setting the slowmode")
                 .setStatus("Danger")
             ], components: []})
         }
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index 29d3bef..5a01287 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -4,8 +4,8 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from '../../utils/readConfig.js';
-import addPlurals from '../../utils/plurals.js';
+import client from "../../utils/client.js";
+import addPlural from "../../utils/plurals.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -28,15 +28,13 @@
             "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
         })
         + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
-        + `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
+        + `${addPlural(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
         + `Are you sure you want to softban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         let dmd = false;
-        let config = await readConfig(interaction.guild.id);
+        let config = await client.database.read(interaction.guild.id);
         try {
             if (interaction.options.getString("notify") != "no") {
                 await (interaction.options.getMember("user") as GuildMember).send({
diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts
index 8231752..f201142 100644
--- a/src/commands/mod/unban.ts
+++ b/src/commands/mod/unban.ts
@@ -11,7 +11,7 @@
     .setDescription("Unbans a user")
     .addStringOption(option => option.setName("user").setDescription("The user to unban (Username or ID)").setRequired(true))
 
-const callback = async (interaction: CommandInteraction) => { // TODO: User search
+const callback = async (interaction: CommandInteraction) => { // TODO: User search UI
     let bans = await interaction.guild.bans.fetch()
     let user = interaction.options.getString("user")
     let resolved = bans.find(ban => ban.user.id == user)
diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts
index b2f8234..2a98c54 100644
--- a/src/commands/mod/unmute.ts
+++ b/src/commands/mod/unmute.ts
@@ -27,8 +27,6 @@
         + `The user **will${interaction.options.getString("notify") === "yes" ? '' : ' not'}** be notified\n\n`
         + `Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         let dmd = false
diff --git a/src/commands/mod/unnamed.ts b/src/commands/mod/unnamed.ts
deleted file mode 100644
index ca0bcef..0000000
--- a/src/commands/mod/unnamed.ts
+++ /dev/null
@@ -1,233 +0,0 @@
-import Discord, { CommandInteraction, GuildMember, MessageActionRow } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
-import getEmojiByName from "../../utils/getEmojiByName.js";
-import confirmationMessage from "../../utils/confirmationMessage.js";
-import keyValueList from "../../utils/generateKeyValueList.js";
-import humanizeDuration from "humanize-duration";
-import { create, areTicketsEnabled } from "../../automations/createModActionTicket.js";
-import readConfig from '../../utils/readConfig.js'
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("unnamed")
-    .setDescription("Gives a user a role")
-    .addUserOption(option => option.setName("user").setDescription("The user to UNNAMED").setRequired(true)) // TODO
-    .addIntegerOption(option => option.setName("days").setDescription("The number of days to UNNAMED the user for | Default 0").setMinValue(0).setMaxValue(27).setRequired(false))
-    .addIntegerOption(option => option.setName("hours").setDescription("The number of hours to UNNAMED the user for | Default 0").setMinValue(0).setMaxValue(23).setRequired(false))
-    .addIntegerOption(option => option.setName("minutes").setDescription("The number of minutes to UNNAMED the user for | Default 0").setMinValue(0).setMaxValue(59).setRequired(false))
-    .addIntegerOption(option => option.setName("seconds").setDescription("The number of seconds to UNNAMED the user for | Default 0").setMinValue(0).setMaxValue(59).setRequired(false))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the UNNAMED").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are UNNAMED | Default yes").setRequired(false)
-        .addChoices([["Yes", "yes"], ["No", "no"]]))
-
-const callback = async (interaction: CommandInteraction) => {
-    // @ts-ignore
-    const { log, NucleusColors, renderUser, entry } = interaction.client.logger
-    let config = await readConfig(interaction.guild.id);
-    const user = interaction.options.getMember("user") as GuildMember
-    const reason = interaction.options.getString("reason")
-    const time = {
-        days: interaction.options.getInteger("days") || 0,
-        hours: interaction.options.getInteger("hours") || 0,
-        minutes: interaction.options.getInteger("minutes") || 0,
-        seconds: interaction.options.getInteger("seconds") || 0
-    }
-    let muteTime = (time.days * 24 * 60 * 60) + (time.hours * 60 * 60) + (time.minutes * 60) + time.seconds
-    if (muteTime == 0) {
-        let m = await interaction.reply({embeds: [
-            new generateEmojiEmbed()
-                .setEmoji("PUNISH.MUTE.GREEN") // TODO
-                .setTitle("UNNAMED")
-                .setDescription("How long should the user be UNNAMED")
-                .setStatus("Success")
-        ], components: [
-            new MessageActionRow().addComponents([
-                new Discord.MessageButton()
-                    .setCustomId("1m")
-                    .setLabel("1 Minute")
-                    .setStyle("SECONDARY"),
-                new Discord.MessageButton()
-                    .setCustomId("10m")
-                    .setLabel("10 Minutes")
-                    .setStyle("SECONDARY"),
-                new Discord.MessageButton()
-                    .setCustomId("30m")
-                    .setLabel("30 Minutes")
-                    .setStyle("SECONDARY"),
-                new Discord.MessageButton()
-                    .setCustomId("1h")
-                    .setLabel("1 Hour")
-                    .setStyle("SECONDARY")
-            ]),
-            new MessageActionRow().addComponents([
-                new Discord.MessageButton()
-                    .setCustomId("6h")
-                    .setLabel("6 Hours")
-                    .setStyle("SECONDARY"),
-                new Discord.MessageButton()
-                    .setCustomId("12h")
-                    .setLabel("12 Hours")
-                    .setStyle("SECONDARY"),
-                new Discord.MessageButton()
-                    .setCustomId("1d")
-                    .setLabel("1 Day")
-                    .setStyle("SECONDARY"),
-                new Discord.MessageButton()
-                    .setCustomId("1w")
-                    .setLabel("1 Week")
-                    .setStyle("SECONDARY")
-            ]),
-            new MessageActionRow().addComponents([
-                new Discord.MessageButton()
-                    .setCustomId("cancel")
-                    .setLabel("Cancel")
-                    .setStyle("DANGER")
-                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-            ])
-        ], ephemeral: true, fetchReply: true})
-        let component;
-        try {
-            component = await (m as Discord.Message).awaitMessageComponent({filter: (m) => m.user.id === interaction.user.id, time: 2.5 * 60 * 1000});
-        } catch { return }
-        component.deferUpdate();
-        if (component.customId == "cancel") return interaction.editReply({embeds: [new generateEmojiEmbed()
-            .setEmoji("PUNISH.MUTE.RED") // TODO
-            .setTitle("UNNAMED")
-            .setDescription("UNNAMED cancelled")
-            .setStatus("Danger")
-        ]})
-        switch (component.customId) {
-            case "1m": { muteTime = 60; break; }
-            case "10m": { muteTime = 60 * 10; break; }
-            case "30m": { muteTime = 60 * 30; break; }
-            case "1h": { muteTime = 60 * 60; break; }
-            case "6h": { muteTime = 60 * 60 * 6; break; }
-            case "12h": { muteTime = 60 * 60 * 12; break; }
-            case "1d": { muteTime = 60 * 60 * 24; break; }
-            case "1w": { muteTime = 60 * 60 * 24 * 7; break; }
-        }
-    } else {
-        await interaction.reply({embeds: [
-            new generateEmojiEmbed()
-                .setEmoji("PUNISH.MUTE.GREEN") // TODO
-                .setTitle("UNNAMED")
-                .setDescription("Loading...")
-                .setStatus("Success")
-        ], ephemeral: true, fetchReply: true})
-    }
-    // TODO:[Modals] Replace this with a modal
-    let confirmation = await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.MUTE.RED") // TODO
-        .setTitle("UNNAMED")
-        .setDescription(keyValueList({
-            "user": `<@!${user.id}> (${user.user.username})`,
-            "time": `${humanizeDuration(muteTime * 1000, {round: true})}`,
-            "reason": `\n> ${reason ? reason : "*No reason provided*"}`
-        })
-        + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
-        + `Are you sure you want to mute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`) // TODO
-        .setColor("Danger")
-        .addCustomBoolean(
-            "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
-            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.client),
-            "An appeal ticket will be created when Confirm is clicked")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
-    .send()
-    if (confirmation.success) {
-        let dmd = false
-        let dm;
-        try {
-            if (interaction.options.getString("notify") != "no") {
-                dm = await (interaction.options.getMember("user") as GuildMember).send({
-                    embeds: [new generateEmojiEmbed()
-                        .setEmoji("PUNISH.MUTE.RED") // TODO
-                        .setTitle("UNNAMED")
-                        .setDescription(`You have been muted in ${interaction.guild.name}` + // TODO
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : ".\n\n" +
-                                    `You will be unmuted at: <t:${Math.round((new Date).getTime() / 1000) + muteTime}:D> at <t:${Math.round((new Date).getTime() / 1000) + muteTime}:T> (<t:${Math.round((new Date).getTime() / 1000) + muteTime}:R>)`)) // TODO
-                        .setStatus("Danger")
-                    ]
-                })
-                dmd = true
-            }
-        } catch {}
-        try {
-            await ((interaction.options.getMember("user") as GuildMember).roles.add(interaction.guild.roles.cache.find((r) => r.id === config.moderation.role.role)))
-            // TODO: Store when to remove the role
-        } catch {
-            await interaction.editReply({embeds: [new generateEmojiEmbed()
-                .setEmoji("PUNISH.MUTE.RED")
-                .setTitle(`Mute`)
-                .setDescription("Something went wrong and the user was not UNNAMED")
-                .setStatus("Danger")
-            ], components: []})
-            if (dmd) await dm.delete()
-            return
-        }
-        let failed = (dmd == false && interaction.options.getString("notify") != "no")
-        await interaction.editReply({embeds: [new generateEmojiEmbed()
-            .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`) // TODO
-            .setTitle(`Mute`) // TODO
-            .setDescription("The member was muted" + (failed ? ", but could not be notified" : "")) // TODO
-            .setStatus(failed ? "Warning" : "Success")
-        ], components: []})
-        let data = {
-            meta:{
-                type: 'memberMute', // TODO
-                displayName: 'Member Muted', // TODO
-                calculateType: 'guildMemberPunish',
-                color: NucleusColors.yellow,
-                emoji: 'PUNISH.WARN.YELLOW', // TODO
-                timestamp: new Date().getTime()
-            },
-            list: {
-                user: entry((interaction.options.getMember("user") as GuildMember).user.id, renderUser((interaction.options.getMember("user") as GuildMember).user)),
-                mutedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)), // TODO
-                time: entry(muteTime, `${humanizeDuration(muteTime * 1000, {round: true})}`),
-                reason: (interaction.options.getString("reason") ? `\n> ${interaction.options.getString("reason")}` : "No reason provided")
-            },
-            hidden: {
-                guild: interaction.guild.id
-            }
-        }
-        log(data, interaction.client);
-    } else {
-        await interaction.editReply({embeds: [new generateEmojiEmbed()
-            .setEmoji("PUNISH.MUTE.GREEN") // TODO
-            .setTitle(`Mute`) // TODO
-            .setDescription("No changes were made")
-            .setStatus("Success")
-        ], components: []})
-    }
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    let member = (interaction.member as GuildMember)
-    let me = (interaction.guild.me as GuildMember)
-    let apply = (interaction.options.getMember("user") as GuildMember)
-    if (member == null || me == null || apply == null) throw "That member is not in the server"
-    let memberPos = member.roles ? member.roles.highest.position : 0
-    let mePos = me.roles ? me.roles.highest.position : 0
-    let applyPos = apply.roles ? apply.roles.highest.position : 0
-    // Check if Nucleus can UNNAMED the member
-    if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
-    // Check if Nucleus has permission to UNNAMED
-    if (! me.permissions.has("MANAGE_ROLES")) throw "I do not have the `manage_roles` permission";
-    // Do not allow the user to have admin or be the owner
-    if (apply.permissions.has("ADMINISTRATOR") || apply.id == interaction.guild.ownerId) throw "You cannot mute an admin or the owner"
-    // Do not allow muting Nucleus
-    if (member.id == me.id) throw "I cannot UNNAMED myself"
-    // Allow the owner to UNNAMED anyone
-    if (member.id == interaction.guild.ownerId) return true
-    // Check if the user has moderate_members permission
-    if (! member.permissions.has("MODERATE_MEMBERS")) throw "You do not have the `moderate_members` permission";
-    // Check if the user is below on the role list
-    if (! (memberPos > applyPos)) throw "You do not have a role higher than that member"
-    // Allow UNNAMED
-    return true
-}
-
-export { command, callback, check };
\ No newline at end of file
diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts
index 715777e..46e2871 100644
--- a/src/commands/mod/warn.ts
+++ b/src/commands/mod/warn.ts
@@ -16,7 +16,7 @@
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
 
-const callback = async (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     // @ts-ignore
     const { log, NucleusColors, renderUser, entry } = interaction.client.logger
     // TODO:[Modals] Replace this with a modal
@@ -32,10 +32,8 @@
         .setColor("Danger")
         .addCustomBoolean(
             "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
-            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.client),
+            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.options.getString("reason")),
             "An appeal ticket will be created when Confirm is clicked")
-//        pluralize("day", interaction.options.getInteger("delete"))
-//        const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
     .send()
     if (confirmation.success) {
         let dmd = false
diff --git a/src/commands/settings/automation.ts b/src/commands/settings/automation.ts
deleted file mode 100644
index 0053f76..0000000
--- a/src/commands/settings/automation.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("automation")
-    .setDescription("Shows all automation options")
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/automation]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/log/channel.ts b/src/commands/settings/log/channel.ts
deleted file mode 100644
index 5843438..0000000
--- a/src/commands/settings/log/channel.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("channel")
-    .setDescription("Sets the log channel")
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/log/channel]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/log/ignore.ts b/src/commands/settings/log/ignore.ts
deleted file mode 100644
index 1b4d245..0000000
--- a/src/commands/settings/log/ignore.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("ignore")
-    .setDescription("Sets which users, channels and roles should be ignored")
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/log/ignore]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/log/ignored.ts b/src/commands/settings/log/ignored.ts
deleted file mode 100644
index bf4a30c..0000000
--- a/src/commands/settings/log/ignored.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("ignored")
-    .setDescription("Gets the ignored users, channels and roles")
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/log/ignored]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/log/_meta.ts b/src/commands/settings/logs/_meta.ts
similarity index 100%
rename from src/commands/settings/log/_meta.ts
rename to src/commands/settings/logs/_meta.ts
diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts
new file mode 100644
index 0000000..18ed8d9
--- /dev/null
+++ b/src/commands/settings/logs/channel.ts
@@ -0,0 +1,129 @@
+import { ChannelType } from 'discord-api-types';
+import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
+import generateEmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../../utils/confirmationMessage.js";
+import getEmojiByName from "../../../utils/getEmojiByName.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import client from "../../../utils/client.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("channel")
+    .setDescription("Sets or shows the log channel")
+    .addChannelOption(option => option.setName("channel").setDescription("The channel to set the log channel to").addChannelTypes([
+        ChannelType.GuildNews, ChannelType.GuildText
+    ]))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let m;
+    m = await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Loading")
+        .setStatus("Danger")
+        .setEmoji("NUCLEUS.LOADING")
+    ], ephemeral: true, fetchReply: true});
+    if (interaction.options.getChannel("channel")) {
+        let channel
+        try {
+            channel = interaction.options.getChannel("channel")
+        } catch {
+            return await interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setEmoji("CHANNEL.TEXT.DELETE")
+                .setTitle("Log Channel")
+                .setDescription("The channel you provided is not a valid channel")
+                .setStatus("Danger")
+            ]})
+        }
+        channel = channel as Discord.TextChannel
+        if (channel.guild.id != interaction.guild.id) {
+            return interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setTitle("Log Channel")
+                .setDescription(`You must choose a channel in this server`)
+                .setStatus("Danger")
+                .setEmoji("CHANNEL.TEXT.DELETE")
+            ]});
+        }
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("CHANNEL.TEXT.EDIT")
+            .setTitle("Log Channel")
+            .setDescription(`Are you sure you want to set the log channel to <#${channel.id}>?`)
+            .setColor("Warning")
+            .setInverted(true)
+        .send(true)
+        if (confirmation.success) {
+            try {
+                await client.database.write(interaction.guild.id, {"logging.logs.channel": channel.id})
+            } catch (e) {
+                console.log(e)
+                return interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setTitle("Log Channel")
+                    .setDescription(`Something went wrong and the log channel could not be set`)
+                    .setStatus("Danger")
+                    .setEmoji("CHANNEL.TEXT.DELETE")
+                ], components: []});
+            }
+        } else {
+            return interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setTitle("Log Channel")
+                .setDescription(`No changes were made`)
+                .setStatus("Success")
+                .setEmoji("CHANNEL.TEXT.CREATE")
+            ], components: []});
+        }
+    }
+    let clicks = 0;
+    let data = await client.database.read(interaction.guild.id);
+    let channel = data.logging.logs.channel;
+    while (true) {
+        await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setTitle("Log channel")
+            .setDescription(channel ? `Your log channel is currently set to <#${channel}>` : "This server does not have a log channel")
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+        ], components: [new MessageActionRow().addComponents([new MessageButton()
+            .setCustomId("clear")
+            .setLabel(clicks ? "Click again to confirm" : "Reset channel")
+            .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
+            .setStyle("DANGER")
+            .setDisabled(!channel)
+        ])]});
+        let i;
+        try {
+            i = await m.awaitMessageComponent({time: 600000});
+        } catch(e) { break }
+        i.deferUpdate()
+        if (i.component.customId == "clear") {
+            clicks += 1;
+            if (clicks == 2) {
+                clicks = 0;
+                await client.database.write(interaction.guild.id, {}, ["logging.logs.channel"])
+                channel = undefined;
+            }
+        } else {
+            break
+        }
+    }
+    await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Log channel")
+        .setDescription(channel ? `Your log channel is currently set to <#${channel}>` : "This server does not have a log channel")
+        .setStatus("Success")
+        .setEmoji("CHANNEL.TEXT.CREATE")
+        .setFooter({text: "Message closed"})
+    ], components: [new MessageActionRow().addComponents([new MessageButton()
+        .setCustomId("clear")
+        .setLabel("Clear")
+        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+        .setStyle("SECONDARY")
+        .setDisabled(true)
+    ])]});
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
diff --git a/src/commands/settings/log/events.ts b/src/commands/settings/logs/events.ts
similarity index 100%
rename from src/commands/settings/log/events.ts
rename to src/commands/settings/logs/events.ts
diff --git a/src/commands/settings/logs/ignore.ts b/src/commands/settings/logs/ignore.ts
new file mode 100644
index 0000000..4b66307
--- /dev/null
+++ b/src/commands/settings/logs/ignore.ts
@@ -0,0 +1,119 @@
+import { ChannelType } from 'discord-api-types';
+import Discord, { CommandInteraction } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import generateEmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import { WrappedCheck } from "jshaiku";
+import confirmationMessage from '../../../utils/confirmationMessage.js';
+import keyValueList from '../../../utils/generateKeyValueList.js';
+import client from '../../../utils/client.js';
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("ignore")
+    .setDescription("Sets which users, channels and roles should be ignored")
+    .addStringOption(o => o.setName("action").setDescription("Add or remove from the list").addChoices([
+        ["Add", "add"], ["Remove", "remove"]
+    ]).setRequired(true))
+    .addChannelOption(o => o.setName("addchannel").setDescription("Add a channel that should be ignored").addChannelTypes([
+        ChannelType.GuildText, ChannelType.GuildVoice, ChannelType.GuildNews, ChannelType.GuildPublicThread, ChannelType.GuildPrivateThread, ChannelType.GuildNewsThread
+    ]))
+    .addUserOption(o => o.setName("adduser").setDescription("Add a user that should be ignored"))
+    .addRoleOption(o => o.setName("addrole").setDescription("Add a role that should be ignored"))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let channel = interaction.options.getChannel("addchannel")
+    let user = interaction.options.getUser("adduser")
+    let role = interaction.options.getRole("addrole")
+    await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Loading")
+        .setStatus("Danger")
+        .setEmoji("NUCLEUS.LOADING")
+    ], ephemeral: true, fetchReply: true});
+    if (channel || user || role) {
+        if (channel) {
+            try {
+                channel = interaction.guild.channels.cache.get(channel.id)
+            } catch {
+                return await interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setEmoji("CHANNEL.TEXT.DELETE")
+                    .setTitle("Logs > Ignore")
+                    .setDescription("The channel you provided is not a valid channel")
+                    .setStatus("Danger")
+                ]})
+            }
+            channel = channel as Discord.TextChannel
+            if (channel.guild.id != interaction.guild.id) {
+                return interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setTitle("Logs > Ignore")
+                    .setDescription(`You must choose a channel in this server`)
+                    .setStatus("Danger")
+                    .setEmoji("CHANNEL.TEXT.DELETE")
+                ]});
+            }
+        }
+        if (user) {
+            try {
+                user = interaction.guild.members.cache.get(user.id).user
+            } catch {
+                return await interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setEmoji("USER.DELETE")
+                    .setTitle("Logs > Ignore")
+                    .setDescription("The user you provided is not a valid user")
+                    .setStatus("Danger")
+                ]})
+            }
+            user = user as Discord.User
+        }
+        if (role) {
+            try {
+                role = interaction.guild.roles.cache.get(role.id)
+            } catch {
+                return await interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setEmoji("ROLE.DELETE")
+                    .setTitle("Logs > Ignore")
+                    .setDescription("The role you provided is not a valid role")
+                    .setStatus("Danger")
+                ]})
+            }
+            role = role as Discord.Role
+            if (role.guild.id != interaction.guild.id) {
+                return interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setTitle("Logs > Ignore")
+                    .setDescription(`You must choose a role in this server`)
+                    .setStatus("Danger")
+                    .setEmoji("ROLE.DELETE")
+                ]});
+            }
+        }
+        let changes = {}
+        if (channel) changes["channel"] = channel.id
+        if (user) changes["user"] = user.id
+        if (role) changes["role"] = role.id
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("NUCLEUS.COMMANDS.IGNORE")
+            .setTitle("Logs > Ignore")
+            .setDescription(keyValueList(changes)
+            + `Are you sure you want to **${interaction.options.getString("action") == "add" ? "add" : "remove"}** these to the ignore list?`)
+            .setColor("Warning")
+        .send(true)
+        if (confirmation.success) {
+            let data = client.database.read(interaction.guild.id)
+            if (channel) data.logging.logs.ignore.channels.concat([channel.id])
+            if (user) data.logging.logs.ignore.users.concat([user.id])
+            if (role) data.logging.logs.ignore.roles.concat([role.id])
+            if (interaction.options.getString("action") == "add") {
+                await client.database.append(interaction.guild.id, data)
+            }
+        }
+    }
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/settings/menu.ts b/src/commands/settings/menu.ts
deleted file mode 100644
index 9950fe4..0000000
--- a/src/commands/settings/menu.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { CommandInteraction, MessageEmbed, MessageSelectMenu } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-import readConfig from "../../utils/readConfig.js";
-import { toHexArray, toHexInteger, logs } from "../../utils/calculate.js"
-import { capitalize } from "../../utils/generateKeyValueList.js";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("menu")
-    .setDescription("Shows a full UI of all settings")
-
-const callback = async (interaction: CommandInteraction) => {
-    return
-    let config = await readConfig(interaction.guild.id);
-
-    let currentValues = toHexArray(config.logging.log.toLog);
-
-    let toLogDropdownOptions = []
-
-    for(let i of logs) {
-        if(currentValues.includes(i)) {
-            toLogDropdownOptions.push({
-                name: capitalize(i),
-                value: i,
-                emoji: "TICK"
-            })
-        } else {
-            toLogDropdownOptions.push({
-                label: capitalize(i),
-                value: i,
-                emoji: "CROSS"
-            })
-        }
-    }
-
-    let toLogDropdown = new MessageSelectMenu()
-        .setCustomId("log")
-        .setMaxValues(22)
-        .addOptions()
-
-    let embed = new MessageEmbed()
-
-    interaction.reply("This command is not yet finished [settings/all]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/mod/channel.ts b/src/commands/settings/mod/channel.ts
deleted file mode 100644
index 88c8396..0000000
--- a/src/commands/settings/mod/channel.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("channel")
-    .setDescription("Sets the channel for staff messages to go to")
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/mod/channel]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/mod/events.ts b/src/commands/settings/mod/events.ts
deleted file mode 100644
index be5de57..0000000
--- a/src/commands/settings/mod/events.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-    .setName("events")
-    .setDescription("Sets which events mods should be notified about")
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/mod/events]");
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/settings/mod/_meta.ts b/src/commands/settings/staff/_meta.ts
similarity index 100%
rename from src/commands/settings/mod/_meta.ts
rename to src/commands/settings/staff/_meta.ts
diff --git a/src/commands/settings/staff/channel.ts b/src/commands/settings/staff/channel.ts
new file mode 100644
index 0000000..69ace92
--- /dev/null
+++ b/src/commands/settings/staff/channel.ts
@@ -0,0 +1,131 @@
+import { ChannelType } from 'discord-api-types';
+import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
+import generateEmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../../utils/confirmationMessage.js";
+import getEmojiByName from "../../../utils/getEmojiByName.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import client from "../../../utils/client.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("channel")
+    .setDescription("Sets or shows the staff notifications channel")
+    .addChannelOption(option => option.setName("channel").setDescription("The channel to set the staff notifications channel to").addChannelTypes([
+        ChannelType.GuildNews, ChannelType.GuildText
+    ]))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let m;
+    m = await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Loading")
+        .setStatus("Danger")
+        .setEmoji("NUCLEUS.LOADING")
+    ], ephemeral: true, fetchReply: true});
+    if (interaction.options.getChannel("channel")) {
+        let channel
+        try {
+            channel = interaction.options.getChannel("channel")
+        } catch {
+            return await interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setEmoji("CHANNEL.TEXT.DELETE")
+                .setTitle("Staff Notifications Channel")
+                .setDescription("The channel you provided is not a valid channel")
+                .setStatus("Danger")
+            ]})
+        }
+        channel = channel as Discord.TextChannel
+        if (channel.guild.id != interaction.guild.id) {
+            return interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setTitle("Staff Notifications Channel")
+                .setDescription(`You must choose a channel in this server`)
+                .setStatus("Danger")
+                .setEmoji("CHANNEL.TEXT.DELETE")
+            ]});
+        }
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("CHANNEL.TEXT.EDIT")
+            .setTitle("Staff Notifications Channel")
+            .setDescription(
+                `This will be the channel all notifications, updates, user reports etc. will be sent to.\n\n` +
+                `Are you sure you want to set the staff notifications channel to <#${channel.id}>?`
+            )
+            .setColor("Warning")
+            .setInverted(true)
+        .send(true)
+        if (confirmation.success) {
+            try {
+                await client.database.write(interaction.guild.id, {"logging.staff.channel": channel.id})
+            } catch (e) {
+                return interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setTitle("Staff Notifications Channel")
+                    .setDescription(`Something went wrong and the staff notifications channel could not be set`)
+                    .setStatus("Danger")
+                    .setEmoji("CHANNEL.TEXT.DELETE")
+                ], components: []});
+            }
+        } else {
+            return interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setTitle("Staff Notifications Channel")
+                .setDescription(`No changes were made`)
+                .setStatus("Success")
+                .setEmoji("CHANNEL.TEXT.CREATE")
+            ], components: []});
+        }
+    }
+    let clicks = 0;
+    let data = await client.database.read(interaction.guild.id);
+    let channel = data.logging.staff.channel;
+    while (true) {
+        await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setTitle("Staff Notifications channel")
+            .setDescription(channel ? `Your staff notifications channel is currently set to <#${channel}>` : "This server does not have a staff notifications channel")
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+        ], components: [new MessageActionRow().addComponents([new MessageButton()
+            .setCustomId("clear")
+            .setLabel(clicks ? "Click again to confirm" : "Reset channel")
+            .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
+            .setStyle("DANGER")
+            .setDisabled(!channel)
+        ])]});
+        let i;
+        try {
+            i = await m.awaitMessageComponent({time: 600000});
+        } catch(e) { break }
+        i.deferUpdate()
+        if (i.component.customId == "clear") {
+            clicks += 1;
+            if (clicks == 2) {
+                clicks = 0;
+                await client.database.write(interaction.guild.id, {}, ["logging.staff.channel"])
+                channel = undefined;
+            }
+        } else {
+            break
+        }
+    }
+    await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Staff Notifications channel")
+        .setDescription(channel ? `Your staff notifications channel is currently set to <#${channel}>` : "This server does not have a staff notifications channel")
+        .setStatus("Success")
+        .setEmoji("CHANNEL.TEXT.CREATE")
+        .setFooter({text: "Message closed"})
+    ], components: [new MessageActionRow().addComponents([new MessageButton()
+        .setCustomId("clear")
+        .setLabel("Clear")
+        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+        .setStyle("SECONDARY")
+        .setDisabled(true)
+    ])]});
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts
index 64dc980..aac271e 100644
--- a/src/commands/settings/tickets.ts
+++ b/src/commands/settings/tickets.ts
@@ -1,24 +1,410 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import getEmojiByName from "../../utils/getEmojiByName.js";
+import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../utils/confirmationMessage.js";
+import Discord, { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu, TextInputComponent } from "discord.js";
+import { SelectMenuOption, SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
 import { ChannelType } from 'discord-api-types';
+import client from "../../utils/client.js";
+import { toHexInteger, toHexArray, tickets as ticketTypes } from "../../utils/calculate.js";
+import { capitalize } from '../../utils/generateKeyValueList.js';
+import { modalInteractionCollector } from "../../utils/dualCollector.js";
 
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
+const command = (builder: SlashCommandSubcommandBuilder) => builder
     .setName("tickets")
-    .setDescription("Shows settings for tickets")
-    .addStringOption(option => option.setName("enabled").setDescription("If users should be able to create tickets | Default yes").setRequired(false)
+    .setDescription("Shows settings for tickets | Use no arguments to manage custom types")
+    .addStringOption(option => option.setName("enabled").setDescription("If users should be able to create tickets").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]]))
     .addChannelOption(option => option.setName("category").setDescription("The category where tickets are created").addChannelType(ChannelType.GuildCategory).setRequired(false))
-    .addNumberOption(option => option.setName("maxtickets").setDescription("The maximum amount of tickets a user can create | Default 5").setRequired(false).setMinValue(1))
-    .addRoleOption(option => option.setName("supportping").setDescription("The role pinged when a ticket is created").setRequired(false))
+    .addNumberOption(option => option.setName("maxticketsperuser").setDescription("The maximum amount of tickets a user can create | Default 5").setRequired(false).setMinValue(1))
+    .addRoleOption(option => option.setName("supportrole").setDescription("This role will have view access to all tickets and will be pinged when a ticket is created").setRequired(false))
 
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/tickets]");
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let m;
+    m = await interaction.reply({
+        embeds: [new generateEmojiEmbed()
+            .setTitle("Loading")
+            .setStatus("Danger")
+            .setEmoji("NUCLEUS.LOADING")
+        ], ephemeral: true, fetchReply: true
+    });
+    let options = {
+        enabled: interaction.options.getString("enabled") as string | boolean,
+        category: interaction.options.getChannel("category"),
+        maxtickets: interaction.options.getNumber("maxticketsperuser"),
+        supportping: interaction.options.getRole("supportrole")
+    }
+    if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
+        options.enabled = options.enabled === "yes" ? true : false;
+        if (options.category) {
+            let channel
+            try {
+                channel = interaction.guild.channels.cache.get(options.category.id)
+            } catch {
+                return await interaction.editReply({
+                    embeds: [new generateEmojiEmbed()
+                        .setEmoji("CHANNEL.TEXT.DELETE")
+                        .setTitle("Tickets > Category")
+                        .setDescription("The channel you provided is not a valid category")
+                        .setStatus("Danger")
+                    ]
+                })
+            }
+            channel = channel as Discord.CategoryChannel
+            if (channel.guild.id != interaction.guild.id) return interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets > Category")
+                    .setDescription(`You must choose a category in this server`)
+                    .setStatus("Danger")
+                    .setEmoji("CHANNEL.TEXT.DELETE")
+                ]
+            });
+        }
+        if (options.maxtickets) {
+            if (options.maxtickets < 1) return interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets > Max Tickets")
+                    .setDescription(`You must choose a number greater than 0`)
+                    .setStatus("Danger")
+                    .setEmoji("CHANNEL.TEXT.DELETE")
+                ]
+            });
+        }
+        let role
+        if (options.supportping) {
+            try {
+                role = interaction.guild.roles.cache.get(options.supportping.id)
+            } catch {
+                return await interaction.editReply({
+                    embeds: [new generateEmojiEmbed()
+                        .setEmoji("GUILD.ROLE.DELETE")
+                        .setTitle("Tickets > Support Ping")
+                        .setDescription("The role you provided is not a valid role")
+                        .setStatus("Danger")
+                    ]
+                })
+            }
+            role = role as Discord.Role
+            if (role.guild.id != interaction.guild.id) return interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets > Support Ping")
+                    .setDescription(`You must choose a role in this server`)
+                    .setStatus("Danger")
+                    .setEmoji("GUILD.ROLE.DELETE")
+                ]
+            });
+        }
+
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("GUILD.TICKET.ARCHIVED")
+            .setTitle("Tickets")
+            .setDescription(
+                (options.category ? `**Category:** ${options.category.name}\n` : "") +
+                (options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
+                (options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
+                (options.enabled !== null ? `**Enabled:** ${options.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
+                    }\n` : "") +
+                `\nAre you sure you want to apply these settings?`
+            )
+            .setColor("Warning")
+            .setInverted(true)
+            .send(true)
+        if (confirmation.success) {
+            let toUpdate = {}
+            if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled
+            if (options.category) toUpdate["tickets.category"] = options.category.id
+            if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets
+            if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id
+            try {
+                await client.database.write(interaction.guild.id, toUpdate)
+            } catch (e) {
+                return interaction.editReply({
+                    embeds: [new generateEmojiEmbed()
+                        .setTitle("Tickets")
+                        .setDescription(`Something went wrong and the staff notifications channel could not be set`)
+                        .setStatus("Danger")
+                        .setEmoji("GUILD.TICKET.DELETE")
+                    ], components: []
+                });
+            }
+        } else {
+            return interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets")
+                    .setDescription(`No changes were made`)
+                    .setStatus("Success")
+                    .setEmoji("GUILD.TICKET.OPEN")
+                ], components: []
+            });
+        }
+    }
+    let data = await client.database.read(interaction.guild.id);
+    data.tickets.customTypes = data.tickets.customTypes.filter((v, i, a) => a.indexOf(v) === i)
+    let lastClicked = "";
+    let embed;
+    data = {
+        enabled: data.tickets.enabled,
+        category: data.tickets.category,
+        maxTickets: data.tickets.maxTickets,
+        supportRole: data.tickets.supportRole,
+        useCustom: data.tickets.useCustom,
+        types: data.tickets.types,
+        customTypes: data.tickets.customTypes
+    }
+    while (true) {
+        embed = new generateEmojiEmbed()
+            .setTitle("Tickets")
+            .setDescription(
+                `${data.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${data.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`}\n` +
+                `${data.category ? "" : getEmojiByName("TICKETS.REPORT")} **Category:** ${data.category ? `<#${data.category}>` : "*None set*"}\n` +
+                `**Max Tickets:** ${data.maxTickets ? data.maxTickets : "*No limit*"}\n` +
+                `**Support Ping:** ${data.supportRole ? `<@&${data.supportRole}>` : "*None set*"}\n\n` +
+                ((data.useCustom && data.customTypes === null) ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
+                `${data.useCustom ? "Custom" : "Default"} types in use` + "\n\n" +
+                `${getEmojiByName("TICKETS.REPORT")} *Indicates a setting stopping tickets from being used*`
+            )
+            .setStatus("Success")
+            .setEmoji("GUILD.TICKET.OPEN")
+        m = await interaction.editReply({
+            embeds: [embed], components: [new MessageActionRow().addComponents([
+                new MessageButton()
+                    .setLabel("Tickets " + (data.enabled ? "enabled" : "disabled"))
+                    .setEmoji(getEmojiByName("CONTROL." + (data.enabled ? "TICK" : "CROSS"), "id"))
+                    .setStyle(data.enabled ? "SUCCESS" : "DANGER")
+                    .setCustomId("enabled"),
+                new MessageButton()
+                    .setLabel(lastClicked == "cat" ? "Click again to confirm" : "Clear category")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setStyle("DANGER")
+                    .setCustomId("clearCategory")
+                    .setDisabled(data.category == null),
+                new MessageButton()
+                    .setLabel(lastClicked == "max" ? "Click again to confirm" : "Reset max tickets")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setStyle("DANGER")
+                    .setCustomId("clearMaxTickets")
+                    .setDisabled(data.maxTickets == 5),
+                new MessageButton()
+                    .setLabel(lastClicked == "sup" ? "Click again to confirm" : "Clear support ping")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setStyle("DANGER")
+                    .setCustomId("clearSupportPing")
+                    .setDisabled(data.supportRole == null),
+            ]), new MessageActionRow().addComponents([
+                new MessageButton()
+                    .setLabel("Manage types")
+                    .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
+                    .setStyle("SECONDARY")
+                    .setCustomId("manageTypes"),
+            ])]
+        });
+        let i;
+        try {
+            i = await m.awaitMessageComponent({ time: 600000 });
+        } catch (e) { break }
+        i.deferUpdate()
+        if (i.component.customId == "clearCategory") {
+            if (lastClicked == "cat") {
+                lastClicked = "";
+                await client.database.write(interaction.guild.id, {}, ["tickets.category"])
+                data.category = undefined;
+            } else lastClicked = "cat";
+        } else if (i.component.customId == "clearMaxTickets") {
+            if (lastClicked == "max") {
+                lastClicked = "";
+                await client.database.write(interaction.guild.id, {}, ["tickets.maxTickets"])
+                data.maxTickets = 5;
+            } else lastClicked = "max";
+        } else if (i.component.customId == "clearSupportPing") {
+            if (lastClicked == "sup") {
+                lastClicked = "";
+                await client.database.write(interaction.guild.id, {}, ["tickets.supportRole"])
+                data.supportRole = undefined;
+            } else lastClicked = "sup";
+        } else if (i.component.customId == "enabled") {
+            await client.database.write(interaction.guild.id, { "tickets.enabled": !data.enabled })
+            data.enabled = !data.enabled;
+        } else if (i.component.customId == "manageTypes") {
+            data = await manageTypes(interaction, data, m);
+        } else {
+            break
+        }
+    }
+    await interaction.editReply({ embeds: [embed.setFooter({ text: "Message closed" })], components: [] });
 }
 
+async function manageTypes(interaction, data, m) {
+    while (true) {
+        if (data.useCustom) {
+            let customTypes = data.customTypes;
+            interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets > Types")
+                    .setDescription(
+                        "**Custom types enabled**\n\n" +
+                        "**Types in use:**\n" + ((customTypes !== null) ?
+                            (customTypes.map((t) => `> ${t}`).join("\n")) :
+                            "*None set*"
+                        ) + "\n\n" + (customTypes === null ?
+                            `${getEmojiByName("TICKETS.REPORT")} Having no types will disable tickets. Please add at least 1 type or use default types` : ""
+                        )
+                    )
+                    .setStatus("Success")
+                    .setEmoji("GUILD.TICKET.OPEN")
+                ], components: (customTypes ? [
+                    new MessageActionRow().addComponents([new Discord.MessageSelectMenu()
+                        .setCustomId("removeTypes")
+                        .setPlaceholder("Select types to remove")
+                        .setMaxValues(customTypes.length)
+                        .setMinValues(1)
+                        .addOptions(customTypes.map((t) => new SelectMenuOption().setLabel(t).setValue(t)))
+                    ])
+                ] : []).concat([
+                    new MessageActionRow().addComponents([
+                        new MessageButton()
+                            .setLabel("Back")
+                            .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+                            .setStyle("PRIMARY")
+                            .setCustomId("back"),
+                        new MessageButton()
+                            .setLabel("Add new type")
+                            .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
+                            .setStyle("PRIMARY")
+                            .setCustomId("addType")
+                            .setDisabled(customTypes !== null && customTypes.length >= 25),
+                        new MessageButton()
+                            .setLabel("Switch to default types")
+                            .setStyle("SECONDARY")
+                            .setCustomId("switchToDefault"),
+                    ])
+                ])
+            });
+        } else {
+            let inUse = toHexArray(data.types, ticketTypes)
+            let options = [];
+            ticketTypes.forEach(type => {
+                options.push(new SelectMenuOption({
+                    label: capitalize(type),
+                    value: type,
+                    emoji: interaction.client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
+                    default: inUse.includes(type)
+                }))
+            })
+            let selectPane = new MessageActionRow().addComponents([
+                new Discord.MessageSelectMenu()
+                    .addOptions(options)
+                    .setCustomId("types")
+                    .setMaxValues(ticketTypes.length)
+                    .setMinValues(1)
+                    .setPlaceholder("Select types to use")
+            ])
+            interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets > Types")
+                    .setDescription(
+                        "**Default types enabled**\n\n" +
+                        "**Types in use:**\n" +
+                        (inUse.map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`).join("\n"))
+                    )
+                    .setStatus("Success")
+                    .setEmoji("GUILD.TICKET.OPEN")
+                ], components: [
+                    selectPane,
+                    new MessageActionRow().addComponents([
+                        new MessageButton()
+                            .setLabel("Back")
+                            .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+                            .setStyle("PRIMARY")
+                            .setCustomId("back"),
+                        new MessageButton()
+                            .setLabel("Switch to custom types")
+                            .setStyle("SECONDARY")
+                            .setCustomId("switchToCustom"),
+                    ])
+                ]
+            });
+        }
+        let i;
+        try {
+            i = await m.awaitMessageComponent({ time: 600000 });
+        } catch (e) { break }
+        if (i.component.customId == "types") {
+            i.deferUpdate()
+            let types = toHexInteger(i.values, ticketTypes);
+            await client.database.write(interaction.guild.id, { "tickets.types": types })
+            data.types = types;
+        } else if (i.component.customId == "removeTypes") {
+            i.deferUpdate()
+            let types = i.values
+            let customTypes = data.customTypes;
+            if (customTypes) {
+                customTypes = customTypes.filter((t) => !types.includes(t));
+                customTypes = customTypes.length > 0 ? customTypes : null;
+                await client.database.write(interaction.guild.id, { "tickets.customTypes": customTypes })
+                data.customTypes = customTypes;
+            }
+        } else if (i.component.customId == "addType") {
+            await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter a name for the new type").addComponents(
+                // @ts-ignore
+                new MessageActionRow().addComponents(new TextInputComponent()
+                    .setCustomId("type")
+                    .setLabel("Name")
+                    .setMaxLength(100)
+                    .setMinLength(1)
+                    .setPlaceholder("E.g. \"Server Idea\"")
+                    .setRequired(true)
+                    .setStyle("SHORT")
+                )
+            ))
+            await interaction.editReply({
+                embeds: [new generateEmojiEmbed()
+                    .setTitle("Tickets > Types")
+                    .setDescription("Modal opened. If you can't see it, click back and try again.")
+                    .setStatus("Success")
+                    .setEmoji("GUILD.TICKET.OPEN")
+                ], components: [new MessageActionRow().addComponents([new MessageButton()
+                    .setLabel("Back")
+                    .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+                    .setStyle("PRIMARY")
+                    .setCustomId("back")
+                ])]
+            });
+            let out
+            try {
+                out = await modalInteractionCollector(m, (m) => m.channel.id == interaction.channel.id, (m) => m.customId == "addType")
+            } catch (e) { continue }
+            if (out.fields) {
+                let toAdd = out.fields.getTextInputValue("type");
+                if (!toAdd) { continue }
+                try {
+                    await client.database.append(interaction.guild.id, "tickets.customTypes", toAdd)
+                } catch { continue }
+                data.customTypes = data.customTypes || [];
+                if (!data.customTypes.includes(toAdd)) {
+                    data.customTypes.push(toAdd);
+                }
+            } else { continue }
+        } else if (i.component.customId == "switchToDefault") {
+            i.deferUpdate()
+            await client.database.write(interaction.guild.id, { "tickets.useCustom": false }, [])
+            data.useCustom = false;
+        } else if (i.component.customId == "switchToCustom") {
+            i.deferUpdate()
+            await client.database.write(interaction.guild.id, { "tickets.useCustom": true }, [])
+            data.useCustom = true;
+        } else {
+            i.deferUpdate()
+            break
+        }
+    }
+    return data
+}
+
+
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return interaction.memberPermissions.has("MANAGE_GUILD");
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command"
+    return true;
 }
 
 export { command };
diff --git a/src/commands/settings/verify/role.ts b/src/commands/settings/verify/role.ts
index c4de7af..dc1d4d9 100644
--- a/src/commands/settings/verify/role.ts
+++ b/src/commands/settings/verify/role.ts
@@ -1,20 +1,126 @@
-import { CommandInteraction } from "discord.js";
+import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
+import generateEmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../../utils/confirmationMessage.js";
+import getEmojiByName from "../../../utils/getEmojiByName.js";
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
+import client from "../../../utils/client.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
     .setName("role")
-    .setDescription("Sets the role given after verifying")
+    .setDescription("Sets or shows the role given to users after using /verify")
+    .addRoleOption(option => option.setName("role").setDescription("The role to give after verifying"))
 
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/verify/role]");
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let m;
+    m = await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Loading")
+        .setStatus("Danger")
+        .setEmoji("NUCLEUS.LOADING")
+    ], ephemeral: true, fetchReply: true});
+    if (interaction.options.getRole("role")) {
+        let role
+        try {
+            role = interaction.options.getRole("role")
+        } catch {
+            return await interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setEmoji("GUILD.ROLES.DELETE")
+                .setTitle("Verify Role")
+                .setDescription("The role you provided is not a valid role")
+                .setStatus("Danger")
+            ]})
+        }
+        role = role as Discord.Role
+        if (role.guild.id != interaction.guild.id) {
+            return interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setTitle("Verify Role")
+                .setDescription(`You must choose a role in this server`)
+                .setStatus("Danger")
+                .setEmoji("GUILD.ROLES.DELETE")
+            ]});
+        }
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("GUILD.ROLES.EDIT")
+            .setTitle("Verify Role")
+            .setDescription(`Are you sure you want to set the verify role to <@&${role.id}>?`)
+            .setColor("Warning")
+            .setInverted(true)
+        .send(true)
+        if (confirmation.success) {
+            try {
+                await client.database.write(interaction.guild.id, {"verify.role": role.id, "verify.enabled": true});
+            } catch (e) {
+                console.log(e)
+                return interaction.editReply({embeds: [new generateEmojiEmbed()
+                    .setTitle("Verify Role")
+                    .setDescription(`Something went wrong while setting the verify role`)
+                    .setStatus("Danger")
+                    .setEmoji("GUILD.ROLES.DELETE")
+                ], components: []});
+            }
+        } else {
+            return interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setTitle("Verify Role")
+                .setDescription(`No changes were made`)
+                .setStatus("Success")
+                .setEmoji("GUILD.ROLES.CREATE")
+            ], components: []});
+        }
+    }
+    let clicks = 0;
+    let data = await client.database.read(interaction.guild.id);
+    let role = data.verify.role;
+    while (true) {
+        await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setTitle("Verify Role")
+            .setDescription(role ? `Your verify role is currently set to <@&${role}>` : `You have not set a verify role`)
+            .setStatus("Success")
+            .setEmoji("GUILD.ROLES.CREATE")
+        ], components: [new MessageActionRow().addComponents([new MessageButton()
+            .setCustomId("clear")
+            .setLabel(clicks ? "Click again to confirm" : "Reset role")
+            .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
+            .setStyle("DANGER")
+            .setDisabled(!role)
+        ])]});
+        let i;
+        try {
+            i = await m.awaitMessageComponent({time: 600000});
+        } catch(e) { break }
+        i.deferUpdate()
+        if (i.component.customId == "clear") {
+            clicks += 1;
+            if (clicks == 2) {
+                clicks = 0;
+                await client.database.write(interaction.guild.id, {}, ["verify.role", "verify.enabled"])
+                role = undefined;
+            }
+        } else {
+            break
+        }
+    }
+    await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Verify Role")
+        .setDescription(role ? `Your verify role is currently set to <@&${role}}>` : `You have not set a verify role`)
+        .setStatus("Success")
+        .setEmoji("GUILD.ROLE.CREATE")
+        .setFooter({text: "Message closed"})
+    ], components: [new MessageActionRow().addComponents([new MessageButton()
+        .setCustomId("clear")
+        .setLabel("Clear")
+        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+        .setStyle("SECONDARY")
+        .setDisabled(true)
+    ])]});
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command"
     return true;
 }
 
 export { command };
 export { callback };
-export { check };
\ No newline at end of file
+export { check };
diff --git a/src/commands/tag.ts b/src/commands/tag.ts
index a5fcfc1..8d6d8c9 100644
--- a/src/commands/tag.ts
+++ b/src/commands/tag.ts
@@ -1,12 +1,14 @@
 import { CommandInteraction } from "discord.js";
 import { SlashCommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
+import { callback as statsChannelAdd } from '../automations/statsChannelAdd.js';
 
 const command = new SlashCommandBuilder()
     .setName("tag")
     .setDescription("Get and manage the servers tags")
 
 const callback = (interaction: CommandInteraction) => {
+    try { statsChannelAdd(interaction.client, interaction.member); } catch {} // TODO: REMOVE THIS FOR PRODUCTION
     interaction.reply("This command is not yet finished [tag]");
 }
 
diff --git a/src/commands/tags/_meta.ts b/src/commands/tags/_meta.ts
new file mode 100644
index 0000000..8c07682
--- /dev/null
+++ b/src/commands/tags/_meta.ts
@@ -0,0 +1,4 @@
+const name = "tags";
+const description = "manage server tags";
+
+export { name, description };
\ No newline at end of file
diff --git a/src/commands/tags/create.ts b/src/commands/tags/create.ts
new file mode 100644
index 0000000..e53f94f
--- /dev/null
+++ b/src/commands/tags/create.ts
@@ -0,0 +1,87 @@
+import Discord, { CommandInteraction } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../utils/confirmationMessage.js";
+import keyValueList from "../../utils/generateKeyValueList.js";
+import client from "../../utils/client.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("create")
+    .setDescription("Creates a tag")
+    .addStringOption(o => o.setName("name").setRequired(true).setDescription("The name of the tag"))
+    .addStringOption(o => o.setName("value").setRequired(true).setDescription("The value of the tag, shown after running /tag name"))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let name = interaction.options.getString("name");
+    let value = interaction.options.getString("value");
+    if (name.length > 100) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Create")
+        .setDescription("Tag names cannot be longer than 100 characters")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    if (value.length > 1000) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Create")
+        .setDescription("Tag values cannot be longer than 1000 characters")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    let data = await client.database.read(interaction.guild.id);
+    if (data.tags.length >= 100) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Create")
+        .setDescription("You cannot have more than 100 tags")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    if (data.tags[name]) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Create")
+        .setDescription("That tag already exists")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    let confirmation = await new confirmationMessage(interaction)
+    .setEmoji("PUNISH.NICKNAME.YELLOW")
+        .setTitle("Tag create")
+        .setDescription(keyValueList({
+            "name": `${name}`,
+            "value": `\n> ${value}`
+        })
+        + `\nAre you sure you want to create this tag?`)
+        .setColor("Warning")
+        .setInverted(true)
+    .send()
+    if (!confirmation) return await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Create")
+        .setDescription("No changes were made")
+        .setStatus("Success")
+        .setEmoji("PUNISH.NICKNAME.GREEN")
+    ]});
+    try {
+        await client.database.write(interaction.guild.id, {[`tags.${name}`]: value});
+    } catch (e) {
+        return await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setTitle("Tag Create")
+            .setDescription("Something went wrong and the tag was not created")
+            .setStatus("Danger")
+            .setEmoji("PUNISH.NICKNAME.RED")
+        ], components: []});
+    }
+    return await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Create")
+        .setDescription("Tag created")
+        .setStatus("Success")
+        .setEmoji("PUNISH.NICKNAME.GREEN")
+    ], components: []});
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the `manage_messages` permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/tags/delete.ts b/src/commands/tags/delete.ts
new file mode 100644
index 0000000..ff54ee5
--- /dev/null
+++ b/src/commands/tags/delete.ts
@@ -0,0 +1,69 @@
+import Discord, { CommandInteraction } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../utils/confirmationMessage.js";
+import keyValueList from "../../utils/generateKeyValueList.js";
+import client from "../../utils/client.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("delete")
+    .setDescription("Deletes a tag")
+    .addStringOption(o => o.setName("name").setRequired(true).setDescription("The name of the tag"))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let name = interaction.options.getString("name");
+    let data = await client.database.read(interaction.guild.id);
+    if (!data.tags[name]) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tags")
+        .setDescription("That tag does not exist")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    let confirmation = await new confirmationMessage(interaction)
+        .setEmoji("PUNISH.NICKNAME.YELLOW")
+        .setTitle("Tag Delete")
+        .setDescription(keyValueList({
+            "name": `${name}`,
+            "value": `\n> ${data.tags[name]}`
+        })
+        + `\nAre you sure you want to delete this tag?`)
+        .setColor("Warning")
+        .setInverted(true)
+    .send()
+    if (!confirmation) return await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Delete")
+        .setDescription("No changes were made")
+        .setStatus("Success")
+        .setEmoji("PUNISH.NICKNAME.GREEN")
+    ]});
+    try {
+        data = await client.database.read(interaction.guild.id);
+        delete data.tags[name];
+        await client.database.write(interaction.guild.id, {tags: data});
+    } catch (e) {
+        return await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setTitle("Tag Delete")
+            .setDescription("Something went wrong and the tag was not deleted")
+            .setStatus("Danger")
+            .setEmoji("PUNISH.NICKNAME.RED")
+        ], components: []});
+    }
+    return await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Delete")
+        .setDescription("Tag deleted")
+        .setStatus("Success")
+        .setEmoji("PUNISH.NICKNAME.GREEN")
+    ], components: []});
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the `manage_messages` permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/tags/edit.ts b/src/commands/tags/edit.ts
new file mode 100644
index 0000000..3cf73e5
--- /dev/null
+++ b/src/commands/tags/edit.ts
@@ -0,0 +1,102 @@
+import Discord, { CommandInteraction } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import confirmationMessage from "../../utils/confirmationMessage.js";
+import keyValueList from "../../utils/generateKeyValueList.js";
+import client from "../../utils/client.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("edit")
+    .setDescription("Edits or renames a tag")
+    .addStringOption(o => o.setName("name").setRequired(true).setDescription("The tag to edit"))
+    .addStringOption(o => o.setName("value").setRequired(false).setDescription("The new value of the tag / Rename"))
+    .addStringOption(o => o.setName("newname").setRequired(false).setDescription("The new name of the tag / Edit"))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    let name = interaction.options.getString("name");
+    let value = interaction.options.getString("value") || "";
+    let newname = interaction.options.getString("newname") || "";
+    if (!newname && !value) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Edit")
+        .setDescription("You must specify a value or a new name")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    if (newname.length > 100) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Edit")
+        .setDescription("Tag names cannot be longer than 100 characters")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    if (value.length > 2000) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Edit")
+        .setDescription("Tag values cannot be longer than 2000 characters")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    let data = await client.database.read(interaction.guild.id);
+    if (!data.tags[name]) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Edit")
+        .setDescription("That tag does not exist")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    if (newname && newname !== name && data.tags[newname]) return await interaction.reply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Edit")
+        .setDescription("A tag with that name already exists")
+        .setStatus("Danger")
+        .setEmoji("PUNISH.NICKNAME.RED")
+    ], ephemeral: true});
+    let confirmation = await new confirmationMessage(interaction)
+    .setEmoji("PUNISH.NICKNAME.YELLOW")
+        .setTitle("Tag Edit")
+        .setDescription(keyValueList({
+            "name": `${name}` + (newname ? ` -> ${newname}` : ""),
+            "value": `\n> ${value ? value : data.tags[name]}`
+        })
+        + `\nAre you sure you want to edit this tag?`)
+        .setColor("Warning")
+        .setInverted(true)
+    .send()
+    if (!confirmation) return await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tag Edit")
+        .setDescription("No changes were made")
+        .setStatus("Success")
+        .setEmoji("PUNISH.NICKNAME.GREEN")
+    ]});
+    try {
+        let toSet = {};
+        let toUnset = []
+        if (value) toSet[`tags.${name}`] = value;
+        if (newname) {
+            toUnset.push(`tags.${name}`);
+            toSet[`tags.${newname}`] = data.tags[name];
+        }
+        await client.database.write(interaction.guild.id, toSet, toUnset);
+    } catch (e) {
+        return await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setTitle("Tag Edit")
+            .setDescription("Something went wrong and the tag was not edited")
+            .setStatus("Danger")
+            .setEmoji("PUNISH.NICKNAME.RED")
+        ], components: []});
+    }
+    return await interaction.editReply({embeds: [new generateEmojiEmbed()
+        .setTitle("Tags")
+        .setDescription("Tag edited successfully")
+        .setStatus("Success")
+        .setEmoji("PUNISH.NICKNAME.GREEN")
+    ], components: []});
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the `manage_messages` permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/tags/list.ts b/src/commands/tags/list.ts
new file mode 100644
index 0000000..bf234d5
--- /dev/null
+++ b/src/commands/tags/list.ts
@@ -0,0 +1,150 @@
+import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import keyValueList from "../../utils/generateKeyValueList.js";
+import client from "../../utils/client.js";
+import { SelectMenuOption } from '@discordjs/builders';
+import getEmojiByName from "../../utils/getEmojiByName.js";
+import createPageIndicator from "../../utils/createPageIndicator.js";
+
+
+class Embed {
+    embed: Discord.MessageEmbed;
+    title: string;
+    description: string = "";
+    pageId: number = 0;
+    setEmbed(embed: Discord.MessageEmbed) { this.embed = embed; return this; }
+    setTitle(title: string) { this.title = title; return this; }
+    setDescription(description: string) { this.description = description; return this; }
+    setPageId(pageId: number) { this.pageId = pageId; return this; }
+}
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+        .setName("list")
+        .setDescription("Lists all tags in the server")
+
+const callback = async (interaction: CommandInteraction) => {
+    let data = await client.database.read(interaction.guild.id);
+    let tags = data.getKey("tags");
+    console.log(tags)
+    let strings = []
+    if (data === {}) strings = ["*No tags exist*"]
+    else {
+        let string = ""
+        for (let tag in tags) {
+            let proposed = `**${tag}:** ${tags[tag]}\n`
+            if (string.length + proposed.length > 2000) {
+                strings.push(string.slice(0, -1))
+                string = ""
+            }
+            console.log(string)
+            string += proposed
+        }
+        strings.push(string.slice(0, -1))
+    }
+
+    let pages = []
+    for (let string of strings) {
+        pages.push(new Embed()
+            .setEmbed(new generateEmojiEmbed()
+                .setTitle("Tags")
+                .setDescription(string)
+                .setEmoji("PUNISH.NICKNAME.GREEN")
+                .setStatus("Success")
+            ).setTitle(`Page ${pages.length + 1}`).setPageId(pages.length))
+    }
+    let m;
+    m = await interaction.reply({
+        embeds: [
+            new generateEmojiEmbed()
+                .setTitle("Welcome")
+                .setDescription(`One moment...`)
+                .setStatus("Danger")
+                .setEmoji("NUCLEUS.LOADING")
+        ], fetchReply: true, ephemeral: true
+    });
+    let page = 0;
+    let selectPaneOpen = false;
+    while (true) {
+        let selectPane = []
+
+        if (selectPaneOpen) {
+            let options = [];
+            pages.forEach(embed => {
+                options.push(new SelectMenuOption({
+                    label: embed.title,
+                    value: embed.pageId.toString(),
+                    description: embed.description || "",
+                }))
+            })
+            selectPane = [new MessageActionRow().addComponents([
+                new Discord.MessageSelectMenu()
+                    .addOptions(options)
+                    .setCustomId("page")
+                    .setMaxValues(1)
+                    .setPlaceholder("Choose a page...")
+            ])]
+        }
+        let em = new Discord.MessageEmbed(pages[page].embed)
+        em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
+        await interaction.editReply({
+            embeds: [em],
+            components: selectPane.concat([new MessageActionRow().addComponents([
+                new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(page === 0),
+                new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(false),
+                new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(page === pages.length - 1),
+                new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER")
+            ])])
+        });
+        let i
+        try {
+            i = await m.awaitMessageComponent({time: 600000 });
+        } catch (e) { break }
+        i.deferUpdate()
+        if (i.component.customId == "left") {
+            if (page > 0) page--;
+            selectPaneOpen = false;
+        } else if (i.component.customId == "right") {
+            if (page < pages.length - 1) page++;
+            selectPaneOpen = false;
+        } else if (i.component.customId == "select") {
+            selectPaneOpen = !selectPaneOpen;
+        } else if (i.component.customId == "page") {
+            page = parseInt(i.values[0]);
+            selectPaneOpen = false;
+        } else {
+            let em = new Discord.MessageEmbed(pages[page].embed)
+            em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message closed");
+            await interaction.editReply({
+                embeds: [em], components: [new MessageActionRow().addComponents([
+                    new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(true),
+                    new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(true),
+                    new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(true),
+                    new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER").setDisabled(true)
+                ])]
+            })
+            return;
+        }
+    }
+    let em = new Discord.MessageEmbed(pages[page])
+    em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message timed out");
+    await interaction.editReply({
+        embeds: [em],
+        components: [new MessageActionRow().addComponents([
+            new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(true),
+            new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle("SECONDARY").setDisabled(true),
+            new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(true),
+            new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER").setDisabled(true)
+        ])]
+    });
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/user/track.ts b/src/commands/user/track.ts
index be32f41..f96f718 100644
--- a/src/commands/user/track.ts
+++ b/src/commands/user/track.ts
@@ -3,9 +3,8 @@
 import { WrappedCheck } from "jshaiku";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../../utils/getEmojiByName.js";
-import generateKeyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from "../../utils/readConfig.js";
 import addPlural from "../../utils/plurals.js";
+import client from "../../utils/client.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -24,10 +23,10 @@
 
 const callback = async (interaction: CommandInteraction) => {
     // @ts-ignore
-    const { renderUser } = interaction.client.logger
+    const { renderUser } = interaction.client.logger;
     const member = interaction.options.getMember("user") as GuildMember;
     const guild = interaction.guild;
-    let config = await readConfig(guild.id);
+    let config = await client.database.read(guild.id);
     await interaction.reply({embeds: [new generateEmojiEmbed()
         .setEmoji("NUCLEUS.LOADING")
         .setTitle("Loading")
@@ -161,14 +160,19 @@
     }
 }
 
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+const check = async (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
     let member = (interaction.member as GuildMember)
     // Allow the owner to promote anyone
     if (member.id == interaction.guild.ownerId) return true
+    // Check if the user can manage any of the tracks
+    // @ts-ignore
+    let tracks = (await interaction.client.database.get(interaction.guild.id)).tracks
+    let managed = false
+    tracks.forEach(element => { if (element.track.manageableBy.some(role => member.roles.cache.has(role))) managed = true });
     // Check if the user has manage_roles permission
-    if (! member.permissions.has("MANAGE_ROLES")) throw "You do not have the `manage_roles` permission";
+    if (!managed && ! member.permissions.has("MANAGE_ROLES")) throw "You do not have the `manage_roles` permission";
     // Allow track
-    return true // TODO: allow if the member has manage perms
+    return true;
 }
 
 export { command };
diff --git a/src/config/default.json b/src/config/default.json
index 129bfe9..3b0882f 100644
--- a/src/config/default.json
+++ b/src/config/default.json
@@ -57,14 +57,18 @@
         "logs": {
             "enabled": true,
             "channel": null,
-            "toLog": "3fffff"
+            "toLog": "3fffff",
+            "ignore": {
+                "users": [],
+                "roles": [],
+                "channels": []
+            }
         },
         "staff": {
             "channel": null
         }
     },
     "verify": {
-        "enabled": false,
         "role": null
     },
     "tickets": {
@@ -72,6 +76,7 @@
         "category": null,
         "types": "3f",
         "customTypes": null,
+        "useCustom": false,
         "supportRole": null,
         "maxTickets": 5
     },
diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts
index 883efd9..ec788f7 100644
--- a/src/events/channelCreate.ts
+++ b/src/events/channelCreate.ts
@@ -1,5 +1,3 @@
-import { Interaction } from "discord.js";
-
 export const event = 'channelCreate'
 
 export async function callback(client, channel) {
diff --git a/src/index.ts b/src/index.ts
index 2616f40..ca14cdb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -18,5 +18,4 @@
 client.memory = new Memory()
 client.database = await new Database(config.mongoUrl).connect()
 
-
 await client.login();
\ No newline at end of file
diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts
index 26c231c..8a297b9 100644
--- a/src/utils/calculate.ts
+++ b/src/utils/calculate.ts
@@ -1,13 +1,13 @@
 const logs = [
     "channelUpdate",
-    "channelPinsUpdate",
+    "channelPinsUpdate", // TODO
     "emojiUpdate",
-    "stickerUpdate",
+    "stickerUpdate", // TODO
     "guildUpdate",
     "guildMemberUpdate",
     "guildMemberPunish",
-    "guildEventUpdate",
-    "guildEventMemberUpdate",
+    "guildEventUpdate", // TODO
+    "guildEventMemberUpdate", // TODO
     "guildRoleUpdate",
     "guildInviteUpdate",
     "messageUpdate",
@@ -16,11 +16,11 @@
     "messageReactionUpdate",
     "messagePing",
     "messageMassPing",
-    "messageAnnounce",
+    "messageAnnounce", // TODO
     "stageUpdate",
     "threadUpdate",
-    "voiceStateUpdate",
-    "webhookUpdate"
+    "voiceStateUpdate", // TODO
+    "webhookUpdate" // TODO
 ]
 
 const tickets = [
diff --git a/src/utils/database.ts b/src/utils/database.ts
index 53ccb97..4e37652 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -1,8 +1,10 @@
 import { Collection, Db, MongoClient } from 'mongodb';
+import structuredClone from '@ungap/structured-clone';
 
 
 export const Entry = data => {
     data = data ?? {};
+    data.getKey = key => data[key]
     return {
         get(target, prop, receiver) {
             let dataToReturn = data[prop]
@@ -38,11 +40,42 @@
 
     async read(guild: string) {
         let entry = await this.guilds.findOne({ id: guild });
-        return new Proxy(this.defaultData, Entry(entry)) as unknown as GuildConfig
+        return new Proxy(structuredClone(this.defaultData), Entry(entry)) as unknown as GuildConfig
     }
 
-    async write(guild: string, config: GuildConfig) {
-        await this.guilds.updateOne({ id: guild }, { $set: config }, { upsert: true });
+    async write(guild: string, set: object = {}, unset: string[] = []) {
+        let uo = {}
+        for (let key of unset) {
+            uo[key] = "";
+        }
+        await this.guilds.updateOne({ id: guild }, {
+            $unset: uo,
+            $set: set
+        }, { upsert: true });
+    }
+
+    async append(guild: string, key: string, value: any) {
+        if (Array.isArray(value)) {
+            await this.guilds.updateOne({ id: guild }, {
+                $addToSet: { [key]: { $each: value } }
+            }, { upsert: true });
+        } else {
+            await this.guilds.updateOne({ id: guild }, {
+                $addToSet: { [key]: value }
+            }, { upsert: true });
+        }
+    }
+
+    async remove(guild: string, key: string, value: any) {
+        if (Array.isArray(value)) {
+            await this.guilds.updateOne({ id: guild }, {
+                $pullAll: { [key]: value }
+            }, { upsert: true });
+        } else {
+            await this.guilds.updateOne({ id: guild }, {
+                $pullAll: { [key]: [value] }
+            }, { upsert: true });
+        }
     }
 }
 
@@ -94,64 +127,65 @@
         enabled: boolean,
         verificationRequired: {
             message: boolean,
-            role: string
+            role: string | null
         },
-        welcomeRole: string,
-        channel: string,
-        message: string
+        welcomeRole: string | null,
+        channel: string | null,
+        message: string | null,
     }
     stats: {
         enabled: boolean,
-        channel: string,
-        text: string
+        channel: string | null,
+        text: string | null,
     }[]
     logging: {
         logs: {
             enabled: boolean,
-            channel: string,
-            toLog: string
+            channel: string | null,
+            toLog: string | null,
         },
         staff: {
-            channel: string
+            channel: string | null,
         }
     }
     verify: {
         enabled: boolean,
-        role: string
+        role: string | null,
     }
     tickets: {
         enabled: boolean,
-        category: string,
-        types: string,
+        category: string | null,
+        types: string | null,
         customTypes: string[],
-        supportRole: string,
+        useCustom: boolean,
+        supportRole: string | null,
         maxTickets: number
     }
     moderation: {
         mute: {
             timeout: boolean,
-            role: string,
-            text: string,
-            link: string
+            role: string | null,
+            text: string | null,
+            link: string | null
         },
         kick: {
-            text: string,
-            link: string
+            text: string | null,
+            link: string | null
         },
         ban: {
-            text: string,
-            link: string
+            text: string | null,
+            link: string | null
         },
         softban: {
-            text: string,
-            link: string
+            text: string | null,
+            link: string | null
         },
         warn: {
-            text: string,
-            link: string
+            text: string | null,
+            link: string | null
         },
         role: {
-            role: string
+            role: string | null,
         }
     }
     tracks: {
diff --git a/src/utils/dualCollector.ts b/src/utils/dualCollector.ts
new file mode 100644
index 0000000..ae63757
--- /dev/null
+++ b/src/utils/dualCollector.ts
@@ -0,0 +1,49 @@
+import Discord from 'discord.js';
+import client from './client.js';
+import generateEmojiEmbed from "./generateEmojiEmbed.js";
+
+export default async function (m, interactionFilter, messageFilter) {
+    let out;
+    try {
+        out = await new Promise((resolve, reject) => {
+            let mes, int;
+            mes = m.createMessageComponentCollector({filter: (m) => interactionFilter(m), time: 600000})
+                .on("collect", (m) => { resolve(m); })
+            int = m.channel.createMessageCollector({filter: (m) => messageFilter(m), time: 600000})
+                .then("collect", (m) => { try {m.delete();} catch {}; resolve(m); })
+            mes.on("end", () => { int.stop(); })
+            int.on("end", () => { mes.stop(); })
+        })
+    } catch(e) {
+        console.log(e)
+        return null;
+    }
+
+    return out;
+}
+
+export async function modalInteractionCollector(m, modalFilter, interactionFilter) {
+    let out;
+    try {
+        out = await new Promise((resolve, reject) => {
+            let mod, int;
+            int = m.createMessageComponentCollector({filter: (m) => interactionFilter(m), time: 600000})
+                .on("collect", (m) => { resolve(m); })
+            mod = new Discord.InteractionCollector(
+                client, {
+                    filter: (m) => modalFilter(m),
+                    time: 600000
+                })
+                .on("collect", async (m) => {
+                    int.stop();
+                    (m as Discord.ModalSubmitInteraction).deferUpdate()
+                    resolve((m as Discord.ModalSubmitInteraction)); })
+            int.on("end", () => { mod.stop(); })
+            mod.on("end", () => { int.stop(); })
+        })
+    } catch(e) {
+        console.log(e)
+        return null;
+    }
+    return out;
+}
\ No newline at end of file
diff --git a/src/utils/generateConfig.ts b/src/utils/generateConfig.ts
deleted file mode 100644
index 39b28b0..0000000
--- a/src/utils/generateConfig.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import * as fs from 'fs';
-
-function writeLogConfig(guild, logs) {
-    if( !fs.existsSync(`./data/guilds/${guild.id}/config.json`) ) {
-        fs.rmSync(`./data/guilds/${guild.id}/config.json`);
-    }
-    if( !fs.existsSync(`./data/guilds/${guild.id}/pins.json`) ) {
-        let pins = guild.channels.cache.filter(c => c.type === "GUILD_TEXT").map(
-            c => c.messages.fetchPinned().then(m => m.map(m => m.id))
-        );
-        fs.writeFileSync(`./data/guilds/${guild.id}/pins.json`, JSON.stringify(pins));
-    }
-    if( !fs.existsSync(`./data/guilds/${guild.id}/logs.json`) ) {
-        fs.writeFileSync(`./data/guilds/${guild.id}/logs.json`, JSON.stringify([]));
-    } else if( logs ) {
-        fs.rmSync(`./data/guilds/${guild.id}/logs.json`);
-        fs.writeFileSync(`./data/guilds/${guild.id}/logs.json`, JSON.stringify([]));
-    }
-    fs.writeFileSync(`./data/guilds/${guild.id}/config.json`, JSON.stringify({
-        metadata: {
-            premium: false
-        },
-        logs: {
-            enabled: true,
-            logChannel: guild.systemChannelId,
-            toLog: "8be71",
-            toIgnore: {
-                bots: false,
-                channels: [],
-                members: [],
-                roles: []
-            }
-        },
-        userVerification: {
-            enabled: false,
-            roleID: null,
-            customMessage: null
-        },
-        modmail: {
-            enabled: false,
-            categoryId: null,
-            namingScheme: "rsm-{user}-{discriminator}",
-        },
-        welcome: {
-            enabled: false,
-            channelId: null,
-            message: null,
-            messageType: "embed",
-        },
-        filters: {
-            images: {
-                NSFW: true,
-                size: true
-            },
-            malware: true,
-            wordFilter: {
-                enabled: true,
-                words: {
-                    strict: [],
-                    loose: []
-                },
-                allowed: {
-                    users: [],
-                    roles: [],
-                    channels: []
-                }
-            },
-            invite: {
-                enabled: true,
-                allowed: {
-                    users: [],
-                    channels: [],
-                    roles: []
-                }
-            },
-            pings: {
-                mass: 5,
-                everyone: true,
-                roles: true,
-                allowed: {
-                    roles: [],
-                    rolesToMention: [],
-                    users: [],
-                    channels: []
-                }
-            }
-        },
-        tags: {}
-    }));
-}
-
-export default writeLogConfig;
\ No newline at end of file
diff --git a/src/utils/log.ts b/src/utils/log.ts
index 238b6f4..19eb2b6 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -1,10 +1,10 @@
 import * as fs from 'fs';
 import * as Discord from 'discord.js';
 import getEmojiByName from './getEmojiByName.js';
-import readConfig from './readConfig.js';
 import { toHexArray } from './calculate.js';
 import { promisify } from 'util';
 import generateKeyValueList from './generateKeyValueList.js';
+import client from './client.js';
 
 const wait = promisify(setTimeout);
 
@@ -52,8 +52,8 @@
         return auditLog;
     }
 
-    async log(log: any, client): Promise<void> {
-        let config = await readConfig(log.hidden.guild);
+    async log(log: any): Promise<void> {
+        let config = await client.database.read(log.hidden.guild);
         if (!config.logging.logs.enabled) return;
         if (!(log.meta.calculateType == true)) {
             if(!toHexArray(config.logging.logs.toLog).includes(log.meta.calculateType)) return console.log('Not logging this type of event');
diff --git a/src/utils/memory.ts b/src/utils/memory.ts
index 0cbf955..7e21fa9 100644
--- a/src/utils/memory.ts
+++ b/src/utils/memory.ts
@@ -1,22 +1,31 @@
-import readConfig from "./readConfig.js";
+import client from "./client.js";
 
 class Memory {
     memory: {};
     constructor() {
         this.memory = {};
+
+        setInterval(() => {
+            for (let guild in this.memory) {
+                if (this.memory[guild].updated + 15 * 60 * 1000 < Date.now()) {
+                    delete this.memory[guild];
+                }
+            }
+        }, 1000 * 60 * 30)
     }
 
     async readGuildInfo(guild: string): Promise<object> {
         if (!this.memory[guild]) {
-            let guildData = await readConfig(guild);
+            let guildData = await client.database.read(guild);
             this.memory[guild] = {
+                lastUpdated: Date.now(),
                 filters: guildData.filters,
                 logging: guildData.logging,
                 tickets: guildData.tickets,
-            }; // TODO: REMOVE GUILD FROM MEMORY WHEN THESE UPDATE
-        } // TODO: Add a "lastAccessed" prop, delete after 15 minutes
+            };
+        };
         return this.memory[guild];
     }
 }
 
-export default Memory;
\ No newline at end of file
+export default Memory;
diff --git a/src/utils/readConfig.ts b/src/utils/readConfig.ts
deleted file mode 100644
index b363fc0..0000000
--- a/src/utils/readConfig.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import client from './client.js';
-
-export default async function readConfig(guild: string): Promise<any> {
-    return await client.database.read(guild);
-}
diff --git a/src/utils/singleNotify.ts b/src/utils/singleNotify.ts
index 4e9e6fe..a983478 100644
--- a/src/utils/singleNotify.ts
+++ b/src/utils/singleNotify.ts
@@ -1,4 +1,4 @@
-import readConfig from "./readConfig.js";
+import client from './client.js';
 import generateEmojiEmbed from "./generateEmojiEmbed.js";
 
 let severities = {
@@ -7,17 +7,18 @@
     "Info": "Success"
 }
 
-export default async function(client, type: string, guild: string, message: string, severity: string) {
-    let config = await readConfig(guild);
-    if (config.singleEventNotifications[type]) return;
-    // TODO: Set config.singleEventNotifications[type] to true
-    let channel = await client.channels.fetch(config.logging.staff);
-    if (!channel) return;
+export default async function(type: string, guild: string, message: string, severity: string) {
+    let data = await client.database.read(guild);
+    if (data.singleEventNotifications[type]) return;
+    data.singleEventNotifications[type] = true;
+    client.database.write(guild, data);
     try {
+        let channel = await client.channels.fetch(data.logging.staff.channel);
+        if (!channel) return;
         await channel.send({embeds: [new generateEmojiEmbed()
             .setTitle(`${severity} notification`)
             .setDescription(message)
-            .setColor(severities[severity])
+            .setStatus(severities[severity])
             .setEmoji("CONTROL.BLOCKCROSS")
         ]})
     } catch (err) {
diff --git a/tsconfig.json b/tsconfig.json
index cbbd2ee..591467f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,7 @@
         "declarationMap": true,
         "resolveJsonModule": true,
         "moduleResolution": "node",
+        "skipLibCheck": true
     },
     "include": ["src/**/*"],
     "exclude": []