Compare commits

...

13 Commits

Author SHA1 Message Date
Ashley Rosch
75a8e38f42 added fae/fem pronouns 2022-06-11 11:17:15 -05:00
Ashley Rosch
f7da37ffe3 minor updates 2022-04-01 13:14:15 -05:00
Ashley Rosch
4e461c3cff updated gitignore 2022-04-01 13:00:06 -05:00
4d886df641 error fixes 2022-04-01 12:56:55 -05:00
fbb70a811c updated roles data 2022-04-01 12:56:40 -05:00
858fe33e62 readme 2022-04-01 12:56:34 -05:00
df4a978476 oops 2022-03-31 15:34:29 -05:00
6e33467984 catch throws 2022-03-31 15:08:19 -05:00
7bac005fe7 reformatted to use my template 2022-03-31 15:04:54 -05:00
Ashley Rosch
a9ea7bf8d5 updated package name 2021-11-13 19:56:08 -06:00
Ashley Rosch
580511b632 nothing really 2021-10-29 20:12:34 -05:00
Ashley Rosch
7bccab26eb removed unused variable 2021-10-29 20:11:28 -05:00
Ashley Rosch
e28b47c349 added slash command support 2021-08-20 03:20:25 -05:00
29 changed files with 2928 additions and 1327 deletions

55
.eslintrc.json Normal file
View File

@@ -0,0 +1,55 @@
{
"root": true,
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/strict-boolean-expressions": [
2,
{
"allowString" : false,
"allowNumber" : false
}
],
/* important */
"prefer-const": "error",
"quotes": ["error", "single"],
"block-scoped-var": "error",
"camelcase": "error",
"consistent-this": ["error", "that"],
"no-else-return": "error",
"no-eq-null": "error",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implied-eval": "error",
"no-invalid-this": "error",
"require-await": "error",
"yoda": "error",
"semi": ["error", "always"],
"semi-style": ["error", "last"],
/* less important */
"no-unreachable-loop": "error",
"no-unused-private-class-members": "error",
"no-use-before-define": "error",
"no-unmodified-loop-condition": "error",
"no-duplicate-imports": "error",
"no-promise-executor-return": "error",
"no-self-compare": "error",
"no-constructor-return": "error",
"no-template-curly-in-string": "error",
"array-callback-return": "error",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error"
},
"ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]
}

7
.gitignore vendored
View File

@@ -1,3 +1,6 @@
node_modules .DS_Store
token .vscode
log log
dist
node_modules
token.txt

14
.pnpm-debug.log Normal file
View File

