From 34100aac9809bc0b38c82806a45b586e1db80e8c Mon Sep 17 00:00:00 2001 From: Atridad Lahiji <88056492+atridadl@users.noreply.github.com> Date: Sun, 28 May 2023 20:20:00 -0600 Subject: [PATCH] First iteration! --- .gitignore | 31 + .npmrc | 3 + .prettierignore | 4 + .sapphirerc.json | 16 + README.md | 24 + package.json | 39 + pnpm-lock.yaml | 722 ++++++++++++++++++ src/.env.example | 2 + src/commands/ping.ts | 46 ++ src/commands/wryna.ts | 41 + src/index.ts | 36 + src/lib/constants.ts | 4 + src/lib/setup.ts | 18 + src/lib/utils.ts | 47 ++ .../chatInputCommandDenied.ts | 23 + .../chatInputCommandSuccess.ts | 14 + .../contextMenuCommandDenied.ts | 23 + .../contextMenuCommandSuccess.ts | 14 + .../messageCommands/messageCommandDenied.ts | 12 + .../messageCommands/messageCommandSuccess.ts | 15 + src/listeners/mentionPrefixOnly.ts | 10 + src/listeners/ready.ts | 50 ++ tsconfig.json | 9 + 23 files changed, 1203 insertions(+) create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .sapphirerc.json create mode 100644 README.md create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/.env.example create mode 100644 src/commands/ping.ts create mode 100644 src/commands/wryna.ts create mode 100644 src/index.ts create mode 100644 src/lib/constants.ts create mode 100644 src/lib/setup.ts create mode 100644 src/lib/utils.ts create mode 100644 src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts create mode 100644 src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts create mode 100644 src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts create mode 100644 src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts create mode 100644 src/listeners/commands/messageCommands/messageCommandDenied.ts create mode 100644 src/listeners/commands/messageCommands/messageCommandSuccess.ts create mode 100644 src/listeners/mentionPrefixOnly.ts create mode 100644 src/listeners/ready.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..602bf0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Ignore a blackhole and the folder for development +node_modules/ +.vs/ +.idea/ +*.iml + +# Yarn files +.yarn/install-state.gz +.yarn/build-state.yml + +# Environment variables +.DS_Store + +dist/ + +# Ignore the config file (contains sensitive information such as tokens) +config.ts + +# Ignore heapsnapshot and log files +*.heapsnapshot +*.log + +# Ignore npm lockfiles file +package-lock.json + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..2cfcc66 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# pnpm only +shamefully-hoist=true +public-hoist-pattern[]=@sapphire/* \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..24ea8ac --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +.yarn/ +examples/*/dist/ diff --git a/.sapphirerc.json b/.sapphirerc.json new file mode 100644 index 0000000..3a901e3 --- /dev/null +++ b/.sapphirerc.json @@ -0,0 +1,16 @@ +{ + "projectLanguage": "ts", + "locations": { + "base": "src", + "arguments": "arguments", + "commands": "commands", + "listeners": "listeners", + "preconditions": "preconditions", + "interaction-handlers": "interaction-handlers", + "routes": "routes" + }, + "customFileTemplates": { + "enabled": false, + "location": "" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..39e0f3b --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# HimBot + +A discord bot written in Typescript. + +## It's dangerous to go alone! Take this! + +- Install Nodejs 16 or later [here](https://nodejs.org/en/download) (required) +- The pnpm package manager `npn i -g pnpm` (recommended) + +## Structure + +Commands and Listeners are all stored in named files within the src/commands and src/listeners directories respectively. + +## Running Locally + +- Copy .env.example and rename to .env +- Create a Discord Bot with all gateway permissions enabled +- Generate a token for this discord bot and paste it in the .env for DISCORD_TOKEN +- Run `pnpm dev` to run locally + +## Adding the bot to a server + +Use the following link (replacing DISCORD_CLIENT_ID with your own bot's client ID of course...) to add your bot: +https://discord.com/oauth2/authorize?client_id=DISCORD_CLIENT_ID&scope=bot&permissions=8 diff --git a/package.json b/package.json new file mode 100644 index 0000000..fc67ff9 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "himbot", + "version": "1.0.0", + "main": "dist/index.js", + "author": "@sapphire", + "license": "UNLICENSE", + "type": "commonjs", + "dependencies": { + "@sapphire/decorators": "^6.0.1", + "@sapphire/discord.js-utilities": "6.0.7", + "@sapphire/framework": "^4.4.3", + "@sapphire/plugin-logger": "^3.0.3", + "@sapphire/utilities": "^3.11.1", + "@skyra/env-utilities": "^1.2.0", + "colorette": "^2.0.20", + "discord.js": "^14.10.2" + }, + "devDependencies": { + "@sapphire/cli": "^1.6.1", + "@sapphire/prettier-config": "^1.4.5", + "@sapphire/ts-config": "^4.0.0", + "@types/node": "^18.16.3", + "@types/ws": "^8.5.4", + "prettier": "^2.8.8", + "tsc-watch": "^6.0.4", + "typescript": "^5.0.4" + }, + "scripts": { + "sapphire": "sapphire", + "generate": "sapphire generate", + "build": "tsc", + "watch": "tsc -w", + "start": "node dist/index.js", + "dev": "npm run build && npm run start", + "watch:start": "tsc-watch --onSuccess \"npm run start\"", + "format": "prettier --write \"src/\"" + }, + "prettier": "@sapphire/prettier-config" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..5f03e56 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,722 @@ +lockfileVersion: '6.1' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +dependencies: + '@sapphire/decorators': + specifier: ^6.0.1 + version: 6.0.1 + '@sapphire/discord.js-utilities': + specifier: 6.0.7 + version: 6.0.7 + '@sapphire/framework': + specifier: ^4.4.3 + version: 4.4.3 + '@sapphire/plugin-logger': + specifier: ^3.0.3 + version: 3.0.3 + '@sapphire/utilities': + specifier: ^3.11.1 + version: 3.11.1 + '@skyra/env-utilities': + specifier: ^1.2.0 + version: 1.2.0 + colorette: + specifier: ^2.0.20 + version: 2.0.20 + discord.js: + specifier: ^14.10.2 + version: 14.10.2 + +devDependencies: + '@sapphire/cli': + specifier: ^1.6.1 + version: 1.6.1 + '@sapphire/prettier-config': + specifier: ^1.4.5 + version: 1.4.5 + '@sapphire/ts-config': + specifier: ^4.0.0 + version: 4.0.0 + '@types/node': + specifier: ^18.16.3 + version: 18.16.3 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.4 + prettier: + specifier: ^2.8.8 + version: 2.8.8 + tsc-watch: + specifier: ^6.0.4 + version: 6.0.4(typescript@5.0.4) + typescript: + specifier: ^5.0.4 + version: 5.0.4 + +packages: + + /@discordjs/builders@1.6.3: + resolution: {integrity: sha512-CTCh8NqED3iecTNuiz49mwSsrc2iQb4d0MjMdmS/8pb69Y4IlzJ/DIy/p5GFlgOrFbNO2WzMHkWKQSiJ3VNXaw==} + engines: {node: '>=16.9.0'} + dependencies: + '@discordjs/formatters': 0.3.1 + '@discordjs/util': 0.3.1 + '@sapphire/shapeshift': 3.9.2 + discord-api-types: 0.37.43 + fast-deep-equal: 3.1.3 + ts-mixer: 6.0.3 + tslib: 2.5.3 + dev: false + + /@discordjs/collection@1.5.1: + resolution: {integrity: sha512-aWEc9DCf3TMDe9iaJoOnO2+JVAjeRNuRxPZQA6GVvBf+Z3gqUuWYBy2NWh4+5CLYq5uoc3MOvUQ5H5m8CJBqOA==} + engines: {node: '>=16.9.0'} + dev: false + + /@discordjs/formatters@0.3.1: + resolution: {integrity: sha512-M7X4IGiSeh4znwcRGcs+49B5tBkNDn4k5bmhxJDAUhRxRHTiFAOTVUNQ6yAKySu5jZTnCbSvTYHW3w0rAzV1MA==} + engines: {node: '>=16.9.0'} + dependencies: + discord-api-types: 0.37.43 + dev: false + + /@discordjs/rest@1.7.1: + resolution: {integrity: sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ==} + engines: {node: '>=16.9.0'} + dependencies: + '@discordjs/collection': 1.5.1 + '@discordjs/util': 0.3.1 + '@sapphire/async-queue': 1.5.0 + '@sapphire/snowflake': 3.5.1 + discord-api-types: 0.37.43 + file-type: 18.5.0 + tslib: 2.5.3 + undici: 5.22.1 + dev: false + + /@discordjs/util@0.3.1: + resolution: {integrity: sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==} + engines: {node: '>=16.9.0'} + dev: false + + /@discordjs/ws@0.8.3: + resolution: {integrity: sha512-hcYtppanjHecbdNyCKQNH2I4RP9UrphDgmRgLYrATEQF1oo4sYSve7ZmGsBEXSzH72MO2tBPdWSThunbxUVk0g==} + engines: {node: '>=16.9.0'} + dependencies: + '@discordjs/collection': 1.5.1 + '@discordjs/rest': 1.7.1 + '@discordjs/util': 0.3.1 + '@sapphire/async-queue': 1.5.0 + '@types/ws': 8.5.4 + '@vladfrangu/async_event_emitter': 2.2.2 + discord-api-types: 0.37.43 + tslib: 2.5.3 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + /@favware/colorette-spinner@1.0.1: + resolution: {integrity: sha512-PPYtcLzhSafdylp8NBOxMCYIcLqTUMNiQc7ciBoAIvxNG2egM+P7e2nNPui5+Svyk89Q+Tnbrp139ZRIIBw3IA==} + engines: {node: '>=v16'} + dependencies: + colorette: 2.0.20 + dev: true + + /@sapphire/async-queue@1.5.0: + resolution: {integrity: sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@sapphire/cli@1.6.1: + resolution: {integrity: sha512-gXA1pPrcR9nd1ipXMVRVjEm029wwoPj5Qzm7G68i3V8QfTl2dAMokZNKHD4nZZanUsBbc+KKWwsCScamDp0RzA==} + engines: {node: '>=v16.6.0', npm: '>=7'} + hasBin: true + dependencies: + '@favware/colorette-spinner': 1.0.1 + '@sapphire/result': 2.6.4 + colorette: 2.0.20 + commander: 10.0.1 + execa: 7.1.1 + find-up: 5.0.0 + js-yaml: 4.1.0 + prompts: 2.4.2 + tslib: 2.5.3 + dev: true + + /@sapphire/decorators@6.0.1: + resolution: {integrity: sha512-CyekhzVeiIda5E/IQzm0TWj+ZqKhWRaLWmB/6MTaOtXUwTlNMm/o7/bS54yM5PEAf1LTwwwuNL91sNUDS8sCPg==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + tslib: 2.5.3 + dev: false + + /@sapphire/discord-utilities@3.0.3: + resolution: {integrity: sha512-1FN1JKU0jaMc1iq7u0SJ0G7QALZ2IC/Z8Pn0Q7ekjbYeB4AE7xyuBG09dZS5C1Dap0/gkThU2yUAsgn9esh4dQ==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + discord-api-types: 0.37.43 + dev: false + + /@sapphire/discord.js-utilities@6.0.7: + resolution: {integrity: sha512-R0T9wHpgDwbAoN+EJA647UWL3ZEbzg3Hi4klDUfUGfPR24i1aZ1W/d3gZHENe6TANsErJ2egoVoiXiBbEpMlcg==} + engines: {node: '>=16.6.0', npm: '>=7.0.0'} + dependencies: + '@sapphire/discord-utilities': 3.0.3 + '@sapphire/duration': 1.1.0 + '@sapphire/utilities': 3.11.1 + tslib: 2.5.3 + dev: false + + /@sapphire/duration@1.1.0: + resolution: {integrity: sha512-ATb2pWPLcSgG7bzvT6MglUcDexFSufr2FLXUmhipWGFtZbvDhkopGBIuHyzoGy7LZvL8UY5T6pRLNdFv5pl/Lg==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@sapphire/framework@4.4.3: + resolution: {integrity: sha512-Bm3tXi9+NGZA/IiPWz4ROozmNH/zzRJ5NfYOCbc3nFuImTc2MwjDot1W43pHczW51Hmra0V5gfBqA5jMse1Wog==} + engines: {node: '>=16.6.0', npm: '>=7.0.0'} + dependencies: + '@discordjs/builders': 1.6.3 + '@sapphire/discord-utilities': 3.0.3 + '@sapphire/discord.js-utilities': 6.0.7 + '@sapphire/lexure': 1.1.5 + '@sapphire/pieces': 3.6.3 + '@sapphire/ratelimits': 2.4.6 + '@sapphire/result': 2.6.4 + '@sapphire/stopwatch': 1.5.0 + '@sapphire/utilities': 3.11.1 + dev: false + + /@sapphire/lexure@1.1.5: + resolution: {integrity: sha512-afVTCLHezlyvdvvOcEeCzMA05l1yje9axtY7gWQpDcppOazskYPgGMVCdAbfHz5BsyABMZYT2MznY+phEgFc8Q==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + '@sapphire/result': 2.6.4 + dev: false + + /@sapphire/pieces@3.6.3: + resolution: {integrity: sha512-mOwp94pfHON3avzihU4X5fiiIZDAtiCsWULiDm3Vie1Osv0X2LRkVKBsSrs7hcYDJfU+puJEQIhVAj6ep7ZUOw==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + '@discordjs/collection': 1.5.1 + '@sapphire/utilities': 3.11.1 + tslib: 2.5.3 + dev: false + + /@sapphire/plugin-logger@3.0.3: + resolution: {integrity: sha512-pBEQhXi3PWPpXxfljg7pLYoI99gO6iyA9rCy6ntBttrJiM1ZirnaSm6p1/uon3/T4gW0poauFS44+S3Jf4MSZA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + '@sapphire/timestamp': 1.0.1 + colorette: 2.0.20 + tslib: 2.5.3 + dev: false + + /@sapphire/prettier-config@1.4.5: + resolution: {integrity: sha512-XFyDXwztGmP7CJo1BMjmBZaX4aLyMPceZX4rQU9WsODnkJBTpAEIPjmioFUHlnLsu+yty0r5635Ye3eIcr1x0A==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + prettier: 2.8.8 + dev: true + + /@sapphire/ratelimits@2.4.6: + resolution: {integrity: sha512-E8ZogD+gtXw/iAFuyd42ZzUcYzOC3qGXZFOjU12o0cSYWObh7kQEqkS62BB0hMXy+GJH0X2+kqgMklME8EhWUg==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@sapphire/result@2.6.4: + resolution: {integrity: sha512-o6rAnNbtumhR4Iy9t1p3xtOG9WtfO2OyyyaBrGUNThmhqf1cwvl5CO3/E0Hd66qdkuSqpJqC9TnnGzTmSoDd6A==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + + /@sapphire/shapeshift@3.9.2: + resolution: {integrity: sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + fast-deep-equal: 3.1.3 + lodash: 4.17.21 + dev: false + + /@sapphire/snowflake@3.5.1: + resolution: {integrity: sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@sapphire/stopwatch@1.5.0: + resolution: {integrity: sha512-DtyKugdy3JTqm6JnEepTY64fGJAqlusDVrlrzifEgSCfGYCqpvB+SBldkWtDH+z+zLcp+PyaFLq7xpVfkhmvGg==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + tslib: 2.5.3 + dev: false + + /@sapphire/timestamp@1.0.1: + resolution: {integrity: sha512-uLg+rBFuBiaQY/pFGDDzZSOH2cfv4ONIB7zQGNuRCTpYKBW/iIhRBIZjJZyn8NVkXQhVi+Q94DI4i6gDhYVs7w==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@sapphire/ts-config@4.0.0: + resolution: {integrity: sha512-0KH37vhrQwcaZIE1JMcEw+dapbHD+yDb5rmyKz4lHjua3D4j1qPDEzygFiPh7NdDM0O7HjKgozeo63jjCHfN2A==} + engines: {node: '>=v16.0.0', npm: '>=8.0.0'} + dependencies: + tslib: 2.5.3 + typescript: 5.0.4 + dev: true + + /@sapphire/utilities@3.11.1: + resolution: {integrity: sha512-SO7WvlPc0QEB+bCT6Z/NGpYTpOtYAJtxpxKV4YU+agEGlMZ5hM8J/xkco4/yFWH7X2gAcG4Ckoi2shFr7CMS/A==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@skyra/env-utilities@1.2.0: + resolution: {integrity: sha512-y8r/ceKnCWhnS46+TTULdCtcDBCrRvmANoHbvfWXXQfG+O4ZOHApnT4Z8lM0txQP3N+CSQXPuS/QCG+CvpaYSA==} + engines: {node: '>=16.9.0', npm: '>=8.0.0'} + dependencies: + dotenv: 16.1.4 + dotenv-expand: 10.0.0 + dev: false + + /@tokenizer/token@0.3.0: + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + dev: false + + /@types/node@18.16.3: + resolution: {integrity: sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==} + + /@types/ws@8.5.4: + resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} + dependencies: + '@types/node': 18.16.3 + + /@vladfrangu/async_event_emitter@2.2.2: + resolution: {integrity: sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /discord-api-types@0.37.43: + resolution: {integrity: sha512-bBhDWU3TF9KADxR/mHp1K4Bvu/LRtFQdGyBjADu4e66F3ZnD4kp12W/SJCttIaCcMXzPV3sfty6eDGRNRph51Q==} + dev: false + + /discord.js@14.10.2: + resolution: {integrity: sha512-yPMJ/vGSWJP8WbbfPX18WGU2wOLBjL+OKVOVKI1eWx3ICcUhDg5MvFkg5aNGiokjPfKlAgqnFIUH0jl59ONHrQ==} + engines: {node: '>=16.9.0'} + dependencies: + '@discordjs/builders': 1.6.3 + '@discordjs/collection': 1.5.1 + '@discordjs/formatters': 0.3.1 + '@discordjs/rest': 1.7.1 + '@discordjs/util': 0.3.1 + '@discordjs/ws': 0.8.3 + '@sapphire/snowflake': 3.5.1 + '@types/ws': 8.5.4 + discord-api-types: 0.37.43 + fast-deep-equal: 3.1.3 + lodash.snakecase: 4.1.1 + tslib: 2.5.3 + undici: 5.22.1 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + /dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dev: false + + /dotenv@16.1.4: + resolution: {integrity: sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==} + engines: {node: '>=12'} + dev: false + + /duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: true + + /event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + dev: true + + /execa@7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: false + + /file-type@18.5.0: + resolution: {integrity: sha512-yvpl5U868+V6PqXHMmsESpg6unQ5GfnPssl4dxdJudBrr9qy7Fddt7EVX1VLlddFfe8Gj9N7goCZH22FXuSQXQ==} + engines: {node: '>=14.16'} + dependencies: + readable-web-to-node-stream: 3.0.2 + strtok3: 7.0.0 + token-types: 5.0.1 + dev: false + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + dev: false + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + dev: true + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + dependencies: + through: 2.3.8 + dev: true + + /peek-readable@5.0.0: + resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} + engines: {node: '>=14.16'} + dev: false + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + dependencies: + event-stream: 3.3.4 + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + /readable-web-to-node-stream@3.0.2: + resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} + engines: {node: '>=8'} + dependencies: + readable-stream: 3.6.2 + dev: false + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + dependencies: + through: 2.3.8 + dev: true + + /stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + dependencies: + duplexer: 0.1.2 + dev: true + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strtok3@7.0.0: + resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} + engines: {node: '>=14.16'} + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 5.0.0 + dev: false + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /token-types@5.0.1: + resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} + engines: {node: '>=14.16'} + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + dev: false + + /ts-mixer@6.0.3: + resolution: {integrity: sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==} + dev: false + + /tsc-watch@6.0.4(typescript@5.0.4): + resolution: {integrity: sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + cross-spawn: 7.0.3 + node-cleanup: 2.1.2 + ps-tree: 1.2.0 + string-argv: 0.3.2 + typescript: 5.0.4 + dev: true + + /tslib@2.5.3: + resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + + /typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + dev: true + + /undici@5.22.1: + resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} + engines: {node: '>=14.0'} + dependencies: + busboy: 1.6.0 + dev: false + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /ws@8.13.0: + resolution: {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 + dev: false + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/.env.example b/src/.env.example new file mode 100644 index 0000000..92662fa --- /dev/null +++ b/src/.env.example @@ -0,0 +1,2 @@ +# Tokens +DISCORD_TOKEN= diff --git a/src/commands/ping.ts b/src/commands/ping.ts new file mode 100644 index 0000000..f0467fb --- /dev/null +++ b/src/commands/ping.ts @@ -0,0 +1,46 @@ +import { ApplyOptions } from '@sapphire/decorators'; +import { Command } from '@sapphire/framework'; +import { Message } from 'discord.js'; + +@ApplyOptions({ + description: 'Pong!' +}) +export class UserCommand extends Command { + // Register Chat Input and Context Menu command + public override registerApplicationCommands(registry: Command.Registry) { + // Register Chat Input command + registry.registerChatInputCommand({ + name: this.name, + description: this.description + }); + } + + // Message command + public async messageRun(message: Message) { + return this.sendPing(message); + } + + // Chat Input (slash) command + public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { + return this.sendPing(interaction); + } + + private async sendPing(interactionOrMessage: Message | Command.ChatInputCommandInteraction | Command.ContextMenuCommandInteraction) { + const pingMessage = + interactionOrMessage instanceof Message + ? await interactionOrMessage.channel.send({ content: 'Ping?' }) + : await interactionOrMessage.reply({ content: 'Ping?', fetchReply: true }); + + const content = `Pong! Bot Latency ${Math.round(this.container.client.ws.ping)}ms. API Latency ${ + pingMessage.createdTimestamp - interactionOrMessage.createdTimestamp + }ms.`; + + if (interactionOrMessage instanceof Message) { + return pingMessage.edit({ content }); + } + + return interactionOrMessage.editReply({ + content: content + }); + } +} diff --git a/src/commands/wryna.ts b/src/commands/wryna.ts new file mode 100644 index 0000000..15856c3 --- /dev/null +++ b/src/commands/wryna.ts @@ -0,0 +1,41 @@ +import { ApplyOptions } from '@sapphire/decorators'; +import { Args, Command } from '@sapphire/framework'; +import { Message } from 'discord.js'; + +@ApplyOptions({ + description: 'This command was your nickname in highschool!', + options: ['nickname'] +}) +export class UserCommand extends Command { + // Register Chat Input and Context Menu command + public override registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand((builder) => + builder // + .setName(this.name) + .setDescription(this.description) + .addStringOption((option) => option.setName('nickname').setDescription('Your nickname in highschool.').setRequired(true)) + ); + } + + // Message command + public async messageRun(message: Message, args: Args) { + return this.sendPing(message, args.getOption('nickname') || 'NOTHING'); + } + + // Chat Input (slash) command + public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { + return this.sendPing(interaction, interaction.options.getString('nickname') || 'NOTHING'); + } + + private async sendPing( + interactionOrMessage: Message | Command.ChatInputCommandInteraction | Command.ContextMenuCommandInteraction, + nickname: string + ) { + interactionOrMessage instanceof Message + ? await interactionOrMessage.channel.send({ content: `${nickname} was ${interactionOrMessage.author.tag}'s nickname in highschool!` }) + : await interactionOrMessage.reply({ + content: `${nickname} was ${interactionOrMessage.user.tag}'s nickname in highschool!`, + fetchReply: true + }); + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..b37364b --- /dev/null +++ b/src/index.ts @@ -0,0 +1,36 @@ +import './lib/setup'; +import { LogLevel, SapphireClient } from '@sapphire/framework'; +import { ActivityType, GatewayIntentBits } from 'discord.js'; + +const client = new SapphireClient({ + defaultPrefix: '!', + presence: { + status: 'online', + activities: [ + { + name: "I'm just here for the vibes my dudes...", + type: ActivityType.Playing + } + ] + }, + caseInsensitiveCommands: true, + logger: { + level: LogLevel.Debug + }, + intents: [GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds, GatewayIntentBits.MessageContent], + loadMessageCommandListeners: true +}); + +const main = async () => { + try { + client.logger.info('Logging in'); + await client.login(); + client.logger.info('logged in'); + } catch (error) { + client.logger.fatal(error); + client.destroy(); + process.exit(1); + } +}; + +main(); diff --git a/src/lib/constants.ts b/src/lib/constants.ts new file mode 100644 index 0000000..97147f0 --- /dev/null +++ b/src/lib/constants.ts @@ -0,0 +1,4 @@ +import { join } from 'path'; + +export const rootDir = join(__dirname, '..', '..'); +export const srcDir = join(rootDir, 'src'); diff --git a/src/lib/setup.ts b/src/lib/setup.ts new file mode 100644 index 0000000..d4124b3 --- /dev/null +++ b/src/lib/setup.ts @@ -0,0 +1,18 @@ +// Unless explicitly defined, set NODE_ENV as development: +process.env.NODE_ENV ??= 'development'; + +import { ApplicationCommandRegistries, RegisterBehavior } from '@sapphire/framework'; +import '@sapphire/plugin-logger/register'; +import { setup } from '@skyra/env-utilities'; +import * as colorette from 'colorette'; +import { join } from 'node:path'; +import { srcDir } from './constants'; + +// Set default behavior to bulk overwrite +ApplicationCommandRegistries.setDefaultBehaviorWhenNotIdentical(RegisterBehavior.BulkOverwrite); + +// Read env var +setup({ path: join(srcDir, '.env') }); + +// Enable colorette +colorette.createColors({ useColor: true }); diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..08e3dce --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,47 @@ +import { + container, + type ChatInputCommandSuccessPayload, + type Command, + type ContextMenuCommandSuccessPayload, + type MessageCommandSuccessPayload +} from '@sapphire/framework'; +import { cyan } from 'colorette'; +import type { APIUser, Guild, User } from 'discord.js'; + +export function logSuccessCommand(payload: ContextMenuCommandSuccessPayload | ChatInputCommandSuccessPayload | MessageCommandSuccessPayload): void { + let successLoggerData: ReturnType; + + if ('interaction' in payload) { + successLoggerData = getSuccessLoggerData(payload.interaction.guild, payload.interaction.user, payload.command); + } else { + successLoggerData = getSuccessLoggerData(payload.message.guild, payload.message.author, payload.command); + } + + container.logger.debug(`${successLoggerData.shard} - ${successLoggerData.commandName} ${successLoggerData.author} ${successLoggerData.sentAt}`); +} + +export function getSuccessLoggerData(guild: Guild | null, user: User, command: Command) { + const shard = getShardInfo(guild?.shardId ?? 0); + const commandName = getCommandInfo(command); + const author = getAuthorInfo(user); + const sentAt = getGuildInfo(guild); + + return { shard, commandName, author, sentAt }; +} + +function getShardInfo(id: number) { + return `[${cyan(id.toString())}]`; +} + +function getCommandInfo(command: Command) { + return cyan(command.name); +} + +function getAuthorInfo(author: User | APIUser) { + return `${author.username}[${cyan(author.id)}]`; +} + +function getGuildInfo(guild: Guild | null) { + if (guild === null) return 'Direct Messages'; + return `${guild.name}[${cyan(guild.id)}]`; +} diff --git a/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts b/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts new file mode 100644 index 0000000..7c5f96a --- /dev/null +++ b/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts @@ -0,0 +1,23 @@ +import type { ChatInputCommandDeniedPayload, Events } from '@sapphire/framework'; +import { Listener, UserError } from '@sapphire/framework'; + +export class UserEvent extends Listener { + public async run({ context, message: content }: UserError, { interaction }: ChatInputCommandDeniedPayload) { + // `context: { silent: true }` should make UserError silent: + // Use cases for this are for example permissions error when running the `eval` command. + if (Reflect.get(Object(context), 'silent')) return; + + if (interaction.deferred || interaction.replied) { + return interaction.editReply({ + content, + allowedMentions: { users: [interaction.user.id], roles: [] } + }); + } + + return interaction.reply({ + content, + allowedMentions: { users: [interaction.user.id], roles: [] }, + ephemeral: true + }); + } +} diff --git a/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts b/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts new file mode 100644 index 0000000..039cd9a --- /dev/null +++ b/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts @@ -0,0 +1,14 @@ +import { Listener, LogLevel, type ChatInputCommandSuccessPayload } from '@sapphire/framework'; +import type { Logger } from '@sapphire/plugin-logger'; +import { logSuccessCommand } from '../../../lib/utils'; + +export class UserListener extends Listener { + public run(payload: ChatInputCommandSuccessPayload) { + logSuccessCommand(payload); + } + + public onLoad() { + this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug; + return super.onLoad(); + } +} diff --git a/src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts b/src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts new file mode 100644 index 0000000..6ec16a7 --- /dev/null +++ b/src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts @@ -0,0 +1,23 @@ +import type { ContextMenuCommandDeniedPayload, Events } from '@sapphire/framework'; +import { Listener, UserError } from '@sapphire/framework'; + +export class UserEvent extends Listener { + public async run({ context, message: content }: UserError, { interaction }: ContextMenuCommandDeniedPayload) { + // `context: { silent: true }` should make UserError silent: + // Use cases for this are for example permissions error when running the `eval` command. + if (Reflect.get(Object(context), 'silent')) return; + + if (interaction.deferred || interaction.replied) { + return interaction.editReply({ + content, + allowedMentions: { users: [interaction.user.id], roles: [] } + }); + } + + return interaction.reply({ + content, + allowedMentions: { users: [interaction.user.id], roles: [] }, + ephemeral: true + }); + } +} diff --git a/src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts b/src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts new file mode 100644 index 0000000..8253a74 --- /dev/null +++ b/src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts @@ -0,0 +1,14 @@ +import { Listener, LogLevel, type ContextMenuCommandSuccessPayload } from '@sapphire/framework'; +import type { Logger } from '@sapphire/plugin-logger'; +import { logSuccessCommand } from '../../../lib/utils'; + +export class UserListener extends Listener { + public run(payload: ContextMenuCommandSuccessPayload) { + logSuccessCommand(payload); + } + + public onLoad() { + this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug; + return super.onLoad(); + } +} diff --git a/src/listeners/commands/messageCommands/messageCommandDenied.ts b/src/listeners/commands/messageCommands/messageCommandDenied.ts new file mode 100644 index 0000000..e0ea3e3 --- /dev/null +++ b/src/listeners/commands/messageCommands/messageCommandDenied.ts @@ -0,0 +1,12 @@ +import type { Events, MessageCommandDeniedPayload } from '@sapphire/framework'; +import { Listener, type UserError } from '@sapphire/framework'; + +export class UserEvent extends Listener { + public async run({ context, message: content }: UserError, { message }: MessageCommandDeniedPayload) { + // `context: { silent: true }` should make UserError silent: + // Use cases for this are for example permissions error when running the `eval` command. + if (Reflect.get(Object(context), 'silent')) return; + + return message.reply({ content, allowedMentions: { users: [message.author.id], roles: [] } }); + } +} diff --git a/src/listeners/commands/messageCommands/messageCommandSuccess.ts b/src/listeners/commands/messageCommands/messageCommandSuccess.ts new file mode 100644 index 0000000..bccb0cb --- /dev/null +++ b/src/listeners/commands/messageCommands/messageCommandSuccess.ts @@ -0,0 +1,15 @@ +import type { MessageCommandSuccessPayload } from '@sapphire/framework'; +import { Listener, LogLevel } from '@sapphire/framework'; +import type { Logger } from '@sapphire/plugin-logger'; +import { logSuccessCommand } from '../../../lib/utils'; + +export class UserEvent extends Listener { + public run(payload: MessageCommandSuccessPayload) { + logSuccessCommand(payload); + } + + public onLoad() { + this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug; + return super.onLoad(); + } +} diff --git a/src/listeners/mentionPrefixOnly.ts b/src/listeners/mentionPrefixOnly.ts new file mode 100644 index 0000000..88974a6 --- /dev/null +++ b/src/listeners/mentionPrefixOnly.ts @@ -0,0 +1,10 @@ +import type { Events } from '@sapphire/framework'; +import { Listener } from '@sapphire/framework'; +import type { Message } from 'discord.js'; + +export class UserEvent extends Listener { + public async run(message: Message) { + const prefix = this.container.client.options.defaultPrefix; + return message.channel.send(prefix ? `My prefix in this guild is: \`${prefix}\`` : 'Cannot find any Prefix for Message Commands.'); + } +} diff --git a/src/listeners/ready.ts b/src/listeners/ready.ts new file mode 100644 index 0000000..88db6e4 --- /dev/null +++ b/src/listeners/ready.ts @@ -0,0 +1,50 @@ +import { ApplyOptions } from '@sapphire/decorators'; +import { Listener, Store } from '@sapphire/framework'; +import { blue, gray, green, magenta, magentaBright, white, yellow } from 'colorette'; + +const dev = process.env.NODE_ENV !== 'production'; + +@ApplyOptions({ once: true }) +export class UserEvent extends Listener { + private readonly style = dev ? yellow : blue; + + public run() { + this.printBanner(); + this.printStoreDebugInformation(); + } + + private printBanner() { + const success = green('+'); + + const llc = dev ? magentaBright : white; + const blc = dev ? magenta : blue; + + const line01 = llc(''); + const line02 = llc(''); + const line03 = llc(''); + + // Offset Pad + const pad = ' '.repeat(7); + + console.log( + String.raw` +${line01} ${pad}${blc('1.0.0')} +${line02} ${pad}[${success}] Gateway +${line03}${dev ? ` ${pad}${blc('<')}${llc('/')}${blc('>')} ${llc('DEVELOPMENT MODE')}` : ''} + `.trim() + ); + } + + private printStoreDebugInformation() { + const { client, logger } = this.container; + const stores = [...client.stores.values()]; + const last = stores.pop()!; + + for (const store of stores) logger.info(this.styleStore(store, false)); + logger.info(this.styleStore(last, true)); + } + + private styleStore(store: Store, last: boolean) { + return gray(`${last ? '└─' : '├─'} Loaded ${this.style(store.size.toString().padEnd(3, ' '))} ${store.name}.`); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..329be80 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@sapphire/ts-config", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" + }, + "include": ["src"] +}