Merge pull request #80 from veganhacktivists/main

Update branch
This commit is contained in:
Anthony Berg 2023-02-18 12:15:45 +00:00 committed by GitHub
commit 06a05d4240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1145 additions and 271 deletions

405
package-lock.json generated
View File

@ -11,14 +11,15 @@
"dependencies": {
"@prisma/client": "^4.10.1",
"@sapphire/discord.js-utilities": "^6.0.0",
"@sapphire/framework": "^4.0.1",
"@sapphire/framework": "^4.1.0",
"@sapphire/plugin-logger": "^3.0.1",
"@sapphire/plugin-scheduled-tasks": "^4.0.0",
"@sapphire/plugin-scheduled-tasks": "^6.0.0",
"@sapphire/plugin-subcommands": "^4.0.0",
"@sapphire/stopwatch": "^1.4.1",
"@sapphire/stopwatch": "^1.5.0",
"@sapphire/time-utilities": "^1.7.8",
"@sapphire/utilities": "^3.9.2",
"@types/node": "^18.0.3",
"bullmq": "^1.89.1",
"bullmq": "^3.6.6",
"discord.js": "^14.7.1",
"dotenv": "^16.0.1",
"ts-node": "^10.8.2",
@ -346,6 +347,18 @@
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/cron": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/cron/-/cron-1.0.0.tgz",
"integrity": "sha512-pKYfpnHiDFknur3yoquA8cqbJZS140y2oqjshwGGmtjiuIbUngQhPHGwdWHNDKDrF6EKbOK06nd2URE+0eUrfQ==",
"dependencies": {
"@sapphire/utilities": "^3.9.3"
},
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/discord-utilities": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/discord-utilities/-/discord-utilities-3.0.0.tgz",
@ -383,9 +396,9 @@
}
},
"node_modules/@sapphire/framework": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.0.2.tgz",
"integrity": "sha512-IoSZGBPJjiINJKJKaBfnpEB1IxPv7yitunnvJ6V5XcTdxP51I/KsVJX2ELxiH7sslg8ZrQQMRIcluGLbVwv4KA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.1.0.tgz",
"integrity": "sha512-jtwZPysF13Sn8h2p8nkIPETveGAxRmYmiqxYkd3VXV8VPWwKBG8IriuI4oExpSnuCqxIs5HRpo3M+Gl+f/mdCg==",
"dependencies": {
"@discordjs/builders": "^1.4.0",
"@sapphire/discord-utilities": "^3.0.0",
@ -443,12 +456,12 @@
}
},
"node_modules/@sapphire/plugin-scheduled-tasks": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-4.0.1.tgz",
"integrity": "sha512-vLxfHBu2vKaJZ9v2f4z+VDZaPeDqS8bm+Sc2minRwJPw1hWAHiPqmxCBPIONY7eOQ9qKayvhKYTIwwruxgO/Mg==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-6.0.0.tgz",
"integrity": "sha512-R9rga1aZk3GSXkmGfBMQR8Ng4ou36l5WGWoDvPaq1xNb56wZJ2zPZPsQHV6lxNoyOT++M8GFhVhh9OUB+xwEXg==",
"dependencies": {
"@sapphire/stopwatch": "^1.4.1",
"@sapphire/utilities": "^3.9.3",
"@sapphire/stopwatch": "^1.5.0",
"@sapphire/utilities": "^3.11.0",
"tslib": "^2.4.0"
},
"engines": {
@ -524,6 +537,21 @@
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/time-utilities": {
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/@sapphire/time-utilities/-/time-utilities-1.7.8.tgz",
"integrity": "sha512-T6X/nwCvKhxmNRexgmA3KwLt3Z+xzlErkre4viflx46hHOmNNb3hoIyQtekgHYrabEaHWNbqW4PW7gC3hBc+ag==",
"dependencies": {
"@sapphire/cron": "^1.0.0",
"@sapphire/duration": "^1.0.0",
"@sapphire/timer-manager": "^1.0.0",
"@sapphire/timestamp": "^1.0.0"
},
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/timer-manager": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/timer-manager/-/timer-manager-1.0.0.tgz",
@ -612,9 +640,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "18.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.19.tgz",
"integrity": "sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw=="
"version": "18.13.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg=="
},
"node_modules/@types/semver": {
"version": "7.3.13",
@ -631,14 +659,14 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz",
"integrity": "sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz",
"integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.51.0",
"@typescript-eslint/type-utils": "5.51.0",
"@typescript-eslint/utils": "5.51.0",
"@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/type-utils": "5.52.0",
"@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
@ -665,14 +693,14 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz",
"integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz",
"integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.51.0",
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/typescript-estree": "5.51.0",
"@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.52.0",
"debug": "^4.3.4"
},
"engines": {
@ -692,13 +720,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz",
"integrity": "sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz",
"integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/visitor-keys": "5.51.0"
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.52.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -709,13 +737,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz",
"integrity": "sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz",
"integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "5.51.0",
"@typescript-eslint/utils": "5.51.0",
"@typescript-eslint/typescript-estree": "5.52.0",
"@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@ -736,9 +764,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz",
"integrity": "sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz",
"integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -749,13 +777,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz",
"integrity": "sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz",
"integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/visitor-keys": "5.51.0",
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.52.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -776,16 +804,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz",
"integrity": "sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz",
"integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.51.0",
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/typescript-estree": "5.51.0",
"@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.52.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
@ -802,12 +830,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz",
"integrity": "sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz",
"integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/types": "5.52.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@ -1001,14 +1029,13 @@
}
},
"node_modules/bullmq": {
"version": "1.91.1",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.91.1.tgz",
"integrity": "sha512-u7dat9I8ZwouZ651AMZkBSvB6NVUPpnAjd4iokd9DM41whqIBnDjuL11h7+kEjcpiDKj6E+wxZiER00FqirZQg==",
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-3.6.6.tgz",
"integrity": "sha512-W71jXrcTdcT3Y5tzMyTx22Cd8O3dTML7vl6KG3YdGVGrO3+UmKRLYfGLn1QwIhIoTQJVvIrSB4qfGs1hgqYRVw==",
"dependencies": {
"cron-parser": "^4.6.0",
"get-port": "6.1.2",
"glob": "^8.0.3",
"ioredis": "^5.2.2",
"ioredis": "^5.3.0",
"lodash": "^4.17.21",
"msgpackr": "^1.6.2",
"semver": "^7.3.7",
@ -1170,9 +1197,9 @@
}
},
"node_modules/define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"dev": true,
"dependencies": {
"has-property-descriptors": "^1.0.0",
@ -1214,9 +1241,9 @@
}
},
"node_modules/discord-api-types": {
"version": "0.37.31",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.31.tgz",
"integrity": "sha512-k9DQQ7Wv+ehiF7901qk/FnP47k6O2MHm3meQFee4gUzi5dfGAVLf7SfLNtb4w7G2dmukJyWQtVJEDF9oMb9yuQ=="
"version": "0.37.33",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.33.tgz",
"integrity": "sha512-ZMH5RU3q1pvYS+2wGUJ5Zvy8jMGTQ4wCpbDlIQDkbIL/k6kJwBPsXnCg81g2GywlOuf0f8ezakxVSe+sZuY6ig=="
},
"node_modules/discord.js": {
"version": "14.7.1",
@ -1976,17 +2003,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-port": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz",
"integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-symbol-description": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@ -2296,12 +2312,12 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/internal-slot": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
"integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
"integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3",
"get-intrinsic": "^1.2.0",
"has": "^1.0.3",
"side-channel": "^1.0.4"
},
@ -2310,9 +2326,9 @@
}
},
"node_modules/ioredis": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz",
"integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz",
"integrity": "sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
@ -2741,9 +2757,9 @@
}
},
"node_modules/minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -2882,9 +2898,9 @@
}
},
"node_modules/open": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz",
"integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==",
"dev": true,
"dependencies": {
"define-lazy-prop": "^2.0.0",
@ -3537,9 +3553,9 @@
}
},
"node_modules/ts-mixer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz",
"integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A=="
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
},
"node_modules/ts-node": {
"version": "10.9.1",
@ -3687,9 +3703,9 @@
}
},
"node_modules/undici": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.18.0.tgz",
"integrity": "sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA==",
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz",
"integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==",
"dependencies": {
"busboy": "^1.6.0"
},
@ -3796,9 +3812,9 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/ws": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"engines": {
"node": ">=10.0.0"
},
@ -4052,6 +4068,14 @@
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
"integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA=="
},
"@sapphire/cron": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/cron/-/cron-1.0.0.tgz",
"integrity": "sha512-pKYfpnHiDFknur3yoquA8cqbJZS140y2oqjshwGGmtjiuIbUngQhPHGwdWHNDKDrF6EKbOK06nd2URE+0eUrfQ==",
"requires": {
"@sapphire/utilities": "^3.9.3"
}
},
"@sapphire/discord-utilities": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/discord-utilities/-/discord-utilities-3.0.0.tgz",
@ -4077,9 +4101,9 @@
"integrity": "sha512-B+6nKYnBmIlqqbamcR4iBvbQHz6/Kq2JUVM0rA3lQ+aYUYDdcA1Spt66CKtPWwdTYEtSv0VY6Jv27WCtFNYTUg=="
},
"@sapphire/framework": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.0.2.tgz",
"integrity": "sha512-IoSZGBPJjiINJKJKaBfnpEB1IxPv7yitunnvJ6V5XcTdxP51I/KsVJX2ELxiH7sslg8ZrQQMRIcluGLbVwv4KA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.1.0.tgz",
"integrity": "sha512-jtwZPysF13Sn8h2p8nkIPETveGAxRmYmiqxYkd3VXV8VPWwKBG8IriuI4oExpSnuCqxIs5HRpo3M+Gl+f/mdCg==",
"requires": {
"@discordjs/builders": "^1.4.0",
"@sapphire/discord-utilities": "^3.0.0",
@ -4121,12 +4145,12 @@
}
},
"@sapphire/plugin-scheduled-tasks": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-4.0.1.tgz",
"integrity": "sha512-vLxfHBu2vKaJZ9v2f4z+VDZaPeDqS8bm+Sc2minRwJPw1hWAHiPqmxCBPIONY7eOQ9qKayvhKYTIwwruxgO/Mg==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-6.0.0.tgz",
"integrity": "sha512-R9rga1aZk3GSXkmGfBMQR8Ng4ou36l5WGWoDvPaq1xNb56wZJ2zPZPsQHV6lxNoyOT++M8GFhVhh9OUB+xwEXg==",
"requires": {
"@sapphire/stopwatch": "^1.4.1",
"@sapphire/utilities": "^3.9.3",
"@sapphire/stopwatch": "^1.5.0",
"@sapphire/utilities": "^3.11.0",
"tslib": "^2.4.0"
}
},
@ -4174,6 +4198,17 @@
"tslib": "^2.4.0"
}
},
"@sapphire/time-utilities": {
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/@sapphire/time-utilities/-/time-utilities-1.7.8.tgz",
"integrity": "sha512-T6X/nwCvKhxmNRexgmA3KwLt3Z+xzlErkre4viflx46hHOmNNb3hoIyQtekgHYrabEaHWNbqW4PW7gC3hBc+ag==",
"requires": {
"@sapphire/cron": "^1.0.0",
"@sapphire/duration": "^1.0.0",
"@sapphire/timer-manager": "^1.0.0",
"@sapphire/timestamp": "^1.0.0"
}
},
"@sapphire/timer-manager": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/timer-manager/-/timer-manager-1.0.0.tgz",
@ -4246,9 +4281,9 @@
"dev": true
},
"@types/node": {
"version": "18.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.19.tgz",
"integrity": "sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw=="
"version": "18.13.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg=="
},
"@types/semver": {
"version": "7.3.13",
@ -4265,14 +4300,14 @@
}
},
"@typescript-eslint/eslint-plugin": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz",
"integrity": "sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz",
"integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.51.0",
"@typescript-eslint/type-utils": "5.51.0",
"@typescript-eslint/utils": "5.51.0",
"@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/type-utils": "5.52.0",
"@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
@ -4283,53 +4318,53 @@
}
},
"@typescript-eslint/parser": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz",
"integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz",
"integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.51.0",
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/typescript-estree": "5.51.0",
"@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.52.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz",
"integrity": "sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz",
"integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/visitor-keys": "5.51.0"
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.52.0"
}
},
"@typescript-eslint/type-utils": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz",
"integrity": "sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz",
"integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "5.51.0",
"@typescript-eslint/utils": "5.51.0",
"@typescript-eslint/typescript-estree": "5.52.0",
"@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/types": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz",
"integrity": "sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz",
"integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz",
"integrity": "sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz",
"integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/visitor-keys": "5.51.0",
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.52.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -4338,28 +4373,28 @@
}
},
"@typescript-eslint/utils": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz",
"integrity": "sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz",
"integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.51.0",
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/typescript-estree": "5.51.0",
"@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.52.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.51.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz",
"integrity": "sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==",
"version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz",
"integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.51.0",
"@typescript-eslint/types": "5.52.0",
"eslint-visitor-keys": "^3.3.0"
}
},
@ -4492,14 +4527,13 @@
}
},
"bullmq": {
"version": "1.91.1",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.91.1.tgz",
"integrity": "sha512-u7dat9I8ZwouZ651AMZkBSvB6NVUPpnAjd4iokd9DM41whqIBnDjuL11h7+kEjcpiDKj6E+wxZiER00FqirZQg==",
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-3.6.6.tgz",
"integrity": "sha512-W71jXrcTdcT3Y5tzMyTx22Cd8O3dTML7vl6KG3YdGVGrO3+UmKRLYfGLn1QwIhIoTQJVvIrSB4qfGs1hgqYRVw==",
"requires": {
"cron-parser": "^4.6.0",
"get-port": "6.1.2",
"glob": "^8.0.3",
"ioredis": "^5.2.2",
"ioredis": "^5.3.0",
"lodash": "^4.17.21",
"msgpackr": "^1.6.2",
"semver": "^7.3.7",
@ -4623,9 +4657,9 @@
"dev": true
},
"define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"dev": true,
"requires": {
"has-property-descriptors": "^1.0.0",
@ -4652,9 +4686,9 @@
}
},
"discord-api-types": {
"version": "0.37.31",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.31.tgz",
"integrity": "sha512-k9DQQ7Wv+ehiF7901qk/FnP47k6O2MHm3meQFee4gUzi5dfGAVLf7SfLNtb4w7G2dmukJyWQtVJEDF9oMb9yuQ=="
"version": "0.37.33",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.33.tgz",
"integrity": "sha512-ZMH5RU3q1pvYS+2wGUJ5Zvy8jMGTQ4wCpbDlIQDkbIL/k6kJwBPsXnCg81g2GywlOuf0f8ezakxVSe+sZuY6ig=="
},
"discord.js": {
"version": "14.7.1",
@ -5247,11 +5281,6 @@
"has-symbols": "^1.0.3"
}
},
"get-port": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz",
"integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw=="
},
"get-symbol-description": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@ -5465,20 +5494,20 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"internal-slot": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
"integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
"integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
"dev": true,
"requires": {
"get-intrinsic": "^1.1.3",
"get-intrinsic": "^1.2.0",
"has": "^1.0.3",
"side-channel": "^1.0.4"
}
},
"ioredis": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz",
"integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz",
"integrity": "sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==",
"requires": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
@ -5780,9 +5809,9 @@
}
},
"minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true
},
"ms": {
@ -5886,9 +5915,9 @@
}
},
"open": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz",
"integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==",
"dev": true,
"requires": {
"define-lazy-prop": "^2.0.0",
@ -6308,9 +6337,9 @@
}
},
"ts-mixer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz",
"integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A=="
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
},
"ts-node": {
"version": "10.9.1",
@ -6410,9 +6439,9 @@
}
},
"undici": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.18.0.tgz",
"integrity": "sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA==",
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz",
"integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==",
"requires": {
"busboy": "^1.6.0"
}
@ -6495,9 +6524,9 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"requires": {}
},
"yallist": {

View File

@ -1,6 +1,6 @@
{
"name": "arabot",
"version": "0.1.0",
"version": "0.2.1",
"description": "A Discord bot for Animal Rights Advocates",
"main": "dist/index.js",
"scripts": {
@ -31,14 +31,15 @@
"dependencies": {
"@prisma/client": "^4.10.1",
"@sapphire/discord.js-utilities": "^6.0.0",
"@sapphire/framework": "^4.0.1",
"@sapphire/framework": "^4.1.0",
"@sapphire/plugin-logger": "^3.0.1",
"@sapphire/plugin-scheduled-tasks": "^4.0.0",
"@sapphire/plugin-scheduled-tasks": "^6.0.0",
"@sapphire/plugin-subcommands": "^4.0.0",
"@sapphire/stopwatch": "^1.4.1",
"@sapphire/stopwatch": "^1.5.0",
"@sapphire/time-utilities": "^1.7.8",
"@sapphire/utilities": "^3.9.2",
"@types/node": "^18.0.3",
"bullmq": "^1.89.1",
"bullmq": "^3.6.6",
"discord.js": "^14.7.1",
"dotenv": "^16.0.1",
"ts-node": "^10.8.2",

View File

@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "TempBan" ADD COLUMN "endModId" TEXT;
-- AddForeignKey
ALTER TABLE "TempBan" ADD CONSTRAINT "TempBan_endModId_fkey" FOREIGN KEY ("endModId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@ -49,6 +49,7 @@ model User {
BanEndMod Ban[] @relation("endBanMod")
TempBanUser TempBan[] @relation("tbanUser")
TempBanMod TempBan[] @relation("tbanMod")
TempBanEndMod TempBan[] @relation("endTbanMod")
VCMuteUser VCMute[] @relation("vcMuteUser")
VCMuteMod VCMute[] @relation("vcMuteMod")
}
@ -178,6 +179,8 @@ model TempBan {
mod User @relation("tbanMod", fields: [modId], references: [id])
modId String
startTime DateTime @default(now())
endMod User? @relation("endTbanMod", fields: [endModId], references: [id])
endModId String?
endTime DateTime
active Boolean @default(true)
reason String

View File

@ -27,8 +27,9 @@ import type {
} from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import IDs from '#utils/ids';
import { addBan, checkActive } from '#utils/database/ban';
import { addBan, checkBan } from '#utils/database/ban';
import { addEmptyUser, updateUser, userExists } from '#utils/database/dbExistingUser';
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
export class BanCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
@ -149,7 +150,7 @@ export class BanCommand extends Command {
return info;
}
if (await checkActive(userId)) {
if (await checkBan(userId)) {
info.message = `${user} is already banned!`;
return info;
}
@ -188,6 +189,10 @@ export class BanCommand extends Command {
// Add ban to database
await addBan(userId, modId, reason);
if (await checkTempBan(userId)) {
await removeTempBan(userId);
}
info.message = `${user} has been banned.`;
info.success = true;

View File

@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2023 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { Duration, DurationFormatter } from '@sapphire/time-utilities';
import type {
User,
Snowflake,
TextChannel,
Guild,
} from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import IDs from '#utils/ids';
import { addTempBan, checkTempBan } from '#utils/database/tempBan';
import { addEmptyUser, updateUser, userExists } from '#utils/database/dbExistingUser';
export class TempBanCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: 'tempban',
description: 'Bans a user for a certain amount of time',
preconditions: ['RestrictedAccessOnly'],
});
}
// Registers that this is a slash command
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand(
(builder) => builder
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) => option.setName('user')
.setDescription('User to ban')
.setRequired(true))
.addStringOption((option) => option.setName('duration')
.setDescription('How long to ban the user for')
.setRequired(true))
.addStringOption((option) => option.setName('reason')
.setDescription('Note about the user')
.setRequired(true)),
{
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
},
);
}
// Command run
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
// Get the arguments
const user = interaction.options.getUser('user', true);
const duration = interaction.options.getString('duration', true);
const reason = interaction.options.getString('reason', true);
const mod = interaction.member;
const { guild } = interaction;
// Checks if all the variables are of the right type
if (guild === null || mod === null) {
await interaction.reply({
content: 'Error fetching user!',
ephemeral: true,
fetchReply: true,
});
return;
}
const time = new Duration(duration);
if (Number.isNaN(time.offset)) {
await interaction.reply({
content: 'Invalid ban duration input',
});
return;
}
const ban = await this.ban(user.id, mod.user.id, time, reason, guild);
await interaction.reply({ content: ban.message });
}
private async ban(
userId: Snowflake,
modId: Snowflake,
time: Duration,
reason: string,
guild: Guild,
) {
const info = {
message: '',
success: false,
};
const banLength = new DurationFormatter().format(time.offset);
let user = guild.client.users.cache.get(userId);
if (user === undefined) {
user = await guild.client.users.fetch(userId) as User;
}
// Gets mod's GuildMember
const mod = guild.members.cache.get(modId);
// Checks if guildMember is null
if (mod === undefined) {
info.message = 'Error fetching mod!';
return info;
}
if (await checkTempBan(userId)) {
info.message = `${user} is already temp banned!`;
return info;
}
// Check if mod is in database
await updateUser(mod);
// Gets guildMember
let member = guild.members.cache.get(userId);
if (member === undefined) {
member = await guild.members.fetch(userId)
.catch(() => undefined);
}
if (member !== undefined) {
// Checks if the user is not restricted
if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
info.message = 'You need to restrict the user first!';
return info;
}
await updateUser(member);
// Send DM for reason of ban
await member.send(`You have been temporarily banned from ARA for ${banLength}. Reason: ${reason}`
+ '\n\nhttps://vbcamp.org/ARA')
.catch(() => {});
// Ban the user
await member.ban({ reason });
} else if (!await userExists(userId)) {
await addEmptyUser(userId);
}
// Add ban to database
await addTempBan(userId, modId, time.fromNow, reason);
// Create scheduled task to unban
this.container.tasks.create('tempBan', {
userId: user.id,
guildId: guild.id,
}, time.offset);
info.message = `${user} has been temporarily banned for ${banLength}.`;
info.success = true;
// Log the ban
let logChannel = guild.channels.cache
.get(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
logChannel = await guild.channels
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
this.container.logger.error('Temp Ban Error: Could not fetch log channel');
info.message = `${user} has been temporarily banned for ${banLength}. `
+ 'This hasn\'t been logged in a text channel as log channel could not be found';
return info;
}
}
const log = new EmbedBuilder()
.setColor('#FF0000')
.setAuthor({ name: `Temp Banned ${user.tag}`, iconURL: `${user.avatarURL()}` })
.addFields(
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
{ name: 'Duration', value: banLength },
{ name: 'Reason', value: reason },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
await logChannel.send({ embeds: [log] });
return info;
}
}

