diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..c156789
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,5 @@
+TOKEN=""
+MONGOURL=""
+PTERO_URL=""
+PTERO_KEY=""
+PTERO_SERVER=""
\ No newline at end of file
diff --git a/.github/workflows/ptero.yml b/.github/workflows/ptero.yml
new file mode 100644
index 0000000..a6649f9
--- /dev/null
+++ b/.github/workflows/ptero.yml
@@ -0,0 +1,17 @@
+name: Restart ptero server
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ restart:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: supercrafter100/Pterodactyl-Restart@v1.0
+ with:
+ PTE_PANEL_URL: 'https://pte.classydev.fr'
+ PTE_BEARER_TOKEN: ${{ secrets.PTE_BEARER_TOKEN }}
+ PTE_PANEL_ID: ${{ secrets.PTE_PANEL_ID }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c6bba59..1bb0976 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
+lib
+.env
+src/config.ts
+
# Logs
logs
*.log
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..d802ace
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Nekonyan
\ No newline at end of file
diff --git a/.idea/Nekonyan.iml b/.idea/Nekonyan.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/Nekonyan.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..918b814
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..956ef06
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,859 @@
+{
+ "name": "nekonyan",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "nekonyan",
+ "version": "0.0.1",
+ "license": "GPL-3.0-or-later",
+ "dependencies": {
+ "@classycrafter/super-logger": "^2.0.0",
+ "axios": "^1.4.0",
+ "discord.js": "^14.13.0",
+ "dotenv": "^16.3.1",
+ "mongoose": "^7.4.3",
+ "ms": "^2.1.3",
+ "nekonya.js": "^1.1.7",
+ "pterodactyl.js": "^2.1.1"
+ },
+ "devDependencies": {
+ "@types/mongoose": "^5.11.97",
+ "@types/ms": "^0.7.31",
+ "@types/node": "^20.5.1",
+ "typescript": "^5.1.6"
+ }
+ },
+ "node_modules/@classycrafter/super-logger": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@classycrafter/super-logger/-/super-logger-2.0.0.tgz",
+ "integrity": "sha512-ET0kjxj6urpWct35fL298I+paPPdjoRhdD8iPjwygff1o6Q0S+4Z3eKQCxxPKGY2fHWSwGalWUPgMbhKWkYejw==",
+ "dependencies": {
+ "@types/node": "^18.11.9",
+ "chalk": "^4.1.2",
+ "fs": "^0.0.1-security",
+ "moment": "^2.29.4",
+ "moment-timezone": "^0.5.34",
+ "path": "^0.12.7"
+ }
+ },
+ "node_modules/@classycrafter/super-logger/node_modules/@types/node": {
+ "version": "18.17.6",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.6.tgz",
+ "integrity": "sha512-fGmT/P7z7ecA6bv/ia5DlaWCH4YeZvAQMNpUhrJjtAhOhZfoxS1VLUgU2pdk63efSjQaOJWdXMuAJsws+8I6dg=="
+ },
+ "node_modules/@discordjs/builders": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.5.tgz",
+ "integrity": "sha512-SdweyCs/+mHj+PNhGLLle7RrRFX9ZAhzynHahMCLqp5Zeq7np7XC6/mgzHc79QoVlQ1zZtOkTTiJpOZu5V8Ufg==",
+ "dependencies": {
+ "@discordjs/formatters": "^0.3.2",
+ "@discordjs/util": "^1.0.1",
+ "@sapphire/shapeshift": "^3.9.2",
+ "discord-api-types": "0.37.50",
+ "fast-deep-equal": "^3.1.3",
+ "ts-mixer": "^6.0.3",
+ "tslib": "^2.6.1"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/collection": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
+ "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/formatters": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.2.tgz",
+ "integrity": "sha512-lE++JZK8LSSDRM5nLjhuvWhGuKiXqu+JZ/DsOR89DVVia3z9fdCJVcHF2W/1Zxgq0re7kCzmAJlCMMX3tetKpA==",
+ "dependencies": {
+ "discord-api-types": "0.37.50"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/rest": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.0.1.tgz",
+ "integrity": "sha512-/eWAdDRvwX/rIE2tuQUmKaxmWeHmGealttIzGzlYfI4+a7y9b6ZoMp8BG/jaohs8D8iEnCNYaZiOFLVFLQb8Zg==",
+ "dependencies": {
+ "@discordjs/collection": "^1.5.3",
+ "@discordjs/util": "^1.0.1",
+ "@sapphire/async-queue": "^1.5.0",
+ "@sapphire/snowflake": "^3.5.1",
+ "@vladfrangu/async_event_emitter": "^2.2.2",
+ "discord-api-types": "0.37.50",
+ "magic-bytes.js": "^1.0.15",
+ "tslib": "^2.6.1",
+ "undici": "5.22.1"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/util": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.1.tgz",
+ "integrity": "sha512-d0N2yCxB8r4bn00/hvFZwM7goDcUhtViC5un4hPj73Ba4yrChLSJD8fy7Ps5jpTLg1fE9n4K0xBLc1y9WGwSsA==",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/ws": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.1.tgz",
+ "integrity": "sha512-avvAolBqN3yrSvdBPcJ/0j2g42ABzrv3PEL76e3YTp2WYMGH7cuspkjfSyNWaqYl1J+669dlLp+YFMxSVQyS5g==",
+ "dependencies": {
+ "@discordjs/collection": "^1.5.3",
+ "@discordjs/rest": "^2.0.1",
+ "@discordjs/util": "^1.0.1",
+ "@sapphire/async-queue": "^1.5.0",
+ "@types/ws": "^8.5.5",
+ "@vladfrangu/async_event_emitter": "^2.2.2",
+ "discord-api-types": "0.37.50",
+ "tslib": "^2.6.1",
+ "ws": "^8.13.0"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@sapphire/async-queue": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
+ "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sapphire/shapeshift": {
+ "version": "3.9.2",
+ "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz",
+ "integrity": "sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sapphire/snowflake": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz",
+ "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@types/mongoose": {
+ "version": "5.11.97",
+ "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
+ "integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
+ "deprecated": "Mongoose publishes its own types, so you do not need to install this package.",
+ "dev": true,
+ "dependencies": {
+ "mongoose": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "0.7.31",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
+ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.5.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz",
+ "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg=="
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
+ "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^3.0.0"
+ }
+ },
+ "node_modules/@types/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
+ },
+ "node_modules/@types/whatwg-url": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
+ "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.5",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz",
+ "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@vladfrangu/async_event_emitter": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz",
+ "integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/axios": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
+ "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
+ "dependencies": {
+ "follow-redirects": "^1.15.0",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axios/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==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bson": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.4.0.tgz",
+ "integrity": "sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==",
+ "engines": {
+ "node": ">=14.20.1"
+ }
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "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==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/debug/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/discord-api-types": {
+ "version": "0.37.50",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
+ "integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
+ },
+ "node_modules/discord.js": {
+ "version": "14.13.0",
+ "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.13.0.tgz",
+ "integrity": "sha512-Kufdvg7fpyTEwANGy9x7i4od4yu5c6gVddGi5CKm4Y5a6sF0VBODObI3o0Bh7TGCj0LfNT8Qp8z04wnLFzgnbA==",
+ "dependencies": {
+ "@discordjs/builders": "^1.6.5",
+ "@discordjs/collection": "^1.5.3",
+ "@discordjs/formatters": "^0.3.2",
+ "@discordjs/rest": "^2.0.1",
+ "@discordjs/util": "^1.0.1",
+ "@discordjs/ws": "^1.0.1",
+ "@sapphire/snowflake": "^3.5.1",
+ "@types/ws": "^8.5.5",
+ "discord-api-types": "0.37.50",
+ "fast-deep-equal": "^3.1.3",
+ "lodash.snakecase": "^4.1.1",
+ "tslib": "^2.6.1",
+ "undici": "5.22.1",
+ "ws": "^8.13.0"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.3.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
+ "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/motdotla/dotenv?sponsor=1"
+ }
+ },
+ "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/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "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==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs": {
+ "version": "0.0.1-security",
+ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
+ },
+ "node_modules/ip": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
+ },
+ "node_modules/kareem": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
+ "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
+ },
+ "node_modules/magic-bytes.js": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.0.15.tgz",
+ "integrity": "sha512-bpRmwbRHqongRhA+mXzbLWjVy7ylqmfMBYaQkSs6pac0z6hBTvsgrH0r4FBYd/UYVJBmS6Rp/O+oCCQVLzKV1g=="
+ },
+ "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==",
+ "optional": true
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/moment-timezone": {
+ "version": "0.5.43",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz",
+ "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==",
+ "dependencies": {
+ "moment": "^2.29.4"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mongodb": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.7.0.tgz",
+ "integrity": "sha512-zm82Bq33QbqtxDf58fLWBwTjARK3NSvKYjyz997KSy6hpat0prjeX/kxjbPVyZY60XYPDNETaHkHJI2UCzSLuw==",
+ "dependencies": {
+ "bson": "^5.4.0",
+ "mongodb-connection-string-url": "^2.6.0",
+ "socks": "^2.7.1"
+ },
+ "engines": {
+ "node": ">=14.20.1"
+ },
+ "optionalDependencies": {
+ "saslprep": "^1.0.3"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.201.0",
+ "@mongodb-js/zstd": "^1.1.0",
+ "kerberos": "^2.0.1",
+ "mongodb-client-encryption": ">=2.3.0 <3",
+ "snappy": "^7.2.2"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "@mongodb-js/zstd": {
+ "optional": true
+ },
+ "kerberos": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mongodb-connection-string-url": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
+ "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
+ "dependencies": {
+ "@types/whatwg-url": "^8.2.1",
+ "whatwg-url": "^11.0.0"
+ }
+ },
+ "node_modules/mongodb-connection-string-url/node_modules/tr46": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongodb-connection-string-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==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongodb-connection-string-url/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==",
+ "dependencies": {
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongoose": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.4.3.tgz",
+ "integrity": "sha512-eok0lW6mZJHK2vVSWyJb9tUfPMUuRF3h7YC4pU2K2/YSZBlNDUwvKsHgftMOANbokP2Ry+4ylvzAdW4KjkRFjw==",
+ "dependencies": {
+ "bson": "^5.4.0",
+ "kareem": "2.5.1",
+ "mongodb": "5.7.0",
+ "mpath": "0.9.0",
+ "mquery": "5.0.0",
+ "ms": "2.1.3",
+ "sift": "16.0.1"
+ },
+ "engines": {
+ "node": ">=14.20.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mongoose"
+ }
+ },
+ "node_modules/mpath": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
+ "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mquery": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
+ "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
+ "dependencies": {
+ "debug": "4.x"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "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=="
+ },
+ "node_modules/nekonya.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/nekonya.js/-/nekonya.js-1.1.7.tgz",
+ "integrity": "sha512-//Bap3QRbLDE2ofk/L1QcF2Sub3vymWHH7XSoaAj5wBoIwWniZYTVX+UHs+DkDYLo/I4kwbToY2dJcHyv3j1jg==",
+ "dependencies": {
+ "@types/node-fetch": "^2.6.2",
+ "node-fetch": "^2.6.7"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.6.13",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
+ "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/path": {
+ "version": "0.12.7",
+ "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
+ "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
+ "dependencies": {
+ "process": "^0.11.1",
+ "util": "^0.10.3"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "node_modules/pterodactyl.js": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/pterodactyl.js/-/pterodactyl.js-2.1.1.tgz",
+ "integrity": "sha512-F48PXw/LlfSFjRk/upDVkPJ4zvnaz8JqNlYkMWLXQ/ZhuUDOLP4c0Ll+t8q1KZqSEwKHfKocSlPXNwWPtYQT5w==",
+ "dependencies": {
+ "node-fetch": "^2.6.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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==",
+ "optional": true,
+ "dependencies": {
+ "sparse-bitfield": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/sift": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
+ "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ=="
+ },
+ "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==",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
+ "dependencies": {
+ "ip": "^2.0.0",
+ "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==",
+ "optional": true,
+ "dependencies": {
+ "memory-pager": "^1.0.2"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/ts-mixer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
+ "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
+ "node_modules/typescript": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
+ "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici": {
+ "version": "5.22.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz",
+ "integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==",
+ "dependencies": {
+ "busboy": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
+ "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
+ "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
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f84ae95
--- /dev/null
+++ b/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "nekonyan",
+ "version": "0.0.1",
+ "description": "Official NekoNya Discord Bot",
+ "main": "lib/index.js",
+ "scripts": {
+ "test": "npx tsc",
+ "start": "npx tsc && node lib/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/NekoNyaDevs/bot.git"
+ },
+ "keywords": [
+ "bot",
+ "discord.js",
+ "discord",
+ "nekonya",
+ "neko"
+ ],
+ "author": "TheDogHusky",
+ "license": "GPL-3.0-or-later",
+ "bugs": {
+ "url": "https://github.com/NekoNyaDevs/bot/issues"
+ },
+ "homepage": "https://github.com/NekoNyaDevs/bot#readme",
+ "dependencies": {
+ "@classycrafter/super-logger": "^2.0.0",
+ "axios": "^1.4.0",
+ "discord.js": "^14.13.0",
+ "dotenv": "^16.3.1",
+ "mongoose": "^7.4.3",
+ "ms": "^2.1.3",
+ "nekonya.js": "^1.1.7",
+ "pterodactyl.js": "^2.1.1"
+ },
+ "devDependencies": {
+ "@types/mongoose": "^5.11.97",
+ "@types/ms": "^0.7.31",
+ "@types/node": "^20.5.1",
+ "typescript": "^5.1.6"
+ }
+}
diff --git a/src/commands/admin/fullrestart.ts b/src/commands/admin/fullrestart.ts
new file mode 100644
index 0000000..1d8da77
--- /dev/null
+++ b/src/commands/admin/fullrestart.ts
@@ -0,0 +1,26 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+
+export default class FullRestartCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'fullrestart',
+ description: 'Restart the bot and the server',
+ category: 'admin',
+ usage: 'fullrestart',
+ ownerOnly: true,
+ nsfw: false,
+ options: [],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ await ctx.reply({
+ content: client.makeReply('Restarting... See ya!', 'loading'),
+ });
+ await client.ptero.restartNeko();
+ };
+};
\ No newline at end of file
diff --git a/src/commands/admin/restart.ts b/src/commands/admin/restart.ts
new file mode 100644
index 0000000..9404345
--- /dev/null
+++ b/src/commands/admin/restart.ts
@@ -0,0 +1,38 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, Collection } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import { slashsync } from '../../struct/functions';
+
+export default class RestartCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'restart',
+ description: 'Restart the bot',
+ category: 'admin',
+ usage: 'restart',
+ ownerOnly: true,
+ nsfw: false,
+ options: [],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ await ctx.reply({
+ content: client.makeReply('Restarting...', 'loading'),
+ });
+ await client.stop();
+ console.log(client.commands.get('hello')?.run.toString())
+ client.resetProperties();
+ await client.start();
+ await ctx.editReply({
+ content: client.makeReply('Restarted, reloading commands...', 'success')
+ });
+ await slashsync(client, { debug: client.config.debug });
+ console.log(client.commands.get('hello')?.run.toString())
+ await ctx.editReply({
+ content: client.makeReply('Reloaded commands, successfully restarted!', 'success')
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/commands/admin/stop.ts b/src/commands/admin/stop.ts
new file mode 100644
index 0000000..7163a88
--- /dev/null
+++ b/src/commands/admin/stop.ts
@@ -0,0 +1,26 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+
+export default class StopCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'stop',
+ description: 'Stop the bot',
+ category: 'admin',
+ usage: 'stop',
+ ownerOnly: true,
+ nsfw: false,
+ options: [],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ await ctx.reply({
+ content: client.makeReply('Stopping... Goodbye!', 'loading'),
+ });
+ await client.ptero.stopNeko();
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/hug.ts b/src/commands/fun/hug.ts
new file mode 100644
index 0000000..724b6ac
--- /dev/null
+++ b/src/commands/fun/hug.ts
@@ -0,0 +1,48 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class KitsuneCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'hug',
+ description: 'hug someone!',
+ category: 'fun',
+ usage: 'hug ',
+ ownerOnly: false,
+ nsfw: false,
+ options: [{
+ name: 'user',
+ description: 'The user to hug',
+ type: ApplicationCommandOptionType.User,
+ required: true
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const user = ctx.options.getUser('user', true);
+ if (user.id === ctx.user.id) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t hug yourself!', 'error')
+ });
+ return;
+ }
+ const hug = await NekoNya.hug();
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(hug)
+ .setTimestamp()
+ .setURL(hug)
+ .setDescription(`> Ooooww!! Sweet! ${ctx.user.toString()} just hugged ${user.toString()}!`)
+ .setTitle(`${client.config.emotes.hug}・Hug!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ await ctx.reply({
+ embeds: [embed]
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/kiss.ts b/src/commands/fun/kiss.ts
new file mode 100644
index 0000000..6d74721
--- /dev/null
+++ b/src/commands/fun/kiss.ts
@@ -0,0 +1,48 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class KitsuneCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'kiss',
+ description: 'kiss someone!',
+ category: 'fun',
+ usage: 'kiss ',
+ ownerOnly: false,
+ nsfw: false,
+ options: [{
+ name: 'user',
+ description: 'The user to kiss',
+ type: ApplicationCommandOptionType.User,
+ required: true
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const user = ctx.options.getUser('user', true);
+ if (user.id === ctx.user.id) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t kiss yourself!', 'error')
+ });
+ return;
+ }
+ const kiss = await NekoNya.kiss();
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(kiss)
+ .setTimestamp()
+ .setURL(kiss)
+ .setDescription(`> Oh my-!! ${ctx.user.toString()} just kissed ${user.toString()}!`)
+ .setTitle(`${client.config.emotes.kiss}・Kiss!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ await ctx.reply({
+ embeds: [embed]
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/kitsune.ts b/src/commands/fun/kitsune.ts
new file mode 100644
index 0000000..88d56db
--- /dev/null
+++ b/src/commands/fun/kitsune.ts
@@ -0,0 +1,59 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class KitsuneCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'neko',
+ description: 'Get a random kitsune image',
+ category: 'fun',
+ usage: 'kitsune [number]',
+ ownerOnly: false,
+ nsfw: false,
+ options: [{
+ name: 'number',
+ description: 'The number of images to get',
+ type: ApplicationCommandOptionType.Number,
+ required: false
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const number = ctx.options.getNumber('number') || 1;
+ if (number > 3) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t get more than 3 images at once!', 'error')
+ });
+ return;
+ }
+ if (number < 1) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t get less than 1 image!', 'error')
+ });
+ return;
+ }
+ for (let i = 0; i < number; i++) {
+ const kitsune = await NekoNya.kitsune();
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(kitsune)
+ .setTimestamp()
+ .setURL(kitsune)
+ .setTitle(`${client.config.emotes.fox}・Kitsune!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ if (i === 0) {
+ await ctx.reply({
+ embeds: [embed]
+ });
+ } else await ctx.channel?.send({
+ embeds: [embed]
+ });
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/lewd.ts b/src/commands/fun/lewd.ts
new file mode 100644
index 0000000..1dd8127
--- /dev/null
+++ b/src/commands/fun/lewd.ts
@@ -0,0 +1,60 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class NekoCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'lewd',
+ description: 'Get a random lewd image',
+ category: 'fun',
+ usage: 'lewd [number]',
+ ownerOnly: false,
+ nsfw: true,
+ options: [{
+ name: 'number',
+ description: 'The number of images to get',
+ type: ApplicationCommandOptionType.Number,
+ required: false
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const number = ctx.options.getNumber('number') || 1;
+ if (number > 3) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t get more than 3 images at once!', 'error')
+ });
+ return;
+ }
+ if (number < 1) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t get less than 1 image!', 'error')
+ });
+ return;
+ }
+ for (let i = 0; i < number; i++) {
+ // @ts-ignore Ignoring for the moment until update to allow lewd endpoint
+ const lewd: { url: string } = await NekoNya.get('random/lewd');
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(lewd.url)
+ .setTimestamp()
+ .setURL(lewd.url)
+ .setTitle(`${client.config.emotes.lewd}・Lewd! You pervert!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ if (i === 0) {
+ await ctx.reply({
+ embeds: [embed]
+ });
+ } else await ctx.channel?.send({
+ embeds: [embed]
+ });
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts
new file mode 100644
index 0000000..5e852a6
--- /dev/null
+++ b/src/commands/fun/neko.ts
@@ -0,0 +1,59 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class NekoCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'neko',
+ description: 'Get a random neko image',
+ category: 'fun',
+ usage: 'neko [number]',
+ ownerOnly: false,
+ nsfw: false,
+ options: [{
+ name: 'number',
+ description: 'The number of images to get',
+ type: ApplicationCommandOptionType.Number,
+ required: false
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const number = ctx.options.getNumber('number') || 1;
+ if (number > 3) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t get more than 3 images at once!', 'error')
+ });
+ return;
+ }
+ if (number < 1) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t get less than 1 image!', 'error')
+ });
+ return;
+ }
+ for (let i = 0; i < number; i++) {
+ const neko = await NekoNya.neko();
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(neko)
+ .setTimestamp()
+ .setURL(neko)
+ .setTitle(`${client.config.emotes.cat}・Neko!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ if (i === 0) {
+ await ctx.reply({
+ embeds: [embed]
+ });
+ } else await ctx.channel?.send({
+ embeds: [embed]
+ });
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/pat.ts b/src/commands/fun/pat.ts
new file mode 100644
index 0000000..6d43d85
--- /dev/null
+++ b/src/commands/fun/pat.ts
@@ -0,0 +1,48 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class KitsuneCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'pat',
+ description: 'Pat someone!',
+ category: 'fun',
+ usage: 'pat ',
+ ownerOnly: false,
+ nsfw: false,
+ options: [{
+ name: 'user',
+ description: 'The user to pat',
+ type: ApplicationCommandOptionType.User,
+ required: true
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const user = ctx.options.getUser('user', true);
+ if (user.id === ctx.user.id) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t pat yourself!', 'error')
+ });
+ return;
+ }
+ const pat = await NekoNya.pat();
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(pat)
+ .setTimestamp()
+ .setURL(pat)
+ .setDescription(`> Oooh! So cute! ${ctx.user.toString()} patted ${user.toString()}!`)
+ .setTitle(`${client.config.emotes.pat}・Pat!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ await ctx.reply({
+ embeds: [embed]
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/commands/fun/slap.ts b/src/commands/fun/slap.ts
new file mode 100644
index 0000000..ed9f70d
--- /dev/null
+++ b/src/commands/fun/slap.ts
@@ -0,0 +1,48 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import * as NekoNya from 'nekonya.js';
+
+export default class KitsuneCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'slap',
+ description: 'Slap someone!',
+ category: 'fun',
+ usage: 'slap ',
+ ownerOnly: false,
+ nsfw: false,
+ options: [{
+ name: 'user',
+ description: 'The user to slap',
+ type: ApplicationCommandOptionType.User,
+ required: true
+ }],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const user = ctx.options.getUser('user', true);
+ if (user.id === ctx.user.id) {
+ await ctx.reply({
+ content: client.makeReply('You can\'t slap yourself!', 'error')
+ });
+ return;
+ }
+ const slap = await NekoNya.slap();
+ const embed = new EmbedBuilder()
+ .setColor(client.config.colors.main)
+ .setImage(slap)
+ .setTimestamp()
+ .setURL(slap)
+ .setDescription(`> Ouchie!! ${ctx.user.toString()} slapped ${user.toString()}!`)
+ .setTitle(`${client.config.emotes.slap}・Slap!`)
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+ await ctx.reply({
+ embeds: [embed]
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/commands/utils/hello.ts b/src/commands/utils/hello.ts
new file mode 100644
index 0000000..552e471
--- /dev/null
+++ b/src/commands/utils/hello.ts
@@ -0,0 +1,26 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+
+export default class HelloCommand extends Command {
+ public constructor(client: Client) {
+ super(client, {
+ name: 'hello',
+ description: 'Say hello to the bot',
+ category: 'utils',
+ usage: 'hello',
+ ownerOnly: false,
+ nsfw: false,
+ options: [],
+ defer: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ await ctx.reply({
+ content: `Hello, ${ctx.user}!`
+ });
+ return;
+ };
+};
\ No newline at end of file
diff --git a/src/commands/utils/help.ts b/src/commands/utils/help.ts
new file mode 100644
index 0000000..77c2bda
--- /dev/null
+++ b/src/commands/utils/help.ts
@@ -0,0 +1,84 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, ApplicationCommandOptionType, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+
+export default class HelpCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'help',
+ description: 'Get help about a command or general help',
+ category: 'utils',
+ usage: 'help [command]',
+ ownerOnly: false,
+ nsfw: false,
+ options: [
+ {
+ name: 'command',
+ description: 'The command you want to get help from',
+ type: ApplicationCommandOptionType.String,
+ required: false
+ }
+ ],
+ defer: true
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const command = ctx.options.getString('command');
+ if (command) {
+ const cmd = client.commands.get(command);
+ if (!cmd) {
+ await ctx.editReply({
+ content: `The command \`${command}\` doesn't exist.`
+ });
+ return;
+ }
+
+ const embed = new EmbedBuilder()
+ .setTitle(`${client.config.emotes.info}・Help for ${cmd.name}`)
+ .setDescription(`> ${cmd.description.split('\n').join('\n> ')}\n\n> **Usage**: \`${cmd.usage}\`\n> **Category**: \`${cmd.category}\`\n> **Owner Only**: ${cmd.ownerOnly ? '`Yes`' : '`No`'}\n> **NSFW**: ${cmd.nsfw ? '`Yes' : '`No`'}`)
+ .setColor(client.config.colors.main)
+ .setTimestamp()
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+
+ await ctx.editReply({
+ embeds: [embed]
+ });
+ } else {
+ const utils = client.commands.filter((cmd) => cmd.category === 'utils')?.map((cmd) => `\`${cmd.name}\``).join(', ') || '*Coming soon!*';
+ const fun = client.commands.filter((cmd) => cmd.category === 'fun')?.map((cmd) => `\`${cmd.name}\``).join(', ') || '*Coming soon!*';
+ const admin = client.commands.filter((cmd) => cmd.category === 'admin')?.map((cmd) => `\`${cmd.name}\``).join(', ') || '*Coming soon!*';
+
+ const embed = new EmbedBuilder()
+ .setTitle(`${client.config.emotes.info}・Help`)
+ .setDescription(`> To get help about a specific command, run \`/help \`.\n> To execute a command, run \`/command\`.`)
+ .addFields([
+ {
+ name: `${client.config.emotes.utils}・Utils`,
+ value: `> ${utils}`,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.fun}・Fun`,
+ value: `> ${fun}`,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.admin}・Admin`,
+ value: `> ${admin}`,
+ inline: true
+ }
+ ])
+ .setColor(client.config.colors.main)
+ .setTimestamp()
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) });
+
+ await ctx.editReply({
+ embeds: [embed]
+ });
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/commands/utils/infos.ts b/src/commands/utils/infos.ts
new file mode 100644
index 0000000..646ab2a
--- /dev/null
+++ b/src/commands/utils/infos.ts
@@ -0,0 +1,97 @@
+import Command from '../../struct/command';
+import Client from '../../struct/client';
+import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js';
+import { IGuild } from '../../struct/typings';
+import { version, latest } from 'nekonya.js';
+import { getAPIStatus, formatUptime } from '../../struct/functions';
+
+export default class InfosCommand extends Command {
+ constructor(client: Client) {
+ super(client, {
+ name: 'infos',
+ description: 'Get some informations about the bot',
+ category: 'utils',
+ usage: 'infos',
+ ownerOnly: false,
+ nsfw: false,
+ options: [],
+ defer: true
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise {
+ const name = client.user?.tag;
+ const id = client.user?.id;
+ const owner = client.config.owners.map((owner) => {
+ const user = client.users.cache.get(owner);
+ return user?.tag || 'An error occurred';
+ }).join(', ');
+ const guilds = client.guilds.cache.size;
+ const users = client.users.cache.size;
+ const channels = client.channels.cache.size;
+ const commands = client.commands.size;
+ const uptime = formatUptime(client.uptime || 0);
+ const apiVersion = version;
+ const apiLatest = latest;
+ const apiStatus = (await getAPIStatus(apiVersion)).statusString;
+
+ const embed = new EmbedBuilder()
+ .setTitle(`${client.config.emotes.info}・Informations`)
+ .setColor(client.config.colors.main)
+ .setDescription(`> Get some informations about the Bot!`)
+ .setTimestamp()
+ .setAuthor({ name: ctx.user.tag, iconURL: ctx.user.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .setFooter({ text: `Requested by ${ctx.user.tag}`, iconURL: client.user?.displayAvatarURL({ size: 1024, extension: 'webp' }) })
+ .addFields([
+ {
+ name: `${client.config.emotes.pen}・Name`,
+ value: `> \`${name}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.id}・ID`,
+ value: `> \`${id}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.owner}・Owner`,
+ value: `> \`${owner}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.guilds}・Guilds`,
+ value: `> \`${guilds}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.silhouettes}・Users`,
+ value: `> \`${users}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.channels}・Channels`,
+ value: `> \`${channels}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.commands}・Commands`,
+ value: `> \`${commands}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.time}・Uptime`,
+ value: `> \`${uptime}\``,
+ inline: true
+ },
+ {
+ name: `${client.config.emotes.api}・API`,
+ value: `> **Status**: \`${apiStatus}\`\n> **Version**: \`${apiVersion}\`\n> **Latest**: \`${apiLatest}\``,
+ inline: true
+ }
+ ]);
+
+ await ctx.editReply({
+ embeds: [embed]
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/config.example.ts b/src/config.example.ts
new file mode 100644
index 0000000..e71accd
--- /dev/null
+++ b/src/config.example.ts
@@ -0,0 +1,42 @@
+export const debug = true;
+export const emotes = {
+ success: ':white_check_mark:',
+ error: ':x:',
+ bug: ':bug:',
+ input: ':inbox_tray:',
+ output: ':outbox_tray:',
+ loading: ':hourglass_flowing_sand:',
+ warning: ':warning:',
+ info: ':information_source:',
+ utils: ':mag_right:',
+ fun: ':tada:',
+ admin: ':gear:',
+ owner: ':crown:',
+ music: ':musical_note:',
+ cat: ':cat:',
+ fox: ':fox:',
+ hug: ':hugging:',
+ kiss: ':kissing_heart:',
+ pat: ':handshake:',
+ slap: ':raised_hand_with_fingers_splayed:',
+ tickle: ':smirk_cat:',
+ cuddle: ':smiling_face_with_3_hearts:',
+ poke: ':point_right:',
+ feed: ':fork_and_knife:',
+ lewd: ':smirk:'
+}
+export const colors = {
+ success: '#4fd92a',
+ error: '#f44336',
+ bug: '#FF0000',
+ warning: '#ff9800',
+ info: '#2196f3',
+ loading: '#FFFF00',
+ debug: '#77c4fd',
+ main: '#eb7763',
+ secondary: '#adadad'
+};
+export const owners = [
+ 'Your ID'
+];
+export const queueInterval = 1000;
\ No newline at end of file
diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts
new file mode 100644
index 0000000..803c1f8
--- /dev/null
+++ b/src/events/interactionCreate.ts
@@ -0,0 +1,64 @@
+import Event from '../struct/event';
+import Client from '../struct/client';
+import { ChatInputCommandInteraction, ChannelType } from 'discord.js';
+
+export default class InteractionCreateEvent extends Event {
+ public constructor(client: Client) {
+ super(client, {
+ name: 'interactionCreate',
+ once: false
+ });
+ };
+
+ public async run(client: Client, ctx: ChatInputCommandInteraction): Promise {
+ if (!ctx.isCommand()) return;
+ if (!ctx.guild) return;
+ if (!client.isReady()) return;
+ if (!ctx.channel) return;
+ if (!ctx.channel.isTextBased()) return;
+ if (ctx.channel.type === ChannelType.DM) return;
+
+ const data = await client.database.getGuild(ctx.guild.id);
+ const command = client.commands.get(ctx.commandName);
+ if (!command) {
+ await ctx.reply({
+ content: client.makeReply("That command does not exist.", "error"),
+ ephemeral: true
+ });
+ try {
+ await client.application.commands.delete(ctx.commandId);
+ } catch(e) {
+ await ctx.guild.commands.delete(ctx.commandId).catch(() => {
+ return;
+ });
+ }
+ return;
+ }
+
+ if (command.ownerOnly && !client.config.owners.includes(ctx.user.id)) {
+ await ctx.reply({
+ content: client.makeReply("You do not have permission to execute this command.", "error"),
+ ephemeral: true
+ });
+ return;
+ }
+
+ if (command.nsfw && !ctx.channel.isThread() && !ctx.channel?.nsfw) {
+ await ctx.reply({
+ content: client.makeReply("This command can only be executed in a NSFW channel.", "error"),
+ ephemeral: true
+ });
+ return;
+ }
+
+ if (command.defer) {
+ await ctx.deferReply();
+ }
+
+ try {
+ await command.run(client, ctx, data);
+ } catch(e) {
+ await command.onError(ctx, e as Error);
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/events/ready.ts b/src/events/ready.ts
new file mode 100644
index 0000000..ac1015c
--- /dev/null
+++ b/src/events/ready.ts
@@ -0,0 +1,24 @@
+import Event from '../struct/event';
+import Client from '../struct/client';
+import { ActivityType } from 'discord.js';
+import { slashsync, wait } from '../struct/functions';
+
+export default class ReadyEvent extends Event {
+ public constructor(client: Client) {
+ super(client, {
+ name: 'ready',
+ once: true
+ });
+ };
+
+ public async run(client: Client): Promise {
+ client.logger.info(`Logged in as ${client.user!.tag}`, 'Ready');
+ client.user!.setActivity(`/help`, { type: ActivityType.Playing });
+ client.user!.setStatus('online');
+
+ await wait('3s');
+
+ await slashsync(client, { debug: client.config.debug });
+ return;
+ };
+};
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..5571610
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,21 @@
+import Client from './struct/client';
+import { existsSync, mkdirSync } from 'fs';
+
+if (!existsSync('logs')) {
+ mkdirSync('logs');
+}
+
+const client = new Client();
+client.start();
+
+if (process.env.NODE_ENV === 'development') {
+ process.on('unhandledRejection', (reason, promise) => {
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
+ });
+ process.on('uncaughtException', (error) => {
+ console.error('Uncaught Exception:', error);
+ });
+ process.on('warning', (warning) => {
+ console.warn(warning);
+ });
+}
\ No newline at end of file
diff --git a/src/models/guild.ts b/src/models/guild.ts
new file mode 100644
index 0000000..4883412
--- /dev/null
+++ b/src/models/guild.ts
@@ -0,0 +1,8 @@
+import { model, Schema } from 'mongoose';
+import { IGuild } from '../struct/typings';
+
+const schema = new Schema({
+ id: { type: String, required: true },
+});
+
+export default model('guilds', schema);
\ No newline at end of file
diff --git a/src/struct/client.ts b/src/struct/client.ts
new file mode 100644
index 0000000..9b5dd6e
--- /dev/null
+++ b/src/struct/client.ts
@@ -0,0 +1,94 @@
+import * as Discord from 'discord.js';
+import { Client, GatewayIntentBits } from 'discord.js';
+import Command from './command';
+import Event from './event';
+import { readdirSync } from 'fs';
+import { join } from 'path';
+import { config } from 'dotenv';
+import Database from './database';
+import { Logger } from '@classycrafter/super-logger';
+import * as conf from '../config';
+import Ptero from './ptero';
+
+config();
+
+export default class Bot extends Client {
+ public commands: Discord.Collection = new Discord.Collection();
+ public events: Discord.Collection = new Discord.Collection();
+ public logger: Logger = new Logger({
+ name: 'Nekonyan',
+ writelogs: true,
+ colored: true,
+ dirpath: join(__dirname, '..', '..', 'logs'),
+ tzformat: 24,
+ timezone: 'Europe/Paris'
+ });
+ public database: Database = new Database(this);
+ public config: typeof conf = conf;
+ public ptero: Ptero = new Ptero(this);
+
+ public constructor() {
+ super({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMembers,
+ ]
+ });
+ };
+
+ private async _init(): Promise {
+ await this.database.connect();
+
+ await this.loadCommands();
+ await this.loadEvents();
+ };
+
+ public async start(): Promise {
+ await this._init();
+ await this.login(process.env.TOKEN);
+ };
+
+ public async stop(): Promise {
+ await this.destroy();
+ await this.database.disconnect();
+ };
+
+ public resetProperties(): void {
+ this.commands = new Discord.Collection();
+ this.events = new Discord.Collection();
+ };
+
+ public async loadCommands(): Promise {
+ const commandPath = join(__dirname, '..', 'commands');
+
+ for (const dir of readdirSync(commandPath)) {
+ const commands = readdirSync(`${commandPath}/${dir}`).filter((file) => file.endsWith('.ts') || file.endsWith('.js'));
+
+ for (const file of commands) {
+ const { default: Command } = await import(`${commandPath}/${dir}/${file}`);
+ const command = new Command(this);
+
+ this.commands.set(command.name, command);
+ }
+ }
+ }
+
+ public async loadEvents(): Promise {
+ const eventPath = join(__dirname, '..', 'events');
+ const events = readdirSync(`${eventPath}`).filter((file) => file.endsWith('.ts') || file.endsWith('.js'));
+
+ for (const file of events) {
+ const { default: Event } = await import(`${eventPath}/${file}`);
+ const event = new Event(this);
+
+ this.events.set(event.name, event);
+ if (event.once) this.once(event.name, (...args) => event.run(this, ...args));
+ else this.on(event.name, (...args) => event.run(this, ...args));
+ }
+ }
+
+ makeReply(content: string, type: typeof this.config.emotes | string): string {
+ // @ts-ignore
+ return `${this.config.emotes[type]}・${content}`;
+ }
+};
\ No newline at end of file
diff --git a/src/struct/command.ts b/src/struct/command.ts
new file mode 100644
index 0000000..762a3a0
--- /dev/null
+++ b/src/struct/command.ts
@@ -0,0 +1,49 @@
+import { Client, ApplicationCommandType, ChatInputCommandInteraction, APIApplicationCommandOption } from 'discord.js';
+import { IInfos, IGuild, CommandOptions } from './typings';
+
+export default abstract class Command {
+ public name: string;
+ public description: string;
+ public category: string;
+ public usage: string;
+ public ownerOnly: boolean;
+ public nsfw: boolean;
+ public client: Client;
+ public defer: boolean;
+ public options: CommandOptions;
+
+ public constructor(client: Client, options: IInfos) {
+ this.client = client;
+ this.name = options.name;
+ this.description = options.description;
+ this.category = options.category;
+ this.usage = options.usage;
+ this.ownerOnly = options.ownerOnly;
+ this.nsfw = options.nsfw;
+ this.options = options.options;
+ this.defer = options.defer;
+ };
+
+ public abstract run(client: Client, ctx: ChatInputCommandInteraction, data: IGuild): Promise;
+
+ public getPostableData(): any {
+ return {
+ name: this.name,
+ description: this.description,
+ options: this.options,
+ type: ApplicationCommandType.ChatInput
+ };
+ };
+
+ public async onError(ctx: ChatInputCommandInteraction, error: Error): Promise {
+ if (ctx.deferred || ctx.replied) {
+ await ctx.editReply({
+ content: `An error occured while executing this command: \`${error.message}\``
+ });
+ } else {
+ await ctx.reply({
+ content: `An error occured while executing this command: \`${error.message}\``
+ });
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/struct/database.ts b/src/struct/database.ts
new file mode 100644
index 0000000..b7aa77d
--- /dev/null
+++ b/src/struct/database.ts
@@ -0,0 +1,44 @@
+import mongoose from 'mongoose';
+import Guild from '../models/guild';
+import { ISchemas, IGuild } from './typings';
+import Client from './client';
+import { Snowflake } from 'discord.js';
+
+export default class Database {
+ public schemas: ISchemas = {
+ Guild: Guild
+ };
+ public client: Client;
+
+ public constructor(client: Client) {
+ this.client = client;
+
+ mongoose.connection.on('connected', () => {
+ client.logger.info('Connected to MongoDB', 'Database');
+ });
+
+ mongoose.connection.on('disconnected', () => {
+ client.logger.warn('Disconnected from MongoDB', 'Database');
+ });
+
+ mongoose.connection.on('error', (err) => {
+ client.logger.error(err.stack, 'Database');
+ });
+ };
+
+ public async connect(): Promise {
+ await mongoose.connect(process.env.MONGOURL as string).catch((err) => {
+ this.client.logger.fatal(err.stack, 'Database');
+ });
+ };
+
+ public async disconnect(): Promise {
+ await mongoose.disconnect();
+ };
+
+ public async getGuild(id: Snowflake): Promise {
+ const guild = await this.schemas.Guild.findOne({ id });
+ if (guild) return guild;
+ else return await (await this.schemas.Guild.create({ id })).save();
+ };
+};
\ No newline at end of file
diff --git a/src/struct/event.ts b/src/struct/event.ts
new file mode 100644
index 0000000..a8db567
--- /dev/null
+++ b/src/struct/event.ts
@@ -0,0 +1,16 @@
+import Client from './client';
+import { IEventInfos } from './typings';
+
+export default abstract class Event {
+ public name: string;
+ public client: Client;
+ public once: boolean;
+
+ public constructor(client: Client, opts: IEventInfos) {
+ this.client = client;
+ this.name = opts.name;
+ this.once = opts.once;
+ };
+
+ public abstract run(client: Client, ...args: any[]): Promise;
+};
\ No newline at end of file
diff --git a/src/struct/functions.ts b/src/struct/functions.ts
new file mode 100644
index 0000000..c00a8e5
--- /dev/null
+++ b/src/struct/functions.ts
@@ -0,0 +1,111 @@
+import Client from './client';
+import * as Discord from 'discord.js';
+import { SlashSyncOptions, APIStatusAnswer } from './typings';
+import ms from 'ms';
+import axios from 'axios';
+
+export async function slashsync(client: Client, options: SlashSyncOptions = {
+ debug: false,
+ guildId: undefined
+}) {
+ const log = (message: string) => {
+ return options.debug && client.logger.debug(message, "SlashSync");
+ };
+
+ const ready = client.readyAt ? Promise.resolve() : new Promise(resolve => client.once('ready', resolve));
+ await ready;
+ const currentCommands = await client.application?.commands?.fetch({ guildId: options.guildId }) ?? new Discord.Collection();
+
+ log(`Synchronizing commands...`);
+ log(`${currentCommands.size} commands are actually posted.`);
+
+ const newCommands = client.commands.filter((command) => !currentCommands.some((c) => c.name === command.name));
+ for (let newCommand of newCommands.values()) {
+ const postableData = newCommand.getPostableData();
+ await client.application?.commands?.create(postableData, options.guildId);
+ }
+
+ log(`${newCommands.size} commands ${newCommands.size > 1 ? 'have' : 'has'} been created.`);
+
+ const deletedCommands = currentCommands.filter((command) => !client.commands.some((c) => c.name === command.name)).toJSON();
+ for (let deletedCommand of deletedCommands) {
+ await deletedCommand.delete().catch(err => {
+ log(`Unable to delete command ${deletedCommand.name}. (InternalErr)`);
+ });
+ }
+
+ log(`${deletedCommands.length} ${deletedCommands.length > 1 ? 'have' : 'has'} been deleted.`);
+
+ const updatedCommands = client.commands.filter((command) => currentCommands.some((c) => c.name === command.name));
+ let updatedCommandCount = 0;
+ for (let updatedCommand of updatedCommands.values()) {
+ const newCommand = updatedCommand;
+ const previousCommand = currentCommands.find((c) => c.name === newCommand.name);
+ let modified = false;
+ if (previousCommand?.description !== newCommand.description) modified = true;
+ if (!Discord.ApplicationCommand.optionsEqual(previousCommand?.options ?? [], newCommand.getPostableData().options ?? [])) modified = true;
+ if (modified) {
+ await previousCommand?.edit(newCommand);
+ updatedCommandCount++;
+ }
+ }
+
+ log(`${updatedCommandCount} ${updatedCommandCount > 1 ? 'have' : 'has'} been updated.`);
+ log(`Successfully synchronized commands!`);
+
+ return {
+ currentCommandCount: currentCommands.size,
+ newCommandCount: newCommands.size,
+ deletedCommandCount: deletedCommands.length,
+ updatedCommandCount
+ };
+}
+
+export function wait(time: number | string): Promise {
+ return new Promise((resolve) => {
+ setTimeout(resolve, typeof time === 'string' ? ms(time) : time);
+ });
+}
+
+export function formatUptime(ms: number): string {
+ const days = Math.floor(ms / 86400000);
+ const hours = Math.floor(ms / 3600000) % 24;
+ const minutes = Math.floor(ms / 60000) % 60;
+ const seconds = Math.floor(ms / 1000) % 60;
+ return `${days}d ${hours}h ${minutes}m ${seconds}s`;
+}
+
+export async function getAPIStatus(version: string): Promise {
+ const res = await axios.get(`https://nekonya.classydev.fr/api/${version}`).catch(() => {
+ return {
+ data: {
+ status: 0,
+ version: 'Unknown'
+ }
+ };
+ });
+ let statusString;
+ switch(res.data.status) {
+ case 200:
+ statusString = 'Online';
+ break;
+ case 503:
+ statusString = 'Maintenance';
+ break;
+ case 502:
+ statusString = 'Offline';
+ break;
+ case 0:
+ statusString = 'Offline';
+ break;
+ default:
+ statusString = 'Error';
+ break;
+ }
+
+ return {
+ status: res.data.status,
+ version: res.data.version,
+ statusString: statusString
+ };
+}
\ No newline at end of file
diff --git a/src/struct/ptero.ts b/src/struct/ptero.ts
new file mode 100644
index 0000000..399499c
--- /dev/null
+++ b/src/struct/ptero.ts
@@ -0,0 +1,26 @@
+import Client from './client';
+import * as Pterodactyl from 'pterodactyl.js';
+
+export default class Ptero {
+ public client: Client;
+ public _ptero: Pterodactyl.UserClient;
+
+ public constructor(client: Client) {
+ this.client = client;
+ this._ptero = new Pterodactyl.Builder(process.env.PTERO_URL as string, process.env.PTERO_KEY as string).asUser();
+ };
+
+ public async getServer(id: string): Promise {
+ return await this._ptero.getClientServer(id);
+ };
+
+ public async restartNeko(): Promise {
+ const server = await this.getServer(process.env.PTERO_SERVER as string);
+ await server.restart();
+ };
+
+ public async stopNeko(): Promise {
+ const server = await this.getServer(process.env.PTERO_SERVER as string);
+ await server.stop();
+ }
+}
\ No newline at end of file
diff --git a/src/struct/timings.ts b/src/struct/timings.ts
new file mode 100644
index 0000000..b377d39
--- /dev/null
+++ b/src/struct/timings.ts
@@ -0,0 +1,111 @@
+import Client from './client';
+import { Timing } from './typings';
+import { QueueOptions } from './typings';
+
+export class Queue {
+ public queue: Array = [];
+ public interval: number;
+
+ public constructor(opts: QueueOptions) {
+ this.interval = opts.interval;
+ };
+
+ public async add(callback: Function): Promise {
+ this.queue.push(callback);
+ };
+
+ public async run(): Promise {
+ for (const callback of this.queue) {
+ await callback();
+ await new Promise(r => setTimeout(r, this.interval));
+ }
+ };
+
+ public async clear(): Promise {
+ this.queue = [];
+ };
+
+ public async restart(): Promise {
+ await this.clear();
+ await this.run();
+ };
+
+ public async stop(): Promise {
+ await this.clear();
+ };
+
+ public async get(): Promise> {
+ return this.queue;
+ };
+
+ public async getLength(): Promise {
+ return this.queue.length;
+ };
+
+ public async getFirst(): Promise {
+ return this.queue[0];
+ };
+
+ public async getLast(): Promise {
+ return this.queue[this.queue.length - 1];
+ };
+
+ public async getAt(index: number): Promise {
+ return this.queue[index];
+ };
+}
+
+export class Timings {
+ public client: Client;
+ public _queue: Queue;
+ public _timings: Array = [];
+
+ public constructor(client: Client) {
+ this.client = client;
+ this._queue = new Queue({ interval: client.config.queueInterval });
+ };
+
+ public async add(timing: Timing): Promise {
+ this._timings.push(timing);
+ };
+
+ public async run(): Promise {
+ for (const timing of this._timings) {
+ this._timings[this._timings.indexOf(timing)].interval = setInterval(async () => {
+ await timing.cb();
+ }, timing.time);
+ }
+ };
+
+ public async clear(): Promise {
+ this._timings = [];
+ };
+
+ public async restart(): Promise {
+ await this.stop();
+ await this.run();
+ };
+
+ public async stop(): Promise {
+ for (const timing of this._timings) {
+ clearInterval(timing.interval);
+ }
+ await this.clear();
+ };
+
+ public async get(): Promise> {
+ return this._timings;
+ };
+
+ public async getLength(): Promise {
+ return this._timings.length;
+ };
+
+ public async getTiming(name: string): Promise {
+ return this._timings.find(timing => timing.name === name);
+ };
+
+ public async getFirst(): Promise {
+ return this._timings[0];
+ };
+}
\ No newline at end of file
diff --git a/src/struct/typings.ts b/src/struct/typings.ts
new file mode 100644
index 0000000..838186e
--- /dev/null
+++ b/src/struct/typings.ts
@@ -0,0 +1,63 @@
+import {APIApplicationCommandOption, ColorResolvable} from 'discord.js';
+import { Document } from 'mongoose';
+import Guild from '../models/guild';
+
+export type CommandOptions = APIApplicationCommandOption[] | undefined[] | Array;
+
+export interface IInfos {
+ name: string;
+ description: string;
+ category: string;
+ usage: string;
+ ownerOnly: boolean;
+ nsfw: boolean;
+ options: CommandOptions;
+ defer: boolean;
+}
+
+export interface IEventInfos {
+ name: string;
+ once: boolean;
+}
+
+export interface IGuild extends Document {
+ id: string;
+}
+
+export interface ISchemas {
+ Guild: typeof Guild;
+}
+
+export type SlashSyncOptions = {
+ guildId?: string;
+ debug?: boolean;
+}
+
+export interface Colors {
+ main: ColorResolvable;
+ error: ColorResolvable;
+ success: ColorResolvable;
+ bug: ColorResolvable;
+ warning: ColorResolvable;
+ info: ColorResolvable;
+ loading: ColorResolvable;
+ debug: ColorResolvable;
+ secondary: ColorResolvable;
+}
+
+export interface Timing {
+ time: number;
+ name: string;
+ interval?: NodeJS.Timeout;
+ cb: () => Promise | void;
+}
+
+export interface QueueOptions {
+ interval: number;
+}
+
+export interface APIStatusAnswer {
+ status: number;
+ version: string;
+ statusString: string;
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..70198e9
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,109 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ "rootDir": "./src", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ "outDir": "./lib", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}