@@ -0,0 +1,14 @@
{
"0 debug pnpm:scope": {
"selected": 1
},
"1 error pnpm": {
"code": "ERR_PNPM_MISSING_PACKAGE_NAME",
"err": {
"name": "pnpm",
"message": "`pnpm add` requires the package name",
"code": "ERR_PNPM_MISSING_PACKAGE_NAME",
"stack": "pnpm: `pnpm add` requires the package name\n at Object.handler [as add] (/Users/zomo/.nvm/versions/node/v17.1.0/lib/node_modules/pnpm/dist/pnpm.cjs:122191:15)\n at /Users/zomo/.nvm/versions/node/v17.1.0/lib/node_modules/pnpm/dist/pnpm.cjs:182192:83\n at async run (/Users/zomo/.nvm/versions/node/v17.1.0/lib/node_modules/pnpm/dist/pnpm.cjs:182168:34)\n at async runPnpm (/Users/zomo/.nvm/versions/node/v17.1.0/lib/node_modules/pnpm/dist/pnpm.cjs:182387:5)\n at async /Users/zomo/.nvm/versions/node/v17.1.0/lib/node_modules/pnpm/dist/pnpm.cjs:182379:7"
}
}
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Ashley Rosch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

16
README.md Normal file
View File

@@ -0,0 +1,16 @@
# IKEA-Canada-Support
## running
| File | Description | Notes |
| - | - | - |
| `scripts/(os)/install` | installs the bot | must be ran again if you change the bot token |
| `scripts/(os)/start` | starts the bot | includes a watchdog (so it automatically restarts if it crashes) |
## dev
| Command | Description |
| - | - |
| `npm run build` | builds the bot and registers the commands |
| `npm run start` | starts the bot |
| `npm run dev` OR `npm run watch` | watches for changes, then builds and starts the bot |

24
build.mjs Normal file
View File

@@ -0,0 +1,24 @@
import { build } from 'esbuild';
import commandLineArgs from 'command-line-args';
const args = commandLineArgs([
{ name: 'watch', alias: 'w', type: Boolean },
{ name: 'bundle', alias: 'b', type: Boolean },
{ name: 'minify', alias: 'm', type: Boolean },
{ name: 'sourcemap', alias: 's', type: Boolean }
]);
//build TS
build({
entryPoints: ['src/main.ts'],
outdir: 'dist',
target: 'es2020',
platform: 'node',
format: 'cjs',
watch: args.watch,
bundle: args.bundle,
minify: args.minify,
sourcemap: args.sourcemap
});

838
package-lock.json generated
View File

@@ -1,838 +0,0 @@
{
"name": "ikea-canada-support",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ikea-canada-support",
"dependencies": {
"@discordjs/builders": "^0.5.0",
"@discordjs/rest": "^0.1.0-canary.0",
"discord.js": "^13.1.0"
},
"devDependencies": {
"@types/node": "^16.3.1",
"ts-node": "^10.1.0"
}
},
"node_modules/@cspotcode/source-map-consumer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
"integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
"dev": true,
"engines": {
"node": ">= 12"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz",
"integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-consumer": "0.8.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@discordjs/builders": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz",
"integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==",
"dependencies": {
"@sindresorhus/is": "^4.0.1",
"discord-api-types": "^0.22.0",
"ow": "^0.27.0",
"ts-mixer": "^6.0.0",
"tslib": "^2.3.0"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@discordjs/collection": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz",
"integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="
},
"node_modules/@discordjs/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@discordjs/rest": {
"version": "0.1.0-canary.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.1.0-canary.0.tgz",
"integrity": "sha512-d+s//ISYVV+e0w/926wMEeO7vju+Pn11x1JM4tcmVMCHSDgpi6pnFCNAXF1TEdnDcy7xf9tq5cf2pQkb/7ySTQ==",
"dependencies": {
"@discordjs/collection": "^0.1.6",
"@sapphire/async-queue": "^1.1.4",
"@sapphire/snowflake": "^1.3.5",
"abort-controller": "^3.0.0",
"discord-api-types": "^0.18.1",
"form-data": "^4.0.0",
"node-fetch": "^2.6.1",
"tslib": "^2.3.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@discordjs/rest/node_modules/discord-api-types": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.18.1.tgz",
"integrity": "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg==",
"deprecated": "No longer supported. Install the latest release (0.20.2)",
"engines": {
"node": ">=12"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz",
"integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==",
"engines": {
"node": ">=14",
"npm": ">=6"
}
},
"node_modules/@sapphire/snowflake": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-1.3.6.tgz",
"integrity": "sha512-QnzuLp+p9D7agynVub/zqlDVriDza9y3STArBhNiNBUgIX8+GL5FpQxstRfw1jDr5jkZUjcuKYAHxjIuXKdJAg==",
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/@sindresorhus/is": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
"integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
"integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
"integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
"integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"dev": true
},
"node_modules/@types/node": {
"version": "16.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.2.tgz",
"integrity": "sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA=="
},
"node_modules/@types/ws": {
"version": "7.4.7",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
"integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/acorn": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz",
"integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/discord-api-types": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz",
"integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==",
"engines": {
"node": ">=12"
}
},
"node_modules/discord.js": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz",
"integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==",
"dependencies": {
"@discordjs/builders": "^0.5.0",
"@discordjs/collection": "^0.2.1",
"@discordjs/form-data": "^3.0.1",
"@sapphire/async-queue": "^1.1.4",
"@types/ws": "^7.4.7",
"discord-api-types": "^0.22.0",
"node-fetch": "^2.6.1",
"ws": "^7.5.1"
},
"engines": {
"node": ">=16.6.0",
"npm": ">=7.0.0"
}
},
"node_modules/discord.js/node_modules/@discordjs/collection": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz",
"integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/dot-prop": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
"dependencies": {
"is-obj": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"engines": {
"node": ">=8"
}
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/mime-db": {
"version": "1.49.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.32",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
"dependencies": {
"mime-db": "1.49.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/ow": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
"integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
"dependencies": {
"@sindresorhus/is": "^4.0.1",
"callsites": "^3.1.0",
"dot-prop": "^6.0.1",
"lodash.isequal": "^4.5.0",
"type-fest": "^1.2.1",
"vali-date": "^1.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ts-mixer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz",
"integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ=="
},
"node_modules/ts-node": {
"version": "10.2.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz",
"integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "0.6.1",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/type-fest": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
"integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
"dev": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/vali-date": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
"integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ws": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
"integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
}
},
"dependencies": {
"@cspotcode/source-map-consumer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
"integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
"dev": true
},
"@cspotcode/source-map-support": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz",
"integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==",
"dev": true,
"requires": {
"@cspotcode/source-map-consumer": "0.8.0"
}
},
"@discordjs/builders": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz",
"integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==",
"requires": {
"@sindresorhus/is": "^4.0.1",
"discord-api-types": "^0.22.0",
"ow": "^0.27.0",
"ts-mixer": "^6.0.0",
"tslib": "^2.3.0"
}
},
"@discordjs/collection": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz",
"integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="
},
"@discordjs/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"@discordjs/rest": {
"version": "0.1.0-canary.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.1.0-canary.0.tgz",
"integrity": "sha512-d+s//ISYVV+e0w/926wMEeO7vju+Pn11x1JM4tcmVMCHSDgpi6pnFCNAXF1TEdnDcy7xf9tq5cf2pQkb/7ySTQ==",
"requires": {
"@discordjs/collection": "^0.1.6",
"@sapphire/async-queue": "^1.1.4",
"@sapphire/snowflake": "^1.3.5",
"abort-controller": "^3.0.0",
"discord-api-types": "^0.18.1",
"form-data": "^4.0.0",
"node-fetch": "^2.6.1",
"tslib": "^2.3.0"
},
"dependencies": {
"discord-api-types": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.18.1.tgz",
"integrity": "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg=="
}
}
},
"@sapphire/async-queue": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz",
"integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA=="
},
"@sapphire/snowflake": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-1.3.6.tgz",
"integrity": "sha512-QnzuLp+p9D7agynVub/zqlDVriDza9y3STArBhNiNBUgIX8+GL5FpQxstRfw1jDr5jkZUjcuKYAHxjIuXKdJAg=="
},
"@sindresorhus/is": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
"integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g=="
},
"@tsconfig/node10": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
"integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
"dev": true
},
"@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
"integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
"dev": true
},
"@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
"integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
"dev": true
},
"@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"dev": true
},
"@types/node": {
"version": "16.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.2.tgz",
"integrity": "sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA=="
},
"@types/ws": {
"version": "7.4.7",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
"integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
"requires": {
"@types/node": "*"
}
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"acorn": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
"dev": true
},
"acorn-walk": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz",
"integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==",
"dev": true
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"discord-api-types": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz",
"integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg=="
},
"discord.js": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz",
"integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==",
"requires": {
"@discordjs/builders": "^0.5.0",
"@discordjs/collection": "^0.2.1",
"@discordjs/form-data": "^3.0.1",
"@sapphire/async-queue": "^1.1.4",
"@types/ws": "^7.4.7",
"discord-api-types": "^0.22.0",
"node-fetch": "^2.6.1",
"ws": "^7.5.1"
},
"dependencies": {
"@discordjs/collection": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz",
"integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog=="
}
}
},
"dot-prop": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
"requires": {
"is-obj": "^2.0.0"
}
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"mime-db": {
"version": "1.49.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA=="
},
"mime-types": {
"version": "2.1.32",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
"requires": {
"mime-db": "1.49.0"
}
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"ow": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
"integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
"requires": {
"@sindresorhus/is": "^4.0.1",
"callsites": "^3.1.0",
"dot-prop": "^6.0.1",
"lodash.isequal": "^4.5.0",
"type-fest": "^1.2.1",
"vali-date": "^1.0.0"
}
},
"ts-mixer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz",
"integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ=="
},
"ts-node": {
"version": "10.2.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz",
"integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==",
"dev": true,
"requires": {
"@cspotcode/source-map-support": "0.6.1",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"yn": "3.1.1"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"type-fest": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
"integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="
},
"typescript": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
"dev": true,
"peer": true
},
"vali-date": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
"integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY="
},
"ws": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
"integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==",
"requires": {}
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
}
}
}

