From 777c260ca8234587de114f9ce723777ab3774380 Mon Sep 17 00:00:00 2001 From: Delta <47292121+ProtogenDelta@users.noreply.github.com> Date: Mon, 30 Jan 2023 09:27:26 +1000 Subject: [PATCH 1/4] Interactions Overhaul, Autocomplete Support Updated to discord.js@latest (v14.7.1), unified command interactions, added autocomplete support. --- TODO.md | 5 +- dockerfile | 4 +- package-lock.json | 317 ++++++++++++++--------- package.json | 3 +- src/classes/Client.ts | 18 +- src/classes/Command.ts | 27 +- src/classes/Module.ts | 6 +- src/commands/chat/autocomplete.ts | 39 +++ src/commands/chat/index.ts | 1 - src/commands/chat/info/ping.ts | 19 -- src/commands/chat/ping.ts | 20 ++ src/commands/index.ts | 9 + src/commands/message/index.ts | 1 - src/commands/message/info.ts | 25 ++ src/commands/message/info/info.ts | 24 -- src/commands/user/index.ts | 1 - src/commands/user/{info => }/info.ts | 11 +- src/config.example.ts | 2 +- src/modules/core/logger.ts | 13 +- src/modules/core/status.ts | 14 +- src/modules/index.ts | 17 +- src/modules/interactions/autocomplete.ts | 22 ++ src/modules/interactions/chat.ts | 43 --- src/modules/interactions/command.ts | 57 ++++ src/modules/interactions/message.ts | 43 --- src/modules/interactions/publish.ts | 6 +- src/modules/interactions/user.ts | 43 --- 27 files changed, 424 insertions(+), 366 deletions(-) create mode 100644 src/commands/chat/autocomplete.ts delete mode 100644 src/commands/chat/index.ts delete mode 100644 src/commands/chat/info/ping.ts create mode 100644 src/commands/chat/ping.ts create mode 100644 src/commands/index.ts delete mode 100644 src/commands/message/index.ts create mode 100644 src/commands/message/info.ts delete mode 100644 src/commands/message/info/info.ts delete mode 100644 src/commands/user/index.ts rename src/commands/user/{info => }/info.ts (66%) create mode 100644 src/modules/interactions/autocomplete.ts delete mode 100644 src/modules/interactions/chat.ts create mode 100644 src/modules/interactions/command.ts delete mode 100644 src/modules/interactions/message.ts delete mode 100644 src/modules/interactions/user.ts diff --git a/TODO.md b/TODO.md index 0d7f370..3edbbb5 100644 --- a/TODO.md +++ b/TODO.md @@ -7,9 +7,6 @@ - [x] Chat (`/`) commands - [x] User commands - [x] Message commands - - [ ] Autocomplete + - [x] Autocomplete - [ ] Error Handling -- [ ] Permissions - - [ ] Owner - - [ ] Admin - [ ] Documentation diff --git a/dockerfile b/dockerfile index 6247ed9..a0daca4 100644 --- a/dockerfile +++ b/dockerfile @@ -10,6 +10,6 @@ ARG NODE_ENV=production ENV NODE_ENV=${NODE_ENV} WORKDIR /usr/src/app COPY package*.json . -RUN npm ci --only=production +RUN npm ci --omit=dev COPY --from=build /usr/src/app/dist ./dist -CMD ["node", "dist/index.js"] \ No newline at end of file +CMD ["node", "dist/index.js"] diff --git a/package-lock.json b/package-lock.json index 07f9c1c..aafc68b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "discord.js": "^14.3.0" + "discord.js": "^14.7.1", + "fuse.js": "^6.6.2" }, "devDependencies": { "ts-node": "^10.9.1", @@ -30,45 +31,55 @@ } }, "node_modules/@discordjs/builders": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.2.0.tgz", - "integrity": "sha512-ARy4BUTMU+S0ZI6605NDqfWO+qZqV2d/xfY32z3hVSsd9IaAKJBZ1ILTZLy87oIjW8+gUpQmk9Kt0ZP9bmmd8Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", + "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", "dependencies": { - "@sapphire/shapeshift": "^3.5.1", - "discord-api-types": "^0.37.3", + "@discordjs/util": "^0.1.0", + "@sapphire/shapeshift": "^3.7.1", + "discord-api-types": "^0.37.20", "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.1", - "tslib": "^2.4.0" + "ts-mixer": "^6.0.2", + "tslib": "^2.4.1" }, "engines": { "node": ">=16.9.0" } }, "node_modules/@discordjs/collection": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.1.0.tgz", - "integrity": "sha512-PQ2Bv6pnT7aGPCKWbvvNRww5tYCGpggIQVgpuF9TdDPeR6n6vQYxezXiLVOS9z2B62Dp4c+qepQ15SgJbLYtCQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", + "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==", "engines": { "node": ">=16.9.0" } }, "node_modules/@discordjs/rest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.1.0.tgz", - "integrity": "sha512-yCrthRTQeUyNThQEpCk7bvQJlwQmz6kU0tf3dcWBv2WX3Bncl41x7Wc+v5b5OsIxfNYq38PvVtWircu9jtYZug==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", + "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", "dependencies": { - "@discordjs/collection": "^1.0.1", + "@discordjs/collection": "^1.3.0", + "@discordjs/util": "^0.1.0", "@sapphire/async-queue": "^1.5.0", "@sapphire/snowflake": "^3.2.2", - "discord-api-types": "^0.37.3", - "file-type": "^17.1.6", - "tslib": "^2.4.0", - "undici": "^5.9.1" + "discord-api-types": "^0.37.23", + "file-type": "^18.0.0", + "tslib": "^2.4.1", + "undici": "^5.13.0" }, "engines": { "node": ">=16.9.0" } }, + "node_modules/@discordjs/util": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", + "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -104,12 +115,12 @@ } }, "node_modules/@sapphire/shapeshift": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.6.0.tgz", - "integrity": "sha512-tu2WLRdo5wotHRvsCkspg3qMiP6ETC3Q1dns1Q5V6zKUki+1itq6AbhMwohF9ZcLoYqg+Y8LkgRRtVxxTQVTBQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", + "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", "dependencies": { "fast-deep-equal": "^3.1.3", - "lodash.uniqwith": "^4.5.0" + "lodash": "^4.17.21" }, "engines": { "node": ">=v14.0.0", @@ -117,9 +128,9 @@ } }, "node_modules/@sapphire/snowflake": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz", - "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" @@ -262,6 +273,17 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "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/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -311,26 +333,27 @@ } }, "node_modules/discord-api-types": { - "version": "0.37.5", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", - "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" + "version": "0.37.30", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.30.tgz", + "integrity": "sha512-TzNF28zWV63clYW1+rbKT2+2qSI+lw/aNG3lyP2fIj5NioGPz4C+bCSvwhP3Ly3uLwL7v8FxIiu8XKGDsvuwWA==" }, "node_modules/discord.js": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.3.0.tgz", - "integrity": "sha512-CpIwoAAuELiHSgVKRMzsCADS6ZlJwAZ9RlvcJYdEgS00aW36dSvXyBgE+S3pigkc7G+jU6BEalMUWIJFveqrBQ==", + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", + "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", "dependencies": { - "@discordjs/builders": "^1.2.0", - "@discordjs/collection": "^1.1.0", - "@discordjs/rest": "^1.1.0", + "@discordjs/builders": "^1.4.0", + "@discordjs/collection": "^1.3.0", + "@discordjs/rest": "^1.4.0", + "@discordjs/util": "^0.1.0", "@sapphire/snowflake": "^3.2.2", "@types/ws": "^8.5.3", - "discord-api-types": "^0.37.3", + "discord-api-types": "^0.37.20", "fast-deep-equal": "^3.1.3", "lodash.snakecase": "^4.1.1", - "tslib": "^2.4.0", - "undici": "^5.9.1", - "ws": "^8.8.1" + "tslib": "^2.4.1", + "undici": "^5.13.0", + "ws": "^8.11.0" }, "engines": { "node": ">=16.9.0" @@ -351,16 +374,16 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/file-type": { - "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.0.tgz", + "integrity": "sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==", "dependencies": { "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0-alpha.9", - "token-types": "^5.0.0-alpha.2" + "strtok3": "^7.0.0", + "token-types": "^5.0.1" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -404,6 +427,14 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/fuse.js": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==", + "engines": { + "node": ">=10" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -536,16 +567,16 @@ "node": ">=0.12.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/lodash.uniqwith": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz", - "integrity": "sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==" - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -746,6 +777,14 @@ "source-map": "^0.6.0" } }, + "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/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -838,9 +877,9 @@ } }, "node_modules/ts-mixer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", - "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", + "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" }, "node_modules/ts-node": { "version": "10.9.1", @@ -932,9 +971,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/typescript": { "version": "4.8.2", @@ -950,9 +989,12 @@ } }, "node_modules/undici": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", - "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.16.0.tgz", + "integrity": "sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==", + "dependencies": { + "busboy": "^1.6.0" + }, "engines": { "node": ">=12.18" } @@ -975,15 +1017,15 @@ "dev": true }, "node_modules/ws": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -1024,36 +1066,43 @@ } }, "@discordjs/builders": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.2.0.tgz", - "integrity": "sha512-ARy4BUTMU+S0ZI6605NDqfWO+qZqV2d/xfY32z3hVSsd9IaAKJBZ1ILTZLy87oIjW8+gUpQmk9Kt0ZP9bmmd8Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", + "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", "requires": { - "@sapphire/shapeshift": "^3.5.1", - "discord-api-types": "^0.37.3", + "@discordjs/util": "^0.1.0", + "@sapphire/shapeshift": "^3.7.1", + "discord-api-types": "^0.37.20", "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.1", - "tslib": "^2.4.0" + "ts-mixer": "^6.0.2", + "tslib": "^2.4.1" } }, "@discordjs/collection": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.1.0.tgz", - "integrity": "sha512-PQ2Bv6pnT7aGPCKWbvvNRww5tYCGpggIQVgpuF9TdDPeR6n6vQYxezXiLVOS9z2B62Dp4c+qepQ15SgJbLYtCQ==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", + "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==" }, "@discordjs/rest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.1.0.tgz", - "integrity": "sha512-yCrthRTQeUyNThQEpCk7bvQJlwQmz6kU0tf3dcWBv2WX3Bncl41x7Wc+v5b5OsIxfNYq38PvVtWircu9jtYZug==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", + "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", "requires": { - "@discordjs/collection": "^1.0.1", + "@discordjs/collection": "^1.3.0", + "@discordjs/util": "^0.1.0", "@sapphire/async-queue": "^1.5.0", "@sapphire/snowflake": "^3.2.2", - "discord-api-types": "^0.37.3", - "file-type": "^17.1.6", - "tslib": "^2.4.0", - "undici": "^5.9.1" + "discord-api-types": "^0.37.23", + "file-type": "^18.0.0", + "tslib": "^2.4.1", + "undici": "^5.13.0" } }, + "@discordjs/util": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", + "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==" + }, "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -1082,18 +1131,18 @@ "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==" }, "@sapphire/shapeshift": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.6.0.tgz", - "integrity": "sha512-tu2WLRdo5wotHRvsCkspg3qMiP6ETC3Q1dns1Q5V6zKUki+1itq6AbhMwohF9ZcLoYqg+Y8LkgRRtVxxTQVTBQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", + "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", "requires": { "fast-deep-equal": "^3.1.3", - "lodash.uniqwith": "^4.5.0" + "lodash": "^4.17.21" } }, "@sapphire/snowflake": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz", - "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==" }, "@tokenizer/token": { "version": "0.3.0", @@ -1214,6 +1263,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1249,26 +1306,27 @@ "dev": true }, "discord-api-types": { - "version": "0.37.5", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", - "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" + "version": "0.37.30", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.30.tgz", + "integrity": "sha512-TzNF28zWV63clYW1+rbKT2+2qSI+lw/aNG3lyP2fIj5NioGPz4C+bCSvwhP3Ly3uLwL7v8FxIiu8XKGDsvuwWA==" }, "discord.js": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.3.0.tgz", - "integrity": "sha512-CpIwoAAuELiHSgVKRMzsCADS6ZlJwAZ9RlvcJYdEgS00aW36dSvXyBgE+S3pigkc7G+jU6BEalMUWIJFveqrBQ==", + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", + "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", "requires": { - "@discordjs/builders": "^1.2.0", - "@discordjs/collection": "^1.1.0", - "@discordjs/rest": "^1.1.0", + "@discordjs/builders": "^1.4.0", + "@discordjs/collection": "^1.3.0", + "@discordjs/rest": "^1.4.0", + "@discordjs/util": "^0.1.0", "@sapphire/snowflake": "^3.2.2", "@types/ws": "^8.5.3", - "discord-api-types": "^0.37.3", + "discord-api-types": "^0.37.20", "fast-deep-equal": "^3.1.3", "lodash.snakecase": "^4.1.1", - "tslib": "^2.4.0", - "undici": "^5.9.1", - "ws": "^8.8.1" + "tslib": "^2.4.1", + "undici": "^5.13.0", + "ws": "^8.11.0" } }, "dynamic-dedupe": { @@ -1286,13 +1344,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "file-type": { - "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.0.tgz", + "integrity": "sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==", "requires": { "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0-alpha.9", - "token-types": "^5.0.0-alpha.2" + "strtok3": "^7.0.0", + "token-types": "^5.0.1" } }, "fill-range": { @@ -1323,6 +1381,11 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "fuse.js": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==" + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1414,16 +1477,16 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" }, - "lodash.uniqwith": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz", - "integrity": "sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==" - }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1557,6 +1620,11 @@ "source-map": "^0.6.0" } }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1617,9 +1685,9 @@ "dev": true }, "ts-mixer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", - "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", + "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" }, "ts-node": { "version": "10.9.1", @@ -1673,9 +1741,9 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "typescript": { "version": "4.8.2", @@ -1684,9 +1752,12 @@ "dev": true }, "undici": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", - "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==" + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.16.0.tgz", + "integrity": "sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==", + "requires": { + "busboy": "^1.6.0" + } }, "util-deprecate": { "version": "1.0.2", @@ -1706,9 +1777,9 @@ "dev": true }, "ws": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "requires": {} }, "xtend": { diff --git a/package.json b/package.json index 6302b5e..4c67595 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "typescript": "^4.8.2" }, "dependencies": { - "discord.js": "^14.3.0" + "discord.js": "^14.7.1", + "fuse.js": "^6.6.2" } } diff --git a/src/classes/Client.ts b/src/classes/Client.ts index fc5baf6..4b8d6ce 100644 --- a/src/classes/Client.ts +++ b/src/classes/Client.ts @@ -1,14 +1,15 @@ -import { Client } from "discord.js"; +import { Client, GatewayIntentBits } from "discord.js"; import config from "../config"; import * as modules from "../modules"; +import logger from "../modules/core/logger"; export default class ExtendedClient extends Client { constructor() { super({ intents: [ - "Guilds", - "GuildBans", - "GuildMembers" + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildModeration, + GatewayIntentBits.GuildMembers, ] }); } @@ -20,17 +21,12 @@ export default class ExtendedClient extends Client { } loadModules() { - // console.log("[🧩] Loading modules..."); - let loaded = 0; Object.values(modules).forEach((v) => { try { new v.default(this); - loaded++; - // console.log(`[🧩] Loaded module ${v.default.id}`); } catch (err) { - console.log(`[⚠️] Failed to load module ${v.default.id}: ${err}`); + logger.warn(`Failed to load module ${v.default.id}: ${err}`); } }); - // console.log(`[🧩] Loaded ${loaded} modules`); } -} \ No newline at end of file +} diff --git a/src/classes/Command.ts b/src/classes/Command.ts index 92d3a8a..411cefc 100644 --- a/src/classes/Command.ts +++ b/src/classes/Command.ts @@ -1,22 +1,15 @@ -import { ApplicationCommandDataResolvable, ChatInputApplicationCommandData, ChatInputCommandInteraction, Interaction, MessageApplicationCommandData, MessageContextMenuCommandInteraction, UserApplicationCommandData, UserContextMenuCommandInteraction } from "discord.js"; +import { ApplicationCommandDataResolvable, AutocompleteInteraction, BaseInteraction } from "discord.js"; import ExtendedClient from "./Client"; -export interface Command { - data: ApplicationCommandDataResolvable; - run(interaction: Interaction, client?: ExtendedClient): void | Promise; -} - -export interface ChatCommand extends Command { - data: ChatInputApplicationCommandData; - run(interaction: ChatInputCommandInteraction, client?: ExtendedClient): void | Promise; +export enum CommandType { + CHAT, + MESSAGE, + USER, } -export interface UserCtxCommand extends Command { - data: UserApplicationCommandData; - run(interaction: UserContextMenuCommandInteraction, client?: ExtendedClient): void | Promise; +export interface Command { + type: CommandType; + data: ApplicationCommandDataResolvable; + run(interaction: BaseInteraction, client?: ExtendedClient): any; + autocomplete?(interaction: AutocompleteInteraction): any; } - -export interface MessageCtxCommand extends Command { - data: MessageApplicationCommandData; - run(interaction: MessageContextMenuCommandInteraction, client?: ExtendedClient): void | Promise; -} \ No newline at end of file diff --git a/src/classes/Module.ts b/src/classes/Module.ts index b4dd306..20479dd 100644 --- a/src/classes/Module.ts +++ b/src/classes/Module.ts @@ -2,8 +2,8 @@ import ExtendedClient from "./Client"; export default class Module { public static id = "core.default"; - - constructor(client: ExtendedClient) { + + constructor(_client: ExtendedClient) { throw new Error("Module does not implement a constructor."); } -} \ No newline at end of file +} diff --git a/src/commands/chat/autocomplete.ts b/src/commands/chat/autocomplete.ts new file mode 100644 index 0000000..e835b6c --- /dev/null +++ b/src/commands/chat/autocomplete.ts @@ -0,0 +1,39 @@ +import { ApplicationCommandOptionType, AutocompleteInteraction, CacheType, ChatInputApplicationCommandData, ChatInputCommandInteraction } from "discord.js"; +import Client from "../../classes/Client"; +import { Command, CommandType } from "../../classes/Command"; +import Fuse from 'fuse.js'; + +const colors = ["Red", "Green", "Blue"]; +const completions = new Fuse(colors); + +export default class AutocompleteCommand implements Command { + type = CommandType.CHAT; + data: ChatInputApplicationCommandData = { + name: "autocomplete", + description: "Test command for autocomplete feature.", + dmPermission: true, + options: [ + { + type: ApplicationCommandOptionType.String, + name: "color", + nameLocalizations: { + "en-GB": "colour", + }, + description: "The color to select.", + descriptionLocalizations: { + "en-GB": "The colour to select." + }, + autocomplete: true + } + ] + } + + run(interaction: ChatInputCommandInteraction, _client: Client) { + interaction.reply({ content: interaction.options.getString("color", true), ephemeral: true }); + } + + autocomplete(interaction: AutocompleteInteraction) { + let matches = completions.search(interaction.options.getString("color", true)); + interaction.respond(matches.map((v) => { return { name: v.item, value: v.item }; })); + } +} diff --git a/src/commands/chat/index.ts b/src/commands/chat/index.ts deleted file mode 100644 index ee5c360..0000000 --- a/src/commands/chat/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as ping from "./info/ping"; \ No newline at end of file diff --git a/src/commands/chat/info/ping.ts b/src/commands/chat/info/ping.ts deleted file mode 100644 index 2d85416..0000000 --- a/src/commands/chat/info/ping.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ChatInputApplicationCommandData, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; -import ExtendedClient from "../../../classes/Client"; -import { ChatCommand } from "../../../classes/Command"; - -export default class PingCommand implements ChatCommand { - data: ChatInputApplicationCommandData = { - name: "ping", - description: "Displays the bot's API Latency." - }; - - async run(interaction: ChatInputCommandInteraction, client: ExtendedClient) { - let embed = new EmbedBuilder() - .setTitle("🏓 Ping") - .setDescription("```yaml\nAPI Latency: "+client.ws.ping+"ms\n```") - .setColor("Random") - .setTimestamp() - interaction.reply({embeds: [embed]}) - } -} \ No newline at end of file diff --git a/src/commands/chat/ping.ts b/src/commands/chat/ping.ts new file mode 100644 index 0000000..78b4e1f --- /dev/null +++ b/src/commands/chat/ping.ts @@ -0,0 +1,20 @@ +import { ChatInputApplicationCommandData, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; +import Client from "../../classes/Client"; +import { Command, CommandType } from "../../classes/Command"; + +export default class PingCommand implements Command { + type = CommandType.CHAT; + data: ChatInputApplicationCommandData = { + name: "ping", + description: "Displays the bot's API Latency." + }; + + async run(interaction: ChatInputCommandInteraction, client: Client) { + let embed = new EmbedBuilder() + .setTitle("🏓 Ping") + .setDescription("```yaml\nAPI Latency: " + client.ws.ping + "ms\n```") + .setColor("Random") + .setTimestamp() + await interaction.reply({ embeds: [embed] }) + } +} diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 0000000..b5d986f --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,9 @@ +// Chat Commands +export * as c_ping from "./chat/ping"; +export * as c_autocomplete from "./chat/autocomplete"; + +// Message Commands +export * as m_info from "./message/info"; + +// User Commands +export * as u_info from "./user/info"; diff --git a/src/commands/message/index.ts b/src/commands/message/index.ts deleted file mode 100644 index 4eaef27..0000000 --- a/src/commands/message/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as info from "./info/info"; \ No newline at end of file diff --git a/src/commands/message/info.ts b/src/commands/message/info.ts new file mode 100644 index 0000000..f00ad99 --- /dev/null +++ b/src/commands/message/info.ts @@ -0,0 +1,25 @@ +import { ApplicationCommandType, EmbedBuilder, MessageApplicationCommandData, MessageContextMenuCommandInteraction } from "discord.js"; +import { Command, CommandType } from "../../classes/Command"; + +export default class UserInfoCtxCommand implements Command { + type = CommandType.MESSAGE; + data: MessageApplicationCommandData = { + type: ApplicationCommandType.Message, + name: "Message Info", + dmPermission: false + }; + + run(interaction: MessageContextMenuCommandInteraction) { + let embed = new EmbedBuilder() + .setAuthor({ name: interaction.user.tag }) + .setDescription(interaction.targetMessage.cleanContent) + .setThumbnail(interaction.targetMessage.member?.displayAvatarURL() ?? interaction.targetMessage.author.displayAvatarURL()) + .addFields({ name: "Information", value: "```yaml\n" + `ID: ${interaction.targetId}\nSent: ${interaction.targetMessage.createdAt.toUTCString()}\nEdited: ${interaction.targetMessage.editedAt?.toUTCString() ?? "(never)"}` + "\n```" }) + .setColor("Random") + .setTimestamp(); + interaction.reply({ + embeds: [embed], + ephemeral: true + }); + } +} diff --git a/src/commands/message/info/info.ts b/src/commands/message/info/info.ts deleted file mode 100644 index 3f1eb92..0000000 --- a/src/commands/message/info/info.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ApplicationCommandType, EmbedBuilder, GuildMember, MessageApplicationCommandData, MessageContextMenuCommandInteraction } from "discord.js"; -import { MessageCtxCommand } from "../../../classes/Command"; - -export default class UserInfoCtxCommand implements MessageCtxCommand { - data: MessageApplicationCommandData = { - type: ApplicationCommandType.Message, - name: "Message Info", - dmPermission: false - }; - - run(interaction: MessageContextMenuCommandInteraction) { - let embed = new EmbedBuilder() - .setAuthor({name: interaction.user.tag}) - .setDescription(interaction.targetMessage.cleanContent) - .setThumbnail(interaction.targetMessage.member?.displayAvatarURL() ?? interaction.targetMessage.author.displayAvatarURL()) - .addFields({name: "Information", value: "```yaml\n"+`ID: ${interaction.targetId}\nSent: ${interaction.targetMessage.createdAt.toUTCString()}\nEdited: ${interaction.targetMessage.editedAt?.toUTCString() ?? "(never)"}`+"\n```"}) - .setColor("Random") - .setTimestamp(); - interaction.reply({ - embeds: [embed], - ephemeral: true - }); - } -} \ No newline at end of file diff --git a/src/commands/user/index.ts b/src/commands/user/index.ts deleted file mode 100644 index 4eaef27..0000000 --- a/src/commands/user/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as info from "./info/info"; \ No newline at end of file diff --git a/src/commands/user/info/info.ts b/src/commands/user/info.ts similarity index 66% rename from src/commands/user/info/info.ts rename to src/commands/user/info.ts index 34e47d2..13bbad3 100644 --- a/src/commands/user/info/info.ts +++ b/src/commands/user/info.ts @@ -1,7 +1,8 @@ import { ApplicationCommandType, EmbedBuilder, GuildMember, UserApplicationCommandData, UserContextMenuCommandInteraction } from "discord.js"; -import { UserCtxCommand } from "../../../classes/Command"; +import { Command, CommandType } from "../../classes/Command"; -export default class UserInfoCtxCommand implements UserCtxCommand { +export default class UserInfoCtxCommand implements Command { + type = CommandType.USER; data: UserApplicationCommandData = { type: ApplicationCommandType.User, name: "User Info", @@ -11,9 +12,9 @@ export default class UserInfoCtxCommand implements UserCtxCommand { run(interaction: UserContextMenuCommandInteraction) { let member = interaction.targetMember as GuildMember; let embed = new EmbedBuilder() - .setAuthor({name: member.displayName}) + .setAuthor({ name: member.displayName }) .setThumbnail(member.displayAvatarURL()) - .setDescription("```yaml\n"+`ID: ${member.id}\nUsername: ${member.user.tag}`+"\n```") + .setDescription("```yaml\n" + `ID: ${member.id}\nUsername: ${member.user.tag}` + "\n```") .setColor("Random") .setTimestamp(); interaction.reply({ @@ -21,4 +22,4 @@ export default class UserInfoCtxCommand implements UserCtxCommand { ephemeral: true }); } -} \ No newline at end of file +} diff --git a/src/config.example.ts b/src/config.example.ts index 7539a08..3cf9843 100644 --- a/src/config.example.ts +++ b/src/config.example.ts @@ -6,4 +6,4 @@ export default { type Config = { TOKEN: string; -} \ No newline at end of file +} diff --git a/src/modules/core/logger.ts b/src/modules/core/logger.ts index 061b66f..59d5cea 100644 --- a/src/modules/core/logger.ts +++ b/src/modules/core/logger.ts @@ -1,3 +1,4 @@ +import { Events } from "discord.js"; import ExtendedClient from "../../classes/Client"; import Module from "../../classes/Module"; @@ -5,10 +6,10 @@ export default class LoggerModule implements Module { public static id = "core.logger"; constructor(client: ExtendedClient) { - client.on("ready", () => this.onReady(client)); - client.on("error", LoggerModule.error); - client.on("warn", LoggerModule.warn); - // client.on("debug", LoggerModule.debug); + client.on(Events.ClientReady, () => this.onReady(client)); + client.on(Events.Error, LoggerModule.error); + client.on(Events.Warn, LoggerModule.warn); + // client.on(Events.Debug, LoggerModule.debug); } private onReady(client: ExtendedClient) { @@ -16,7 +17,7 @@ export default class LoggerModule implements Module { } public static error(err: Error) { - console.log(`[🛑] ${err.name}: ${err.message}`+err.stack?`\n\t${err.stack?.replaceAll("\n","\n\t")}`:""); + console.log(`[🛑] ${err.name}: ${err.message}` + err.stack ? `\n\t${err.stack?.replaceAll("\n", "\n\t")}` : ""); } public static warn(msg: string) { @@ -26,4 +27,4 @@ export default class LoggerModule implements Module { public static debug(msg: string) { console.log(`[🐛] ${msg}`); } -} \ No newline at end of file +} diff --git a/src/modules/core/status.ts b/src/modules/core/status.ts index 6c0f999..85752df 100644 --- a/src/modules/core/status.ts +++ b/src/modules/core/status.ts @@ -1,25 +1,25 @@ -import { ActivityOptions, ActivityType } from "discord.js"; +import { ActivityOptions, ActivityType, Events } from "discord.js"; import ExtendedClient from "../../classes/Client"; import Module from "../../classes/Module"; const activities: ActivityOptions[] = [ - {type: ActivityType.Watching, name: "over the chats."}, - {type: ActivityType.Listening, name: "various beeps and boops."} + { type: ActivityType.Watching, name: "over the chats." }, + { type: ActivityType.Listening, name: "various beeps and boops." } ] export default class LoggerModule implements Module { public static id = "core.status"; constructor(client: ExtendedClient) { - client.on("ready", () => this.onReady(client)); + client.on(Events.ClientReady, () => this.onReady(client)); } onReady(client: ExtendedClient) { - client.user?.setActivity({type: ActivityType.Watching, name: "myself start up..."}); + client.user?.setActivity({ type: ActivityType.Watching, name: "myself start up..." }); let act = 0; setInterval(() => { client.user?.setActivity(activities[act]); - if(++act>=activities.length) act = 0; + if (++act >= activities.length) act = 0; }, 30_000); } -} \ No newline at end of file +} diff --git a/src/modules/index.ts b/src/modules/index.ts index 5480343..17ddb53 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -1,12 +1,13 @@ -//? Internal +// Internal export * as logger from "./core/logger"; export * as status from "./core/status"; -//? Command Handlers -export * as chat from "./interactions/chat"; -export * as user from "./interactions/user"; -export * as message from "./interactions/message"; +// Command Handler +export * as message from "./interactions/command"; -//? Command Publisher -//* Always keep this last, to ensure command publishing works as expected. -export * as publish from "./interactions/publish"; \ No newline at end of file +// Autocomplete +export * as autocomplete from "./interactions/autocomplete"; + +// Command Publisher +// Always keep this last, to ensure command publishing works as expected. +export * as publish from "./interactions/publish"; diff --git a/src/modules/interactions/autocomplete.ts b/src/modules/interactions/autocomplete.ts new file mode 100644 index 0000000..927558b --- /dev/null +++ b/src/modules/interactions/autocomplete.ts @@ -0,0 +1,22 @@ +import { BaseInteraction, Events } from "discord.js"; +import Client from "../../classes/Client"; +import Module from "../../classes/Module" +import { chatCommands } from "./command"; + +export default class AutocompleteModule implements Module { + public static id = "interactions.autocomplete"; + constructor(client: Client) { + client.on(Events.InteractionCreate, (i) => this.handleInteraction(i)); + } + + private handleInteraction(interaction: BaseInteraction) { + if (interaction.isAutocomplete()) { + let completion = chatCommands.get(interaction.commandName)?.autocomplete; + if (completion !== undefined) { + completion.call(null, interaction); + } else { + interaction.respond([]); + } + } + } +} diff --git a/src/modules/interactions/chat.ts b/src/modules/interactions/chat.ts deleted file mode 100644 index 88f3cee..0000000 --- a/src/modules/interactions/chat.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Interaction } from "discord.js"; -import ExtendedClient from "../../classes/Client"; -import { ChatCommand } from "../../classes/Command"; -import Module from "../../classes/Module"; -import * as chatCommands from "../../commands/chat"; -import { publish } from "./publish"; - -export default class ChatInteractionModule implements Module { - public static id = "interactions.chat"; - private commands: Map = new Map(); - - constructor(client: ExtendedClient) { - this.loadCommands(); - this.publishCommands(); - client.on("interactionCreate", (i) => this.handleInteraction(i, client)); - } - - private handleInteraction(interaction: Interaction, client: ExtendedClient) { - if(!interaction.isChatInputCommand()) return; - let command = this.commands.get(interaction.commandName); - command?.run(interaction, client); - } - - private loadCommands() { - // console.log("[🧩] Loading commands..."); - let loaded = 0; - Object.values(chatCommands).forEach((v) => { - try { - let command = new v.default(); - this.commands.set(command.data.name, command); - loaded++; - // console.log(`[🧩] Loaded command ${command.data.name}`); - } catch (err) { - console.log(`[⚠️] Failed to load command: ${err}`); - } - }); - // console.log(`[🧩] Loaded ${loaded} commands`); - } - - private publishCommands() { - this.commands.forEach(publish); - } -} \ No newline at end of file diff --git a/src/modules/interactions/command.ts b/src/modules/interactions/command.ts new file mode 100644 index 0000000..d55b254 --- /dev/null +++ b/src/modules/interactions/command.ts @@ -0,0 +1,57 @@ +import { Events, Interaction } from "discord.js"; +import logger from "../core/logger"; +import ExtendedClient from "../../classes/Client"; +import { Command, CommandType } from "../../classes/Command"; +import Module from "../../classes/Module"; +import * as commands from "../../commands"; +import { publish } from "./publish"; + +export const chatCommands: Map = new Map(); +export const messageCommands: Map = new Map(); +export const userCommands: Map = new Map(); + +export default class CommandInteractionModule implements Module { + public static id = "interactions.chat"; + + constructor(client: ExtendedClient) { + this.loadCommands(); + this.publishCommands(); + client.on(Events.InteractionCreate, (i) => this.handleInteraction(i, client)); + } + + private handleInteraction(interaction: Interaction, client: ExtendedClient) { + if (interaction.isChatInputCommand()) + chatCommands.get(interaction.commandName)?.run(interaction, client); + else if (interaction.isMessageContextMenuCommand()) + messageCommands.get(interaction.commandName)?.run(interaction, client); + else if (interaction.isUserContextMenuCommand()) + userCommands.get(interaction.commandName)?.run(interaction, client); + } + + private loadCommands() { + Object.values(commands).forEach((v) => { + try { + let command = new v.default(); + switch (command.type) { + case CommandType.CHAT: + chatCommands.set(command.data.name, command as Command); + break; + case CommandType.MESSAGE: + messageCommands.set(command.data.name, command as Command); + break; + case CommandType.USER: + userCommands.set(command.data.name, command as Command); + break; + } + } catch (err) { + logger.warn(`Failed to load command: ${err}`); + } + }); + } + + private publishCommands() { + chatCommands.forEach(publish); + messageCommands.forEach(publish); + userCommands.forEach(publish); + } +} diff --git a/src/modules/interactions/message.ts b/src/modules/interactions/message.ts deleted file mode 100644 index 124a8c4..0000000 --- a/src/modules/interactions/message.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Interaction } from "discord.js"; -import ExtendedClient from "../../classes/Client"; -import { MessageCtxCommand } from "../../classes/Command"; -import Module from "../../classes/Module"; -import * as chatCommands from "../../commands/message"; -import { publish } from "./publish"; - -export default class MessageInteractionModule implements Module { - public static id = "interactions.message"; - private commands: Map = new Map(); - - constructor(client: ExtendedClient) { - this.loadCommands(); - this.publishCommands(); - client.on("interactionCreate", (i) => this.handleInteraction(i, client)); - } - - private handleInteraction(interaction: Interaction, client: ExtendedClient) { - if(!interaction.isMessageContextMenuCommand()) return; - let command = this.commands.get(interaction.commandName); - command?.run(interaction, client); - } - - private loadCommands() { - // console.log("[🧩] Loading commands..."); - let loaded = 0; - Object.values(chatCommands).forEach((v) => { - try { - let command = new v.default(); - this.commands.set(command.data.name, command); - loaded++; - // console.log(`[🧩] Loaded command ${command.data.name}`); - } catch (err) { - console.log(`[⚠️] Failed to load command: ${err}`); - } - }); - // console.log(`[🧩] Loaded ${loaded} commands`); - } - - private publishCommands() { - this.commands.forEach(publish); - } -} \ No newline at end of file diff --git a/src/modules/interactions/publish.ts b/src/modules/interactions/publish.ts index 4ee5c6b..fe3e253 100644 --- a/src/modules/interactions/publish.ts +++ b/src/modules/interactions/publish.ts @@ -1,4 +1,4 @@ -import { ApplicationCommandDataResolvable } from "discord.js"; +import { ApplicationCommandDataResolvable, Events } from "discord.js"; import ExtendedClient from "../../classes/Client"; import { Command } from "../../classes/Command"; import Module from "../../classes/Module"; @@ -12,11 +12,11 @@ export default class PublishCommandsModule implements Module { public static id = "interactions.publish"; constructor(client: ExtendedClient) { - client.on("ready", () => this.publish(client)); + client.on(Events.ClientReady, () => this.publish(client)); } private publish(client: ExtendedClient) { client.application?.commands.set(commands); console.log(`[✅] Published ${commands.length} commands`); } -} \ No newline at end of file +} diff --git a/src/modules/interactions/user.ts b/src/modules/interactions/user.ts deleted file mode 100644 index c636ff4..0000000 --- a/src/modules/interactions/user.ts +++ /dev/null @@ -1,43 +0,0 @@ -import ExtendedClient from "../../classes/Client"; -import { UserCtxCommand } from "../../classes/Command"; -import Module from "../../classes/Module"; -import { publish } from "./publish"; -import * as userCommands from "../../commands/user"; -import { Interaction } from "discord.js"; - -export default class UserInteractionModule implements Module { - public static id = "interactions.user"; - private commands: Map = new Map(); - - constructor(client: ExtendedClient) { - this.loadCommands(); - this.publishCommands(); - client.on("interactionCreate", (i) => this.handleInteraction(i, client)); - } - - private handleInteraction(interaction: Interaction, client: ExtendedClient) { - if(!interaction.isUserContextMenuCommand()) return; - let command = this.commands.get(interaction.commandName); - command?.run(interaction, client); - } - - private loadCommands() { - // console.log("[🧩] Loading commands..."); - let loaded = 0; - Object.values(userCommands).forEach((v) => { - try { - let command = new v.default(); - this.commands.set(command.data.name, command); - loaded++; - // console.log(`[🧩] Loaded command ${command.data.name}`); - } catch (err) { - console.log(`[⚠️] Failed to load command: ${err}`); - } - }); - // console.log(`[🧩] Loaded ${loaded} commands`); - } - - private publishCommands() { - this.commands.forEach(publish); - } -} \ No newline at end of file From 4b41c0ae48c432f02a41f63c3e080071aa72a456 Mon Sep 17 00:00:00 2001 From: Delta <47292121+ProtogenDelta@users.noreply.github.com> Date: Mon, 30 Jan 2023 18:39:04 +1000 Subject: [PATCH 2/4] Error Handling Added basic error handling for Commands and Autocomplete, updated command run and autocomplete return types. --- src/classes/Command.ts | 4 +-- src/commands/chat/autocomplete.ts | 18 ++++++----- src/modules/interactions/autocomplete.ts | 20 +++++++++---- src/modules/interactions/command.ts | 38 +++++++++++++++++------- src/utils/autocomplete.ts | 5 ++++ 5 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 src/utils/autocomplete.ts diff --git a/src/classes/Command.ts b/src/classes/Command.ts index 411cefc..5ae78b2 100644 --- a/src/classes/Command.ts +++ b/src/classes/Command.ts @@ -10,6 +10,6 @@ export enum CommandType { export interface Command { type: CommandType; data: ApplicationCommandDataResolvable; - run(interaction: BaseInteraction, client?: ExtendedClient): any; - autocomplete?(interaction: AutocompleteInteraction): any; + run(interaction: BaseInteraction, client?: ExtendedClient): void | Promise; + autocomplete?(interaction: AutocompleteInteraction): void | Promise; } diff --git a/src/commands/chat/autocomplete.ts b/src/commands/chat/autocomplete.ts index e835b6c..c247330 100644 --- a/src/commands/chat/autocomplete.ts +++ b/src/commands/chat/autocomplete.ts @@ -1,7 +1,8 @@ -import { ApplicationCommandOptionType, AutocompleteInteraction, CacheType, ChatInputApplicationCommandData, ChatInputCommandInteraction } from "discord.js"; +import { ApplicationCommandOptionType, AutocompleteInteraction, ChatInputApplicationCommandData, ChatInputCommandInteraction } from "discord.js"; import Client from "../../classes/Client"; import { Command, CommandType } from "../../classes/Command"; import Fuse from 'fuse.js'; +import { stringToOption } from "../../utils/autocomplete"; const colors = ["Red", "Green", "Blue"]; const completions = new Fuse(colors); @@ -23,17 +24,20 @@ export default class AutocompleteCommand implements Command { descriptionLocalizations: { "en-GB": "The colour to select." }, - autocomplete: true + autocomplete: true, + required: true } ] } - run(interaction: ChatInputCommandInteraction, _client: Client) { - interaction.reply({ content: interaction.options.getString("color", true), ephemeral: true }); + async run(interaction: ChatInputCommandInteraction, _client: Client) { + await interaction.reply({ content: interaction.options.getString("color", true), ephemeral: true }); } - autocomplete(interaction: AutocompleteInteraction) { - let matches = completions.search(interaction.options.getString("color", true)); - interaction.respond(matches.map((v) => { return { name: v.item, value: v.item }; })); + async autocomplete(interaction: AutocompleteInteraction) { + let text = interaction.options.getFocused(); + if (text.length == 0) return await interaction.respond(colors.map(stringToOption)); + let matches = completions.search(text); + await interaction.respond(matches.map(v => stringToOption(v.item))); } } diff --git a/src/modules/interactions/autocomplete.ts b/src/modules/interactions/autocomplete.ts index 927558b..90e3dc0 100644 --- a/src/modules/interactions/autocomplete.ts +++ b/src/modules/interactions/autocomplete.ts @@ -1,4 +1,5 @@ -import { BaseInteraction, Events } from "discord.js"; +import { AutocompleteInteraction, BaseInteraction, Events } from "discord.js"; +import logger from "../core/logger"; import Client from "../../classes/Client"; import Module from "../../classes/Module" import { chatCommands } from "./command"; @@ -12,11 +13,20 @@ export default class AutocompleteModule implements Module { private handleInteraction(interaction: BaseInteraction) { if (interaction.isAutocomplete()) { let completion = chatCommands.get(interaction.commandName)?.autocomplete; - if (completion !== undefined) { - completion.call(null, interaction); - } else { - interaction.respond([]); + try { + if (completion !== undefined) { + completion(interaction)?.catch((e: unknown) => this.handleError(interaction, e)); + } else { + interaction.respond([]).catch((e: unknown) => this.handleError(interaction, e)); + } + } catch (err) { + this.handleError(interaction, err); } } } + + private handleError(interaction: AutocompleteInteraction, err: unknown) { + logger.warn(`Error Autocompleting ${interaction.commandName}: ${err}`); + if (!interaction.responded) interaction.respond([]).catch((e: unknown) => logger.warn(`Failover, unable to reply: ${e}`)); + } } diff --git a/src/modules/interactions/command.ts b/src/modules/interactions/command.ts index d55b254..bc499c7 100644 --- a/src/modules/interactions/command.ts +++ b/src/modules/interactions/command.ts @@ -1,4 +1,4 @@ -import { Events, Interaction } from "discord.js"; +import { CommandInteraction, Events, Interaction } from "discord.js"; import logger from "../core/logger"; import ExtendedClient from "../../classes/Client"; import { Command, CommandType } from "../../classes/Command"; @@ -10,6 +10,8 @@ export const chatCommands: Map = new Map(); export const messageCommands: Map = new Map(); export const userCommands: Map = new Map(); +const labels = ["Chat", "User", "Message"]; + export default class CommandInteractionModule implements Module { public static id = "interactions.chat"; @@ -20,12 +22,28 @@ export default class CommandInteractionModule implements Module { } private handleInteraction(interaction: Interaction, client: ExtendedClient) { - if (interaction.isChatInputCommand()) - chatCommands.get(interaction.commandName)?.run(interaction, client); - else if (interaction.isMessageContextMenuCommand()) - messageCommands.get(interaction.commandName)?.run(interaction, client); - else if (interaction.isUserContextMenuCommand()) - userCommands.get(interaction.commandName)?.run(interaction, client); + if (!interaction.isCommand()) return; + try { + if (interaction.isChatInputCommand()) + chatCommands.get(interaction.commandName)?.run(interaction, client)?.catch((e: unknown) => this.handleError(interaction, e)); + else if (interaction.isMessageContextMenuCommand()) + messageCommands.get(interaction.commandName)?.run(interaction, client)?.catch((e: unknown) => this.handleError(interaction, e)); + else if (interaction.isUserContextMenuCommand()) + userCommands.get(interaction.commandName)?.run(interaction, client)?.catch((e: unknown) => this.handleError(interaction, e)); + } catch (err) { + this.handleError(interaction, err); + } + } + + private handleError(interaction: CommandInteraction, err: unknown) { + logger.warn(`Error running ${labels[interaction.commandType + 1]} Command "${interaction.commandName}": ${err}`); + if (interaction.deferred) { + interaction.editReply({ content: ":warning: **Whoops!** Something went wrong while executing that command." }) + .catch((e: unknown) => logger.warn(`Failover, unable to edit reply: ${e}`)); + } else if (!interaction.replied) { + interaction.reply({ content: ":warning: **Whoops!** Something went wrong while executing that command.", ephemeral: true }) + .catch((e: unknown) => logger.warn(`Failover, unable to reply: ${e}`)); + } } private loadCommands() { @@ -34,13 +52,13 @@ export default class CommandInteractionModule implements Module { let command = new v.default(); switch (command.type) { case CommandType.CHAT: - chatCommands.set(command.data.name, command as Command); + chatCommands.set(command.data.name, command); break; case CommandType.MESSAGE: - messageCommands.set(command.data.name, command as Command); + messageCommands.set(command.data.name, command); break; case CommandType.USER: - userCommands.set(command.data.name, command as Command); + userCommands.set(command.data.name, command); break; } } catch (err) { diff --git a/src/utils/autocomplete.ts b/src/utils/autocomplete.ts new file mode 100644 index 0000000..ceba928 --- /dev/null +++ b/src/utils/autocomplete.ts @@ -0,0 +1,5 @@ +import { ApplicationCommandOptionChoiceData } from "discord.js"; + +export function stringToOption(str: string): ApplicationCommandOptionChoiceData { + return { name: str, value: str } +} From c76e5c6114c269bb03dc3080777a901915cf9b0a Mon Sep 17 00:00:00 2001 From: Delta <47292121+ProtogenDelta@users.noreply.github.com> Date: Mon, 30 Jan 2023 20:11:23 +1000 Subject: [PATCH 3/4] Modules Clean-up Re-wrote Modules and Commands systems to make types less ambiguous. --- src/classes/Client.ts | 8 ++--- src/classes/Command.ts | 4 +-- src/classes/Module.ts | 8 ++--- src/commands/index.ts | 17 +++++----- src/modules/core/logger.ts | 8 ++--- src/modules/core/status.ts | 10 +++--- src/modules/index.ts | 20 ++++++------ src/modules/interactions/autocomplete.ts | 3 +- src/modules/interactions/command.ts | 40 ++++++++++-------------- src/modules/interactions/publish.ts | 6 ++-- 10 files changed, 54 insertions(+), 70 deletions(-) diff --git a/src/classes/Client.ts b/src/classes/Client.ts index 4b8d6ce..5002c25 100644 --- a/src/classes/Client.ts +++ b/src/classes/Client.ts @@ -1,6 +1,6 @@ import { Client, GatewayIntentBits } from "discord.js"; import config from "../config"; -import * as modules from "../modules"; +import { modules } from "../modules"; import logger from "../modules/core/logger"; export default class ExtendedClient extends Client { @@ -21,11 +21,11 @@ export default class ExtendedClient extends Client { } loadModules() { - Object.values(modules).forEach((v) => { + modules.forEach((v) => { try { - new v.default(this); + new v().load(this); } catch (err) { - logger.warn(`Failed to load module ${v.default.id}: ${err}`); + logger.warn(`Failed to load ${v.name}: ${err}`); } }); } diff --git a/src/classes/Command.ts b/src/classes/Command.ts index 5ae78b2..b84eabc 100644 --- a/src/classes/Command.ts +++ b/src/classes/Command.ts @@ -1,4 +1,4 @@ -import { ApplicationCommandDataResolvable, AutocompleteInteraction, BaseInteraction } from "discord.js"; +import { ApplicationCommandData, AutocompleteInteraction, BaseInteraction } from "discord.js"; import ExtendedClient from "./Client"; export enum CommandType { @@ -9,7 +9,7 @@ export enum CommandType { export interface Command { type: CommandType; - data: ApplicationCommandDataResolvable; + data: ApplicationCommandData; run(interaction: BaseInteraction, client?: ExtendedClient): void | Promise; autocomplete?(interaction: AutocompleteInteraction): void | Promise; } diff --git a/src/classes/Module.ts b/src/classes/Module.ts index 20479dd..43d7f40 100644 --- a/src/classes/Module.ts +++ b/src/classes/Module.ts @@ -1,9 +1,5 @@ import ExtendedClient from "./Client"; -export default class Module { - public static id = "core.default"; - - constructor(_client: ExtendedClient) { - throw new Error("Module does not implement a constructor."); - } +export default interface Module { + load(client: ExtendedClient): void | Promise; } diff --git a/src/commands/index.ts b/src/commands/index.ts index b5d986f..12fc01c 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,9 +1,12 @@ -// Chat Commands -export * as c_ping from "./chat/ping"; -export * as c_autocomplete from "./chat/autocomplete"; +import { Command } from "../classes/Command"; +type Commands = { new(): Command; }[]; -// Message Commands -export * as m_info from "./message/info"; +import c_ping from "./chat/ping"; +import c_autocomplete from "./chat/autocomplete"; +export const chat: Commands = [c_ping, c_autocomplete]; -// User Commands -export * as u_info from "./user/info"; +import m_info from "./message/info"; +export const message: Commands = [m_info]; + +import u_info from "./user/info"; +export const user: Commands = [u_info]; diff --git a/src/modules/core/logger.ts b/src/modules/core/logger.ts index 59d5cea..8b92a89 100644 --- a/src/modules/core/logger.ts +++ b/src/modules/core/logger.ts @@ -1,18 +1,16 @@ import { Events } from "discord.js"; -import ExtendedClient from "../../classes/Client"; +import Client from "../../classes/Client"; import Module from "../../classes/Module"; export default class LoggerModule implements Module { - public static id = "core.logger"; - - constructor(client: ExtendedClient) { + load(client: Client) { client.on(Events.ClientReady, () => this.onReady(client)); client.on(Events.Error, LoggerModule.error); client.on(Events.Warn, LoggerModule.warn); // client.on(Events.Debug, LoggerModule.debug); } - private onReady(client: ExtendedClient) { + private onReady(client: Client) { console.log(`[📝] Logged in as ${client.user?.tag ?? "(unknown)"}`); } diff --git a/src/modules/core/status.ts b/src/modules/core/status.ts index 85752df..204df2b 100644 --- a/src/modules/core/status.ts +++ b/src/modules/core/status.ts @@ -1,5 +1,5 @@ import { ActivityOptions, ActivityType, Events } from "discord.js"; -import ExtendedClient from "../../classes/Client"; +import Client from "../../classes/Client"; import Module from "../../classes/Module"; const activities: ActivityOptions[] = [ @@ -7,14 +7,12 @@ const activities: ActivityOptions[] = [ { type: ActivityType.Listening, name: "various beeps and boops." } ] -export default class LoggerModule implements Module { - public static id = "core.status"; - - constructor(client: ExtendedClient) { +export default class StatusModule implements Module { + load(client: Client): void { client.on(Events.ClientReady, () => this.onReady(client)); } - onReady(client: ExtendedClient) { + private onReady(client: Client) { client.user?.setActivity({ type: ActivityType.Watching, name: "myself start up..." }); let act = 0; setInterval(() => { diff --git a/src/modules/index.ts b/src/modules/index.ts index 17ddb53..2b2bb08 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -1,13 +1,11 @@ -// Internal -export * as logger from "./core/logger"; -export * as status from "./core/status"; +import Module from "../classes/Module"; -// Command Handler -export * as message from "./interactions/command"; +import LoggerModule from "./core/logger"; +import StatusModule from "./core/status"; +import AutocompleteModule from "./interactions/autocomplete"; +import CommandModule from "./interactions/command"; +import PublishModule from "./interactions/publish"; -// Autocomplete -export * as autocomplete from "./interactions/autocomplete"; - -// Command Publisher -// Always keep this last, to ensure command publishing works as expected. -export * as publish from "./interactions/publish"; +// Hack: apparently, typescript doesn't think Module[] is an appropriate type. +type Modules = { new(): Module; }[]; +export const modules: Modules = [LoggerModule, StatusModule, CommandModule, AutocompleteModule, PublishModule]; diff --git a/src/modules/interactions/autocomplete.ts b/src/modules/interactions/autocomplete.ts index 90e3dc0..417c8d7 100644 --- a/src/modules/interactions/autocomplete.ts +++ b/src/modules/interactions/autocomplete.ts @@ -5,8 +5,7 @@ import Module from "../../classes/Module" import { chatCommands } from "./command"; export default class AutocompleteModule implements Module { - public static id = "interactions.autocomplete"; - constructor(client: Client) { + load(client: Client) { client.on(Events.InteractionCreate, (i) => this.handleInteraction(i)); } diff --git a/src/modules/interactions/command.ts b/src/modules/interactions/command.ts index bc499c7..89732e0 100644 --- a/src/modules/interactions/command.ts +++ b/src/modules/interactions/command.ts @@ -1,21 +1,19 @@ import { CommandInteraction, Events, Interaction } from "discord.js"; import logger from "../core/logger"; import ExtendedClient from "../../classes/Client"; -import { Command, CommandType } from "../../classes/Command"; +import { Command } from "../../classes/Command"; import Module from "../../classes/Module"; import * as commands from "../../commands"; import { publish } from "./publish"; export const chatCommands: Map = new Map(); -export const messageCommands: Map = new Map(); export const userCommands: Map = new Map(); +export const messageCommands: Map = new Map(); const labels = ["Chat", "User", "Message"]; -export default class CommandInteractionModule implements Module { - public static id = "interactions.chat"; - - constructor(client: ExtendedClient) { +export default class CommandModule implements Module { + load(client: ExtendedClient) { this.loadCommands(); this.publishCommands(); client.on(Events.InteractionCreate, (i) => this.handleInteraction(i, client)); @@ -47,23 +45,19 @@ export default class CommandInteractionModule implements Module { } private loadCommands() { - Object.values(commands).forEach((v) => { - try { - let command = new v.default(); - switch (command.type) { - case CommandType.CHAT: - chatCommands.set(command.data.name, command); - break; - case CommandType.MESSAGE: - messageCommands.set(command.data.name, command); - break; - case CommandType.USER: - userCommands.set(command.data.name, command); - break; - } - } catch (err) { - logger.warn(`Failed to load command: ${err}`); - } + commands.chat.forEach((c) => { + let command = new c(); + chatCommands.set(command.data.name, command); + }); + + commands.user.forEach((c) => { + let command = new c(); + userCommands.set(command.data.name, command); + }); + + commands.message.forEach((c) => { + let command = new c(); + messageCommands.set(command.data.name, command); }); } diff --git a/src/modules/interactions/publish.ts b/src/modules/interactions/publish.ts index fe3e253..df07e74 100644 --- a/src/modules/interactions/publish.ts +++ b/src/modules/interactions/publish.ts @@ -8,10 +8,8 @@ export function publish(command: Command) { commands.push(command.data); } -export default class PublishCommandsModule implements Module { - public static id = "interactions.publish"; - - constructor(client: ExtendedClient) { +export default class PublishModule implements Module { + load(client: ExtendedClient) { client.on(Events.ClientReady, () => this.publish(client)); } From 88731891e1604b4cfece2db8820c30a0c5c71b32 Mon Sep 17 00:00:00 2001 From: Delta <47292121+ProtogenDelta@users.noreply.github.com> Date: Mon, 30 Jan 2023 20:39:22 +1000 Subject: [PATCH 4/4] Update Todo List --- TODO.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 3edbbb5..10b0f06 100644 --- a/TODO.md +++ b/TODO.md @@ -8,5 +8,7 @@ - [x] User commands - [x] Message commands - [x] Autocomplete -- [ ] Error Handling + - [ ] Components + - [ ] Modals +- [x] Error Handling - [ ] Documentation