mirror of
https://github.com/veganhacktivists/arabot.git
synced 2025-05-19 12:54:17 +02:00
commit
a38677c4e3
@ -22,6 +22,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
container_name: arabot
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
465
package-lock.json
generated
465
package-lock.json
generated
@ -9,16 +9,17 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^4.0.0",
|
"@prisma/client": "^4.10.1",
|
||||||
"@sapphire/discord.js-utilities": "^6.0.0",
|
"@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-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/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",
|
"@sapphire/utilities": "^3.9.2",
|
||||||
"@types/node": "^18.0.3",
|
"@types/node": "^18.0.3",
|
||||||
"bullmq": "^1.89.1",
|
"bullmq": "^3.6.6",
|
||||||
"discord.js": "^14.7.1",
|
"discord.js": "^14.7.1",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"ts-node": "^10.8.2",
|
"ts-node": "^10.8.2",
|
||||||
@ -34,7 +35,7 @@
|
|||||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.5.3",
|
"eslint-import-resolver-typescript": "^3.5.3",
|
||||||
"eslint-plugin-import": "^2.27.5",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"prisma": "^4.4.0"
|
"prisma": "^4.10.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cspotcode/source-map-support": {
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
@ -306,12 +307,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/client": {
|
"node_modules/@prisma/client": {
|
||||||
"version": "4.9.0",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.10.1.tgz",
|
||||||
"integrity": "sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==",
|
"integrity": "sha512-VonXLJZybdt8e5XZH5vnIGCRNnIh6OMX1FS3H/yzMGLT3STj5TJ/OkMcednrvELgk8PK89Vo3aSh51MWNO0axA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/engines-version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5"
|
"@prisma/engines-version": "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
@ -326,16 +327,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/engines": {
|
"node_modules/@prisma/engines": {
|
||||||
"version": "4.9.0",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.10.1.tgz",
|
||||||
"integrity": "sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==",
|
"integrity": "sha512-B3tcTxjx196nuAu1GOTKO9cGPUgTFHYRdkPkTS4m5ptb2cejyBlH9X7GOfSt3xlI7p4zAJDshJP4JJivCg9ouA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true
|
"hasInstallScript": true
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/engines-version": {
|
"node_modules/@prisma/engines-version": {
|
||||||
"version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5",
|
"version": "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19.tgz",
|
||||||
"integrity": "sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA=="
|
"integrity": "sha512-tsjTho7laDhf9EJ9EnDxAPEf7yrigSMDhniXeU4YoWc7azHAs4GPxRi2P9LTFonmHkJLMOLjR77J1oIP8Ife1w=="
|
||||||
},
|
},
|
||||||
"node_modules/@sapphire/async-queue": {
|
"node_modules/@sapphire/async-queue": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
@ -346,6 +347,18 @@
|
|||||||
"npm": ">=7.0.0"
|
"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": {
|
"node_modules/@sapphire/discord-utilities": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/discord-utilities/-/discord-utilities-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/discord-utilities/-/discord-utilities-3.0.0.tgz",
|
||||||
@ -383,9 +396,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sapphire/framework": {
|
"node_modules/@sapphire/framework": {
|
||||||
"version": "4.0.2",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.1.0.tgz",
|
||||||
"integrity": "sha512-IoSZGBPJjiINJKJKaBfnpEB1IxPv7yitunnvJ6V5XcTdxP51I/KsVJX2ELxiH7sslg8ZrQQMRIcluGLbVwv4KA==",
|
"integrity": "sha512-jtwZPysF13Sn8h2p8nkIPETveGAxRmYmiqxYkd3VXV8VPWwKBG8IriuI4oExpSnuCqxIs5HRpo3M+Gl+f/mdCg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/builders": "^1.4.0",
|
"@discordjs/builders": "^1.4.0",
|
||||||
"@sapphire/discord-utilities": "^3.0.0",
|
"@sapphire/discord-utilities": "^3.0.0",
|
||||||
@ -443,12 +456,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sapphire/plugin-scheduled-tasks": {
|
"node_modules/@sapphire/plugin-scheduled-tasks": {
|
||||||
"version": "4.0.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-6.0.0.tgz",
|
||||||
"integrity": "sha512-vLxfHBu2vKaJZ9v2f4z+VDZaPeDqS8bm+Sc2minRwJPw1hWAHiPqmxCBPIONY7eOQ9qKayvhKYTIwwruxgO/Mg==",
|
"integrity": "sha512-R9rga1aZk3GSXkmGfBMQR8Ng4ou36l5WGWoDvPaq1xNb56wZJ2zPZPsQHV6lxNoyOT++M8GFhVhh9OUB+xwEXg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sapphire/stopwatch": "^1.4.1",
|
"@sapphire/stopwatch": "^1.5.0",
|
||||||
"@sapphire/utilities": "^3.9.3",
|
"@sapphire/utilities": "^3.11.0",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -524,6 +537,21 @@
|
|||||||
"npm": ">=7.0.0"
|
"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": {
|
"node_modules/@sapphire/timer-manager": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/timer-manager/-/timer-manager-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/timer-manager/-/timer-manager-1.0.0.tgz",
|
||||||
@ -612,9 +640,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.11.19",
|
"version": "18.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.19.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
|
||||||
"integrity": "sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw=="
|
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.3.13",
|
"version": "7.3.13",
|
||||||
@ -631,14 +659,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz",
|
||||||
"integrity": "sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==",
|
"integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "5.51.0",
|
"@typescript-eslint/scope-manager": "5.52.0",
|
||||||
"@typescript-eslint/type-utils": "5.51.0",
|
"@typescript-eslint/type-utils": "5.52.0",
|
||||||
"@typescript-eslint/utils": "5.51.0",
|
"@typescript-eslint/utils": "5.52.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"grapheme-splitter": "^1.0.4",
|
"grapheme-splitter": "^1.0.4",
|
||||||
"ignore": "^5.2.0",
|
"ignore": "^5.2.0",
|
||||||
@ -665,14 +693,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz",
|
||||||
"integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==",
|
"integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "5.51.0",
|
"@typescript-eslint/scope-manager": "5.52.0",
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.51.0",
|
"@typescript-eslint/typescript-estree": "5.52.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -692,13 +720,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz",
|
||||||
"integrity": "sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==",
|
"integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.51.0"
|
"@typescript-eslint/visitor-keys": "5.52.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
@ -709,13 +737,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz",
|
||||||
"integrity": "sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==",
|
"integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "5.51.0",
|
"@typescript-eslint/typescript-estree": "5.52.0",
|
||||||
"@typescript-eslint/utils": "5.51.0",
|
"@typescript-eslint/utils": "5.52.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"tsutils": "^3.21.0"
|
"tsutils": "^3.21.0"
|
||||||
},
|
},
|
||||||
@ -736,9 +764,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz",
|
||||||
"integrity": "sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==",
|
"integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
@ -749,13 +777,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz",
|
||||||
"integrity": "sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==",
|
"integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.51.0",
|
"@typescript-eslint/visitor-keys": "5.52.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -776,16 +804,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz",
|
||||||
"integrity": "sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==",
|
"integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
"@types/semver": "^7.3.12",
|
"@types/semver": "^7.3.12",
|
||||||
"@typescript-eslint/scope-manager": "5.51.0",
|
"@typescript-eslint/scope-manager": "5.52.0",
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.51.0",
|
"@typescript-eslint/typescript-estree": "5.52.0",
|
||||||
"eslint-scope": "^5.1.1",
|
"eslint-scope": "^5.1.1",
|
||||||
"eslint-utils": "^3.0.0",
|
"eslint-utils": "^3.0.0",
|
||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
@ -802,12 +830,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz",
|
||||||
"integrity": "sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==",
|
"integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1001,14 +1029,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bullmq": {
|
"node_modules/bullmq": {
|
||||||
"version": "1.91.1",
|
"version": "3.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.91.1.tgz",
|
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-3.6.6.tgz",
|
||||||
"integrity": "sha512-u7dat9I8ZwouZ651AMZkBSvB6NVUPpnAjd4iokd9DM41whqIBnDjuL11h7+kEjcpiDKj6E+wxZiER00FqirZQg==",
|
"integrity": "sha512-W71jXrcTdcT3Y5tzMyTx22Cd8O3dTML7vl6KG3YdGVGrO3+UmKRLYfGLn1QwIhIoTQJVvIrSB4qfGs1hgqYRVw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cron-parser": "^4.6.0",
|
"cron-parser": "^4.6.0",
|
||||||
"get-port": "6.1.2",
|
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
"ioredis": "^5.2.2",
|
"ioredis": "^5.3.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"msgpackr": "^1.6.2",
|
"msgpackr": "^1.6.2",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.3.7",
|
||||||
@ -1170,9 +1197,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/define-properties": {
|
"node_modules/define-properties": {
|
||||||
"version": "1.1.4",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||||
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-property-descriptors": "^1.0.0",
|
"has-property-descriptors": "^1.0.0",
|
||||||
@ -1214,9 +1241,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/discord-api-types": {
|
"node_modules/discord-api-types": {
|
||||||
"version": "0.37.31",
|
"version": "0.37.33",
|
||||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.31.tgz",
|
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.33.tgz",
|
||||||
"integrity": "sha512-k9DQQ7Wv+ehiF7901qk/FnP47k6O2MHm3meQFee4gUzi5dfGAVLf7SfLNtb4w7G2dmukJyWQtVJEDF9oMb9yuQ=="
|
"integrity": "sha512-ZMH5RU3q1pvYS+2wGUJ5Zvy8jMGTQ4wCpbDlIQDkbIL/k6kJwBPsXnCg81g2GywlOuf0f8ezakxVSe+sZuY6ig=="
|
||||||
},
|
},
|
||||||
"node_modules/discord.js": {
|
"node_modules/discord.js": {
|
||||||
"version": "14.7.1",
|
"version": "14.7.1",
|
||||||
@ -1976,17 +2003,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/get-symbol-description": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
|
"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=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
"node_modules/internal-slot": {
|
"node_modules/internal-slot": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
|
||||||
"integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
|
"integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.1.3",
|
"get-intrinsic": "^1.2.0",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.4"
|
||||||
},
|
},
|
||||||
@ -2310,9 +2326,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ioredis": {
|
"node_modules/ioredis": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz",
|
||||||
"integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==",
|
"integrity": "sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ioredis/commands": "^1.1.1",
|
"@ioredis/commands": "^1.1.1",
|
||||||
"cluster-key-slot": "^1.1.0",
|
"cluster-key-slot": "^1.1.0",
|
||||||
@ -2741,9 +2757,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimist": {
|
"node_modules/minimist": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@ -2882,9 +2898,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/open": {
|
"node_modules/open": {
|
||||||
"version": "8.4.0",
|
"version": "8.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz",
|
||||||
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
|
"integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-lazy-prop": "^2.0.0",
|
"define-lazy-prop": "^2.0.0",
|
||||||
@ -3039,13 +3055,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prisma": {
|
"node_modules/prisma": {
|
||||||
"version": "4.9.0",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.10.1.tgz",
|
||||||
"integrity": "sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==",
|
"integrity": "sha512-0jDxgg+DruB1kHVNlcspXQB9au62IFfVg9drkhzXudszHNUAQn0lVuu+T8np0uC2z1nKD5S3qPeCyR8u5YFLnA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/engines": "4.9.0"
|
"@prisma/engines": "4.10.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"prisma": "build/index.js",
|
"prisma": "build/index.js",
|
||||||
@ -3537,9 +3553,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-mixer": {
|
"node_modules/ts-mixer": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
|
||||||
"integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A=="
|
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ts-node": {
|
"node_modules/ts-node": {
|
||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
@ -3687,9 +3703,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
"node_modules/undici": {
|
||||||
"version": "5.18.0",
|
"version": "5.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz",
|
||||||
"integrity": "sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA==",
|
"integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"busboy": "^1.6.0"
|
"busboy": "^1.6.0"
|
||||||
},
|
},
|
||||||
@ -3796,9 +3812,9 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||||
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
@ -4029,29 +4045,37 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@prisma/client": {
|
"@prisma/client": {
|
||||||
"version": "4.9.0",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.10.1.tgz",
|
||||||
"integrity": "sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==",
|
"integrity": "sha512-VonXLJZybdt8e5XZH5vnIGCRNnIh6OMX1FS3H/yzMGLT3STj5TJ/OkMcednrvELgk8PK89Vo3aSh51MWNO0axA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@prisma/engines-version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5"
|
"@prisma/engines-version": "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@prisma/engines": {
|
"@prisma/engines": {
|
||||||
"version": "4.9.0",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.10.1.tgz",
|
||||||
"integrity": "sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==",
|
"integrity": "sha512-B3tcTxjx196nuAu1GOTKO9cGPUgTFHYRdkPkTS4m5ptb2cejyBlH9X7GOfSt3xlI7p4zAJDshJP4JJivCg9ouA==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"@prisma/engines-version": {
|
"@prisma/engines-version": {
|
||||||
"version": "4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5",
|
"version": "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19.tgz",
|
||||||
"integrity": "sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA=="
|
"integrity": "sha512-tsjTho7laDhf9EJ9EnDxAPEf7yrigSMDhniXeU4YoWc7azHAs4GPxRi2P9LTFonmHkJLMOLjR77J1oIP8Ife1w=="
|
||||||
},
|
},
|
||||||
"@sapphire/async-queue": {
|
"@sapphire/async-queue": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
|
||||||
"integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA=="
|
"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": {
|
"@sapphire/discord-utilities": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/discord-utilities/-/discord-utilities-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/discord-utilities/-/discord-utilities-3.0.0.tgz",
|
||||||
@ -4077,9 +4101,9 @@
|
|||||||
"integrity": "sha512-B+6nKYnBmIlqqbamcR4iBvbQHz6/Kq2JUVM0rA3lQ+aYUYDdcA1Spt66CKtPWwdTYEtSv0VY6Jv27WCtFNYTUg=="
|
"integrity": "sha512-B+6nKYnBmIlqqbamcR4iBvbQHz6/Kq2JUVM0rA3lQ+aYUYDdcA1Spt66CKtPWwdTYEtSv0VY6Jv27WCtFNYTUg=="
|
||||||
},
|
},
|
||||||
"@sapphire/framework": {
|
"@sapphire/framework": {
|
||||||
"version": "4.0.2",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/framework/-/framework-4.1.0.tgz",
|
||||||
"integrity": "sha512-IoSZGBPJjiINJKJKaBfnpEB1IxPv7yitunnvJ6V5XcTdxP51I/KsVJX2ELxiH7sslg8ZrQQMRIcluGLbVwv4KA==",
|
"integrity": "sha512-jtwZPysF13Sn8h2p8nkIPETveGAxRmYmiqxYkd3VXV8VPWwKBG8IriuI4oExpSnuCqxIs5HRpo3M+Gl+f/mdCg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@discordjs/builders": "^1.4.0",
|
"@discordjs/builders": "^1.4.0",
|
||||||
"@sapphire/discord-utilities": "^3.0.0",
|
"@sapphire/discord-utilities": "^3.0.0",
|
||||||
@ -4121,12 +4145,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sapphire/plugin-scheduled-tasks": {
|
"@sapphire/plugin-scheduled-tasks": {
|
||||||
"version": "4.0.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/plugin-scheduled-tasks/-/plugin-scheduled-tasks-6.0.0.tgz",
|
||||||
"integrity": "sha512-vLxfHBu2vKaJZ9v2f4z+VDZaPeDqS8bm+Sc2minRwJPw1hWAHiPqmxCBPIONY7eOQ9qKayvhKYTIwwruxgO/Mg==",
|
"integrity": "sha512-R9rga1aZk3GSXkmGfBMQR8Ng4ou36l5WGWoDvPaq1xNb56wZJ2zPZPsQHV6lxNoyOT++M8GFhVhh9OUB+xwEXg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sapphire/stopwatch": "^1.4.1",
|
"@sapphire/stopwatch": "^1.5.0",
|
||||||
"@sapphire/utilities": "^3.9.3",
|
"@sapphire/utilities": "^3.11.0",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4174,6 +4198,17 @@
|
|||||||
"tslib": "^2.4.0"
|
"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": {
|
"@sapphire/timer-manager": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/timer-manager/-/timer-manager-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sapphire/timer-manager/-/timer-manager-1.0.0.tgz",
|
||||||
@ -4246,9 +4281,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "18.11.19",
|
"version": "18.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.19.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
|
||||||
"integrity": "sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw=="
|
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg=="
|
||||||
},
|
},
|
||||||
"@types/semver": {
|
"@types/semver": {
|
||||||
"version": "7.3.13",
|
"version": "7.3.13",
|
||||||
@ -4265,14 +4300,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz",
|
||||||
"integrity": "sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==",
|
"integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "5.51.0",
|
"@typescript-eslint/scope-manager": "5.52.0",
|
||||||
"@typescript-eslint/type-utils": "5.51.0",
|
"@typescript-eslint/type-utils": "5.52.0",
|
||||||
"@typescript-eslint/utils": "5.51.0",
|
"@typescript-eslint/utils": "5.52.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"grapheme-splitter": "^1.0.4",
|
"grapheme-splitter": "^1.0.4",
|
||||||
"ignore": "^5.2.0",
|
"ignore": "^5.2.0",
|
||||||
@ -4283,53 +4318,53 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/parser": {
|
"@typescript-eslint/parser": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz",
|
||||||
"integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==",
|
"integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "5.51.0",
|
"@typescript-eslint/scope-manager": "5.52.0",
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.51.0",
|
"@typescript-eslint/typescript-estree": "5.52.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/scope-manager": {
|
"@typescript-eslint/scope-manager": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz",
|
||||||
"integrity": "sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==",
|
"integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.51.0"
|
"@typescript-eslint/visitor-keys": "5.52.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/type-utils": {
|
"@typescript-eslint/type-utils": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz",
|
||||||
"integrity": "sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==",
|
"integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/typescript-estree": "5.51.0",
|
"@typescript-eslint/typescript-estree": "5.52.0",
|
||||||
"@typescript-eslint/utils": "5.51.0",
|
"@typescript-eslint/utils": "5.52.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"tsutils": "^3.21.0"
|
"tsutils": "^3.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/types": {
|
"@typescript-eslint/types": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz",
|
||||||
"integrity": "sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==",
|
"integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/typescript-estree": {
|
"@typescript-eslint/typescript-estree": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz",
|
||||||
"integrity": "sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==",
|
"integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.51.0",
|
"@typescript-eslint/visitor-keys": "5.52.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -4338,28 +4373,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/utils": {
|
"@typescript-eslint/utils": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz",
|
||||||
"integrity": "sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==",
|
"integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
"@types/semver": "^7.3.12",
|
"@types/semver": "^7.3.12",
|
||||||
"@typescript-eslint/scope-manager": "5.51.0",
|
"@typescript-eslint/scope-manager": "5.52.0",
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.51.0",
|
"@typescript-eslint/typescript-estree": "5.52.0",
|
||||||
"eslint-scope": "^5.1.1",
|
"eslint-scope": "^5.1.1",
|
||||||
"eslint-utils": "^3.0.0",
|
"eslint-utils": "^3.0.0",
|
||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/visitor-keys": {
|
"@typescript-eslint/visitor-keys": {
|
||||||
"version": "5.51.0",
|
"version": "5.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz",
|
||||||
"integrity": "sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==",
|
"integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "5.51.0",
|
"@typescript-eslint/types": "5.52.0",
|
||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4492,14 +4527,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bullmq": {
|
"bullmq": {
|
||||||
"version": "1.91.1",
|
"version": "3.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.91.1.tgz",
|
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-3.6.6.tgz",
|
||||||
"integrity": "sha512-u7dat9I8ZwouZ651AMZkBSvB6NVUPpnAjd4iokd9DM41whqIBnDjuL11h7+kEjcpiDKj6E+wxZiER00FqirZQg==",
|
"integrity": "sha512-W71jXrcTdcT3Y5tzMyTx22Cd8O3dTML7vl6KG3YdGVGrO3+UmKRLYfGLn1QwIhIoTQJVvIrSB4qfGs1hgqYRVw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"cron-parser": "^4.6.0",
|
"cron-parser": "^4.6.0",
|
||||||
"get-port": "6.1.2",
|
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
"ioredis": "^5.2.2",
|
"ioredis": "^5.3.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"msgpackr": "^1.6.2",
|
"msgpackr": "^1.6.2",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.3.7",
|
||||||
@ -4623,9 +4657,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"define-properties": {
|
"define-properties": {
|
||||||
"version": "1.1.4",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||||
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"has-property-descriptors": "^1.0.0",
|
"has-property-descriptors": "^1.0.0",
|
||||||
@ -4652,9 +4686,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"discord-api-types": {
|
"discord-api-types": {
|
||||||
"version": "0.37.31",
|
"version": "0.37.33",
|
||||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.31.tgz",
|
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.33.tgz",
|
||||||
"integrity": "sha512-k9DQQ7Wv+ehiF7901qk/FnP47k6O2MHm3meQFee4gUzi5dfGAVLf7SfLNtb4w7G2dmukJyWQtVJEDF9oMb9yuQ=="
|
"integrity": "sha512-ZMH5RU3q1pvYS+2wGUJ5Zvy8jMGTQ4wCpbDlIQDkbIL/k6kJwBPsXnCg81g2GywlOuf0f8ezakxVSe+sZuY6ig=="
|
||||||
},
|
},
|
||||||
"discord.js": {
|
"discord.js": {
|
||||||
"version": "14.7.1",
|
"version": "14.7.1",
|
||||||
@ -5247,11 +5281,6 @@
|
|||||||
"has-symbols": "^1.0.3"
|
"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": {
|
"get-symbol-description": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
|
"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=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
"internal-slot": {
|
"internal-slot": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
|
||||||
"integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
|
"integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"get-intrinsic": "^1.1.3",
|
"get-intrinsic": "^1.2.0",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ioredis": {
|
"ioredis": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz",
|
||||||
"integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==",
|
"integrity": "sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ioredis/commands": "^1.1.1",
|
"@ioredis/commands": "^1.1.1",
|
||||||
"cluster-key-slot": "^1.1.0",
|
"cluster-key-slot": "^1.1.0",
|
||||||
@ -5780,9 +5809,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
@ -5886,9 +5915,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"open": {
|
"open": {
|
||||||
"version": "8.4.0",
|
"version": "8.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz",
|
||||||
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
|
"integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"define-lazy-prop": "^2.0.0",
|
"define-lazy-prop": "^2.0.0",
|
||||||
@ -5991,12 +6020,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"version": "4.9.0",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.10.1.tgz",
|
||||||
"integrity": "sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==",
|
"integrity": "sha512-0jDxgg+DruB1kHVNlcspXQB9au62IFfVg9drkhzXudszHNUAQn0lVuu+T8np0uC2z1nKD5S3qPeCyR8u5YFLnA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@prisma/engines": "4.9.0"
|
"@prisma/engines": "4.10.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
@ -6308,9 +6337,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-mixer": {
|
"ts-mixer": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
|
||||||
"integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A=="
|
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
|
||||||
},
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
@ -6410,9 +6439,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"undici": {
|
"undici": {
|
||||||
"version": "5.18.0",
|
"version": "5.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz",
|
||||||
"integrity": "sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA==",
|
"integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"busboy": "^1.6.0"
|
"busboy": "^1.6.0"
|
||||||
}
|
}
|
||||||
@ -6495,9 +6524,9 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||||
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
|
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "arabot",
|
"name": "arabot",
|
||||||
"version": "0.1.0",
|
"version": "0.2.1",
|
||||||
"description": "A Discord bot for Animal Rights Advocates",
|
"description": "A Discord bot for Animal Rights Advocates",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -29,16 +29,17 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/veganhacktivists/arabot#readme",
|
"homepage": "https://github.com/veganhacktivists/arabot#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^4.0.0",
|
"@prisma/client": "^4.10.1",
|
||||||
"@sapphire/discord.js-utilities": "^6.0.0",
|
"@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-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/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",
|
"@sapphire/utilities": "^3.9.2",
|
||||||
"@types/node": "^18.0.3",
|
"@types/node": "^18.0.3",
|
||||||
"bullmq": "^1.89.1",
|
"bullmq": "^3.6.6",
|
||||||
"discord.js": "^14.7.1",
|
"discord.js": "^14.7.1",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"ts-node": "^10.8.2",
|
"ts-node": "^10.8.2",
|
||||||
@ -54,6 +55,6 @@
|
|||||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.5.3",
|
"eslint-import-resolver-typescript": "^3.5.3",
|
||||||
"eslint-plugin-import": "^2.27.5",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"prisma": "^4.4.0"
|
"prisma": "^4.10.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
prisma/migrations/20230211001033_vcmute/migration.sql
Normal file
17
prisma/migrations/20230211001033_vcmute/migration.sql
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "VCMute" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"modId" TEXT NOT NULL,
|
||||||
|
"time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"endTime" TIMESTAMP(3),
|
||||||
|
"reason" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "VCMute_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "VCMute" ADD CONSTRAINT "VCMute_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "VCMute" ADD CONSTRAINT "VCMute_modId_fkey" FOREIGN KEY ("modId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
5
prisma/migrations/20230211163443_restrict/migration.sql
Normal file
5
prisma/migrations/20230211163443_restrict/migration.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Restrict" ADD COLUMN "endModId" TEXT;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Restrict" ADD CONSTRAINT "Restrict_endModId_fkey" FOREIGN KEY ("endModId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
15
prisma/migrations/20230211163734_fix_typo/migration.sql
Normal file
15
prisma/migrations/20230211163734_fix_typo/migration.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `endedTime` on the `Restrict` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `endedTime` on the `TempBan` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `endTime` to the `TempBan` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Restrict" DROP COLUMN "endedTime",
|
||||||
|
ADD COLUMN "endTime" TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "TempBan" DROP COLUMN "endedTime",
|
||||||
|
ADD COLUMN "endTime" TIMESTAMP(3) NOT NULL;
|
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `section` to the `Restrict` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Restrict" ADD COLUMN "section" INTEGER NOT NULL;
|
17
prisma/migrations/20230214100026_warnings/migration.sql
Normal file
17
prisma/migrations/20230214100026_warnings/migration.sql
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Warnings" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"modId" TEXT NOT NULL,
|
||||||
|
"time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"note" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Warnings_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Warnings" ADD CONSTRAINT "Warnings_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Warnings" ADD CONSTRAINT "Warnings_modId_fkey" FOREIGN KEY ("modId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `Warnings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Warnings" DROP CONSTRAINT "Warnings_modId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Warnings" DROP CONSTRAINT "Warnings_userId_fkey";
|
||||||
|
|
||||||
|
-- RenameTable
|
||||||
|
ALTER TABLE "Warnings" RENAME TO "Warning";
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Warning" ADD CONSTRAINT "Warning_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Warning" ADD CONSTRAINT "Warning_modId_fkey" FOREIGN KEY ("modId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Warning" RENAME CONSTRAINT "Warnings_pkey" TO "Warning_pkey";
|
@ -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;
|
@ -39,13 +39,19 @@ model User {
|
|||||||
VerifyVerifier Verify[] @relation("verVerifier")
|
VerifyVerifier Verify[] @relation("verVerifier")
|
||||||
SusUser Sus[] @relation("susUser")
|
SusUser Sus[] @relation("susUser")
|
||||||
SusMod Sus[] @relation("susMod")
|
SusMod Sus[] @relation("susMod")
|
||||||
|
WarnUser Warning[] @relation("warnUser")
|
||||||
|
WarnMod Warning[] @relation("warnMod")
|
||||||
RestrictUser Restrict[] @relation("restUser")
|
RestrictUser Restrict[] @relation("restUser")
|
||||||
RestrictMod Restrict[] @relation("restMod")
|
RestrictMod Restrict[] @relation("restMod")
|
||||||
|
RestrictEndMod Restrict[] @relation("endRestMod")
|
||||||
BanUser Ban[] @relation("banUser")
|
BanUser Ban[] @relation("banUser")
|
||||||
BanMod Ban[] @relation("banMod")
|
BanMod Ban[] @relation("banMod")
|
||||||
BanEndMod Ban[] @relation("endBanMod")
|
BanEndMod Ban[] @relation("endBanMod")
|
||||||
TempBanUser TempBan[] @relation("tbanUser")
|
TempBanUser TempBan[] @relation("tbanUser")
|
||||||
TempBanMod TempBan[] @relation("tbanMod")
|
TempBanMod TempBan[] @relation("tbanMod")
|
||||||
|
TempBanEndMod TempBan[] @relation("endTbanMod")
|
||||||
|
VCMuteUser VCMute[] @relation("vcMuteUser")
|
||||||
|
VCMuteMod VCMute[] @relation("vcMuteMod")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Verify {
|
model Verify {
|
||||||
@ -88,6 +94,17 @@ model Sus {
|
|||||||
note String
|
note String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Warning {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
user User @relation("warnUser", fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
mod User @relation("warnMod", fields: [modId], references: [id])
|
||||||
|
modId String
|
||||||
|
time DateTime @default(now())
|
||||||
|
active Boolean @default(true)
|
||||||
|
note String
|
||||||
|
}
|
||||||
|
|
||||||
model Restrict {
|
model Restrict {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
user User @relation("restUser", fields: [userId], references: [id])
|
user User @relation("restUser", fields: [userId], references: [id])
|
||||||
@ -95,8 +112,11 @@ model Restrict {
|
|||||||
mod User @relation("restMod", fields: [modId], references: [id])
|
mod User @relation("restMod", fields: [modId], references: [id])
|
||||||
modId String
|
modId String
|
||||||
startTime DateTime @default(now())
|
startTime DateTime @default(now())
|
||||||
endedTime DateTime?
|
endMod User? @relation("endRestMod", fields: [endModId], references: [id])
|
||||||
|
endModId String?
|
||||||
|
endTime DateTime?
|
||||||
reason String
|
reason String
|
||||||
|
section Int
|
||||||
}
|
}
|
||||||
|
|
||||||
model Ban {
|
model Ban {
|
||||||
@ -120,7 +140,20 @@ model TempBan {
|
|||||||
mod User @relation("tbanMod", fields: [modId], references: [id])
|
mod User @relation("tbanMod", fields: [modId], references: [id])
|
||||||
modId String
|
modId String
|
||||||
startTime DateTime @default(now())
|
startTime DateTime @default(now())
|
||||||
endedTime DateTime
|
endMod User? @relation("endTbanMod", fields: [endModId], references: [id])
|
||||||
|
endModId String?
|
||||||
|
endTime DateTime
|
||||||
active Boolean @default(true)
|
active Boolean @default(true)
|
||||||
reason String
|
reason String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model VCMute {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
user User @relation("vcMuteUser", fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
mod User @relation("vcMuteMod", fields: [modId], references: [id])
|
||||||
|
modId String
|
||||||
|
time DateTime @default(now())
|
||||||
|
endTime DateTime?
|
||||||
|
reason String?
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
/*
|
/*
|
||||||
Animal Rights Advocates Discord Bot
|
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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -18,10 +18,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
import type { User, Message, TextChannel } from 'discord.js';
|
import type {
|
||||||
|
User,
|
||||||
|
Message,
|
||||||
|
Snowflake,
|
||||||
|
TextChannel,
|
||||||
|
Guild,
|
||||||
|
} from 'discord.js';
|
||||||
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { addBan, checkActive } from '#utils/database/ban';
|
import { addBan, checkBan } from '#utils/database/ban';
|
||||||
import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser';
|
import { addEmptyUser, updateUser, userExists } from '#utils/database/dbExistingUser';
|
||||||
|
import { checkTempBan, removeTempBan } from '#utils/database/tempBan';
|
||||||
|
|
||||||
export class BanCommand extends Command {
|
export class BanCommand extends Command {
|
||||||
public constructor(context: Command.Context, options: Command.Options) {
|
public constructor(context: Command.Context, options: Command.Options) {
|
||||||
@ -54,13 +62,13 @@ export class BanCommand extends Command {
|
|||||||
// Command run
|
// Command run
|
||||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
// Get the arguments
|
// Get the arguments
|
||||||
const user = interaction.options.getUser('user');
|
const user = interaction.options.getUser('user', true);
|
||||||
const reason = interaction.options.getString('reason');
|
const reason = interaction.options.getString('reason', true);
|
||||||
const mod = interaction.member;
|
const mod = interaction.member;
|
||||||
const { guild } = interaction;
|
const { guild } = interaction;
|
||||||
|
|
||||||
// Checks if all the variables are of the right type
|
// Checks if all the variables are of the right type
|
||||||
if (user === null || guild === null || reason === null || mod === null) {
|
if (guild === null || mod === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching user!',
|
content: 'Error fetching user!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
@ -69,87 +77,9 @@ export class BanCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets mod's GuildMember
|
const ban = await this.ban(user.id, mod.user.id, reason, guild);
|
||||||
const modGuildMember = guild.members.cache.get(mod.user.id);
|
|
||||||
|
|
||||||
// Checks if guildMember is null
|
await interaction.reply({ content: ban.message });
|
||||||
if (modGuildMember === undefined) {
|
|
||||||
await interaction.reply({
|
|
||||||
content: 'Error fetching mod!',
|
|
||||||
ephemeral: true,
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await checkActive(user.id)) {
|
|
||||||
await interaction.reply(`${user} is already banned!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if mod is in database
|
|
||||||
if (!await userExists(modGuildMember.id)) {
|
|
||||||
await addExistingUser(modGuildMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets guildMember
|
|
||||||
let guildMember = guild.members.cache.get(user.id);
|
|
||||||
|
|
||||||
if (guildMember === undefined) {
|
|
||||||
guildMember = await guild.members.fetch(user.id)
|
|
||||||
.catch(() => undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (guildMember !== undefined) {
|
|
||||||
// Checks if the user is not restricted
|
|
||||||
if (guildMember.roles.cache.has(IDs.roles.vegan.vegan)) {
|
|
||||||
await interaction.reply({
|
|
||||||
content: 'You need to restrict the user first!',
|
|
||||||
ephemeral: true,
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user and mod are on the database
|
|
||||||
if (!await userExists(guildMember.id)) {
|
|
||||||
await addExistingUser(guildMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send DM for reason of ban
|
|
||||||
await user.send(`You have been banned from ARA for: ${reason}`
|
|
||||||
+ '\n\nhttps://vbcamp.org/ARA')
|
|
||||||
.catch(() => {});
|
|
||||||
|
|
||||||
// Ban the user
|
|
||||||
await guildMember.ban({ reason });
|
|
||||||
} else if (!await userExists(user.id)) {
|
|
||||||
await addEmptyUser(user.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
content: `${user} has been banned.`,
|
|
||||||
ephemeral: true,
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add ban to database
|
|
||||||
await addBan(user.id, mod.user.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('Ban Error: Could not fetch log channel');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await logChannel.send(`${user} was banned for: ${reason} by ${mod}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non Application Command method of banning a user
|
// Non Application Command method of banning a user
|
||||||
@ -186,62 +116,85 @@ export class BanCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await checkActive(user.id)) {
|
|
||||||
await message.react('❌');
|
|
||||||
await message.reply(`${user} is already banned!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.channel.id !== IDs.channels.restricted.moderators) {
|
if (message.channel.id !== IDs.channels.restricted.moderators) {
|
||||||
await message.react('❌');
|
await message.react('❌');
|
||||||
await message.reply(`You can only run this command in <#${IDs.channels.restricted.moderators}> `
|
await message.reply(`You can only run this command in <#${IDs.channels.restricted.moderators}> `
|
||||||
+ 'or alternatively use the slash command!');
|
+ 'or alternatively use the slash command!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if mod is in database
|
const ban = await this.ban(user.id, mod.user.id, reason, guild);
|
||||||
if (!await userExists(mod.id)) {
|
|
||||||
await addExistingUser(mod);
|
await message.reply(ban.message);
|
||||||
|
await message.react(ban.success ? '✅' : '❌');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ban(userId: Snowflake, modId: Snowflake, reason: string, guild: Guild) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
success: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId) as User;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets guildMember
|
// Gets mod's GuildMember
|
||||||
let guildMember = await guild.members.cache.get(user.id);
|
const mod = guild.members.cache.get(modId);
|
||||||
|
|
||||||
if (guildMember === undefined) {
|
// Checks if guildMember is null
|
||||||
guildMember = await guild.members.fetch(user.id)
|
if (mod === undefined) {
|
||||||
|
info.message = 'Error fetching mod!';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await checkBan(userId)) {
|
||||||
|
info.message = `${user} is already 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);
|
.catch(() => undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guildMember !== undefined) {
|
if (member !== undefined) {
|
||||||
// Checks if the user is not restricted
|
// Checks if the user is not restricted
|
||||||
if (guildMember.roles.cache.has(IDs.roles.vegan.vegan)) {
|
if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
|
||||||
await message.react('❌');
|
info.message = 'You need to restrict the user first!';
|
||||||
await message.reply({
|
return info;
|
||||||
content: 'You need to restrict the user first!',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user and mod are on the database
|
await updateUser(member);
|
||||||
if (!await userExists(guildMember.id)) {
|
|
||||||
await addExistingUser(guildMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send DM for reason of ban
|
// Send DM for reason of ban
|
||||||
await user.send(`You have been banned from ARA for: ${reason}`
|
await member.send(`You have been banned from ARA for: ${reason}`
|
||||||
+ '\n\nhttps://vbcamp.org/ARA')
|
+ '\n\nhttps://vbcamp.org/ARA')
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
|
||||||
// Ban the user
|
// Ban the user
|
||||||
await guildMember.ban({ reason });
|
await member.ban({ reason });
|
||||||
} else if (!await userExists(user.id)) {
|
} else if (!await userExists(userId)) {
|
||||||
await addEmptyUser(user.id);
|
await addEmptyUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ban to database
|
// Add ban to database
|
||||||
await addBan(user.id, mod.id, reason);
|
await addBan(userId, modId, reason);
|
||||||
|
|
||||||
await message.react('✅');
|
if (await checkTempBan(userId)) {
|
||||||
|
await removeTempBan(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.message = `${user} has been banned.`;
|
||||||
|
info.success = true;
|
||||||
|
|
||||||
// Log the ban
|
// Log the ban
|
||||||
let logChannel = guild.channels.cache
|
let logChannel = guild.channels.cache
|
||||||
@ -252,10 +205,24 @@ export class BanCommand extends Command {
|
|||||||
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
|
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
|
||||||
if (logChannel === undefined) {
|
if (logChannel === undefined) {
|
||||||
this.container.logger.error('Ban Error: Could not fetch log channel');
|
this.container.logger.error('Ban Error: Could not fetch log channel');
|
||||||
return;
|
info.message = `${user} has been banned. This hasn't been logged in a text channel as log channel could not be found`;
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await logChannel.send(`${user} was banned for: ${reason} by ${mod}`);
|
const log = new EmbedBuilder()
|
||||||
|
.setColor('#FF0000')
|
||||||
|
.setAuthor({ name: `Banned ${user.tag}`, 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] });
|
||||||
|
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
205
src/commands/mod/ban/tempBan.ts
Normal file
205
src/commands/mod/ban/tempBan.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
/*
|
/*
|
||||||
Animal Rights Advocates Discord Bot
|
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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -21,11 +21,15 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
|||||||
import type {
|
import type {
|
||||||
User,
|
User,
|
||||||
Message,
|
Message,
|
||||||
|
Snowflake,
|
||||||
TextChannel,
|
TextChannel,
|
||||||
|
Guild,
|
||||||
GuildBan,
|
GuildBan,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
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';
|
import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
export class UnbanCommand extends Command {
|
export class UnbanCommand extends Command {
|
||||||
@ -56,12 +60,12 @@ export class UnbanCommand extends Command {
|
|||||||
// Command run
|
// Command run
|
||||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
// Get the arguments
|
// Get the arguments
|
||||||
const user = interaction.options.getUser('user');
|
const user = interaction.options.getUser('user', true);
|
||||||
const mod = interaction.member;
|
const mod = interaction.member;
|
||||||
const { guild } = interaction;
|
const { guild } = interaction;
|
||||||
|
|
||||||
// Checks if all the variables are of the right type
|
// Checks if all the variables are of the right type
|
||||||
if (user === null || guild === null || mod === null) {
|
if (guild === null || mod === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching user!',
|
content: 'Error fetching user!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
@ -70,80 +74,9 @@ export class UnbanCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets mod's GuildMember
|
const unban = await this.unban(user.id, mod.user.id, guild);
|
||||||
const modGuildMember = guild.members.cache.get(mod.user.id);
|
|
||||||
|
|
||||||
// Checks if guildMember is null
|
await interaction.reply({ content: unban.message });
|
||||||
if (modGuildMember === undefined) {
|
|
||||||
await interaction.reply({
|
|
||||||
content: 'Error fetching mod!',
|
|
||||||
ephemeral: true,
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if mod is in database
|
|
||||||
if (!await userExists(modGuildMember.id)) {
|
|
||||||
await addExistingUser(modGuildMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await checkActive(user.id)) {
|
|
||||||
let ban: GuildBan;
|
|
||||||
try {
|
|
||||||
ban = await guild.bans.fetch(user.id);
|
|
||||||
} catch {
|
|
||||||
try {
|
|
||||||
ban = await guild.bans.fetch({ user, force: true });
|
|
||||||
} catch {
|
|
||||||
await interaction.reply({
|
|
||||||
content: `${user} is not banned.`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let { reason } = ban;
|
|
||||||
|
|
||||||
if (reason === null || reason === undefined) {
|
|
||||||
reason = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.user.id, `(Mod who banned is not accurate) - ${reason}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unban the user
|
|
||||||
await guild.members.unban(user)
|
|
||||||
.catch(() => {});
|
|
||||||
|
|
||||||
// Add unban to database
|
|
||||||
await removeBan(user.id, mod.user.id);
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
content: `${user} has been unbanned.`,
|
|
||||||
ephemeral: true,
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Log the ban
|
|
||||||
let modRestrict = guild.channels.cache
|
|
||||||
.get(IDs.channels.restricted.moderators) as TextChannel | undefined;
|
|
||||||
|
|
||||||
if (modRestrict === undefined) {
|
|
||||||
modRestrict = await guild.channels
|
|
||||||
.fetch(IDs.channels.restricted.moderators) as TextChannel | undefined;
|
|
||||||
if (modRestrict === undefined) {
|
|
||||||
this.container.logger.error('Unban Error: Could not fetch mod channel');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await modRestrict.send(`${user} was unbanned by ${mod}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non Application Command method of banning a user
|
// Non Application Command method of banning a user
|
||||||
@ -174,24 +107,55 @@ export class UnbanCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unban = await this.unban(user.id, mod.user.id, guild);
|
||||||
|
|
||||||
|
await message.reply(unban.message);
|
||||||
|
await message.react(unban.success ? '✅' : '❌');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async unban(userId: Snowflake, modId: Snowflake, guild: Guild) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
success: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if mod is in database
|
// Check if mod is in database
|
||||||
if (!await userExists(mod.id)) {
|
if (!await userExists(modId)) {
|
||||||
await addExistingUser(mod);
|
await addExistingUser(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await checkActive(user.id)) {
|
let user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
info.message = 'Could not fetch the user!';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbBan = await checkBan(userId);
|
||||||
|
const dbTempBan = await checkTempBan(userId);
|
||||||
|
|
||||||
|
if (!dbBan && !dbTempBan) {
|
||||||
let ban: GuildBan;
|
let ban: GuildBan;
|
||||||
try {
|
try {
|
||||||
ban = await guild.bans.fetch(user.id);
|
ban = await guild.bans.fetch(userId);
|
||||||
} catch {
|
} catch {
|
||||||
try {
|
try {
|
||||||
ban = await guild.bans.fetch({ user, force: true });
|
ban = await guild.bans.fetch({ user, force: true });
|
||||||
} catch {
|
} catch {
|
||||||
await message.react('❌');
|
info.message = `${user} is not banned.`;
|
||||||
await message.reply({
|
return info;
|
||||||
content: `${user} is not banned.`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let { reason } = ban;
|
let { reason } = ban;
|
||||||
@ -206,35 +170,50 @@ export class UnbanCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add missing ban
|
// Add missing ban
|
||||||
await addBan(user.id, mod.user.id, `(Mod who banned is not accurate) - ${reason}`);
|
await addBan(userId, modId, `(Mod who banned is not accurate) - ${reason}`);
|
||||||
|
dbBan = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unban the user
|
// Unban the user
|
||||||
await guild.members.unban(user)
|
await guild.members.unban(user)
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
|
||||||
// Add unban to database
|
if (dbBan) {
|
||||||
await removeBan(user.id, mod.id);
|
// Add unban to database
|
||||||
|
await removeBan(user.id, mod.user.id);
|
||||||
|
} else if (dbTempBan) {
|
||||||
|
await removeTempBan(user.id, mod.user.id);
|
||||||
|
}
|
||||||
|
|
||||||
await message.react('✅');
|
info.message = `${user} has been unbanned.`;
|
||||||
|
info.success = true;
|
||||||
|
|
||||||
await message.reply({
|
// Log unban
|
||||||
content: `${user} has been unbanned.`,
|
let logChannel = guild.channels.cache
|
||||||
});
|
.get(IDs.channels.logs.restricted) as TextChannel | undefined;
|
||||||
|
|
||||||
// Log the ban
|
if (logChannel === undefined) {
|
||||||
let modRestrict = guild.channels.cache
|
logChannel = await guild.channels
|
||||||
.get(IDs.channels.restricted.moderators) as TextChannel | undefined;
|
.fetch(IDs.channels.logs.restricted) as TextChannel | undefined;
|
||||||
|
if (logChannel === undefined) {
|
||||||
if (modRestrict === undefined) {
|
this.container.logger.error('Ban Error: Could not fetch log channel');
|
||||||
modRestrict = await guild.channels
|
info.message = `${user} has been banned. This hasn't been logged in a text channel as log channel could not be found`;
|
||||||
.fetch(IDs.channels.restricted.moderators) as TextChannel | undefined;
|
return info;
|
||||||
if (modRestrict === undefined) {
|
|
||||||
this.container.logger.error('Unban Error: Could not fetch mod channel');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await modRestrict.send(`${user} was unbanned by ${mod}`);
|
const log = new EmbedBuilder()
|
||||||
|
.setColor('#28A745')
|
||||||
|
.setAuthor({ name: `Unbanned ${user.tag}`, 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] });
|
||||||
|
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -53,14 +53,14 @@ export class RenameUserCommand extends Command {
|
|||||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
// TODO add database updates
|
// TODO add database updates
|
||||||
// Get the arguments
|
// Get the arguments
|
||||||
const user = interaction.options.getUser('user');
|
const user = interaction.options.getUser('user', true);
|
||||||
const nickname = interaction.options.getString('nickname');
|
const nickname = interaction.options.getString('nickname');
|
||||||
const { guild } = interaction;
|
const { guild } = interaction;
|
||||||
|
|
||||||
// Checks if all the variables are of the right type
|
// Checks if all the variables are of the right type
|
||||||
if (user === null || guild === null) {
|
if (guild === null) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching user!',
|
content: 'Error fetching guild!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
fetchReply: true,
|
fetchReply: true,
|
||||||
});
|
});
|
||||||
@ -68,10 +68,10 @@ export class RenameUserCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets guildMember whilst removing the ability of each other variables being null
|
// Gets guildMember whilst removing the ability of each other variables being null
|
||||||
const guildMember = guild.members.cache.get(user.id);
|
const member = guild.members.cache.get(user?.id);
|
||||||
|
|
||||||
// Checks if guildMember is null
|
// Checks if guildMember is null
|
||||||
if (guildMember === undefined) {
|
if (member === undefined) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Error fetching user!',
|
content: 'Error fetching user!',
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
@ -81,7 +81,16 @@ export class RenameUserCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change nickname
|
// Change nickname
|
||||||
await guildMember.setNickname(nickname);
|
try {
|
||||||
|
await member.setNickname(nickname);
|
||||||
|
} catch {
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'Bot doesn\'t have permission to change the user\'s name!',
|
||||||
|
ephemeral: true,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: `Changed ${user}'s nickname`,
|
content: `Changed ${user}'s nickname`,
|
||||||
fetchReply: true,
|
fetchReply: true,
|
||||||
@ -91,9 +100,9 @@ export class RenameUserCommand extends Command {
|
|||||||
|
|
||||||
public async messageRun(message: Message, args: Args) {
|
public async messageRun(message: Message, args: Args) {
|
||||||
// Get arguments
|
// Get arguments
|
||||||
let user: GuildMember;
|
let member: GuildMember;
|
||||||
try {
|
try {
|
||||||
user = await args.pick('member');
|
member = await args.pick('member');
|
||||||
} catch {
|
} catch {
|
||||||
await message.react('❌');
|
await message.react('❌');
|
||||||
await message.reply('User was not provided!');
|
await message.reply('User was not provided!');
|
||||||
@ -108,7 +117,13 @@ export class RenameUserCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.setNickname(nickname);
|
try {
|
||||||
|
await member.setNickname(nickname);
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Bot doesn\'t have permission to change the user\'s name!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await message.react('✅');
|
await message.react('✅');
|
||||||
}
|
}
|
||||||
|
361
src/commands/mod/restriction/restrict.ts
Normal file
361
src/commands/mod/restriction/restrict.ts
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
// 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,
|
||||||
|
container,
|
||||||
|
} from '@sapphire/framework';
|
||||||
|
import {
|
||||||
|
ChannelType,
|
||||||
|
EmbedBuilder,
|
||||||
|
PermissionsBitField,
|
||||||
|
time,
|
||||||
|
} from 'discord.js';
|
||||||
|
import type {
|
||||||
|
User,
|
||||||
|
Message,
|
||||||
|
TextChannel,
|
||||||
|
Guild,
|
||||||
|
Snowflake,
|
||||||
|
} from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import {
|
||||||
|
addEmptyUser,
|
||||||
|
updateUser,
|
||||||
|
userExists,
|
||||||
|
fetchRoles,
|
||||||
|
} from '#utils/database/dbExistingUser';
|
||||||
|
import { restrict, checkActive } from '#utils/database/restriction';
|
||||||
|
import { randint } from '#utils/maths';
|
||||||
|
|
||||||
|
export async function restrictRun(
|
||||||
|
userId: Snowflake,
|
||||||
|
modId: Snowflake,
|
||||||
|
reason: string,
|
||||||
|
guild: Guild,
|
||||||
|
tolerance = false,
|
||||||
|
) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Checks if guildMember is null
|
||||||
|
if (mod === undefined) {
|
||||||
|
info.message = 'Error fetching mod';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mod is in database
|
||||||
|
await updateUser(mod);
|
||||||
|
|
||||||
|
if (await checkActive(userId)) {
|
||||||
|
info.message = `<@${userId}> is already restricted!`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets guildMember
|
||||||
|
let member = guild.members.cache.get(userId);
|
||||||
|
|
||||||
|
if (member === undefined) {
|
||||||
|
member = await guild.members.fetch(userId)
|
||||||
|
.catch(() => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const restrictRoles = IDs.roles.restrictions.restricted;
|
||||||
|
|
||||||
|
let section = tolerance ? randint(3, 4) : randint(1, 2);
|
||||||
|
|
||||||
|
if (member !== undefined) {
|
||||||
|
// Checks if the user is not restricted
|
||||||
|
if (member.roles.cache.hasAny(...restrictRoles)) {
|
||||||
|
info.message = `${member} is already restricted!`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user and mod are on the database
|
||||||
|
await updateUser(member);
|
||||||
|
|
||||||
|
if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
|
||||||
|
section = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
await member.roles.add(restrictRoles[section - 1]);
|
||||||
|
|
||||||
|
if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
|
||||||
|
const voiceChannel = await guild.channels.create({
|
||||||
|
name: 'Restricted Voice Channel',
|
||||||
|
type: ChannelType.GuildVoice,
|
||||||
|
parent: IDs.categories.restricted,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: member.id,
|
||||||
|
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.restricted,
|
||||||
|
allow: [PermissionsBitField.Flags.SendMessages,
|
||||||
|
PermissionsBitField.Flags.ViewChannel,
|
||||||
|
PermissionsBitField.Flags.Connect,
|
||||||
|
PermissionsBitField.Flags.MuteMembers],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let restrictedChannel: TextChannel;
|
||||||
|
let bannedName = false;
|
||||||
|
try {
|
||||||
|
restrictedChannel = await guild.channels.create({
|
||||||
|
name: `⛔┃${member.user.username}-restricted`,
|
||||||
|
type: ChannelType.GuildText,
|
||||||
|
topic: `Restricted channel. ${member.id} ${voiceChannel.id} (Please do not change this)`,
|
||||||
|
parent: IDs.categories.restricted,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
allow: [PermissionsBitField.Flags.ReadMessageHistory],
|
||||||
|
deny: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: member.id,
|
||||||
|
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.restricted,
|
||||||
|
allow: [PermissionsBitField.Flags.SendMessages,
|
||||||
|
PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
restrictedChannel = await guild.channels.create({
|
||||||
|
name: `⛔┃${member.user.id}-restricted`,
|
||||||
|
type: ChannelType.GuildText,
|
||||||
|
topic: `Restricted channel. ${member.id} ${voiceChannel.id} (Please do not change this)`,
|
||||||
|
parent: IDs.categories.restricted,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
allow: [PermissionsBitField.Flags.ReadMessageHistory],
|
||||||
|
deny: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: member.id,
|
||||||
|
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: IDs.roles.staff.restricted,
|
||||||
|
allow: [PermissionsBitField.Flags.SendMessages,
|
||||||
|
PermissionsBitField.Flags.ViewChannel],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
bannedName = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bannedName) {
|
||||||
|
await voiceChannel.setName(`${member.user.username}-restricted`);
|
||||||
|
} else {
|
||||||
|
await voiceChannel.setName(`${member.user.id}-restricted`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinTime = time(member.joinedAt!);
|
||||||
|
const registerTime = time(member.user.createdAt);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(member.displayHexColor)
|
||||||
|
.setTitle(`Restricted channel for ${member.user.username}`)
|
||||||
|
.setDescription(`${member}`)
|
||||||
|
.setThumbnail(member.user.avatarURL()!)
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Joined:', value: `${joinTime}`, inline: true },
|
||||||
|
{ name: 'Created:', value: `${registerTime}`, inline: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
await restrictedChannel.send({ embeds: [embed] });
|
||||||
|
}
|
||||||
|
|
||||||
|
await member.roles.remove([
|
||||||
|
IDs.roles.vegan.vegan,
|
||||||
|
IDs.roles.vegan.plus,
|
||||||
|
IDs.roles.vegan.activist,
|
||||||
|
IDs.roles.trusted,
|
||||||
|
IDs.roles.nonvegan.nonvegan,
|
||||||
|
IDs.roles.nonvegan.convinced,
|
||||||
|
IDs.roles.nonvegan.vegCurious,
|
||||||
|
]);
|
||||||
|
} else if (!await userExists(userId)) {
|
||||||
|
await addEmptyUser(userId);
|
||||||
|
} else {
|
||||||
|
const dbRoles = await fetchRoles(userId);
|
||||||
|
if (dbRoles.includes(IDs.roles.vegan.vegan)) {
|
||||||
|
section = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restrict the user on the database
|
||||||
|
await restrict(userId, modId, reason, section);
|
||||||
|
|
||||||
|
info.message = `Restricted ${user}`;
|
||||||
|
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) {
|
||||||
|
container.logger.error('Restrict Error: Could not fetch log channel');
|
||||||
|
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 ${user.tag}`, iconURL: `${user.avatarURL()}` })
|
||||||
|
.addFields(
|
||||||
|
{ name: 'User', value: `${user}`, inline: true },
|
||||||
|
{ name: 'Moderator', value: `${mod}`, inline: true },
|
||||||
|
{ name: 'Reason', value: reason },
|
||||||
|
)
|
||||||
|
.setTimestamp()
|
||||||
|
.setFooter({ text: `ID: ${userId}` });
|
||||||
|
|
||||||
|
await logChannel.send({ embeds: [message] });
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RestrictCommand extends Command {
|
||||||
|
public constructor(context: Command.Context, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'restrict',
|
||||||
|
aliases: ['r', 'rest', 'rr', 'rv'],
|
||||||
|
description: 'Restricts a user',
|
||||||
|
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)
|
||||||
|
.addUserOption((option) => option.setName('user')
|
||||||
|
.setDescription('User to restrict')
|
||||||
|
.setRequired(true))
|
||||||
|
.addStringOption((option) => option.setName('reason')
|
||||||
|
.setDescription('Reason for restricting 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 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 info = await restrictRun(user?.id, mod.user.id, reason, guild);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: info.message,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Application Command method of banning a user
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let user: User;
|
||||||
|
try {
|
||||||
|
user = await args.pick('user');
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reason = args.finished ? null : await args.rest('string');
|
||||||
|
const mod = message.member;
|
||||||
|
|
||||||
|
if (reason === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Restrict reason was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Moderator not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guild } = message;
|
||||||
|
|
||||||
|
if (guild === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Guild not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = await restrictRun(user?.id, mod.user.id, reason, guild);
|
||||||
|
|
||||||
|
await message.reply(info.message);
|
||||||
|
await message.react(info.success ? '✅' : '❌');
|
||||||
|
}
|
||||||
|
}
|
118
src/commands/mod/restriction/restrictTolerance.ts
Normal file
118
src/commands/mod/restriction/restrictTolerance.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// 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 { User, Message } from 'discord.js';
|
||||||
|
import { restrictRun } from './restrict';
|
||||||
|
|
||||||
|
export class RestrictToleranceCommand extends Command {
|
||||||
|
public constructor(context: Command.Context, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'restricttolerance',
|
||||||
|
aliases: ['rt'],
|
||||||
|
description: 'Restricts a user for bigoted reasons',
|
||||||
|
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)
|
||||||
|
.addUserOption((option) => option.setName('user')
|
||||||
|
.setDescription('User to restrict')
|
||||||
|
.setRequired(true))
|
||||||
|
.addStringOption((option) => option.setName('reason')
|
||||||
|
.setDescription('Reason for restricting 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 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 info = await restrictRun(user?.id, mod.user.id, reason, guild, true);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: info.message,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Application Command method of banning a user
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let user: User;
|
||||||
|
try {
|
||||||
|
user = await args.pick('user');
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reason = args.finished ? null : await args.rest('string');
|
||||||
|
const mod = message.member;
|
||||||
|
|
||||||
|
if (reason === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Restrict reason was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Moderator not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guild } = message;
|
||||||
|
|
||||||
|
if (guild === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Guild not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = await restrictRun(user?.id, mod.user.id, reason, guild, true);
|
||||||
|
|
||||||
|
await message.reply(info.message);
|
||||||
|
await message.react(info.success ? '✅' : '❌');
|
||||||
|
}
|
||||||
|
}
|
252
src/commands/mod/restriction/unrestrict.ts
Normal file
252
src/commands/mod/restriction/unrestrict.ts
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// 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 { CategoryChannel, ChannelType, EmbedBuilder } from 'discord.js';
|
||||||
|
import type {
|
||||||
|
User,
|
||||||
|
Message,
|
||||||
|
TextChannel,
|
||||||
|
Guild,
|
||||||
|
Snowflake,
|
||||||
|
} from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import { fetchRoles, addExistingUser, userExists } from '#utils/database/dbExistingUser';
|
||||||
|
import { unRestrict, checkActive, unRestrictLegacy } from '#utils/database/restriction';
|
||||||
|
|
||||||
|
export class UnRestrictCommand extends Command {
|
||||||
|
public constructor(context: Command.Context, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'unrestrict',
|
||||||
|
aliases: ['ur', 'urv'],
|
||||||
|
description: 'Unrestricts a user',
|
||||||
|
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)
|
||||||
|
.addUserOption((option) => option.setName('user')
|
||||||
|
.setDescription('User to unrestrict')
|
||||||
|
.setRequired(true)),
|
||||||
|
{
|
||||||
|
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command run
|
||||||
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
|
// Get the arguments
|
||||||
|
const user = interaction.options.getUser('user', 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 info = await this.unRestrictRun(user?.id, mod.user.id, guild);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: info.message,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Application Command method of banning a user
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let user: User;
|
||||||
|
try {
|
||||||
|
user = await args.pick('user');
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod = message.member;
|
||||||
|
|
||||||
|
if (mod === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Moderator not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guild } = message;
|
||||||
|
|
||||||
|
if (guild === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Guild not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = await this.unRestrictRun(user?.id, mod.user.id, guild);
|
||||||
|
|
||||||
|
await message.reply(info.message);
|
||||||
|
await message.react(info.success ? '✅' : '❌');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async unRestrictRun(userId: Snowflake, modId: Snowflake, guild: Guild) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Checks if guildMember is null
|
||||||
|
if (mod === undefined) {
|
||||||
|
info.message = 'Error fetching mod';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mod is in database
|
||||||
|
if (!await userExists(modId)) {
|
||||||
|
await addExistingUser(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets guildMember
|
||||||
|
let member = guild.members.cache.get(userId);
|
||||||
|
|
||||||
|
if (member === undefined) {
|
||||||
|
member = await guild.members.fetch(userId)
|
||||||
|
.catch(() => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member === undefined) {
|
||||||
|
info.message = 'Can\'t unrestrict the user as they are not on this server';
|
||||||
|
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 = `${user} is not restricted!`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await checkActive(userId)) {
|
||||||
|
const roles = await fetchRoles(userId);
|
||||||
|
await member.roles.add(roles);
|
||||||
|
// Unrestricts the user on the database
|
||||||
|
await unRestrict(userId, modId);
|
||||||
|
} else {
|
||||||
|
let section = 1;
|
||||||
|
for (let i = 0; i < restrictRoles.length; i += 1) {
|
||||||
|
if (member.roles.cache.has(restrictRoles[i])) {
|
||||||
|
section = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await member.roles.add(IDs.roles.nonvegan.nonvegan);
|
||||||
|
// Unrestricts the user on the database but for restricts done on the old bot
|
||||||
|
await unRestrictLegacy(userId, modId, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
await member.roles.remove(restrictRoles);
|
||||||
|
|
||||||
|
// Remove vegan restrict channels
|
||||||
|
if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
|
||||||
|
const category = guild.channels.cache
|
||||||
|
.get(IDs.categories.restricted) as CategoryChannel | undefined;
|
||||||
|
|
||||||
|
let topic: string[];
|
||||||
|
|
||||||
|
if (category !== undefined) {
|
||||||
|
const textChannels = category.children.cache
|
||||||
|
.filter((c) => c.type === ChannelType.GuildText);
|
||||||
|
textChannels.forEach((c) => {
|
||||||
|
const textChannel = c as TextChannel;
|
||||||
|
// Checks if the channel topic has the user's snowflake
|
||||||
|
if (textChannel.topic?.includes(userId)) {
|
||||||
|
topic = textChannel.topic.split(' ');
|
||||||
|
const vcId = topic[topic.indexOf(userId) + 1];
|
||||||
|
const voiceChannel = guild.channels.cache.get(vcId);
|
||||||
|
|
||||||
|
if (voiceChannel !== undefined
|
||||||
|
&& voiceChannel.parentId === IDs.categories.restricted) {
|
||||||
|
voiceChannel.delete();
|
||||||
|
}
|
||||||
|
textChannel.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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('Restrict Error: Could not fetch log channel');
|
||||||
|
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 ${user.tag}`, iconURL: `${user.avatarURL()}` })
|
||||||
|
.addFields(
|
||||||
|
{ name: 'User', value: `${user}`, inline: true },
|
||||||
|
{ name: 'Moderator', value: `${mod}`, inline: true },
|
||||||
|
)
|
||||||
|
.setTimestamp()
|
||||||
|
.setFooter({ text: `ID: ${userId}` });
|
||||||
|
|
||||||
|
await logChannel.send({ embeds: [message] });
|
||||||
|
|
||||||
|
info.message = `Unrestricted ${user}`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
129
src/commands/mod/slowmode.ts
Normal file
129
src/commands/mod/slowmode.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
163
src/commands/mod/vcMute.ts
Normal file
163
src/commands/mod/vcMute.ts
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// 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 { GuildMember, Message } from 'discord.js';
|
||||||
|
import { addMute, removeMute, checkActive } from '#utils/database/vcMute';
|
||||||
|
import { addExistingUser, userExists } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
|
export class VCMuteCommand extends Command {
|
||||||
|
public constructor(context: Command.Context, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'vcmute',
|
||||||
|
aliases: ['vmute'],
|
||||||
|
description: 'Persists a server mute if a user is trying to bypass mute',
|
||||||
|
preconditions: [['CoordinatorOnly', 'ModOnly']],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 persistently mute')
|
||||||
|
.setRequired(true))
|
||||||
|
.addStringOption((option) => option.setName('reason')
|
||||||
|
.setDescription('Reason for persistently muting the user')),
|
||||||
|
{
|
||||||
|
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command run
|
||||||
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
|
// Get the arguments
|
||||||
|
const user = interaction.options.getUser('user', true);
|
||||||
|
const reason = interaction.options.getString('reason');
|
||||||
|
const modUser = interaction.member;
|
||||||
|
const { guild } = interaction;
|
||||||
|
|
||||||
|
// Checks if all the variables are of the right type
|
||||||
|
if (modUser === null || guild === null) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'Error fetching user!',
|
||||||
|
ephemeral: true,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets guildMember whilst removing the ability of each other variables being null
|
||||||
|
const member = guild.members.cache.get(user.id);
|
||||||
|
const mod = guild.members.cache.get(modUser.user.id);
|
||||||
|
|
||||||
|
// Checks if guildMember is null
|
||||||
|
if (member === undefined || mod === undefined) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'Error fetching user!',
|
||||||
|
ephemeral: true,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if removing VC Mute
|
||||||
|
if (await checkActive(member.id)) {
|
||||||
|
await removeMute(member.id);
|
||||||
|
if (member.voice.channel !== null) {
|
||||||
|
await member.voice.setMute(false, reason === null ? undefined : reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Removed server mute from ${user}`,
|
||||||
|
fetchReply: true,
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mod is in database
|
||||||
|
if (!await userExists(mod.id)) {
|
||||||
|
await addExistingUser(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add VC Mute
|
||||||
|
if (member.voice.channel !== null) {
|
||||||
|
await member.voice.setMute(true, reason === null ? undefined : reason);
|
||||||
|
}
|
||||||
|
await addMute(member.id, mod.id, reason);
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Server muted ${user}`,
|
||||||
|
fetchReply: true,
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let member: GuildMember;
|
||||||
|
try {
|
||||||
|
member = await args.pick('member');
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reason = args.finished ? null : await args.rest('string');
|
||||||
|
const mod = message.member;
|
||||||
|
|
||||||
|
if (mod === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Moderator not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if removing VC Mute
|
||||||
|
if (await checkActive(member.id)) {
|
||||||
|
await removeMute(member.id);
|
||||||
|
if (member.voice.channel !== null) {
|
||||||
|
await member.voice.setMute(false, reason === null ? undefined : reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
await message.reply(`Removed server mute from ${member}`);
|
||||||
|
await message.react('✅');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mod is in database
|
||||||
|
if (!await userExists(mod.id)) {
|
||||||
|
await addExistingUser(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add VC Mute
|
||||||
|
if (member.voice.channel !== null) {
|
||||||
|
await member.voice.setMute(true, reason === null ? undefined : reason);
|
||||||
|
}
|
||||||
|
await addMute(member.id, mod.id, reason);
|
||||||
|
await message.reply(`Server muted ${member}`);
|
||||||
|
|
||||||
|
await message.react('✅');
|
||||||
|
}
|
||||||
|
}
|
128
src/commands/mod/warn.ts
Normal file
128
src/commands/mod/warn.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// 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 } from '@sapphire/framework';
|
||||||
|
import type {
|
||||||
|
User,
|
||||||
|
Message,
|
||||||
|
Snowflake,
|
||||||
|
Guild,
|
||||||
|
} from 'discord.js';
|
||||||
|
import { addExistingUser, updateUser, userExists } from '#utils/database/dbExistingUser';
|
||||||
|
import { addWarn } from '#utils/database/warnings';
|
||||||
|
|
||||||
|
/*
|
||||||
|
This command is not intended to be functional for now, this is purely to log
|
||||||
|
warnings onto a database, so if we were to switch purely to ARA Bot, it would
|
||||||
|
mean we would have a lot of the warns already in the database.
|
||||||
|
*/
|
||||||
|
export class WarnCommand extends Command {
|
||||||
|
public constructor(context: Command.Context, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'warn',
|
||||||
|
description: 'Warns a user (only used for logging to a database for now)',
|
||||||
|
preconditions: [['CoordinatorOnly', 'ModOnly']],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let user: User;
|
||||||
|
try {
|
||||||
|
user = await args.pick('user');
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reason = args.finished ? null : await args.rest('string');
|
||||||
|
const mod = message.member;
|
||||||
|
|
||||||
|
if (reason === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Warn reason was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Moderator not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guild } = message;
|
||||||
|
|
||||||
|
if (guild === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Guild not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const warn = await this.warn(user.id, mod.id, reason, guild);
|
||||||
|
|
||||||
|
if (!warn.success) {
|
||||||
|
await message.react('❌');
|
||||||
|
}
|
||||||
|
|
||||||
|
// await message.react('✅');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async warn(userId: Snowflake, modId: Snowflake, reason: string, guild: Guild) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
success: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
info.message = 'User is not on this server';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await userExists(userId))) {
|
||||||
|
await addExistingUser(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
await addWarn(userId, modId, reason);
|
||||||
|
|
||||||
|
info.message = `Warned ${member}`;
|
||||||
|
info.success = true;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
142
src/listeners/ban/ban.ts
Normal file
142
src/listeners/ban/ban.ts
Normal 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] });
|
||||||
|
}
|
||||||
|
}
|
@ -19,9 +19,10 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { GuildMember } from 'discord.js';
|
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) {
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
@ -31,12 +32,13 @@ export class BanJoin extends Listener {
|
|||||||
|
|
||||||
public async run(user: GuildMember) {
|
public async run(user: GuildMember) {
|
||||||
// Check if the user is banned
|
// Check if the user is banned
|
||||||
if (!await checkActive(user.id)) {
|
if (!await checkBan(user.id)
|
||||||
|
&& !await checkTempBan(user.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get reason from database
|
// Get reason from database
|
||||||
const reason = await getReason(user.id);
|
const reason = await getBanReason(user.id);
|
||||||
|
|
||||||
// Send DM for ban reason
|
// Send DM for ban reason
|
||||||
await user.send(`You have been banned from ARA for: ${reason}`
|
await user.send(`You have been banned from ARA for: ${reason}`
|
126
src/listeners/ban/unban.ts
Normal file
126
src/listeners/ban/unban.ts
Normal 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] });
|
||||||
|
}
|
||||||
|
}
|
42
src/listeners/botAppreciation.ts
Normal file
42
src/listeners/botAppreciation.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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 { Listener } from '@sapphire/framework';
|
||||||
|
import type { Message } from 'discord.js';
|
||||||
|
|
||||||
|
export class BotAppreciationListener extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
event: 'messageCreate',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(message: Message) {
|
||||||
|
const content = message.content.toLowerCase();
|
||||||
|
if (!content.includes('thanks arabot')
|
||||||
|
&& !content.includes('thanks ara bot')
|
||||||
|
&& !content.includes('thank you arabot')
|
||||||
|
&& !content.includes('thank you ara bot')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await message.react('💚');
|
||||||
|
}
|
||||||
|
}
|
43
src/listeners/dbLeaveServer.ts
Normal file
43
src/listeners/dbLeaveServer.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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 { GuildMember } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
|
|
||||||
|
export class DbLeaveServerListener extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
event: 'guildMemberRemove',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(member: GuildMember) {
|
||||||
|
if (!member.roles.cache.hasAny(
|
||||||
|
IDs.roles.vegan.vegan,
|
||||||
|
IDs.roles.nonvegan.nonvegan,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateUser(member);
|
||||||
|
}
|
||||||
|
}
|
@ -19,11 +19,12 @@
|
|||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import type { GuildMember } from 'discord.js';
|
import type { GuildMember } from 'discord.js';
|
||||||
// import { fetchRoles } from '../../utils/database/dbExistingUser';
|
// import { fetchRoles } from '#utils/database/dbExistingUser';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { blockTime } from '#utils/database/verification';
|
import { blockTime } from '#utils/database/verification';
|
||||||
|
import { checkActive, getSection } from '#utils/database/restriction';
|
||||||
|
|
||||||
export class VerificationReady extends Listener {
|
export class RolesJoinServerListener extends Listener {
|
||||||
public constructor(context: Listener.Context, options: Listener.Options) {
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
@ -31,20 +32,26 @@ export class VerificationReady extends Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run(user: GuildMember) {
|
public async run(member: GuildMember) {
|
||||||
// Add basic roles
|
// Add basic roles
|
||||||
// Removed this because it can give restricted people access back,
|
|
||||||
// Currently using another bot for this
|
const roles = [];
|
||||||
// const roles = await fetchRoles(user.id);
|
// const roles = await fetchRoles(member.id);
|
||||||
const roles: string[] = [];
|
|
||||||
|
// Check if the user is restricted
|
||||||
|
if (await checkActive(member.id)) {
|
||||||
|
const section = await getSection(member.id);
|
||||||
|
roles.length = 0;
|
||||||
|
roles.push(IDs.roles.restrictions.restricted[section - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user has a verification block
|
// Check if the user has a verification block
|
||||||
const timeout = await blockTime(user.id);
|
const timeout = await blockTime(member.id);
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
roles.push(IDs.roles.verifyBlock);
|
roles.push(IDs.roles.verifyBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add roles if they don't have verification block
|
// Add roles if they don't have verification block
|
||||||
await user.roles.add(roles);
|
await member.roles.add(roles);
|
||||||
}
|
}
|
||||||
}
|
}
|
77
src/listeners/vcMute.ts
Normal file
77
src/listeners/vcMute.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// 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 { Listener } from '@sapphire/framework';
|
||||||
|
import type { VoiceState } from 'discord.js';
|
||||||
|
import { checkActive, removeMute } from '#utils/database/vcMute';
|
||||||
|
|
||||||
|
export class VCMuteListener extends Listener {
|
||||||
|
public constructor(context: Listener.Context, options: Listener.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
event: 'voiceStateUpdate',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(oldState: VoiceState, newState: VoiceState) {
|
||||||
|
// Check the user joining the channel if they need to be muted
|
||||||
|
if (oldState.channel === null && newState.channel !== null) {
|
||||||
|
const { member } = newState;
|
||||||
|
|
||||||
|
if (member === null) {
|
||||||
|
this.container.logger.error('VCMute Listener - GuildMember not found when joining');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is already muted
|
||||||
|
if (member.voice.serverMute) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is muted on the database
|
||||||
|
if (!await checkActive(member.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server mute the user
|
||||||
|
await member.voice.setMute(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user has been unmuted by a mod
|
||||||
|
if (oldState.channel !== null && newState.channel !== null) {
|
||||||
|
const { member } = newState;
|
||||||
|
|
||||||
|
if (member === null) {
|
||||||
|
this.container.logger.error('VCMute Listener - GuildMember not found when unmuting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is muted on the database
|
||||||
|
if (!await checkActive(member.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the user has been unmuted
|
||||||
|
if (oldState.serverMute && !newState.serverMute) {
|
||||||
|
await removeMute(member.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -128,7 +128,6 @@ export class VerificationJoinVCListener extends Listener {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Start 15-minute timer if verifier does not join
|
// Start 15-minute timer if verifier does not join
|
||||||
// @ts-ignore
|
|
||||||
this.container.tasks.create('verifyTimeout', {
|
this.container.tasks.create('verifyTimeout', {
|
||||||
channelId: channel.id,
|
channelId: channel.id,
|
||||||
userId: member.id,
|
userId: member.id,
|
||||||
|
@ -25,7 +25,7 @@ import { time, ChannelType, PermissionsBitField } from 'discord.js';
|
|||||||
import { maxVCs, leaveBan } from '#utils/verificationConfig';
|
import { maxVCs, leaveBan } from '#utils/verificationConfig';
|
||||||
import { getUser, checkFinish, countIncomplete } from '#utils/database/verification';
|
import { getUser, checkFinish, countIncomplete } from '#utils/database/verification';
|
||||||
import { fetchRoles } from '#utils/database/dbExistingUser';
|
import { fetchRoles } from '#utils/database/dbExistingUser';
|
||||||
import { fibonacci } from '#utils/mathsSeries';
|
import { fibonacci } from '#utils/maths';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
export class VerificationLeaveVCListener extends Listener {
|
export class VerificationLeaveVCListener extends Listener {
|
||||||
@ -88,7 +88,6 @@ export class VerificationLeaveVCListener extends Listener {
|
|||||||
// Creates the length of the time for the ban
|
// Creates the length of the time for the ban
|
||||||
const banLength = fibonacci(incompleteCount) * 3600_000;
|
const banLength = fibonacci(incompleteCount) * 3600_000;
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
this.container.tasks.create('verifyUnblock', {
|
this.container.tasks.create('verifyUnblock', {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
guildId: guild.id,
|
guildId: guild.id,
|
||||||
|
@ -26,7 +26,7 @@ export class DiversityMonMessageTask extends ScheduledTask {
|
|||||||
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
cron: '0 15 * * 1',
|
pattern: '0 15 * * 1',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +53,6 @@ export class DiversityMonMessageTask extends ScheduledTask {
|
|||||||
|
|
||||||
declare module '@sapphire/plugin-scheduled-tasks' {
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
interface ScheduledTasks {
|
interface ScheduledTasks {
|
||||||
cron: never;
|
pattern: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,7 @@ export class DiversityWedMessageTask extends ScheduledTask {
|
|||||||
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
cron: '0 15 * * 3',
|
pattern: '0 15 * * 3',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +54,6 @@ export class DiversityWedMessageTask extends ScheduledTask {
|
|||||||
|
|
||||||
declare module '@sapphire/plugin-scheduled-tasks' {
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
interface ScheduledTasks {
|
interface ScheduledTasks {
|
||||||
cron: never;
|
pattern: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,7 @@ export class RestrictedMessageTask extends ScheduledTask {
|
|||||||
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
cron: '0 17 * * *',
|
pattern: '0 17 * * *',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +49,6 @@ export class RestrictedMessageTask extends ScheduledTask {
|
|||||||
|
|
||||||
declare module '@sapphire/plugin-scheduled-tasks' {
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
interface ScheduledTasks {
|
interface ScheduledTasks {
|
||||||
cron: never;
|
pattern: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,7 @@ export class StandupTask extends ScheduledTask {
|
|||||||
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
cron: '0 12 * * 1',
|
pattern: '0 12 * * 1',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +42,6 @@ export class StandupTask extends ScheduledTask {
|
|||||||
|
|
||||||
declare module '@sapphire/plugin-scheduled-tasks' {
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
interface ScheduledTasks {
|
interface ScheduledTasks {
|
||||||
cron: never;
|
pattern: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,7 @@ export class VerifyReminder extends ScheduledTask {
|
|||||||
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
public constructor(context: ScheduledTask.Context, options: ScheduledTask.Options) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
cron: '0 */1 * * *',
|
pattern: '0 */1 * * *',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +43,6 @@ export class VerifyReminder extends ScheduledTask {
|
|||||||
|
|
||||||
declare module '@sapphire/plugin-scheduled-tasks' {
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
interface VerifyReminder {
|
interface VerifyReminder {
|
||||||
cron: never;
|
pattern: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
100
src/scheduled-tasks/tempBan.ts
Normal file
100
src/scheduled-tasks/tempBan.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -51,6 +51,6 @@ export class VerifyTimeout extends ScheduledTask {
|
|||||||
|
|
||||||
declare module '@sapphire/plugin-scheduled-tasks' {
|
declare module '@sapphire/plugin-scheduled-tasks' {
|
||||||
interface ScheduledTasks {
|
interface ScheduledTasks {
|
||||||
verifyUnblock: never;
|
verifyTimeout: never;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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({
|
const ban = await container.database.ban.findFirst({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
@ -63,7 +63,7 @@ export async function checkActive(userId: string) {
|
|||||||
return ban.active;
|
return ban.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getReason(userId: string) {
|
export async function getBanReason(userId: string) {
|
||||||
const ban = await container.database.ban.findFirst({
|
const ban = await container.database.ban.findFirst({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { GuildMember, GuildMemberRoleManager } from 'discord.js';
|
import type { GuildMember, GuildMemberRoleManager, Snowflake } from 'discord.js';
|
||||||
import { container } from '@sapphire/framework';
|
import { container } from '@sapphire/framework';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
// Checks if the user exists on the database
|
// Checks if the user exists on the database
|
||||||
export async function userExists(userId: string) {
|
export async function userExists(userId: Snowflake) {
|
||||||
// Counts if the user is on the database by their snowflake
|
// Counts if the user is on the database by their snowflake
|
||||||
const userQuery = await container.database.user.count({
|
const userQuery = await container.database.user.count({
|
||||||
where: {
|
where: {
|
||||||
@ -51,11 +51,11 @@ function getRoles(roles: GuildMemberRoleManager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adds the user to the database if they were already on the server before the bot/database
|
// Adds the user to the database if they were already on the server before the bot/database
|
||||||
export async function addExistingUser(user: GuildMember) {
|
export async function addExistingUser(member: GuildMember) {
|
||||||
// Counts if the user is on the database by their snowflake
|
// Counts if the user is on the database by their snowflake
|
||||||
const userQuery = await container.database.user.count({
|
const userQuery = await container.database.user.count({
|
||||||
where: {
|
where: {
|
||||||
id: user.id,
|
id: member.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,12 +65,12 @@ export async function addExistingUser(user: GuildMember) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse all the roles into a dictionary
|
// Parse all the roles into a dictionary
|
||||||
const roles = getRoles(user.roles);
|
const roles = getRoles(member.roles);
|
||||||
|
|
||||||
// Create the user in the database
|
// Create the user in the database
|
||||||
await container.database.user.create({
|
await container.database.user.create({
|
||||||
data: {
|
data: {
|
||||||
id: user.id,
|
id: member.id,
|
||||||
vegan: roles.vegan,
|
vegan: roles.vegan,
|
||||||
trusted: roles.trusted,
|
trusted: roles.trusted,
|
||||||
activist: roles.activist,
|
activist: roles.activist,
|
||||||
@ -84,7 +84,7 @@ export async function addExistingUser(user: GuildMember) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add an empty user to database in case they are not on the server
|
// Add an empty user to database in case they are not on the server
|
||||||
export async function addEmptyUser(userId: string) {
|
export async function addEmptyUser(userId: Snowflake) {
|
||||||
// Counts if the user is on the database by their snowflake
|
// Counts if the user is on the database by their snowflake
|
||||||
const userQuery = await container.database.user.count({
|
const userQuery = await container.database.user.count({
|
||||||
where: {
|
where: {
|
||||||
@ -105,22 +105,22 @@ export async function addEmptyUser(userId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateUser(user: GuildMember) {
|
export async function updateUser(member: GuildMember) {
|
||||||
// Check if the user is already on the database
|
// Check if the user is already on the database
|
||||||
if (!(await userExists(user.id))) {
|
if (!(await userExists(member.id))) {
|
||||||
await addExistingUser(user);
|
await addExistingUser(member);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse all the roles into a dictionary
|
// Parse all the roles into a dictionary
|
||||||
const roles = getRoles(user.roles);
|
const roles = getRoles(member.roles);
|
||||||
|
|
||||||
await container.database.user.update({
|
await container.database.user.update({
|
||||||
where: {
|
where: {
|
||||||
id: user.id,
|
id: member.id,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
id: user.id,
|
id: member.id,
|
||||||
vegan: roles.vegan,
|
vegan: roles.vegan,
|
||||||
trusted: roles.trusted,
|
trusted: roles.trusted,
|
||||||
activist: roles.activist,
|
activist: roles.activist,
|
||||||
@ -133,11 +133,11 @@ export async function updateUser(user: GuildMember) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchRoles(user: string) {
|
export async function fetchRoles(userId: Snowflake) {
|
||||||
// Get the user's roles
|
// Get the user's roles
|
||||||
const roleQuery = await container.database.user.findUnique({
|
const roleQuery = await container.database.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: user,
|
id: userId,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
vegan: true,
|
vegan: true,
|
||||||
|
124
src/utils/database/restriction.ts
Normal file
124
src/utils/database/restriction.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { container } from '@sapphire/framework';
|
||||||
|
import type { Snowflake } from 'discord.js';
|
||||||
|
|
||||||
|
export async function restrict(
|
||||||
|
userId: Snowflake,
|
||||||
|
modId: Snowflake,
|
||||||
|
reason: string,
|
||||||
|
section: number,
|
||||||
|
) {
|
||||||
|
// Add the user to the database
|
||||||
|
await container.database.restrict.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reason,
|
||||||
|
section,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unRestrict(userId: Snowflake, modId: Snowflake) {
|
||||||
|
const restriction = await container.database.restrict.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (restriction === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await container.database.restrict.update({
|
||||||
|
where: {
|
||||||
|
id: restriction.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
endMod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
endTime: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkActive(userId: Snowflake) {
|
||||||
|
const restriction = await container.database.restrict.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
endTime: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (restriction === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return restriction.endTime === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSection(userId: Snowflake) {
|
||||||
|
const restriction = await container.database.restrict.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
section: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (restriction === null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return restriction.section;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is only for restrictions created with the old bot
|
||||||
|
export async function unRestrictLegacy(userId: Snowflake, modId: Snowflake, section: number) {
|
||||||
|
await container.database.restrict.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
endMod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reason: 'This user was restricted with the old bot. Restrict reason, time and mod unknown, check old bot logs.',
|
||||||
|
section,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
98
src/utils/database/tempBan.ts
Normal file
98
src/utils/database/tempBan.ts
Normal 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;
|
||||||
|
}
|
69
src/utils/database/vcMute.ts
Normal file
69
src/utils/database/vcMute.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { container } from '@sapphire/framework';
|
||||||
|
import type { Snowflake } from 'discord.js';
|
||||||
|
|
||||||
|
export async function addMute(userId: Snowflake, modId: Snowflake, reason: string | null) {
|
||||||
|
// Add the user to the database
|
||||||
|
await container.database.vCMute.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reason,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeMute(userId: Snowflake) {
|
||||||
|
const mute = await container.database.vCMute.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mute === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query to deactivate the specific sus note
|
||||||
|
await container.database.vCMute.update({
|
||||||
|
where: {
|
||||||
|
id: mute.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
endTime: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkActive(userId: Snowflake) {
|
||||||
|
const mute = await container.database.vCMute.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
endTime: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mute === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mute.endTime === null;
|
||||||
|
}
|
@ -21,7 +21,7 @@ import type { GuildMember } from 'discord.js';
|
|||||||
import { container } from '@sapphire/framework';
|
import { container } from '@sapphire/framework';
|
||||||
import { updateUser } from '#utils/database/dbExistingUser';
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
import { leaveBan } from '#utils/verificationConfig';
|
import { leaveBan } from '#utils/verificationConfig';
|
||||||
import { fibonacci } from '#utils/mathsSeries';
|
import { fibonacci } from '#utils/maths';
|
||||||
|
|
||||||
export async function joinVerification(channelId: string, user: GuildMember) {
|
export async function joinVerification(channelId: string, user: GuildMember) {
|
||||||
// Update the user on the database with the current roles they have
|
// Update the user on the database with the current roles they have
|
||||||
|
20
src/utils/database/warnings.ts
Normal file
20
src/utils/database/warnings.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { container } from '@sapphire/framework';
|
||||||
|
import type { Snowflake } from 'discord.js';
|
||||||
|
|
||||||
|
export async function addWarn(userId: Snowflake, modId: Snowflake, message: string) {
|
||||||
|
await container.database.warning.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mod: {
|
||||||
|
connect: {
|
||||||
|
id: modId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
note: message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -38,6 +38,13 @@ const devIDs = {
|
|||||||
restricted2: '999431674997788676',
|
restricted2: '999431674997788676',
|
||||||
restricted3: '999431674997788675',
|
restricted3: '999431674997788675',
|
||||||
restricted4: '999431674997788674',
|
restricted4: '999431674997788674',
|
||||||
|
restricted: [
|
||||||
|
'999431674997788677', // Restricted 1
|
||||||
|
'999431674997788676', // Restricted 2
|
||||||
|
'999431674997788675', // Restricted 3
|
||||||
|
'999431674997788674', // Restricted 4
|
||||||
|
'1075952207091994726', // Restricted Vegan
|
||||||
|
],
|
||||||
},
|
},
|
||||||
staff: {
|
staff: {
|
||||||
coordinator: '999431675165556822',
|
coordinator: '999431675165556822',
|
||||||
|
@ -41,6 +41,13 @@ let IDs = {
|
|||||||
restricted2: '872482843304001566',
|
restricted2: '872482843304001566',
|
||||||
restricted3: '856582673258774538',
|
restricted3: '856582673258774538',
|
||||||
restricted4: '872472182888992858',
|
restricted4: '872472182888992858',
|
||||||
|
restricted: [
|
||||||
|
'809769217477050369', // Restricted 1
|
||||||
|
'872482843304001566', // Restricted 2
|
||||||
|
'856582673258774538', // Restricted 3
|
||||||
|
'872472182888992858', // Restricted 4
|
||||||
|
'1075951477379567646', // Restricted Vegan
|
||||||
|
],
|
||||||
},
|
},
|
||||||
staff: {
|
staff: {
|
||||||
coordinator: '993636242019323904',
|
coordinator: '993636242019323904',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
/*
|
/*
|
||||||
Animal Rights Advocates Discord Bot
|
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
|
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
|
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/>.
|
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
|
// Created because Stove loves Fibonacci sequences
|
||||||
// A fibonacci sequence where n = 0 => 1
|
// A fibonacci sequence where n = 0 => 1
|
||||||
export function fibonacci(position: number) {
|
export function fibonacci(position: number) {
|
Loading…
x
Reference in New Issue
Block a user