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. */ + } +}