View File

@@ -4,13 +4,51 @@
"type": "git", "type": "git",
"url": "git@git.zomo.dev:zomo/IKEA-Canada-Support.git" "url": "git@git.zomo.dev:zomo/IKEA-Canada-Support.git"
}, },
"version": "1.0.0",
"description": "",
"main": "dist/main.js",
"scripts": {
"build": "node ./build.mjs -bs",
"start": "node .",
"watch": "npm-watch",
"dev": "npm-watch",
"getname": "echo $npm_package_name"
},
"watch": {
"build": {
"patterns": [
"src"
],
"extensions": "ts",
"legacyWatch": true
},
"start": {
"patterns": [
"dist"
],
"extensions": "js",
"legacyWatch": true,
"runOnChangeOnly": true
}
},
"author": "",
"license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^16.3.1", "@discordjs/rest": "^0.3.0",
"ts-node": "^10.1.0" "@types/command-line-args": "^5.2.0",
"@types/node": "^17.0.23",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"command-line-args": "^5.2.1",
"esbuild": "^0.14.29",
"eslint": "^8.12.0",
"npm-watch": "^0.11.0",
"ts-node": "^10.7.0",
"typescript": "^4.6.3"
}, },
"dependencies": { "dependencies": {
"@discordjs/builders": "^0.5.0", "discord.js": "^13.6.0",
"@discordjs/rest": "^0.1.0-canary.0", "discordslash": "github:zomoxyz/discordslash",
"discord.js": "^13.1.0" "lang": "github:zomoxyz/lang"
} }
} }

2030
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

1
readme
View File

@@ -1 +0,0 @@
create token file

View File

