aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-07-04 15:25:32 -0400
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-07-04 15:25:32 -0400
commitcf564dbb6435886f97e2e9870363144386af368d (patch)
treed535cd73f24a145ca9d7ce1a0e1174cba0d38b31
parent34f0d1d3ff3e2a90193c9a4d4de29d8335160d6a (diff)
downloadtanzanite-cf564dbb6435886f97e2e9870363144386af368d.tar.gz
tanzanite-cf564dbb6435886f97e2e9870363144386af368d.tar.bz2
tanzanite-cf564dbb6435886f97e2e9870363144386af368d.zip
mute command
-rwxr-xr-x.pnp.js128
-rw-r--r--package.json10
-rw-r--r--src/commands/dev/eval.ts2
-rw-r--r--src/commands/dev/reload.ts6
-rw-r--r--src/commands/dev/testDuration.ts54
-rw-r--r--src/commands/moderation/kick.ts2
-rw-r--r--src/commands/moderation/mute.ts171
-rw-r--r--src/lib/extensions/discord-akairo/BushClient.ts14
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts106
-rw-r--r--src/lib/extensions/discord.js/BushGuildMember.ts62
-rw-r--r--src/lib/models/Ban.ts10
-rw-r--r--src/lib/models/Guild.ts8
-rw-r--r--src/lib/models/ModLog.ts11
-rw-r--r--src/lib/models/Mute.ts10
-rw-r--r--src/lib/models/PunishmentRole.ts10
-rw-r--r--src/lib/utils/BushConstants.ts24
-rw-r--r--src/listeners/commands/commandError.ts17
-rw-r--r--src/listeners/commands/commandMissingPermissions.ts12
-rw-r--r--src/listeners/commands/slashCommandError.ts17
-rw-r--r--yarn.lock80
20 files changed, 494 insertions, 260 deletions
diff --git a/.pnp.js b/.pnp.js
index ece27cb..8bb060f 100755
--- a/.pnp.js
+++ b/.pnp.js
@@ -40,8 +40,9 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@discordjs/voice", "npm:0.4.0"],
["@types/common-tags", "npm:1.8.0"],
["@types/express", "npm:4.17.12"],
+ ["@types/humanize-duration", "npm:3.25.0"],
["@types/node", "npm:14.17.4"],
- ["@types/uuid", "npm:8.3.0"],
+ ["@types/uuid", "npm:8.3.1"],
["@typescript-eslint/eslint-plugin", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.1"],
["@typescript-eslint/parser", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.1"],
["body-parser", "npm:1.19.0"],
@@ -49,11 +50,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["common-tags", "npm:1.8.0"],
["discord-akairo", "https://github.com/NotEnoughUpdates/discord-akairo.git#commit=58e5a8610d9cb47257cb163a94e284113a39a62b"],
["discord-api-types", "npm:0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e"],
- ["discord.js", "npm:13.0.0-dev.fe6cc0c.1625184228"],
+ ["discord.js", "npm:13.0.0-dev.7e30011.1625338909"],
["esbuild", "npm:0.12.14"],
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["eslint-config-prettier", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.3.0"],
["got", "npm:11.8.2"],
+ ["humanize-duration", "npm:3.27.0"],
["moment", "npm:2.29.1"],
["pg", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.6.0"],
["pg-hstore", "npm:2.3.4"],
@@ -99,10 +101,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]
]],
["@discordjs/builders", [
- ["npm:0.1.1", {
- "packageLocation": "./.yarn/cache/@discordjs-builders-npm-0.1.1-86a3db61e4-2902d53542.zip/node_modules/@discordjs/builders/",
+ ["npm:0.2.0", {
+ "packageLocation": "./.yarn/cache/@discordjs-builders-npm-0.2.0-7a1dcef5dd-d273d10d71.zip/node_modules/@discordjs/builders/",
"packageDependencies": [
- ["@discordjs/builders", "npm:0.1.1"],
+ ["@discordjs/builders", "npm:0.2.0"],
["discord-api-types", "npm:0.18.1"],
["tslib", "npm:2.3.0"]
],
@@ -135,7 +137,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./.yarn/cache/@discordjs-voice-npm-0.4.0-275bceca0f-a1b65c946d.zip/node_modules/@discordjs/voice/",
"packageDependencies": [
["@discordjs/voice", "npm:0.4.0"],
- ["@types/ws", "npm:7.4.5"],
+ ["@types/ws", "npm:7.4.6"],
["discord-api-types", "npm:0.18.1"],
["prism-media", "virtual:275bceca0f79f8e7aeb7f77acb5a6bd201e7eea264e9dfd8dcc0bcf35691357327303c09557dfc1495b9a1f8e9cf8278133d234783570df6cb70a249778f3198#npm:1.3.1"],
["tiny-typed-emitter", "npm:2.0.3"],
@@ -162,6 +164,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["@humanwhocodes/config-array", [
+ ["npm:0.5.0", {
+ "packageLocation": "./.yarn/cache/@humanwhocodes-config-array-npm-0.5.0-5ded120470-71e3c1fef4.zip/node_modules/@humanwhocodes/config-array/",
+ "packageDependencies": [
+ ["@humanwhocodes/config-array", "npm:0.5.0"],
+ ["@humanwhocodes/object-schema", "npm:1.2.0"],
+ ["debug", "virtual:f8b688e6542ef635eefae684748f7db5410cd1803b0f43b02ae3ebd860a79349e9ae07146ce05bdfb5fca9536ad9dbe230ed7f4c2f2ffe26893f1f1b0b8700e7#npm:4.3.2"],
+ ["minimatch", "npm:3.0.4"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
+ ["@humanwhocodes/object-schema", [
+ ["npm:1.2.0", {
+ "packageLocation": "./.yarn/cache/@humanwhocodes-object-schema-npm-1.2.0-6bc0ff9fda-ef533ee0d2.zip/node_modules/@humanwhocodes/object-schema/",
+ "packageDependencies": [
+ ["@humanwhocodes/object-schema", "npm:1.2.0"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["@nodelib/fs.scandir", [
["npm:2.1.5", {
"packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-91b3de88d9.zip/node_modules/@nodelib/fs.scandir/",
@@ -227,7 +250,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageDependencies": [
["@types/body-parser", "npm:1.19.0"],
["@types/connect", "npm:3.4.34"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
@@ -239,7 +262,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/cacheable-request", "npm:6.0.1"],
["@types/http-cache-semantics", "npm:4.0.0"],
["@types/keyv", "npm:3.1.1"],
- ["@types/node", "npm:15.14.0"],
+ ["@types/node", "npm:16.0.0"],
["@types/responselike", "npm:1.0.0"]
],
"linkType": "HARD",
@@ -259,7 +282,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./.yarn/cache/@types-connect-npm-3.4.34-39e4f7bb55-6f712a0408.zip/node_modules/@types/connect/",
"packageDependencies": [
["@types/connect", "npm:3.4.34"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
@@ -282,7 +305,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./.yarn/cache/@types-express-serve-static-core-npm-4.17.22-e9a70ec107-e9ed12d6fd.zip/node_modules/@types/express-serve-static-core/",
"packageDependencies": [
["@types/express-serve-static-core", "npm:4.17.22"],
- ["@types/node", "npm:15.14.0"],
+ ["@types/node", "npm:16.0.0"],
["@types/qs", "npm:6.9.6"],
["@types/range-parser", "npm:1.2.3"]
],
@@ -298,6 +321,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["@types/humanize-duration", [
+ ["npm:3.25.0", {
+ "packageLocation": "./.yarn/cache/@types-humanize-duration-npm-3.25.0-2f39dfbb90-cd28065813.zip/node_modules/@types/humanize-duration/",
+ "packageDependencies": [
+ ["@types/humanize-duration", "npm:3.25.0"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["@types/json-schema", [
["npm:7.0.7", {
"packageLocation": "./.yarn/cache/@types-json-schema-npm-7.0.7-95fb8178d7-b9d2c509fa.zip/node_modules/@types/json-schema/",
@@ -312,7 +344,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./.yarn/cache/@types-keyv-npm-3.1.1-779a80f2c7-3aaf557d5b.zip/node_modules/@types/keyv/",
"packageDependencies": [
["@types/keyv", "npm:3.1.1"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
@@ -334,10 +366,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
],
"linkType": "HARD",
}],
- ["npm:15.14.0", {
- "packageLocation": "./.yarn/cache/@types-node-npm-15.14.0-b271eff214-b36835ddec.zip/node_modules/@types/node/",
+ ["npm:16.0.0", {
+ "packageLocation": "./.yarn/cache/@types-node-npm-16.0.0-99daaf613d-0302e376d5.zip/node_modules/@types/node/",
"packageDependencies": [
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
@@ -365,7 +397,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./.yarn/cache/@types-responselike-npm-1.0.0-85dd08af42-e6e6613c80.zip/node_modules/@types/responselike/",
"packageDependencies": [
["@types/responselike", "npm:1.0.0"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
@@ -376,26 +408,26 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageDependencies": [
["@types/serve-static", "npm:1.13.9"],
["@types/mime", "npm:1.3.2"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
]],
["@types/uuid", [
- ["npm:8.3.0", {
- "packageLocation": "./.yarn/cache/@types-uuid-npm-8.3.0-60b441a3d7-620985aed8.zip/node_modules/@types/uuid/",
+ ["npm:8.3.1", {
+ "packageLocation": "./.yarn/cache/@types-uuid-npm-8.3.1-4239b14bac-5864be8303.zip/node_modules/@types/uuid/",
"packageDependencies": [
- ["@types/uuid", "npm:8.3.0"]
+ ["@types/uuid", "npm:8.3.1"]
],
"linkType": "HARD",
}]
]],
["@types/ws", [
- ["npm:7.4.5", {
- "packageLocation": "./.yarn/cache/@types-ws-npm-7.4.5-3f11000a5b-b467f596cc.zip/node_modules/@types/ws/",
+ ["npm:7.4.6", {
+ "packageLocation": "./.yarn/cache/@types-ws-npm-7.4.6-292d5dff7c-bf81b170ab.zip/node_modules/@types/ws/",
"packageDependencies": [
- ["@types/ws", "npm:7.4.5"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/ws", "npm:7.4.6"],
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
@@ -418,7 +450,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@typescript-eslint/parser", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.1"],
["@typescript-eslint/scope-manager", "npm:4.28.1"],
["debug", "virtual:f8b688e6542ef635eefae684748f7db5410cd1803b0f43b02ae3ebd860a79349e9ae07146ce05bdfb5fca9536ad9dbe230ed7f4c2f2ffe26893f1f1b0b8700e7#npm:4.3.2"],
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["functional-red-black-tree", "npm:1.0.1"],
["regexpp", "npm:3.2.0"],
["semver", "npm:7.3.5"],
@@ -452,7 +484,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@typescript-eslint/scope-manager", "npm:4.28.1"],
["@typescript-eslint/types", "npm:4.28.1"],
["@typescript-eslint/typescript-estree", "virtual:fa0658a3262f0ec4c70032bfff5a258eb88f64f6e176c2c2ba25415d8f40b4600e45fe35d5759e7bb129491bde554953d2634a2029329b48b524f61629e9358b#npm:4.28.1"],
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["eslint-scope", "npm:5.1.1"],
["eslint-utils", "virtual:fa0658a3262f0ec4c70032bfff5a258eb88f64f6e176c2c2ba25415d8f40b4600e45fe35d5759e7bb129491bde554953d2634a2029329b48b524f61629e9358b#npm:3.0.0"]
],
@@ -480,7 +512,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@typescript-eslint/types", "npm:4.28.1"],
["@typescript-eslint/typescript-estree", "virtual:0dc5aa9f6f0f733d6c5540fb4e88f0a87a2e2803a976d019384f6d94d38591e0499786bdfa77aa81af9beb4356234f85a21e2901f1bfbc3ea45729114940d517#npm:4.28.1"],
["debug", "virtual:f8b688e6542ef635eefae684748f7db5410cd1803b0f43b02ae3ebd860a79349e9ae07146ce05bdfb5fca9536ad9dbe230ed7f4c2f2ffe26893f1f1b0b8700e7#npm:4.3.2"],
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["typescript", "patch:typescript@npm%3A4.2.4#builtin<compat/typescript>::version=4.2.4&hash=ddfc1b"]
],
"packagePeers": [
@@ -789,8 +821,9 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@discordjs/voice", "npm:0.4.0"],
["@types/common-tags", "npm:1.8.0"],
["@types/express", "npm:4.17.12"],
+ ["@types/humanize-duration", "npm:3.25.0"],
["@types/node", "npm:14.17.4"],
- ["@types/uuid", "npm:8.3.0"],
+ ["@types/uuid", "npm:8.3.1"],
["@typescript-eslint/eslint-plugin", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.1"],
["@typescript-eslint/parser", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.1"],
["body-parser", "npm:1.19.0"],
@@ -798,11 +831,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["common-tags", "npm:1.8.0"],
["discord-akairo", "https://github.com/NotEnoughUpdates/discord-akairo.git#commit=58e5a8610d9cb47257cb163a94e284113a39a62b"],
["discord-api-types", "npm:0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e"],
- ["discord.js", "npm:13.0.0-dev.fe6cc0c.1625184228"],
+ ["discord.js", "npm:13.0.0-dev.7e30011.1625338909"],
["esbuild", "npm:0.12.14"],
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["eslint-config-prettier", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.3.0"],
["got", "npm:11.8.2"],
+ ["humanize-duration", "npm:3.27.0"],
["moment", "npm:2.29.1"],
["pg", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.6.0"],
["pg-hstore", "npm:2.3.4"],
@@ -1071,7 +1105,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
]],
["discord-akairo", [
["https://github.com/NotEnoughUpdates/discord-akairo.git#commit=58e5a8610d9cb47257cb163a94e284113a39a62b", {
- "packageLocation": "./.yarn/cache/discord-akairo-https-10183ac4c6-3a07eb6541.zip/node_modules/discord-akairo/",
+ "packageLocation": "./.yarn/unplugged/discord-akairo-https-10183ac4c6/node_modules/discord-akairo/",
"packageDependencies": [
["discord-akairo", "https://github.com/NotEnoughUpdates/discord-akairo.git#commit=58e5a8610d9cb47257cb163a94e284113a39a62b"]
],
@@ -1095,15 +1129,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]
]],
["discord.js", [
- ["npm:13.0.0-dev.fe6cc0c.1625184228", {
- "packageLocation": "./.yarn/cache/discord.js-npm-13.0.0-dev.fe6cc0c.1625184228-ea99e41fe8-0b2a39e708.zip/node_modules/discord.js/",
+ ["npm:13.0.0-dev.7e30011.1625338909", {
+ "packageLocation": "./.yarn/unplugged/discord.js-npm-13.0.0-dev.7e30011.1625338909-42f62a3a15/node_modules/discord.js/",
"packageDependencies": [
- ["discord.js", "npm:13.0.0-dev.fe6cc0c.1625184228"],
- ["@discordjs/builders", "npm:0.1.1"],
+ ["discord.js", "npm:13.0.0-dev.7e30011.1625338909"],
+ ["@discordjs/builders", "npm:0.2.0"],
["@discordjs/collection", "npm:0.1.6"],
["@discordjs/form-data", "npm:3.0.1"],
["@sapphire/async-queue", "npm:1.1.4"],
- ["@types/ws", "npm:7.4.5"],
+ ["@types/ws", "npm:7.4.6"],
["abort-controller", "npm:3.0.0"],
["discord-api-types", "npm:0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e"],
["node-fetch", "npm:2.6.1"],
@@ -1195,12 +1229,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]
]],
["eslint", [
- ["npm:7.29.0", {
- "packageLocation": "./.yarn/cache/eslint-npm-7.29.0-aec1968387-812f8c5123.zip/node_modules/eslint/",
+ ["npm:7.30.0", {
+ "packageLocation": "./.yarn/cache/eslint-npm-7.30.0-8a8a06f9b4-028048847e.zip/node_modules/eslint/",
"packageDependencies": [
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["@babel/code-frame", "npm:7.12.11"],
["@eslint/eslintrc", "npm:0.4.2"],
+ ["@humanwhocodes/config-array", "npm:0.5.0"],
["ajv", "npm:6.12.6"],
["chalk", "npm:4.1.1"],
["cross-spawn", "npm:7.0.3"],
@@ -1255,7 +1290,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageDependencies": [
["eslint-config-prettier", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.3.0"],
["@types/eslint", null],
- ["eslint", "npm:7.29.0"]
+ ["eslint", "npm:7.30.0"]
],
"packagePeers": [
"@types/eslint",
@@ -1296,7 +1331,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageDependencies": [
["eslint-utils", "virtual:fa0658a3262f0ec4c70032bfff5a258eb88f64f6e176c2c2ba25415d8f40b4600e45fe35d5759e7bb129491bde554953d2634a2029329b48b524f61629e9358b#npm:3.0.0"],
["@types/eslint", null],
- ["eslint", "npm:7.29.0"],
+ ["eslint", "npm:7.30.0"],
["eslint-visitor-keys", "npm:2.1.0"]
],
"packagePeers": [
@@ -1636,6 +1671,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["humanize-duration", [
+ ["npm:3.27.0", {
+ "packageLocation": "./.yarn/cache/humanize-duration-npm-3.27.0-dde7ed8208-2cda40de2b.zip/node_modules/humanize-duration/",
+ "packageDependencies": [
+ ["humanize-duration", "npm:3.27.0"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["iconv-lite", [
["npm:0.4.24", {
"packageLocation": "./.yarn/cache/iconv-lite-npm-0.4.24-c5c4ac6695-a9b9521066.zip/node_modules/iconv-lite/",
@@ -2964,7 +3008,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./.yarn/cache/wkx-npm-0.5.0-fca5152cd8-ea38c886b8.zip/node_modules/wkx/",
"packageDependencies": [
["wkx", "npm:0.5.0"],
- ["@types/node", "npm:15.14.0"]
+ ["@types/node", "npm:16.0.0"]
],
"linkType": "HARD",
}]
diff --git a/package.json b/package.json
index f0f63f0..0d1e9bb 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"devDependencies": {
"@types/common-tags": "^1.8.0",
"@types/express": "^4.17.12",
+ "@types/humanize-duration": "^3",
"@types/node": "^14.14.22",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.14.1",
@@ -41,6 +42,7 @@
"discord-api-types": "0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e",
"discord.js": "dev",
"got": "^11.8.2",
+ "humanize-duration": "^3.27.0",
"moment": "^2.29.1",
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
@@ -94,5 +96,13 @@
"quoteProps": "consistent",
"singleQuote": true,
"trailingComma": "none"
+ },
+ "dependenciesMeta": {
+ "discord-akairo@8.2.2": {
+ "unplugged": true
+ },
+ "discord.js@13.0.0-dev.7e30011.1625338909": {
+ "unplugged": true
+ }
}
}
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts
index 4896945..ebf055b 100644
--- a/src/commands/dev/eval.ts
+++ b/src/commands/dev/eval.ts
@@ -259,7 +259,7 @@ export default class EvalCommand extends BushCommand {
} else {
embed.addField('📥 Input', await this.client.util.codeblock(inputJS, 1024, 'js'));
}
- embed.addField('📤 Output', await this.client.util.codeblock(e?.stack, 1024, 'js'));
+ embed.addField('📤 Output', await this.client.util.codeblock(typeof e === 'object' ? e?.stack : e, 1024, 'js'));
}
if (!args.silent && !message.util.isSlash) {
await message.util.reply({ embeds: [embed], ephemeral: args.silent });
diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts
index d932816..4a69098 100644
--- a/src/commands/dev/reload.ts
+++ b/src/commands/dev/reload.ts
@@ -46,7 +46,11 @@ export default class ReloadCommand extends BushCommand {
return message.util.send(`🔁 Successfully reloaded! (${new Date().getTime() - s.getTime()}ms)`);
} catch (e) {
return message.util.send(
- `An error occurred while reloading:\n${await this.client.util.codeblock(e.stack, 2048 - 34, 'js')}`
+ `An error occurred while reloading:\n${await this.client.util.codeblock(
+ typeof e === 'object' ? e?.stack : e,
+ 2048 - 34,
+ 'js'
+ )}`
);
}
}
diff --git a/src/commands/dev/testDuration.ts b/src/commands/dev/testDuration.ts
new file mode 100644
index 0000000..bf30840
--- /dev/null
+++ b/src/commands/dev/testDuration.ts
@@ -0,0 +1,54 @@
+import { stripIndents } from 'common-tags';
+import { Message } from 'discord.js';
+import { BushCommand } from '../../lib/extensions/discord-akairo/BushCommand';
+import { BushSlashMessage } from '../../lib/extensions/discord-akairo/BushSlashMessage';
+
+export default class TestDurationCommand extends BushCommand {
+ public constructor() {
+ super('testduration', {
+ aliases: ['testduration'],
+ category: 'dev',
+ description: {
+ content: 'Tests duration parsing.',
+ usage: 'testduration [reason]',
+ examples: ['testduration']
+ },
+ args: [
+ {
+ id: 'reason',
+ type: 'contentWithDuration',
+ match: 'rest',
+ prompt: {
+ start: 'Enter text and a duration here.',
+ retry: '{error} Error parsing duration and text.',
+ optional: true
+ }
+ }
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ type: 'STRING',
+ name: 'reason',
+ description: 'Enter text and a duration here.',
+ required: false
+ }
+ ],
+ hidden: true,
+ ownerOnly: true
+ });
+ }
+
+ async exec(
+ message: Message | BushSlashMessage,
+ { reason }: { reason?: { duration: number; contentWithoutTime: string } }
+ ): Promise<unknown> {
+ const rawDuration = reason.duration;
+ const text = reason.contentWithoutTime;
+ const humanizedDuration = this.client.util.humanizeDuration(rawDuration);
+ return await message.util.reply(stripIndents`
+ **rawDuration:** ${rawDuration}
+ **text:** ${text}
+ **humanizedDuration:** ${humanizedDuration}`);
+ }
+}
diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts
index df538bc..f88819b 100644
--- a/src/commands/moderation/kick.ts
+++ b/src/commands/moderation/kick.ts
@@ -76,7 +76,7 @@ export default class KickCommand extends BushCommand {
// });
// await modlogEnry.save();
// } catch (e) {
- // this.client.console.error(`KickCommand`, `Error saving to database. ${e?.stack}`);
+ // this.client.console.error(`KickCommand`, `Error saving to database. ${typeof e === 'object' ? e?.stack : e}`);
// yield `${this.client.util.emojis.error} Error saving to database. Please report this to a developer.`;
// return;
// }
diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts
index ffad432..8ac77a7 100644
--- a/src/commands/moderation/mute.ts
+++ b/src/commands/moderation/mute.ts
@@ -53,136 +53,83 @@ export default class MuteCommand extends BushCommand {
slash: true
});
}
- // async *genResponses(
- // message: Message | CommandInteraction,
- // user: User,
- // reason?: string,
- // time?: number
- // ): AsyncIterable<string> {
- // const duration = moment.duration(time);
- // let modlogEnry: ModLog;
- // let muteEntry: Mute;
- // // Create guild entry so postgres doesn't get mad when I try and add a modlog entry
- // await Guild.findOrCreate({
- // where: {
- // id: message.guild.id
- // },
- // defaults: {
- // id: message.guild.id
- // }
- // });
- // try {
- // const muteRole = (await Guild.findByPk(message.guild.id)).get('muteRole');
- // try {
- // if (time) {
- // modlogEnry = ModLog.build({
- // user: user.id,
- // guild: message.guild.id,
- // reason,
- // type: ModLogType.TEMP_MUTE,
- // duration: duration.asMilliseconds(),
- // moderator: message instanceof CommandInteraction ? message.user.id : message.author.id
- // });
- // muteEntry = Mute.build({
- // user: user.id,
- // guild: message.guild.id,
- // reason,
- // expires: new Date(new Date().getTime() + duration.asMilliseconds()),
- // modlog: modlogEnry.id
- // });
- // } else {
- // modlogEnry = ModLog.build({
- // user: user.id,
- // guild: message.guild.id,
- // reason,
- // type: ModLogType.MUTE,
- // moderator: message instanceof CommandInteraction ? message.user.id : message.author.id
- // });
- // muteEntry = Mute.build({
- // user: user.id,
- // guild: message.guild.id,
- // reason,
- // modlog: modlogEnry.id
- // });
- // }
- // await modlogEnry.save();
- // await muteEntry.save();
- // } catch (e) {
- // this.client.console.error(`MuteCommand`, `Error saving to database. ${e?.stack}`);
- // yield `${this.client.util.emojis.error} Error saving to database. Please report this to a developer.`;
- // return;
- // }
- // try {
- // await user.send(
- // `You were muted in ${message.guild.name} ${time ? `for ${duration.humanize()}` : 'permanently'} with reason \`${
- // reason || 'No reason given'
- // }\``
- // );
- // } catch (e) {
- // yield `${this.client.util.emojis.warn} Unable to dm user`;
- // }
- // await (
- // await message.guild.members.fetch(user)
- // ).roles.add(
- // muteRole,
- // `Muted by ${message instanceof CommandInteraction ? message.user.tag : message.author.tag} with ${
- // reason ? `reason ${reason}` : 'no reason'
- // }`
- // );
- // yield `${this.client.util.emojis.success} muted <@!${user.id}> ${
- // time ? `for ${duration.humanize()}` : 'permanently'
- // } with reason \`${reason || 'No reason given'}\``;
- // } catch {
- // yield `${this.client.util.emojis.error} Error muting :/`;
- // await muteEntry.destroy();
- // await modlogEnry.destroy();
- // return;
- // }
- // }
async exec(
message: BushMessage,
{ user, reason }: { user: BushUser; reason?: { duration: number; contentWithoutTime: string } }
): Promise<unknown> {
- return message.util.reply(`${this.client.util.emojis.error} This command is not finished.`);
- // this.client.console.debug(reason);
-
- // if (typeof time === 'string') {
- // time = (await Argument.cast('duration', this.client.commandHandler.resolver, message, time)) as number;
- // }
- // for await (const response of this.genResponses(message, user, reason.join(' '), time)) {
- // await message.util.sendNew(response);
- // }
-
+ const error = this.client.util.emojis.error;
const member = message.guild.members.cache.get(user.id) as BushGuildMember;
- if (!this.client.util.moderatorCanModerateUser(message.member, member)) {
- return message.util.reply({
- content: `${this.client.util.emojis.error} You cannot mute **${member.user.tag}**.`
- });
+ const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member);
+ const victimBoldTag = `**${member.user.tag}**`;
+ switch (canModerateResponse) {
+ case 'moderator':
+ return message.util.reply(`${error} You cannot mute ${victimBoldTag} because they are a moderator.`);
+ case 'user hierarchy':
+ return message.util.reply(
+ `${error} You cannot mute ${victimBoldTag} because they have higher or equal role hierarchy as you do.`
+ );
+ case 'client hierarchy':
+ return message.util.reply(
+ `${error} You cannot mute ${victimBoldTag} because they have higher or equal role hierarchy as I do.`
+ );
+ case 'self':
+ return message.util.reply(`${error} You cannot mute yourself.`);
}
- const time =
- typeof reason === 'string'
- ? //@ts-ignore: you are unreachable bitch
- await Argument.cast('duration', this.client.commandHandler.resolver, message, reason)
- : reason.duration;
+ let time;
+ if (reason) {
+ time =
+ typeof reason === 'string'
+ ? await Argument.cast('duration', this.client.commandHandler.resolver, message, reason)
+ : reason.duration;
+ }
const parsedReason = reason.contentWithoutTime;
const response = await member.mute({
reason: parsedReason,
moderator: message.author,
- duration: time,
- createModLogEntry: true
+ duration: time
});
switch (response) {
- case 'success':
- return message.util.reply(`${this.client.util.emojis.success} Successfully muted **${member.user.tag}**.`);
+ case 'missing permissions':
+ return message.util.reply(
+ `${error} Could not mute ${victimBoldTag} because I am missing the \`Manage Roles\` permission.`
+ );
case 'no mute role':
return message.util.reply(
- `${this.client.util.emojis.error} Could not mute **${
- member.user.tag
- }**, you must set a mute role with ${message.guild.getSetting('prefix')}.`
+ `${error} Could not mute ${victimBoldTag}, you must set a mute role with \`${message.guild.getSetting(
+ 'prefix'
+ )}muterole\`.`
);
+ case 'invalid mute role':
+ return message.util.reply(
+ `${error} Could not mute ${victimBoldTag} because the current mute role no longer exists. Please set a new mute role with \`${message.guild.getSetting(
+ 'prefix'
+ )}muterole\`.`
+ );
+ case 'mute role not manageable':
+ return message.util.reply(
+ `${error} Could not mute ${victimBoldTag} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${message.guild.getSetting(
+ 'prefix'
+ )}muterole\`.`
+ );
+ case 'error giving mute role':
+ return message.util.reply(`${error} Could not mute ${victimBoldTag}, there was an error assigning them the mute role.`);
+ case 'error creating modlog entry':
+ return message.util.reply(
+ `${error} While muting ${victimBoldTag}, there was an error creating a modlog entry, please report this to my developers.`
+ );
+ case 'error creating mute entry':
+ return message.util.reply(
+ `${error} While muting ${victimBoldTag}, there was an error creating a mute entry, please report this to my developers.`
+ );
+ case 'failed to dm':
+ return message.util.reply(
+ `${this.client.util.emojis.warn} Muted **${member.user.tag}** however I could not send them a dm.`
+ );
+ case 'success':
+ return message.util.reply(`${this.client.util.emojis.success} Successfully muted **${member.user.tag}**.`);
}
}
}
diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts
index 6911573..ed5c90a 100644
--- a/src/lib/extensions/discord-akairo/BushClient.ts
+++ b/src/lib/extensions/discord-akairo/BushClient.ts
@@ -216,7 +216,11 @@ export class BushClient extends AkairoClient {
loaders[loader].loadAll();
await this.logger.success('Startup', `Successfully loaded <<${loader}>>.`, false);
} catch (e) {
- await this.logger.error('Startup', `Unable to load loader <<${loader}>> with error:\n${e?.stack}`, false);
+ await this.logger.error(
+ 'Startup',
+ `Unable to load loader <<${loader}>> with error:\n${typeof e === 'object' ? e?.stack : e}`,
+ false
+ );
}
}
await this.dbPreInit();
@@ -237,8 +241,12 @@ export class BushClient extends AkairoClient {
Models.StickyRole.initModel(this.db);
await this.db.sync({ alter: true }); // Sync all tables to fix everything if updated
await this.console.success('Startup', `Successfully connected to <<database>>.`, false);
- } catch (error) {
- await this.console.error('Startup', `Failed to connect to <<database>> with error:\n` + error?.stack, false);
+ } catch (e) {
+ await this.console.error(
+ 'Startup',
+ `Failed to connect to <<database>> with error:\n` + typeof e === 'object' ? e?.stack : e,
+ false
+ );
}
}
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index 9289598..5a22efc 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -24,8 +24,9 @@ import {
WebhookEditMessageOptions
} from 'discord.js';
import got from 'got';
+import humanizeDuration from 'humanize-duration';
import { promisify } from 'util';
-import { Global, Guild, ModLog, ModLogType } from '../../models';
+import { Ban, Global, Guild, ModLog, ModLogType, Mute, PunishmentRole } from '../../models';
import { BushCache } from '../../utils/BushCache';
import { BushConstants } from '../../utils/BushConstants';
import { BushGuildResolvable } from '../discord.js/BushCommandInteraction';
@@ -302,7 +303,7 @@ export class BushClientUtil extends ClientUtil {
});
const filter = (interaction: ButtonInteraction) =>
interaction.customID.startsWith('paginate_') && interaction.message == msg;
- const collector = msg.createMessageComponentInteractionCollector({ filter, time: 300000 });
+ const collector = msg.createMessageComponentCollector({ filter, time: 300000 });
collector.on('collect', async (interaction: MessageComponentInteraction) => {
if (interaction.user.id == message.author.id || this.client.config.owners.includes(interaction.user.id)) {
switch (interaction.customID) {
@@ -391,7 +392,7 @@ export class BushClientUtil extends ClientUtil {
updateOptions();
const msg = await message.util.reply(options as MessageOptions & { split?: false });
const filter = (interaction: ButtonInteraction) => interaction.customID == 'paginate__stop' && interaction.message == msg;
- const collector = msg.createMessageComponentInteractionCollector({ filter, time: 300000 });
+ const collector = msg.createMessageComponentCollector({ filter, time: 300000 });
collector.on('collect', async (interaction: MessageComponentInteraction) => {
if (interaction.user.id == message.author.id || this.client.config.owners.includes(interaction.user.id)) {
await interaction.deferUpdate().catch(() => undefined);
@@ -530,23 +531,36 @@ export class BushClientUtil extends ClientUtil {
return newArray;
}
- public parseDuration(content: string): { duration: number; contentWithoutTime: string } {
+ public parseDuration(content: string, remove = true): { duration: number; contentWithoutTime: string } {
if (!content) return { duration: 0, contentWithoutTime: null };
- let duration = 0,
- contentWithoutTime = content;
+ let duration = 0;
+ // Try to reduce false positives by requiring a space before the duration, this makes sure it still matches if it is
+ // in the beginning of the argument
+ let contentWithoutTime = ` ${content}`;
- const regexString = Object.entries(BushConstants.TimeUnits)
- .map(([name, { label }]) => String.raw`(?:(?<${name}>-?(?:\d+)?\.?\d+) *${label})?`)
- .join('\\s*');
- const match = new RegExp(`^${regexString}$`, 'im').exec(content);
- if (!match) return null;
+ for (const unit in BushConstants.TimeUnits) {
+ const regex = BushConstants.TimeUnits[unit].match;
+ const match = regex.exec(contentWithoutTime);
+ const value = Number(match?.groups?.[unit] || 0);
+ // this.client.console.debug(unit + ': ' + value);
+ duration += value * BushConstants.TimeUnits[unit].value;
- for (const key in match.groups) {
- contentWithoutTime = contentWithoutTime.replace(match.groups[key], '');
- const value = Number(match.groups[key] || 0);
- duration += value * BushConstants.TimeUnits[key].value;
+ if (remove) contentWithoutTime = contentWithoutTime.replace(regex, '');
+ // this.client.console.debug(contentWithoutTime);
}
+ //^(?:(?<years>-?(?:\d+)?\.?\d+) *(?:years?|y))?\s*(?:(?<months>-?(?:\d+)?\.?\d+) *(?:months?|mon|mo?))?\s*(?:(?<weeks>-?(?:\d+)?\.?\d+) *(?:weeks?|w))?\s*(?:(?<days>-?(?:\d+)?\.?\d+) *(?:days?|d))?\s*(?:(?<hours>-?(?:\d+)?\.?\d+) *(?:hours?|hrs?|h))?\s*(?:(?<minutes>-?(?:\d+)?\\.?\\d+) *(?:minutes?|mins?))?\s*(?:(?<seconds>-?(?:\d+)?\\.?\d+) *(?:seconds?|secs?|s))?\s*(?:(?<milliseconds>-?(?:\d+)?\.?\d+) *(?:milliseconds?|msecs?|ms))?$
+ // const regexString = Object.entries(BushConstants.TimeUnits)
+ // .map(([name, { label }]) => String.raw`(?: (?<${name}>-?(?:\d+)?\.?\d+) *${label})`)
+ // .join(' |');
+ // const match = new RegExp(`^${regexString}$`, 'img').exec(' ' + content + ' ');
+ // if (!match) return null;
+ // console.
+ // const contentWithoutTime = content.replace(new RegExp(`^${regexString}$`, 'img'), '');
+ // for (const key in match.groups) {
+ // const value = Number(match.groups[key] || 0);
+ // duration += value * BushConstants.TimeUnits[key].value;
+ // }
return { duration, contentWithoutTime };
}
@@ -555,11 +569,20 @@ export class BushClientUtil extends ClientUtil {
* Checks if a moderator can perform a moderation action on another user.
* @param moderator - The person trying to perform the action.
* @param victim - The person getting punished.
+ * @param checkModerator - Whether or not to check if the victim is a moderator.
*/
- public moderatorCanModerateUser(moderator: BushGuildMember, victim: BushGuildMember): boolean {
- throw 'not implemented';
+ public moderationPermissionCheck(
+ moderator: BushGuildMember,
+ victim: BushGuildMember,
+ checkModerator = true
+ ): true | 'user hierarchy' | 'client hierarchy' | 'moderator' | 'self' {
if (moderator.guild.id !== victim.guild.id) throw 'wtf';
- if (moderator.guild.ownerID === moderator.id) return true;
+ const isOwner = moderator.guild.ownerID === moderator.id;
+ if (moderator.id === victim.id) return 'self';
+ if (moderator.roles.highest.position <= victim.roles.highest.position && !isOwner) return 'user hierarchy';
+ if (victim.roles.highest.position >= victim.guild.me.roles.highest.position) return 'client hierarchy';
+ if (checkModerator && victim.permissions.has('MANAGE_MESSAGES')) return 'moderator';
+ return true;
}
public async createModLogEntry(options: {
@@ -569,10 +592,11 @@ export class BushClientUtil extends ClientUtil {
reason: string;
duration: number;
guild: BushGuildResolvable;
- }): Promise<void> {
+ }): Promise<ModLog> {
const user = this.client.users.resolveID(options.user);
const moderator = this.client.users.resolveID(options.moderator);
const guild = this.client.guilds.resolveID(options.guild);
+ const duration = options.duration || null;
// If guild does not exist create it so the modlog can reference a guild.
await Guild.findOrCreate({
@@ -589,9 +613,49 @@ export class BushClientUtil extends ClientUtil {
user,
moderator,
reason: options.reason,
- duration: options.duration,
+ duration: duration,
guild
});
- await modLogEntry.save();
+ return modLogEntry.save().catch((err) => {
+ this.client.console.error('createModLogEntry', err);
+ return null;
+ });
+ }
+
+ public async createPunishmentEntry(options: {
+ type: 'mute' | 'ban' | 'role';
+ user: BushGuildMemberResolvable;
+ duration: number;
+ guild: BushGuildResolvable;
+ modlog: string;
+ }): Promise<Mute | Ban | PunishmentRole> {
+ let dbModel: typeof Mute | typeof Ban | typeof PunishmentRole;
+ switch (options.type) {
+ case 'mute':
+ dbModel = Mute;
+ break;
+ case 'ban':
+ dbModel = Ban;
+ break;
+ case 'role':
+ dbModel = PunishmentRole;
+ break;
+ default:
+ throw 'choose a valid punishment entry type';
+ }
+
+ const expires = options.duration ? new Date(new Date().getTime() + options.duration) : null;
+ const user = this.client.users.resolveID(options.user);
+ const guild = this.client.guilds.resolveID(options.guild);
+
+ const entry = dbModel.build({ user, guild, expires, modlog: options.modlog });
+ return await entry.save().catch((err) => {
+ this.client.console.error('createPunishmentEntry', err);
+ return null;
+ });
+ }
+
+ public humanizeDuration(duration: number): string {
+ return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2 });
}
}
diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts
index 59dc777..2fefcdd 100644
--- a/src/lib/extensions/discord.js/BushGuildMember.ts
+++ b/src/lib/extensions/discord.js/BushGuildMember.ts
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { GuildMember } from 'discord.js';
+import { ModLogType } from '../../models';
import { BushClient, BushUserResolvable } from '../discord-akairo/BushClient';
import { BushGuild } from './BushGuild';
import { BushUser } from './BushUser';
@@ -7,7 +8,6 @@ import { BushUser } from './BushUser';
interface BushPunishmentOptions {
reason?: string;
moderator: BushUserResolvable;
- createModLogEntry?: boolean;
}
interface BushTimedPunishmentOptions extends BushPunishmentOptions {
@@ -18,7 +18,16 @@ type PunishmentResponse = 'success';
type WarnResponse = PunishmentResponse;
-type MuteResponse = PunishmentResponse | 'no mute role';
+type MuteResponse =
+ | PunishmentResponse
+ | 'missing permissions'
+ | 'no mute role'
+ | 'invalid mute role'
+ | 'mute role not manageable'
+ | 'error giving mute role'
+ | 'error creating modlog entry'
+ | 'error creating mute entry'
+ | 'failed to dm';
type UnmuteResponse = PunishmentResponse;
@@ -44,7 +53,54 @@ export class BushGuildMember extends GuildMember {
}
public async mute(options: BushTimedPunishmentOptions): Promise<MuteResponse> {
- throw 'not implemented';
+ //checks
+ if (!this.guild.me.permissions.has('MANAGE_ROLES')) return 'missing permissions';
+ const muteRoleID = await this.guild.getSetting('muteRole');
+ if (!muteRoleID) return 'no mute role';
+ const muteRole = this.guild.roles.cache.get(muteRoleID);
+ if (!muteRole) return 'invalid mute role';
+ if (muteRole.position >= this.guild.me.roles.highest.position || muteRole.managed) return 'mute role not manageable';
+
+ //add role
+ const success = await this.roles.add(muteRole).catch(() => null);
+ if (!success) return 'error giving mute role';
+
+ //add modlog entry
+ const modlog = await this.client.util
+ .createModLogEntry({
+ type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE,
+ user: this,
+ moderator: options.moderator,
+ reason: options.reason,
+ duration: options.duration,
+ guild: this.guild
+ })
+ .catch(() => null);
+ if (!modlog) return 'error creating modlog entry';
+
+ // add punishment entry so they can be unmuted later
+ const mute = await this.client.util
+ .createPunishmentEntry({
+ type: 'mute',
+ user: this,
+ guild: this.guild,
+ duration: options.duration,
+ modlog: modlog.id
+ })
+ .catch(() => null);
+ if (!mute) return 'error creating mute entry';
+
+ //dm user
+ const ending = this.guild.getSetting('punishmentEnding');
+ const dmSuccess = await this.send({
+ content: `You have been muted ${
+ options.duration ? 'for ' + this.client.util.humanizeDuration(options.duration) : 'permanently'
+ } in **${this.guild}** for **${options.reason || 'No reason provided'}**.${ending ? `\n\n${ending}` : ''}`
+ }).catch(() => null);
+
+ if (!dmSuccess) return 'failed to dm';
+
+ return 'success';
}
public async unmute(options: BushPunishmentOptions): Promise<UnmuteResponse> {
diff --git a/src/lib/models/Ban.ts b/src/lib/models/Ban.ts
index f4463b8..54ca6ae 100644
--- a/src/lib/models/Ban.ts
+++ b/src/lib/models/Ban.ts
@@ -7,7 +7,6 @@ export interface BanModel {
id: string;
user: string;
guild: string;
- reason: string;
expires: Date;
modlog: string;
}
@@ -15,7 +14,6 @@ export interface BanModelCreationAttributes {
id?: string;
user: string;
guild: string;
- reason?: string;
expires?: Date;
modlog: string;
}
@@ -34,10 +32,6 @@ export class Ban extends BaseModel<BanModel, BanModelCreationAttributes> impleme
*/
guild: Snowflake;
/**
- * The reason they are banned (optional)
- */
- reason: string | null;
- /**
* The date at which this ban expires and should be unbanned (optional)
*/
expires: Date | null;
@@ -71,10 +65,6 @@ export class Ban extends BaseModel<BanModel, BanModelCreationAttributes> impleme
type: DataTypes.DATE,
allowNull: true
},
- reason: {
- type: DataTypes.STRING,
- allowNull: true
- },
modlog: {
type: DataTypes.STRING,
allowNull: false,
diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts
index 303335b..0fc3413 100644
--- a/src/lib/models/Guild.ts
+++ b/src/lib/models/Guild.ts
@@ -10,11 +10,12 @@ export interface GuildModel {
blacklistedChannels: Snowflake[];
welcomeChannel: Snowflake;
muteRole: Snowflake;
+ punishmentEnding: string;
}
export type GuildModelCreationAttributes = Optional<
GuildModel,
- 'prefix' | 'autoPublishChannels' | 'blacklistedChannels' | 'welcomeChannel' | 'muteRole'
+ 'prefix' | 'autoPublishChannels' | 'blacklistedChannels' | 'welcomeChannel' | 'muteRole' | 'punishmentEnding'
>;
export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> implements GuildModel {
@@ -24,6 +25,7 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
blacklistedChannels: Snowflake[];
welcomeChannel: Snowflake;
muteRole: Snowflake;
+ punishmentEnding: string;
static initModel(sequelize: Sequelize, client: BushClient): void {
Guild.init(
@@ -64,6 +66,10 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
muteRole: {
type: DataTypes.STRING,
allowNull: true
+ },
+ punishmentEnding: {
+ type: DataTypes.TEXT,
+ allowNull: true
}
},
{ sequelize: sequelize }
diff --git a/src/lib/models/ModLog.ts b/src/lib/models/ModLog.ts
index 1d850d9..6261794 100644
--- a/src/lib/models/ModLog.ts
+++ b/src/lib/models/ModLog.ts
@@ -4,14 +4,17 @@ import { v4 as uuidv4 } from 'uuid';
import { BaseModel } from './BaseModel';
export enum ModLogType {
- BAN = 'BAN',
+ PERM_BAN = 'PERM_BAN',
TEMP_BAN = 'TEMP_BAN',
+ UNBAN = 'UNBAN',
KICK = 'KICK',
- MUTE = 'MUTE',
+ PERM_MUTE = 'PERM_MUTE',
TEMP_MUTE = 'TEMP_MUTE',
+ UNMUTE = 'UNMUTE',
WARN = 'WARN',
- PUNISHMENT_ROLE = 'PUNISHMENT_ROLE',
- TEMP_PUNISHMENT_ROLE = 'TEMP_PUNISHMENT_ROLE'
+ PERM_PUNISHMENT_ROLE = 'PERM_PUNISHMENT_ROLE',
+ TEMP_PUNISHMENT_ROLE = 'TEMP_PUNISHMENT_ROLE',
+ REMOVE_PUNISHMENT_ROLE = 'REMOVE_PUNISHMENT_ROLE'
}
export interface ModLogModel {
diff --git a/src/lib/models/Mute.ts b/src/lib/models/Mute.ts
index 273d5b1..71a32e3 100644
--- a/src/lib/models/Mute.ts
+++ b/src/lib/models/Mute.ts
@@ -7,7 +7,6 @@ export interface MuteModel {
id: string;
user: string;
guild: string;
- reason: string;
expires: Date;
modlog: string;
}
@@ -15,7 +14,6 @@ export interface MuteModelCreationAttributes {
id?: string;
user: string;
guild: string;
- reason?: string;
expires?: Date;
modlog: string;
}
@@ -34,10 +32,6 @@ export class Mute extends BaseModel<MuteModel, MuteModelCreationAttributes> impl
*/
guild: Snowflake;
/**
- * The reason they are muted (optional)
- */
- reason: string | null;
- /**
* The date at which this Mute expires and should be unmuted (optional)
*/
expires: Date | null;
@@ -71,10 +65,6 @@ export class Mute extends BaseModel<MuteModel, MuteModelCreationAttributes> impl
type: DataTypes.DATE,
allowNull: true
},
- reason: {
- type: DataTypes.STRING,
- allowNull: true
- },
modlog: {
type: DataTypes.STRING,
allowNull: false,
diff --git a/src/lib/models/PunishmentRole.ts b/src/lib/models/PunishmentRole.ts
index 3326dca..927cf28 100644
--- a/src/lib/models/PunishmentRole.ts
+++ b/src/lib/models/PunishmentRole.ts
@@ -7,7 +7,6 @@ export interface PunishmentRoleModel {
id: string;
user: string;
guild: string;
- reason: string;
expires: Date;
modlog: string;
}
@@ -15,7 +14,6 @@ export interface PunishmentRoleModelCreationAttributes {
id?: string;
user: string;
guild: string;
- reason?: string;
expires?: Date;
modlog: string;
}
@@ -37,10 +35,6 @@ export class PunishmentRole
*/
guild: Snowflake;
/**
- * The reason they received a role (optional)
- */
- reason: string | null;
- /**
* The date at which this role expires and should be removed (optional)
*/
expires: Date | null;
@@ -74,10 +68,6 @@ export class PunishmentRole
type: DataTypes.DATE,
allowNull: true
},
- reason: {
- type: DataTypes.STRING,
- allowNull: true
- },
modlog: {
type: DataTypes.STRING,
allowNull: false,
diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts
index 0e3f6bb..7e6013d 100644
--- a/src/lib/utils/BushConstants.ts
+++ b/src/lib/utils/BushConstants.ts
@@ -1,36 +1,36 @@
export class BushConstants {
- // Stolen from @Mzato0001 (pr to discord akairo that hasn't been merged yet)
- public static TimeUnits = {
+ // Somewhat stolen from @Mzato0001
+ public static TimeUnits: { [key: string]: { match: RegExp; value: number } } = {
years: {
- label: '(?:years?|y)',
- value: 1000 * 60 * 60 * 24 * 365
+ match: / (?:(?<years>-?(?:\d+)?\.?\d+) *(?:years?|y))/im,
+ value: 1000 * 60 * 60 * 24 * 365.25 //leap years
},
months: {
- label: '(?:months?|mon|mo?)',
- value: 1000 * 60 * 60 * 24 * 30
+ match: / (?:(?<months>-?(?:\d+)?\.?\d+) *(?:months?|mon|mo?))/im,
+ value: 1000 * 60 * 60 * 24 * 30.4375 // average of days in months including leap years
},
weeks: {
- label: '(?:weeks?|w)',
+ match: / (?:(?<weeks>-?(?:\d+)?\.?\d+) *(?:weeks?|w))/im,
value: 1000 * 60 * 60 * 24 * 7
},
days: {
- label: '(?:days?|d)',
+ match: / (?:(?<days>-?(?:\d+)?\.?\d+) *(?:days?|d))/im,
value: 1000 * 60 * 60 * 24
},
hours: {
- label: '(?:hours?|hrs?|h)',
+ match: / (?:(?<hours>-?(?:\d+)?\.?\d+) *(?:hours?|hrs?|h))/im,
value: 1000 * 60 * 60
},
minutes: {
- label: '(?:minutes?|mins?)',
+ match: / (?:(?<minutes>-?(?:\d+)?\.?\d+) *(?:minutes?|mins?))/im,
value: 1000 * 60
},
seconds: {
- label: '(?:seconds?|secs?|s)',
+ match: / (?:(?<seconds>-?(?:\d+)?\.?\d+) *(?:seconds?|secs?|s))/im,
value: 1000
},
milliseconds: {
- label: '(?:milliseconds?|msecs?|ms)',
+ match: / (?:(?<milliseconds>-?(?:\d+)?\.?\d+) *(?:milliseconds?|msecs?|ms))/im,
value: 1
}
};
diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts
index 3cb5046..20132dd 100644
--- a/src/listeners/commands/commandError.ts
+++ b/src/listeners/commands/commandError.ts
@@ -22,7 +22,7 @@ export default class CommandErrorListener extends BushListener {
**Channel:** ${message.channel} (${message.channel?.id})
**Message:** [link](${message.url})`
)
- .addField('Error', await this.client.util.codeblock(`${error?.stack}`, 1024, 'js'))
+ .addField('Error', await this.client.util.codeblock(`${typeof error === 'object' ? error?.stack : error}`, 1024, 'js'))
.setColor(this.client.util.colors.error)
.setTimestamp();
@@ -41,7 +41,10 @@ export default class CommandErrorListener extends BushListener {
);
await message.util.send({ embeds: [errorUserEmbed] }).catch((e) => {
const channel = message.channel.type === 'dm' ? message.channel.recipient.tag : message.channel.name;
- this.client.console.warn('CommandError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack);
+ this.client.console.warn(
+ 'CommandError',
+ `Failed to send user error embed in <<${channel}>>:\n` + typeof e === 'object' ? e?.stack : e
+ );
});
} else {
const errorDevEmbed = new MessageEmbed()
@@ -51,7 +54,10 @@ export default class CommandErrorListener extends BushListener {
.setDescription(await this.client.util.codeblock(`${error?.stack}`, 2048, 'js'));
await message.util.send({ embeds: [errorDevEmbed] }).catch((e) => {
const channel = message.channel.type === 'dm' ? message.channel.recipient.tag : message.channel.name;
- this.client.console.warn('CommandError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack);
+ this.client.console.warn(
+ 'CommandError',
+ `Failed to send owner error stack in <<${channel}>>.` + typeof e === 'object' ? e?.stack : e
+ );
});
}
}
@@ -59,7 +65,10 @@ export default class CommandErrorListener extends BushListener {
this.client.console.error(
'CommandError',
`an error occurred with the <<${command}>> command in <<${channel}>> triggered by <<${message?.author?.tag}>>:\n` +
- error?.stack,
+ typeof error ===
+ 'object'
+ ? error?.stack
+ : error,
false
);
}
diff --git a/src/listeners/commands/commandMissingPermissions.ts b/src/listeners/commands/commandMissingPermissions.ts
index e3420ae..bef8e9c 100644
--- a/src/listeners/commands/commandMissingPermissions.ts
+++ b/src/listeners/commands/commandMissingPermissions.ts
@@ -1,3 +1,4 @@
+import { PermissionString } from 'discord.js';
import { BushCommand } from '../../lib/extensions/discord-akairo/BushCommand';
import { BushListener } from '../../lib/extensions/discord-akairo/BushListener';
import { BushMessage } from '../../lib/extensions/discord.js/BushMessage';
@@ -15,8 +16,17 @@ export default class CommandMissingPermissionsListener extends BushListener {
message: BushMessage,
command: BushCommand | null | undefined,
type: 'client' | 'user',
- missing: Array<string>
+ missing: Array<PermissionString>
): Promise<void> {
+ this.client.console.debug(message.guild.me.permissions.toArray());
+ missing.forEach((permission) => {
+ this.client.console.debug(message.guild.me.permissions.has(permission));
+ });
+ message.guild.me.permissions;
+ this.client.console.debug(type);
+ this.client.console.debug(command.clientPermissions);
+ this.client.console.debug(command.userPermissions);
+ this.client.console.debug(missing);
const niceMissing = [];
missing.forEach((missing) => {
if (this.client.consts.mappings.permissions[missing]) {
diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts
index 767bd3d..b172670 100644
--- a/src/listeners/commands/slashCommandError.ts
+++ b/src/listeners/commands/slashCommandError.ts
@@ -21,7 +21,7 @@ export default class SlashCommandErrorListener extends BushListener {
**Channel:** ${message.channel || message.interaction.user?.tag} ${message.channel ? `(${message.channel?.id})` : ''}
**Message:** [link](https://discord.com/${message.guild?.id}/${message.channel?.id}/${message.id})`
)
- .addField('Error', await this.client.util.codeblock(`${error?.stack}`, 1024, 'js'))
+ .addField('Error', await this.client.util.codeblock(`${typeof error === 'object' ? error?.stack : error}`, 1024, 'js'))
.setColor(this.client.util.colors.error)
.setTimestamp();
@@ -40,7 +40,10 @@ export default class SlashCommandErrorListener extends BushListener {
`Oh no! While running the command \`${command.id}\`, an error occurred. Please give the developers code \`${errorNo}\`.`
);
await message.util.send({ embeds: [errorUserEmbed] }).catch((e) => {
- this.client.console.warn('SlashError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack);
+ this.client.console.warn(
+ 'SlashError',
+ `Failed to send user error embed in <<${channel}>>:\n` + typeof e === 'object' ? e?.stack : e
+ );
});
} else {
const errorDevEmbed = new MessageEmbed()
@@ -49,7 +52,10 @@ export default class SlashCommandErrorListener extends BushListener {
.setTimestamp()
.setDescription(await this.client.util.codeblock(`${error?.stack}`, 2048, 'js'));
await message.util.send({ embeds: [errorDevEmbed] }).catch((e) => {
- this.client.console.warn('SlashError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack);
+ this.client.console.warn(
+ 'SlashError',
+ `Failed to send owner error stack in <<${channel}>>.` + typeof e === 'object' ? e?.stack : e
+ );
});
}
}
@@ -57,7 +63,10 @@ export default class SlashCommandErrorListener extends BushListener {
this.client.console.error(
'SlashError',
`an error occurred with the <<${command}>> command in <<${channel}>> triggered by <<${message?.author?.tag}>>:\n` +
- error?.stack,
+ typeof error ===
+ 'object'
+ ? error?.stack
+ : error,
false
);
}
diff --git a/yarn.lock b/yarn.lock
index bac6a5d..8719dc6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -32,13 +32,13 @@ __metadata:
languageName: node
linkType: hard
-"@discordjs/builders@npm:^0.1.1":
- version: 0.1.1
- resolution: "@discordjs/builders@npm:0.1.1"
+"@discordjs/builders@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "@discordjs/builders@npm:0.2.0"
dependencies:
discord-api-types: ^0.18.1
tslib: ^2.3.0
- checksum: 2902d53542467c9fe3353b412c12b273a9ecba10ad3f86029613ef3822eba68a83b491fac644bdc37e254520b6a1fcaa91ae58c3ad859d0a768e47fbd2fe3e06
+ checksum: d273d10d711ba0d45e0fff35d7e722231ce008bdbef04fe993488d551aea222d8e5754e4727e71e3e6cbe7d39900a47320d859c4db5c49c1ea3f780b4ab5d64b
languageName: node
linkType: hard
@@ -90,6 +90,24 @@ __metadata:
languageName: node
linkType: hard
+"@humanwhocodes/config-array@npm:^0.5.0":
+ version: 0.5.0
+ resolution: "@humanwhocodes/config-array@npm:0.5.0"
+ dependencies:
+ "@humanwhocodes/object-schema": ^1.2.0
+ debug: ^4.1.1
+ minimatch: ^3.0.4
+ checksum: 71e3c1fef40166ecaacbe29b681499dc6bab3fe45df5bfb3e137baf6e50f22813cf14f24ff759a4da43b6743d7f5a776298ae1e0e266c9602bab62da2ee3b302
+ languageName: node
+ linkType: hard
+
+"@humanwhocodes/object-schema@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "@humanwhocodes/object-schema@npm:1.2.0"
+ checksum: ef533ee0d227b8036e4220013575fedc3d0346e2e40bc5f5536ba5761825f23577eb4b71e52f18a2d3b827c9d83cfa60c821a71e30d5f6537918a94bc1990963
+ languageName: node
+ linkType: hard
+
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -208,6 +226,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/humanize-duration@npm:^3":
+ version: 3.25.0
+ resolution: "@types/humanize-duration@npm:3.25.0"
+ checksum: cd280658130bb9f5a8f6880b2161389f0057601dcc0a804b1b5cd700e43580727a349e380909fdaa3b29f4de1d42f56c05b4655db9999ace41c661904bd171d7
+ languageName: node
+ linkType: hard
+
"@types/json-schema@npm:^7.0.7":
version: 7.0.7
resolution: "@types/json-schema@npm:7.0.7"
@@ -232,9 +257,9 @@ __metadata:
linkType: hard
"@types/node@npm:*":
- version: 15.14.0
- resolution: "@types/node@npm:15.14.0"
- checksum: b36835ddec52e4aec18dd042b7e960790641e895c3d32d6ed7363bfa33b012b8a41add6a82c5b9f4b83a10f9654b6eb60f5a19f6b648c3a5f4a59e20a36301b6
+ version: 16.0.0
+ resolution: "@types/node@npm:16.0.0"
+ checksum: 0302e376d5272170478468f023a34dd8b4da998bee02bddfed9b0f1c80add77d02fe9c737fad8943ab2dc981601b71c5714b9f971560d5519c1c814cf2c8bc50
languageName: node
linkType: hard
@@ -279,18 +304,18 @@ __metadata:
linkType: hard
"@types/uuid@npm:^8.3.0":
- version: 8.3.0
- resolution: "@types/uuid@npm:8.3.0"
- checksum: 620985aed8fdb4aed153daca5235f4303457a67a4d181efac0021712eabd9edd7e88895f79730c0e651effde7633b1b61e49a81519ac8d4e75019d2ac9b6412f
+ version: 8.3.1
+ resolution: "@types/uuid@npm:8.3.1"
+ checksum: 5864be83031f5836be985187ecebb2769936bf223bad896dac381f1a0fdd2ae239f448263196f752d62a4ecc8563817d6d94e329eb2099a28e743ab206e7bfea
languageName: node
linkType: hard
"@types/ws@npm:^7.4.4, @types/ws@npm:^7.4.5":
- version: 7.4.5
- resolution: "@types/ws@npm:7.4.5"
+ version: 7.4.6
+ resolution: "@types/ws@npm:7.4.6"
dependencies:
"@types/node": "*"
- checksum: b467f596cc9992dde38ea9bb824135f20d0eae92515304e3de25e4b98a3b41e71897801900563c1e72f36fa7a6277c873896b842bd6d1b9d4b9457834533903b
+ checksum: bf81b170ab2caeb5f602e1094058d31b2e19fbb725d25007fbca01c1043c10c3fdd0ee67dd29cadab3bdfed52cf930d9d0ec06bb51b811e67176959476cbd043
languageName: node
linkType: hard
@@ -578,6 +603,7 @@ __metadata:
"@discordjs/voice": ^0.4.0
"@types/common-tags": ^1.8.0
"@types/express": ^4.17.12
+ "@types/humanize-duration": ^3
"@types/node": ^14.14.22
"@types/uuid": ^8.3.0
"@typescript-eslint/eslint-plugin": ^4.14.1
@@ -592,6 +618,7 @@ __metadata:
eslint: ^7.29.0
eslint-config-prettier: ^8.3.0
got: ^11.8.2
+ humanize-duration: ^3.27.0
moment: ^2.29.1
pg: ^8.5.1
pg-hstore: ^2.3.3
@@ -601,6 +628,11 @@ __metadata:
source-map-support: ^0.5.19
typescript: 4.2.4
uuid: ^8.3.2
+ dependenciesMeta:
+ discord-akairo@8.2.2:
+ unplugged: true
+ discord.js@13.0.0-dev.7e30011.1625338909:
+ unplugged: true
languageName: unknown
linkType: soft
@@ -832,10 +864,10 @@ discord-akairo@NotEnoughUpdates/discord-akairo:
linkType: hard
"discord.js@npm:dev":
- version: 13.0.0-dev.fe6cc0c.1625184228
- resolution: "discord.js@npm:13.0.0-dev.fe6cc0c.1625184228"
+ version: 13.0.0-dev.7e30011.1625338909
+ resolution: "discord.js@npm:13.0.0-dev.7e30011.1625338909"
dependencies:
- "@discordjs/builders": ^0.1.1
+ "@discordjs/builders": ^0.2.0
"@discordjs/collection": ^0.1.6
"@discordjs/form-data": ^3.0.1
"@sapphire/async-queue": ^1.1.4
@@ -844,7 +876,7 @@ discord-akairo@NotEnoughUpdates/discord-akairo:
discord-api-types: ^0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e
node-fetch: ^2.6.1
ws: ^7.5.1
- checksum: 0b2a39e70883f5bba769879063d3973ace05853192b217b253b0bdcf8dfbd7e27f0e4eca2b7838a420c71db9184038e486d7ceba328c5f52a5b7eed6f7777b9d
+ checksum: 8bd01ed23858ea5a97229f1323c9f64e56616fae666141d3d0a197a8c6f93af9fb550648a0ce96700068cd683431f0e326582983d8e029c71b194f4de70f4b45
languageName: node
linkType: hard
@@ -975,11 +1007,12 @@ discord-akairo@NotEnoughUpdates/discord-akairo:
linkType: hard
"eslint@npm:^7.29.0":
- version: 7.29.0
- resolution: "eslint@npm:7.29.0"
+ version: 7.30.0
+ resolution: "eslint@npm:7.30.0"
dependencies:
"@babel/code-frame": 7.12.11
"@eslint/eslintrc": ^0.4.2
+ "@humanwhocodes/config-array": ^0.5.0
ajv: ^6.10.0
chalk: ^4.0.0
cross-spawn: ^7.0.2
@@ -1019,7 +1052,7 @@ discord-akairo@NotEnoughUpdates/discord-akairo:
v8-compile-cache: ^2.0.3
bin:
eslint: bin/eslint.js
- checksum: 812f8c5123860cf5bd877018ffd29abd52bbaaca55fdd616008c97da9bf47a20a7b7c7ecb7c8f753c06f77ea5d59480f3d6d76475699b2ea380237fbb7c6b3a2
+ checksum: 028048847e0252d6c972ac612fe37be55822ae2255d10d1bf68255749f7ae1364b2aa538cd95f3bc4d951fc7d36195e46b62fe8d215297ef9a6a8aeb8ca28a90
languageName: node
linkType: hard
@@ -1300,6 +1333,13 @@ discord-akairo@NotEnoughUpdates/discord-akairo:
languageName: node
linkType: hard
+"humanize-duration@npm:^3.27.0":
+ version: 3.27.0
+ resolution: "humanize-duration@npm:3.27.0"
+ checksum: 2cda40de2b350a82496ce4b0afd35f819e49f2e2c95a57db786358bb84f90ea8715c257717ab59592fc46fbdab60a77819fb300e93ba24f8b66392264bb6bdab
+ languageName: node
+ linkType: hard
+
"iconv-lite@npm:0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"