View File

@ -28,7 +28,8 @@ import type {
} from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import IDs from '#utils/ids';
import { removeBan, checkActive, addBan } from '#utils/database/ban';
import { removeBan, checkBan, addBan } from '#utils/database/ban';
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser';
export class UnbanCommand extends Command {
@ -135,10 +136,17 @@ export class UnbanCommand extends Command {
let user = guild.client.users.cache.get(userId);
if (user === undefined) {
user = await guild.client.users.fetch(userId) as User;
user = await guild.client.users.fetch(userId);
if (user === undefined) {
info.message = 'Could not fetch the user!';
return info;
}
}
if (!await checkActive(userId)) {
let dbBan = await checkBan(userId);
const dbTempBan = await checkTempBan(userId);
if (!dbBan && !dbTempBan) {
let ban: GuildBan;
try {
ban = await guild.bans.fetch(userId);
@ -163,14 +171,19 @@ export class UnbanCommand extends Command {
// Add missing ban
await addBan(userId, modId, `(Mod who banned is not accurate) - ${reason}`);
dbBan = true;
}
// Unban the user
await guild.members.unban(user)
.catch(() => {});
// Add unban to database
await removeBan(user.id, mod.user.id);
if (dbBan) {
// Add unban to database
await removeBan(user.id, mod.user.id);
} else if (dbTempBan) {
await removeTempBan(user.id, mod.user.id);
}
info.message = `${user} has been unbanned.`;
info.success = true;

View File

@ -37,7 +37,6 @@ import type {
Snowflake,
} from 'discord.js';
import IDs from '#utils/ids';
import { randint } from '#utils/random';
import {
addEmptyUser,
updateUser,
@ -45,6 +44,7 @@ import {
fetchRoles,
} from '#utils/database/dbExistingUser';
import { restrict, checkActive } from '#utils/database/restriction';
import { randint } from '#utils/maths';
export async function restrictRun(
userId: Snowflake,
@ -58,6 +58,16 @@ export async function restrictRun(
success: false,
};
let user = guild.client.users.cache.get(userId);
if (user === undefined) {
user = await guild.client.users.fetch(userId);
if (user === undefined) {
info.message = 'Error fetching user';
return info;
}
}
// Gets mod's GuildMember
const mod = guild.members.cache.get(modId);
@ -79,7 +89,8 @@ export async function restrictRun(
let member = guild.members.cache.get(userId);
if (member === undefined) {
member = await guild.members.fetch(userId);
member = await guild.members.fetch(userId)
.catch(() => undefined);
}
const restrictRoles = IDs.roles.restrictions.restricted;
@ -97,9 +108,6 @@ export async function restrictRun(
await updateUser(member);
if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
// TODO remove this error before enabling vegan restricts
info.message = `${member} is vegan, can't restrict them yet 😭`;
return info;
section = 5;
}
@ -212,10 +220,8 @@ export async function restrictRun(
IDs.roles.nonvegan.vegCurious,
]);
} else if (!await userExists(userId)) {
// TODO remove this error before replacing other bot role replacement
info.message = `<@${userId}> is not on this server, can't restrict them yet! 😭`;
return info;
await addEmptyUser(userId);
} else {
const dbRoles = await fetchRoles(userId);
if (dbRoles.includes(IDs.roles.vegan.vegan)) {
section = 5;
@ -225,7 +231,7 @@ export async function restrictRun(
// Restrict the user on the database
await restrict(userId, modId, reason, section);
info.message = `Restricted ${member}`;
info.message = `Restricted ${user}`;
info.success = true;
// Log the ban
@ -237,21 +243,21 @@ export async function restrictRun(
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
container.logger.error('Restrict Error: Could not fetch log channel');
info.message = `Restricted ${member} but could not find the log channel. This has been logged to the database.`;
info.message = `Restricted ${user} but could not find the log channel. This has been logged to the database.`;
return info;
}
}
const message = new EmbedBuilder()
.setColor('#FF6700')
.setAuthor({ name: `Restricted ${member.user.tag}`, iconURL: `${member.user.avatarURL()}` })
.setAuthor({ name: `Restricted ${user.tag}`, iconURL: `${user.avatarURL()}` })
.addFields(
{ name: 'User', value: `${member}`, inline: true },
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
{ name: 'Reason', value: reason },
)
.setTimestamp()
.setFooter({ text: `ID: ${member.id}` });
.setFooter({ text: `ID: ${userId}` });
await logChannel.send({ embeds: [message] });
@ -263,7 +269,7 @@ export class RestrictCommand extends Command {
super(context, {
...options,
name: 'restrict',
aliases: ['r', 'rest', 'rr'], // TODO add 'rv' when enabling vegan restrictions
aliases: ['r', 'rest', 'rr', 'rv'],
description: 'Restricts a user',
preconditions: ['ModOnly'],
});

View File

@ -35,7 +35,7 @@ export class UnRestrictCommand extends Command {
super(context, {
...options,
name: 'unrestrict',
aliases: ['ur'], // TODO add urv for when restrict vegan will be implemented
aliases: ['ur', 'urv'],
description: 'Unrestricts a user',
preconditions: ['ModOnly'],
});
@ -121,6 +121,16 @@ export class UnRestrictCommand extends Command {
success: false,
};
let user = guild.client.users.cache.get(userId);
if (user === undefined) {
user = await guild.client.users.fetch(userId);
if (user === undefined) {
info.message = 'Error fetching user';
return info;
}
}
// Gets mod's GuildMember
const mod = guild.members.cache.get(modId);
@ -131,7 +141,7 @@ export class UnRestrictCommand extends Command {
}
// Check if mod is in database
if (!await userExists(mod.id)) {
if (!await userExists(modId)) {
await addExistingUser(mod);
}
@ -139,7 +149,8 @@ export class UnRestrictCommand extends Command {
let member = guild.members.cache.get(userId);
if (member === undefined) {
member = await guild.members.fetch(userId);
member = await guild.members.fetch(userId)
.catch(() => undefined);
}
if (member === undefined) {
@ -147,11 +158,16 @@ export class UnRestrictCommand extends Command {
return info;
}
// Check if mod is in database
if (!await userExists(userId)) {
await addExistingUser(member);
}
const restrictRoles = IDs.roles.restrictions.restricted;
// Checks if the user is not restricted
if (!member.roles.cache.hasAny(...restrictRoles)) {
info.message = `${member} is not restricted!`;
info.message = `${user} is not restricted!`;
return info;
}
@ -162,7 +178,7 @@ export class UnRestrictCommand extends Command {
await unRestrict(userId, modId);
} else {
let section = 1;
for (let i = 0; i < restrictRoles.length; i += 0) {
for (let i = 0; i < restrictRoles.length; i += 1) {
if (member.roles.cache.has(restrictRoles[i])) {
section = i + 1;
}
@ -213,24 +229,24 @@ export class UnRestrictCommand extends Command {
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
this.container.logger.error('Restrict Error: Could not fetch log channel');
info.message = `Unrestricted ${member} but could not find the log channel. This has been logged to the database.`;
info.message = `Unrestricted ${user} but could not find the log channel. This has been logged to the database.`;
return info;
}
}
const message = new EmbedBuilder()
.setColor('#28A745')
.setAuthor({ name: `Unrestricted ${member.user.tag}`, iconURL: `${member.user.avatarURL()}` })
.setAuthor({ name: `Unrestricted ${user.tag}`, iconURL: `${user.avatarURL()}` })
.addFields(
{ name: 'User', value: `${member}`, inline: true },
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
)
.setTimestamp()
.setFooter({ text: `ID: ${member.id}` });
.setFooter({ text: `ID: ${userId}` });
await logChannel.send({ embeds: [message] });
info.message = `Unrestricted ${member}`;
info.message = `Unrestricted ${user}`;
return info;
}
}

View File

@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2023 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Message, TextBasedChannel } from 'discord.js';
import { ChannelType } from 'discord.js';
import { Duration, DurationFormatter } from '@sapphire/time-utilities';
import { isNumber } from '#utils/maths';
export class SlowmodeCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: 'slowmode',
description: 'Sets slowmode for a channel',
preconditions: ['ModOnly'],
});
}
// Registers that this is a slash command
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand(
(builder) => builder
.setName(this.name)
.setDescription(this.description)
.addStringOption((option) => option.setName('duration')
.setDescription('Set the slowmode time')
.setRequired(true)),
{
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
},
);
}
// Command run
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
// Get the arguments
const duration = interaction.options.getString('duration', true);
const { channel } = interaction;
if (channel === null) {
await interaction.reply({
content: 'Could not fetch channel!',
ephemeral: true,
fetchReply: true,
});
return;
}
const slowmode = await this.slowmode(duration, channel);
await interaction.reply({ content: slowmode.message });
}
public async messageRun(message: Message, args: Args) {
// Get arguments
const duration = args.finished ? null : await args.rest('string');
const { channel } = message;
if (duration === null) {
await message.react('❌');
await message.reply('Slowmode length was not provided!');
return;
}
const slowmode = await this.slowmode(duration, channel);
await message.reply(slowmode.message);
await message.react(slowmode.success ? '✅' : '❌');
}
private async slowmode(duration: string, channel: TextBasedChannel) {
const info = {
message: '',
success: false,
};
if (channel.type !== ChannelType.GuildText) {
info.message = 'Channel is not a text channel!';
return info;
}
let durationCheck = duration;
if (isNumber(durationCheck)) {
durationCheck += 's';
}
this.container.logger.debug(durationCheck);
const durationParsed = new Duration(durationCheck);
let time = 0;
if (Number.isNaN(durationParsed.offset)) {
if (duration !== 'off') {
info.message = 'Invalid time format!';
return info;
}
time = 0;
} else {
time = durationParsed.offset;
}
await channel.setRateLimitPerUser(time / 1000);
info.success = true;
if (time === 0) {
info.message = `${channel} is no longer in slowmode.`;
return info;
}
info.message = `${channel} has now been set to a post every ${new DurationFormatter().format(time)}.`;
return info;
}
}

142
src/listeners/ban/ban.ts Normal file
View File

@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2022 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Listener } from '@sapphire/framework';
import type { GuildBan } from 'discord.js';
import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js';
import { addBan, checkBan } from '#utils/database/ban';
import IDs from '#utils/ids';
import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser';
export class BanListener extends Listener {
public constructor(context: Listener.Context, options: Listener.Options) {
super(context, {
...options,
event: 'guildBanAdd',
});
}
public async run(ban: GuildBan) {
if (await checkBan(ban.user.id)) {
return;
}
// Get the audit logs for the ban
const logs = await ban.guild.fetchAuditLogs({
limit: 1,
type: AuditLogEvent.MemberBanAdd,
});
const banLog = logs.entries.first();
if (banLog === undefined) {
this.container.logger.error('BanListener: banLog is undefined.');
return;
}
const { executor, target } = banLog;
if (ban.user !== target) {
this.container.logger.error('BanListener: ban.user !== target.');
return;
}
if (executor === null) {
this.container.logger.error('BanListener: mod not found.');
return;
}
if (this.container.client.user === null) {
this.container.logger.error('BanListener: client.user is null.');
return;
}
// Check if the bot banned the user
if (executor.id === this.container.client.user.id) {
this.container.logger.error('BanListener: got past the checkActive and bot banned this user.');
return;
}
const { user } = ban;
const { guild } = ban;
// Gets mod's GuildMember
let mod = guild.members.cache.get(executor.id);
// Checks if GuildMember is null
if (mod === undefined) {
mod = await guild.members.fetch(executor.id)
.catch(() => undefined);
if (mod === undefined) {
this.container.logger.error('UnbanListener: Could not fetch moderator.');
return;
}
}
// Check if mod is in database
if (!await userExists(mod.id)) {
await addExistingUser(mod);
}
if (await checkBan(user.id)) {
this.container.logger.error('BanListener: got past the checkActive at the start.');
return;
}
// Check if user and mod are on the database
if (!await userExists(user.id)) {
await addEmptyUser(user.id);
}
let { reason } = banLog;
if (reason === null) {
reason = 'Was banned without using the bot, reason was not given';
}
// Add missing ban
await addBan(user.id, mod.id, `${reason}`);
// Log the ban
let logChannel = guild.channels.cache
.get(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
logChannel = await guild.channels
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
this.container.logger.error('BanListener: Could not fetch log channel');
return;
}
}
const log = new EmbedBuilder()
.setColor('#FF0000')
.setAuthor({ name: `Banned ${user.tag} (not done via bot)`, iconURL: `${user.avatarURL()}` })
.addFields(
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
{ name: 'Reason', value: reason },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
await logChannel.send({ embeds: [log] });
}
}

View File

@ -19,9 +19,10 @@
import { Listener } from '@sapphire/framework';
import type { GuildMember } from 'discord.js';
import { checkActive, getReason } from '#utils/database/ban';
import { checkBan, getBanReason } from '#utils/database/ban';
import { checkTempBan } from '#utils/database/tempBan';
export class BanJoin extends Listener {
export class BanJoinListener extends Listener {
public constructor(context: Listener.Context, options: Listener.Options) {
super(context, {
...options,
@ -31,12 +32,13 @@ export class BanJoin extends Listener {
public async run(user: GuildMember) {
// Check if the user is banned
if (!await checkActive(user.id)) {
if (!await checkBan(user.id)
&& !await checkTempBan(user.id)) {
return;
}
// Get reason from database
const reason = await getReason(user.id);
const reason = await getBanReason(user.id);
// Send DM for ban reason
await user.send(`You have been banned from ARA for: ${reason}`

126
src/listeners/ban/unban.ts Normal file
View File

@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2022 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Listener } from '@sapphire/framework';
import type { GuildBan } from 'discord.js';
import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js';
import { addBan, checkBan, removeBan } from '#utils/database/ban';
import IDs from '#utils/ids';
import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser';
export class UnbanListener extends Listener {
public constructor(context: Listener.Context, options: Listener.Options) {
super(context, {
...options,
event: 'guildBanRemove',
});
}
public async run(ban: GuildBan) {
// Check if the bot unbanned the user
const logs = await ban.guild.fetchAuditLogs({
limit: 1,
type: AuditLogEvent.MemberBanRemove,
});
const banLog = logs.entries.first();
if (banLog === undefined) {
return;
}
const { executor, target } = banLog;
if (ban.user !== target) {
return;
}
if (executor === null) {
return;
}
if (this.container.client.user === null) {
this.container.logger.error('UnbanListener: client.user is null.');
return;
}
if (executor.id === this.container.client.user.id) {
return;
}
const { user } = ban;
const { guild } = ban;
// Gets mod's GuildMember
let mod = guild.members.cache.get(executor.id);
// Checks if GuildMember is null
if (mod === undefined) {
mod = await guild.members.fetch(executor.id)
.catch(() => undefined);
if (mod === undefined) {
this.container.logger.error('UnbanListener: Could not fetch moderator.');
return;
}
}
// Check if mod is in database
if (!await userExists(mod.id)) {
await addExistingUser(mod);
}
// Check for missing ban on database
if (!await checkBan(user.id)) {
// Check if user and mod are on the database
if (!await userExists(user.id)) {
await addEmptyUser(user.id);
}
// Add missing ban
await addBan(user.id, mod.id, '(Mod who banned is not accurate) - ');
}
// Add unban to database
await removeBan(user.id, mod.id);
// Log the ban
let logChannel = guild.channels.cache
.get(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
logChannel = await guild.channels
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
this.container.logger.error('UnbanListener: Could not fetch log channel');
return;
}
}
const log = new EmbedBuilder()
.setColor('#28A745')
.setAuthor({ name: `Unbanned ${user.tag} (not done via bot)`, iconURL: `${user.avatarURL()}` })
.addFields(
{ name: 'User', value: `${user}`, inline: true },
{ name: 'Moderator', value: `${mod}`, inline: true },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
await logChannel.send({ embeds: [log] });
}
}

View File

@ -128,7 +128,6 @@ export class VerificationJoinVCListener extends Listener {
]);
// Start 15-minute timer if verifier does not join
// @ts-ignore
this.container.tasks.create('verifyTimeout', {
channelId: channel.id,
userId: member.id,

View File

@ -25,7 +25,7 @@ import { time, ChannelType, PermissionsBitField } from 'discord.js';
import { maxVCs, leaveBan } from '#utils/verificationConfig';
import { getUser, checkFinish, countIncomplete } from '#utils/database/verification';
import { fetchRoles } from '#utils/database/dbExistingUser';
import { fibonacci } from '#utils/mathsSeries';
import { fibonacci } from '#utils/maths';
import IDs from '#utils/ids';
export class VerificationLeaveVCListener extends Listener {
@ -88,7 +88,6 @@ export class VerificationLeaveVCListener extends Listener {
// Creates the length of the time for the ban
const banLength = fibonacci(incompleteCount) * 3600_000;
// @ts-ignore
this.container.tasks.create('verifyUnblock', {
userId: user.id,
guildId: guild.id,

View File

@ -26,7 +26,7 @@ export class DiversityMonMessageTask extends ScheduledTask {
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
super(context, {
...options,
cron: '0 15 * * 1',
pattern: '0 15 * * 1',
});
}
@ -53,6 +53,6 @@ export class DiversityMonMessageTask extends ScheduledTask {
declare module '@sapphire/plugin-scheduled-tasks' {
interface ScheduledTasks {
cron: never;
pattern: never;
}
}

View File

@ -26,7 +26,7 @@ export class DiversityWedMessageTask extends ScheduledTask {
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
super(context, {
...options,
cron: '0 15 * * 3',
pattern: '0 15 * * 3',
});
}
@ -54,6 +54,6 @@ export class DiversityWedMessageTask extends ScheduledTask {
declare module '@sapphire/plugin-scheduled-tasks' {
interface ScheduledTasks {
cron: never;
pattern: never;
}
}

View File

@ -26,7 +26,7 @@ export class RestrictedMessageTask extends ScheduledTask {
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
super(context, {
...options,
cron: '0 17 * * *',
pattern: '0 17 * * *',
});
}
@ -49,6 +49,6 @@ export class RestrictedMessageTask extends ScheduledTask {
declare module '@sapphire/plugin-scheduled-tasks' {
interface ScheduledTasks {
cron: never;
pattern: never;
}
}

View File

@ -26,7 +26,7 @@ export class StandupTask extends ScheduledTask {
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
super(context, {
...options,
cron: '0 12 * * 1',
pattern: '0 12 * * 1',
});
}
@ -42,6 +42,6 @@ export class StandupTask extends ScheduledTask {
declare module '@sapphire/plugin-scheduled-tasks' {
interface ScheduledTasks {
cron: never;
pattern: never;
}
}

View File

@ -26,7 +26,7 @@ export class VerifyReminder extends ScheduledTask {
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
super(context, {
...options,
cron: '0 */1 * * *',
pattern: '0 */1 * * *',
});
}
@ -43,6 +43,6 @@ export class VerifyReminder extends ScheduledTask {
declare module '@sapphire/plugin-scheduled-tasks' {
interface VerifyReminder {
cron: never;
pattern: never;
}
}

View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2023 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks';
import IDs from '#utils/ids';
import {
TextChannel,
EmbedBuilder,
} from 'discord.js';
import { checkBan } from '#utils/database/ban';
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
export class TempBan extends ScheduledTask {
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
super(context, options);
}
public async run(payload: { userId: string, guildId: string }) {
this.container.logger.debug('Temp Unban Task: Currently running unban');
// Get the guild where the user is in
let guild = this.container.client.guilds.cache.get(payload.guildId);
if (guild === undefined) {
guild = await this.container.client.guilds.fetch(payload.guildId);
if (guild === undefined) {
this.container.logger.error('Temp Unban Task: Guild not found!');
return;
}
}
const { userId } = payload;
let user = guild.client.users.cache.get(userId);
if (user === undefined) {
user = await guild.client.users.fetch(userId);
if (user === undefined) {
this.container.logger.error('Temp Unban Task: Could not fetch banned user!');
return;
}
}
if (await checkBan(userId)
|| !await checkTempBan(userId)) {
this.container.logger.debug('Temp Unban Task: User is either permanently banned or no longer temporarily banned.');
return;
}
// Unban the user
await guild.members.unban(user)
.catch(() => {});
await removeTempBan(userId);
// Log unban
let logChannel = guild.channels.cache
.get(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
logChannel = await guild.channels
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
if (logChannel === undefined) {
this.container.logger.error(`Temp Ban Listener: Could not fetch log channel. User Snowflake: ${userId}`);
return;
}
}
const log = new EmbedBuilder()
.setColor('#28A745')
.setAuthor({ name: `Unbanned ${user.tag} (tempban)`, iconURL: `${user.avatarURL()}` })
.addFields(
{ name: 'User', value: `${user}`, inline: true },
)
.setTimestamp()
.setFooter({ text: `ID: ${user.id}` });
await logChannel.send({ embeds: [log] });
}
}
declare module '@sapphire/plugin-scheduled-tasks' {
interface ScheduledTasks {
tempBan: never;
}
}

View File

@ -51,6 +51,6 @@ export class VerifyTimeout extends ScheduledTask {
declare module '@sapphire/plugin-scheduled-tasks' {
interface ScheduledTasks {
verifyUnblock: never;
verifyTimeout: never;
}
}

View File

@ -46,7 +46,7 @@ export async function removeBan(userId: string, modId: string) {
});
}
export async function checkActive(userId: string) {
export async function checkBan(userId: string) {
const ban = await container.database.ban.findFirst({
where: {
userId,
@ -63,7 +63,7 @@ export async function checkActive(userId: string) {
return ban.active;
}
export async function getReason(userId: string) {
export async function getBanReason(userId: string) {
const ban = await container.database.ban.findFirst({
where: {
userId,

View File

@ -0,0 +1,98 @@
import { container } from '@sapphire/framework';
import type { Snowflake } from 'discord.js';
export async function addTempBan(
userId: Snowflake,
modId: Snowflake,
endTime: Date,
reason: string,
) {
// Add the user to the database
await container.database.tempBan.create({
data: {
user: {
connect: {
id: userId,
},
},
mod: {
connect: {
id: modId,
},
},
endTime,
reason,
},
});
}
export async function removeTempBan(userId: Snowflake, modId?: Snowflake) {
const ban = await container.database.tempBan.findFirst({
where: {
userId,
},
orderBy: {
id: 'desc',
},
});
if (ban === null) {
return;
}
if (modId !== undefined) {
await container.database.tempBan.update({
where: {
id: ban.id,
},
data: {
endModId: modId,
active: false,
},
});
return;
}
await container.database.tempBan.update({
where: {
id: ban.id,
},
data: {
active: false,
},
});
}
export async function checkTempBan(userId: Snowflake) {
const ban = await container.database.tempBan.findFirst({
where: {
userId,
},
orderBy: {
id: 'desc',
},
});
if (ban === null) {
return false;
}
return ban.active;
}
export async function getTempBanReason(userId: Snowflake) {
const ban = await container.database.tempBan.findFirst({
where: {
userId,
},
orderBy: {
id: 'desc',
},
});
if (ban === null) {
return '';
}
return ban.reason;
}

View File

@ -21,7 +21,7 @@ import type { GuildMember } from 'discord.js';
import { container } from '@sapphire/framework';
import { updateUser } from '#utils/database/dbExistingUser';
import { leaveBan } from '#utils/verificationConfig';
import { fibonacci } from '#utils/mathsSeries';
import { fibonacci } from '#utils/maths';
export async function joinVerification(channelId: string, user: GuildMember) {
// Update the user on the database with the current roles they have

View File

@ -43,7 +43,7 @@ const devIDs = {
'999431674997788676', // Restricted 2
'999431674997788675', // Restricted 3
'999431674997788674', // Restricted 4
// '999431674997788677', // Restricted Vegan
'1075952207091994726', // Restricted Vegan
],
},
staff: {

View File

@ -46,7 +46,7 @@ let IDs = {
'872482843304001566', // Restricted 2
'856582673258774538', // Restricted 3
'872472182888992858', // Restricted 4
// '809769217477050369', // Restricted Vegan
'1075951477379567646', // Restricted Vegan
],
},
staff: {

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2022 Anthony Berg
Copyright (C) 2023 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,6 +17,25 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Checks if any parsed value is a number.
* @param number check if variable is a number
* @returns {boolean} true if it is a number
*/
export function isNumber(number: any) {
return !Number.isNaN(+number);
}
/**
* Creates a (PRNG) random integer between minimum and maximum both inclusive
* @param min minimum integer
* @param max maximum integer
* @returns number a random integer between min and max
*/
export function randint(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Created because Stove loves Fibonacci sequences
// A fibonacci sequence where n = 0 => 1
export function fibonacci(position: number) {

View File

@ -1,23 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Animal Rights Advocates Discord Bot
Copyright (C) 2023 Anthony Berg
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Random integer between min and max
export function randint(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}