@@ -1,386 +0,0 @@
import { ButtonInteraction, Client, CommandInteraction, GuildMember, Interaction, Message, MessageActionRow, MessageButton, MessageSelectMenu, MessageSelectOptionData, PartialDMChannel, SelectMenuInteraction, TextBasedChannels } from "discord.js";
import { TextChannel } from "discord.js";
import * as Opt from "../types/Opt";
import refreshCommands from "./slash";
import { InteractionRoles, GuildId } from "./data";
type ReplyableInteraction = ButtonInteraction | CommandInteraction | SelectMenuInteraction;
const isReplyableInteraction = (res: any): boolean => res instanceof ButtonInteraction || res instanceof CommandInteraction || res instanceof SelectMenuInteraction;
const sendMessage = (res: ReplyableInteraction | TextBasedChannels, msg: any, ephemeral?: boolean) => {
let arg: any = {};
if (typeof msg === 'object')
arg = msg;
else
arg.content = msg;
if (isReplyableInteraction(res) && ephemeral)
arg.ephemeral = true;
if (isReplyableInteraction(res))
(res as ReplyableInteraction).reply(arg);
else
(res as TextBasedChannels).send(arg);
}
//initialize discord
const Discord = require('discord.js'),
client: Client = new Discord.Client({ intents: [
'GUILD_MEMBERS'
] });
/**
* send message containing menu
* @param res text channel to send message or interaction to reply to
* @param menuOpts menu data object
*/
function sendMenu(res: ReplyableInteraction|TextBasedChannels, menuOpts: Opt.Menu, ephemeral?: boolean) {
let row = new MessageActionRow()
let select = new MessageSelectMenu()
.setCustomId(menuOpts.id)
.setPlaceholder(menuOpts.palceholder)
.setMaxValues(menuOpts.max === 'all' ? menuOpts.options.length : menuOpts.max)
.setMinValues(0);
let options: MessageSelectOptionData[] = [];
for (let i = 0; i < menuOpts.options.length; i++) {
let opts: Opt.MenuOptions = menuOpts.options[i];
let option: MessageSelectOptionData = {
label: opts.label,
value: opts.value
};
if (opts.emoji && opts.emoji.length)
option.emoji = opts.emoji;
if (opts.description && opts.description.length)
option.description = opts.description;
if (opts.default)
option.default = true;
options.push(option);
}
select.addOptions(options);
row.addComponents(select)
let arg = {
content: menuOpts.message,
components: [row]
};
sendMessage(res, arg, ephemeral);
}
/**
* send message containing row
* @param c text channel to send message
* @param rowOpts row data object
*/
function sendRow(res: ReplyableInteraction|TextBasedChannels, rowOpts: Opt.Row, ephemeral?: boolean) {
let row = new MessageActionRow();
let options: MessageButton[] = [];
for (let i = 0; i < rowOpts.buttons.length; i++) {
let opts: Opt.RowButton = rowOpts.buttons[i];
let option = new MessageButton()
.setStyle(opts.style)
.setLabel(opts.label)
.setCustomId(opts.id);
options.push(option);
}
row.addComponents(options);
let arg = {
content: rowOpts.message,
components: [row]
};
sendMessage(res, arg, ephemeral);
}
//on interaction event
client.on('interactionCreate', (interaction: Interaction) => {
if (interaction.inGuild()) {
if (interaction.isSelectMenu())
handleSelect(interaction as SelectMenuInteraction);
else if (interaction.isButton())
handleButton(interaction as ButtonInteraction);
else if (interaction.isCommand())
handleCommand(interaction as CommandInteraction);
}
});
async function handleButton(interaction: ButtonInteraction) {
//start typing
await interaction.deferReply({ ephemeral: true });
//get custom id
let customId: string;
if (interaction.component instanceof MessageButton)
customId = interaction.component.customId;
else if ('custom_id' in interaction.component)
customId = interaction.component.custom_id;
//list of button rows from data
let rows = InteractionRoles.filter(opt => opt.type === 'row');
//loop through each the buttons rows
for (let i = 0; i < rows.length; i++) {
let row = rows[i] as Opt.Row;
//filter to just buttons with matching id
let matchingButtons = row.buttons.filter(opt => opt.id === customId);
if (matchingButtons.length) {
//if there's no role id, then nothing can be done
if (matchingButtons[0].roleid && interaction.member instanceof GuildMember) {
//since it is a GuildMember, let's force typescript to rememeber it
let member = interaction.member as GuildMember;
//get corresponding role
let role = await interaction.guild.roles.fetch(matchingButtons[0].roleid);
//if clicker (member) does have the role
if (member.roles.cache.some(r => r.id === role.id)) {
//remove role
await member.roles.remove(role.id);
interaction.editReply(`Removed \`${role.name}\``);
} else {
//add role
await member.roles.add(role.id);
interaction.editReply(`Added \`${role.name}\``);
}
break;
}
}
}
};
//on menu click
async function handleSelect(interaction: SelectMenuInteraction) {
//start typing
await interaction.deferReply({ ephemeral: true });
//get custom id
let customId: string;
if (interaction.component instanceof MessageSelectMenu)
customId = interaction.component.customId;
else if ('custom_id' in interaction.component)
customId = interaction.component.custom_id;
//list of menus from data with matching id
let menus = InteractionRoles.filter(opt => opt.type === 'menu' && opt.id === customId);
if (menus.length) {
//use first menu with matching id
let m = menus[0] as Opt.Menu,
vals = interaction.values || [];
//list of changes
let changed: {
add: string[],
rem: string[]
} = {
add: [],
rem: []
};
//loop through all the options in the menu
for (let i = 0; i < m.options.length; i++) {
let opt: Opt.MenuOptions = m.options[i],
{roleid} = opt;
//if there's no role id, then nothing can be done
if (roleid && interaction.member instanceof GuildMember) {
//since it is a GuildMember, let's force typescript to rememeber it
let member = interaction.member as GuildMember;
//get corresponding role
let role = await interaction.guild.roles.fetch(roleid);
//if enabled
if (vals && vals.includes(opt.value)) {
//if member doesn't have the role
if (!member.roles.cache.some(r => r.id === role.id)) {
//give role
await member.roles.add(role);
changed.add.push(role.name);
}
//else if member does have the role
} else if (member.roles.cache.some(r => r.id === role.id)) {
//remove role
await member.roles.remove(role);
changed.rem.push(role.name);
}
}
}
//convert changes to a message
let message = [];
if (changed.add.length)
message.push(`Added \`${changed.add.join(', ')}\``);
if (changed.rem.length)
message.push(`Removed \`${changed.rem.join(', ')}\``);
interaction.editReply(message.length ? message.join('\n') : 'No Changes');
} else
interaction.editReply('Error: Unknown menu');
};
//on slash command
async function handleCommand(interaction: CommandInteraction) {
console.log(interaction.commandName)
let category: number;
switch (interaction.commandName) {
case 'roles':
category = interaction.options.getInteger('category', true);
sendRoles(interaction, InteractionRoles[category].id, true);
break;
case 'rolesall':
if (interaction.user.id === '167336999844315137') {
let givenChannel = interaction.options.getChannel('channel'),
channel: TextChannel = await client.channels.fetch(interaction.channelId) as TextChannel;
if (givenChannel && givenChannel.type === 'GUILD_TEXT')
channel = givenChannel as TextChannel;
if (channel) {
category = interaction.options.getInteger('category');
sendRoles(channel, category ? InteractionRoles[category].id : undefined);
interaction.reply({
content: `sending message in <#${channel.id}>`,
ephemeral: true
})
} else
interaction.reply({
content: 'error finding channel to send message in',
ephemeral: true
})
} else
interaction.reply({
content: 'missing permissions',
ephemeral: true
})
break;
case 'updateroles':
//TODO reload data.ts
//TODO reload slash.ts
interaction.reply({
content: 'command incomplete',
ephemeral: true
})
break;
}
}
async function sendRoles(res: ReplyableInteraction|TextBasedChannels, id?: string, ephemeral?: boolean) {
if (id) {
let rowarr = InteractionRoles.filter(r => r.id === id);
if (rowarr.length) {
let row = rowarr[0];
if (row.type === 'menu')
sendMenu(res, row as Opt.Menu, ephemeral);
else if (row.type === 'row')
sendRow(res, row as Opt.Row, ephemeral);
} else
sendMessage(res, `invalid id ${id}`, ephemeral);
} else if (isReplyableInteraction(res))
sendMessage(res, `missing id`, ephemeral);
else {
for (let i = 0; i < InteractionRoles.length; i++) {
switch (InteractionRoles[i].type) {
case 'menu':
sendMenu(res, InteractionRoles[i] as Opt.Menu, ephemeral);
break;
case 'row':
sendRow(res, InteractionRoles[i] as Opt.Row, ephemeral);
break;
}
}
}
}
//register slash commands
client.on('ready', () => {
console.log('Ready')
setInterval(() => refreshCommands(client.user.id), 1000*60*60*24);
refreshCommands(client.user.id)
});
//login with token
client.login(require('fs').readFileSync('../token').toString());

