diff options
author | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-07-04 15:25:32 -0400 |
---|---|---|
committer | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-07-04 15:25:32 -0400 |
commit | cf564dbb6435886f97e2e9870363144386af368d (patch) | |
tree | d535cd73f24a145ca9d7ce1a0e1174cba0d38b31 | |
parent | 34f0d1d3ff3e2a90193c9a4d4de29d8335160d6a (diff) | |
download | tanzanite-cf564dbb6435886f97e2e9870363144386af368d.tar.gz tanzanite-cf564dbb6435886f97e2e9870363144386af368d.tar.bz2 tanzanite-cf564dbb6435886f97e2e9870363144386af368d.zip |
mute command
-rwxr-xr-x | .pnp.js | 128 | ||||
-rw-r--r-- | package.json | 10 | ||||
-rw-r--r-- | src/commands/dev/eval.ts | 2 | ||||
-rw-r--r-- | src/commands/dev/reload.ts | 6 | ||||
-rw-r--r-- | src/commands/dev/testDuration.ts | 54 | ||||
-rw-r--r-- | src/commands/moderation/kick.ts | 2 | ||||
-rw-r--r-- | src/commands/moderation/mute.ts | 171 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClient.ts | 14 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClientUtil.ts | 106 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuildMember.ts | 62 | ||||
-rw-r--r-- | src/lib/models/Ban.ts | 10 | ||||
-rw-r--r-- | src/lib/models/Guild.ts | 8 | ||||
-rw-r--r-- | src/lib/models/ModLog.ts | 11 | ||||
-rw-r--r-- | src/lib/models/Mute.ts | 10 | ||||
-rw-r--r-- | src/lib/models/PunishmentRole.ts | 10 | ||||
-rw-r--r-- | src/lib/utils/BushConstants.ts | 24 | ||||
-rw-r--r-- | src/listeners/commands/commandError.ts | 17 | ||||
-rw-r--r-- | src/listeners/commands/commandMissingPermissions.ts | 12 | ||||
-rw-r--r-- | src/listeners/commands/slashCommandError.ts | 17 | ||||
-rw-r--r-- | yarn.lock | 80 |
20 files changed, 494 insertions, 260 deletions
@@ -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 ); } @@ -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" |