View File

@@ -1,13 +0,0 @@
{
"name": "rolemanager",
"main": "index.ts",
"scripts": {
"start": "ts-node .",
"ts-node": "ts-node",
"watchdog": "./run.sh",
"tmux": "tmux new-session -d -s $npm_package_name \"./run.sh\"",
"resume": "tmux a -t $npm_package_name",
"log": "tail -f log/$(cat log/number).out",
"logerr": "tail -f log/$(cat log/number).err"
}
}

View File

@@ -1,67 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { REST } from '@discordjs/rest';
import { Routes } from 'discord-api-types/v9';
import { GuildId, InteractionRoles } from './data';
const commands = [
//roles ======
new SlashCommandBuilder()
.setName('roles')
.setDescription('displays the role selections')
.addIntegerOption((option) => {
option
.setName('category')
.setDescription('the category of roles that will be displayed')
.setRequired(true)
InteractionRoles.forEach((opt, i) => option.addChoice(opt.message, i));
return option;
})
.toJSON(),
//rolesall ======
new SlashCommandBuilder()
.setName('rolesall')
.setDescription('displays the role selections for everyone')
.addIntegerOption((option) => {
option
.setName('category')
.setDescription('the category of roles that will be displayed')
InteractionRoles.forEach((opt, i) => option.addChoice(opt.message, i));
return option;
})
.addChannelOption(option =>
option
.setName('channel')
.setDescription('the channel the message(s) will be sent in'))
.toJSON(),
//updateroles ======
new SlashCommandBuilder()
.setName('updateroles')
.setDescription('updates the cached role options')
.toJSON()
];
const rest = new REST({ version: '9' }).setToken(require('fs').readFileSync('../token').toString());
export default async function refreshCommands(ClientId: string) {
try {
await rest.put(
Routes.applicationGuildCommands(ClientId, GuildId),
{ body: commands },
);
} catch (error) {
console.error(error);
}
}

19
scripts/mac-linux/install Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
#change to ccurrent directory
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"
cd ../..
#discord token
[ ! -f "token.txt" ] && touch "token.txt"
if [ ! -s "token.txt" ]
then
printf "Enter your discord bot token: "
read token
printf $token > "token.txt"
fi
#run
npm install
npm run build

View File

@@ -1,4 +1,13 @@
#/bin/sh #!/bin/bash
#change to current directory
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"
cd ../..
#colors
C1='\033[1;37m'
C2='\033[1;33m'
NC='\033[0m'
#make log folder #make log folder
if [[ ! -d log ]] if [[ ! -d log ]]
@@ -18,14 +27,16 @@ echo $num > log/number
#watchdog #watchdog
while true while true
do do
#increment log file number #increment log file number
num=$(( num + 1 )) num=$(( num + 1 ))
echo $num > log/number echo $num > log/number
#run #run
npm start >> log/$num.out 2> log/$num.err npm start 2> log/$num.err | tee log/$num.out
#give chance to close program #give chance to close program
sleep 3 printf "\n\n\n${C1}press ${C2}Control+C${C1} to close${NC}\n\n\n"
echo "\n\n^C to exit\n\n" sleep 5
done done

12
scripts/mac-linux/start_screen Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
#change to ccurrent directory
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"
#start
if [[ $(screen -ls | grep $(npm run getname -s)) ]]; then
echo "Session already exists"
else
screen -S $(npm run getname -s) -d -m ./start
echo "Created session"
fi

12
scripts/mac-linux/start_tmux Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
#change to ccurrent directory
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"
#start
if [[ $(tmux ls | grep $(npm run getname -s)) ]]; then
echo "Session already exists"
else
tmux new-session -d -s $(npm run getname -s) ./start
echo "Created session"
fi

View File

@@ -0,0 +1,17 @@
@echo off
@REM change to ccurrent directory
cd %~f0\..\..\..\
@REM discord token
if not exist "token.txt" copy NUL "token.txt"
for /f %%i in ("token.txt") do set size=%%~zi
if %size% equ 0 (
set /p token="Enter your discord bot token: "
echo | set /p=%id% > "token.txt"
)
@REM run
npm install
npm run build

28
scripts/windows/start.bat Normal file
View File

@@ -0,0 +1,28 @@
@echo off
@Rem change to current directory
cd %~f0\..\..\..\
@Rem make log folder
if not exist log mkdir log
@Rem current file number
set /A num=0
if exist log\number set /p num=<log\number
echo %num > log\number
@Rem watchdog
:watchdog
@Rem increment log file number
set /A num=num+1
echo %num > log\number
@Rem run
powershell "npm start 2> log\%num%.err | tee log\%num%.out"
@Rem give chance to close program
timeout /t 5 /nobreak
goto watchdog

69
src/lang.ts Normal file
View File

@@ -0,0 +1,69 @@
import Lang, { setLang } from "lang";
import { setEmsgShim } from "discordslash";
export default function() {
setLang({
main: {
login: 'Logged in as {user}'
},
command: {
ping: {
embed: true,
title: 'Pong!',
description: [
'Woah!',
'This descriptions has multiple lines'
],
color: '#a0cc75',
timestamp: true,
footer: {
text: '{username}',
iconURL: '{avatar}'
}
},
roles: {
description: {
command: 'displays the role selections',
category: 'the category of roles that will be displayed'
}
},
rolesall: {
description: {
command: 'displays the role selections for everyone',
category: 'the category of roles that will be displayed',
channel: 'the channel the message(s) will be sent in'
}
}
},
error: {
main: {
missingToken: 'Missing Discord Token, please enter the bot token into the token file'
},
general: {
noUser: 'Unable to retrieve guild user information, please try again',
noMember: 'Unable to retrieve guild member information, please try again',
noChannel: 'Unable to retrieve text channel information, please try again'
},
interaction: {
noid: 'Interaction ID not found',
norole: 'No role found from interaction'
}
}
})
setEmsgShim(Lang)
}

33
src/main.ts Normal file
View File

@@ -0,0 +1,33 @@
import { Client, Intents } from 'discord.js';
import { initClient } from 'discordslash';
import Lang from 'lang';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import SetLang from './lang';
import { initRolemanager } from './rolemanager';
//init logs with a timestamp
console.log(new Date().toISOString()+'\n\n');
SetLang();
//get token
if (!existsSync('./token.txt')) {
writeFileSync('./token.txt', '');
console.error(Lang.get('error.main.missingToken'));
process.exit(1);
}
const TOKEN = readFileSync('./token.txt').toString(),
CLIENT = new Client({ intents: [Intents.FLAGS.GUILDS] });
initClient(CLIENT);
initRolemanager(CLIENT);
//discord connections
CLIENT.on('ready', client => {
console.log(Lang.get('main.login', {
user: client.user.tag
}));
});
CLIENT.login(TOKEN);

View File

@@ -0,0 +1,38 @@
import { CommandInteraction, TextChannel } from "discord.js";
import { InteractionRoles } from "./data";
import { sendRoles } from "../util/interactions";
export function RolesCommand(interaction: CommandInteraction) {
let category = interaction.options.getInteger('category', true);
sendRoles(interaction, InteractionRoles[category].id, true);
}
export async function RolesAllCommand(interaction: CommandInteraction) {
if (interaction.user.id === '167336999844315137') {
let givenChannel = interaction.options.getChannel('channel'),
channel: TextChannel = await interaction.client.channels.fetch(interaction.channelId) as TextChannel;
if (givenChannel && givenChannel.type === 'GUILD_TEXT')
channel = givenChannel as TextChannel;
if (channel) {
let category = interaction.options.getInteger('category');
sendRoles(channel, category ? InteractionRoles[category].id : undefined);
interaction.reply({
content: `sending message in <#${channel.id}>`,
ephemeral: true
})
} else
interaction.reply({
content: 'error finding channel to send message in',
ephemeral: true
})
} else
interaction.reply({
content: 'missing permissions',
ephemeral: true
})
}

View File

@@ -32,9 +32,14 @@ export const InteractionRoles: (Opt.Row|Opt.Menu)[] = [
}, },
{ {
label: 'Fae/Faer', label: 'Fae/Faer',
value: 'fae', value: 'faefaer',
roleid: '862687559464386601' roleid: '862687559464386601'
}, },
{
label: 'Fae/Fem',
value: 'faefem',
roleid: '985215761796694106'
},
{ {
label: 'Any', label: 'Any',
value: 'any', value: 'any',
@@ -118,6 +123,11 @@ export const InteractionRoles: (Opt.Row|Opt.Menu)[] = [
label: 'Dungeons and Dragons', label: 'Dungeons and Dragons',
value: 'dnd', value: 'dnd',
roleid: '872684620666265602' roleid: '872684620666265602'
},
{
label: 'VR Haver',
value: 'vr',
roleid: '927020684918534195'
} }
] ]
} }

186
src/rolemanager/index.ts Normal file
View File

@@ -0,0 +1,186 @@
import { ButtonInteraction, Client, GuildMember, Interaction, MessageButton, MessageSelectMenu, SelectMenuInteraction } from "discord.js";
import { emsg } from "discordslash";
import * as Opt from "../types/Opt";
import { getGuild, getMember } from "../util/discord";
import { InteractionRoles } from "./data";
import { initSlash } from "./slash";
export function initRolemanager(client: Client) {
initSlash();
//on interaction event
client.on('interactionCreate', (interaction: Interaction) => {
if (!interaction.inGuild()) return;
try {
if (interaction.isSelectMenu())
handleSelect(interaction as SelectMenuInteraction);
else if (interaction.isButton())
handleButton(interaction as ButtonInteraction);
} catch (e) {
console.error(e);
}
});
}
async function handleButton(interaction: ButtonInteraction) {
//start typing
await interaction.deferReply({ ephemeral: true });
//get custom id
let customId: string;
if (interaction.component instanceof MessageButton && interaction.component.customId !== null)
customId = interaction.component.customId;
else if ('custom_id' in interaction.component)
customId = interaction.component.custom_id;
else
throw emsg('interaction.noid');
//list of button rows from data
let rows = InteractionRoles.filter(opt => opt.type === 'row');
//loop through each the buttons rows
for (let i = 0; i < rows.length; i++) {
let row = rows[i] as Opt.Row;
//filter to just buttons with matching id
let matchingButtons = row.buttons.filter(opt => opt && opt.id === customId) as Opt.RowButton[];
if (matchingButtons.length) {
let member = getMember(interaction);
//if there's no role id, then nothing can be done
if (matchingButtons[0].roleid) {
//get corresponding role
let role = await getGuild(interaction).roles.fetch(matchingButtons[0].roleid);
if (role === null)
throw emsg('interaction.norole');
//if clicker (member) does have the role
//TODO why do i need role !== null here
if (member.roles.cache.some(r => role !== null && r.id === role.id)) {
//remove role
await member.roles.remove(role.id);
interaction.editReply(`Removed \`${role.name}\``);
} else {
//add role
await member.roles.add(role.id);
interaction.editReply(`Added \`${role.name}\``);
}
break;
}
}
}
};
//on menu click
async function handleSelect(interaction: SelectMenuInteraction) {
//start typing
await interaction.deferReply({ ephemeral: true });
//get custom id
let customId: string;
if (interaction.component instanceof MessageSelectMenu && interaction.component.customId !== null)
customId = interaction.component.customId;
else if ('custom_id' in interaction.component)
customId = interaction.component.custom_id;
//list of menus from data with matching id
let menus = InteractionRoles.filter(opt => opt.type === 'menu' && opt.id === customId);
if (menus.length) {
//use first menu with matching id
let m = menus[0] as Opt.Menu,
vals = interaction.values || [];
//list of changes
let changed: {
add: string[],
rem: string[]
} = {
add: [],
rem: []
};
//loop through all the options in the menu
for (let i = 0; i < m.options.length; i++) {
let opt: Opt.MenuOptions = m.options[i],
{roleid} = opt;
//if there's no role id, then nothing can be done
if (roleid && interaction.member instanceof GuildMember) {
//since it is a GuildMember, let's force typescript to rememeber it
let member = getMember(interaction);
//get corresponding role
let role = await getGuild(interaction).roles.fetch(roleid);
if (role === null)
throw emsg('interaction.norole');
//if enabled
if (vals && vals.includes(opt.value)) {
//if member doesn't have the role
if (!member.roles.cache.some(r => role !== null && r.id === role.id)) {
//give role
await member.roles.add(role);
changed.add.push(role.name);
}
//else if member does have the role
} else if (member.roles.cache.some(r => role !== null && r.id === role.id)) {
//remove role
await member.roles.remove(role);
changed.rem.push(role.name);
}
}
}
//convert changes to a message
let message = [];
if (changed.add.length)
message.push(`Added \`${changed.add.join(', ')}\``);
if (changed.rem.length)
message.push(`Removed \`${changed.rem.join(', ')}\``);
interaction.editReply(message.length ? message.join('\n') : 'No Changes');
} else
interaction.editReply('Error: Unknown menu');
};

52
src/rolemanager/slash.ts Normal file
View File

@@ -0,0 +1,52 @@
import { addCommand, CommandGenerator, CommandOptionGenerator, OptionChoices } from 'discordslash';
import { InteractionRoles } from './data';
import Lang from 'lang';
import { RolesAllCommand, RolesCommand } from './commands';
export function initSlash() {
const Categories: OptionChoices[] = InteractionRoles.map(opt => ({
name: opt.message,
value: opt.id
}));
addCommand([
//roles ======
new CommandGenerator()
.setName('roles')
.setDescription(Lang.get('command.roles.description.command'))
.addOption([
new CommandOptionGenerator()
.setName('category')
.setType('string')
.setDescription(Lang.get('commands.roles.categorydescription.category'))
.addChoice(Categories)
.setRequired()
])
.setRun(RolesCommand),
//rolesall ======
new CommandGenerator()
.setName('rolesall')
.setDescription(Lang.get('command.rolesall.description.command'))
.addOption([
new CommandOptionGenerator()
.setName('category')
.setType('string')
.setDescription(Lang.get('command.rolesall.description.category'))
.addChoice(Categories),
new CommandOptionGenerator()
.setName('channel')
.setType('channel')
.setDescription(Lang.get('command.rolesall.description.command'))
])
.setRun(RolesAllCommand)
]);
}

View File

@@ -1,6 +1,5 @@
import { MessageButtonStyleResolvable } from 'discord.js'; import { MessageButtonStyleResolvable } from 'discord.js';
//menu //menu
export interface MenuOptions { export interface MenuOptions {
label: string; label: string;

58
src/util/discord.ts Normal file
View File

@@ -0,0 +1,58 @@
import { ButtonInteraction, CommandInteraction, Guild, GuildMember, SelectMenuInteraction, TextChannel, User } from 'discord.js';
import { emsg } from 'discordslash';
export type ReplyableInteraction = ButtonInteraction | CommandInteraction | SelectMenuInteraction;
export const isReplyableInteraction = (res: any): boolean => res instanceof ButtonInteraction || res instanceof CommandInteraction || res instanceof SelectMenuInteraction;
/**
* get the User of an interaction
* @throws errorMessage class if it cannot be read
*/
export function getUser(interaction: ReplyableInteraction): User {
const user = interaction.user;
if (!(user instanceof User))
throw emsg('general.noUser');
return user;
}
/**
* get the GuildMember of an interaction
* @throws errorMessage class if it cannot be read
*/
export function getMember(interaction: ReplyableInteraction): GuildMember {
const member = interaction.member;
if (!(member instanceof GuildMember))
throw emsg('general.noMember');
return member;
}
/**
* get the TextChannel of an interaction
* @throws errorMessage class if it cannot be read
*/
export function getChannel(interaction: ReplyableInteraction): TextChannel {
const channel = interaction.channel;
if (!(channel instanceof TextChannel))
throw emsg('general.noChannel');
return channel;
}
/**
* get the TextChannel of an interaction
* @throws errorMessage class if it cannot be read
*/
export function getGuild(interaction: ReplyableInteraction): Guild {
const guild = interaction.guild;
if (!(guild instanceof Guild))
throw emsg('general.noGuild');
return guild;
}

148
src/util/interactions.ts Normal file
View File

@@ -0,0 +1,148 @@
import { MessageActionRow, MessageButton, MessageSelectMenu, MessageSelectOptionData, TextBasedChannel } from "discord.js";
import { InteractionRoles } from "../rolemanager/data";
import * as Opt from "../types/Opt";
import { isReplyableInteraction, ReplyableInteraction } from "./discord";
export const sendMessage = (res: ReplyableInteraction | TextBasedChannel, msg: any, ephemeral?: boolean) => {
let arg: any = {};
if (typeof msg === 'object')
arg = msg;
else
arg.content = msg;
if (isReplyableInteraction(res) && ephemeral)
arg.ephemeral = true;
if (isReplyableInteraction(res))
(res as ReplyableInteraction).reply(arg);
else
(res as TextBasedChannel).send(arg);
}
/**
* send message containing menu
* @param res text channel to send message or interaction to reply to
* @param menuOpts menu data object
*/
function sendMenu(res: ReplyableInteraction|TextBasedChannel, menuOpts: Opt.Menu, ephemeral?: boolean) {
let row = new MessageActionRow()
let select = new MessageSelectMenu()
.setCustomId(menuOpts.id)
.setPlaceholder(menuOpts.palceholder)
.setMaxValues(menuOpts.max === 'all' ? menuOpts.options.length : menuOpts.max)
.setMinValues(0);
let options: MessageSelectOptionData[] = [];
for (let i = 0; i < menuOpts.options.length; i++) {
let opts: Opt.MenuOptions = menuOpts.options[i];
let option: MessageSelectOptionData = {
label: opts.label,
value: opts.value
};
if (opts.emoji && opts.emoji.length)
option.emoji = opts.emoji;
if (opts.description && opts.description.length)
option.description = opts.description;
if (opts.default)
option.default = true;
options.push(option);
}
select.addOptions(options);
row.addComponents(select)
let arg = {
content: menuOpts.message,
components: [row]
};
sendMessage(res, arg, ephemeral);
}
/**
* send message containing row
* @param res text channel to send message
* @param rowOpts row data object
*/
function sendRow(res: ReplyableInteraction|TextBasedChannel, rowOpts: Opt.Row, ephemeral?: boolean) {
let row = new MessageActionRow();
let options: MessageButton[] = [];
for (let i = 0; i < rowOpts.buttons.length; i++) {
let opts = rowOpts.buttons[i];
if (opts) {
let option = new MessageButton()
.setStyle(opts.style)
.setLabel(opts.label)
.setCustomId(opts.id);
options.push(option);
}
}
row.addComponents(options);
let arg = {
content: rowOpts.message,
components: [row]
};
sendMessage(res, arg, ephemeral);
}
export async function sendRoles(res: ReplyableInteraction|TextBasedChannel, id?: string, ephemeral?: boolean) {
if (id) {
let rowarr = InteractionRoles.filter(r => r.id === id);
if (rowarr.length) {
let row = rowarr[0];
if (row.type === 'menu')
sendMenu(res, row as Opt.Menu, ephemeral);
else if (row.type === 'row')
sendRow(res, row as Opt.Row, ephemeral);
} else
sendMessage(res, `invalid id ${id}`, ephemeral);
} else if (isReplyableInteraction(res))
sendMessage(res, `missing id`, ephemeral);
else {
for (let i = 0; i < InteractionRoles.length; i++) {
switch (InteractionRoles[i].type) {
case 'menu':
sendMenu(res, InteractionRoles[i] as Opt.Menu, ephemeral);
break;
case 'row':
sendRow(res, InteractionRoles[i] as Opt.Row, ephemeral);
break;
}
}
}
}

View File

@@ -1,8 +1,21 @@
{ {
"compilerOptions": { "compilerOptions": {
"strictNullChecks": false, "target": "es2020",
"lib": [ "module": "commonjs",
"ESNext" "rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"strict": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"typeRoots": [
"./src/types/"
]
},
"include": [
"src"
] ]
}
} }