diff --git a/backend/package-lock.json b/backend/package-lock.json index 3f8c6f55..dae025ac 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,8 +13,7 @@ "cors": "^2.8.5", "cross-env": "^5.2.0", "deep-diff": "^1.0.2", - "discord-api-types": "^0.33.1", - "discord.js": "^13.14.0", + "discord.js": "^14.8.0", "dotenv": "^4.0.0", "emoji-regex": "^8.0.0", "erlpack": "github:discord/erlpack", @@ -24,7 +23,7 @@ "humanize-duration": "^3.15.0", "io-ts": "^2.0.0", "js-yaml": "^3.13.1", - "knub": "^30.0.0-beta.46", + "knub": "^32.0.0-next.4", "knub-command-manager": "^9.1.0", "last-commit-log": "^2.1.0", "lodash.chunk": "^4.2.0", @@ -53,6 +52,7 @@ "tsconfig-paths": "^3.9.0", "twemoji": "^12.1.4", "typeorm": "^0.2.31", + "typescript": "~4.9.5", "utf-8-validate": "^5.0.5", "uuid": "^3.3.2", "yawn-yaml": "github:dragory/yawn-yaml#string-number-fix-build", @@ -124,35 +124,73 @@ } }, "node_modules/@discordjs/builders": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.16.0.tgz", - "integrity": "sha512-9/NCiZrLivgRub2/kBc0Vm5pMBE5AUdYbdXsLu/yg9ANgvnaJ0bZKTY8yYnLbsEc/LYUP79lEIdC73qEYhWq7A==", - "deprecated": "no longer supported", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.5.0.tgz", + "integrity": "sha512-7XxT78mnNBPigHn2y6KAXkicxIBFtZREGWaRZ249EC1l6gBUEP8IyVY5JTciIjJArxkF+tg675aZvsTNTKBpmA==", "dependencies": { - "@sapphire/shapeshift": "^3.5.1", - "discord-api-types": "^0.36.2", + "@discordjs/formatters": "^0.2.0", + "@discordjs/util": "^0.2.0", + "@sapphire/shapeshift": "^3.8.1", + "discord-api-types": "^0.37.35", "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.1", - "tslib": "^2.4.0" + "ts-mixer": "^6.0.3", + "tslib": "^2.5.0" }, "engines": { "node": ">=16.9.0" } }, - "node_modules/@discordjs/builders/node_modules/discord-api-types": { - "version": "0.36.3", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", - "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" - }, "node_modules/@discordjs/builders/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@discordjs/collection": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", - "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.4.0.tgz", + "integrity": "sha512-hiOJyk2CPFf1+FL3a4VKCuu1f448LlROVuu8nLz1+jCOAPokUcdFAV+l4pd3B3h6uJlJQSASoZzrdyNdjdtfzQ==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.2.0.tgz", + "integrity": "sha512-vn4oMSXuMZUm8ITqVOtvE7/fMMISj4cI5oLsR09PEQXHKeKDAMLltG/DWeeIs7Idfy6V8Fk3rn1e69h7NfzuNA==", + "dependencies": { + "discord-api-types": "^0.37.35" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.6.0.tgz", + "integrity": "sha512-HGvqNCZ5Z5j0tQHjmT1lFvE5ETO4hvomJ1r0cbnpC1zM23XhCpZ9wgTCiEmaxKz05cyf2CI9p39+9LL+6Yz1bA==", + "dependencies": { + "@discordjs/collection": "^1.4.0", + "@discordjs/util": "^0.2.0", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.4.0", + "discord-api-types": "^0.37.35", + "file-type": "^18.2.1", + "tslib": "^2.5.0", + "undici": "^5.20.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@discordjs/util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz", + "integrity": "sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg==", "engines": { "node": ">=16.9.0" } @@ -211,6 +249,15 @@ "npm": ">=7.0.0" } }, + "node_modules/@sapphire/snowflake": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.1", "license": "Apache-2.0" @@ -238,6 +285,11 @@ "node": ">=6" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@types/body-parser": { "version": "1.17.1", "dev": true, @@ -348,9 +400,10 @@ "license": "MIT" }, "node_modules/@types/node-fetch": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz", - "integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, "dependencies": { "@types/node": "*", "form-data": "^3.0.0" @@ -426,8 +479,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.3", - "license": "MIT", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", "dependencies": { "@types/node": "*" } @@ -580,6 +634,7 @@ }, "node_modules/asynckit": { "version": "0.4.0", + "dev": true, "license": "MIT" }, "node_modules/ava": { @@ -1351,6 +1406,7 @@ }, "node_modules/combined-stream": { "version": "1.0.8", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -1642,6 +1698,7 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -1698,46 +1755,37 @@ } }, "node_modules/discord-api-types": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.4.tgz", - "integrity": "sha512-Y6RMvXsHKiBgQhm/q5MgRieXc4Tzh5p/JuDyqreI48lmy+AQfO+g9Xhz0tuGBaN1FtsrLT7mD+lbFONPo5vdwA==" + "version": "0.37.35", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.35.tgz", + "integrity": "sha512-iyKZ/82k7FX3lcmHiAvvWu5TmyfVo78RtghBV/YsehK6CID83k5SI03DKKopBcln+TiEIYw5MGgq7SJXSpNzMg==" }, "node_modules/discord.js": { - "version": "13.14.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.14.0.tgz", - "integrity": "sha512-EXHAZmFHMf6qBHDsIANwSG792SYJpzEFv2nssfakyDqEn0HLxFLLXMaOxBtVohdkUMgtD+dzyeBlbDvAW/A0AA==", + "version": "14.8.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.8.0.tgz", + "integrity": "sha512-UOxYtc/YnV7jAJ2gISluJyYeBw4e+j8gWn+IoqG8unaHAVuvZ13DdYN0M1f9fbUgUvSarV798inIrYFtDNDjwQ==", "dependencies": { - "@discordjs/builders": "^0.16.0", - "@discordjs/collection": "^0.7.0", - "@sapphire/async-queue": "^1.5.0", - "@types/node-fetch": "^2.6.2", - "@types/ws": "^8.5.3", - "discord-api-types": "^0.33.5", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "ws": "^8.9.0" + "@discordjs/builders": "^1.5.0", + "@discordjs/collection": "^1.4.0", + "@discordjs/formatters": "^0.2.0", + "@discordjs/rest": "^1.6.0", + "@discordjs/util": "^0.2.0", + "@sapphire/snowflake": "^3.4.0", + "@types/ws": "^8.5.4", + "discord-api-types": "^0.37.35", + "fast-deep-equal": "^3.1.3", + "lodash.snakecase": "^4.1.1", + "tslib": "^2.5.0", + "undici": "^5.20.0", + "ws": "^8.12.1" }, "engines": { - "node": ">=16.6.0", - "npm": ">=7.0.0" + "node": ">=16.9.0" } }, - "node_modules/discord.js/node_modules/discord-api-types": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz", - "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==" - }, - "node_modules/discord.js/node_modules/form-data": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } + "node_modules/discord.js/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/dot-prop": { "version": "5.3.0", @@ -2001,6 +2049,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-type": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.1.tgz", + "integrity": "sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==", + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "license": "MIT" @@ -2044,6 +2108,7 @@ }, "node_modules/form-data": { "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -2639,13 +2704,17 @@ } }, "node_modules/knub": { - "version": "30.0.0-beta.46", - "license": "MIT", + "version": "32.0.0-next.4", + "resolved": "https://registry.npmjs.org/knub/-/knub-32.0.0-next.4.tgz", + "integrity": "sha512-ywZbwcGFSr4Erl/nEUDVmziQHXKVIykWtI2Z05DLt01YmxDS+rTO8l/E6LYx7ZL3m+f2DbtLH0HB8zaZb0pUag==", "dependencies": { - "discord-api-types": "^0.22.0", - "discord.js": "^13.0.1", + "discord.js": "^14.8.0", "knub-command-manager": "^9.1.0", - "ts-essentials": "^6.0.7" + "ts-essentials": "^9", + "zod": "^3.19.1" + }, + "engines": { + "node": ">=16" } }, "node_modules/knub-command-manager": { @@ -2662,13 +2731,6 @@ "node": ">=8" } }, - "node_modules/knub/node_modules/discord-api-types": { - "version": "0.22.0", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/last-commit-log": { "version": "2.1.0", "license": "MIT", @@ -2749,6 +2811,11 @@ "version": "4.4.0", "license": "MIT" }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, @@ -3577,6 +3644,18 @@ "through": "~2.3" } }, + "node_modules/peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picomatch": { "version": "2.2.2", "dev": true, @@ -3846,6 +3925,34 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "dev": true, @@ -4344,6 +4451,22 @@ "node": ">=0.10.0" } }, + "node_modules/strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supertap": { "version": "2.0.0", "dev": true, @@ -4494,6 +4617,22 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tr46": { "version": "0.0.3", "license": "MIT" @@ -4508,10 +4647,11 @@ } }, "node_modules/ts-essentials": { - "version": "6.0.7", - "license": "MIT", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.1.tgz", + "integrity": "sha512-9CChSvQMyVRo29Vb1A2jbs+LKo3d/bAf+ndSaX0T8cEiy/HChVaRN/HY5DqUryZ8hZ6uol9bEgCnGmnDbwBR9Q==", "peerDependencies": { - "typescript": ">=3.7.0" + "typescript": ">=4.1.0" } }, "node_modules/ts-mixer": { @@ -4797,9 +4937,9 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "license": "Apache-2.0", - "peer": true, + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4811,6 +4951,36 @@ "node_modules/uid2": { "version": "0.0.3" }, + "node_modules/undici": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", + "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.18" + } + }, + "node_modules/undici/node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/undici/node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/unique-string": { "version": "2.0.0", "dev": true, @@ -5276,8 +5446,9 @@ } }, "node_modules/zod": { - "version": "3.14.4", - "license": "MIT", + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -5318,22 +5489,19 @@ } }, "@discordjs/builders": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.16.0.tgz", - "integrity": "sha512-9/NCiZrLivgRub2/kBc0Vm5pMBE5AUdYbdXsLu/yg9ANgvnaJ0bZKTY8yYnLbsEc/LYUP79lEIdC73qEYhWq7A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.5.0.tgz", + "integrity": "sha512-7XxT78mnNBPigHn2y6KAXkicxIBFtZREGWaRZ249EC1l6gBUEP8IyVY5JTciIjJArxkF+tg675aZvsTNTKBpmA==", "requires": { - "@sapphire/shapeshift": "^3.5.1", - "discord-api-types": "^0.36.2", + "@discordjs/formatters": "^0.2.0", + "@discordjs/util": "^0.2.0", + "@sapphire/shapeshift": "^3.8.1", + "discord-api-types": "^0.37.35", "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.1", - "tslib": "^2.4.0" + "ts-mixer": "^6.0.3", + "tslib": "^2.5.0" }, "dependencies": { - "discord-api-types": { - "version": "0.36.3", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", - "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" - }, "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", @@ -5342,9 +5510,44 @@ } }, "@discordjs/collection": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", - "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.4.0.tgz", + "integrity": "sha512-hiOJyk2CPFf1+FL3a4VKCuu1f448LlROVuu8nLz1+jCOAPokUcdFAV+l4pd3B3h6uJlJQSASoZzrdyNdjdtfzQ==" + }, + "@discordjs/formatters": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.2.0.tgz", + "integrity": "sha512-vn4oMSXuMZUm8ITqVOtvE7/fMMISj4cI5oLsR09PEQXHKeKDAMLltG/DWeeIs7Idfy6V8Fk3rn1e69h7NfzuNA==", + "requires": { + "discord-api-types": "^0.37.35" + } + }, + "@discordjs/rest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.6.0.tgz", + "integrity": "sha512-HGvqNCZ5Z5j0tQHjmT1lFvE5ETO4hvomJ1r0cbnpC1zM23XhCpZ9wgTCiEmaxKz05cyf2CI9p39+9LL+6Yz1bA==", + "requires": { + "@discordjs/collection": "^1.4.0", + "@discordjs/util": "^0.2.0", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.4.0", + "discord-api-types": "^0.37.35", + "file-type": "^18.2.1", + "tslib": "^2.5.0", + "undici": "^5.20.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@discordjs/util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz", + "integrity": "sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg==" }, "@nodelib/fs.scandir": { "version": "2.1.3", @@ -5380,6 +5583,11 @@ "lodash": "^4.17.21" } }, + "@sapphire/snowflake": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==" + }, "@silvia-odwyer/photon-node": { "version": "0.3.1" }, @@ -5397,6 +5605,11 @@ "defer-to-connect": "^1.0.1" } }, + "@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "@types/body-parser": { "version": "1.17.1", "dev": true, @@ -5491,9 +5704,10 @@ "version": "14.0.14" }, "@types/node-fetch": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz", - "integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, "requires": { "@types/node": "*", "form-data": "^3.0.0" @@ -5559,7 +5773,9 @@ "dev": true }, "@types/ws": { - "version": "8.5.3", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", "requires": { "@types/node": "*" } @@ -5651,7 +5867,8 @@ "dev": true }, "asynckit": { - "version": "0.4.0" + "version": "0.4.0", + "dev": true }, "ava": { "version": "3.15.0", @@ -6146,6 +6363,7 @@ }, "combined-stream": { "version": "1.0.8", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -6337,7 +6555,8 @@ } }, "delayed-stream": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "depd": { "version": "1.1.2" @@ -6377,38 +6596,34 @@ } }, "discord-api-types": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.4.tgz", - "integrity": "sha512-Y6RMvXsHKiBgQhm/q5MgRieXc4Tzh5p/JuDyqreI48lmy+AQfO+g9Xhz0tuGBaN1FtsrLT7mD+lbFONPo5vdwA==" + "version": "0.37.35", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.35.tgz", + "integrity": "sha512-iyKZ/82k7FX3lcmHiAvvWu5TmyfVo78RtghBV/YsehK6CID83k5SI03DKKopBcln+TiEIYw5MGgq7SJXSpNzMg==" }, "discord.js": { - "version": "13.14.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.14.0.tgz", - "integrity": "sha512-EXHAZmFHMf6qBHDsIANwSG792SYJpzEFv2nssfakyDqEn0HLxFLLXMaOxBtVohdkUMgtD+dzyeBlbDvAW/A0AA==", + "version": "14.8.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.8.0.tgz", + "integrity": "sha512-UOxYtc/YnV7jAJ2gISluJyYeBw4e+j8gWn+IoqG8unaHAVuvZ13DdYN0M1f9fbUgUvSarV798inIrYFtDNDjwQ==", "requires": { - "@discordjs/builders": "^0.16.0", - "@discordjs/collection": "^0.7.0", - "@sapphire/async-queue": "^1.5.0", - "@types/node-fetch": "^2.6.2", - "@types/ws": "^8.5.3", - "discord-api-types": "^0.33.5", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "ws": "^8.9.0" + "@discordjs/builders": "^1.5.0", + "@discordjs/collection": "^1.4.0", + "@discordjs/formatters": "^0.2.0", + "@discordjs/rest": "^1.6.0", + "@discordjs/util": "^0.2.0", + "@sapphire/snowflake": "^3.4.0", + "@types/ws": "^8.5.4", + "discord-api-types": "^0.37.35", + "fast-deep-equal": "^3.1.3", + "lodash.snakecase": "^4.1.1", + "tslib": "^2.5.0", + "undici": "^5.20.0", + "ws": "^8.12.1" }, "dependencies": { - "discord-api-types": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz", - "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==" - }, - "form-data": { - "version": "4.0.0", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -6588,6 +6803,16 @@ "escape-string-regexp": "^1.0.5" } }, + "file-type": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.1.tgz", + "integrity": "sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==", + "requires": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + } + }, "file-uri-to-path": { "version": "1.0.0" }, @@ -6618,6 +6843,7 @@ }, "form-data": { "version": "3.0.1", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -6975,17 +7201,14 @@ } }, "knub": { - "version": "30.0.0-beta.46", + "version": "32.0.0-next.4", + "resolved": "https://registry.npmjs.org/knub/-/knub-32.0.0-next.4.tgz", + "integrity": "sha512-ywZbwcGFSr4Erl/nEUDVmziQHXKVIykWtI2Z05DLt01YmxDS+rTO8l/E6LYx7ZL3m+f2DbtLH0HB8zaZb0pUag==", "requires": { - "discord-api-types": "^0.22.0", - "discord.js": "^13.0.1", + "discord.js": "^14.8.0", "knub-command-manager": "^9.1.0", - "ts-essentials": "^6.0.7" - }, - "dependencies": { - "discord-api-types": { - "version": "0.22.0" - } + "ts-essentials": "^9", + "zod": "^3.19.1" } }, "knub-command-manager": { @@ -7055,6 +7278,11 @@ "lodash.pick": { "version": "4.4.0" }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "log-symbols": { "version": "4.1.0", "dev": true, @@ -7556,6 +7784,11 @@ "through": "~2.3" } }, + "peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==" + }, "picomatch": { "version": "2.2.2", "dev": true @@ -7726,6 +7959,26 @@ "util-deprecate": "~1.0.1" } }, + "readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "requires": { + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "readdirp": { "version": "3.6.0", "dev": true, @@ -8052,6 +8305,15 @@ "version": "2.0.1", "dev": true }, + "strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "requires": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + } + }, "supertap": { "version": "2.0.0", "dev": true, @@ -8145,6 +8407,15 @@ "toidentifier": { "version": "1.0.0" }, + "token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "requires": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + } + }, "tr46": { "version": "0.0.3" }, @@ -8155,7 +8426,9 @@ "dev": true }, "ts-essentials": { - "version": "6.0.7", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.1.tgz", + "integrity": "sha512-9CChSvQMyVRo29Vb1A2jbs+LKo3d/bAf+ndSaX0T8cEiy/HChVaRN/HY5DqUryZ8hZ6uol9bEgCnGmnDbwBR9Q==", "requires": {} }, "ts-mixer": { @@ -8333,12 +8606,36 @@ } }, "typescript": { - "version": "4.4.4", - "peer": true + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "uid2": { "version": "0.0.3" }, + "undici": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", + "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", + "requires": { + "busboy": "^1.6.0" + }, + "dependencies": { + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + } + } + }, "unique-string": { "version": "2.0.0", "dev": true, @@ -8631,7 +8928,9 @@ } }, "zod": { - "version": "3.14.4" + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==" } } } diff --git a/backend/package.json b/backend/package.json index acc5851f..b307b431 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,8 +28,7 @@ "cors": "^2.8.5", "cross-env": "^5.2.0", "deep-diff": "^1.0.2", - "discord-api-types": "^0.33.1", - "discord.js": "^13.14.0", + "discord.js": "^14.8.0", "dotenv": "^4.0.0", "emoji-regex": "^8.0.0", "erlpack": "github:discord/erlpack", @@ -39,7 +38,7 @@ "humanize-duration": "^3.15.0", "io-ts": "^2.0.0", "js-yaml": "^3.13.1", - "knub": "^30.0.0-beta.46", + "knub": "^32.0.0-next.4", "knub-command-manager": "^9.1.0", "last-commit-log": "^2.1.0", "lodash.chunk": "^4.2.0", @@ -68,6 +67,7 @@ "tsconfig-paths": "^3.9.0", "twemoji": "^12.1.4", "typeorm": "^0.2.31", + "typescript": "~4.9.5", "utf-8-validate": "^5.0.5", "uuid": "^3.3.2", "yawn-yaml": "github:dragory/yawn-yaml#string-number-fix-build", diff --git a/backend/src/api/auth.ts b/backend/src/api/auth.ts index ca947d8c..9bb6e3f1 100644 --- a/backend/src/api/auth.ts +++ b/backend/src/api/auth.ts @@ -8,8 +8,8 @@ import { ApiLogins } from "../data/ApiLogins"; import { ApiPermissionAssignments } from "../data/ApiPermissionAssignments"; import { ApiUserInfo } from "../data/ApiUserInfo"; import { ApiUserInfoData } from "../data/entities/ApiUserInfo"; -import { ok } from "./responses"; import { env } from "../env"; +import { ok } from "./responses"; interface IPassportApiUser { apiKey: string; diff --git a/backend/src/api/docs.ts b/backend/src/api/docs.ts index 29a3dba9..e2dd63a8 100644 --- a/backend/src/api/docs.ts +++ b/backend/src/api/docs.ts @@ -56,7 +56,7 @@ export function initDocs(app: express.Express) { const name = plugin.name; const info = plugin.info || {}; - const commands = (plugin.commands || []).map((cmd) => ({ + const commands = (plugin.messageCommands || []).map((cmd) => ({ trigger: cmd.trigger, permission: cmd.permission, signature: cmd.signature, @@ -66,7 +66,7 @@ export function initDocs(app: express.Express) { })); const defaultOptions = plugin.defaultOptions || {}; - const configSchema = plugin.configSchema && formatConfigSchema(plugin.configSchema); + const configSchema = plugin.info?.configSchema && formatConfigSchema(plugin.info.configSchema); res.json({ name, diff --git a/backend/src/api/guilds.ts b/backend/src/api/guilds.ts index 0706b4be..c85f5b1e 100644 --- a/backend/src/api/guilds.ts +++ b/backend/src/api/guilds.ts @@ -1,20 +1,20 @@ import { ApiPermissions } from "@shared/apiPermissions"; import express, { Request, Response } from "express"; import { YAMLException } from "js-yaml"; +import moment from "moment-timezone"; import { validateGuildConfig } from "../configValidator"; import { AllowedGuilds } from "../data/AllowedGuilds"; +import { ApiAuditLog } from "../data/ApiAuditLog"; +import { AuditLogEventTypes } from "../data/apiAuditLogTypes"; import { ApiPermissionAssignments, ApiPermissionTypes } from "../data/ApiPermissionAssignments"; import { Configs } from "../data/Configs"; +import { Queue } from "../Queue"; +import { isSnowflake } from "../utils"; +import { loadYamlSafely } from "../utils/loadYamlSafely"; +import { ObjectAliasError } from "../utils/validateNoObjectAliases"; import { apiTokenAuthHandlers } from "./auth"; import { hasGuildPermission, requireGuildPermission } from "./permissions"; import { clientError, ok, serverError, unauthorized } from "./responses"; -import { loadYamlSafely } from "../utils/loadYamlSafely"; -import { ObjectAliasError } from "../utils/validateNoObjectAliases"; -import { isSnowflake } from "../utils"; -import moment from "moment-timezone"; -import { ApiAuditLog } from "../data/ApiAuditLog"; -import { AuditLogEventTypes } from "../data/apiAuditLogTypes"; -import { Queue } from "../Queue"; const apiPermissionAssignments = new ApiPermissionAssignments(); const auditLog = new ApiAuditLog(); diff --git a/backend/src/api/guilds/importExport.ts b/backend/src/api/guilds/importExport.ts index 8bb46748..9e91f4af 100644 --- a/backend/src/api/guilds/importExport.ts +++ b/backend/src/api/guilds/importExport.ts @@ -1,13 +1,13 @@ import { ApiPermissions } from "@shared/apiPermissions"; import express, { Request, Response } from "express"; -import { requireGuildPermission } from "../permissions"; -import { clientError, ok } from "../responses"; -import { GuildCases } from "../../data/GuildCases"; +import moment from "moment-timezone"; import { z } from "zod"; import { Case } from "../../data/entities/Case"; -import { rateLimit } from "../rateLimits"; +import { GuildCases } from "../../data/GuildCases"; import { MINUTES } from "../../utils"; -import moment from "moment-timezone"; +import { requireGuildPermission } from "../permissions"; +import { rateLimit } from "../rateLimits"; +import { clientError, ok } from "../responses"; const caseHandlingModeSchema = z.union([ z.literal("replace"), diff --git a/backend/src/api/guilds/index.ts b/backend/src/api/guilds/index.ts index 36fe4383..1f37aca8 100644 --- a/backend/src/api/guilds/index.ts +++ b/backend/src/api/guilds/index.ts @@ -1,7 +1,7 @@ import express from "express"; import { apiTokenAuthHandlers } from "../auth"; -import { initGuildsMiscAPI } from "./misc"; import { initGuildsImportExportAPI } from "./importExport"; +import { initGuildsMiscAPI } from "./misc"; export function initGuildsAPI(app: express.Express) { const guildRouter = express.Router(); diff --git a/backend/src/api/guilds/misc.ts b/backend/src/api/guilds/misc.ts index c4083c05..8ec25d56 100644 --- a/backend/src/api/guilds/misc.ts +++ b/backend/src/api/guilds/misc.ts @@ -1,22 +1,19 @@ import { ApiPermissions } from "@shared/apiPermissions"; import express, { Request, Response } from "express"; import { YAMLException } from "js-yaml"; +import moment from "moment-timezone"; import { validateGuildConfig } from "../../configValidator"; import { AllowedGuilds } from "../../data/AllowedGuilds"; -import { ApiPermissionAssignments, ApiPermissionTypes } from "../../data/ApiPermissionAssignments"; -import { Configs } from "../../data/Configs"; -import { apiTokenAuthHandlers } from "../auth"; -import { hasGuildPermission, requireGuildPermission } from "../permissions"; -import { clientError, ok, serverError, unauthorized } from "../responses"; -import { loadYamlSafely } from "../../utils/loadYamlSafely"; -import { ObjectAliasError } from "../../utils/validateNoObjectAliases"; -import { isSnowflake } from "../../utils"; -import moment from "moment-timezone"; import { ApiAuditLog } from "../../data/ApiAuditLog"; import { AuditLogEventTypes } from "../../data/apiAuditLogTypes"; +import { ApiPermissionAssignments, ApiPermissionTypes } from "../../data/ApiPermissionAssignments"; +import { Configs } from "../../data/Configs"; import { Queue } from "../../Queue"; -import { GuildCases } from "../../data/GuildCases"; -import { z } from "zod"; +import { isSnowflake } from "../../utils"; +import { loadYamlSafely } from "../../utils/loadYamlSafely"; +import { ObjectAliasError } from "../../utils/validateNoObjectAliases"; +import { hasGuildPermission, requireGuildPermission } from "../permissions"; +import { clientError, ok, serverError, unauthorized } from "../responses"; const apiPermissionAssignments = new ApiPermissionAssignments(); const auditLog = new ApiAuditLog(); diff --git a/backend/src/api/index.ts b/backend/src/api/index.ts index 75bcea5e..649a3c0b 100644 --- a/backend/src/api/index.ts +++ b/backend/src/api/index.ts @@ -1,6 +1,6 @@ import { connect } from "../data/db"; -import { setIsAPI } from "../globals"; import { env } from "../env"; +import { setIsAPI } from "../globals"; if (!env.KEY) { // tslint:disable-next-line:no-console @@ -20,5 +20,5 @@ setIsAPI(true); // Connect to the database before loading the rest of the code (that depend on the database connection) console.log("Connecting to database..."); // tslint:disable-line connect().then(() => { - import("./start"); + import("./start.js"); }); diff --git a/backend/src/api/start.ts b/backend/src/api/start.ts index 59a20da6..8e542cc0 100644 --- a/backend/src/api/start.ts +++ b/backend/src/api/start.ts @@ -1,14 +1,14 @@ import cors from "cors"; import express from "express"; +import multer from "multer"; import { TokenError } from "passport-oauth2"; +import { env } from "../env"; import { initArchives } from "./archives"; import { initAuth } from "./auth"; import { initDocs } from "./docs"; import { initGuildsAPI } from "./guilds/index"; import { clientError, error, notFound } from "./responses"; import { startBackgroundTasks } from "./tasks"; -import multer from "multer"; -import { env } from "../env"; const app = express(); diff --git a/backend/src/commandTypes.ts b/backend/src/commandTypes.ts index 75c6225c..bdc25f17 100644 --- a/backend/src/commandTypes.ts +++ b/backend/src/commandTypes.ts @@ -1,5 +1,18 @@ -import { GuildChannel, GuildMember, Snowflake, Util, User, GuildTextBasedChannel } from "discord.js"; -import { baseCommandParameterTypeHelpers, baseTypeConverters, CommandContext, TypeConversionError } from "knub"; +import { + escapeCodeBlock, + escapeInlineCode, + GuildChannel, + GuildMember, + GuildTextBasedChannel, + Snowflake, + User, +} from "discord.js"; +import { + baseCommandParameterTypeHelpers, + CommandContext, + messageCommandBaseTypeConverters, + TypeConversionError, +} from "knub"; import { createTypeHelper } from "knub-command-manager"; import { channelMentionRegex, @@ -14,11 +27,9 @@ import { import { isValidTimezone } from "./utils/isValidTimezone"; import { MessageTarget, resolveMessageTarget } from "./utils/resolveMessageTarget"; import { inputPatternToRegExp } from "./validatorUtils"; -import { getChannelId } from "knub/dist/utils"; -import { disableCodeBlocks } from "knub/dist/helpers"; export const commandTypes = { - ...baseTypeConverters, + ...messageCommandBaseTypeConverters, delay(value) { const result = convertDelayStringToMS(value); @@ -32,7 +43,7 @@ export const commandTypes = { async resolvedUser(value, context: CommandContext) { const result = await resolveUser(context.pluginData.client, value); if (result == null || result instanceof UnknownUser) { - throw new TypeConversionError(`User \`${Util.escapeCodeBlock(value)}\` was not found`); + throw new TypeConversionError(`User \`${escapeCodeBlock(value)}\` was not found`); } return result; }, @@ -40,7 +51,7 @@ export const commandTypes = { async resolvedUserLoose(value, context: CommandContext) { const result = await resolveUser(context.pluginData.client, value); if (result == null) { - throw new TypeConversionError(`Invalid user: \`${Util.escapeCodeBlock(value)}\``); + throw new TypeConversionError(`Invalid user: \`${escapeCodeBlock(value)}\``); } return result; }, @@ -52,9 +63,7 @@ export const commandTypes = { const result = await resolveMember(context.pluginData.client, context.message.channel.guild, value); if (result == null) { - throw new TypeConversionError( - `Member \`${Util.escapeCodeBlock(value)}\` was not found or they have left the server`, - ); + throw new TypeConversionError(`Member \`${escapeCodeBlock(value)}\` was not found or they have left the server`); } return result; }, @@ -64,7 +73,7 @@ export const commandTypes = { const result = await resolveMessageTarget(context.pluginData, value); if (!result) { - throw new TypeConversionError(`Unknown message \`${Util.escapeInlineCode(value)}\``); + throw new TypeConversionError(`Unknown message \`${escapeInlineCode(value)}\``); } return result; @@ -84,28 +93,27 @@ export const commandTypes = { return value as Snowflake; } - throw new TypeConversionError(`Could not parse ID: \`${Util.escapeInlineCode(value)}\``); + throw new TypeConversionError(`Could not parse ID: \`${escapeInlineCode(value)}\``); }, regex(value: string, context: CommandContext): RegExp { try { return inputPatternToRegExp(value); } catch (e) { - throw new TypeConversionError(`Could not parse RegExp: \`${Util.escapeInlineCode(e.message)}\``); + throw new TypeConversionError(`Could not parse RegExp: \`${escapeInlineCode(e.message)}\``); } }, timezone(value: string) { if (!isValidTimezone(value)) { - throw new TypeConversionError(`Invalid timezone: ${Util.escapeInlineCode(value)}`); + throw new TypeConversionError(`Invalid timezone: ${escapeInlineCode(value)}`); } return value; }, guildTextBasedChannel(value: string, context: CommandContext) { - // FIXME: Remove once Knub's types have been fixed - return baseTypeConverters.textChannel(value, context) as GuildTextBasedChannel; + return messageCommandBaseTypeConverters.textChannel(value, context); }, }; diff --git a/backend/src/configValidator.ts b/backend/src/configValidator.ts index b5543dc4..4d62536f 100644 --- a/backend/src/configValidator.ts +++ b/backend/src/configValidator.ts @@ -1,4 +1,4 @@ -import { configUtils, ConfigValidationError, PluginOptions } from "knub"; +import { ConfigValidationError, PluginConfigManager } from "knub"; import moment from "moment-timezone"; import { guildPlugins } from "./plugins/availablePlugins"; import { ZeppelinPlugin } from "./plugins/ZeppelinPlugin"; @@ -34,9 +34,12 @@ export async function validateGuildConfig(config: any): Promise { } const plugin = pluginNameToPlugin.get(pluginName)!; + const configManager = new PluginConfigManager(plugin.defaultOptions || { config: {} }, pluginOptions, { + levels: {}, + parser: plugin.configParser, + }); try { - const mergedOptions = configUtils.mergeConfig(plugin.defaultOptions || {}, pluginOptions); - await plugin.configPreprocessor?.(mergedOptions as unknown as PluginOptions, true); + await configManager.init(); } catch (err) { if (err instanceof ConfigValidationError || err instanceof StrictValidationError) { return `${pluginName}: ${err.message}`; diff --git a/backend/src/data/AllowedGuilds.ts b/backend/src/data/AllowedGuilds.ts index a9d86220..63941d4b 100644 --- a/backend/src/data/AllowedGuilds.ts +++ b/backend/src/data/AllowedGuilds.ts @@ -1,10 +1,9 @@ +import moment from "moment-timezone"; import { getRepository, Repository } from "typeorm"; +import { DBDateFormat } from "../utils"; import { ApiPermissionTypes } from "./ApiPermissionAssignments"; import { BaseRepository } from "./BaseRepository"; import { AllowedGuild } from "./entities/AllowedGuild"; -import moment from "moment-timezone"; -import { DBDateFormat } from "../utils"; -import { env } from "../env"; export class AllowedGuilds extends BaseRepository { private allowedGuilds: Repository; diff --git a/backend/src/data/ApiAuditLog.ts b/backend/src/data/ApiAuditLog.ts index 199747ef..e6d33999 100644 --- a/backend/src/data/ApiAuditLog.ts +++ b/backend/src/data/ApiAuditLog.ts @@ -1,8 +1,7 @@ -import { BaseRepository } from "./BaseRepository"; import { getRepository, Repository } from "typeorm/index"; -import { ApiAuditLogEntry } from "./entities/ApiAuditLogEntry"; -import { ApiLogin } from "./entities/ApiLogin"; import { AuditLogEventData, AuditLogEventType } from "./apiAuditLogTypes"; +import { BaseRepository } from "./BaseRepository"; +import { ApiAuditLogEntry } from "./entities/ApiAuditLogEntry"; export class ApiAuditLog extends BaseRepository { private auditLog: Repository>; diff --git a/backend/src/data/ApiPermissionAssignments.ts b/backend/src/data/ApiPermissionAssignments.ts index a83a91f9..03ba7c4f 100644 --- a/backend/src/data/ApiPermissionAssignments.ts +++ b/backend/src/data/ApiPermissionAssignments.ts @@ -1,10 +1,9 @@ import { ApiPermissions } from "@shared/apiPermissions"; import { getRepository, Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { ApiPermissionAssignment } from "./entities/ApiPermissionAssignment"; -import { Permissions } from "discord.js"; import { ApiAuditLog } from "./ApiAuditLog"; import { AuditLogEventTypes } from "./apiAuditLogTypes"; +import { BaseRepository } from "./BaseRepository"; +import { ApiPermissionAssignment } from "./entities/ApiPermissionAssignment"; export enum ApiPermissionTypes { User = "USER", diff --git a/backend/src/data/Archives.ts b/backend/src/data/Archives.ts index 06ed0760..b696c33e 100644 --- a/backend/src/data/Archives.ts +++ b/backend/src/data/Archives.ts @@ -1,6 +1,6 @@ import { getRepository, Repository } from "typeorm"; -import { ArchiveEntry } from "./entities/ArchiveEntry"; import { BaseRepository } from "./BaseRepository"; +import { ArchiveEntry } from "./entities/ArchiveEntry"; export class Archives extends BaseRepository { protected archives: Repository; diff --git a/backend/src/data/GuildArchives.ts b/backend/src/data/GuildArchives.ts index b9ae4c05..297028a3 100644 --- a/backend/src/data/GuildArchives.ts +++ b/backend/src/data/GuildArchives.ts @@ -1,18 +1,14 @@ -import { Guild, Snowflake, User } from "discord.js"; +import { Guild, Snowflake } from "discord.js"; import moment from "moment-timezone"; import { isDefaultSticker } from "src/utils/isDefaultSticker"; import { getRepository, Repository } from "typeorm"; import { renderTemplate, TemplateSafeValueContainer } from "../templateFormatter"; import { trimLines } from "../utils"; +import { decrypt, encrypt } from "../utils/crypt"; +import { channelToTemplateSafeChannel, guildToTemplateSafeGuild } from "../utils/templateSafeObjects"; import { BaseGuildRepository } from "./BaseGuildRepository"; import { ArchiveEntry } from "./entities/ArchiveEntry"; -import { - channelToTemplateSafeChannel, - guildToTemplateSafeGuild, - userToTemplateSafeUser, -} from "../utils/templateSafeObjects"; import { SavedMessage } from "./entities/SavedMessage"; -import { decrypt, encrypt } from "../utils/crypt"; const DEFAULT_EXPIRY_DAYS = 30; diff --git a/backend/src/data/GuildCases.ts b/backend/src/data/GuildCases.ts index 9c41c8b6..22b389e7 100644 --- a/backend/src/data/GuildCases.ts +++ b/backend/src/data/GuildCases.ts @@ -1,12 +1,11 @@ import { getRepository, In, InsertResult, Repository } from "typeorm"; +import { Queue } from "../Queue"; +import { chunkArray } from "../utils"; import { BaseGuildRepository } from "./BaseGuildRepository"; import { CaseTypes } from "./CaseTypes"; import { connection } from "./db"; import { Case } from "./entities/Case"; import { CaseNote } from "./entities/CaseNote"; -import moment from "moment-timezone"; -import { chunkArray } from "../utils"; -import { Queue } from "../Queue"; const CASE_SUMMARY_REASON_MAX_LENGTH = 300; diff --git a/backend/src/data/GuildEvents.ts b/backend/src/data/GuildEvents.ts index 24f14858..4d5a8705 100644 --- a/backend/src/data/GuildEvents.ts +++ b/backend/src/data/GuildEvents.ts @@ -1,6 +1,6 @@ import { Mute } from "./entities/Mute"; -import { ScheduledPost } from "./entities/ScheduledPost"; import { Reminder } from "./entities/Reminder"; +import { ScheduledPost } from "./entities/ScheduledPost"; import { Tempban } from "./entities/Tempban"; import { VCAlert } from "./entities/VCAlert"; diff --git a/backend/src/data/GuildRoleButtons.ts b/backend/src/data/GuildRoleButtons.ts index 106e0055..833fb775 100644 --- a/backend/src/data/GuildRoleButtons.ts +++ b/backend/src/data/GuildRoleButtons.ts @@ -1,11 +1,5 @@ import { getRepository, Repository } from "typeorm"; -import { Reminder } from "./entities/Reminder"; -import { BaseRepository } from "./BaseRepository"; -import moment from "moment-timezone"; -import { DBDateFormat } from "../utils"; import { BaseGuildRepository } from "./BaseGuildRepository"; -import { RoleQueueItem } from "./entities/RoleQueueItem"; -import { connection } from "./db"; import { RoleButtonsItem } from "./entities/RoleButtonsItem"; export class GuildRoleButtons extends BaseGuildRepository { diff --git a/backend/src/data/GuildRoleQueue.ts b/backend/src/data/GuildRoleQueue.ts index 20d84012..116df838 100644 --- a/backend/src/data/GuildRoleQueue.ts +++ b/backend/src/data/GuildRoleQueue.ts @@ -1,11 +1,7 @@ import { getRepository, Repository } from "typeorm"; -import { Reminder } from "./entities/Reminder"; -import { BaseRepository } from "./BaseRepository"; -import moment from "moment-timezone"; -import { DBDateFormat } from "../utils"; import { BaseGuildRepository } from "./BaseGuildRepository"; -import { RoleQueueItem } from "./entities/RoleQueueItem"; import { connection } from "./db"; +import { RoleQueueItem } from "./entities/RoleQueueItem"; export class GuildRoleQueue extends BaseGuildRepository { private roleQueue: Repository; diff --git a/backend/src/data/GuildSavedMessages.ts b/backend/src/data/GuildSavedMessages.ts index e6c51e91..5ea29cd7 100644 --- a/backend/src/data/GuildSavedMessages.ts +++ b/backend/src/data/GuildSavedMessages.ts @@ -1,15 +1,13 @@ import { GuildChannel, Message } from "discord.js"; import moment from "moment-timezone"; import { getRepository, Repository } from "typeorm"; -import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; import { QueuedEventEmitter } from "../QueuedEventEmitter"; -import { BaseGuildRepository } from "./BaseGuildRepository"; -import { ISavedMessageData, SavedMessage } from "./entities/SavedMessage"; -import { buildEntity } from "./buildEntity"; import { noop } from "../utils"; -import { decrypt } from "../utils/crypt"; -import { decryptJson, encryptJson } from "../utils/cryptHelpers"; import { asyncMap } from "../utils/async"; +import { decryptJson, encryptJson } from "../utils/cryptHelpers"; +import { BaseGuildRepository } from "./BaseGuildRepository"; +import { buildEntity } from "./buildEntity"; +import { ISavedMessageData, SavedMessage } from "./entities/SavedMessage"; export class GuildSavedMessages extends BaseGuildRepository { private messages: Repository; @@ -53,13 +51,13 @@ export class GuildSavedMessages extends BaseGuildRepository { title: embed.title, description: embed.description, url: embed.url, - timestamp: embed.timestamp, + timestamp: embed.timestamp ? Date.parse(embed.timestamp) : null, color: embed.color, fields: embed.fields.map((field) => ({ name: field.name, value: field.value, - inline: field.inline, + inline: field.inline ?? false, })), author: embed.author diff --git a/backend/src/data/Mutes.ts b/backend/src/data/Mutes.ts index 5734f334..17efed37 100644 --- a/backend/src/data/Mutes.ts +++ b/backend/src/data/Mutes.ts @@ -1,8 +1,8 @@ import moment from "moment-timezone"; -import { Brackets, getRepository, Repository } from "typeorm"; -import { Mute } from "./entities/Mute"; +import { getRepository, Repository } from "typeorm"; import { DAYS, DBDateFormat } from "../utils"; import { BaseRepository } from "./BaseRepository"; +import { Mute } from "./entities/Mute"; const OLD_EXPIRED_MUTE_THRESHOLD = 7 * DAYS; diff --git a/backend/src/data/Phisherman.ts b/backend/src/data/Phisherman.ts index 2f7b90fc..b3e16e36 100644 --- a/backend/src/data/Phisherman.ts +++ b/backend/src/data/Phisherman.ts @@ -1,12 +1,12 @@ -import { getRepository, Repository } from "typeorm"; -import { PhishermanCacheEntry } from "./entities/PhishermanCacheEntry"; -import { PhishermanDomainInfo, PhishermanUnknownDomain } from "./types/phisherman"; -import fetch, { Headers } from "node-fetch"; -import { DAYS, DBDateFormat, HOURS, MINUTES } from "../utils"; -import moment from "moment-timezone"; -import { PhishermanKeyCacheEntry } from "./entities/PhishermanKeyCacheEntry"; import crypto from "crypto"; +import moment from "moment-timezone"; +import fetch, { Headers } from "node-fetch"; +import { getRepository, Repository } from "typeorm"; import { env } from "../env"; +import { DAYS, DBDateFormat, HOURS, MINUTES } from "../utils"; +import { PhishermanCacheEntry } from "./entities/PhishermanCacheEntry"; +import { PhishermanKeyCacheEntry } from "./entities/PhishermanKeyCacheEntry"; +import { PhishermanDomainInfo, PhishermanUnknownDomain } from "./types/phisherman"; const API_URL = "https://api.phisherman.gg"; const MASTER_API_KEY = env.PHISHERMAN_API_KEY; diff --git a/backend/src/data/Reminders.ts b/backend/src/data/Reminders.ts index 7f8d2f1a..0c35c4e2 100644 --- a/backend/src/data/Reminders.ts +++ b/backend/src/data/Reminders.ts @@ -1,8 +1,8 @@ -import { getRepository, Repository } from "typeorm"; -import { Reminder } from "./entities/Reminder"; -import { BaseRepository } from "./BaseRepository"; import moment from "moment-timezone"; +import { getRepository, Repository } from "typeorm"; import { DBDateFormat } from "../utils"; +import { BaseRepository } from "./BaseRepository"; +import { Reminder } from "./entities/Reminder"; export class Reminders extends BaseRepository { private reminders: Repository; diff --git a/backend/src/data/ScheduledPosts.ts b/backend/src/data/ScheduledPosts.ts index 72f1dda4..1931ce6e 100644 --- a/backend/src/data/ScheduledPosts.ts +++ b/backend/src/data/ScheduledPosts.ts @@ -1,8 +1,8 @@ -import { getRepository, Repository } from "typeorm"; -import { ScheduledPost } from "./entities/ScheduledPost"; -import { BaseRepository } from "./BaseRepository"; import moment from "moment-timezone"; +import { getRepository, Repository } from "typeorm"; import { DBDateFormat } from "../utils"; +import { BaseRepository } from "./BaseRepository"; +import { ScheduledPost } from "./entities/ScheduledPost"; export class ScheduledPosts extends BaseRepository { private scheduledPosts: Repository; diff --git a/backend/src/data/Tempbans.ts b/backend/src/data/Tempbans.ts index 5a78c30e..a24d3fd8 100644 --- a/backend/src/data/Tempbans.ts +++ b/backend/src/data/Tempbans.ts @@ -1,8 +1,8 @@ import moment from "moment-timezone"; import { getRepository, Repository } from "typeorm"; -import { Tempban } from "./entities/Tempban"; -import { BaseRepository } from "./BaseRepository"; import { DBDateFormat } from "../utils"; +import { BaseRepository } from "./BaseRepository"; +import { Tempban } from "./entities/Tempban"; export class Tempbans extends BaseRepository { private tempbans: Repository; diff --git a/backend/src/data/VCAlerts.ts b/backend/src/data/VCAlerts.ts index 27e85f90..1296b033 100644 --- a/backend/src/data/VCAlerts.ts +++ b/backend/src/data/VCAlerts.ts @@ -1,8 +1,8 @@ -import { getRepository, Repository } from "typeorm"; -import { VCAlert } from "./entities/VCAlert"; -import { BaseRepository } from "./BaseRepository"; import moment from "moment-timezone"; +import { getRepository, Repository } from "typeorm"; import { DBDateFormat } from "../utils"; +import { BaseRepository } from "./BaseRepository"; +import { VCAlert } from "./entities/VCAlert"; export class VCAlerts extends BaseRepository { private allAlerts: Repository; diff --git a/backend/src/data/Webhooks.ts b/backend/src/data/Webhooks.ts index ce69b2c8..fb1e92ac 100644 --- a/backend/src/data/Webhooks.ts +++ b/backend/src/data/Webhooks.ts @@ -1,7 +1,7 @@ import { getRepository, Repository } from "typeorm"; -import { Webhook } from "./entities/Webhook"; -import { BaseRepository } from "./BaseRepository"; import { decrypt, encrypt } from "../utils/crypt"; +import { BaseRepository } from "./BaseRepository"; +import { Webhook } from "./entities/Webhook"; export class Webhooks extends BaseRepository { repository: Repository = getRepository(Webhook); diff --git a/backend/src/data/db.ts b/backend/src/data/db.ts index fd42f361..decfb431 100644 --- a/backend/src/data/db.ts +++ b/backend/src/data/db.ts @@ -1,8 +1,8 @@ +import path from "path"; import { Connection, createConnection } from "typeorm"; +import { backendDir } from "../paths"; import { SimpleError } from "../SimpleError"; import { QueryLogger } from "./queryLogger"; -import path from "path"; -import { backendDir } from "../paths"; const ormconfigPath = path.join(backendDir, "ormconfig.js"); const connectionOptions = require(ormconfigPath); diff --git a/backend/src/data/entities/ApiAuditLogEntry.ts b/backend/src/data/entities/ApiAuditLogEntry.ts index 0491c313..6c142bd8 100644 --- a/backend/src/data/entities/ApiAuditLogEntry.ts +++ b/backend/src/data/entities/ApiAuditLogEntry.ts @@ -1,5 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; -import { ApiUserInfo } from "./ApiUserInfo"; +import { Column, Entity, PrimaryColumn } from "typeorm"; import { AuditLogEventData, AuditLogEventType } from "../apiAuditLogTypes"; @Entity("api_audit_log") diff --git a/backend/src/data/entities/ApiPermissionAssignment.ts b/backend/src/data/entities/ApiPermissionAssignment.ts index 0160241f..49e07878 100644 --- a/backend/src/data/entities/ApiPermissionAssignment.ts +++ b/backend/src/data/entities/ApiPermissionAssignment.ts @@ -1,6 +1,6 @@ import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; -import { ApiUserInfo } from "./ApiUserInfo"; import { ApiPermissionTypes } from "../ApiPermissionAssignments"; +import { ApiUserInfo } from "./ApiUserInfo"; @Entity("api_permissions") export class ApiPermissionAssignment { diff --git a/backend/src/data/entities/CounterTrigger.ts b/backend/src/data/entities/CounterTrigger.ts index 91cbf995..90cdf488 100644 --- a/backend/src/data/entities/CounterTrigger.ts +++ b/backend/src/data/entities/CounterTrigger.ts @@ -2,7 +2,7 @@ import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; export const TRIGGER_COMPARISON_OPS = ["=", "!=", ">", "<", ">=", "<="] as const; -export type TriggerComparisonOp = typeof TRIGGER_COMPARISON_OPS[number]; +export type TriggerComparisonOp = (typeof TRIGGER_COMPARISON_OPS)[number]; const REVERSE_OPS: Record = { "=": "!=", diff --git a/backend/src/data/entities/Reminder.ts b/backend/src/data/entities/Reminder.ts index e7ce506b..e876efd7 100644 --- a/backend/src/data/entities/Reminder.ts +++ b/backend/src/data/entities/Reminder.ts @@ -1,4 +1,4 @@ -import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; +import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; @Entity("reminders") export class Reminder { diff --git a/backend/src/data/entities/SavedMessage.ts b/backend/src/data/entities/SavedMessage.ts index 5456970b..67ac729b 100644 --- a/backend/src/data/entities/SavedMessage.ts +++ b/backend/src/data/entities/SavedMessage.ts @@ -1,4 +1,4 @@ -import { Snowflake } from "discord.js"; +import { Snowflake, StickerFormatType, StickerType } from "discord.js"; import { Column, Entity, PrimaryColumn } from "typeorm"; export interface ISavedMessageAttachmentData { @@ -55,13 +55,13 @@ export interface ISavedMessageEmbedData { } export interface ISavedMessageStickerData { - format: string; + format: StickerFormatType; guildId: Snowflake | null; id: Snowflake; name: string; description: string | null; available: boolean | null; - type: string | null; + type: StickerType | null; } export interface ISavedMessageData { diff --git a/backend/src/data/entities/ScheduledPost.ts b/backend/src/data/entities/ScheduledPost.ts index 1729bca7..45ba547e 100644 --- a/backend/src/data/entities/ScheduledPost.ts +++ b/backend/src/data/entities/ScheduledPost.ts @@ -1,5 +1,5 @@ -import { MessageAttachment } from "discord.js"; -import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; +import { Attachment } from "discord.js"; +import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; import { StrictMessageContent } from "../../utils"; @Entity("scheduled_posts") @@ -17,7 +17,7 @@ export class ScheduledPost { @Column("simple-json") content: StrictMessageContent; - @Column("simple-json") attachments: MessageAttachment[]; + @Column("simple-json") attachments: Attachment[]; @Column({ type: String, nullable: true }) post_at: string | null; diff --git a/backend/src/data/entities/VCAlert.ts b/backend/src/data/entities/VCAlert.ts index 5f0011d7..d82ae033 100644 --- a/backend/src/data/entities/VCAlert.ts +++ b/backend/src/data/entities/VCAlert.ts @@ -1,4 +1,4 @@ -import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; +import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; @Entity("vc_alerts") export class VCAlert { diff --git a/backend/src/data/loops/expiredArchiveDeletionLoop.ts b/backend/src/data/loops/expiredArchiveDeletionLoop.ts index 7aa794ed..be39ab27 100644 --- a/backend/src/data/loops/expiredArchiveDeletionLoop.ts +++ b/backend/src/data/loops/expiredArchiveDeletionLoop.ts @@ -2,7 +2,6 @@ import { lazyMemoize, MINUTES } from "../../utils"; import { Archives } from "../Archives"; -import moment from "moment-timezone"; const LOOP_INTERVAL = 15 * MINUTES; const getArchivesRepository = lazyMemoize(() => new Archives()); diff --git a/backend/src/data/loops/expiringMutesLoop.ts b/backend/src/data/loops/expiringMutesLoop.ts index 027a3ac9..9060e853 100644 --- a/backend/src/data/loops/expiringMutesLoop.ts +++ b/backend/src/data/loops/expiringMutesLoop.ts @@ -1,11 +1,11 @@ // tslint:disable:no-console -import { lazyMemoize, memoize, MINUTES } from "../../utils"; -import { Mutes } from "../Mutes"; -import Timeout = NodeJS.Timeout; import moment from "moment-timezone"; +import { lazyMemoize, MINUTES } from "../../utils"; import { Mute } from "../entities/Mute"; import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; +import { Mutes } from "../Mutes"; +import Timeout = NodeJS.Timeout; const LOOP_INTERVAL = 15 * MINUTES; const MAX_TRIES_PER_SERVER = 3; diff --git a/backend/src/data/loops/expiringTempbansLoop.ts b/backend/src/data/loops/expiringTempbansLoop.ts index cef9b547..3929125d 100644 --- a/backend/src/data/loops/expiringTempbansLoop.ts +++ b/backend/src/data/loops/expiringTempbansLoop.ts @@ -1,10 +1,10 @@ // tslint:disable:no-console -import { lazyMemoize, MINUTES } from "../../utils"; import moment from "moment-timezone"; +import { lazyMemoize, MINUTES } from "../../utils"; +import { Tempban } from "../entities/Tempban"; import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; import { Tempbans } from "../Tempbans"; -import { Tempban } from "../entities/Tempban"; import Timeout = NodeJS.Timeout; const LOOP_INTERVAL = 15 * MINUTES; diff --git a/backend/src/data/loops/expiringVCAlertsLoop.ts b/backend/src/data/loops/expiringVCAlertsLoop.ts index 54bb7032..dbf8c2ef 100644 --- a/backend/src/data/loops/expiringVCAlertsLoop.ts +++ b/backend/src/data/loops/expiringVCAlertsLoop.ts @@ -1,11 +1,11 @@ // tslint:disable:no-console -import { lazyMemoize, MINUTES } from "../../utils"; import moment from "moment-timezone"; -import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; -import Timeout = NodeJS.Timeout; -import { VCAlerts } from "../VCAlerts"; +import { lazyMemoize, MINUTES } from "../../utils"; import { VCAlert } from "../entities/VCAlert"; +import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; +import { VCAlerts } from "../VCAlerts"; +import Timeout = NodeJS.Timeout; const LOOP_INTERVAL = 15 * MINUTES; const MAX_TRIES_PER_SERVER = 3; diff --git a/backend/src/data/loops/phishermanLoops.ts b/backend/src/data/loops/phishermanLoops.ts index 1ed9a2d2..6ab89488 100644 --- a/backend/src/data/loops/phishermanLoops.ts +++ b/backend/src/data/loops/phishermanLoops.ts @@ -1,6 +1,6 @@ // tslint:disable:no-console -import { HOURS, MINUTES } from "../../utils"; +import { MINUTES } from "../../utils"; import { deleteStalePhishermanCacheEntries, deleteStalePhishermanKeyCacheEntries, diff --git a/backend/src/data/loops/upcomingRemindersLoop.ts b/backend/src/data/loops/upcomingRemindersLoop.ts index b3aa2f4b..3ecc661c 100644 --- a/backend/src/data/loops/upcomingRemindersLoop.ts +++ b/backend/src/data/loops/upcomingRemindersLoop.ts @@ -1,9 +1,9 @@ // tslint:disable:no-console -import { lazyMemoize, MINUTES } from "../../utils"; import moment from "moment-timezone"; -import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; +import { lazyMemoize, MINUTES } from "../../utils"; import { Reminder } from "../entities/Reminder"; +import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; import { Reminders } from "../Reminders"; import Timeout = NodeJS.Timeout; diff --git a/backend/src/data/loops/upcomingScheduledPostsLoop.ts b/backend/src/data/loops/upcomingScheduledPostsLoop.ts index 465939f4..edd9fdad 100644 --- a/backend/src/data/loops/upcomingScheduledPostsLoop.ts +++ b/backend/src/data/loops/upcomingScheduledPostsLoop.ts @@ -1,10 +1,10 @@ // tslint:disable:no-console -import { lazyMemoize, MINUTES } from "../../utils"; import moment from "moment-timezone"; +import { lazyMemoize, MINUTES } from "../../utils"; +import { ScheduledPost } from "../entities/ScheduledPost"; import { emitGuildEvent, hasGuildEventListener } from "../GuildEvents"; import { ScheduledPosts } from "../ScheduledPosts"; -import { ScheduledPost } from "../entities/ScheduledPost"; import Timeout = NodeJS.Timeout; const LOOP_INTERVAL = 15 * MINUTES; diff --git a/backend/src/data/queryLogger.ts b/backend/src/data/queryLogger.ts index bc38cc0e..269c19bf 100644 --- a/backend/src/data/queryLogger.ts +++ b/backend/src/data/queryLogger.ts @@ -1,5 +1,5 @@ -import { AdvancedConsoleLogger } from "typeorm/logger/AdvancedConsoleLogger"; import type { QueryRunner } from "typeorm"; +import { AdvancedConsoleLogger } from "typeorm/logger/AdvancedConsoleLogger"; let groupedQueryStats: Map = new Map(); diff --git a/backend/src/env.ts b/backend/src/env.ts index a125a744..3d98506d 100644 --- a/backend/src/env.ts +++ b/backend/src/env.ts @@ -1,8 +1,8 @@ -import path from "path"; -import fs from "fs"; import dotenv from "dotenv"; -import { rootDir } from "./paths"; +import fs from "fs"; +import path from "path"; import { z } from "zod"; +import { rootDir } from "./paths"; const envType = z.object({ KEY: z.string().length(32), @@ -52,11 +52,11 @@ const envType = z.object({ DB_DATABASE: z.string().optional().default("zeppelin"), }); -let toValidate = {}; +let toValidate = { ...process.env }; const envPath = path.join(rootDir, ".env"); if (fs.existsSync(envPath)) { const buf = fs.readFileSync(envPath); - toValidate = dotenv.parse(buf); + toValidate = { ...toValidate, ...dotenv.parse(buf) }; } export const env = envType.parse(toValidate); diff --git a/backend/src/index.ts b/backend/src/index.ts index 31b2c0a8..ed0d4c9e 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,42 +1,45 @@ -import { Client, Constants, Intents, Options, TextChannel, ThreadChannel } from "discord.js"; -import { Knub, PluginError } from "knub"; -import { PluginLoadError } from "knub/dist/plugins/PluginLoadError"; -// Always use UTC internally -// This is also enforced for the database in data/db.ts +import { + Client, + Events, + GatewayIntentBits, + Options, + Partials, + RESTEvents, + TextChannel, + ThreadChannel, +} from "discord.js"; +import { EventEmitter } from "events"; +import { Knub, PluginError, PluginLoadError, PluginNotLoadedError } from "knub"; import moment from "moment-timezone"; +import { performance } from "perf_hooks"; import { AllowedGuilds } from "./data/AllowedGuilds"; import { Configs } from "./data/Configs"; import { connect } from "./data/db"; import { GuildLogs } from "./data/GuildLogs"; import { LogType } from "./data/LogType"; -import { DiscordJSError } from "./DiscordJSError"; -import { logger } from "./logger"; -import { baseGuildPlugins, globalPlugins, guildPlugins } from "./plugins/availablePlugins"; -import { RecoverablePluginError } from "./RecoverablePluginError"; -import { SimpleError } from "./SimpleError"; -import { ZeppelinGlobalConfig, ZeppelinGuildConfig } from "./types"; -import { startUptimeCounter } from "./uptime"; -import { errorMessage, isDiscordAPIError, isDiscordHTTPError, MINUTES, SECONDS, sleep, successMessage } from "./utils"; -import { loadYamlSafely } from "./utils/loadYamlSafely"; -import { DecayingCounter } from "./utils/DecayingCounter"; -import { PluginNotLoadedError } from "knub/dist/plugins/PluginNotLoadedError"; -import { logRestCall } from "./restCallStats"; -import { logRateLimit } from "./rateLimitStats"; +import { runExpiredArchiveDeletionLoop } from "./data/loops/expiredArchiveDeletionLoop"; import { runExpiringMutesLoop } from "./data/loops/expiringMutesLoop"; -import { runUpcomingRemindersLoop } from "./data/loops/upcomingRemindersLoop"; -import { runUpcomingScheduledPostsLoop } from "./data/loops/upcomingScheduledPostsLoop"; import { runExpiringTempbansLoop } from "./data/loops/expiringTempbansLoop"; import { runExpiringVCAlertsLoop } from "./data/loops/expiringVCAlertsLoop"; -import { runExpiredArchiveDeletionLoop } from "./data/loops/expiredArchiveDeletionLoop"; -import { runSavedMessageCleanupLoop } from "./data/loops/savedMessageCleanupLoop"; -import { performance } from "perf_hooks"; -import { setProfiler } from "./profiler"; -import { enableProfiling } from "./utils/easyProfiler"; import { runPhishermanCacheCleanupLoop, runPhishermanReportingLoop } from "./data/loops/phishermanLoops"; +import { runSavedMessageCleanupLoop } from "./data/loops/savedMessageCleanupLoop"; +import { runUpcomingRemindersLoop } from "./data/loops/upcomingRemindersLoop"; +import { runUpcomingScheduledPostsLoop } from "./data/loops/upcomingScheduledPostsLoop"; import { hasPhishermanMasterAPIKey } from "./data/Phisherman"; import { consumeQueryStats } from "./data/queryLogger"; -import { EventEmitter } from "events"; +import { DiscordJSError } from "./DiscordJSError"; import { env } from "./env"; +import { logger } from "./logger"; +import { baseGuildPlugins, globalPlugins, guildPlugins } from "./plugins/availablePlugins"; +import { setProfiler } from "./profiler"; +import { logRateLimit } from "./rateLimitStats"; +import { RecoverablePluginError } from "./RecoverablePluginError"; +import { SimpleError } from "./SimpleError"; +import { startUptimeCounter } from "./uptime"; +import { errorMessage, isDiscordAPIError, isDiscordHTTPError, MINUTES, SECONDS, sleep, successMessage } from "./utils"; +import { DecayingCounter } from "./utils/DecayingCounter"; +import { enableProfiling } from "./utils/easyProfiler"; +import { loadYamlSafely } from "./utils/loadYamlSafely"; // Error handling let recentPluginErrors = 0; @@ -162,6 +165,8 @@ for (const [i, part] of actualVersionParts.entries()) { throw new SimpleError(`Unsupported Node.js version! Must be at least ${REQUIRED_NODE_VERSION}`); } +// Always use UTC internally +// This is also enforced for the database in data/db.ts moment.tz.setDefault("UTC"); // Blocking check @@ -188,17 +193,19 @@ setInterval(() => { logger.info("Connecting to database"); connect().then(async () => { const client = new Client({ - partials: ["USER", "CHANNEL", "GUILD_MEMBER", "MESSAGE", "REACTION"], + partials: [Partials.User, Partials.Channel, Partials.GuildMember, Partials.Message, Partials.Reaction], makeCache: Options.cacheWithLimits({ - ...Options.defaultMakeCacheSettings, + ...Options.DefaultMakeCacheSettings, MessageManager: 1, // GuildMemberManager: 15000, GuildInviteManager: 0, }), - restGlobalRateLimit: 50, - // restTimeOffset: 1000, + rest: { + // globalRequestsPerSecond: 50, + // offset: 1000, + }, // Disable mentions by default allowedMentions: { @@ -209,25 +216,26 @@ connect().then(async () => { }, intents: [ // Privileged - Intents.FLAGS.GUILD_MEMBERS, - // Intents.FLAGS.GUILD_PRESENCES, - Intents.FLAGS.GUILD_MESSAGE_TYPING, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.MessageContent, + // GatewayIntentBits.GuildPresences, // Regular - Intents.FLAGS.DIRECT_MESSAGES, - Intents.FLAGS.GUILD_BANS, - Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS, - Intents.FLAGS.GUILD_INVITES, - Intents.FLAGS.GUILD_MESSAGE_REACTIONS, - Intents.FLAGS.GUILD_MESSAGES, - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_VOICE_STATES, + GatewayIntentBits.GuildMessageTyping, + GatewayIntentBits.DirectMessages, + GatewayIntentBits.GuildModeration, + GatewayIntentBits.GuildEmojisAndStickers, + GatewayIntentBits.GuildInvites, + GatewayIntentBits.GuildMessageReactions, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildVoiceStates, ], }); // FIXME: TS doesn't see Client as a child of EventEmitter for some reason (client as unknown as EventEmitter).setMaxListeners(200); - client.on(Constants.Events.RATE_LIMIT, (data) => { + client.rest.on(RESTEvents.RateLimited, (data) => { // tslint:disable-next-line:no-console // console.log(`[DEBUG] [RATE_LIMIT] ${JSON.stringify(data)}`); }); @@ -235,7 +243,7 @@ connect().then(async () => { const safe429DecayInterval = 5 * SECONDS; const safe429MaxCount = 5; const safe429Counter = new DecayingCounter(safe429DecayInterval); - client.on(Constants.Events.DEBUG, (errorText) => { + client.on(Events.Debug, (errorText) => { if (!errorText.includes("429")) { return; } @@ -258,7 +266,7 @@ connect().then(async () => { const allowedGuilds = new AllowedGuilds(); const guildConfigs = new Configs(); - const bot = new Knub(client, { + const bot = new Knub(client, { guildPlugins, globalPlugins, @@ -329,6 +337,7 @@ connect().then(async () => { sendSuccessMessageFn(channel, body) { const guildId = channel instanceof TextChannel || channel instanceof ThreadChannel ? channel.guild.id : undefined; + // @ts-expect-error const emoji = guildId ? bot.getLoadedGuild(guildId)!.config.success_emoji : undefined; channel.send(successMessage(body, emoji)); }, @@ -336,6 +345,7 @@ connect().then(async () => { sendErrorMessageFn(channel, body) { const guildId = channel instanceof TextChannel || channel instanceof ThreadChannel ? channel.guild.id : undefined; + // @ts-expect-error const emoji = guildId ? bot.getLoadedGuild(guildId)!.config.error_emoji : undefined; channel.send(errorMessage(body, emoji)); }, @@ -346,7 +356,7 @@ connect().then(async () => { startUptimeCounter(); }); - client.on(Constants.Events.RATE_LIMIT, (data) => { + client.rest.on(RESTEvents.RateLimited, (data) => { logRateLimit(data); }); diff --git a/backend/src/pluginUtils.ts b/backend/src/pluginUtils.ts index 17cd7631..c58b08ba 100644 --- a/backend/src/pluginUtils.ts +++ b/backend/src/pluginUtils.ts @@ -2,19 +2,23 @@ * @file Utility functions that are plugin-instance-specific (i.e. use PluginData) */ -import { GuildMember, Message, MessageMentionOptions, MessageOptions, TextChannel } from "discord.js"; +import { GuildMember, Message, MessageCreateOptions, MessageMentionOptions, TextBasedChannel } from "discord.js"; import * as t from "io-ts"; -import { CommandContext, configUtils, ConfigValidationError, GuildPluginData, helpers, PluginOptions } from "knub"; -import { PluginOverrideCriteria } from "knub/dist/config/configTypes"; -import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; // TODO: Export from Knub index -import { AnyPluginData } from "knub/dist/plugins/PluginData"; +import { + AnyPluginData, + CommandContext, + ConfigValidationError, + ExtendedMatchParams, + GuildPluginData, + helpers, + PluginOverrideCriteria, +} from "knub"; import { logger } from "./logger"; -import { ZeppelinPlugin } from "./plugins/ZeppelinPlugin"; -import { TZeppelinKnub } from "./types"; -import { deepKeyIntersect, errorMessage, successMessage, tDeepPartial, tNullable } from "./utils"; -import { Tail } from "./utils/typeUtils"; -import { decodeAndValidateStrict, StrictValidationError, validate } from "./validatorUtils"; import { isStaff } from "./staff"; +import { TZeppelinKnub } from "./types"; +import { errorMessage, successMessage, tNullable } from "./utils"; +import { Tail } from "./utils/typeUtils"; +import { StrictValidationError, validate } from "./validatorUtils"; const { getMemberLevel } = helpers; @@ -91,117 +95,32 @@ export function strictValidationErrorToConfigValidationError(err: StrictValidati ); } -export function getPluginConfigPreprocessor( - blueprint: ZeppelinPlugin, - customPreprocessor?: ZeppelinPlugin["configPreprocessor"], -) { - return async (options: PluginOptions, strict?: boolean) => { - // 1. Validate the basic structure of plugin config - const basicOptionsValidation = validate(BasicPluginStructureType, options); - if (basicOptionsValidation instanceof StrictValidationError) { - throw strictValidationErrorToConfigValidationError(basicOptionsValidation); +export function makeIoTsConfigParser>(schema: Schema): (input: unknown) => t.TypeOf { + return (input: unknown) => { + const error = validate(schema, input); + if (error) { + throw error; } - - // 2. Validate config/overrides against *partial* config schema. This ensures valid properties have valid types. - const partialConfigSchema = tDeepPartial(blueprint.configSchema); - - if (options.config) { - const partialConfigValidation = validate(partialConfigSchema, options.config); - if (partialConfigValidation instanceof StrictValidationError) { - throw strictValidationErrorToConfigValidationError(partialConfigValidation); - } - } - - if (options.overrides) { - for (const override of options.overrides) { - // Validate criteria and extra criteria - // FIXME: This is ugly - for (const key of Object.keys(override)) { - if (!validTopLevelOverrideKeys.includes(key)) { - if (strict) { - throw new ConfigValidationError(`Unknown override criterion '${key}'`); - } - - delete override[key]; - } - } - if (override.extra != null) { - for (const extraCriterion of Object.keys(override.extra)) { - if (!blueprint.customOverrideCriteriaFunctions?.[extraCriterion]) { - if (strict) { - throw new ConfigValidationError(`Unknown override extra criterion '${extraCriterion}'`); - } - - delete override.extra[extraCriterion]; - } - } - } - - // Validate override config - const partialOverrideConfigValidation = decodeAndValidateStrict(partialConfigSchema, override.config || {}); - if (partialOverrideConfigValidation instanceof StrictValidationError) { - throw strictValidationErrorToConfigValidationError(partialOverrideConfigValidation); - } - } - } - - // 3. Run custom preprocessor, if any - if (customPreprocessor) { - options = await customPreprocessor(options); - } - - // 4. Merge with default options and validate/decode the entire config - let decodedConfig = {}; - const decodedOverrides: Array & { config: any }> = []; - - if (options.config) { - decodedConfig = blueprint.configSchema - ? decodeAndValidateStrict(blueprint.configSchema, options.config) - : options.config; - if (decodedConfig instanceof StrictValidationError) { - throw strictValidationErrorToConfigValidationError(decodedConfig); - } - } - - if (options.overrides) { - for (const override of options.overrides) { - const overrideConfigMergedWithBaseConfig = configUtils.mergeConfig(options.config || {}, override.config || {}); - const decodedOverrideConfig = blueprint.configSchema - ? decodeAndValidateStrict(blueprint.configSchema, overrideConfigMergedWithBaseConfig) - : overrideConfigMergedWithBaseConfig; - if (decodedOverrideConfig instanceof StrictValidationError) { - throw strictValidationErrorToConfigValidationError(decodedOverrideConfig); - } - decodedOverrides.push({ - ...override, - config: deepKeyIntersect(decodedOverrideConfig, override.config || {}), - }); - } - } - - return { - config: decodedConfig, - overrides: decodedOverrides, - }; + return input as t.TypeOf; }; } export async function sendSuccessMessage( pluginData: AnyPluginData, - channel: TextChannel, + channel: TextBasedChannel, body: string, allowedMentions?: MessageMentionOptions, ): Promise { const emoji = pluginData.fullConfig.success_emoji || undefined; const formattedBody = successMessage(body, emoji); - const content: MessageOptions = allowedMentions + const content: MessageCreateOptions = allowedMentions ? { content: formattedBody, allowedMentions } : { content: formattedBody }; return channel .send({ ...content }) // Force line break .catch((err) => { - const channelInfo = channel.guild ? `${channel.id} (${channel.guild.id})` : channel.id; + const channelInfo = "guild" in channel ? `${channel.id} (${channel.guild.id})` : channel.id; logger.warn(`Failed to send success message to ${channelInfo}): ${err.code} ${err.message}`); return undefined; }); @@ -209,20 +128,20 @@ export async function sendSuccessMessage( export async function sendErrorMessage( pluginData: AnyPluginData, - channel: TextChannel, + channel: TextBasedChannel, body: string, allowedMentions?: MessageMentionOptions, ): Promise { const emoji = pluginData.fullConfig.error_emoji || undefined; const formattedBody = errorMessage(body, emoji); - const content: MessageOptions = allowedMentions + const content: MessageCreateOptions = allowedMentions ? { content: formattedBody, allowedMentions } : { content: formattedBody }; return channel .send({ ...content }) // Force line break .catch((err) => { - const channelInfo = channel.guild ? `${channel.id} (${channel.guild.id})` : channel.id; + const channelInfo = "guild" in channel ? `${channel.id} (${channel.guild.id})` : channel.id; logger.warn(`Failed to send error message to ${channelInfo}): ${err.code} ${err.message}`); return undefined; }); @@ -230,11 +149,13 @@ export async function sendErrorMessage( export function getBaseUrl(pluginData: AnyPluginData) { const knub = pluginData.getKnubInstance() as TZeppelinKnub; + // @ts-expect-error return knub.getGlobalConfig().url; } export function isOwner(pluginData: AnyPluginData, userId: string) { const knub = pluginData.getKnubInstance() as TZeppelinKnub; + // @ts-expect-error const owners = knub.getGlobalConfig()?.owners; if (!owners) { return false; diff --git a/backend/src/plugins/AutoDelete/AutoDeletePlugin.ts b/backend/src/plugins/AutoDelete/AutoDeletePlugin.ts index 6c8251b8..af3ff607 100644 --- a/backend/src/plugins/AutoDelete/AutoDeletePlugin.ts +++ b/backend/src/plugins/AutoDelete/AutoDeletePlugin.ts @@ -1,6 +1,7 @@ import { PluginOptions } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -23,10 +24,11 @@ export const AutoDeletePlugin = zeppelinGuildPlugin()({ prettyName: "Auto-delete", description: "Allows Zeppelin to auto-delete messages from a channel after a delay", configurationGuide: "Maximum deletion delay is currently 5 minutes", + configSchema: ConfigSchema, }, dependencies: () => [TimeAndDatePlugin, LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, beforeLoad(pluginData) { @@ -56,8 +58,10 @@ export const AutoDeletePlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - pluginData.state.guildSavedMessages.events.off("create", pluginData.state.onMessageCreateFn); - pluginData.state.guildSavedMessages.events.off("delete", pluginData.state.onMessageDeleteFn); - pluginData.state.guildSavedMessages.events.off("deleteBulk", pluginData.state.onMessageDeleteBulkFn); + const { state, guild } = pluginData; + + state.guildSavedMessages.events.off("create", state.onMessageCreateFn); + state.guildSavedMessages.events.off("delete", state.onMessageDeleteFn); + state.guildSavedMessages.events.off("deleteBulk", state.onMessageDeleteBulkFn); }, }); diff --git a/backend/src/plugins/AutoDelete/util/deleteNextItem.ts b/backend/src/plugins/AutoDelete/util/deleteNextItem.ts index 9ff6a8f0..c3aa6f72 100644 --- a/backend/src/plugins/AutoDelete/util/deleteNextItem.ts +++ b/backend/src/plugins/AutoDelete/util/deleteNextItem.ts @@ -1,7 +1,6 @@ -import { Permissions, Snowflake, TextChannel } from "discord.js"; +import { ChannelType, PermissionsBitField, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { resolveUser, verboseChannelMention } from "../../../utils"; @@ -17,8 +16,8 @@ export async function deleteNextItem(pluginData: GuildPluginData { + channel.messages.delete(itemToDelete.message.id as Snowflake).catch((err) => { if (err.code === 10008) { // "Unknown Message", probably already deleted by automod or another bot, ignore return; diff --git a/backend/src/plugins/AutoDelete/util/onMessageCreate.ts b/backend/src/plugins/AutoDelete/util/onMessageCreate.ts index 1fb07295..024e3cf0 100644 --- a/backend/src/plugins/AutoDelete/util/onMessageCreate.ts +++ b/backend/src/plugins/AutoDelete/util/onMessageCreate.ts @@ -1,10 +1,9 @@ import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; -import { LogType } from "../../../data/LogType"; import { convertDelayStringToMS, resolveMember } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { AutoDeletePluginType, MAX_DELAY } from "../types"; import { addMessageToDeletionQueue } from "./addMessageToDeletionQueue"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export async function onMessageCreate(pluginData: GuildPluginData, msg: SavedMessage) { const member = await resolveMember(pluginData.client, pluginData.guild, msg.user_id); diff --git a/backend/src/plugins/AutoReactions/AutoReactionsPlugin.ts b/backend/src/plugins/AutoReactions/AutoReactionsPlugin.ts index b8575cc4..8ec1ecd7 100644 --- a/backend/src/plugins/AutoReactions/AutoReactionsPlugin.ts +++ b/backend/src/plugins/AutoReactions/AutoReactionsPlugin.ts @@ -1,6 +1,7 @@ import { PluginOptions } from "knub"; import { GuildAutoReactions } from "../../data/GuildAutoReactions"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -31,6 +32,7 @@ export const AutoReactionsPlugin = zeppelinGuildPlugin( description: trimPluginDescription(` Allows setting up automatic reactions to all new messages on a channel `), + configSchema: ConfigSchema, }, // prettier-ignore @@ -38,11 +40,11 @@ export const AutoReactionsPlugin = zeppelinGuildPlugin( LogsPlugin, ], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ NewAutoReactionsCmd, DisableAutoReactionsCmd, ], @@ -53,8 +55,10 @@ export const AutoReactionsPlugin = zeppelinGuildPlugin( ], beforeLoad(pluginData) { - pluginData.state.savedMessages = GuildSavedMessages.getGuildInstance(pluginData.guild.id); - pluginData.state.autoReactions = GuildAutoReactions.getGuildInstance(pluginData.guild.id); - pluginData.state.cache = new Map(); + const { state, guild } = pluginData; + + state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id); + state.autoReactions = GuildAutoReactions.getGuildInstance(guild.id); + state.cache = new Map(); }, }); diff --git a/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts b/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts index 17b377b2..a64cb95c 100644 --- a/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts +++ b/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts @@ -1,4 +1,4 @@ -import { GuildChannel, Permissions } from "discord.js"; +import { PermissionsBitField } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { canUseEmoji, customEmojiRegex, isEmoji } from "../../../utils"; @@ -7,7 +7,7 @@ import { missingPermissionError } from "../../../utils/missingPermissionError"; import { readChannelPermissions } from "../../../utils/readChannelPermissions"; import { autoReactionsCmd } from "../types"; -const requiredPermissions = readChannelPermissions | Permissions.FLAGS.ADD_REACTIONS; +const requiredPermissions = readChannelPermissions | PermissionsBitField.Flags.AddReactions; export const NewAutoReactionsCmd = autoReactionsCmd({ trigger: "auto_reactions", diff --git a/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts b/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts index a959cdcf..574e500f 100644 --- a/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts +++ b/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts @@ -1,14 +1,13 @@ -import { GuildChannel, GuildTextBasedChannel, Permissions } from "discord.js"; -import { LogType } from "../../../data/LogType"; +import { GuildTextBasedChannel, PermissionsBitField } from "discord.js"; +import { AutoReaction } from "../../../data/entities/AutoReaction"; import { isDiscordAPIError } from "../../../utils"; import { getMissingChannelPermissions } from "../../../utils/getMissingChannelPermissions"; import { missingPermissionError } from "../../../utils/missingPermissionError"; import { readChannelPermissions } from "../../../utils/readChannelPermissions"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { autoReactionsEvt } from "../types"; -import { AutoReaction } from "../../../data/entities/AutoReaction"; -const p = Permissions.FLAGS; +const p = PermissionsBitField.Flags; export const AddReactionsEvt = autoReactionsEvt({ event: "messageCreate", @@ -40,7 +39,7 @@ export const AddReactionsEvt = autoReactionsEvt({ const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; if (me) { - const missingPermissions = getMissingChannelPermissions(me, channel, readChannelPermissions | p.ADD_REACTIONS); + const missingPermissions = getMissingChannelPermissions(me, channel, readChannelPermissions | p.AddReactions); if (missingPermissions) { const logs = pluginData.getPlugin(LogsPlugin); logs.logBotAlert({ diff --git a/backend/src/plugins/AutoReactions/types.ts b/backend/src/plugins/AutoReactions/types.ts index 4751fa94..99bbe8c8 100644 --- a/backend/src/plugins/AutoReactions/types.ts +++ b/backend/src/plugins/AutoReactions/types.ts @@ -1,9 +1,9 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; +import { AutoReaction } from "../../data/entities/AutoReaction"; import { GuildAutoReactions } from "../../data/GuildAutoReactions"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; -import { AutoReaction } from "../../data/entities/AutoReaction"; export const ConfigSchema = t.type({ can_manage: t.boolean, @@ -20,5 +20,5 @@ export interface AutoReactionsPluginType extends BasePluginType { }; } -export const autoReactionsCmd = typedGuildCommand(); -export const autoReactionsEvt = typedGuildEventListener(); +export const autoReactionsCmd = guildPluginMessageCommand(); +export const autoReactionsEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Automod/AutomodPlugin.ts b/backend/src/plugins/Automod/AutomodPlugin.ts index b485b4b9..81a7792c 100644 --- a/backend/src/plugins/Automod/AutomodPlugin.ts +++ b/backend/src/plugins/Automod/AutomodPlugin.ts @@ -1,5 +1,5 @@ +import * as t from "io-ts"; import { configUtils, CooldownManager } from "knub"; -import { ConfigPreprocessorFn } from "knub/dist/config/configTypes"; import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildLogs } from "../../data/GuildLogs"; @@ -9,11 +9,13 @@ import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners"; import { MINUTES, SECONDS } from "../../utils"; import { registerEventListenersFromMap } from "../../utils/registerEventListenersFromMap"; import { unregisterEventListenersFromMap } from "../../utils/unregisterEventListenersFromMap"; -import { StrictValidationError } from "../../validatorUtils"; +import { StrictValidationError, validate } from "../../validatorUtils"; import { CountersPlugin } from "../Counters/CountersPlugin"; +import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { ModActionsPlugin } from "../ModActions/ModActionsPlugin"; import { MutesPlugin } from "../Mutes/MutesPlugin"; +import { PhishermanPlugin } from "../Phisherman/PhishermanPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { availableActions } from "./actions/availableActions"; import { AntiraidClearCmd } from "./commands/AntiraidClearCmd"; @@ -35,8 +37,6 @@ import { clearOldRecentSpam } from "./functions/clearOldRecentSpam"; import { pluginInfo } from "./info"; import { availableTriggers } from "./triggers/availableTriggers"; import { AutomodPluginType, ConfigSchema } from "./types"; -import { PhishermanPlugin } from "../Phisherman/PhishermanPlugin"; -import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin"; const defaultOptions = { config: { @@ -63,13 +63,15 @@ const defaultOptions = { /** * Config preprocessor to set default values for triggers and perform extra validation + * TODO: Separate input and output types */ -const configPreprocessor: ConfigPreprocessorFn = (options) => { - if (options.config?.rules) { +const configParser = (input: unknown) => { + const rules = (input as any).rules; + if (rules) { // Loop through each rule - for (const [name, rule] of Object.entries(options.config.rules)) { + for (const [name, rule] of Object.entries(rules)) { if (rule == null) { - delete options.config.rules[name]; + delete rules[name]; continue; } @@ -97,7 +99,7 @@ const configPreprocessor: ConfigPreprocessorFn = (options) => for (const triggerObj of rule["triggers"]) { for (const triggerName in triggerObj) { if (!availableTriggers[triggerName]) { - throw new StrictValidationError([`Unknown trigger '${triggerName}' in rule '${rule.name}'`]); + throw new StrictValidationError([`Unknown trigger '${triggerName}' in rule '${rule["name"]}'`]); } const triggerBlueprint = availableTriggers[triggerName]; @@ -117,11 +119,11 @@ const configPreprocessor: ConfigPreprocessorFn = (options) => if (white && black) { throw new StrictValidationError([ - `Cannot have both blacklist and whitelist enabled at rule <${rule.name}/match_attachment_type>`, + `Cannot have both blacklist and whitelist enabled at rule <${rule["name"]}/match_attachment_type>`, ]); } else if (!white && !black) { throw new StrictValidationError([ - `Must have either blacklist or whitelist enabled at rule <${rule.name}/match_attachment_type>`, + `Must have either blacklist or whitelist enabled at rule <${rule["name"]}/match_attachment_type>`, ]); } } @@ -132,11 +134,11 @@ const configPreprocessor: ConfigPreprocessorFn = (options) => if (white && black) { throw new StrictValidationError([ - `Cannot have both blacklist and whitelist enabled at rule <${rule.name}/match_mime_type>`, + `Cannot have both blacklist and whitelist enabled at rule <${rule["name"]}/match_mime_type>`, ]); } else if (!white && !black) { throw new StrictValidationError([ - `Must have either blacklist or whitelist enabled at rule <${rule.name}/match_mime_type>`, + `Must have either blacklist or whitelist enabled at rule <${rule["name"]}/match_mime_type>`, ]); } } @@ -147,7 +149,7 @@ const configPreprocessor: ConfigPreprocessorFn = (options) => if (rule["actions"]) { for (const actionName in rule["actions"]) { if (!availableActions[actionName]) { - throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule.name}'`]); + throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule["name"]}'`]); } const actionBlueprint = availableActions[actionName]; @@ -163,9 +165,9 @@ const configPreprocessor: ConfigPreprocessorFn = (options) => // Enable logging of automod actions by default if (rule["actions"]) { - for (const actionName in rule.actions) { + for (const actionName in rule["actions"]) { if (!availableActions[actionName]) { - throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule.name}'`]); + throw new StrictValidationError([`Unknown action '${actionName}' in rule '${rule["name"]}'`]); } } @@ -173,13 +175,18 @@ const configPreprocessor: ConfigPreprocessorFn = (options) => rule["actions"]["log"] = true; } if (rule["actions"]["clean"] && rule["actions"]["start_thread"]) { - throw new StrictValidationError([`Cannot have both clean and start_thread at rule '${rule.name}'`]); + throw new StrictValidationError([`Cannot have both clean and start_thread at rule '${rule["name"]}'`]); } } } } - return options; + const error = validate(ConfigSchema, input); + if (error) { + throw error; + } + + return input as t.TypeOf; }; export const AutomodPlugin = zeppelinGuildPlugin()({ @@ -197,9 +204,8 @@ export const AutomodPlugin = zeppelinGuildPlugin()({ InternalPosterPlugin, ], - configSchema: ConfigSchema, defaultOptions, - configPreprocessor, + configParser, customOverrideCriteriaFunctions: { antiraid_level: (pluginData, matchParams, value) => { @@ -218,136 +224,126 @@ export const AutomodPlugin = zeppelinGuildPlugin()({ // Messages use message events from SavedMessages, see onLoad below ], - commands: [AntiraidClearCmd, SetAntiraidCmd, ViewAntiraidCmd], + messageCommands: [AntiraidClearCmd, SetAntiraidCmd, ViewAntiraidCmd], async beforeLoad(pluginData) { - pluginData.state.queue = new Queue(); + const { state, guild } = pluginData; - pluginData.state.regexRunner = getRegExpRunner(`guild-${pluginData.guild.id}`); + state.queue = new Queue(); - pluginData.state.recentActions = []; + state.regexRunner = getRegExpRunner(`guild-${guild.id}`); - pluginData.state.recentSpam = []; + state.recentActions = []; - pluginData.state.recentNicknameChanges = new Map(); + state.recentSpam = []; - pluginData.state.ignoredRoleChanges = new Set(); + state.recentNicknameChanges = new Map(); - pluginData.state.cooldownManager = new CooldownManager(); + state.ignoredRoleChanges = new Set(); - pluginData.state.logs = new GuildLogs(pluginData.guild.id); - pluginData.state.savedMessages = GuildSavedMessages.getGuildInstance(pluginData.guild.id); - pluginData.state.antiraidLevels = GuildAntiraidLevels.getGuildInstance(pluginData.guild.id); - pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id); + state.cooldownManager = new CooldownManager(); - pluginData.state.cachedAntiraidLevel = await pluginData.state.antiraidLevels.get(); + state.logs = new GuildLogs(guild.id); + state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id); + state.antiraidLevels = GuildAntiraidLevels.getGuildInstance(guild.id); + state.archives = GuildArchives.getGuildInstance(guild.id); + + state.cachedAntiraidLevel = await state.antiraidLevels.get(); }, async afterLoad(pluginData) { - pluginData.state.clearRecentActionsInterval = setInterval(() => clearOldRecentActions(pluginData), 1 * MINUTES); - pluginData.state.clearRecentSpamInterval = setInterval(() => clearOldRecentSpam(pluginData), 1 * SECONDS); - pluginData.state.clearRecentNicknameChangesInterval = setInterval( + const { state, guild } = pluginData; + + state.clearRecentActionsInterval = setInterval(() => clearOldRecentActions(pluginData), 1 * MINUTES); + state.clearRecentSpamInterval = setInterval(() => clearOldRecentSpam(pluginData), 1 * SECONDS); + state.clearRecentNicknameChangesInterval = setInterval( () => clearOldRecentNicknameChanges(pluginData), 30 * SECONDS, ); - pluginData.state.onMessageCreateFn = (message) => runAutomodOnMessage(pluginData, message, false); - pluginData.state.savedMessages.events.on("create", pluginData.state.onMessageCreateFn); - - pluginData.state.onMessageUpdateFn = (message) => runAutomodOnMessage(pluginData, message, true); - pluginData.state.savedMessages.events.on("update", pluginData.state.onMessageUpdateFn); + state.onMessageCreateFn = (message) => runAutomodOnMessage(pluginData, message, false); + state.savedMessages.events.on("create", state.onMessageCreateFn); + state.onMessageUpdateFn = (message) => runAutomodOnMessage(pluginData, message, true); + state.savedMessages.events.on("update", state.onMessageUpdateFn); const countersPlugin = pluginData.getPlugin(CountersPlugin); - pluginData.state.onCounterTrigger = (name, triggerName, channelId, userId) => { + state.onCounterTrigger = (name, triggerName, channelId, userId) => { runAutomodOnCounterTrigger(pluginData, name, triggerName, channelId, userId, false); }; - pluginData.state.onCounterReverseTrigger = (name, triggerName, channelId, userId) => { + state.onCounterReverseTrigger = (name, triggerName, channelId, userId) => { runAutomodOnCounterTrigger(pluginData, name, triggerName, channelId, userId, true); }; - - countersPlugin.onCounterEvent("trigger", pluginData.state.onCounterTrigger); - countersPlugin.onCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger); + countersPlugin.onCounterEvent("trigger", state.onCounterTrigger); + countersPlugin.onCounterEvent("reverseTrigger", state.onCounterReverseTrigger); const modActionsEvents = pluginData.getPlugin(ModActionsPlugin).getEventEmitter(); - pluginData.state.modActionsListeners = new Map(); - pluginData.state.modActionsListeners.set("note", (userId: string) => - runAutomodOnModAction(pluginData, "note", userId), + state.modActionsListeners = new Map(); + state.modActionsListeners.set("note", (userId: string) => runAutomodOnModAction(pluginData, "note", userId)); + state.modActionsListeners.set("warn", (userId: string, reason: string | undefined, isAutomodAction: boolean) => + runAutomodOnModAction(pluginData, "warn", userId, reason, isAutomodAction), ); - pluginData.state.modActionsListeners.set( - "warn", - (userId: string, reason: string | undefined, isAutomodAction: boolean) => - runAutomodOnModAction(pluginData, "warn", userId, reason, isAutomodAction), + state.modActionsListeners.set("kick", (userId: string, reason: string | undefined, isAutomodAction: boolean) => + runAutomodOnModAction(pluginData, "kick", userId, reason, isAutomodAction), ); - pluginData.state.modActionsListeners.set( - "kick", - (userId: string, reason: string | undefined, isAutomodAction: boolean) => - runAutomodOnModAction(pluginData, "kick", userId, reason, isAutomodAction), + state.modActionsListeners.set("ban", (userId: string, reason: string | undefined, isAutomodAction: boolean) => + runAutomodOnModAction(pluginData, "ban", userId, reason, isAutomodAction), ); - pluginData.state.modActionsListeners.set( - "ban", - (userId: string, reason: string | undefined, isAutomodAction: boolean) => - runAutomodOnModAction(pluginData, "ban", userId, reason, isAutomodAction), - ); - pluginData.state.modActionsListeners.set("unban", (userId: string) => - runAutomodOnModAction(pluginData, "unban", userId), - ); - registerEventListenersFromMap(modActionsEvents, pluginData.state.modActionsListeners); + state.modActionsListeners.set("unban", (userId: string) => runAutomodOnModAction(pluginData, "unban", userId)); + registerEventListenersFromMap(modActionsEvents, state.modActionsListeners); const mutesEvents = pluginData.getPlugin(MutesPlugin).getEventEmitter(); - pluginData.state.mutesListeners = new Map(); - pluginData.state.mutesListeners.set( - "mute", - (userId: string, reason: string | undefined, isAutomodAction: boolean) => - runAutomodOnModAction(pluginData, "mute", userId, reason, isAutomodAction), + state.mutesListeners = new Map(); + state.mutesListeners.set("mute", (userId: string, reason: string | undefined, isAutomodAction: boolean) => + runAutomodOnModAction(pluginData, "mute", userId, reason, isAutomodAction), ); - pluginData.state.mutesListeners.set("unmute", (userId: string) => - runAutomodOnModAction(pluginData, "unmute", userId), - ); - registerEventListenersFromMap(mutesEvents, pluginData.state.mutesListeners); + state.mutesListeners.set("unmute", (userId: string) => runAutomodOnModAction(pluginData, "unmute", userId)); + registerEventListenersFromMap(mutesEvents, state.mutesListeners); }, async beforeUnload(pluginData) { + const { state, guild } = pluginData; + const countersPlugin = pluginData.getPlugin(CountersPlugin); - if (pluginData.state.onCounterTrigger) { - countersPlugin.offCounterEvent("trigger", pluginData.state.onCounterTrigger); + if (state.onCounterTrigger) { + countersPlugin.offCounterEvent("trigger", state.onCounterTrigger); } - if (pluginData.state.onCounterReverseTrigger) { - countersPlugin.offCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger); + if (state.onCounterReverseTrigger) { + countersPlugin.offCounterEvent("reverseTrigger", state.onCounterReverseTrigger); } const modActionsEvents = pluginData.getPlugin(ModActionsPlugin).getEventEmitter(); - if (pluginData.state.modActionsListeners) { - unregisterEventListenersFromMap(modActionsEvents, pluginData.state.modActionsListeners); + if (state.modActionsListeners) { + unregisterEventListenersFromMap(modActionsEvents, state.modActionsListeners); } const mutesEvents = pluginData.getPlugin(MutesPlugin).getEventEmitter(); - if (pluginData.state.mutesListeners) { - unregisterEventListenersFromMap(mutesEvents, pluginData.state.mutesListeners); + if (state.mutesListeners) { + unregisterEventListenersFromMap(mutesEvents, state.mutesListeners); } - pluginData.state.queue.clear(); + state.queue.clear(); - discardRegExpRunner(`guild-${pluginData.guild.id}`); + discardRegExpRunner(`guild-${guild.id}`); - if (pluginData.state.clearRecentActionsInterval) { - clearInterval(pluginData.state.clearRecentActionsInterval); + if (state.clearRecentActionsInterval) { + clearInterval(state.clearRecentActionsInterval); } - if (pluginData.state.clearRecentSpamInterval) { - clearInterval(pluginData.state.clearRecentSpamInterval); + if (state.clearRecentSpamInterval) { + clearInterval(state.clearRecentSpamInterval); } - if (pluginData.state.clearRecentNicknameChangesInterval) { - clearInterval(pluginData.state.clearRecentNicknameChangesInterval); + if (state.clearRecentNicknameChangesInterval) { + clearInterval(state.clearRecentNicknameChangesInterval); } - if (pluginData.state.onMessageCreateFn) { - pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn); + if (state.onMessageCreateFn) { + state.savedMessages.events.off("create", state.onMessageCreateFn); } - if (pluginData.state.onMessageUpdateFn) { - pluginData.state.savedMessages.events.off("update", pluginData.state.onMessageUpdateFn); + if (state.onMessageUpdateFn) { + state.savedMessages.events.off("update", state.onMessageUpdateFn); } }, }); diff --git a/backend/src/plugins/Automod/actions/addRoles.ts b/backend/src/plugins/Automod/actions/addRoles.ts index ee577ae9..ce2f02d9 100644 --- a/backend/src/plugins/Automod/actions/addRoles.ts +++ b/backend/src/plugins/Automod/actions/addRoles.ts @@ -1,6 +1,5 @@ -import { Permissions, Snowflake } from "discord.js"; +import { PermissionFlagsBits, Snowflake } from "discord.js"; import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; import { nonNullish, unique } from "../../../utils"; import { canAssignRole } from "../../../utils/canAssignRole"; import { getMissingPermissions } from "../../../utils/getMissingPermissions"; @@ -10,7 +9,7 @@ import { LogsPlugin } from "../../Logs/LogsPlugin"; import { ignoreRoleChange } from "../functions/ignoredRoleChanges"; import { automodAction } from "../helpers"; -const p = Permissions.FLAGS; +const p = PermissionFlagsBits; export const AddRolesAction = automodAction({ configType: t.array(t.string), @@ -20,7 +19,7 @@ export const AddRolesAction = automodAction({ const members = unique(contexts.map((c) => c.member).filter(nonNullish)); const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; - const missingPermissions = getMissingPermissions(me.permissions, p.MANAGE_ROLES); + const missingPermissions = getMissingPermissions(me.permissions, p.ManageRoles); if (missingPermissions) { const logs = pluginData.getPlugin(LogsPlugin); logs.logBotAlert({ diff --git a/backend/src/plugins/Automod/actions/addToCounter.ts b/backend/src/plugins/Automod/actions/addToCounter.ts index 04d73f83..842534ef 100644 --- a/backend/src/plugins/Automod/actions/addToCounter.ts +++ b/backend/src/plugins/Automod/actions/addToCounter.ts @@ -1,8 +1,7 @@ import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; import { CountersPlugin } from "../../Counters/CountersPlugin"; -import { automodAction } from "../helpers"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { automodAction } from "../helpers"; export const AddToCounterAction = automodAction({ configType: t.type({ diff --git a/backend/src/plugins/Automod/actions/alert.ts b/backend/src/plugins/Automod/actions/alert.ts index 8bfce04d..eb6f3348 100644 --- a/backend/src/plugins/Automod/actions/alert.ts +++ b/backend/src/plugins/Automod/actions/alert.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import * as t from "io-ts"; import { erisAllowedMentionsToDjsMentionOptions } from "src/utils/erisAllowedMentionsToDjsMentionOptions"; import { LogType } from "../../../data/LogType"; @@ -9,21 +9,19 @@ import { TemplateSafeValueContainer, } from "../../../templateFormatter"; import { - createChunkedMessage, + chunkMessageLines, + isTruthy, messageLink, - stripObjectToScalars, tAllowedMentions, tNormalizedNullOptional, - isTruthy, - verboseChannelMention, validateAndParseMessageContent, - chunkMessageLines, + verboseChannelMention, } from "../../../utils"; +import { messageIsEmpty } from "../../../utils/messageIsEmpty"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { automodAction } from "../helpers"; -import { TemplateSafeUser, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; -import { messageIsEmpty } from "../../../utils/messageIsEmpty"; -import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"; export const AlertAction = automodAction({ configType: t.type({ @@ -38,7 +36,7 @@ export const AlertAction = automodAction({ const channel = pluginData.guild.channels.cache.get(actionConfig.channel as Snowflake); const logs = pluginData.getPlugin(LogsPlugin); - if (channel?.isText()) { + if (channel?.isTextBased()) { const text = actionConfig.text; const theMessageLink = contexts[0].message && messageLink(pluginData.guild.id, contexts[0].message.channel_id, contexts[0].message.id); @@ -96,7 +94,7 @@ export const AlertAction = automodAction({ const chunks = chunkMessageLines(rendered); for (const chunk of chunks) { await poster.sendMessage(channel, { - content: rendered, + content: chunk, allowedMentions: erisAllowedMentionsToDjsMentionOptions(actionConfig.allowed_mentions), }); } diff --git a/backend/src/plugins/Automod/actions/archiveThread.ts b/backend/src/plugins/Automod/actions/archiveThread.ts index 6fe871a5..d94afdf7 100644 --- a/backend/src/plugins/Automod/actions/archiveThread.ts +++ b/backend/src/plugins/Automod/actions/archiveThread.ts @@ -1,4 +1,4 @@ -import { ThreadChannel } from "discord.js"; +import { AnyThreadChannel } from "discord.js"; import * as t from "io-ts"; import { noop } from "../../../utils"; import { automodAction } from "../helpers"; @@ -11,7 +11,7 @@ export const ArchiveThreadAction = automodAction({ const threads = contexts .filter((c) => c.message?.channel_id) .map((c) => pluginData.guild.channels.cache.get(c.message!.channel_id)) - .filter((c): c is ThreadChannel => c?.isThread() ?? false); + .filter((c): c is AnyThreadChannel => c?.isThread() ?? false); for (const thread of threads) { await thread.setArchived().catch(noop); diff --git a/backend/src/plugins/Automod/actions/changeNickname.ts b/backend/src/plugins/Automod/actions/changeNickname.ts index 0a368355..9cd4790e 100644 --- a/backend/src/plugins/Automod/actions/changeNickname.ts +++ b/backend/src/plugins/Automod/actions/changeNickname.ts @@ -1,5 +1,4 @@ import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; import { nonNullish, unique } from "../../../utils"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { automodAction } from "../helpers"; diff --git a/backend/src/plugins/Automod/actions/changePerms.ts b/backend/src/plugins/Automod/actions/changePerms.ts index ad060879..3f22948c 100644 --- a/backend/src/plugins/Automod/actions/changePerms.ts +++ b/backend/src/plugins/Automod/actions/changePerms.ts @@ -1,20 +1,72 @@ -import { Permissions, PermissionString } from "discord.js"; +import { PermissionsBitField, PermissionsString } from "discord.js"; import * as t from "io-ts"; -import { automodAction } from "../helpers"; -import { tNullable, isValidSnowflake, tPartialDictionary } from "../../../utils"; -import { noop } from "knub/dist/utils"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; +import { isValidSnowflake, noop, tNullable, tPartialDictionary } from "../../../utils"; import { guildToTemplateSafeGuild, savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; +import { automodAction } from "../helpers"; + +type LegacyPermMap = Record; +const legacyPermMap = { + CREATE_INSTANT_INVITE: "CreateInstantInvite", + KICK_MEMBERS: "KickMembers", + BAN_MEMBERS: "BanMembers", + ADMINISTRATOR: "Administrator", + MANAGE_CHANNELS: "ManageChannels", + MANAGE_GUILD: "ManageGuild", + ADD_REACTIONS: "AddReactions", + VIEW_AUDIT_LOG: "ViewAuditLog", + PRIORITY_SPEAKER: "PrioritySpeaker", + STREAM: "Stream", + VIEW_CHANNEL: "ViewChannel", + SEND_MESSAGES: "SendMessages", + SEND_TTSMESSAGES: "SendTTSMessages", + MANAGE_MESSAGES: "ManageMessages", + EMBED_LINKS: "EmbedLinks", + ATTACH_FILES: "AttachFiles", + READ_MESSAGE_HISTORY: "ReadMessageHistory", + MENTION_EVERYONE: "MentionEveryone", + USE_EXTERNAL_EMOJIS: "UseExternalEmojis", + VIEW_GUILD_INSIGHTS: "ViewGuildInsights", + CONNECT: "Connect", + SPEAK: "Speak", + MUTE_MEMBERS: "MuteMembers", + DEAFEN_MEMBERS: "DeafenMembers", + MOVE_MEMBERS: "MoveMembers", + USE_VAD: "UseVAD", + CHANGE_NICKNAME: "ChangeNickname", + MANAGE_NICKNAMES: "ManageNicknames", + MANAGE_ROLES: "ManageRoles", + MANAGE_WEBHOOKS: "ManageWebhooks", + MANAGE_EMOJIS_AND_STICKERS: "ManageEmojisAndStickers", + USE_APPLICATION_COMMANDS: "UseApplicationCommands", + REQUEST_TO_SPEAK: "RequestToSpeak", + MANAGE_EVENTS: "ManageEvents", + MANAGE_THREADS: "ManageThreads", + CREATE_PUBLIC_THREADS: "CreatePublicThreads", + CREATE_PRIVATE_THREADS: "CreatePrivateThreads", + USE_EXTERNAL_STICKERS: "UseExternalStickers", + SEND_MESSAGES_IN_THREADS: "SendMessagesInThreads", + USE_EMBEDDED_ACTIVITIES: "UseEmbeddedActivities", + MODERATE_MEMBERS: "ModerateMembers", +} satisfies LegacyPermMap; + +const realToLegacyMap = Object.entries(legacyPermMap).reduce((map, pair) => { + map[pair[1]] = pair[0]; + return map; +}, {}) as Record; export const ChangePermsAction = automodAction({ configType: t.type({ target: t.string, channel: tNullable(t.string), - perms: tPartialDictionary(t.keyof(Permissions.FLAGS), tNullable(t.boolean)), + perms: tPartialDictionary( + t.union([t.keyof(PermissionsBitField.Flags), t.keyof(legacyPermMap)]), + tNullable(t.boolean), + ), }), defaultConfig: {}, @@ -52,13 +104,15 @@ export const ChangePermsAction = automodAction({ const channel = pluginData.guild.channels.resolve(channelId); if (!channel || channel.isThread()) return; const overwrite = channel.permissionOverwrites.cache.find((pw) => pw.id === target); - const allow = new Permissions(overwrite?.allow ?? 0n).serialize(); - const deny = new Permissions(overwrite?.deny ?? 0n).serialize(); - const newPerms: Partial> = {}; + const allow = new PermissionsBitField(overwrite?.allow ?? 0n).serialize(); + const deny = new PermissionsBitField(overwrite?.deny ?? 0n).serialize(); + const newPerms: Partial> = {}; for (const key in allow) { - if (typeof actionConfig.perms[key] !== "undefined") { - newPerms[key] = actionConfig.perms[key]; + const legacyKey = realToLegacyMap[key]; + const configEntry = actionConfig.perms[key] ?? actionConfig.perms[legacyKey]; + if (typeof configEntry !== "undefined") { + newPerms[key] = configEntry; continue; } if (allow[key]) { @@ -86,11 +140,12 @@ export const ChangePermsAction = automodAction({ if (!role) return; - const perms = new Permissions(role.permissions).serialize(); + const perms = new PermissionsBitField(role.permissions).serialize(); for (const key in actionConfig.perms) { - perms[key] = actionConfig.perms[key]; + const realKey = legacyPermMap[key] ?? key; + perms[realKey] = actionConfig.perms[key]; } - const permsArray = Object.keys(perms).filter((key) => perms[key]); - await role.setPermissions(new Permissions(permsArray)).catch(noop); + const permsArray = Object.keys(perms).filter((key) => perms[key]); + await role.setPermissions(new PermissionsBitField(permsArray)).catch(noop); }, }); diff --git a/backend/src/plugins/Automod/actions/clean.ts b/backend/src/plugins/Automod/actions/clean.ts index 27ee1cb6..91bf37ac 100644 --- a/backend/src/plugins/Automod/actions/clean.ts +++ b/backend/src/plugins/Automod/actions/clean.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake } from "discord.js"; import * as t from "io-ts"; import { LogType } from "../../../data/LogType"; import { noop } from "../../../utils"; @@ -32,7 +32,7 @@ export const CleanAction = automodAction({ pluginData.state.logs.ignoreLog(LogType.MESSAGE_DELETE, id); } - const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as TextChannel; + const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as GuildTextBasedChannel; await channel.bulkDelete(messageIds as Snowflake[]).catch(noop); } }, diff --git a/backend/src/plugins/Automod/actions/log.ts b/backend/src/plugins/Automod/actions/log.ts index 62f57a20..82075a2e 100644 --- a/backend/src/plugins/Automod/actions/log.ts +++ b/backend/src/plugins/Automod/actions/log.ts @@ -1,9 +1,7 @@ import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; -import { isTruthy, stripObjectToScalars, unique } from "../../../utils"; +import { isTruthy, unique } from "../../../utils"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { automodAction } from "../helpers"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; export const LogAction = automodAction({ configType: t.boolean, diff --git a/backend/src/plugins/Automod/actions/mute.ts b/backend/src/plugins/Automod/actions/mute.ts index a1bb9299..3219c712 100644 --- a/backend/src/plugins/Automod/actions/mute.ts +++ b/backend/src/plugins/Automod/actions/mute.ts @@ -1,5 +1,4 @@ import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; import { convertDelayStringToMS, nonNullish, tDelayString, tNullable, unique } from "../../../utils"; import { CaseArgs } from "../../Cases/types"; diff --git a/backend/src/plugins/Automod/actions/removeRoles.ts b/backend/src/plugins/Automod/actions/removeRoles.ts index 62b71480..a46d8262 100644 --- a/backend/src/plugins/Automod/actions/removeRoles.ts +++ b/backend/src/plugins/Automod/actions/removeRoles.ts @@ -1,6 +1,5 @@ -import { Permissions, Snowflake } from "discord.js"; +import { PermissionFlagsBits, Snowflake } from "discord.js"; import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; import { nonNullish, unique } from "../../../utils"; import { canAssignRole } from "../../../utils/canAssignRole"; import { getMissingPermissions } from "../../../utils/getMissingPermissions"; @@ -10,7 +9,7 @@ import { LogsPlugin } from "../../Logs/LogsPlugin"; import { ignoreRoleChange } from "../functions/ignoredRoleChanges"; import { automodAction } from "../helpers"; -const p = Permissions.FLAGS; +const p = PermissionFlagsBits; export const RemoveRolesAction = automodAction({ configType: t.array(t.string), @@ -21,7 +20,7 @@ export const RemoveRolesAction = automodAction({ const members = unique(contexts.map((c) => c.member).filter(nonNullish)); const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; - const missingPermissions = getMissingPermissions(me.permissions, p.MANAGE_ROLES); + const missingPermissions = getMissingPermissions(me.permissions, p.ManageRoles); if (missingPermissions) { const logs = pluginData.getPlugin(LogsPlugin); logs.logBotAlert({ diff --git a/backend/src/plugins/Automod/actions/reply.ts b/backend/src/plugins/Automod/actions/reply.ts index 865ef127..ec7ffc1b 100644 --- a/backend/src/plugins/Automod/actions/reply.ts +++ b/backend/src/plugins/Automod/actions/reply.ts @@ -1,6 +1,5 @@ -import { MessageOptions, Permissions, Snowflake, TextChannel, ThreadChannel, User } from "discord.js"; +import { GuildTextBasedChannel, MessageCreateOptions, PermissionsBitField, Snowflake, User } from "discord.js"; import * as t from "io-ts"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; import { convertDelayStringToMS, @@ -14,10 +13,11 @@ import { verboseChannelMention, } from "../../../utils"; import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions"; +import { messageIsEmpty } from "../../../utils/messageIsEmpty"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { automodAction } from "../helpers"; import { AutomodContext } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { messageIsEmpty } from "../../../utils/messageIsEmpty"; export const ReplyAction = automodAction({ configType: t.union([ @@ -36,7 +36,7 @@ export const ReplyAction = automodAction({ .filter((c) => c.message?.channel_id) .filter((c) => { const channel = pluginData.guild.channels.cache.get(c.message!.channel_id as Snowflake); - return channel?.isText(); + return channel?.isTextBased(); }); const contextsByChannelId = contextsWithTextChannels.reduce((map: Map, context) => { @@ -63,16 +63,16 @@ export const ReplyAction = automodAction({ const formatted = typeof actionConfig === "string" ? await renderReplyText(actionConfig) - : ((await renderRecursively(actionConfig.text, renderReplyText)) as MessageOptions); + : ((await renderRecursively(actionConfig.text, renderReplyText)) as MessageCreateOptions); if (formatted) { - const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as TextChannel; + const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as GuildTextBasedChannel; // Check for basic Send Messages and View Channel permissions if ( !hasDiscordPermissions( channel.permissionsFor(pluginData.client.user!.id), - Permissions.FLAGS.SEND_MESSAGES | Permissions.FLAGS.VIEW_CHANNEL, + PermissionsBitField.Flags.SendMessages | PermissionsBitField.Flags.ViewChannel, ) ) { pluginData.getPlugin(LogsPlugin).logBotAlert({ @@ -84,7 +84,10 @@ export const ReplyAction = automodAction({ // If the message is an embed, check for embed permissions if ( typeof formatted !== "string" && - !hasDiscordPermissions(channel.permissionsFor(pluginData.client.user!.id), Permissions.FLAGS.EMBED_LINKS) + !hasDiscordPermissions( + channel.permissionsFor(pluginData.client.user!.id), + PermissionsBitField.Flags.EmbedLinks, + ) ) { pluginData.getPlugin(LogsPlugin).logBotAlert({ body: `Missing permissions to reply **with an embed** in ${verboseChannelMention( @@ -96,7 +99,7 @@ export const ReplyAction = automodAction({ const messageContent = validateAndParseMessageContent(formatted); - const messageOpts: MessageOptions = { + const messageOpts: MessageCreateOptions = { ...messageContent, allowedMentions: { users: [user.id], @@ -118,7 +121,7 @@ export const ReplyAction = automodAction({ if (typeof actionConfig === "object" && actionConfig.auto_delete) { const delay = convertDelayStringToMS(String(actionConfig.auto_delete))!; - setTimeout(() => !replyMsg.deleted && replyMsg.delete().catch(noop), delay); + setTimeout(() => replyMsg.deletable && replyMsg.delete().catch(noop), delay); } } } diff --git a/backend/src/plugins/Automod/actions/setCounter.ts b/backend/src/plugins/Automod/actions/setCounter.ts index 5be82cf6..3088f848 100644 --- a/backend/src/plugins/Automod/actions/setCounter.ts +++ b/backend/src/plugins/Automod/actions/setCounter.ts @@ -1,8 +1,7 @@ import * as t from "io-ts"; -import { LogType } from "../../../data/LogType"; import { CountersPlugin } from "../../Counters/CountersPlugin"; -import { automodAction } from "../helpers"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { automodAction } from "../helpers"; export const SetCounterAction = automodAction({ configType: t.type({ diff --git a/backend/src/plugins/Automod/actions/setSlowmode.ts b/backend/src/plugins/Automod/actions/setSlowmode.ts index ad333e6d..7b527f10 100644 --- a/backend/src/plugins/Automod/actions/setSlowmode.ts +++ b/backend/src/plugins/Automod/actions/setSlowmode.ts @@ -1,10 +1,8 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { ChannelType, GuildTextBasedChannel, Snowflake } from "discord.js"; import * as t from "io-ts"; -import { ChannelTypeStrings } from "src/types"; -import { LogType } from "../../../data/LogType"; import { convertDelayStringToMS, isDiscordAPIError, tDelayString, tNullable } from "../../../utils"; -import { automodAction } from "../helpers"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { automodAction } from "../helpers"; export const SetSlowmodeAction = automodAction({ configType: t.type({ @@ -23,29 +21,27 @@ export const SetSlowmodeAction = automodAction({ const channel = pluginData.guild.channels.cache.get(channelId as Snowflake); // Only text channels and text channels within categories support slowmodes - if (!channel || !(channel.type === ChannelTypeStrings.TEXT || ChannelTypeStrings.CATEGORY)) { + if (!channel || (!channel.isTextBased() && channel.type !== ChannelType.GuildCategory)) { continue; } - const channelsToSlowmode: TextChannel[] = []; - if (channel.type === ChannelTypeStrings.CATEGORY) { + const channelsToSlowmode: GuildTextBasedChannel[] = []; + if (channel.type === ChannelType.GuildCategory) { // Find all text channels within the category for (const ch of pluginData.guild.channels.cache.values()) { - if (ch.parentId === channel.id && ch.type === ChannelTypeStrings.TEXT) { - channelsToSlowmode.push(ch as TextChannel); + if (ch.parentId === channel.id && ch.type === ChannelType.GuildText) { + channelsToSlowmode.push(ch); } } } else { - channelsToSlowmode.push(channel as TextChannel); + channelsToSlowmode.push(channel); } const slowmodeSeconds = Math.ceil(slowmodeMs / 1000); try { for (const chan of channelsToSlowmode) { - await chan.edit({ - rateLimitPerUser: slowmodeSeconds, - }); + await chan.setRateLimitPerUser(slowmodeSeconds); } } catch (e) { // Check for invalid form body -> indicates duration was too large diff --git a/backend/src/plugins/Automod/actions/startThread.ts b/backend/src/plugins/Automod/actions/startThread.ts index 943f9be6..055c9493 100644 --- a/backend/src/plugins/Automod/actions/startThread.ts +++ b/backend/src/plugins/Automod/actions/startThread.ts @@ -1,8 +1,12 @@ -import { GuildFeature, ThreadAutoArchiveDuration } from "discord-api-types/v9"; -import { TextChannel } from "discord.js"; +import { + ChannelType, + GuildFeature, + GuildTextThreadCreateOptions, + ThreadAutoArchiveDuration, + ThreadChannel, +} from "discord.js"; import * as t from "io-ts"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; -import { ChannelTypeStrings } from "../../../types"; import { convertDelayStringToMS, MINUTES, noop, tDelayString, tNullable } from "../../../utils"; import { savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { automodAction } from "../helpers"; @@ -32,12 +36,11 @@ export const StartThreadAction = automodAction({ const threads = contexts.filter((c) => { if (!c.message || !c.user) return false; const channel = pluginData.guild.channels.cache.get(c.message.channel_id); - if (channel?.type !== ChannelTypeStrings.TEXT || !channel.isText()) return false; // for some reason the typing here for channel.type defaults to ThreadChannelTypes (?) + if (channel?.type !== ChannelType.GuildText || !channel.isTextBased()) return false; // for some reason the typing here for channel.type defaults to ThreadChannelTypes (?) // check against max threads per channel if (actionConfig.limit_per_channel && actionConfig.limit_per_channel > 0) { const threadCount = channel.threads.cache.filter( - (tr) => - tr.ownerId === pluginData.client.user!.id && !tr.deleted && !tr.archived && tr.parentId === channel.id, + (tr) => tr.ownerId === pluginData.client.user!.id && !tr.archived && tr.parentId === channel.id, ).size; if (threadCount >= actionConfig.limit_per_channel) return false; } @@ -53,7 +56,9 @@ export const StartThreadAction = automodAction({ : ThreadAutoArchiveDuration.OneHour; for (const threadContext of threads) { - const channel = pluginData.guild.channels.cache.get(threadContext.message!.channel_id) as TextChannel; + const channel = pluginData.guild.channels.cache.get(threadContext.message!.channel_id); + if (!channel || !("threads" in channel) || channel.type === ChannelType.GuildForum) continue; + const renderThreadName = async (str: string) => renderTemplate( str, @@ -63,20 +68,35 @@ export const StartThreadAction = automodAction({ }), ); const threadName = await renderThreadName(actionConfig.name ?? "{user.tag}s thread"); - const thread = await channel.threads - .create({ - name: threadName, - autoArchiveDuration: autoArchive, - type: - actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads) - ? ChannelTypeStrings.PRIVATE_THREAD - : ChannelTypeStrings.PUBLIC_THREAD, - startMessage: - !actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads) - ? threadContext.message!.id - : undefined, - }) - .catch(noop); + const threadOptions: GuildTextThreadCreateOptions = { + name: threadName, + autoArchiveDuration: autoArchive, + startMessage: + !actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads) + ? threadContext.message!.id + : undefined, + }; + + let thread: ThreadChannel | undefined; + if (channel.type === ChannelType.GuildNews) { + thread = await channel.threads + .create({ + ...threadOptions, + type: ChannelType.AnnouncementThread, + }) + .catch(() => undefined); + } else { + thread = await channel.threads + .create({ + ...threadOptions, + type: actionConfig.private ? ChannelType.PrivateThread : ChannelType.PublicThread, + startMessage: + !actionConfig.private && guild.features.includes(GuildFeature.PrivateThreads) + ? threadContext.message!.id + : undefined, + }) + .catch(() => undefined); + } if (actionConfig.slowmode && thread) { const dur = Math.ceil(Math.max(convertDelayStringToMS(actionConfig.slowmode) ?? 0, 0) / 1000); if (dur > 0) { diff --git a/backend/src/plugins/Automod/commands/AntiraidClearCmd.ts b/backend/src/plugins/Automod/commands/AntiraidClearCmd.ts index 3f119b6b..fd31d4b1 100644 --- a/backend/src/plugins/Automod/commands/AntiraidClearCmd.ts +++ b/backend/src/plugins/Automod/commands/AntiraidClearCmd.ts @@ -1,9 +1,9 @@ -import { typedGuildCommand } from "knub"; +import { guildPluginMessageCommand } from "knub"; import { sendSuccessMessage } from "../../../pluginUtils"; import { setAntiraidLevel } from "../functions/setAntiraidLevel"; import { AutomodPluginType } from "../types"; -export const AntiraidClearCmd = typedGuildCommand()({ +export const AntiraidClearCmd = guildPluginMessageCommand()({ trigger: ["antiraid clear", "antiraid reset", "antiraid none", "antiraid off"], permission: "can_set_antiraid", diff --git a/backend/src/plugins/Automod/commands/SetAntiraidCmd.ts b/backend/src/plugins/Automod/commands/SetAntiraidCmd.ts index 3ec0f2f9..159ff576 100644 --- a/backend/src/plugins/Automod/commands/SetAntiraidCmd.ts +++ b/backend/src/plugins/Automod/commands/SetAntiraidCmd.ts @@ -1,10 +1,10 @@ -import { typedGuildCommand } from "knub"; +import { guildPluginMessageCommand } from "knub"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { setAntiraidLevel } from "../functions/setAntiraidLevel"; import { AutomodPluginType } from "../types"; -export const SetAntiraidCmd = typedGuildCommand()({ +export const SetAntiraidCmd = guildPluginMessageCommand()({ trigger: "antiraid", permission: "can_set_antiraid", diff --git a/backend/src/plugins/Automod/commands/ViewAntiraidCmd.ts b/backend/src/plugins/Automod/commands/ViewAntiraidCmd.ts index 5c208497..f25f8e96 100644 --- a/backend/src/plugins/Automod/commands/ViewAntiraidCmd.ts +++ b/backend/src/plugins/Automod/commands/ViewAntiraidCmd.ts @@ -1,7 +1,7 @@ -import { typedGuildCommand } from "knub"; +import { guildPluginMessageCommand } from "knub"; import { AutomodPluginType } from "../types"; -export const ViewAntiraidCmd = typedGuildCommand()({ +export const ViewAntiraidCmd = guildPluginMessageCommand()({ trigger: "antiraid", permission: "can_view_antiraid", diff --git a/backend/src/plugins/Automod/events/RunAutomodOnJoinLeaveEvt.ts b/backend/src/plugins/Automod/events/RunAutomodOnJoinLeaveEvt.ts index faaff383..4cf34db2 100644 --- a/backend/src/plugins/Automod/events/RunAutomodOnJoinLeaveEvt.ts +++ b/backend/src/plugins/Automod/events/RunAutomodOnJoinLeaveEvt.ts @@ -1,9 +1,9 @@ -import { typedGuildEventListener } from "knub"; +import { guildPluginEventListener } from "knub"; import { RecentActionType } from "../constants"; import { runAutomod } from "../functions/runAutomod"; import { AutomodContext, AutomodPluginType } from "../types"; -export const RunAutomodOnJoinEvt = typedGuildEventListener()({ +export const RunAutomodOnJoinEvt = guildPluginEventListener()({ event: "guildMemberAdd", listener({ pluginData, args: { member } }) { const context: AutomodContext = { @@ -26,7 +26,7 @@ export const RunAutomodOnJoinEvt = typedGuildEventListener()( }, }); -export const RunAutomodOnLeaveEvt = typedGuildEventListener()({ +export const RunAutomodOnLeaveEvt = guildPluginEventListener()({ event: "guildMemberRemove", listener({ pluginData, args: { member } }) { const context: AutomodContext = { diff --git a/backend/src/plugins/Automod/events/RunAutomodOnMemberUpdate.ts b/backend/src/plugins/Automod/events/RunAutomodOnMemberUpdate.ts index 86e8ad46..f6c3518a 100644 --- a/backend/src/plugins/Automod/events/RunAutomodOnMemberUpdate.ts +++ b/backend/src/plugins/Automod/events/RunAutomodOnMemberUpdate.ts @@ -1,10 +1,10 @@ -import { typedGuildEventListener } from "knub"; +import { guildPluginEventListener } from "knub"; import diff from "lodash.difference"; import isEqual from "lodash.isequal"; import { runAutomod } from "../functions/runAutomod"; import { AutomodContext, AutomodPluginType } from "../types"; -export const RunAutomodOnMemberUpdate = typedGuildEventListener()({ +export const RunAutomodOnMemberUpdate = guildPluginEventListener()({ event: "guildMemberUpdate", listener({ pluginData, args: { oldMember, newMember } }) { if (!oldMember) return; diff --git a/backend/src/plugins/Automod/events/runAutomodOnCounterTrigger.ts b/backend/src/plugins/Automod/events/runAutomodOnCounterTrigger.ts index 01f75116..9de3dae0 100644 --- a/backend/src/plugins/Automod/events/runAutomodOnCounterTrigger.ts +++ b/backend/src/plugins/Automod/events/runAutomodOnCounterTrigger.ts @@ -14,7 +14,6 @@ export async function runAutomodOnCounterTrigger( ) { const user = userId ? await resolveUser(pluginData.client, userId) : undefined; const member = (userId && (await resolveMember(pluginData.client, pluginData.guild, userId))) || undefined; - const prettyCounterName = pluginData.getPlugin(CountersPlugin).getPrettyNameForCounter(counterName); const prettyTriggerName = pluginData .getPlugin(CountersPlugin) diff --git a/backend/src/plugins/Automod/events/runAutomodOnMessage.ts b/backend/src/plugins/Automod/events/runAutomodOnMessage.ts index 4b595036..915a0cd7 100644 --- a/backend/src/plugins/Automod/events/runAutomodOnMessage.ts +++ b/backend/src/plugins/Automod/events/runAutomodOnMessage.ts @@ -1,13 +1,12 @@ -import { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; +import { performance } from "perf_hooks"; import { SavedMessage } from "../../../data/entities/SavedMessage"; +import { profilingEnabled } from "../../../utils/easyProfiler"; import { addRecentActionsFromMessage } from "../functions/addRecentActionsFromMessage"; import { clearRecentActionsForMessage } from "../functions/clearRecentActionsForMessage"; import { runAutomod } from "../functions/runAutomod"; import { AutomodContext, AutomodPluginType } from "../types"; -import { performance } from "perf_hooks"; -import { profilingEnabled } from "../../../utils/easyProfiler"; export async function runAutomodOnMessage( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Automod/events/runAutomodOnThreadEvents.ts b/backend/src/plugins/Automod/events/runAutomodOnThreadEvents.ts index 2687778e..977c8426 100644 --- a/backend/src/plugins/Automod/events/runAutomodOnThreadEvents.ts +++ b/backend/src/plugins/Automod/events/runAutomodOnThreadEvents.ts @@ -1,9 +1,9 @@ -import { typedGuildEventListener } from "knub"; +import { guildPluginEventListener } from "knub"; import { RecentActionType } from "../constants"; import { runAutomod } from "../functions/runAutomod"; import { AutomodContext, AutomodPluginType } from "../types"; -export const RunAutomodOnThreadCreate = typedGuildEventListener()({ +export const RunAutomodOnThreadCreate = guildPluginEventListener()({ event: "threadCreate", async listener({ pluginData, args: { thread } }) { const user = thread.ownerId @@ -32,7 +32,7 @@ export const RunAutomodOnThreadCreate = typedGuildEventListener()({ +export const RunAutomodOnThreadDelete = guildPluginEventListener()({ event: "threadDelete", async listener({ pluginData, args: { thread } }) { const user = thread.ownerId @@ -54,7 +54,7 @@ export const RunAutomodOnThreadDelete = typedGuildEventListener()({ +export const RunAutomodOnThreadUpdate = guildPluginEventListener()({ event: "threadUpdate", async listener({ pluginData, args: { oldThread, newThread: thread } }) { const user = thread.ownerId diff --git a/backend/src/plugins/Automod/functions/clearOldRecentActions.ts b/backend/src/plugins/Automod/functions/clearOldRecentActions.ts index 5d75e68d..881f3ff1 100644 --- a/backend/src/plugins/Automod/functions/clearOldRecentActions.ts +++ b/backend/src/plugins/Automod/functions/clearOldRecentActions.ts @@ -1,7 +1,7 @@ import { GuildPluginData } from "knub"; +import { startProfiling } from "../../../utils/easyProfiler"; import { RECENT_ACTION_EXPIRY_TIME } from "../constants"; import { AutomodPluginType } from "../types"; -import { startProfiling } from "../../../utils/easyProfiler"; export function clearOldRecentActions(pluginData: GuildPluginData) { const stopProfiling = startProfiling(pluginData.getKnubInstance().profiler, "automod:fns:clearOldRecentActions"); diff --git a/backend/src/plugins/Automod/functions/clearOldRecentSpam.ts b/backend/src/plugins/Automod/functions/clearOldRecentSpam.ts index a4b9ad1b..f240b2da 100644 --- a/backend/src/plugins/Automod/functions/clearOldRecentSpam.ts +++ b/backend/src/plugins/Automod/functions/clearOldRecentSpam.ts @@ -1,7 +1,7 @@ import { GuildPluginData } from "knub"; +import { startProfiling } from "../../../utils/easyProfiler"; import { RECENT_SPAM_EXPIRY_TIME } from "../constants"; import { AutomodPluginType } from "../types"; -import { startProfiling } from "../../../utils/easyProfiler"; export function clearOldRecentSpam(pluginData: GuildPluginData) { const stopProfiling = startProfiling(pluginData.getKnubInstance().profiler, "automod:fns:clearOldRecentSpam"); diff --git a/backend/src/plugins/Automod/functions/clearRecentActionsForMessage.ts b/backend/src/plugins/Automod/functions/clearRecentActionsForMessage.ts index 8a21f8ad..6f1fcdb6 100644 --- a/backend/src/plugins/Automod/functions/clearRecentActionsForMessage.ts +++ b/backend/src/plugins/Automod/functions/clearRecentActionsForMessage.ts @@ -1,6 +1,6 @@ import { GuildPluginData } from "knub"; -import { AutomodContext, AutomodPluginType } from "../types"; import { startProfiling } from "../../../utils/easyProfiler"; +import { AutomodContext, AutomodPluginType } from "../types"; export function clearRecentActionsForMessage(pluginData: GuildPluginData, context: AutomodContext) { const stopProfiling = startProfiling( diff --git a/backend/src/plugins/Automod/functions/findRecentSpam.ts b/backend/src/plugins/Automod/functions/findRecentSpam.ts index 809fbefd..93553b83 100644 --- a/backend/src/plugins/Automod/functions/findRecentSpam.ts +++ b/backend/src/plugins/Automod/functions/findRecentSpam.ts @@ -1,7 +1,7 @@ import { GuildPluginData } from "knub"; +import { startProfiling } from "../../../utils/easyProfiler"; import { RecentActionType } from "../constants"; import { AutomodPluginType } from "../types"; -import { startProfiling } from "../../../utils/easyProfiler"; export function findRecentSpam( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Automod/functions/getMatchingMessageRecentActions.ts b/backend/src/plugins/Automod/functions/getMatchingMessageRecentActions.ts index 48ad51d7..99dae716 100644 --- a/backend/src/plugins/Automod/functions/getMatchingMessageRecentActions.ts +++ b/backend/src/plugins/Automod/functions/getMatchingMessageRecentActions.ts @@ -1,10 +1,10 @@ import { GuildPluginData } from "knub"; import moment from "moment-timezone"; import { SavedMessage } from "../../../data/entities/SavedMessage"; +import { startProfiling } from "../../../utils/easyProfiler"; import { RecentActionType } from "../constants"; import { AutomodPluginType } from "../types"; import { getMatchingRecentActions } from "./getMatchingRecentActions"; -import { startProfiling } from "../../../utils/easyProfiler"; export function getMatchingMessageRecentActions( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Automod/functions/getMatchingRecentActions.ts b/backend/src/plugins/Automod/functions/getMatchingRecentActions.ts index e8ecb6c2..b22817c4 100644 --- a/backend/src/plugins/Automod/functions/getMatchingRecentActions.ts +++ b/backend/src/plugins/Automod/functions/getMatchingRecentActions.ts @@ -1,7 +1,7 @@ import { GuildPluginData } from "knub"; +import { startProfiling } from "../../../utils/easyProfiler"; import { RecentActionType } from "../constants"; import { AutomodPluginType } from "../types"; -import { startProfiling } from "../../../utils/easyProfiler"; export function getMatchingRecentActions( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Automod/functions/getTextMatchPartialSummary.ts b/backend/src/plugins/Automod/functions/getTextMatchPartialSummary.ts index 31ece554..3d7e993f 100644 --- a/backend/src/plugins/Automod/functions/getTextMatchPartialSummary.ts +++ b/backend/src/plugins/Automod/functions/getTextMatchPartialSummary.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { ActivityType, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { messageSummary, verboseChannelMention } from "../../../utils"; import { AutomodContext, AutomodPluginType } from "../types"; @@ -11,13 +11,13 @@ export function getTextMatchPartialSummary( ) { if (type === "message") { const message = context.message!; - const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake) as TextChannel; + const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake); const channelMention = channel ? verboseChannelMention(channel) : `\`#${message.channel_id}\``; return `message in ${channelMention}:\n${messageSummary(message)}`; } else if (type === "embed") { const message = context.message!; - const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake) as TextChannel; + const channel = pluginData.guild.channels.cache.get(message.channel_id as Snowflake); const channelMention = channel ? verboseChannelMention(channel) : `\`#${message.channel_id}\``; return `message embed in ${channelMention}:\n${messageSummary(message)}`; @@ -29,6 +29,6 @@ export function getTextMatchPartialSummary( const visibleName = context.member?.nickname || context.user!.username; return `visible name: ${visibleName}`; } else if (type === "customstatus") { - return `custom status: ${context.member!.presence?.activities.find((a) => a.type === "CUSTOM")?.name}`; + return `custom status: ${context.member!.presence?.activities.find((a) => a.type === ActivityType.Custom)?.name}`; } } diff --git a/backend/src/plugins/Automod/functions/matchMultipleTextTypesOnMessage.ts b/backend/src/plugins/Automod/functions/matchMultipleTextTypesOnMessage.ts index 08492589..29d07cd9 100644 --- a/backend/src/plugins/Automod/functions/matchMultipleTextTypesOnMessage.ts +++ b/backend/src/plugins/Automod/functions/matchMultipleTextTypesOnMessage.ts @@ -1,7 +1,8 @@ -import { Constants, MessageEmbed } from "discord.js"; +import { ActivityType, Embed } from "discord.js"; import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { resolveMember } from "../../../utils"; +import { DeepMutable } from "../../../utils/typeUtils.js"; import { AutomodPluginType } from "../types"; type TextTriggerWithMultipleMatchTypes = { @@ -33,7 +34,7 @@ export async function* matchMultipleTextTypesOnMessage( } if (trigger.match_embeds && msg.data.embeds?.length) { - const copiedEmbed: MessageEmbed = JSON.parse(JSON.stringify(msg.data.embeds[0])); + const copiedEmbed: DeepMutable = JSON.parse(JSON.stringify(msg.data.embeds[0])); if (copiedEmbed.video) { copiedEmbed.description = ""; // The description is not rendered, hence it doesn't need to be matched } @@ -53,7 +54,7 @@ export async function* matchMultipleTextTypesOnMessage( } for (const activity of member.presence?.activities ?? []) { - if (activity.type === Constants.ActivityTypes[4]) { + if (activity.type === ActivityType.Custom) { yield ["customstatus", `${activity.emoji} ${activity.name}`]; break; } diff --git a/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts b/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts index 9576d149..d26c318b 100644 --- a/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts +++ b/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; import { disableUserNotificationStrings, UserNotificationMethod } from "../../../utils"; @@ -19,7 +19,7 @@ export function resolveActionContactMethods( } const channel = pluginData.guild.channels.cache.get(actionConfig.notifyChannel as Snowflake); - if (!channel?.isText()) { + if (!channel?.isTextBased()) { throw new RecoverablePluginError(ERRORS.INVALID_USER_NOTIFICATION_CHANNEL); } diff --git a/backend/src/plugins/Automod/functions/runAutomod.ts b/backend/src/plugins/Automod/functions/runAutomod.ts index d21818ff..e9c2e3a7 100644 --- a/backend/src/plugins/Automod/functions/runAutomod.ts +++ b/backend/src/plugins/Automod/functions/runAutomod.ts @@ -1,13 +1,13 @@ -import { Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; +import { performance } from "perf_hooks"; +import { calculateBlocking, profilingEnabled } from "../../../utils/easyProfiler"; import { availableActions } from "../actions/availableActions"; import { CleanAction } from "../actions/clean"; import { AutomodTriggerMatchResult } from "../helpers"; import { availableTriggers } from "../triggers/availableTriggers"; import { AutomodContext, AutomodPluginType } from "../types"; import { checkAndUpdateCooldown } from "./checkAndUpdateCooldown"; -import { performance } from "perf_hooks"; -import { calculateBlocking, profilingEnabled } from "../../../utils/easyProfiler"; export async function runAutomod(pluginData: GuildPluginData, context: AutomodContext) { const userId = context.user?.id || context.member?.id || context.message?.user_id; @@ -18,7 +18,7 @@ export async function runAutomod(pluginData: GuildPluginData, const channelOrThread = context.channel ?? (channelIdOrThreadId - ? (pluginData.guild.channels.cache.get(channelIdOrThreadId as Snowflake) as TextChannel | ThreadChannel) + ? (pluginData.guild.channels.cache.get(channelIdOrThreadId as Snowflake) as GuildTextBasedChannel) : null); const channelId = channelOrThread?.isThread() ? channelOrThread.parent?.id : channelIdOrThreadId; const threadId = channelOrThread?.isThread() ? channelOrThread.id : null; diff --git a/backend/src/plugins/Automod/functions/setAntiraidLevel.ts b/backend/src/plugins/Automod/functions/setAntiraidLevel.ts index c77cb414..a21d58b0 100644 --- a/backend/src/plugins/Automod/functions/setAntiraidLevel.ts +++ b/backend/src/plugins/Automod/functions/setAntiraidLevel.ts @@ -1,7 +1,5 @@ import { User } from "discord.js"; import { GuildPluginData } from "knub"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { runAutomodOnAntiraidLevel } from "../events/runAutomodOnAntiraidLevel"; import { AutomodPluginType } from "../types"; diff --git a/backend/src/plugins/Automod/helpers.ts b/backend/src/plugins/Automod/helpers.ts index 20bafe7e..15afc545 100644 --- a/backend/src/plugins/Automod/helpers.ts +++ b/backend/src/plugins/Automod/helpers.ts @@ -1,6 +1,6 @@ import * as t from "io-ts"; import { GuildPluginData } from "knub"; -import { Awaitable } from "knub/dist/utils"; +import { Awaitable } from "../../utils/typeUtils"; import { AutomodContext, AutomodPluginType } from "./types"; interface BaseAutomodTriggerMatchResult { diff --git a/backend/src/plugins/Automod/info.ts b/backend/src/plugins/Automod/info.ts index 6a053757..e0102459 100644 --- a/backend/src/plugins/Automod/info.ts +++ b/backend/src/plugins/Automod/info.ts @@ -1,5 +1,6 @@ import { trimPluginDescription } from "../../utils"; import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint"; +import { ConfigSchema } from "./types"; export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = { prettyName: "Automod", @@ -99,4 +100,5 @@ export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = { {matchSummary} ~~~ `), + configSchema: ConfigSchema, }; diff --git a/backend/src/plugins/Automod/triggers/anyMessage.ts b/backend/src/plugins/Automod/triggers/anyMessage.ts index 9422f4f5..8ed6eedc 100644 --- a/backend/src/plugins/Automod/triggers/anyMessage.ts +++ b/backend/src/plugins/Automod/triggers/anyMessage.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import * as t from "io-ts"; import { verboseChannelMention } from "../../../utils"; import { automodTrigger } from "../helpers"; @@ -22,7 +22,7 @@ export const AnyMessageTrigger = automodTrigger()({ }, renderMatchInformation({ pluginData, contexts, matchResult }) { - const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake) as TextChannel; + const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake); return `Matched message (\`${contexts[0].message!.id}\`) in ${ channel ? verboseChannelMention(channel) : "Unknown Channel" }`; diff --git a/backend/src/plugins/Automod/triggers/availableTriggers.ts b/backend/src/plugins/Automod/triggers/availableTriggers.ts index ea8b6f04..822e44ff 100644 --- a/backend/src/plugins/Automod/triggers/availableTriggers.ts +++ b/backend/src/plugins/Automod/triggers/availableTriggers.ts @@ -11,9 +11,9 @@ import { KickTrigger } from "./kick"; import { LineSpamTrigger } from "./lineSpam"; import { LinkSpamTrigger } from "./linkSpam"; import { MatchAttachmentTypeTrigger } from "./matchAttachmentType"; -import { MatchMimeTypeTrigger } from "./matchMimeType"; import { MatchInvitesTrigger } from "./matchInvites"; import { MatchLinksTrigger } from "./matchLinks"; +import { MatchMimeTypeTrigger } from "./matchMimeType"; import { MatchRegexTrigger } from "./matchRegex"; import { MatchWordsTrigger } from "./matchWords"; import { MemberJoinTrigger } from "./memberJoin"; @@ -26,14 +26,14 @@ import { NoteTrigger } from "./note"; import { RoleAddedTrigger } from "./roleAdded"; import { RoleRemovedTrigger } from "./roleRemoved"; import { StickerSpamTrigger } from "./stickerSpam"; +import { ThreadArchiveTrigger } from "./threadArchive"; import { ThreadCreateTrigger } from "./threadCreate"; import { ThreadCreateSpamTrigger } from "./threadCreateSpam"; import { ThreadDeleteTrigger } from "./threadDelete"; +import { ThreadUnarchiveTrigger } from "./threadUnarchive"; import { UnbanTrigger } from "./unban"; import { UnmuteTrigger } from "./unmute"; import { WarnTrigger } from "./warn"; -import { ThreadArchiveTrigger } from "./threadArchive"; -import { ThreadUnarchiveTrigger } from "./threadUnarchive"; export const availableTriggers: Record> = { any_message: AnyMessageTrigger, diff --git a/backend/src/plugins/Automod/triggers/matchAttachmentType.ts b/backend/src/plugins/Automod/triggers/matchAttachmentType.ts index 142e7b0e..8328caef 100644 --- a/backend/src/plugins/Automod/triggers/matchAttachmentType.ts +++ b/backend/src/plugins/Automod/triggers/matchAttachmentType.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel, Util } from "discord.js"; +import { escapeInlineCode, Snowflake } from "discord.js"; import * as t from "io-ts"; import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils"; import { automodTrigger } from "../helpers"; @@ -66,12 +66,12 @@ export const MatchAttachmentTypeTrigger = automodTrigger()({ }, renderMatchInformation({ pluginData, contexts, matchResult }) { - const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake) as TextChannel; + const channel = pluginData.guild.channels.cache.get(contexts[0].message!.channel_id as Snowflake)!; const prettyChannel = verboseChannelMention(channel); return ( asSingleLine(` - Matched attachment type \`${Util.escapeInlineCode(matchResult.extra.matchedType)}\` + Matched attachment type \`${escapeInlineCode(matchResult.extra.matchedType)}\` (${matchResult.extra.mode === "blacklist" ? "blacklisted" : "not in whitelist"}) in message (\`${contexts[0].message!.id}\`) in ${prettyChannel}: `) + messageSummary(contexts[0].message!) diff --git a/backend/src/plugins/Automod/triggers/matchLinks.ts b/backend/src/plugins/Automod/triggers/matchLinks.ts index 248a19f3..39fb7320 100644 --- a/backend/src/plugins/Automod/triggers/matchLinks.ts +++ b/backend/src/plugins/Automod/triggers/matchLinks.ts @@ -1,16 +1,15 @@ -import { Util } from "discord.js"; -import escapeStringRegexp from "escape-string-regexp"; +import { escapeInlineCode } from "discord.js"; import * as t from "io-ts"; +import { phishermanDomainIsSafe } from "../../../data/Phisherman"; import { allowTimeout } from "../../../RegExpRunner"; import { getUrlsInString, tNullable } from "../../../utils"; +import { mergeRegexes } from "../../../utils/mergeRegexes"; +import { mergeWordsIntoRegex } from "../../../utils/mergeWordsIntoRegex"; import { TRegex } from "../../../validatorUtils"; +import { PhishermanPlugin } from "../../Phisherman/PhishermanPlugin"; import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary"; import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage"; import { automodTrigger } from "../helpers"; -import { mergeRegexes } from "../../../utils/mergeRegexes"; -import { mergeWordsIntoRegex } from "../../../utils/mergeWordsIntoRegex"; -import { PhishermanPlugin } from "../../Phisherman/PhishermanPlugin"; -import { phishermanDomainIsSafe } from "../../../data/Phisherman"; interface MatchResultType { type: MatchableTextType; @@ -186,7 +185,7 @@ export const MatchLinksTrigger = automodTrigger()({ renderMatchInformation({ pluginData, contexts, matchResult }) { const partialSummary = getTextMatchPartialSummary(pluginData, matchResult.extra.type, contexts[0]); - let information = `Matched link \`${Util.escapeInlineCode(matchResult.extra.link)}\``; + let information = `Matched link \`${escapeInlineCode(matchResult.extra.link)}\``; if (matchResult.extra.details) { information += ` ${matchResult.extra.details}`; } diff --git a/backend/src/plugins/Automod/triggers/matchMimeType.ts b/backend/src/plugins/Automod/triggers/matchMimeType.ts index 6e1b447e..1af12471 100644 --- a/backend/src/plugins/Automod/triggers/matchMimeType.ts +++ b/backend/src/plugins/Automod/triggers/matchMimeType.ts @@ -1,7 +1,7 @@ -import { automodTrigger } from "../helpers"; +import { escapeInlineCode } from "discord.js"; import * as t from "io-ts"; import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils"; -import { GuildChannel, Util } from "discord.js"; +import { automodTrigger } from "../helpers"; interface MatchResultType { matchedType: string; @@ -65,13 +65,13 @@ export const MatchMimeTypeTrigger = automodTrigger()({ renderMatchInformation({ pluginData, contexts, matchResult }) { const { message } = contexts[0]; - const channel = pluginData.guild.channels.resolve(message!.channel_id); - const prettyChannel = verboseChannelMention(channel as GuildChannel); + const channel = pluginData.guild.channels.resolve(message!.channel_id)!; + const prettyChannel = verboseChannelMention(channel); const { matchedType, mode } = matchResult.extra; return ( asSingleLine(` - Matched MIME type \`${Util.escapeInlineCode(matchedType)}\` + Matched MIME type \`${escapeInlineCode(matchedType)}\` (${mode === "blacklist" ? "blacklisted" : "not in whitelist"}) in message (\`${message!.id}\`) in ${prettyChannel} `) + messageSummary(message!) diff --git a/backend/src/plugins/Automod/triggers/matchRegex.ts b/backend/src/plugins/Automod/triggers/matchRegex.ts index 0afc3c1f..7d011891 100644 --- a/backend/src/plugins/Automod/triggers/matchRegex.ts +++ b/backend/src/plugins/Automod/triggers/matchRegex.ts @@ -1,12 +1,12 @@ import * as t from "io-ts"; import { allowTimeout } from "../../../RegExpRunner"; +import { mergeRegexes } from "../../../utils/mergeRegexes"; import { normalizeText } from "../../../utils/normalizeText"; import { stripMarkdown } from "../../../utils/stripMarkdown"; import { TRegex } from "../../../validatorUtils"; import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary"; import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage"; import { automodTrigger } from "../helpers"; -import { mergeRegexes } from "../../../utils/mergeRegexes"; interface MatchResultType { pattern: string; diff --git a/backend/src/plugins/Automod/triggers/matchWords.ts b/backend/src/plugins/Automod/triggers/matchWords.ts index c7603496..c5ca79fc 100644 --- a/backend/src/plugins/Automod/triggers/matchWords.ts +++ b/backend/src/plugins/Automod/triggers/matchWords.ts @@ -1,4 +1,3 @@ -import { Util } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; import * as t from "io-ts"; import { normalizeText } from "../../../utils/normalizeText"; diff --git a/backend/src/plugins/Automod/triggers/threadArchive.ts b/backend/src/plugins/Automod/triggers/threadArchive.ts index d4ed631b..6177983e 100644 --- a/backend/src/plugins/Automod/triggers/threadArchive.ts +++ b/backend/src/plugins/Automod/triggers/threadArchive.ts @@ -1,5 +1,4 @@ -import { Snowflake } from "discord-api-types/v9"; -import { User, Util } from "discord.js"; +import { escapeBold, User, type Snowflake } from "discord.js"; import * as t from "io-ts"; import { tNullable } from "../../../utils"; import { automodTrigger } from "../helpers"; @@ -49,7 +48,7 @@ export const ThreadArchiveTrigger = automodTrigger()({ const parentName = matchResult.extra.matchedThreadParentName; const base = `Thread **#${threadName}** (\`${threadId}\`) has been archived in the **#${parentName}** (\`${parentId}\`) channel`; if (threadOwner) { - return `${base} by **${Util.escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`; + return `${base} by **${escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`; } return base; }, diff --git a/backend/src/plugins/Automod/triggers/threadCreate.ts b/backend/src/plugins/Automod/triggers/threadCreate.ts index c0ae6ad3..640432c3 100644 --- a/backend/src/plugins/Automod/triggers/threadCreate.ts +++ b/backend/src/plugins/Automod/triggers/threadCreate.ts @@ -1,5 +1,4 @@ -import { Snowflake } from "discord-api-types/v9"; -import { User, Util } from "discord.js"; +import { escapeBold, User, type Snowflake } from "discord.js"; import * as t from "io-ts"; import { automodTrigger } from "../helpers"; @@ -41,7 +40,7 @@ export const ThreadCreateTrigger = automodTrigger()({ const parentName = matchResult.extra.matchedThreadParentName; const base = `Thread **#${threadName}** (\`${threadId}\`) has been created in the **#${parentName}** (\`${parentId}\`) channel`; if (threadOwner) { - return `${base} by **${Util.escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`; + return `${base} by **${escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`; } return base; }, diff --git a/backend/src/plugins/Automod/triggers/threadDelete.ts b/backend/src/plugins/Automod/triggers/threadDelete.ts index cecb5862..25a93268 100644 --- a/backend/src/plugins/Automod/triggers/threadDelete.ts +++ b/backend/src/plugins/Automod/triggers/threadDelete.ts @@ -1,5 +1,4 @@ -import { Snowflake } from "discord-api-types/v9"; -import { User, Util } from "discord.js"; +import { escapeBold, User, type Snowflake } from "discord.js"; import * as t from "io-ts"; import { automodTrigger } from "../helpers"; @@ -40,7 +39,7 @@ export const ThreadDeleteTrigger = automodTrigger()({ const parentId = matchResult.extra.matchedThreadParentId; const parentName = matchResult.extra.matchedThreadParentName; if (threadOwner) { - return `Thread **#${threadName ?? "Unknown"}** (\`${threadId}\`) created by **${Util.escapeBold( + return `Thread **#${threadName ?? "Unknown"}** (\`${threadId}\`) created by **${escapeBold( threadOwner.tag, )}** (\`${threadOwner.id}\`) in the **#${parentName}** (\`${parentId}\`) channel has been deleted`; } diff --git a/backend/src/plugins/Automod/triggers/threadUnarchive.ts b/backend/src/plugins/Automod/triggers/threadUnarchive.ts index ce3adf81..897f685e 100644 --- a/backend/src/plugins/Automod/triggers/threadUnarchive.ts +++ b/backend/src/plugins/Automod/triggers/threadUnarchive.ts @@ -1,5 +1,4 @@ -import { Snowflake } from "discord-api-types/v9"; -import { User, Util } from "discord.js"; +import { escapeBold, User, type Snowflake } from "discord.js"; import * as t from "io-ts"; import { tNullable } from "../../../utils"; import { automodTrigger } from "../helpers"; @@ -49,7 +48,7 @@ export const ThreadUnarchiveTrigger = automodTrigger()({ const parentName = matchResult.extra.matchedThreadParentName; const base = `Thread **#${threadName}** (\`${threadId}\`) has been unarchived in the **#${parentName}** (\`${parentId}\`) channel`; if (threadOwner) { - return `${base} by **${Util.escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`; + return `${base} by **${escapeBold(threadOwner.tag)}** (\`${threadOwner.id}\`)`; } return base; }, diff --git a/backend/src/plugins/Automod/types.ts b/backend/src/plugins/Automod/types.ts index ed7cf56e..0711d76b 100644 --- a/backend/src/plugins/Automod/types.ts +++ b/backend/src/plugins/Automod/types.ts @@ -1,4 +1,4 @@ -import { GuildMember, PartialGuildMember, TextChannel, ThreadChannel, User } from "discord.js"; +import { GuildMember, GuildTextBasedChannel, PartialGuildMember, ThreadChannel, User } from "discord.js"; import * as t from "io-ts"; import { BasePluginType, CooldownManager } from "knub"; import { SavedMessage } from "../../data/entities/SavedMessage"; @@ -139,7 +139,7 @@ export interface AutomodContext { locked?: ThreadChannel; unlocked?: ThreadChannel; }; - channel?: TextChannel | ThreadChannel; + channel?: GuildTextBasedChannel; } export interface RecentAction { diff --git a/backend/src/plugins/BotControl/BotControlPlugin.ts b/backend/src/plugins/BotControl/BotControlPlugin.ts index 7897d837..76478ca2 100644 --- a/backend/src/plugins/BotControl/BotControlPlugin.ts +++ b/backend/src/plugins/BotControl/BotControlPlugin.ts @@ -3,26 +3,26 @@ import { AllowedGuilds } from "../../data/AllowedGuilds"; import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments"; import { Configs } from "../../data/Configs"; import { GuildArchives } from "../../data/GuildArchives"; -import { sendSuccessMessage } from "../../pluginUtils"; +import { makeIoTsConfigParser, sendSuccessMessage } from "../../pluginUtils"; import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint"; import { getActiveReload, resetActiveReload } from "./activeReload"; import { AddDashboardUserCmd } from "./commands/AddDashboardUserCmd"; +import { AddServerFromInviteCmd } from "./commands/AddServerFromInviteCmd"; import { AllowServerCmd } from "./commands/AllowServerCmd"; +import { ChannelToServerCmd } from "./commands/ChannelToServerCmd"; import { DisallowServerCmd } from "./commands/DisallowServerCmd"; import { EligibleCmd } from "./commands/EligibleCmd"; import { LeaveServerCmd } from "./commands/LeaveServerCmd"; import { ListDashboardPermsCmd } from "./commands/ListDashboardPermsCmd"; import { ListDashboardUsersCmd } from "./commands/ListDashboardUsersCmd"; +import { ProfilerDataCmd } from "./commands/ProfilerDataCmd"; +import { RateLimitPerformanceCmd } from "./commands/RateLimitPerformanceCmd"; import { ReloadGlobalPluginsCmd } from "./commands/ReloadGlobalPluginsCmd"; import { ReloadServerCmd } from "./commands/ReloadServerCmd"; import { RemoveDashboardUserCmd } from "./commands/RemoveDashboardUserCmd"; +import { RestPerformanceCmd } from "./commands/RestPerformanceCmd"; import { ServersCmd } from "./commands/ServersCmd"; import { BotControlPluginType, ConfigSchema } from "./types"; -import { ProfilerDataCmd } from "./commands/ProfilerDataCmd"; -import { AddServerFromInviteCmd } from "./commands/AddServerFromInviteCmd"; -import { ChannelToServerCmd } from "./commands/ChannelToServerCmd"; -import { RestPerformanceCmd } from "./commands/RestPerformanceCmd"; -import { RateLimitPerformanceCmd } from "./commands/RateLimitPerformanceCmd"; const defaultOptions = { config: { @@ -37,11 +37,11 @@ const defaultOptions = { export const BotControlPlugin = zeppelinGlobalPlugin()({ name: "bot_control", - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ ReloadGlobalPluginsCmd, ServersCmd, LeaveServerCmd, @@ -61,17 +61,19 @@ export const BotControlPlugin = zeppelinGlobalPlugin()({ ], async afterLoad(pluginData) { - pluginData.state.archives = new GuildArchives(0); - pluginData.state.allowedGuilds = new AllowedGuilds(); - pluginData.state.configs = new Configs(); - pluginData.state.apiPermissionAssignments = new ApiPermissionAssignments(); + const { state, client } = pluginData; + + state.archives = new GuildArchives(0); + state.allowedGuilds = new AllowedGuilds(); + state.configs = new Configs(); + state.apiPermissionAssignments = new ApiPermissionAssignments(); const activeReload = getActiveReload(); if (activeReload) { const [guildId, channelId] = activeReload; resetActiveReload(); - const guild = await pluginData.client.guilds.fetch(guildId as Snowflake); + const guild = await client.guilds.fetch(guildId as Snowflake); if (guild) { const channel = guild.channels.cache.get(channelId as Snowflake); if (channel instanceof TextChannel) { diff --git a/backend/src/plugins/BotControl/commands/AddDashboardUserCmd.ts b/backend/src/plugins/BotControl/commands/AddDashboardUserCmd.ts index 531016a3..4ac812a7 100644 --- a/backend/src/plugins/BotControl/commands/AddDashboardUserCmd.ts +++ b/backend/src/plugins/BotControl/commands/AddDashboardUserCmd.ts @@ -1,5 +1,4 @@ import { ApiPermissions } from "@shared/apiPermissions"; -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { botControlCmd } from "../types"; @@ -19,7 +18,7 @@ export const AddDashboardUserCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const guild = await pluginData.state.allowedGuilds.find(args.guildId); if (!guild) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin"); + sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin"); return; } @@ -38,7 +37,7 @@ export const AddDashboardUserCmd = botControlCmd({ const userNameList = args.users.map((user) => `<@!${user.id}> (**${user.tag}**, \`${user.id}\`)`); sendSuccessMessage( pluginData, - msg.channel as TextChannel, + msg.channel, `The following users were given dashboard access for **${guild.name}**:\n\n${userNameList}`, ); }, diff --git a/backend/src/plugins/BotControl/commands/AddServerFromInviteCmd.ts b/backend/src/plugins/BotControl/commands/AddServerFromInviteCmd.ts index 1f29e822..cbf53bf4 100644 --- a/backend/src/plugins/BotControl/commands/AddServerFromInviteCmd.ts +++ b/backend/src/plugins/BotControl/commands/AddServerFromInviteCmd.ts @@ -1,11 +1,10 @@ import { ApiPermissions } from "@shared/apiPermissions"; -import { TextChannel } from "discord.js"; -import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { DBDateFormat, isGuildInvite, isSnowflake, resolveInvite } from "../../../utils"; -import { botControlCmd } from "../types"; import moment from "moment-timezone"; +import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; +import { DBDateFormat, isGuildInvite, resolveInvite } from "../../../utils"; import { isEligible } from "../functions/isEligible"; +import { botControlCmd } from "../types"; export const AddServerFromInviteCmd = botControlCmd({ trigger: ["add_server_from_invite", "allow_server_from_invite", "adv"], @@ -19,23 +18,19 @@ export const AddServerFromInviteCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const invite = await resolveInvite(pluginData.client, args.inviteCode, true); if (!invite || !isGuildInvite(invite)) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not resolve invite"); // :D + sendErrorMessage(pluginData, msg.channel, "Could not resolve invite"); // :D return; } const existing = await pluginData.state.allowedGuilds.find(invite.guild.id); if (existing) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is already allowed!"); + sendErrorMessage(pluginData, msg.channel, "Server is already allowed!"); return; } const { result, explanation } = await isEligible(pluginData, args.user, invite); if (!result) { - sendErrorMessage( - pluginData, - msg.channel as TextChannel, - `Could not add server because it's not eligible: ${explanation}`, - ); + sendErrorMessage(pluginData, msg.channel, `Could not add server because it's not eligible: ${explanation}`); return; } @@ -56,10 +51,6 @@ export const AddServerFromInviteCmd = botControlCmd({ ); } - sendSuccessMessage( - pluginData, - msg.channel as TextChannel, - "Server was eligible and is now allowed to use Zeppelin!", - ); + sendSuccessMessage(pluginData, msg.channel, "Server was eligible and is now allowed to use Zeppelin!"); }, }); diff --git a/backend/src/plugins/BotControl/commands/AllowServerCmd.ts b/backend/src/plugins/BotControl/commands/AllowServerCmd.ts index 1aa665b5..ce2c3b0c 100644 --- a/backend/src/plugins/BotControl/commands/AllowServerCmd.ts +++ b/backend/src/plugins/BotControl/commands/AllowServerCmd.ts @@ -1,10 +1,9 @@ import { ApiPermissions } from "@shared/apiPermissions"; -import { TextChannel } from "discord.js"; +import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { DBDateFormat, isSnowflake } from "../../../utils"; import { botControlCmd } from "../types"; -import moment from "moment-timezone"; export const AllowServerCmd = botControlCmd({ trigger: ["allow_server", "allowserver", "add_server", "addserver"], @@ -21,17 +20,17 @@ export const AllowServerCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const existing = await pluginData.state.allowedGuilds.find(args.guildId); if (existing) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is already allowed!"); + sendErrorMessage(pluginData, msg.channel, "Server is already allowed!"); return; } if (!isSnowflake(args.guildId)) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Invalid server ID!"); + sendErrorMessage(pluginData, msg.channel, "Invalid server ID!"); return; } if (args.userId && !isSnowflake(args.userId)) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Invalid user ID!"); + sendErrorMessage(pluginData, msg.channel, "Invalid user ID!"); return; } @@ -52,6 +51,6 @@ export const AllowServerCmd = botControlCmd({ ); } - sendSuccessMessage(pluginData, msg.channel as TextChannel, "Server is now allowed to use Zeppelin!"); + sendSuccessMessage(pluginData, msg.channel, "Server is now allowed to use Zeppelin!"); }, }); diff --git a/backend/src/plugins/BotControl/commands/ChannelToServerCmd.ts b/backend/src/plugins/BotControl/commands/ChannelToServerCmd.ts index 49f0ff73..11d3a487 100644 --- a/backend/src/plugins/BotControl/commands/ChannelToServerCmd.ts +++ b/backend/src/plugins/BotControl/commands/ChannelToServerCmd.ts @@ -1,9 +1,6 @@ -import { Guild, GuildChannel, TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { GuildInvite, isGuildInvite, resolveInvite, verboseUserMention } from "../../../utils"; +import { isStaffPreFilter, sendErrorMessage } from "../../../pluginUtils"; import { botControlCmd } from "../types"; -import { isEligible } from "../functions/isEligible"; export const ChannelToServerCmd = botControlCmd({ trigger: ["channel_to_server", "channel2server"], @@ -19,13 +16,13 @@ export const ChannelToServerCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const channel = pluginData.client.channels.cache.get(args.channelId); if (!channel) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Channel not found in cache!"); + sendErrorMessage(pluginData, msg.channel, "Channel not found in cache!"); return; } - const channelName = channel.isVoice() ? channel.name : `#${(channel as TextChannel).name}`; + const channelName = channel.isVoiceBased() ? channel.name : `#${"name" in channel ? channel.name : channel.id}`; - const guild: Guild | null = (channel as GuildChannel).guild ?? null; + const guild = "guild" in channel ? channel.guild : null; const guildInfo = guild ? `${guild.name} (\`${guild.id}\`)` : "Not a server"; msg.channel.send(`**Channel:** ${channelName} (\`${channel.type}\`) (<#${channel.id}>)\n**Server:** ${guildInfo}`); diff --git a/backend/src/plugins/BotControl/commands/DisallowServerCmd.ts b/backend/src/plugins/BotControl/commands/DisallowServerCmd.ts index c7067341..06bc1b14 100644 --- a/backend/src/plugins/BotControl/commands/DisallowServerCmd.ts +++ b/backend/src/plugins/BotControl/commands/DisallowServerCmd.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { noop } from "../../../utils"; @@ -18,7 +18,7 @@ export const DisallowServerCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const existing = await pluginData.state.allowedGuilds.find(args.guildId); if (!existing) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "That server is not allowed in the first place!"); + sendErrorMessage(pluginData, msg.channel, "That server is not allowed in the first place!"); return; } @@ -27,6 +27,6 @@ export const DisallowServerCmd = botControlCmd({ .get(args.guildId as Snowflake) ?.leave() .catch(noop); - sendSuccessMessage(pluginData, msg.channel as TextChannel, "Server removed!"); + sendSuccessMessage(pluginData, msg.channel, "Server removed!"); }, }); diff --git a/backend/src/plugins/BotControl/commands/EligibleCmd.ts b/backend/src/plugins/BotControl/commands/EligibleCmd.ts index 0be29978..face42a8 100644 --- a/backend/src/plugins/BotControl/commands/EligibleCmd.ts +++ b/backend/src/plugins/BotControl/commands/EligibleCmd.ts @@ -1,9 +1,8 @@ -import { Guild, TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { GuildInvite, isGuildInvite, resolveInvite, verboseUserMention } from "../../../utils"; -import { botControlCmd } from "../types"; +import { isGuildInvite, resolveInvite } from "../../../utils"; import { isEligible } from "../functions/isEligible"; +import { botControlCmd } from "../types"; export const EligibleCmd = botControlCmd({ trigger: ["eligible", "is_eligible", "iseligible"], @@ -17,17 +16,17 @@ export const EligibleCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const invite = await resolveInvite(pluginData.client, args.inviteCode, true); if (!invite || !isGuildInvite(invite)) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not resolve invite"); + sendErrorMessage(pluginData, msg.channel, "Could not resolve invite"); return; } const { result, explanation } = await isEligible(pluginData, args.user, invite); if (result) { - sendSuccessMessage(pluginData, msg.channel as TextChannel, `Server is eligible: ${explanation}`); + sendSuccessMessage(pluginData, msg.channel, `Server is eligible: ${explanation}`); return; } - sendErrorMessage(pluginData, msg.channel as TextChannel, `Server is **NOT** eligible: ${explanation}`); + sendErrorMessage(pluginData, msg.channel, `Server is **NOT** eligible: ${explanation}`); }, }); diff --git a/backend/src/plugins/BotControl/commands/LeaveServerCmd.ts b/backend/src/plugins/BotControl/commands/LeaveServerCmd.ts index 20d0b14b..14aaf833 100644 --- a/backend/src/plugins/BotControl/commands/LeaveServerCmd.ts +++ b/backend/src/plugins/BotControl/commands/LeaveServerCmd.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { botControlCmd } from "../types"; @@ -16,7 +16,7 @@ export const LeaveServerCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { if (!pluginData.client.guilds.cache.has(args.guildId as Snowflake)) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "I am not in that guild"); + sendErrorMessage(pluginData, msg.channel, "I am not in that guild"); return; } @@ -26,10 +26,10 @@ export const LeaveServerCmd = botControlCmd({ try { await pluginData.client.guilds.cache.get(args.guildId as Snowflake)?.leave(); } catch (e) { - sendErrorMessage(pluginData, msg.channel as TextChannel, `Failed to leave guild: ${e.message}`); + sendErrorMessage(pluginData, msg.channel, `Failed to leave guild: ${e.message}`); return; } - sendSuccessMessage(pluginData, msg.channel as TextChannel, `Left guild **${guildName}**`); + sendSuccessMessage(pluginData, msg.channel, `Left guild **${guildName}**`); }, }); diff --git a/backend/src/plugins/BotControl/commands/ListDashboardPermsCmd.ts b/backend/src/plugins/BotControl/commands/ListDashboardPermsCmd.ts index 590522f1..6513276c 100644 --- a/backend/src/plugins/BotControl/commands/ListDashboardPermsCmd.ts +++ b/backend/src/plugins/BotControl/commands/ListDashboardPermsCmd.ts @@ -1,8 +1,7 @@ -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { AllowedGuild } from "../../../data/entities/AllowedGuild"; import { ApiPermissionAssignment } from "../../../data/entities/ApiPermissionAssignment"; -import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; +import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveUser } from "../../../utils"; import { botControlCmd } from "../types"; @@ -17,7 +16,7 @@ export const ListDashboardPermsCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { if (!args.user && !args.guildId) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Must specify at least guildId, user, or both."); + sendErrorMessage(pluginData, msg.channel, "Must specify at least guildId, user, or both."); return; } @@ -25,7 +24,7 @@ export const ListDashboardPermsCmd = botControlCmd({ if (args.guildId) { guild = await pluginData.state.allowedGuilds.find(args.guildId); if (!guild) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin"); + sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin"); return; } } @@ -34,7 +33,7 @@ export const ListDashboardPermsCmd = botControlCmd({ if (args.user) { existingUserAssignment = await pluginData.state.apiPermissionAssignments.getByUserId(args.user.id); if (existingUserAssignment.length === 0) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "The user has no assigned permissions."); + sendErrorMessage(pluginData, msg.channel, "The user has no assigned permissions."); return; } } @@ -57,7 +56,7 @@ export const ListDashboardPermsCmd = botControlCmd({ if (finalMessage === "") { sendErrorMessage( pluginData, - msg.channel as TextChannel, + msg.channel, `The user ${userInfo} has no assigned permissions on the specified server.`, ); return; @@ -68,11 +67,7 @@ export const ListDashboardPermsCmd = botControlCmd({ const existingGuildAssignment = await pluginData.state.apiPermissionAssignments.getByGuildId(guild.id); if (existingGuildAssignment.length === 0) { - sendErrorMessage( - pluginData, - msg.channel as TextChannel, - `The server ${guildInfo} has no assigned permissions.`, - ); + sendErrorMessage(pluginData, msg.channel, `The server ${guildInfo} has no assigned permissions.`); return; } @@ -83,6 +78,6 @@ export const ListDashboardPermsCmd = botControlCmd({ } } - await sendSuccessMessage(pluginData, msg.channel as TextChannel, finalMessage.trim(), {}); + await sendSuccessMessage(pluginData, msg.channel, finalMessage.trim(), {}); }, }); diff --git a/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts b/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts index 90fee6ff..2c53d27b 100644 --- a/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts +++ b/backend/src/plugins/BotControl/commands/ListDashboardUsersCmd.ts @@ -1,6 +1,5 @@ -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; +import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveUser } from "../../../utils"; import { botControlCmd } from "../types"; @@ -15,7 +14,7 @@ export const ListDashboardUsersCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const guild = await pluginData.state.allowedGuilds.find(args.guildId); if (!guild) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin"); + sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin"); return; } @@ -33,7 +32,7 @@ export const ListDashboardUsersCmd = botControlCmd({ sendSuccessMessage( pluginData, - msg.channel as TextChannel, + msg.channel, `The following users have dashboard access for **${guild.name}**:\n\n${userNameList.join("\n")}`, {}, ); diff --git a/backend/src/plugins/BotControl/commands/ProfilerDataCmd.ts b/backend/src/plugins/BotControl/commands/ProfilerDataCmd.ts index 1ddc9dc6..3b5a177f 100644 --- a/backend/src/plugins/BotControl/commands/ProfilerDataCmd.ts +++ b/backend/src/plugins/BotControl/commands/ProfilerDataCmd.ts @@ -1,10 +1,9 @@ -import { TextChannel } from "discord.js"; -import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { getBaseUrl, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { createChunkedMessage, formatNumber, resolveInvite, sorter, verboseUserMention } from "../../../utils"; -import { botControlCmd } from "../types"; -import { GuildArchives } from "../../../data/GuildArchives"; import moment from "moment-timezone"; +import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { GuildArchives } from "../../../data/GuildArchives"; +import { getBaseUrl } from "../../../pluginUtils"; +import { sorter } from "../../../utils"; +import { botControlCmd } from "../types"; const sortProps = { totalTime: "TOTAL TIME", diff --git a/backend/src/plugins/BotControl/commands/RateLimitPerformanceCmd.ts b/backend/src/plugins/BotControl/commands/RateLimitPerformanceCmd.ts index ac355689..c97b6c55 100644 --- a/backend/src/plugins/BotControl/commands/RateLimitPerformanceCmd.ts +++ b/backend/src/plugins/BotControl/commands/RateLimitPerformanceCmd.ts @@ -1,9 +1,8 @@ -import { botControlCmd } from "../types"; -import { getRateLimitStats } from "../../../rateLimitStats"; import moment from "moment-timezone"; import { GuildArchives } from "../../../data/GuildArchives"; -import { getBaseUrl, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { TextChannel } from "discord.js"; +import { getBaseUrl, sendSuccessMessage } from "../../../pluginUtils"; +import { getRateLimitStats } from "../../../rateLimitStats"; +import { botControlCmd } from "../types"; export const RateLimitPerformanceCmd = botControlCmd({ trigger: ["rate_limit_performance"], @@ -14,7 +13,7 @@ export const RateLimitPerformanceCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const logItems = getRateLimitStats(); if (logItems.length === 0) { - sendSuccessMessage(pluginData, msg.channel as TextChannel, `No rate limits hit`); + sendSuccessMessage(pluginData, msg.channel, `No rate limits hit`); return; } @@ -25,7 +24,7 @@ export const RateLimitPerformanceCmd = botControlCmd({ if (item.data.global) items.push("GLOBAL"); items.push(item.data.method.toUpperCase()); items.push(item.data.route); - items.push(`stalled for ${item.data.timeout}ms`); + items.push(`stalled for ${item.data.timeToReset}ms`); items.push(`(max requests ${item.data.limit})`); return items.join(" "); }); diff --git a/backend/src/plugins/BotControl/commands/ReloadGlobalPluginsCmd.ts b/backend/src/plugins/BotControl/commands/ReloadGlobalPluginsCmd.ts index 0cd4ec0b..4f59f8a2 100644 --- a/backend/src/plugins/BotControl/commands/ReloadGlobalPluginsCmd.ts +++ b/backend/src/plugins/BotControl/commands/ReloadGlobalPluginsCmd.ts @@ -1,5 +1,4 @@ -import { TextChannel } from "discord.js"; -import { isStaffPreFilter } from "../../../pluginUtils"; +import { isStaffPreFilter, sendErrorMessage } from "../../../pluginUtils"; import { getActiveReload, setActiveReload } from "../activeReload"; import { botControlCmd } from "../types"; @@ -13,7 +12,13 @@ export const ReloadGlobalPluginsCmd = botControlCmd({ async run({ pluginData, message }) { if (getActiveReload()) return; - setActiveReload((message.channel as TextChannel).guild?.id, message.channel.id); + const guildId = "guild" in message.channel ? message.channel.guild.id : null; + if (!guildId) { + sendErrorMessage(pluginData, message.channel, "This command can only be used in a server"); + return; + } + + setActiveReload(guildId, message.channel.id); await message.channel.send("Reloading global plugins..."); pluginData.getKnubInstance().reloadGlobalContext(); diff --git a/backend/src/plugins/BotControl/commands/ReloadServerCmd.ts b/backend/src/plugins/BotControl/commands/ReloadServerCmd.ts index 645e4cf4..0440129b 100644 --- a/backend/src/plugins/BotControl/commands/ReloadServerCmd.ts +++ b/backend/src/plugins/BotControl/commands/ReloadServerCmd.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { botControlCmd } from "../types"; @@ -16,18 +16,18 @@ export const ReloadServerCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { if (!pluginData.client.guilds.cache.has(args.guildId as Snowflake)) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "I am not in that guild"); + sendErrorMessage(pluginData, msg.channel, "I am not in that guild"); return; } try { await pluginData.getKnubInstance().reloadGuild(args.guildId); } catch (e) { - sendErrorMessage(pluginData, msg.channel as TextChannel, `Failed to reload guild: ${e.message}`); + sendErrorMessage(pluginData, msg.channel, `Failed to reload guild: ${e.message}`); return; } const guild = await pluginData.client.guilds.fetch(args.guildId as Snowflake); - sendSuccessMessage(pluginData, msg.channel as TextChannel, `Reloaded guild **${guild?.name || "???"}**`); + sendSuccessMessage(pluginData, msg.channel, `Reloaded guild **${guild?.name || "???"}**`); }, }); diff --git a/backend/src/plugins/BotControl/commands/RemoveDashboardUserCmd.ts b/backend/src/plugins/BotControl/commands/RemoveDashboardUserCmd.ts index d5dd674b..c3f0c0d7 100644 --- a/backend/src/plugins/BotControl/commands/RemoveDashboardUserCmd.ts +++ b/backend/src/plugins/BotControl/commands/RemoveDashboardUserCmd.ts @@ -1,4 +1,3 @@ -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { botControlCmd } from "../types"; @@ -18,7 +17,7 @@ export const RemoveDashboardUserCmd = botControlCmd({ async run({ pluginData, message: msg, args }) { const guild = await pluginData.state.allowedGuilds.find(args.guildId); if (!guild) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Server is not using Zeppelin"); + sendErrorMessage(pluginData, msg.channel, "Server is not using Zeppelin"); return; } @@ -37,7 +36,7 @@ export const RemoveDashboardUserCmd = botControlCmd({ const userNameList = args.users.map((user) => `<@!${user.id}> (**${user.tag}**, \`${user.id}\`)`); sendSuccessMessage( pluginData, - msg.channel as TextChannel, + msg.channel, `The following users were removed from the dashboard for **${guild.name}**:\n\n${userNameList}`, ); }, diff --git a/backend/src/plugins/BotControl/commands/RestPerformanceCmd.ts b/backend/src/plugins/BotControl/commands/RestPerformanceCmd.ts index 84b1f40e..a32eb8dc 100644 --- a/backend/src/plugins/BotControl/commands/RestPerformanceCmd.ts +++ b/backend/src/plugins/BotControl/commands/RestPerformanceCmd.ts @@ -1,9 +1,7 @@ -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { createChunkedMessage, formatNumber, resolveInvite, sorter, verboseUserMention } from "../../../utils"; -import { botControlCmd } from "../types"; import { getTopRestCallStats } from "../../../restCallStats"; +import { createChunkedMessage } from "../../../utils"; +import { botControlCmd } from "../types"; const leadingPathRegex = /(?<=\().+\/backend\//g; @@ -22,6 +20,6 @@ export const RestPerformanceCmd = botControlCmd({ const cleanSource = callStats.source.replace(leadingPathRegex, ""); return `**${callStats.count} calls**\n${callStats.method.toUpperCase()} ${callStats.path}\n${cleanSource}`; }); - createChunkedMessage(msg.channel as TextChannel, `Top rest calls:\n\n${formatted.join("\n")}`); + createChunkedMessage(msg.channel, `Top rest calls:\n\n${formatted.join("\n")}`); }, }); diff --git a/backend/src/plugins/BotControl/commands/ServersCmd.ts b/backend/src/plugins/BotControl/commands/ServersCmd.ts index cee486d2..23146d21 100644 --- a/backend/src/plugins/BotControl/commands/ServersCmd.ts +++ b/backend/src/plugins/BotControl/commands/ServersCmd.ts @@ -1,4 +1,3 @@ -import { TextChannel } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isStaffPreFilter } from "../../../pluginUtils"; @@ -51,7 +50,7 @@ export const ServersCmd = botControlCmd({ const owner = getUser(pluginData.client, g.ownerId); return `\`${paddedId}\` **${g.name}** (${g.memberCount} members) (owner **${owner.tag}** \`${owner.id}\`)`; }); - createChunkedMessage(msg.channel as TextChannel, lines.join("\n")); + createChunkedMessage(msg.channel, lines.join("\n")); } else { msg.channel.send("No servers matched the filters"); } diff --git a/backend/src/plugins/BotControl/functions/isEligible.ts b/backend/src/plugins/BotControl/functions/isEligible.ts index 164430f3..ba4dd31f 100644 --- a/backend/src/plugins/BotControl/functions/isEligible.ts +++ b/backend/src/plugins/BotControl/functions/isEligible.ts @@ -1,7 +1,7 @@ import { User } from "discord.js"; -import { BotControlPluginType } from "../types"; import { GlobalPluginData } from "knub"; import { GuildInvite } from "../../../utils"; +import { BotControlPluginType } from "../types"; const REQUIRED_MEMBER_COUNT = 5000; diff --git a/backend/src/plugins/BotControl/types.ts b/backend/src/plugins/BotControl/types.ts index f2da13ef..6f4139f8 100644 --- a/backend/src/plugins/BotControl/types.ts +++ b/backend/src/plugins/BotControl/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGlobalCommand, typedGlobalEventListener } from "knub"; +import { BasePluginType, globalPluginEventListener, globalPluginMessageCommand } from "knub"; import { AllowedGuilds } from "../../data/AllowedGuilds"; import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments"; import { Configs } from "../../data/Configs"; @@ -26,5 +26,5 @@ export interface BotControlPluginType extends BasePluginType { }; } -export const botControlCmd = typedGlobalCommand(); -export const botControlEvt = typedGlobalEventListener(); +export const botControlCmd = globalPluginMessageCommand(); +export const botControlEvt = globalPluginEventListener(); diff --git a/backend/src/plugins/Cases/CasesPlugin.ts b/backend/src/plugins/Cases/CasesPlugin.ts index 4f011982..45f63c79 100644 --- a/backend/src/plugins/Cases/CasesPlugin.ts +++ b/backend/src/plugins/Cases/CasesPlugin.ts @@ -3,8 +3,9 @@ import { Case } from "../../data/entities/Case"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildCases } from "../../data/GuildCases"; import { GuildLogs } from "../../data/GuildLogs"; -import { mapToPublicFn } from "../../pluginUtils"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; +import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { createCase } from "./functions/createCase"; @@ -16,7 +17,11 @@ import { getRecentCasesByMod } from "./functions/getRecentCasesByMod"; import { getTotalCasesByMod } from "./functions/getTotalCasesByMod"; import { postCaseToCaseLogChannel } from "./functions/postToCaseLogChannel"; import { CaseArgs, CaseNoteArgs, CasesPluginType, ConfigSchema } from "./types"; -import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin"; + +// The `any` cast here is to prevent TypeScript from locking up from the circular dependency +function getLogsPlugin(): Promise { + return import("../Logs/LogsPlugin.js") as Promise; +} const defaultOptions = { config: { @@ -37,15 +42,11 @@ export const CasesPlugin = zeppelinGuildPlugin()({ description: trimPluginDescription(` This plugin contains basic configuration for cases created by other plugins `), + configSchema: ConfigSchema, }, - dependencies: async () => [ - TimeAndDatePlugin, - InternalPosterPlugin, - // The `as any` cast here is to prevent TypeScript from locking up from the circular dependency - ((await import("../Logs/LogsPlugin")) as any).LogsPlugin, - ], - configSchema: ConfigSchema, + dependencies: async () => [TimeAndDatePlugin, InternalPosterPlugin, (await getLogsPlugin()).LogsPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, public: { @@ -81,8 +82,10 @@ export const CasesPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - pluginData.state.logs = new GuildLogs(pluginData.guild.id); - pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id); - pluginData.state.cases = GuildCases.getGuildInstance(pluginData.guild.id); + const { state, guild } = pluginData; + + state.logs = new GuildLogs(pluginData.guild.id); + state.archives = GuildArchives.getGuildInstance(guild.id); + state.cases = GuildCases.getGuildInstance(guild.id); }, }); diff --git a/backend/src/plugins/Cases/functions/createCase.ts b/backend/src/plugins/Cases/functions/createCase.ts index 0a0925d7..ad5dd86f 100644 --- a/backend/src/plugins/Cases/functions/createCase.ts +++ b/backend/src/plugins/Cases/functions/createCase.ts @@ -1,4 +1,4 @@ -import type { Snowflake } from "discord-api-types/globals"; +import type { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { logger } from "../../../logger"; import { resolveUser } from "../../../utils"; diff --git a/backend/src/plugins/Cases/functions/getCaseEmbed.ts b/backend/src/plugins/Cases/functions/getCaseEmbed.ts index 5a1b2ab8..3ce1f590 100644 --- a/backend/src/plugins/Cases/functions/getCaseEmbed.ts +++ b/backend/src/plugins/Cases/functions/getCaseEmbed.ts @@ -1,4 +1,4 @@ -import { MessageEditOptions, MessageOptions, Util } from "discord.js"; +import { escapeCodeBlock, MessageCreateOptions, MessageEditOptions } from "discord.js"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; import { CaseTypes } from "../../../data/CaseTypes"; @@ -14,7 +14,7 @@ export async function getCaseEmbed( caseOrCaseId: Case | number, requestMemberId?: string, noOriginalCaseLink?: boolean, -): Promise { +): Promise { const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId)); if (!theCase) { throw new Error("Unknown case"); @@ -67,7 +67,7 @@ export async function getCaseEmbed( if (theCase.notes.length) { for (const note of theCase.notes) { const noteDate = moment.utc(note.created_at); - let noteBody = Util.escapeCodeBlock(note.body.trim()); + let noteBody = escapeCodeBlock(note.body.trim()); if (noteBody === "") { noteBody = emptyEmbedValue; } diff --git a/backend/src/plugins/Cases/functions/getCaseSummary.ts b/backend/src/plugins/Cases/functions/getCaseSummary.ts index 98841451..c4135e67 100644 --- a/backend/src/plugins/Cases/functions/getCaseSummary.ts +++ b/backend/src/plugins/Cases/functions/getCaseSummary.ts @@ -1,5 +1,5 @@ import { GuildPluginData } from "knub"; -import { splitMessageIntoChunks } from "knub/dist/helpers"; +import { splitMessageIntoChunks } from "knub/helpers"; import moment from "moment-timezone"; import { Case } from "../../../data/entities/Case"; import { convertDelayStringToMS, DAYS, DBDateFormat, disableLinkPreviews, messageLink } from "../../../utils"; diff --git a/backend/src/plugins/Cases/functions/postToCaseLogChannel.ts b/backend/src/plugins/Cases/functions/postToCaseLogChannel.ts index 1d363d60..efb1feb0 100644 --- a/backend/src/plugins/Cases/functions/postToCaseLogChannel.ts +++ b/backend/src/plugins/Cases/functions/postToCaseLogChannel.ts @@ -1,18 +1,18 @@ -import { FileOptions, MessageOptions, NewsChannel, Snowflake, TextChannel } from "discord.js"; +import { MessageCreateOptions, NewsChannel, RESTJSONErrorCodes, Snowflake, TextChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { Case } from "../../../data/entities/Case"; import { isDiscordAPIError } from "../../../utils"; +import { InternalPosterMessageResult } from "../../InternalPoster/functions/sendMessage"; +import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { CasesPluginType } from "../types"; import { getCaseEmbed } from "./getCaseEmbed"; import { resolveCaseId } from "./resolveCaseId"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"; -import { InternalPosterMessageResult } from "../../InternalPoster/functions/sendMessage"; export async function postToCaseLogChannel( pluginData: GuildPluginData, - content: MessageOptions, - file?: FileOptions[], + content: MessageCreateOptions, + files?: MessageCreateOptions["files"], ): Promise { const caseLogChannelId = pluginData.config.get().case_log_channel; if (!caseLogChannelId) return null; @@ -23,13 +23,16 @@ export async function postToCaseLogChannel( let result: InternalPosterMessageResult | null = null; try { - if (file != null) { - content.files = file; + if (files != null) { + content.files = files; } const poster = pluginData.getPlugin(InternalPosterPlugin); result = await poster.sendMessage(caseLogChannel, { ...content }); } catch (e) { - if (isDiscordAPIError(e) && (e.code === 50013 || e.code === 50001)) { + if ( + isDiscordAPIError(e) && + (e.code === RESTJSONErrorCodes.MissingPermissions || e.code === RESTJSONErrorCodes.MissingAccess) + ) { pluginData.getPlugin(LogsPlugin).logBotAlert({ body: `Missing permissions to post mod cases in <#${caseLogChannel.id}>`, }); @@ -57,10 +60,12 @@ export async function postCaseToCaseLogChannel( try { const poster = pluginData.getPlugin(InternalPosterPlugin); - const channel = pluginData.guild.channels.resolve(channelId as Snowflake) as TextChannel; - const message = await channel.messages.fetch(messageId); - if (message) { - await poster.editMessage(message, caseEmbed); + const channel = pluginData.guild.channels.resolve(channelId as Snowflake); + if (channel?.isTextBased()) { + const message = await channel.messages.fetch(messageId); + if (message) { + await poster.editMessage(message, caseEmbed); + } } return; } catch {} // tslint:disable-line:no-empty diff --git a/backend/src/plugins/Censor/CensorPlugin.ts b/backend/src/plugins/Censor/CensorPlugin.ts index d8b74477..61991e12 100644 --- a/backend/src/plugins/Censor/CensorPlugin.ts +++ b/backend/src/plugins/Censor/CensorPlugin.ts @@ -1,6 +1,7 @@ import { PluginOptions } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners"; import { trimPluginDescription } from "../../utils"; import { LogsPlugin } from "../Logs/LogsPlugin"; @@ -53,10 +54,11 @@ export const CensorPlugin = zeppelinGuildPlugin()({ For more advanced filtering, check out the Automod plugin! `), legacy: true, + configSchema: ConfigSchema, }, dependencies: () => [LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, beforeLoad(pluginData) { @@ -69,7 +71,7 @@ export const CensorPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - const { state, guild } = pluginData; + const { state } = pluginData; state.onMessageCreateFn = (msg) => onMessageCreate(pluginData, msg); state.savedMessages.events.on("create", state.onMessageCreateFn); @@ -79,9 +81,11 @@ export const CensorPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - discardRegExpRunner(`guild-${pluginData.guild.id}`); + const { state, guild } = pluginData; - pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn); - pluginData.state.savedMessages.events.off("update", pluginData.state.onMessageUpdateFn); + discardRegExpRunner(`guild-${guild.id}`); + + state.savedMessages.events.off("create", state.onMessageCreateFn); + state.savedMessages.events.off("update", state.onMessageUpdateFn); }, }); diff --git a/backend/src/plugins/Censor/util/applyFiltersToMsg.ts b/backend/src/plugins/Censor/util/applyFiltersToMsg.ts index 5e92a305..514ee67e 100644 --- a/backend/src/plugins/Censor/util/applyFiltersToMsg.ts +++ b/backend/src/plugins/Censor/util/applyFiltersToMsg.ts @@ -1,4 +1,4 @@ -import { Invite, MessageEmbed } from "discord.js"; +import { Embed, Invite } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; import { GuildPluginData } from "knub"; import cloneDeep from "lodash.clonedeep"; @@ -19,7 +19,7 @@ export async function applyFiltersToMsg( let messageContent = savedMessage.data.content || ""; if (savedMessage.data.attachments) messageContent += " " + JSON.stringify(savedMessage.data.attachments); if (savedMessage.data.embeds) { - const embeds = (savedMessage.data.embeds as MessageEmbed[]).map((e) => cloneDeep(e)); + const embeds = (savedMessage.data.embeds as Embed[]).map((e) => cloneDeep(e)); for (const embed of embeds) { if (embed.type === "video") { // Ignore video descriptions as they're not actually shown on the embed diff --git a/backend/src/plugins/Censor/util/censorMessage.ts b/backend/src/plugins/Censor/util/censorMessage.ts index 6375da79..5471ada4 100644 --- a/backend/src/plugins/Censor/util/censorMessage.ts +++ b/backend/src/plugins/Censor/util/censorMessage.ts @@ -1,11 +1,10 @@ -import { BaseGuildTextChannel, GuildTextBasedChannel, Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; import { resolveUser } from "../../../utils"; -import { CensorPluginType } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { CensorPluginType } from "../types"; export async function censorMessage( pluginData: GuildPluginData, @@ -15,8 +14,8 @@ export async function censorMessage( pluginData.state.serverLogs.ignoreLog(LogType.MESSAGE_DELETE, savedMessage.id); try { - const resolvedChannel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake) as TextChannel; - await resolvedChannel.messages.delete(savedMessage.id as Snowflake); + const resolvedChannel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake); + if (resolvedChannel?.isTextBased()) await resolvedChannel.messages.delete(savedMessage.id as Snowflake); } catch { return; } diff --git a/backend/src/plugins/ChannelArchiver/ChannelArchiverPlugin.ts b/backend/src/plugins/ChannelArchiver/ChannelArchiverPlugin.ts index 9ff38a84..b4467819 100644 --- a/backend/src/plugins/ChannelArchiver/ChannelArchiverPlugin.ts +++ b/backend/src/plugins/ChannelArchiver/ChannelArchiverPlugin.ts @@ -1,18 +1,21 @@ import * as t from "io-ts"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { ArchiveChannelCmd } from "./commands/ArchiveChannelCmd"; import { ChannelArchiverPluginType } from "./types"; +const ConfigSchema = t.type({}); + export const ChannelArchiverPlugin = zeppelinGuildPlugin()({ name: "channel_archiver", showInDocs: false, dependencies: () => [TimeAndDatePlugin], - configSchema: t.type({}), + configParser: makeIoTsConfigParser(ConfigSchema), // prettier-ignore - commands: [ + messageCommands: [ ArchiveChannelCmd, ], }); diff --git a/backend/src/plugins/ChannelArchiver/commands/ArchiveChannelCmd.ts b/backend/src/plugins/ChannelArchiver/commands/ArchiveChannelCmd.ts index 2fe94bb9..9b8b2239 100644 --- a/backend/src/plugins/ChannelArchiver/commands/ArchiveChannelCmd.ts +++ b/backend/src/plugins/ChannelArchiver/commands/ArchiveChannelCmd.ts @@ -1,4 +1,4 @@ -import { Collection, Message, Snowflake } from "discord.js"; +import { Snowflake } from "discord.js"; import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { isOwner, sendErrorMessage } from "../../../pluginUtils"; diff --git a/backend/src/plugins/ChannelArchiver/rehostAttachment.ts b/backend/src/plugins/ChannelArchiver/rehostAttachment.ts index 9f5c6423..8159ddb2 100644 --- a/backend/src/plugins/ChannelArchiver/rehostAttachment.ts +++ b/backend/src/plugins/ChannelArchiver/rehostAttachment.ts @@ -1,14 +1,11 @@ -import { MessageAttachment, MessageOptions, TextChannel, ThreadChannel } from "discord.js"; +import { Attachment, GuildTextBasedChannel, MessageCreateOptions } from "discord.js"; import fs from "fs"; import { downloadFile } from "../../utils"; const fsp = fs.promises; const MAX_ATTACHMENT_REHOST_SIZE = 1024 * 1024 * 8; -export async function rehostAttachment( - attachment: MessageAttachment, - targetChannel: TextChannel | ThreadChannel, -): Promise { +export async function rehostAttachment(attachment: Attachment, targetChannel: GuildTextBasedChannel): Promise { if (attachment.size > MAX_ATTACHMENT_REHOST_SIZE) { return "Attachment too big to rehost"; } @@ -21,7 +18,7 @@ export async function rehostAttachment( } try { - const content: MessageOptions = { + const content: MessageCreateOptions = { content: `Rehost of attachment ${attachment.id}`, files: [{ name: attachment.name ? attachment.name : undefined, attachment: await fsp.readFile(downloaded.path) }], }; diff --git a/backend/src/plugins/ChannelArchiver/types.ts b/backend/src/plugins/ChannelArchiver/types.ts index ba33834c..b7968b3a 100644 --- a/backend/src/plugins/ChannelArchiver/types.ts +++ b/backend/src/plugins/ChannelArchiver/types.ts @@ -1,7 +1,7 @@ -import { BasePluginType, typedGuildCommand } from "knub"; +import { BasePluginType, guildPluginMessageCommand } from "knub"; export interface ChannelArchiverPluginType extends BasePluginType { state: {}; } -export const channelArchiverCmd = typedGuildCommand(); +export const channelArchiverCmd = guildPluginMessageCommand(); diff --git a/backend/src/plugins/CompanionChannels/CompanionChannelsPlugin.ts b/backend/src/plugins/CompanionChannels/CompanionChannelsPlugin.ts index cadee696..bd05d800 100644 --- a/backend/src/plugins/CompanionChannels/CompanionChannelsPlugin.ts +++ b/backend/src/plugins/CompanionChannels/CompanionChannelsPlugin.ts @@ -1,5 +1,6 @@ import { CooldownManager } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -22,10 +23,11 @@ export const CompanionChannelsPlugin = zeppelinGuildPlugin [LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, events: [VoiceStateUpdateEvt], diff --git a/backend/src/plugins/CompanionChannels/functions/handleCompanionPermissions.ts b/backend/src/plugins/CompanionChannels/functions/handleCompanionPermissions.ts index ea09b548..840207dd 100644 --- a/backend/src/plugins/CompanionChannels/functions/handleCompanionPermissions.ts +++ b/backend/src/plugins/CompanionChannels/functions/handleCompanionPermissions.ts @@ -1,11 +1,11 @@ -import { Permissions, Snowflake, StageChannel, TextChannel, VoiceChannel } from "discord.js"; +import { PermissionsBitField, Snowflake, StageChannel, TextChannel, VoiceChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { LogType } from "../../../data/LogType"; import { isDiscordAPIError, MINUTES } from "../../../utils"; +import { filterObject } from "../../../utils/filterObject"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { CompanionChannelsPluginType, TCompanionChannelOpts } from "../types"; import { getCompanionChannelOptsForVoiceChannelId } from "./getCompanionChannelOptsForVoiceChannelId"; -import { filterObject } from "../../../utils/filterObject"; const ERROR_COOLDOWN_KEY = "errorCooldown"; const ERROR_COOLDOWN = 5 * MINUTES; @@ -64,7 +64,7 @@ export async function handleCompanionPermissions( const channel = pluginData.guild.channels.cache.get(channelId as Snowflake); if (!channel || !(channel instanceof TextChannel)) continue; pluginData.state.serverLogs.ignoreLog(LogType.CHANNEL_UPDATE, channelId, 3 * 1000); - const fullSerialized = new Permissions(BigInt(permissions)).serialize(); + const fullSerialized = new PermissionsBitField(BigInt(permissions)).serialize(); const onlyAllowed = filterObject(fullSerialized, (v) => v === true); await channel.permissionOverwrites.create(userId, onlyAllowed, { reason: `Companion Channel for ${voiceChannel!.id} | User Joined`, diff --git a/backend/src/plugins/CompanionChannels/types.ts b/backend/src/plugins/CompanionChannels/types.ts index 070295af..46a8a45f 100644 --- a/backend/src/plugins/CompanionChannels/types.ts +++ b/backend/src/plugins/CompanionChannels/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, CooldownManager, typedGuildEventListener } from "knub"; +import { BasePluginType, CooldownManager, guildPluginEventListener } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { tNullable } from "../../utils"; @@ -29,4 +29,4 @@ export interface CompanionChannelsPluginType extends BasePluginType { }; } -export const companionChannelsEvt = typedGuildEventListener(); +export const companionChannelsEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/ContextMenus/ContextMenuPlugin.ts b/backend/src/plugins/ContextMenus/ContextMenuPlugin.ts index 4443bf37..c41e8c09 100644 --- a/backend/src/plugins/ContextMenus/ContextMenuPlugin.ts +++ b/backend/src/plugins/ContextMenus/ContextMenuPlugin.ts @@ -1,12 +1,13 @@ import { PluginOptions } from "knub"; import { GuildContextMenuLinks } from "../../data/GuildContextMenuLinks"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { MutesPlugin } from "../Mutes/MutesPlugin"; +import { UtilityPlugin } from "../Utility/UtilityPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { ContextClickedEvt } from "./events/ContextClickedEvt"; import { ConfigSchema, ContextMenuPluginType } from "./types"; import { loadAllCommands } from "./utils/loadAllCommands"; -import { UtilityPlugin } from "../Utility/UtilityPlugin"; const defaultOptions: PluginOptions = { config: { @@ -35,8 +36,8 @@ export const ContextMenuPlugin = zeppelinGuildPlugin()({ name: "context_menu", showInDocs: false, - configSchema: ConfigSchema, dependencies: () => [MutesPlugin, LogsPlugin, UtilityPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore diff --git a/backend/src/plugins/ContextMenus/actions/clean.ts b/backend/src/plugins/ContextMenus/actions/clean.ts index 1831f7d8..7fd124e9 100644 --- a/backend/src/plugins/ContextMenus/actions/clean.ts +++ b/backend/src/plugins/ContextMenus/actions/clean.ts @@ -1,6 +1,5 @@ -import { ContextMenuInteraction, TextChannel } from "discord.js"; +import { ContextMenuCommandInteraction, TextChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogType } from "../../../data/LogType"; import { UtilityPlugin } from "../../../plugins/Utility/UtilityPlugin"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; import { LogsPlugin } from "../../Logs/LogsPlugin"; @@ -9,7 +8,7 @@ import { ContextMenuPluginType } from "../types"; export async function cleanAction( pluginData: GuildPluginData, amount: number, - interaction: ContextMenuInteraction, + interaction: ContextMenuCommandInteraction, ) { interaction.deferReply({ ephemeral: true }); const executingMember = await pluginData.guild.members.fetch(interaction.user.id); diff --git a/backend/src/plugins/ContextMenus/actions/mute.ts b/backend/src/plugins/ContextMenus/actions/mute.ts index 5735337a..4dfc682e 100644 --- a/backend/src/plugins/ContextMenus/actions/mute.ts +++ b/backend/src/plugins/ContextMenus/actions/mute.ts @@ -1,9 +1,8 @@ -import { ContextMenuInteraction } from "discord.js"; +import { ContextMenuCommandInteraction } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import { ModActionsPlugin } from "src/plugins/ModActions/ModActionsPlugin"; import { canActOn } from "src/pluginUtils"; -import { LogType } from "../../../data/LogType"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; import { convertDelayStringToMS } from "../../../utils"; import { CaseArgs } from "../../Cases/types"; @@ -14,7 +13,7 @@ import { ContextMenuPluginType } from "../types"; export async function muteAction( pluginData: GuildPluginData, duration: string | undefined, - interaction: ContextMenuInteraction, + interaction: ContextMenuCommandInteraction, ) { interaction.deferReply({ ephemeral: true }); const executingMember = await pluginData.guild.members.fetch(interaction.user.id); diff --git a/backend/src/plugins/ContextMenus/actions/userInfo.ts b/backend/src/plugins/ContextMenus/actions/userInfo.ts index c4dd4a34..5818d57c 100644 --- a/backend/src/plugins/ContextMenus/actions/userInfo.ts +++ b/backend/src/plugins/ContextMenus/actions/userInfo.ts @@ -1,11 +1,11 @@ -import { ContextMenuInteraction } from "discord.js"; +import { ContextMenuCommandInteraction } from "discord.js"; import { GuildPluginData } from "knub"; import { UtilityPlugin } from "../../../plugins/Utility/UtilityPlugin"; import { ContextMenuPluginType } from "../types"; export async function userInfoAction( pluginData: GuildPluginData, - interaction: ContextMenuInteraction, + interaction: ContextMenuCommandInteraction, ) { interaction.deferReply({ ephemeral: true }); const executingMember = await pluginData.guild.members.fetch(interaction.user.id); diff --git a/backend/src/plugins/ContextMenus/events/ContextClickedEvt.ts b/backend/src/plugins/ContextMenus/events/ContextClickedEvt.ts index 62675e41..98e6ab1f 100644 --- a/backend/src/plugins/ContextMenus/events/ContextClickedEvt.ts +++ b/backend/src/plugins/ContextMenus/events/ContextClickedEvt.ts @@ -1,4 +1,3 @@ -import { ContextMenuInteraction } from "discord.js"; import { contextMenuEvt } from "../types"; import { routeContextAction } from "../utils/contextRouter"; @@ -6,8 +5,8 @@ export const ContextClickedEvt = contextMenuEvt({ event: "interactionCreate", async listener(meta) { - if (!meta.args.interaction.isContextMenu) return; - const inter = meta.args.interaction as ContextMenuInteraction; + if (!meta.args.interaction.isContextMenuCommand()) return; + const inter = meta.args.interaction; await routeContextAction(meta.pluginData, inter); }, }); diff --git a/backend/src/plugins/ContextMenus/types.ts b/backend/src/plugins/ContextMenus/types.ts index e73d686d..02c4a29c 100644 --- a/backend/src/plugins/ContextMenus/types.ts +++ b/backend/src/plugins/ContextMenus/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener } from "knub"; import { GuildContextMenuLinks } from "../../data/GuildContextMenuLinks"; export const ConfigSchema = t.type({ @@ -22,4 +22,4 @@ export interface ContextMenuPluginType extends BasePluginType { }; } -export const contextMenuEvt = typedGuildEventListener(); +export const contextMenuEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/ContextMenus/utils/contextRouter.ts b/backend/src/plugins/ContextMenus/utils/contextRouter.ts index 1bda0ff8..18b7b064 100644 --- a/backend/src/plugins/ContextMenus/utils/contextRouter.ts +++ b/backend/src/plugins/ContextMenus/utils/contextRouter.ts @@ -1,11 +1,11 @@ -import { ContextMenuInteraction } from "discord.js"; +import { ContextMenuCommandInteraction } from "discord.js"; import { GuildPluginData } from "knub"; import { ContextMenuPluginType } from "../types"; import { hardcodedActions } from "./hardcodedContextOptions"; export async function routeContextAction( pluginData: GuildPluginData, - interaction: ContextMenuInteraction, + interaction: ContextMenuCommandInteraction, ) { const contextLink = await pluginData.state.contextMenuLinks.get(interaction.commandId); if (!contextLink) return; diff --git a/backend/src/plugins/ContextMenus/utils/loadAllCommands.ts b/backend/src/plugins/ContextMenus/utils/loadAllCommands.ts index 8272d8b3..97d73dfb 100644 --- a/backend/src/plugins/ContextMenus/utils/loadAllCommands.ts +++ b/backend/src/plugins/ContextMenus/utils/loadAllCommands.ts @@ -1,7 +1,6 @@ -import { ApplicationCommandData, Constants } from "discord.js"; -import { LogType } from "../../../data/LogType"; -import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin"; +import { ApplicationCommandData, ApplicationCommandType } from "discord.js"; import { GuildPluginData } from "knub"; +import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin"; import { ContextMenuPluginType } from "../types"; import { hardcodedContext } from "./hardcodedContextOptions"; @@ -14,9 +13,7 @@ export async function loadAllCommands(pluginData: GuildPluginData = { ], }; -const configPreprocessor: ConfigPreprocessorFn = (options) => { - for (const [counterName, counter] of Object.entries(options.config?.counters || {})) { - counter.name = counterName; - counter.per_user = counter.per_user ?? false; - counter.per_channel = counter.per_channel ?? false; - counter.initial_value = counter.initial_value ?? 0; - counter.triggers = counter.triggers || {}; - - if (Object.values(counter.triggers).length > MAX_TRIGGERS_PER_COUNTER) { - throw new StrictValidationError([`You can only have at most ${MAX_TRIGGERS_PER_COUNTER} triggers per counter`]); - } - - // Normalize triggers - for (const [triggerName, trigger] of Object.entries(counter.triggers)) { - const triggerObj: Partial = typeof trigger === "string" ? { condition: trigger } : trigger; - - triggerObj.name = triggerName; - const parsedCondition = parseCounterConditionString(triggerObj.condition || ""); - if (!parsedCondition) { - throw new StrictValidationError([ - `Invalid comparison in counter trigger ${counterName}/${triggerName}: "${triggerObj.condition}"`, - ]); - } - - triggerObj.condition = buildCounterConditionString(parsedCondition[0], parsedCondition[1]); - triggerObj.reverse_condition = - triggerObj.reverse_condition || - buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]); - - counter.triggers[triggerName] = triggerObj as TTrigger; - } - } - - if (Object.values(options.config?.counters || {}).length > MAX_COUNTERS) { - throw new StrictValidationError([`You can only have at most ${MAX_COUNTERS} counters`]); - } - - return options; -}; - /** * The Counters plugin keeps track of simple integer values that are tied to a user, channel, both, or neither — "counters". * These values can be changed using the functions in the plugin's public interface. @@ -113,11 +73,55 @@ export const CountersPlugin = zeppelinGuildPlugin()({ description: "Keep track of per-user, per-channel, or global numbers and trigger specific actions based on this number", configurationGuide: "See Counters setup guide", + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, defaultOptions, - configPreprocessor, + // TODO: Separate input and output types + configParser: (input) => { + for (const [counterName, counter] of Object.entries((input as any).counters || {})) { + counter.name = counterName; + counter.per_user = counter.per_user ?? false; + counter.per_channel = counter.per_channel ?? false; + counter.initial_value = counter.initial_value ?? 0; + counter.triggers = counter.triggers || {}; + + if (Object.values(counter.triggers).length > MAX_TRIGGERS_PER_COUNTER) { + throw new StrictValidationError([`You can only have at most ${MAX_TRIGGERS_PER_COUNTER} triggers per counter`]); + } + + // Normalize triggers + for (const [triggerName, trigger] of Object.entries(counter.triggers)) { + const triggerObj = (typeof trigger === "string" ? { condition: trigger } : trigger) as Partial; + + triggerObj.name = triggerName; + const parsedCondition = parseCounterConditionString(triggerObj.condition || ""); + if (!parsedCondition) { + throw new StrictValidationError([ + `Invalid comparison in counter trigger ${counterName}/${triggerName}: "${triggerObj.condition}"`, + ]); + } + + triggerObj.condition = buildCounterConditionString(parsedCondition[0], parsedCondition[1]); + triggerObj.reverse_condition = + triggerObj.reverse_condition || + buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]); + + counter.triggers[triggerName] = triggerObj as TTrigger; + } + } + + if (Object.values((input as any).counters || {}).length > MAX_COUNTERS) { + throw new StrictValidationError([`You can only have at most ${MAX_COUNTERS} counters`]); + } + + const error = validate(ConfigSchema, input); + if (error) { + throw error; + } + + return input as t.TypeOf; + }, public: { counterExists: mapToPublicFn(counterExists), @@ -136,7 +140,7 @@ export const CountersPlugin = zeppelinGuildPlugin()({ }, // prettier-ignore - commands: [ + messageCommands: [ CountersListCmd, ViewCounterCmd, AddCounterCmd, @@ -146,32 +150,30 @@ export const CountersPlugin = zeppelinGuildPlugin()({ ], async beforeLoad(pluginData) { - pluginData.state.counters = new GuildCounters(pluginData.guild.id); - pluginData.state.events = new EventEmitter(); - pluginData.state.counterTriggersByCounterId = new Map(); + const { state, guild } = pluginData; + + state.counters = new GuildCounters(guild.id); + state.events = new EventEmitter(); + state.counterTriggersByCounterId = new Map(); const activeTriggerIds: number[] = []; // Initialize and store the IDs of each of the counters internally - pluginData.state.counterIds = {}; + state.counterIds = {}; const config = pluginData.config.get(); for (const counter of Object.values(config.counters)) { - const dbCounter = await pluginData.state.counters.findOrCreateCounter( - counter.name, - counter.per_channel, - counter.per_user, - ); - pluginData.state.counterIds[counter.name] = dbCounter.id; + const dbCounter = await state.counters.findOrCreateCounter(counter.name, counter.per_channel, counter.per_user); + state.counterIds[counter.name] = dbCounter.id; const thisCounterTriggers: CounterTrigger[] = []; - pluginData.state.counterTriggersByCounterId.set(dbCounter.id, thisCounterTriggers); + state.counterTriggersByCounterId.set(dbCounter.id, thisCounterTriggers); // Initialize triggers for (const trigger of Object.values(counter.triggers)) { const theTrigger = trigger as TTrigger; const parsedCondition = parseCounterConditionString(theTrigger.condition)!; const parsedReverseCondition = parseCounterConditionString(theTrigger.reverse_condition)!; - const counterTrigger = await pluginData.state.counters.initCounterTrigger( + const counterTrigger = await state.counters.initCounterTrigger( dbCounter.id, theTrigger.name, parsedCondition[0], @@ -185,17 +187,19 @@ export const CountersPlugin = zeppelinGuildPlugin()({ } // Mark old/unused counters to be deleted later - await pluginData.state.counters.markUnusedCountersToBeDeleted([...Object.values(pluginData.state.counterIds)]); + await state.counters.markUnusedCountersToBeDeleted([...Object.values(state.counterIds)]); // Mark old/unused triggers to be deleted later - await pluginData.state.counters.markUnusedTriggersToBeDeleted(activeTriggerIds); + await state.counters.markUnusedTriggersToBeDeleted(activeTriggerIds); }, async afterLoad(pluginData) { + const { state } = pluginData; + const config = pluginData.config.get(); // Start decay timers - pluginData.state.decayTimers = []; + state.decayTimers = []; for (const [counterName, counter] of Object.entries(config.counters)) { if (!counter.decay) { continue; @@ -207,7 +211,7 @@ export const CountersPlugin = zeppelinGuildPlugin()({ continue; } - pluginData.state.decayTimers.push( + state.decayTimers.push( setInterval(() => { decayCounter(pluginData, counterName, decayPeriodMs, decay.amount); }, DECAY_APPLY_INTERVAL), @@ -216,12 +220,14 @@ export const CountersPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - if (pluginData.state.decayTimers) { - for (const interval of pluginData.state.decayTimers) { + const { state } = pluginData; + + if (state.decayTimers) { + for (const interval of state.decayTimers) { clearInterval(interval); } } - pluginData.state.events.removeAllListeners(); + state.events.removeAllListeners(); }, }); diff --git a/backend/src/plugins/Counters/commands/AddCounterCmd.ts b/backend/src/plugins/Counters/commands/AddCounterCmd.ts index c045469f..ac8c3698 100644 --- a/backend/src/plugins/Counters/commands/AddCounterCmd.ts +++ b/backend/src/plugins/Counters/commands/AddCounterCmd.ts @@ -1,13 +1,13 @@ import { Snowflake, TextChannel } from "discord.js"; -import { typedGuildCommand } from "knub"; -import { waitForReply } from "knub/dist/helpers"; +import { guildPluginMessageCommand } from "knub"; +import { waitForReply } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { resolveUser, UnknownUser } from "../../../utils"; import { changeCounterValue } from "../functions/changeCounterValue"; import { CountersPluginType } from "../types"; -export const AddCounterCmd = typedGuildCommand()({ +export const AddCounterCmd = guildPluginMessageCommand()({ trigger: ["counters add", "counter add", "addcounter"], permission: "can_edit", diff --git a/backend/src/plugins/Counters/commands/CountersListCmd.ts b/backend/src/plugins/Counters/commands/CountersListCmd.ts index f725ac71..58b5712b 100644 --- a/backend/src/plugins/Counters/commands/CountersListCmd.ts +++ b/backend/src/plugins/Counters/commands/CountersListCmd.ts @@ -1,10 +1,10 @@ -import { typedGuildCommand } from "knub"; +import { guildPluginMessageCommand } from "knub"; import { sendErrorMessage } from "../../../pluginUtils"; import { trimMultilineString, ucfirst } from "../../../utils"; import { getGuildPrefix } from "../../../utils/getGuildPrefix"; import { CountersPluginType } from "../types"; -export const CountersListCmd = typedGuildCommand()({ +export const CountersListCmd = guildPluginMessageCommand()({ trigger: ["counters list", "counter list", "counters"], permission: "can_view", @@ -44,7 +44,7 @@ export const CountersListCmd = typedGuildCommand()({ message.channel.send( trimMultilineString(` ${counterLines.join("\n\n")} - + ${hintLines.join("\n")} `), ); diff --git a/backend/src/plugins/Counters/commands/ResetAllCounterValuesCmd.ts b/backend/src/plugins/Counters/commands/ResetAllCounterValuesCmd.ts index af8dc9ad..3a44ecaa 100644 --- a/backend/src/plugins/Counters/commands/ResetAllCounterValuesCmd.ts +++ b/backend/src/plugins/Counters/commands/ResetAllCounterValuesCmd.ts @@ -1,11 +1,11 @@ -import { typedGuildCommand } from "knub"; +import { guildPluginMessageCommand } from "knub"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { confirm, noop, trimMultilineString } from "../../../utils"; import { resetAllCounterValues } from "../functions/resetAllCounterValues"; import { CountersPluginType } from "../types"; -export const ResetAllCounterValuesCmd = typedGuildCommand()({ +export const ResetAllCounterValuesCmd = guildPluginMessageCommand()({ trigger: ["counters reset_all"], permission: "can_reset_all", diff --git a/backend/src/plugins/Counters/commands/ResetCounterCmd.ts b/backend/src/plugins/Counters/commands/ResetCounterCmd.ts index 2c92d329..21191abf 100644 --- a/backend/src/plugins/Counters/commands/ResetCounterCmd.ts +++ b/backend/src/plugins/Counters/commands/ResetCounterCmd.ts @@ -1,13 +1,13 @@ import { Snowflake, TextChannel } from "discord.js"; -import { typedGuildCommand } from "knub"; -import { waitForReply } from "knub/dist/helpers"; +import { guildPluginMessageCommand } from "knub"; +import { waitForReply } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { resolveUser, UnknownUser } from "../../../utils"; import { setCounterValue } from "../functions/setCounterValue"; import { CountersPluginType } from "../types"; -export const ResetCounterCmd = typedGuildCommand()({ +export const ResetCounterCmd = guildPluginMessageCommand()({ trigger: ["counters reset", "counter reset", "resetcounter"], permission: "can_edit", diff --git a/backend/src/plugins/Counters/commands/SetCounterCmd.ts b/backend/src/plugins/Counters/commands/SetCounterCmd.ts index 5052c23c..98ffcc9b 100644 --- a/backend/src/plugins/Counters/commands/SetCounterCmd.ts +++ b/backend/src/plugins/Counters/commands/SetCounterCmd.ts @@ -1,13 +1,13 @@ import { Snowflake, TextChannel } from "discord.js"; -import { typedGuildCommand } from "knub"; -import { waitForReply } from "knub/dist/helpers"; +import { guildPluginMessageCommand } from "knub"; +import { waitForReply } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { resolveUser, UnknownUser } from "../../../utils"; import { setCounterValue } from "../functions/setCounterValue"; import { CountersPluginType } from "../types"; -export const SetCounterCmd = typedGuildCommand()({ +export const SetCounterCmd = guildPluginMessageCommand()({ trigger: ["counters set", "counter set", "setcounter"], permission: "can_edit", diff --git a/backend/src/plugins/Counters/commands/ViewCounterCmd.ts b/backend/src/plugins/Counters/commands/ViewCounterCmd.ts index 9a4cbca5..d2715c0c 100644 --- a/backend/src/plugins/Counters/commands/ViewCounterCmd.ts +++ b/backend/src/plugins/Counters/commands/ViewCounterCmd.ts @@ -1,12 +1,12 @@ -import { Snowflake, TextChannel } from "discord.js"; -import { typedGuildCommand } from "knub"; -import { waitForReply } from "knub/dist/helpers"; +import { Snowflake } from "discord.js"; +import { guildPluginMessageCommand } from "knub"; +import { waitForReply } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { resolveUser, UnknownUser } from "../../../utils"; import { CountersPluginType } from "../types"; -export const ViewCounterCmd = typedGuildCommand()({ +export const ViewCounterCmd = guildPluginMessageCommand()({ trigger: ["counters view", "counter view", "viewcounter", "counter"], permission: "can_view", @@ -68,7 +68,7 @@ export const ViewCounterCmd = typedGuildCommand()({ } const potentialChannel = pluginData.guild.channels.resolve(reply.content as Snowflake); - if (!potentialChannel?.isText()) { + if (!potentialChannel?.isTextBased()) { sendErrorMessage(pluginData, message.channel, "Channel is not a text channel, cancelling"); return; } diff --git a/backend/src/plugins/CustomEvents/CustomEventsPlugin.ts b/backend/src/plugins/CustomEvents/CustomEventsPlugin.ts index 391ee191..50ad7833 100644 --- a/backend/src/plugins/CustomEvents/CustomEventsPlugin.ts +++ b/backend/src/plugins/CustomEvents/CustomEventsPlugin.ts @@ -1,18 +1,20 @@ -import { parseSignature, typedGuildCommand } from "knub"; +import { GuildChannel, GuildMember, User } from "discord.js"; +import { guildPluginMessageCommand, parseSignature } from "knub"; +import { TSignature } from "knub-command-manager"; import { commandTypes } from "../../commandTypes"; -import { stripObjectToScalars, UnknownUser } from "../../utils"; -import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; -import { runEvent } from "./functions/runEvent"; -import { ConfigSchema, CustomEventsPluginType } from "./types"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { createTypedTemplateSafeValueContainer, TemplateSafeValueContainer } from "../../templateFormatter"; -import { Channel, GuildChannel, GuildMember, ThreadChannel, User } from "discord.js"; +import { UnknownUser } from "../../utils"; +import { isScalar } from "../../utils/isScalar"; import { channelToTemplateSafeChannel, memberToTemplateSafeMember, messageToTemplateSafeMessage, userToTemplateSafeUser, } from "../../utils/templateSafeObjects"; -import { isScalar } from "../../utils/isScalar"; +import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; +import { runEvent } from "./functions/runEvent"; +import { ConfigSchema, CustomEventsPluginType } from "./types"; const defaultOptions = { config: { @@ -24,15 +26,17 @@ export const CustomEventsPlugin = zeppelinGuildPlugin()( name: "custom_events", showInDocs: false, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, afterLoad(pluginData) { const config = pluginData.config.get(); for (const [key, event] of Object.entries(config.events)) { if (event.trigger.type === "command") { - const signature = event.trigger.params ? parseSignature(event.trigger.params, commandTypes) : {}; - const eventCommand = typedGuildCommand({ + const signature: TSignature = event.trigger.params + ? parseSignature(event.trigger.params, commandTypes) + : {}; + const eventCommand = guildPluginMessageCommand()({ trigger: event.trigger.name, permission: `events.${key}.trigger.can_use`, signature, @@ -43,7 +47,7 @@ export const CustomEventsPlugin = zeppelinGuildPlugin()( safeArgs[argKey] = userToTemplateSafeUser(argValue); } else if (argValue instanceof GuildMember) { safeArgs[argKey] = memberToTemplateSafeMember(argValue); - } else if (argValue instanceof GuildChannel || argValue instanceof ThreadChannel) { + } else if (argValue instanceof GuildChannel && argValue.isTextBased()) { safeArgs[argKey] = channelToTemplateSafeChannel(argValue); } else if (isScalar(argValue)) { safeArgs[argKey] = argValue; @@ -58,7 +62,7 @@ export const CustomEventsPlugin = zeppelinGuildPlugin()( runEvent(pluginData, event, { msg: message, args }, values); }, }); - pluginData.commands.add(eventCommand); + pluginData.messageCommands.add(eventCommand); } } }, diff --git a/backend/src/plugins/CustomEvents/actions/addRoleAction.ts b/backend/src/plugins/CustomEvents/actions/addRoleAction.ts index 52d6fd59..29ded857 100644 --- a/backend/src/plugins/CustomEvents/actions/addRoleAction.ts +++ b/backend/src/plugins/CustomEvents/actions/addRoleAction.ts @@ -1,4 +1,3 @@ -import { Snowflake } from "discord.js"; import * as t from "io-ts"; import { GuildPluginData } from "knub"; import { canActOn } from "../../../pluginUtils"; diff --git a/backend/src/plugins/CustomEvents/actions/makeRoleMentionableAction.ts b/backend/src/plugins/CustomEvents/actions/makeRoleMentionableAction.ts index 73994d2e..a372bb0e 100644 --- a/backend/src/plugins/CustomEvents/actions/makeRoleMentionableAction.ts +++ b/backend/src/plugins/CustomEvents/actions/makeRoleMentionableAction.ts @@ -1,10 +1,10 @@ import { Snowflake } from "discord.js"; import * as t from "io-ts"; import { GuildPluginData } from "knub"; +import { TemplateSafeValueContainer } from "../../../templateFormatter"; import { convertDelayStringToMS, noop, tDelayString } from "../../../utils"; import { ActionError } from "../ActionError"; import { CustomEventsPluginType, TCustomEvent } from "../types"; -import { TemplateSafeValueContainer } from "../../../templateFormatter"; export const MakeRoleMentionableAction = t.type({ type: t.literal("make_role_mentionable"), @@ -25,22 +25,10 @@ export async function makeRoleMentionableAction( throw new ActionError(`Unknown role: ${role}`); } - await role.edit( - { - mentionable: true, - }, - `Custom event: ${event.name}`, - ); + await role.setMentionable(true, `Custom event: ${event.name}`); const timeout = convertDelayStringToMS(action.timeout)!; setTimeout(() => { - role - .edit( - { - mentionable: false, - }, - `Custom event: ${event.name}`, - ) - .catch(noop); + role.setMentionable(false, `Custom event: ${event.name}`).catch(noop); }, timeout); } diff --git a/backend/src/plugins/CustomEvents/actions/makeRoleUnmentionableAction.ts b/backend/src/plugins/CustomEvents/actions/makeRoleUnmentionableAction.ts index 1975b1c1..f5ab5bc8 100644 --- a/backend/src/plugins/CustomEvents/actions/makeRoleUnmentionableAction.ts +++ b/backend/src/plugins/CustomEvents/actions/makeRoleUnmentionableAction.ts @@ -1,9 +1,9 @@ import { Snowflake } from "discord.js"; import * as t from "io-ts"; import { GuildPluginData } from "knub"; +import { TemplateSafeValueContainer } from "../../../templateFormatter"; import { ActionError } from "../ActionError"; import { CustomEventsPluginType, TCustomEvent } from "../types"; -import { TemplateSafeValueContainer } from "../../../templateFormatter"; export const MakeRoleUnmentionableAction = t.type({ type: t.literal("make_role_unmentionable"), @@ -23,10 +23,5 @@ export async function makeRoleUnmentionableAction( throw new ActionError(`Unknown role: ${role}`); } - await role.edit( - { - mentionable: false, - }, - `Custom event: ${event.name}`, - ); + await role.setMentionable(false, `Custom event: ${event.name}`); } diff --git a/backend/src/plugins/CustomEvents/actions/setChannelPermissionOverrides.ts b/backend/src/plugins/CustomEvents/actions/setChannelPermissionOverrides.ts index 6b9e9b1b..71cf8785 100644 --- a/backend/src/plugins/CustomEvents/actions/setChannelPermissionOverrides.ts +++ b/backend/src/plugins/CustomEvents/actions/setChannelPermissionOverrides.ts @@ -1,9 +1,9 @@ -import { Permissions, Snowflake, TextChannel, PermissionString } from "discord.js"; +import { PermissionsBitField, PermissionsString, Snowflake } from "discord.js"; import * as t from "io-ts"; import { GuildPluginData } from "knub"; +import { TemplateSafeValueContainer } from "../../../templateFormatter"; import { ActionError } from "../ActionError"; import { CustomEventsPluginType, TCustomEvent } from "../types"; -import { TemplateSafeValueContainer } from "../../../templateFormatter"; export const SetChannelPermissionOverridesAction = t.type({ type: t.literal("set_channel_permission_overrides"), @@ -26,15 +26,15 @@ export async function setChannelPermissionOverridesAction( event: TCustomEvent, eventData: any, ) { - const channel = pluginData.guild.channels.cache.get(action.channel as Snowflake) as TextChannel; - if (!channel) { + const channel = pluginData.guild.channels.cache.get(action.channel as Snowflake); + if (!channel || channel.isThread() || !("guild" in channel)) { throw new ActionError(`Unknown channel: ${action.channel}`); } for (const override of action.overrides) { - const allow = new Permissions(BigInt(override.allow)).serialize(); - const deny = new Permissions(BigInt(override.deny)).serialize(); - const perms: Partial> = {}; + const allow = new PermissionsBitField(BigInt(override.allow)).serialize(); + const deny = new PermissionsBitField(BigInt(override.deny)).serialize(); + const perms: Partial> = {}; for (const key in allow) { if (allow[key]) { perms[key] = true; diff --git a/backend/src/plugins/CustomEvents/functions/runEvent.ts b/backend/src/plugins/CustomEvents/functions/runEvent.ts index 404774fd..8e38b9ab 100644 --- a/backend/src/plugins/CustomEvents/functions/runEvent.ts +++ b/backend/src/plugins/CustomEvents/functions/runEvent.ts @@ -1,6 +1,7 @@ -import { Message, TextChannel } from "discord.js"; +import { Message } from "discord.js"; import { GuildPluginData } from "knub"; import { sendErrorMessage } from "../../../pluginUtils"; +import { TemplateSafeValueContainer } from "../../../templateFormatter"; import { ActionError } from "../ActionError"; import { addRoleAction } from "../actions/addRoleAction"; import { createCaseAction } from "../actions/createCaseAction"; @@ -10,7 +11,6 @@ import { messageAction } from "../actions/messageAction"; import { moveToVoiceChannelAction } from "../actions/moveToVoiceChannelAction"; import { setChannelPermissionOverridesAction } from "../actions/setChannelPermissionOverrides"; import { CustomEventsPluginType, TCustomEvent } from "../types"; -import { TemplateSafeValueContainer } from "../../../templateFormatter"; export async function runEvent( pluginData: GuildPluginData, @@ -39,7 +39,7 @@ export async function runEvent( } catch (e) { if (e instanceof ActionError) { if (event.trigger.type === "command") { - sendErrorMessage(pluginData, (eventData.msg as Message).channel as TextChannel, e.message); + sendErrorMessage(pluginData, (eventData.msg as Message).channel, e.message); } else { // TODO: Where to log action errors from other kinds of triggers? } diff --git a/backend/src/plugins/GuildAccessMonitor/GuildAccessMonitorPlugin.ts b/backend/src/plugins/GuildAccessMonitor/GuildAccessMonitorPlugin.ts index 80da1ebd..447094c2 100644 --- a/backend/src/plugins/GuildAccessMonitor/GuildAccessMonitorPlugin.ts +++ b/backend/src/plugins/GuildAccessMonitor/GuildAccessMonitorPlugin.ts @@ -1,10 +1,11 @@ import { Guild } from "discord.js"; import * as t from "io-ts"; -import { BasePluginType, GlobalPluginData, typedGlobalEventListener } from "knub"; +import { BasePluginType, GlobalPluginData, globalPluginEventListener } from "knub"; import { AllowedGuilds } from "../../data/AllowedGuilds"; -import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint"; -import { env } from "../../env"; import { Configs } from "../../data/Configs"; +import { env } from "../../env"; +import { makeIoTsConfigParser } from "../../pluginUtils"; +import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint"; interface GuildAccessMonitorPluginType extends BasePluginType { config: {}; @@ -26,10 +27,10 @@ async function checkGuild(pluginData: GlobalPluginData()({ name: "guild_access_monitor", - configSchema: t.type({}), + configParser: makeIoTsConfigParser(t.type({})), events: [ - typedGlobalEventListener()({ + globalPluginEventListener()({ event: "guildCreate", listener({ pluginData, args: { guild } }) { checkGuild(pluginData, guild); @@ -38,15 +39,17 @@ export const GuildAccessMonitorPlugin = zeppelinGlobalPlugin()({ name: "guild_info_saver", showInDocs: false, - configSchema: t.type({}), + configParser: makeIoTsConfigParser(t.type({})), events: [ - typedGuildEventListener({ + guildPluginEventListener({ event: "guildUpdate", listener({ args }) { void updateGuildInfo(args.newGuild); diff --git a/backend/src/plugins/GuildInfoSaver/types.ts b/backend/src/plugins/GuildInfoSaver/types.ts index 29013186..4ff63cba 100644 --- a/backend/src/plugins/GuildInfoSaver/types.ts +++ b/backend/src/plugins/GuildInfoSaver/types.ts @@ -1,5 +1,4 @@ import { BasePluginType } from "knub"; -import { AllowedGuilds } from "../../data/AllowedGuilds"; export interface GuildInfoSaverPluginType extends BasePluginType { state: { diff --git a/backend/src/plugins/InternalPoster/InternalPosterPlugin.ts b/backend/src/plugins/InternalPoster/InternalPosterPlugin.ts index 2f35c1bd..b0827058 100644 --- a/backend/src/plugins/InternalPoster/InternalPosterPlugin.ts +++ b/backend/src/plugins/InternalPoster/InternalPosterPlugin.ts @@ -1,18 +1,11 @@ -import { PluginOptions, typedGuildCommand } from "knub"; -import { GuildPingableRoles } from "../../data/GuildPingableRoles"; -import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; -import { ConfigSchema, InternalPosterPluginType } from "./types"; -import { - getPhishermanDomainInfo, - hasPhishermanMasterAPIKey, - phishermanApiKeyIsValid, - reportTrackedDomainsToPhisherman, -} from "../../data/Phisherman"; -import { mapToPublicFn } from "../../pluginUtils"; +import { PluginOptions } from "knub"; import { Webhooks } from "../../data/Webhooks"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { Queue } from "../../Queue"; -import { sendMessage } from "./functions/sendMessage"; +import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { editMessage } from "./functions/editMessage"; +import { sendMessage } from "./functions/sendMessage"; +import { ConfigSchema, InternalPosterPluginType } from "./types"; const defaultOptions: PluginOptions = { config: {}, @@ -23,7 +16,7 @@ export const InternalPosterPlugin = zeppelinGuildPlugin, message: Message, - content: MessageEditOptions & WebhookEditMessageOptions, + content: MessageEditOptions & WebhookMessageEditOptions, ): Promise { - if (!(message.channel instanceof TextChannel || message.channel instanceof NewsChannel)) { + const channel = message.channel; + if (!channel.isTextBased()) { return; } - const channel = message.channel as TextChannel | NewsChannel; - await pluginData.state.queue.add(async () => { if (message.webhookId) { const webhook = await pluginData.state.webhooks.find(message.webhookId); diff --git a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts index aea1523c..b97e2cde 100644 --- a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts +++ b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts @@ -1,6 +1,6 @@ +import { WebhookClient } from "discord.js"; import { GuildPluginData } from "knub"; import { InternalPosterPluginType } from "../types"; -import { WebhookClient } from "discord.js"; import { getOrCreateWebhookForChannel, WebhookableChannel } from "./getOrCreateWebhookForChannel"; export async function getOrCreateWebhookClientForChannel( diff --git a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts index 9785271e..d5784212 100644 --- a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts +++ b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts @@ -1,13 +1,13 @@ +import { GuildBasedChannel, PermissionsBitField } from "discord.js"; import { GuildPluginData } from "knub"; -import { InternalPosterPluginType } from "../types"; -import { AnyChannel, GuildChannel, MessageManager, NewsChannel, Permissions, TextChannel } from "discord.js"; import { isDiscordAPIError } from "../../../utils"; +import { InternalPosterPluginType } from "../types"; type WebhookInfo = [id: string, token: string]; -export type WebhookableChannel = Extract any }>; +export type WebhookableChannel = Extract any }>; -export function channelIsWebhookable(channel: AnyChannel): channel is WebhookableChannel { +export function channelIsWebhookable(channel: GuildBasedChannel): channel is WebhookableChannel { return "createWebhook" in channel; } @@ -27,9 +27,9 @@ export async function getOrCreateWebhookForChannel( // Create new webhook const member = pluginData.client.user && pluginData.guild.members.cache.get(pluginData.client.user.id); - if (!member || member.permissions.has(Permissions.FLAGS.MANAGE_WEBHOOKS)) { + if (!member || member.permissions.has(PermissionsBitField.Flags.ManageWebhooks)) { try { - const webhook = await channel.createWebhook(`Zephook ${channel.id}`); + const webhook = await channel.createWebhook({ name: `Zephook ${channel.id}` }); await pluginData.state.webhooks.create({ id: webhook.id, guild_id: pluginData.guild.id, diff --git a/backend/src/plugins/InternalPoster/functions/sendMessage.ts b/backend/src/plugins/InternalPoster/functions/sendMessage.ts index 3afa9614..ec811240 100644 --- a/backend/src/plugins/InternalPoster/functions/sendMessage.ts +++ b/backend/src/plugins/InternalPoster/functions/sendMessage.ts @@ -1,9 +1,9 @@ -import { GuildTextBasedChannel, MessageOptions, WebhookClient } from "discord.js"; +import { GuildTextBasedChannel, MessageCreateOptions, WebhookClient } from "discord.js"; import { GuildPluginData } from "knub"; -import { InternalPosterPluginType } from "../types"; -import { channelIsWebhookable } from "./getOrCreateWebhookForChannel"; import { isDiscordAPIError } from "../../../utils"; +import { InternalPosterPluginType } from "../types"; import { getOrCreateWebhookClientForChannel } from "./getOrCreateWebhookClientForChannel"; +import { channelIsWebhookable } from "./getOrCreateWebhookForChannel"; export type InternalPosterMessageResult = { id: string; @@ -12,7 +12,7 @@ export type InternalPosterMessageResult = { async function sendDirectly( channel: GuildTextBasedChannel, - content: MessageOptions, + content: MessageCreateOptions, ): Promise { return channel.send(content).then((message) => ({ id: message.id, @@ -26,7 +26,7 @@ async function sendDirectly( export async function sendMessage( pluginData: GuildPluginData, channel: GuildTextBasedChannel, - content: MessageOptions, + content: MessageCreateOptions, ): Promise { return pluginData.state.queue.add(async () => { let webhookClient: WebhookClient | null = null; diff --git a/backend/src/plugins/InternalPoster/types.ts b/backend/src/plugins/InternalPoster/types.ts index 54a0ed01..2c4447eb 100644 --- a/backend/src/plugins/InternalPoster/types.ts +++ b/backend/src/plugins/InternalPoster/types.ts @@ -1,8 +1,8 @@ +import { WebhookClient } from "discord.js"; import * as t from "io-ts"; import { BasePluginType } from "knub"; import { Webhooks } from "../../data/Webhooks"; import { Queue } from "../../Queue"; -import { WebhookClient } from "discord.js"; export const ConfigSchema = t.type({}); export type TConfigSchema = t.TypeOf; diff --git a/backend/src/plugins/LocateUser/LocateUserPlugin.ts b/backend/src/plugins/LocateUser/LocateUserPlugin.ts index 52a9b6fe..fc3e4303 100644 --- a/backend/src/plugins/LocateUser/LocateUserPlugin.ts +++ b/backend/src/plugins/LocateUser/LocateUserPlugin.ts @@ -1,5 +1,7 @@ import { PluginOptions } from "knub"; +import { onGuildEvent } from "../../data/GuildEvents"; import { GuildVCAlerts } from "../../data/GuildVCAlerts"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { FollowCmd } from "./commands/FollowCmd"; @@ -8,9 +10,8 @@ import { WhereCmd } from "./commands/WhereCmd"; import { GuildBanRemoveAlertsEvt } from "./events/BanRemoveAlertsEvt"; import { VoiceStateUpdateAlertEvt } from "./events/SendAlertsEvts"; import { ConfigSchema, LocateUserPluginType } from "./types"; -import { fillActiveAlertsList } from "./utils/fillAlertsList"; -import { onGuildEvent } from "../../data/GuildEvents"; import { clearExpiredAlert } from "./utils/clearExpiredAlert"; +import { fillActiveAlertsList } from "./utils/fillAlertsList"; const defaultOptions: PluginOptions = { config: { @@ -38,13 +39,14 @@ export const LocateUserPlugin = zeppelinGuildPlugin()({ * Instantly receive an invite to the voice channel of a user * Be notified as soon as a user switches or joins a voice channel `), + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ WhereCmd, FollowCmd, ListFollowCmd, @@ -65,13 +67,17 @@ export const LocateUserPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - pluginData.state.unregisterGuildEventListener = onGuildEvent(pluginData.guild.id, "expiredVCAlert", (alert) => + const { state, guild } = pluginData; + + state.unregisterGuildEventListener = onGuildEvent(guild.id, "expiredVCAlert", (alert) => clearExpiredAlert(pluginData, alert), ); fillActiveAlertsList(pluginData); }, beforeUnload(pluginData) { - pluginData.state.unregisterGuildEventListener?.(); + const { state, guild } = pluginData; + + state.unregisterGuildEventListener?.(); }, }); diff --git a/backend/src/plugins/LocateUser/commands/FollowCmd.ts b/backend/src/plugins/LocateUser/commands/FollowCmd.ts index 82f84e50..1011f771 100644 --- a/backend/src/plugins/LocateUser/commands/FollowCmd.ts +++ b/backend/src/plugins/LocateUser/commands/FollowCmd.ts @@ -1,10 +1,10 @@ import humanizeDuration from "humanize-duration"; import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { registerExpiringVCAlert } from "../../../data/loops/expiringVCAlertsLoop"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { MINUTES, SECONDS } from "../../../utils"; import { locateUserCmd } from "../types"; -import { registerExpiringVCAlert } from "../../../data/loops/expiringVCAlertsLoop"; export const FollowCmd = locateUserCmd({ trigger: ["follow", "f"], diff --git a/backend/src/plugins/LocateUser/commands/ListFollowCmd.ts b/backend/src/plugins/LocateUser/commands/ListFollowCmd.ts index 5db75917..d598d8a9 100644 --- a/backend/src/plugins/LocateUser/commands/ListFollowCmd.ts +++ b/backend/src/plugins/LocateUser/commands/ListFollowCmd.ts @@ -1,8 +1,8 @@ import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { clearExpiringVCAlert } from "../../../data/loops/expiringVCAlertsLoop"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { createChunkedMessage, sorter } from "../../../utils"; import { locateUserCmd } from "../types"; -import { clearExpiringVCAlert } from "../../../data/loops/expiringVCAlertsLoop"; export const ListFollowCmd = locateUserCmd({ trigger: ["follows", "fs"], diff --git a/backend/src/plugins/LocateUser/events/BanRemoveAlertsEvt.ts b/backend/src/plugins/LocateUser/events/BanRemoveAlertsEvt.ts index 42b71312..aead6776 100644 --- a/backend/src/plugins/LocateUser/events/BanRemoveAlertsEvt.ts +++ b/backend/src/plugins/LocateUser/events/BanRemoveAlertsEvt.ts @@ -1,5 +1,5 @@ -import { locateUserEvt } from "../types"; import { clearExpiringVCAlert } from "../../../data/loops/expiringVCAlertsLoop"; +import { locateUserEvt } from "../types"; export const GuildBanRemoveAlertsEvt = locateUserEvt({ event: "guildBanAdd", diff --git a/backend/src/plugins/LocateUser/events/SendAlertsEvts.ts b/backend/src/plugins/LocateUser/events/SendAlertsEvts.ts index 6510540c..691afc3d 100644 --- a/backend/src/plugins/LocateUser/events/SendAlertsEvts.ts +++ b/backend/src/plugins/LocateUser/events/SendAlertsEvts.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { locateUserEvt } from "../types"; import { sendAlerts } from "../utils/sendAlerts"; @@ -20,11 +20,13 @@ export const VoiceStateUpdateAlertEvt = locateUserEvt({ const voiceChannel = meta.args.oldState.channel!; triggeredAlerts.forEach((alert) => { - const txtChannel = meta.pluginData.guild.channels.resolve(alert.channel_id as Snowflake) as TextChannel; - txtChannel.send({ - content: `🔴 <@!${alert.requestor_id}> the user <@!${alert.user_id}> disconnected out of \`${voiceChannel.name}\``, - allowedMentions: { users: [alert.requestor_id as Snowflake] }, - }); + const txtChannel = meta.pluginData.guild.channels.resolve(alert.channel_id as Snowflake); + if (txtChannel?.isTextBased()) { + txtChannel.send({ + content: `🔴 <@!${alert.requestor_id}> the user <@!${alert.user_id}> disconnected out of \`${voiceChannel.name}\``, + allowedMentions: { users: [alert.requestor_id as Snowflake] }, + }); + } }); } }, diff --git a/backend/src/plugins/LocateUser/types.ts b/backend/src/plugins/LocateUser/types.ts index 51f3a34d..1bfb063e 100644 --- a/backend/src/plugins/LocateUser/types.ts +++ b/backend/src/plugins/LocateUser/types.ts @@ -1,7 +1,6 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildVCAlerts } from "../../data/GuildVCAlerts"; -import Timeout = NodeJS.Timeout; export const ConfigSchema = t.type({ can_where: t.boolean, @@ -18,5 +17,5 @@ export interface LocateUserPluginType extends BasePluginType { }; } -export const locateUserCmd = typedGuildCommand(); -export const locateUserEvt = typedGuildEventListener(); +export const locateUserCmd = guildPluginMessageCommand(); +export const locateUserEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/LocateUser/utils/clearExpiredAlert.ts b/backend/src/plugins/LocateUser/utils/clearExpiredAlert.ts index 5ee86836..9683417b 100644 --- a/backend/src/plugins/LocateUser/utils/clearExpiredAlert.ts +++ b/backend/src/plugins/LocateUser/utils/clearExpiredAlert.ts @@ -1,7 +1,7 @@ import { GuildPluginData } from "knub"; +import { VCAlert } from "../../../data/entities/VCAlert"; import { LocateUserPluginType } from "../types"; import { removeUserIdFromActiveAlerts } from "./removeUserIdFromActiveAlerts"; -import { VCAlert } from "../../../data/entities/VCAlert"; export async function clearExpiredAlert(pluginData: GuildPluginData, alert: VCAlert) { await pluginData.state.alerts.delete(alert.id); diff --git a/backend/src/plugins/LocateUser/utils/moveMember.ts b/backend/src/plugins/LocateUser/utils/moveMember.ts index d3c85ecd..8d044d59 100644 --- a/backend/src/plugins/LocateUser/utils/moveMember.ts +++ b/backend/src/plugins/LocateUser/utils/moveMember.ts @@ -1,4 +1,4 @@ -import { GuildMember, Snowflake, TextChannel } from "discord.js"; +import { GuildMember, GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { sendErrorMessage } from "../../../pluginUtils"; import { LocateUserPluginType } from "../types"; @@ -7,7 +7,7 @@ export async function moveMember( pluginData: GuildPluginData, toMoveID: string, target: GuildMember, - errorChannel: TextChannel, + errorChannel: GuildTextBasedChannel, ) { const modMember: GuildMember = await pluginData.guild.members.fetch(toMoveID as Snowflake); if (modMember.voice.channelId != null) { diff --git a/backend/src/plugins/LocateUser/utils/sendAlerts.ts b/backend/src/plugins/LocateUser/utils/sendAlerts.ts index d93bf47d..66e6a222 100644 --- a/backend/src/plugins/LocateUser/utils/sendAlerts.ts +++ b/backend/src/plugins/LocateUser/utils/sendAlerts.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { resolveMember } from "../../../utils"; import { LocateUserPluginType } from "../types"; @@ -12,10 +12,12 @@ export async function sendAlerts(pluginData: GuildPluginData { const prepend = `<@!${alert.requestor_id}>, an alert requested by you has triggered!\nReminder: \`${alert.body}\`\n`; - const txtChannel = pluginData.guild.channels.resolve(alert.channel_id as Snowflake) as TextChannel; - sendWhere(pluginData, member, txtChannel, prepend); - if (alert.active) { - moveMember(pluginData, alert.requestor_id, member, txtChannel); + const txtChannel = pluginData.guild.channels.resolve(alert.channel_id as Snowflake); + if (txtChannel?.isTextBased()) { + sendWhere(pluginData, member, txtChannel, prepend); + if (alert.active) { + moveMember(pluginData, alert.requestor_id, member, txtChannel); + } } }); } diff --git a/backend/src/plugins/LocateUser/utils/sendWhere.ts b/backend/src/plugins/LocateUser/utils/sendWhere.ts index dd6ddd69..7faaad80 100644 --- a/backend/src/plugins/LocateUser/utils/sendWhere.ts +++ b/backend/src/plugins/LocateUser/utils/sendWhere.ts @@ -1,6 +1,6 @@ -import { GuildMember, Invite, TextChannel, VoiceChannel } from "discord.js"; +import { GuildMember, GuildTextBasedChannel, Invite, VoiceChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { getInviteLink } from "knub/dist/helpers"; +import { getInviteLink } from "knub/helpers"; import { sendErrorMessage } from "../../../pluginUtils"; import { LocateUserPluginType } from "../types"; import { createOrReuseInvite } from "./createOrReuseInvite"; @@ -8,7 +8,7 @@ import { createOrReuseInvite } from "./createOrReuseInvite"; export async function sendWhere( pluginData: GuildPluginData, member: GuildMember, - channel: TextChannel, + channel: GuildTextBasedChannel, prepend: string, ) { const voice = member.voice.channelId diff --git a/backend/src/plugins/Logs/LogsPlugin.ts b/backend/src/plugins/Logs/LogsPlugin.ts index e0c39d35..4543a33a 100644 --- a/backend/src/plugins/Logs/LogsPlugin.ts +++ b/backend/src/plugins/Logs/LogsPlugin.ts @@ -6,18 +6,12 @@ import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { LogType } from "../../data/LogType"; import { logger } from "../../logger"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners"; +import { createTypedTemplateSafeValueContainer, TypedTemplateSafeValueContainer } from "../../templateFormatter"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { LogsChannelCreateEvt, LogsChannelDeleteEvt, LogsChannelUpdateEvt } from "./events/LogsChannelModifyEvts"; -import { LogsGuildMemberAddEvt } from "./events/LogsGuildMemberAddEvt"; -import { LogsGuildMemberRemoveEvt } from "./events/LogsGuildMemberRemoveEvt"; -import { LogsRoleCreateEvt, LogsRoleDeleteEvt, LogsRoleUpdateEvt } from "./events/LogsRoleModifyEvts"; -import { - LogsStageInstanceCreateEvt, - LogsStageInstanceDeleteEvt, - LogsStageInstanceUpdateEvt, -} from "./events/LogsStageInstanceModifyEvts"; import { LogsEmojiCreateEvt, LogsEmojiDeleteEvt, @@ -26,6 +20,14 @@ import { LogsStickerDeleteEvt, LogsStickerUpdateEvt, } from "./events/LogsEmojiAndStickerModifyEvts"; +import { LogsGuildMemberAddEvt } from "./events/LogsGuildMemberAddEvt"; +import { LogsGuildMemberRemoveEvt } from "./events/LogsGuildMemberRemoveEvt"; +import { LogsRoleCreateEvt, LogsRoleDeleteEvt, LogsRoleUpdateEvt } from "./events/LogsRoleModifyEvts"; +import { + LogsStageInstanceCreateEvt, + LogsStageInstanceDeleteEvt, + LogsStageInstanceUpdateEvt, +} from "./events/LogsStageInstanceModifyEvts"; import { LogsThreadCreateEvt, LogsThreadDeleteEvt, LogsThreadUpdateEvt } from "./events/LogsThreadModifyEvts"; import { LogsGuildMemberUpdateEvt } from "./events/LogsUserUpdateEvts"; import { LogsVoiceStateUpdateEvt } from "./events/LogsVoiceChannelEvts"; @@ -35,14 +37,9 @@ import { log } from "./util/log"; import { onMessageDelete } from "./util/onMessageDelete"; import { onMessageDeleteBulk } from "./util/onMessageDeleteBulk"; import { onMessageUpdate } from "./util/onMessageUpdate"; -import { Util } from "discord.js"; -import { - createTypedTemplateSafeValueContainer, - TemplateSafeValueContainer, - TypedTemplateSafeValueContainer, -} from "../../templateFormatter"; -import { mapToPublicFn } from "../../pluginUtils"; +import { escapeCodeBlock } from "discord.js"; +import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin"; import { logAutomodAction } from "./logFunctions/logAutomodAction"; import { logBotAlert } from "./logFunctions/logBotAlert"; import { logCaseCreate } from "./logFunctions/logCaseCreate"; @@ -53,6 +50,7 @@ import { logChannelCreate } from "./logFunctions/logChannelCreate"; import { logChannelDelete } from "./logFunctions/logChannelDelete"; import { logChannelUpdate } from "./logFunctions/logChannelUpdate"; import { logClean } from "./logFunctions/logClean"; +import { logDmFailed } from "./logFunctions/logDmFailed"; import { logEmojiCreate } from "./logFunctions/logEmojiCreate"; import { logEmojiDelete } from "./logFunctions/logEmojiDelete"; import { logEmojiUpdate } from "./logFunctions/logEmojiUpdate"; @@ -76,6 +74,7 @@ import { logMemberRoleChanges } from "./logFunctions/logMemberRoleChanges"; import { logMemberRoleRemove } from "./logFunctions/logMemberRoleRemove"; import { logMemberTimedBan } from "./logFunctions/logMemberTimedBan"; import { logMemberTimedMute } from "./logFunctions/logMemberTimedMute"; +import { logMemberTimedUnban } from "./logFunctions/logMemberTimedUnban"; import { logMemberTimedUnmute } from "./logFunctions/logMemberTimedUnmute"; import { logMemberUnban } from "./logFunctions/logMemberUnban"; import { logMemberUnmute } from "./logFunctions/logMemberUnmute"; @@ -110,9 +109,11 @@ import { logVoiceChannelForceMove } from "./logFunctions/logVoiceChannelForceMov import { logVoiceChannelJoin } from "./logFunctions/logVoiceChannelJoin"; import { logVoiceChannelLeave } from "./logFunctions/logVoiceChannelLeave"; import { logVoiceChannelMove } from "./logFunctions/logVoiceChannelMove"; -import { logMemberTimedUnban } from "./logFunctions/logMemberTimedUnban"; -import { logDmFailed } from "./logFunctions/logDmFailed"; -import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin"; + +// The `any` cast here is to prevent TypeScript from locking up from the circular dependency +function getCasesPlugin(): Promise { + return import("../Cases/CasesPlugin.js") as Promise; +} const defaultOptions: PluginOptions = { config: { @@ -142,15 +143,11 @@ export const LogsPlugin = zeppelinGuildPlugin()({ showInDocs: true, info: { prettyName: "Logs", + configSchema: ConfigSchema, }, - dependencies: async () => [ - TimeAndDatePlugin, - InternalPosterPlugin, - // The `as any` cast here is to prevent TypeScript from locking up from the circular dependency - ((await import("../Cases/CasesPlugin")) as any).CasesPlugin, - ], - configSchema: ConfigSchema, + dependencies: async () => [TimeAndDatePlugin, InternalPosterPlugin, (await getCasesPlugin()).CasesPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, events: [ @@ -275,7 +272,7 @@ export const LogsPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - const { state, guild } = pluginData; + const { state } = pluginData; state.logListener = ({ type, data }) => log(pluginData, type, data); state.guildLogs.on("log", state.logListener); @@ -300,7 +297,7 @@ export const LogsPlugin = zeppelinGuildPlugin()({ The following regex has taken longer than ${timeoutMs}ms for ${failedTimes} times and has been temporarily disabled: `.trim() + "\n```" + - Util.escapeCodeBlock(regexSource) + + escapeCodeBlock(regexSource) + "```", }), ); @@ -309,23 +306,25 @@ export const LogsPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - if (pluginData.state.logListener) { - pluginData.state.guildLogs.removeListener("log", pluginData.state.logListener); + const { state, guild } = pluginData; + + if (state.logListener) { + state.guildLogs.removeListener("log", state.logListener); } - if (pluginData.state.onMessageDeleteFn) { - pluginData.state.savedMessages.events.off("delete", pluginData.state.onMessageDeleteFn); + if (state.onMessageDeleteFn) { + state.savedMessages.events.off("delete", state.onMessageDeleteFn); } - if (pluginData.state.onMessageDeleteBulkFn) { - pluginData.state.savedMessages.events.off("deleteBulk", pluginData.state.onMessageDeleteBulkFn); + if (state.onMessageDeleteBulkFn) { + state.savedMessages.events.off("deleteBulk", state.onMessageDeleteBulkFn); } - if (pluginData.state.onMessageUpdateFn) { - pluginData.state.savedMessages.events.off("update", pluginData.state.onMessageUpdateFn); + if (state.onMessageUpdateFn) { + state.savedMessages.events.off("update", state.onMessageUpdateFn); } - if (pluginData.state.regexRunnerRepeatedTimeoutListener) { - pluginData.state.regexRunner.off("repeatedTimeout", pluginData.state.regexRunnerRepeatedTimeoutListener); + if (state.regexRunnerRepeatedTimeoutListener) { + state.regexRunner.off("repeatedTimeout", state.regexRunnerRepeatedTimeoutListener); } - discardRegExpRunner(`guild-${pluginData.guild.id}`); + discardRegExpRunner(`guild-${guild.id}`); }, }); diff --git a/backend/src/plugins/Logs/events/LogsChannelModifyEvts.ts b/backend/src/plugins/Logs/events/LogsChannelModifyEvts.ts index 33bd7df9..79c06dcf 100644 --- a/backend/src/plugins/Logs/events/LogsChannelModifyEvts.ts +++ b/backend/src/plugins/Logs/events/LogsChannelModifyEvts.ts @@ -1,12 +1,10 @@ -import { LogType } from "../../../data/LogType"; +import { TextChannel, VoiceChannel } from "discord.js"; import { differenceToString, getScalarDifference } from "../../../utils"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; +import { filterObject } from "../../../utils/filterObject"; import { logChannelCreate } from "../logFunctions/logChannelCreate"; import { logChannelDelete } from "../logFunctions/logChannelDelete"; import { logChannelUpdate } from "../logFunctions/logChannelUpdate"; -import { TextChannel, VoiceChannel } from "discord.js"; -import { filterObject } from "../../../utils/filterObject"; +import { logsEvt } from "../types"; export const LogsChannelCreateEvt = logsEvt({ event: "channelCreate", diff --git a/backend/src/plugins/Logs/events/LogsEmojiAndStickerModifyEvts.ts b/backend/src/plugins/Logs/events/LogsEmojiAndStickerModifyEvts.ts index 29b8eb61..414073cf 100644 --- a/backend/src/plugins/Logs/events/LogsEmojiAndStickerModifyEvts.ts +++ b/backend/src/plugins/Logs/events/LogsEmojiAndStickerModifyEvts.ts @@ -1,13 +1,13 @@ +import { GuildEmoji, Sticker } from "discord.js"; import { differenceToString, getScalarDifference } from "../../../utils"; -import { logsEvt } from "../types"; +import { filterObject } from "../../../utils/filterObject"; import { logEmojiCreate } from "../logFunctions/logEmojiCreate"; import { logEmojiDelete } from "../logFunctions/logEmojiDelete"; import { logEmojiUpdate } from "../logFunctions/logEmojiUpdate"; import { logStickerCreate } from "../logFunctions/logStickerCreate"; import { logStickerDelete } from "../logFunctions/logStickerDelete"; import { logStickerUpdate } from "../logFunctions/logStickerUpdate"; -import { Emoji, GuildEmoji, Sticker, ThreadChannel } from "discord.js"; -import { filterObject } from "../../../utils/filterObject"; +import { logsEvt } from "../types"; export const LogsEmojiCreateEvt = logsEvt({ event: "emojiCreate", diff --git a/backend/src/plugins/Logs/events/LogsGuildBanEvts.ts b/backend/src/plugins/Logs/events/LogsGuildBanEvts.ts index fcf84249..1fda8b80 100644 --- a/backend/src/plugins/Logs/events/LogsGuildBanEvts.ts +++ b/backend/src/plugins/Logs/events/LogsGuildBanEvts.ts @@ -1,11 +1,10 @@ -import { GuildAuditLogs } from "discord.js"; +import { AuditLogEvent } from "discord.js"; import { LogType } from "../../../data/LogType"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; -import { logMemberBan } from "../logFunctions/logMemberBan"; -import { isLogIgnored } from "../util/isLogIgnored"; -import { logMemberUnban } from "../logFunctions/logMemberUnban"; import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; +import { logMemberBan } from "../logFunctions/logMemberBan"; +import { logMemberUnban } from "../logFunctions/logMemberUnban"; +import { logsEvt } from "../types"; +import { isLogIgnored } from "../util/isLogIgnored"; export const LogsGuildBanAddEvt = logsEvt({ event: "guildBanAdd", @@ -18,7 +17,11 @@ export const LogsGuildBanAddEvt = logsEvt({ return; } - const relevantAuditLogEntry = await findMatchingAuditLogEntry(pluginData.guild, "MEMBER_BAN_ADD", user.id); + const relevantAuditLogEntry = await findMatchingAuditLogEntry( + pluginData.guild, + AuditLogEvent.MemberBanAdd, + user.id, + ); const mod = relevantAuditLogEntry?.executor ?? null; logMemberBan(meta.pluginData, { mod, @@ -40,7 +43,11 @@ export const LogsGuildBanRemoveEvt = logsEvt({ return; } - const relevantAuditLogEntry = await findMatchingAuditLogEntry(pluginData.guild, "MEMBER_BAN_REMOVE", user.id); + const relevantAuditLogEntry = await findMatchingAuditLogEntry( + pluginData.guild, + AuditLogEvent.MemberBanRemove, + user.id, + ); const mod = relevantAuditLogEntry?.executor ?? null; logMemberUnban(pluginData, { diff --git a/backend/src/plugins/Logs/events/LogsGuildMemberAddEvt.ts b/backend/src/plugins/Logs/events/LogsGuildMemberAddEvt.ts index c86a1fc3..31e3ecd0 100644 --- a/backend/src/plugins/Logs/events/LogsGuildMemberAddEvt.ts +++ b/backend/src/plugins/Logs/events/LogsGuildMemberAddEvt.ts @@ -1,10 +1,5 @@ -import humanizeDuration from "humanize-duration"; -import moment from "moment-timezone"; -import { LogType } from "../../../data/LogType"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; import { logMemberJoin } from "../logFunctions/logMemberJoin"; -import { logMemberJoinWithPriorRecords } from "../logFunctions/logMemberJoinWithPriorRecords"; +import { logsEvt } from "../types"; export const LogsGuildMemberAddEvt = logsEvt({ event: "guildMemberAdd", diff --git a/backend/src/plugins/Logs/events/LogsGuildMemberRemoveEvt.ts b/backend/src/plugins/Logs/events/LogsGuildMemberRemoveEvt.ts index 289372e1..5c3fc923 100644 --- a/backend/src/plugins/Logs/events/LogsGuildMemberRemoveEvt.ts +++ b/backend/src/plugins/Logs/events/LogsGuildMemberRemoveEvt.ts @@ -1,7 +1,5 @@ -import { LogType } from "../../../data/LogType"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; import { logMemberLeave } from "../logFunctions/logMemberLeave"; +import { logsEvt } from "../types"; export const LogsGuildMemberRemoveEvt = logsEvt({ event: "guildMemberRemove", diff --git a/backend/src/plugins/Logs/events/LogsRoleModifyEvts.ts b/backend/src/plugins/Logs/events/LogsRoleModifyEvts.ts index 81d6e2bf..c44a4894 100644 --- a/backend/src/plugins/Logs/events/LogsRoleModifyEvts.ts +++ b/backend/src/plugins/Logs/events/LogsRoleModifyEvts.ts @@ -1,12 +1,10 @@ -import { LogType } from "../../../data/LogType"; +import { Role } from "discord.js"; import { differenceToString, getScalarDifference } from "../../../utils"; -import { roleToTemplateSafeRole } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; +import { filterObject } from "../../../utils/filterObject"; import { logRoleCreate } from "../logFunctions/logRoleCreate"; import { logRoleDelete } from "../logFunctions/logRoleDelete"; import { logRoleUpdate } from "../logFunctions/logRoleUpdate"; -import { GuildEmoji, Role } from "discord.js"; -import { filterObject } from "../../../utils/filterObject"; +import { logsEvt } from "../types"; export const LogsRoleCreateEvt = logsEvt({ event: "roleCreate", diff --git a/backend/src/plugins/Logs/events/LogsStageInstanceModifyEvts.ts b/backend/src/plugins/Logs/events/LogsStageInstanceModifyEvts.ts index f9ddb54a..202fee0d 100644 --- a/backend/src/plugins/Logs/events/LogsStageInstanceModifyEvts.ts +++ b/backend/src/plugins/Logs/events/LogsStageInstanceModifyEvts.ts @@ -1,12 +1,10 @@ -import { LogType } from "../../../data/LogType"; +import { StageChannel, StageInstance } from "discord.js"; import { differenceToString, getScalarDifference } from "../../../utils"; -import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; +import { filterObject } from "../../../utils/filterObject"; import { logStageInstanceCreate } from "../logFunctions/logStageInstanceCreate"; -import { Role, StageChannel, StageInstance } from "discord.js"; import { logStageInstanceDelete } from "../logFunctions/logStageInstanceDelete"; import { logStageInstanceUpdate } from "../logFunctions/logStageInstanceUpdate"; -import { filterObject } from "../../../utils/filterObject"; +import { logsEvt } from "../types"; export const LogsStageInstanceCreateEvt = logsEvt({ event: "stageInstanceCreate", diff --git a/backend/src/plugins/Logs/events/LogsThreadModifyEvts.ts b/backend/src/plugins/Logs/events/LogsThreadModifyEvts.ts index b760d21d..2568a1bc 100644 --- a/backend/src/plugins/Logs/events/LogsThreadModifyEvts.ts +++ b/backend/src/plugins/Logs/events/LogsThreadModifyEvts.ts @@ -1,12 +1,10 @@ -import { LogType } from "../../../data/LogType"; +import { ThreadChannel } from "discord.js"; import { differenceToString, getScalarDifference } from "../../../utils"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; -import { logsEvt } from "../types"; +import { filterObject } from "../../../utils/filterObject"; import { logThreadCreate } from "../logFunctions/logThreadCreate"; import { logThreadDelete } from "../logFunctions/logThreadDelete"; import { logThreadUpdate } from "../logFunctions/logThreadUpdate"; -import { TextChannel, ThreadChannel, VoiceChannel } from "discord.js"; -import { filterObject } from "../../../utils/filterObject"; +import { logsEvt } from "../types"; export const LogsThreadCreateEvt = logsEvt({ event: "threadCreate", diff --git a/backend/src/plugins/Logs/events/LogsUserUpdateEvts.ts b/backend/src/plugins/Logs/events/LogsUserUpdateEvts.ts index ac24a0fe..2fee32ab 100644 --- a/backend/src/plugins/Logs/events/LogsUserUpdateEvts.ts +++ b/backend/src/plugins/Logs/events/LogsUserUpdateEvts.ts @@ -1,13 +1,11 @@ -import { GuildAuditLogs } from "discord.js"; import diff from "lodash.difference"; import isEqual from "lodash.isequal"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { LogType } from "../../../data/LogType"; -import { logsEvt } from "../types"; import { logMemberNickChange } from "../logFunctions/logMemberNickChange"; -import { logMemberRoleChanges } from "../logFunctions/logMemberRoleChanges"; import { logMemberRoleAdd } from "../logFunctions/logMemberRoleAdd"; +import { logMemberRoleChanges } from "../logFunctions/logMemberRoleChanges"; import { logMemberRoleRemove } from "../logFunctions/logMemberRoleRemove"; +import { logsEvt } from "../types"; export const LogsGuildMemberUpdateEvt = logsEvt({ event: "guildMemberUpdate", diff --git a/backend/src/plugins/Logs/events/LogsVoiceChannelEvts.ts b/backend/src/plugins/Logs/events/LogsVoiceChannelEvts.ts index 51e349e7..4b21610e 100644 --- a/backend/src/plugins/Logs/events/LogsVoiceChannelEvts.ts +++ b/backend/src/plugins/Logs/events/LogsVoiceChannelEvts.ts @@ -1,9 +1,7 @@ -import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; -import { logsEvt } from "../types"; -import { logVoiceChannelLeave } from "../logFunctions/logVoiceChannelLeave"; import { logVoiceChannelJoin } from "../logFunctions/logVoiceChannelJoin"; +import { logVoiceChannelLeave } from "../logFunctions/logVoiceChannelLeave"; import { logVoiceChannelMove } from "../logFunctions/logVoiceChannelMove"; +import { logsEvt } from "../types"; export const LogsVoiceStateUpdateEvt = logsEvt({ event: "voiceStateUpdate", diff --git a/backend/src/plugins/Logs/logFunctions/logAutomodAction.ts b/backend/src/plugins/Logs/logFunctions/logAutomodAction.ts index d7046826..2a2fd69c 100644 --- a/backend/src/plugins/Logs/logFunctions/logAutomodAction.ts +++ b/backend/src/plugins/Logs/logFunctions/logAutomodAction.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogAutomodActionData { rule: string; diff --git a/backend/src/plugins/Logs/logFunctions/logBotAlert.ts b/backend/src/plugins/Logs/logFunctions/logBotAlert.ts index 9f47dea0..1438b249 100644 --- a/backend/src/plugins/Logs/logFunctions/logBotAlert.ts +++ b/backend/src/plugins/Logs/logFunctions/logBotAlert.ts @@ -1,8 +1,8 @@ import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogBotAlertData { body: string; diff --git a/backend/src/plugins/Logs/logFunctions/logCaseCreate.ts b/backend/src/plugins/Logs/logFunctions/logCaseCreate.ts index 412dc8fa..d2fe96b9 100644 --- a/backend/src/plugins/Logs/logFunctions/logCaseCreate.ts +++ b/backend/src/plugins/Logs/logFunctions/logCaseCreate.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogCaseCreateData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logCaseDelete.ts b/backend/src/plugins/Logs/logFunctions/logCaseDelete.ts index 85c2e0d8..adc3bfa0 100644 --- a/backend/src/plugins/Logs/logFunctions/logCaseDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logCaseDelete.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; -import { caseToTemplateSafeCase, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; import { Case } from "../../../data/entities/Case"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; +import { caseToTemplateSafeCase, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogCaseDeleteData { mod: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logCaseUpdate.ts b/backend/src/plugins/Logs/logFunctions/logCaseUpdate.ts index d8db4499..4c8b8fd6 100644 --- a/backend/src/plugins/Logs/logFunctions/logCaseUpdate.ts +++ b/backend/src/plugins/Logs/logFunctions/logCaseUpdate.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogCaseUpdateData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logCensor.ts b/backend/src/plugins/Logs/logFunctions/logCensor.ts index c5048730..1d9affdd 100644 --- a/backend/src/plugins/Logs/logFunctions/logCensor.ts +++ b/backend/src/plugins/Logs/logFunctions/logCensor.ts @@ -1,18 +1,18 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; +import { deactivateMentions, disableCodeBlocks } from "knub/helpers"; +import { resolveChannelIds } from "src/utils/resolveChannelIds"; +import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; +import { UnknownUser } from "../../../utils"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import { SavedMessage } from "../../../data/entities/SavedMessage"; -import { UnknownUser } from "../../../utils"; -import { deactivateMentions, disableCodeBlocks } from "knub/dist/helpers"; -import { resolveChannelIds } from "src/utils/resolveChannelIds"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogCensorData { user: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logChannelCreate.ts b/backend/src/plugins/Logs/logFunctions/logChannelCreate.ts index a95ffb48..be8adf8c 100644 --- a/backend/src/plugins/Logs/logFunctions/logChannelCreate.ts +++ b/backend/src/plugins/Logs/logFunctions/logChannelCreate.ts @@ -1,14 +1,14 @@ +import { GuildBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildChannel, NewsChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogChannelCreateData { - channel: GuildChannel | NewsChannel; + channel: GuildBasedChannel; } export function logChannelCreate(pluginData: GuildPluginData, data: LogChannelCreateData) { diff --git a/backend/src/plugins/Logs/logFunctions/logChannelDelete.ts b/backend/src/plugins/Logs/logFunctions/logChannelDelete.ts index 25bd4374..6078f311 100644 --- a/backend/src/plugins/Logs/logFunctions/logChannelDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logChannelDelete.ts @@ -1,14 +1,14 @@ +import { GuildBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildChannel, NewsChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogChannelDeleteData { - channel: GuildChannel | NewsChannel; + channel: GuildBasedChannel; } export function logChannelDelete(pluginData: GuildPluginData, data: LogChannelDeleteData) { diff --git a/backend/src/plugins/Logs/logFunctions/logChannelUpdate.ts b/backend/src/plugins/Logs/logFunctions/logChannelUpdate.ts index 7942501e..2963520a 100644 --- a/backend/src/plugins/Logs/logFunctions/logChannelUpdate.ts +++ b/backend/src/plugins/Logs/logFunctions/logChannelUpdate.ts @@ -1,15 +1,15 @@ +import { GuildBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildChannel, NewsChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogChannelUpdateData { - oldChannel: GuildChannel | NewsChannel; - newChannel: GuildChannel | NewsChannel; + oldChannel: GuildBasedChannel; + newChannel: GuildBasedChannel; differenceString: string; } diff --git a/backend/src/plugins/Logs/logFunctions/logClean.ts b/backend/src/plugins/Logs/logFunctions/logClean.ts index 6486873f..2a156868 100644 --- a/backend/src/plugins/Logs/logFunctions/logClean.ts +++ b/backend/src/plugins/Logs/logFunctions/logClean.ts @@ -1,15 +1,15 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, User } from "discord.js"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogCleanData { mod: User; - channel: BaseGuildTextChannel; + channel: GuildTextBasedChannel; count: number; archiveUrl: string; } diff --git a/backend/src/plugins/Logs/logFunctions/logDmFailed.ts b/backend/src/plugins/Logs/logFunctions/logDmFailed.ts index 32ba3bde..4b540624 100644 --- a/backend/src/plugins/Logs/logFunctions/logDmFailed.ts +++ b/backend/src/plugins/Logs/logFunctions/logDmFailed.ts @@ -1,11 +1,11 @@ +import { User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; -import { User } from "discord.js"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogDmFailedData { source: string; diff --git a/backend/src/plugins/Logs/logFunctions/logEmojiCreate.ts b/backend/src/plugins/Logs/logFunctions/logEmojiCreate.ts index d49d940b..11c6c0d9 100644 --- a/backend/src/plugins/Logs/logFunctions/logEmojiCreate.ts +++ b/backend/src/plugins/Logs/logFunctions/logEmojiCreate.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { Emoji } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { emojiToTemplateSafeEmoji } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogEmojiCreateData { emoji: Emoji; diff --git a/backend/src/plugins/Logs/logFunctions/logEmojiDelete.ts b/backend/src/plugins/Logs/logFunctions/logEmojiDelete.ts index 6f5b717c..4ef31c4e 100644 --- a/backend/src/plugins/Logs/logFunctions/logEmojiDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logEmojiDelete.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { Emoji } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { emojiToTemplateSafeEmoji } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogEmojiDeleteData { emoji: Emoji; diff --git a/backend/src/plugins/Logs/logFunctions/logEmojiUpdate.ts b/backend/src/plugins/Logs/logFunctions/logEmojiUpdate.ts index 9934a045..3d455530 100644 --- a/backend/src/plugins/Logs/logFunctions/logEmojiUpdate.ts +++ b/backend/src/plugins/Logs/logFunctions/logEmojiUpdate.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { Emoji } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { emojiToTemplateSafeEmoji } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogEmojiUpdateData { oldEmoji: Emoji; diff --git a/backend/src/plugins/Logs/logFunctions/logMassBan.ts b/backend/src/plugins/Logs/logFunctions/logMassBan.ts index e920fe03..6f2695cc 100644 --- a/backend/src/plugins/Logs/logFunctions/logMassBan.ts +++ b/backend/src/plugins/Logs/logFunctions/logMassBan.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMassBanData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logMassMute.ts b/backend/src/plugins/Logs/logFunctions/logMassMute.ts index f7a510a9..74c8694b 100644 --- a/backend/src/plugins/Logs/logFunctions/logMassMute.ts +++ b/backend/src/plugins/Logs/logFunctions/logMassMute.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMassMuteData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logMassUnban.ts b/backend/src/plugins/Logs/logFunctions/logMassUnban.ts index 138c5be9..59709bef 100644 --- a/backend/src/plugins/Logs/logFunctions/logMassUnban.ts +++ b/backend/src/plugins/Logs/logFunctions/logMassUnban.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMassUnbanData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberBan.ts b/backend/src/plugins/Logs/logFunctions/logMemberBan.ts index 21777c2b..cdeaec62 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberBan.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberBan.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberBanData { mod: User | UnknownUser | null; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberForceban.ts b/backend/src/plugins/Logs/logFunctions/logMemberForceban.ts index 2f9acaa3..dc47736c 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberForceban.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberForceban.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember, Snowflake } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberForcebanData { mod: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberJoin.ts b/backend/src/plugins/Logs/logFunctions/logMemberJoin.ts index 0daf7a33..f1368209 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberJoin.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberJoin.ts @@ -1,12 +1,12 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; -import moment from "moment-timezone"; import humanizeDuration from "humanize-duration"; +import { GuildPluginData } from "knub"; +import moment from "moment-timezone"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; +import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberJoinData { member: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberJoinWithPriorRecords.ts b/backend/src/plugins/Logs/logFunctions/logMemberJoinWithPriorRecords.ts index 92ca6cb7..01cbd81f 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberJoinWithPriorRecords.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberJoinWithPriorRecords.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberJoinWithPriorRecordsData { member: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberKick.ts b/backend/src/plugins/Logs/logFunctions/logMemberKick.ts index 704b8769..e60f1124 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberKick.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberKick.ts @@ -1,11 +1,11 @@ +import { User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildMember, User } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberKickData { mod: User | UnknownUser | null; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberLeave.ts b/backend/src/plugins/Logs/logFunctions/logMemberLeave.ts index 05460a20..8753b68b 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberLeave.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberLeave.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember, PartialGuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberLeaveData { member: GuildMember | PartialGuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberMute.ts b/backend/src/plugins/Logs/logFunctions/logMemberMute.ts index c99fd300..76e7f352 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberMute.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberMute.ts @@ -1,11 +1,11 @@ +import { User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildMember, User } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberMuteData { mod: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberMuteExpired.ts b/backend/src/plugins/Logs/logFunctions/logMemberMuteExpired.ts index 1a969120..24e52aba 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberMuteExpired.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberMuteExpired.ts @@ -1,15 +1,15 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; +import { UnknownUser } from "../../../utils"; import { memberToTemplateSafeMember, TemplateSafeUnknownMember, TemplateSafeUnknownUser, } from "../../../utils/templateSafeObjects"; -import { UnknownUser } from "../../../utils"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberMuteExpiredData { member: GuildMember | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberMuteRejoin.ts b/backend/src/plugins/Logs/logFunctions/logMemberMuteRejoin.ts index fcd9a641..455a13d8 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberMuteRejoin.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberMuteRejoin.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberMuteRejoinData { member: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberNickChange.ts b/backend/src/plugins/Logs/logFunctions/logMemberNickChange.ts index ddab9b08..437dfa02 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberNickChange.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberNickChange.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberNickChangeData { member: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberNote.ts b/backend/src/plugins/Logs/logFunctions/logMemberNote.ts index 4f05c9a9..c81ccb7a 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberNote.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberNote.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberNoteData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberRestore.ts b/backend/src/plugins/Logs/logFunctions/logMemberRestore.ts index d6eab53f..b93b5558 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberRestore.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberRestore.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberRestoreData { member: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberRoleAdd.ts b/backend/src/plugins/Logs/logFunctions/logMemberRoleAdd.ts index 2db68838..e5175e3d 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberRoleAdd.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberRoleAdd.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember, Role, User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberRoleAddData { mod: User | null; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberRoleChanges.ts b/backend/src/plugins/Logs/logFunctions/logMemberRoleChanges.ts index 7eee36a9..3af76352 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberRoleChanges.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberRoleChanges.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember, Role, User } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberRoleChangesData { mod: User | UnknownUser | null; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberRoleRemove.ts b/backend/src/plugins/Logs/logFunctions/logMemberRoleRemove.ts index 54dfff29..67ce1c38 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberRoleRemove.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberRoleRemove.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember, Role, User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberRoleRemoveData { mod: User | null; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberTimedBan.ts b/backend/src/plugins/Logs/logFunctions/logMemberTimedBan.ts index aa4c7846..ecc1d2a2 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberTimedBan.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberTimedBan.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberTimedBanData { mod: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberTimedMute.ts b/backend/src/plugins/Logs/logFunctions/logMemberTimedMute.ts index 70fd663c..4ef04bed 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberTimedMute.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberTimedMute.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberTimedMuteData { mod: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberTimedUnban.ts b/backend/src/plugins/Logs/logFunctions/logMemberTimedUnban.ts index 8716157b..ada44504 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberTimedUnban.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberTimedUnban.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberTimedUnbanData { mod: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberTimedUnmute.ts b/backend/src/plugins/Logs/logFunctions/logMemberTimedUnmute.ts index 72ccea64..ce036761 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberTimedUnmute.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberTimedUnmute.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberTimedUnmuteData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberUnban.ts b/backend/src/plugins/Logs/logFunctions/logMemberUnban.ts index d9702531..305bbada 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberUnban.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberUnban.ts @@ -1,11 +1,11 @@ +import { Snowflake, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildMember, Snowflake, User } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberUnbanData { mod: User | UnknownUser | null; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberUnmute.ts b/backend/src/plugins/Logs/logFunctions/logMemberUnmute.ts index ef2aca40..5afbf075 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberUnmute.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberUnmute.ts @@ -1,11 +1,11 @@ +import { User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { GuildMember, User } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { UnknownUser } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberUnmuteData { mod: User; diff --git a/backend/src/plugins/Logs/logFunctions/logMemberWarn.ts b/backend/src/plugins/Logs/logFunctions/logMemberWarn.ts index 180582b1..8cdc533b 100644 --- a/backend/src/plugins/Logs/logFunctions/logMemberWarn.ts +++ b/backend/src/plugins/Logs/logFunctions/logMemberWarn.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMemberWarnData { mod: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts b/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts index e103b13b..4e04c49f 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts @@ -1,19 +1,19 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { FORMAT_NO_TIMESTAMP, LogsPluginType } from "../types"; +import moment from "moment-timezone"; +import { ISavedMessageAttachmentData, SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; +import { UnknownUser, useMediaUrls } from "../../../utils"; +import { resolveChannelIds } from "../../../utils/resolveChannelIds"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import moment from "moment-timezone"; -import { ISavedMessageAttachmentData, SavedMessage } from "../../../data/entities/SavedMessage"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; -import { UnknownUser, useMediaUrls } from "../../../utils"; -import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { FORMAT_NO_TIMESTAMP, LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMessageDeleteData { user: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDeleteAuto.ts b/backend/src/plugins/Logs/logFunctions/logMessageDeleteAuto.ts index d4e66f28..32cfcfa7 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDeleteAuto.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDeleteAuto.ts @@ -1,22 +1,22 @@ +import { GuildBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; +import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, User } from "discord.js"; +import { UnknownUser } from "../../../utils"; +import { resolveChannelIds } from "../../../utils/resolveChannelIds"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import { SavedMessage } from "../../../data/entities/SavedMessage"; -import { UnknownUser } from "../../../utils"; -import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMessageDeleteAutoData { message: SavedMessage; user: User | UnknownUser; - channel: BaseGuildTextChannel; + channel: GuildBasedChannel; messageDate: string; } diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts index ad9320e3..8e1787ab 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts @@ -1,11 +1,11 @@ +import { GuildTextBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMessageDeleteBareData { messageId: string; diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts index 2ffe36bf..46ca1648 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts @@ -1,11 +1,11 @@ +import { GuildTextBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMessageDeleteBulkData { count: number; diff --git a/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts b/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts index 64f12e8f..73c669ef 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts @@ -1,17 +1,17 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; +import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; +import { UnknownUser } from "../../../utils"; +import { resolveChannelIds } from "../../../utils/resolveChannelIds"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import { SavedMessage } from "../../../data/entities/SavedMessage"; -import { UnknownUser } from "../../../utils"; -import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMessageEditData { user: User | UnknownUser; diff --git a/backend/src/plugins/Logs/logFunctions/logMessageSpamDetected.ts b/backend/src/plugins/Logs/logFunctions/logMessageSpamDetected.ts index a18f197c..5a79e5ab 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageSpamDetected.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageSpamDetected.ts @@ -1,15 +1,15 @@ +import { GuildMember, GuildTextBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildChannel, GuildMember, ThreadChannel } from "discord.js"; -import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogMessageSpamDetectedData { member: GuildMember; - channel: GuildChannel | ThreadChannel; + channel: GuildTextBasedChannel; description: string; limit: number; interval: number; diff --git a/backend/src/plugins/Logs/logFunctions/logOtherSpamDetected.ts b/backend/src/plugins/Logs/logFunctions/logOtherSpamDetected.ts index d4c4d0c5..9cbd8b25 100644 --- a/backend/src/plugins/Logs/logFunctions/logOtherSpamDetected.ts +++ b/backend/src/plugins/Logs/logFunctions/logOtherSpamDetected.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogOtherSpamDetectedData { member: GuildMember; diff --git a/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts b/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts index 38a8f668..e92d0292 100644 --- a/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts @@ -1,11 +1,11 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogPostedScheduledMessageData { author: User; diff --git a/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts b/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts index bc2cc31f..227ca7f7 100644 --- a/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts @@ -1,11 +1,11 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogRepeatedMessageData { author: User; diff --git a/backend/src/plugins/Logs/logFunctions/logRoleCreate.ts b/backend/src/plugins/Logs/logFunctions/logRoleCreate.ts index b4137bf2..9bb177d6 100644 --- a/backend/src/plugins/Logs/logFunctions/logRoleCreate.ts +++ b/backend/src/plugins/Logs/logFunctions/logRoleCreate.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { Role } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { roleToTemplateSafeRole } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogRoleCreateData { role: Role; diff --git a/backend/src/plugins/Logs/logFunctions/logRoleDelete.ts b/backend/src/plugins/Logs/logFunctions/logRoleDelete.ts index bce63794..0ec6e87a 100644 --- a/backend/src/plugins/Logs/logFunctions/logRoleDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logRoleDelete.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { Role } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { roleToTemplateSafeRole } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogRoleDeleteData { role: Role; diff --git a/backend/src/plugins/Logs/logFunctions/logRoleUpdate.ts b/backend/src/plugins/Logs/logFunctions/logRoleUpdate.ts index be36dfda..caf8b0c5 100644 --- a/backend/src/plugins/Logs/logFunctions/logRoleUpdate.ts +++ b/backend/src/plugins/Logs/logFunctions/logRoleUpdate.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { Role } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { roleToTemplateSafeRole } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogRoleUpdateData { oldRole: Role; diff --git a/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts b/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts index 8067dd6a..098b7918 100644 --- a/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts @@ -1,11 +1,11 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogScheduledMessageData { author: User; diff --git a/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts b/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts index 849b1a4a..dc4c4200 100644 --- a/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts @@ -1,11 +1,11 @@ +import { GuildTextBasedChannel, User } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogScheduledRepeatedMessageData { author: User; diff --git a/backend/src/plugins/Logs/logFunctions/logSetAntiraidAuto.ts b/backend/src/plugins/Logs/logFunctions/logSetAntiraidAuto.ts index 5e3261a7..6c841eeb 100644 --- a/backend/src/plugins/Logs/logFunctions/logSetAntiraidAuto.ts +++ b/backend/src/plugins/Logs/logFunctions/logSetAntiraidAuto.ts @@ -1,8 +1,8 @@ import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogSetAntiraidAutoData { level: string; diff --git a/backend/src/plugins/Logs/logFunctions/logSetAntiraidUser.ts b/backend/src/plugins/Logs/logFunctions/logSetAntiraidUser.ts index bd65a67d..7d43423b 100644 --- a/backend/src/plugins/Logs/logFunctions/logSetAntiraidUser.ts +++ b/backend/src/plugins/Logs/logFunctions/logSetAntiraidUser.ts @@ -1,10 +1,10 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { User } from "discord.js"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogSetAntiraidUserData { level: string; diff --git a/backend/src/plugins/Logs/logFunctions/logStageInstanceCreate.ts b/backend/src/plugins/Logs/logFunctions/logStageInstanceCreate.ts index 6172a2bc..31d900c4 100644 --- a/backend/src/plugins/Logs/logFunctions/logStageInstanceCreate.ts +++ b/backend/src/plugins/Logs/logFunctions/logStageInstanceCreate.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { StageChannel, StageInstance } from "discord.js"; -import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogStageInstanceCreateData { stageInstance: StageInstance; diff --git a/backend/src/plugins/Logs/logFunctions/logStageInstanceDelete.ts b/backend/src/plugins/Logs/logFunctions/logStageInstanceDelete.ts index 844af38b..ff69b13f 100644 --- a/backend/src/plugins/Logs/logFunctions/logStageInstanceDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logStageInstanceDelete.ts @@ -1,11 +1,11 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { StageChannel, StageInstance } from "discord.js"; -import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogStageInstanceDeleteData { stageInstance: StageInstance; diff --git a/backend/src/plugins/Logs/logFunctions/logStageInstanceUpdate.ts b/backend/src/plugins/Logs/logFunctions/logStageInstanceUpdate.ts index 8c68cf42..be53b598 100644 --- a/backend/src/plugins/Logs/logFunctions/logStageInstanceUpdate.ts +++ b/backend/src/plugins/Logs/logFunctions/logStageInstanceUpdate.ts @@ -1,14 +1,14 @@ -import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; -import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; -import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { StageChannel, StageInstance } from "discord.js"; -import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; +import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; +import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogStageInstanceUpdateData { - oldStageInstance: StageInstance; + oldStageInstance: StageInstance | null; newStageInstance: StageInstance; stageChannel: StageChannel; differenceString: string; @@ -19,7 +19,7 @@ export function logStageInstanceUpdate(pluginData: GuildPluginData, data: LogThreadCreateData) { diff --git a/backend/src/plugins/Logs/logFunctions/logThreadDelete.ts b/backend/src/plugins/Logs/logFunctions/logThreadDelete.ts index 04827083..52dd4908 100644 --- a/backend/src/plugins/Logs/logFunctions/logThreadDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logThreadDelete.ts @@ -1,14 +1,14 @@ +import { AnyThreadChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { ThreadChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogThreadDeleteData { - thread: ThreadChannel; + thread: AnyThreadChannel; } export function logThreadDelete(pluginData: GuildPluginData, data: LogThreadDeleteData) { diff --git a/backend/src/plugins/Logs/logFunctions/logThreadUpdate.ts b/backend/src/plugins/Logs/logFunctions/logThreadUpdate.ts index c359a683..f73bab5d 100644 --- a/backend/src/plugins/Logs/logFunctions/logThreadUpdate.ts +++ b/backend/src/plugins/Logs/logFunctions/logThreadUpdate.ts @@ -1,15 +1,15 @@ +import { AnyThreadChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { ThreadChannel } from "discord.js"; -import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogThreadUpdateData { - oldThread: ThreadChannel; - newThread: ThreadChannel; + oldThread: AnyThreadChannel; + newThread: AnyThreadChannel; differenceString: string; } diff --git a/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceDisconnect.ts b/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceDisconnect.ts index 9940a1a3..79efc844 100644 --- a/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceDisconnect.ts +++ b/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceDisconnect.ts @@ -1,20 +1,20 @@ +import { GuildMember, User, VoiceBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildVoiceChannel, GuildMember, User } from "discord.js"; +import { resolveChannelIds } from "../../../utils/resolveChannelIds"; import { channelToTemplateSafeChannel, memberToTemplateSafeMember, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogVoiceChannelForceDisconnectData { mod: User; member: GuildMember; - oldChannel: BaseGuildVoiceChannel; + oldChannel: VoiceBasedChannel; } export function logVoiceChannelForceDisconnect( diff --git a/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceMove.ts b/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceMove.ts index 90b5478c..70978304 100644 --- a/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceMove.ts +++ b/backend/src/plugins/Logs/logFunctions/logVoiceChannelForceMove.ts @@ -1,21 +1,21 @@ +import { GuildMember, User, VoiceBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildVoiceChannel, GuildMember, User } from "discord.js"; +import { resolveChannelIds } from "../../../utils/resolveChannelIds"; import { channelToTemplateSafeChannel, memberToTemplateSafeMember, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogVoiceChannelForceMoveData { mod: User; member: GuildMember; - oldChannel: BaseGuildVoiceChannel; - newChannel: BaseGuildVoiceChannel; + oldChannel: VoiceBasedChannel; + newChannel: VoiceBasedChannel; } export function logVoiceChannelForceMove( diff --git a/backend/src/plugins/Logs/logFunctions/logVoiceChannelJoin.ts b/backend/src/plugins/Logs/logFunctions/logVoiceChannelJoin.ts index 71d5ea0c..f91cf4a2 100644 --- a/backend/src/plugins/Logs/logFunctions/logVoiceChannelJoin.ts +++ b/backend/src/plugins/Logs/logFunctions/logVoiceChannelJoin.ts @@ -1,15 +1,15 @@ +import { GuildMember, VoiceBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildVoiceChannel, GuildMember } from "discord.js"; -import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogVoiceChannelJoinData { member: GuildMember; - channel: BaseGuildVoiceChannel; + channel: VoiceBasedChannel; } export function logVoiceChannelJoin(pluginData: GuildPluginData, data: LogVoiceChannelJoinData) { diff --git a/backend/src/plugins/Logs/logFunctions/logVoiceChannelLeave.ts b/backend/src/plugins/Logs/logFunctions/logVoiceChannelLeave.ts index 509494fc..99b35155 100644 --- a/backend/src/plugins/Logs/logFunctions/logVoiceChannelLeave.ts +++ b/backend/src/plugins/Logs/logFunctions/logVoiceChannelLeave.ts @@ -1,15 +1,15 @@ +import { GuildMember, VoiceBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildVoiceChannel, GuildMember } from "discord.js"; -import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogVoiceChannelLeaveData { member: GuildMember; - channel: BaseGuildVoiceChannel; + channel: VoiceBasedChannel; } export function logVoiceChannelLeave(pluginData: GuildPluginData, data: LogVoiceChannelLeaveData) { diff --git a/backend/src/plugins/Logs/logFunctions/logVoiceChannelMove.ts b/backend/src/plugins/Logs/logFunctions/logVoiceChannelMove.ts index 56090907..679c66d3 100644 --- a/backend/src/plugins/Logs/logFunctions/logVoiceChannelMove.ts +++ b/backend/src/plugins/Logs/logFunctions/logVoiceChannelMove.ts @@ -1,16 +1,16 @@ +import { GuildMember, VoiceBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; -import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildVoiceChannel, GuildMember } from "discord.js"; -import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { resolveChannelIds } from "../../../utils/resolveChannelIds"; +import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; +import { LogsPluginType } from "../types"; +import { log } from "../util/log"; interface LogVoiceChannelMoveData { member: GuildMember; - oldChannel: BaseGuildVoiceChannel; - newChannel: BaseGuildVoiceChannel; + oldChannel: VoiceBasedChannel; + newChannel: VoiceBasedChannel; } export function logVoiceChannelMove(pluginData: GuildPluginData, data: LogVoiceChannelMoveData) { diff --git a/backend/src/plugins/Logs/types.ts b/backend/src/plugins/Logs/types.ts index 511636a6..654b72de 100644 --- a/backend/src/plugins/Logs/types.ts +++ b/backend/src/plugins/Logs/types.ts @@ -1,15 +1,14 @@ import * as t from "io-ts"; +import { BasePluginType, CooldownManager, guildPluginEventListener } from "knub"; import { z } from "zod"; -import { BasePluginType, CooldownManager, typedGuildEventListener } from "knub"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildCases } from "../../data/GuildCases"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; -import { RegExpRunner } from "../../RegExpRunner"; -import { StrictMessageContent, tMessageContent, tNullable } from "../../utils"; -import { TRegex } from "../../validatorUtils"; import { LogType } from "../../data/LogType"; -import { GuildMember } from "discord.js"; +import { RegExpRunner } from "../../RegExpRunner"; +import { tMessageContent, tNullable } from "../../utils"; +import { MessageBuffer } from "../../utils/MessageBuffer"; import { TemplateSafeCase, TemplateSafeChannel, @@ -23,12 +22,7 @@ import { TemplateSafeUnknownUser, TemplateSafeUser, } from "../../utils/templateSafeObjects"; -import { - TemplateSafeValue, - TemplateSafeValueContainer, - TypedTemplateSafeValueContainer, -} from "../../templateFormatter"; -import { MessageBuffer } from "../../utils/MessageBuffer"; +import { TRegex } from "../../validatorUtils"; export const tLogFormats = t.record(t.string, t.union([t.string, tMessageContent])); export type TLogFormats = t.TypeOf; @@ -96,7 +90,7 @@ export interface LogsPluginType extends BasePluginType { }; } -export const logsEvt = typedGuildEventListener(); +export const logsEvt = guildPluginEventListener(); export const LogTypeData = z.object({ [LogType.MEMBER_WARN]: z.object({ @@ -280,7 +274,7 @@ export const LogTypeData = z.object({ }), [LogType.STAGE_INSTANCE_UPDATE]: z.object({ - oldStageInstance: z.instanceof(TemplateSafeStage), + oldStageInstance: z.instanceof(TemplateSafeStage).or(z.null()), newStageInstance: z.instanceof(TemplateSafeStage), stageChannel: z.instanceof(TemplateSafeChannel), differenceString: z.string(), diff --git a/backend/src/plugins/Logs/util/getLogMessage.ts b/backend/src/plugins/Logs/util/getLogMessage.ts index c69c7dbb..4887cb82 100644 --- a/backend/src/plugins/Logs/util/getLogMessage.ts +++ b/backend/src/plugins/Logs/util/getLogMessage.ts @@ -1,4 +1,4 @@ -import { MessageOptions } from "discord.js"; +import { MessageCreateOptions } from "discord.js"; import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; @@ -18,21 +18,21 @@ import { verboseUserMention, verboseUserName, } from "../../../utils"; -import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; -import { FORMAT_NO_TIMESTAMP, ILogTypeData, LogsPluginType, TLogChannel } from "../types"; import { getTemplateSafeMemberLevel, - TemplateSafeMember, memberToTemplateSafeMember, + TemplateSafeMember, TemplateSafeUser, } from "../../../utils/templateSafeObjects"; +import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; +import { FORMAT_NO_TIMESTAMP, ILogTypeData, LogsPluginType, TLogChannel } from "../types"; export async function getLogMessage( pluginData: GuildPluginData, type: TLogType, data: TypedTemplateSafeValueContainer, opts?: Pick, -): Promise { +): Promise { const config = pluginData.config.get(); const format = opts?.format?.[LogType[type]] || config.format[LogType[type]] || ""; if (format === "" || format == null) return null; diff --git a/backend/src/plugins/Logs/util/isLogIgnored.ts b/backend/src/plugins/Logs/util/isLogIgnored.ts index 093fb9a6..eb23b927 100644 --- a/backend/src/plugins/Logs/util/isLogIgnored.ts +++ b/backend/src/plugins/Logs/util/isLogIgnored.ts @@ -1,6 +1,6 @@ import { GuildPluginData } from "knub"; -import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; +import { LogsPluginType } from "../types"; export function isLogIgnored(pluginData: GuildPluginData, type: LogType, ignoreId: string) { return pluginData.state.guildLogs.isLogIgnored(type, ignoreId); diff --git a/backend/src/plugins/Logs/util/log.ts b/backend/src/plugins/Logs/util/log.ts index 26be9585..05416aa4 100644 --- a/backend/src/plugins/Logs/util/log.ts +++ b/backend/src/plugins/Logs/util/log.ts @@ -1,13 +1,13 @@ -import { MessageEmbedOptions, MessageMentionTypes, Snowflake, TextChannel } from "discord.js"; +import { APIEmbed, MessageMentionTypes, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; +import { LogType } from "../../../data/LogType"; import { allowTimeout } from "../../../RegExpRunner"; +import { TypedTemplateSafeValueContainer } from "../../../templateFormatter"; +import { isDiscordAPIError, MINUTES } from "../../../utils"; +import { MessageBuffer } from "../../../utils/MessageBuffer"; +import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"; import { ILogTypeData, LogsPluginType, TLogChannel, TLogChannelMap } from "../types"; import { getLogMessage } from "./getLogMessage"; -import { TypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { LogType } from "../../../data/LogType"; -import { MessageBuffer } from "../../../utils/MessageBuffer"; -import { createChunkedMessage, isDiscordAPIError, MINUTES } from "../../../utils"; -import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"; const excludedUserProps = ["user", "member", "mod"]; const excludedRoleProps = ["message.member.roles", "member.roles"]; @@ -88,7 +88,7 @@ export async function log( logChannelLoop: for (const [channelId, opts] of Object.entries(logChannels)) { const channel = pluginData.guild.channels.cache.get(channelId as Snowflake); - if (!channel?.isText()) continue; + if (!channel?.isTextBased()) continue; if (pluginData.state.channelCooldowns.isOnCooldown(channelId)) continue; if (opts.include?.length && !opts.include.includes(typeStr)) continue; if (opts.exclude && opts.exclude.includes(typeStr)) continue; @@ -141,7 +141,7 @@ export async function log( const buffer = pluginData.state.buffers.get(channelId)!; buffer.push({ content: typeof message === "string" ? message : message.content || "", - embeds: typeof message === "string" ? [] : ((message.embeds || []) as MessageEmbedOptions[]), + embeds: typeof message === "string" ? [] : ((message.embeds || []) as APIEmbed[]), }); } } diff --git a/backend/src/plugins/Logs/util/onMessageDelete.ts b/backend/src/plugins/Logs/util/onMessageDelete.ts index 0cbcc03a..e21967b8 100644 --- a/backend/src/plugins/Logs/util/onMessageDelete.ts +++ b/backend/src/plugins/Logs/util/onMessageDelete.ts @@ -1,21 +1,18 @@ -import { Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; import { resolveUser } from "../../../utils"; -import { LogsPluginType } from "../types"; import { logMessageDelete } from "../logFunctions/logMessageDelete"; -import { isLogIgnored } from "./isLogIgnored"; import { logMessageDeleteBare } from "../logFunctions/logMessageDeleteBare"; +import { LogsPluginType } from "../types"; +import { isLogIgnored } from "./isLogIgnored"; export async function onMessageDelete(pluginData: GuildPluginData, savedMessage: SavedMessage) { const user = await resolveUser(pluginData.client, savedMessage.user_id); - const channel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake) as - | TextChannel - | ThreadChannel - | null; + const channel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake); - if (channel == null) { + if (!channel?.isTextBased()) { return; } diff --git a/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts b/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts index 36ad96f1..d5a6366e 100644 --- a/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts +++ b/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts @@ -1,10 +1,10 @@ -import { BaseGuildTextChannel, GuildTextBasedChannel, Snowflake, ThreadChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; import { getBaseUrl } from "../../../pluginUtils"; -import { LogsPluginType } from "../types"; import { logMessageDeleteBulk } from "../logFunctions/logMessageDeleteBulk"; +import { LogsPluginType } from "../types"; import { isLogIgnored } from "./isLogIgnored"; export async function onMessageDeleteBulk(pluginData: GuildPluginData, savedMessages: SavedMessage[]) { @@ -12,9 +12,11 @@ export async function onMessageDeleteBulk(pluginData: GuildPluginData `\`${item.user_id}\``))); diff --git a/backend/src/plugins/Logs/util/onMessageUpdate.ts b/backend/src/plugins/Logs/util/onMessageUpdate.ts index 13d611ed..a94b1fe6 100644 --- a/backend/src/plugins/Logs/util/onMessageUpdate.ts +++ b/backend/src/plugins/Logs/util/onMessageUpdate.ts @@ -1,12 +1,10 @@ -import { BaseGuildTextChannel, GuildTextBasedChannel, MessageEmbed, Snowflake, ThreadChannel } from "discord.js"; +import { EmbedData, GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import cloneDeep from "lodash.clonedeep"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { SavedMessage } from "../../../data/entities/SavedMessage"; -import { LogType } from "../../../data/LogType"; import { resolveUser } from "../../../utils"; -import { LogsPluginType } from "../types"; import { logMessageEdit } from "../logFunctions/logMessageEdit"; +import { LogsPluginType } from "../types"; export async function onMessageUpdate( pluginData: GuildPluginData, @@ -16,13 +14,13 @@ export async function onMessageUpdate( // To log a message update, either the message content or a rich embed has to change let logUpdate = false; - const oldEmbedsToCompare = ((oldSavedMessage.data.embeds || []) as MessageEmbed[]) + const oldEmbedsToCompare = ((oldSavedMessage.data.embeds || []) as EmbedData[]) .map((e) => cloneDeep(e)) - .filter((e) => (e as MessageEmbed).type === "rich"); + .filter((e) => (e as EmbedData).type === "rich"); - const newEmbedsToCompare = ((savedMessage.data.embeds || []) as MessageEmbed[]) + const newEmbedsToCompare = ((savedMessage.data.embeds || []) as EmbedData[]) .map((e) => cloneDeep(e)) - .filter((e) => (e as MessageEmbed).type === "rich"); + .filter((e) => (e as EmbedData).type === "rich"); for (const embed of [...oldEmbedsToCompare, ...newEmbedsToCompare]) { if (embed.thumbnail) { diff --git a/backend/src/plugins/MessageSaver/MessageSaverPlugin.ts b/backend/src/plugins/MessageSaver/MessageSaverPlugin.ts index c8a603e1..9e775251 100644 --- a/backend/src/plugins/MessageSaver/MessageSaverPlugin.ts +++ b/backend/src/plugins/MessageSaver/MessageSaverPlugin.ts @@ -1,11 +1,11 @@ import { PluginOptions } from "knub"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { SaveMessagesToDBCmd } from "./commands/SaveMessagesToDB"; import { SavePinsToDBCmd } from "./commands/SavePinsToDB"; import { MessageCreateEvt, MessageDeleteBulkEvt, MessageDeleteEvt, MessageUpdateEvt } from "./events/SaveMessagesEvts"; import { ConfigSchema, MessageSaverPluginType } from "./types"; -import { Queue } from "../../Queue"; const defaultOptions: PluginOptions = { config: { @@ -25,11 +25,11 @@ export const MessageSaverPlugin = zeppelinGuildPlugin()( name: "message_saver", showInDocs: false, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ SaveMessagesToDBCmd, SavePinsToDBCmd, ], diff --git a/backend/src/plugins/MessageSaver/events/SaveMessagesEvts.ts b/backend/src/plugins/MessageSaver/events/SaveMessagesEvts.ts index 8b279129..594e2b53 100644 --- a/backend/src/plugins/MessageSaver/events/SaveMessagesEvts.ts +++ b/backend/src/plugins/MessageSaver/events/SaveMessagesEvts.ts @@ -1,9 +1,7 @@ -import { Constants, Message, MessageType, Snowflake } from "discord.js"; +import { Message, MessageType } from "discord.js"; import { messageSaverEvt } from "../types"; -import { SECONDS } from "../../../utils"; -import moment from "moment-timezone"; -const AFFECTED_MESSAGE_TYPES: MessageType[] = ["DEFAULT", "REPLY", "APPLICATION_COMMAND"]; +const AFFECTED_MESSAGE_TYPES: MessageType[] = [MessageType.Default, MessageType.Reply, MessageType.ChatInputCommand]; export const MessageCreateEvt = messageSaverEvt({ event: "messageCreate", diff --git a/backend/src/plugins/MessageSaver/saveMessagesToDB.ts b/backend/src/plugins/MessageSaver/saveMessagesToDB.ts index e4206fce..750ab632 100644 --- a/backend/src/plugins/MessageSaver/saveMessagesToDB.ts +++ b/backend/src/plugins/MessageSaver/saveMessagesToDB.ts @@ -1,10 +1,10 @@ -import { Message, Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { GuildTextBasedChannel, Message } from "discord.js"; import { GuildPluginData } from "knub"; import { MessageSaverPluginType } from "./types"; export async function saveMessagesToDB( pluginData: GuildPluginData, - channel: TextChannel | ThreadChannel, + channel: GuildTextBasedChannel, ids: string[], ) { const failed: string[] = []; diff --git a/backend/src/plugins/MessageSaver/types.ts b/backend/src/plugins/MessageSaver/types.ts index 28495da3..2fdef665 100644 --- a/backend/src/plugins/MessageSaver/types.ts +++ b/backend/src/plugins/MessageSaver/types.ts @@ -1,7 +1,6 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; -import { Queue } from "../../Queue"; export const ConfigSchema = t.type({ can_manage: t.boolean, @@ -15,5 +14,5 @@ export interface MessageSaverPluginType extends BasePluginType { }; } -export const messageSaverCmd = typedGuildCommand(); -export const messageSaverEvt = typedGuildEventListener(); +export const messageSaverCmd = guildPluginMessageCommand(); +export const messageSaverEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/ModActions/ModActionsPlugin.ts b/backend/src/plugins/ModActions/ModActionsPlugin.ts index ba19a0b0..70636c6c 100644 --- a/backend/src/plugins/ModActions/ModActionsPlugin.ts +++ b/backend/src/plugins/ModActions/ModActionsPlugin.ts @@ -1,13 +1,15 @@ import { GuildMember, Message, Snowflake } from "discord.js"; import { EventEmitter } from "events"; import { GuildCases } from "../../data/GuildCases"; +import { onGuildEvent } from "../../data/GuildEvents"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildMutes } from "../../data/GuildMutes"; import { GuildTempbans } from "../../data/GuildTempbans"; -import { mapToPublicFn } from "../../pluginUtils"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { Queue } from "../../Queue"; import { MINUTES, trimPluginDescription } from "../../utils"; import { CasesPlugin } from "../Cases/CasesPlugin"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { MutesPlugin } from "../Mutes/MutesPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -37,6 +39,7 @@ import { CreateBanCaseOnManualBanEvt } from "./events/CreateBanCaseOnManualBanEv import { CreateUnbanCaseOnManualUnbanEvt } from "./events/CreateUnbanCaseOnManualUnbanEvt"; import { PostAlertOnMemberJoinEvt } from "./events/PostAlertOnMemberJoinEvt"; import { banUserId } from "./functions/banUserId"; +import { clearTempban } from "./functions/clearTempban"; import { hasMutePermission } from "./functions/hasMutePerm"; import { kickMember } from "./functions/kickMember"; import { offModActionsEvent } from "./functions/offModActionsEvent"; @@ -44,9 +47,6 @@ import { onModActionsEvent } from "./functions/onModActionsEvent"; import { updateCase } from "./functions/updateCase"; import { warnMember } from "./functions/warnMember"; import { BanOptions, ConfigSchema, KickOptions, ModActionsPluginType, WarnOptions } from "./types"; -import { LogsPlugin } from "../Logs/LogsPlugin"; -import { onGuildEvent } from "../../data/GuildEvents"; -import { clearTempban } from "./functions/clearTempban"; const defaultOptions = { config: { @@ -120,15 +120,16 @@ export const ModActionsPlugin = zeppelinGuildPlugin()({ description: trimPluginDescription(` This plugin contains the 'typical' mod actions such as warning, muting, kicking, banning, etc. `), + configSchema: ConfigSchema, }, dependencies: () => [TimeAndDatePlugin, CasesPlugin, MutesPlugin, LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, events: [CreateBanCaseOnManualBanEvt, CreateUnbanCaseOnManualUnbanEvt, PostAlertOnMemberJoinEvt], - commands: [ + messageCommands: [ UpdateCmd, NoteCmd, WarnCmd, @@ -209,14 +210,18 @@ export const ModActionsPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - pluginData.state.unregisterGuildEventListener = onGuildEvent(pluginData.guild.id, "expiredTempban", (tempban) => + const { state, guild } = pluginData; + + state.unregisterGuildEventListener = onGuildEvent(guild.id, "expiredTempban", (tempban) => clearTempban(pluginData, tempban), ); }, beforeUnload(pluginData) { - pluginData.state.unloaded = true; - pluginData.state.unregisterGuildEventListener?.(); - pluginData.state.events.removeAllListeners(); + const { state, guild } = pluginData; + + state.unloaded = true; + state.unregisterGuildEventListener?.(); + state.events.removeAllListeners(); }, }); diff --git a/backend/src/plugins/ModActions/commands/AddCaseCmd.ts b/backend/src/plugins/ModActions/commands/AddCaseCmd.ts index b28b1eeb..567bfac6 100644 --- a/backend/src/plugins/ModActions/commands/AddCaseCmd.ts +++ b/backend/src/plugins/ModActions/commands/AddCaseCmd.ts @@ -1,14 +1,12 @@ -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { Case } from "../../../data/entities/Case"; -import { LogType } from "../../../data/LogType"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { canActOn, hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveMember, resolveUser } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; const opts = { mod: ct.member({ option: true }), diff --git a/backend/src/plugins/ModActions/commands/BanCmd.ts b/backend/src/plugins/ModActions/commands/BanCmd.ts index 0371a3df..630ad95d 100644 --- a/backend/src/plugins/ModActions/commands/BanCmd.ts +++ b/backend/src/plugins/ModActions/commands/BanCmd.ts @@ -1,21 +1,19 @@ import humanizeDuration from "humanize-duration"; -import { getMemberLevel } from "knub/dist/helpers"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { getMemberLevel } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; -import { LogType } from "../../../data/LogType"; +import { clearExpiringTempban, registerExpiringTempban } from "../../../data/loops/expiringTempbansLoop"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { canActOn, hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveMember, resolveUser } from "../../../utils"; import { banLock } from "../../../utils/lockNameHelpers"; import { waitForButtonConfirm } from "../../../utils/waitForInteraction"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { banUserId } from "../functions/banUserId"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { isBanned } from "../functions/isBanned"; import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromArgs"; import { modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { clearExpiringTempban, registerExpiringTempban } from "../../../data/loops/expiringTempbansLoop"; const opts = { mod: ct.member({ option: true }), diff --git a/backend/src/plugins/ModActions/commands/CasesModCmd.ts b/backend/src/plugins/ModActions/commands/CasesModCmd.ts index 14e89870..5b0e3273 100644 --- a/backend/src/plugins/ModActions/commands/CasesModCmd.ts +++ b/backend/src/plugins/ModActions/commands/CasesModCmd.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions, User } from "discord.js"; +import { APIEmbed, User } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { emptyEmbedValue, resolveUser, trimLines } from "../../../utils"; @@ -54,10 +54,10 @@ export const CasesModCmd = modActionsCmd({ const lastCaseNum = page * casesPerPage; const title = `Most recent cases ${firstCaseNum}-${lastCaseNum} of ${totalCases} by ${modName}`; - const embed: MessageEmbedOptions = { + const embed = { author: { name: title, - iconURL: mod instanceof User ? mod.displayAvatarURL() : undefined, + icon_url: mod instanceof User ? mod.displayAvatarURL() : undefined, }, fields: [ ...getChunkedEmbedFields(emptyEmbedValue, lines.join("\n")), @@ -69,7 +69,7 @@ export const CasesModCmd = modActionsCmd({ `), }, ], - }; + } satisfies APIEmbed; return { embeds: [embed] }; }, diff --git a/backend/src/plugins/ModActions/commands/CasesUserCmd.ts b/backend/src/plugins/ModActions/commands/CasesUserCmd.ts index c6646711..7a8aa89b 100644 --- a/backend/src/plugins/ModActions/commands/CasesUserCmd.ts +++ b/backend/src/plugins/ModActions/commands/CasesUserCmd.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions, User } from "discord.js"; +import { APIEmbed, User } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; @@ -116,7 +116,7 @@ export const CasesUserCmd = modActionsCmd({ const chunkStart = i * linesPerChunk + 1; const chunkEnd = Math.min((i + 1) * linesPerChunk, lines.length); - const embed: MessageEmbedOptions = { + const embed = { author: { name: lineChunks.length === 1 @@ -128,7 +128,7 @@ export const CasesUserCmd = modActionsCmd({ ...getChunkedEmbedFields(emptyEmbedValue, linesInChunk.join("\n")), ...(isLastChunk ? [footerField] : []), ], - }; + } satisfies APIEmbed; msg.channel.send({ embeds: [embed] }); } diff --git a/backend/src/plugins/ModActions/commands/DeleteCaseCmd.ts b/backend/src/plugins/ModActions/commands/DeleteCaseCmd.ts index 352b6d00..823bc726 100644 --- a/backend/src/plugins/ModActions/commands/DeleteCaseCmd.ts +++ b/backend/src/plugins/ModActions/commands/DeleteCaseCmd.ts @@ -1,11 +1,8 @@ -import { TextChannel } from "discord.js"; import { helpers } from "knub"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { Case } from "../../../data/entities/Case"; -import { LogType } from "../../../data/LogType"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { SECONDS, stripObjectToScalars, trimLines } from "../../../utils"; +import { SECONDS, trimLines } from "../../../utils"; import { CasesPlugin } from "../../Cases/CasesPlugin"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; @@ -54,12 +51,7 @@ export const DeleteCaseCmd = modActionsCmd({ content: "Delete the following case? Answer 'Yes' to continue, 'No' to cancel.", }); - const reply = await helpers.waitForReply( - pluginData.client, - message.channel as TextChannel, - message.author.id, - 15 * SECONDS, - ); + const reply = await helpers.waitForReply(pluginData.client, message.channel, message.author.id, 15 * SECONDS); const normalizedReply = (reply?.content || "").toLowerCase().trim(); if (normalizedReply !== "yes" && normalizedReply !== "y") { message.channel.send("Cancelled. Case was not deleted."); diff --git a/backend/src/plugins/ModActions/commands/ForcebanCmd.ts b/backend/src/plugins/ModActions/commands/ForcebanCmd.ts index 1df592b6..efdc8cdd 100644 --- a/backend/src/plugins/ModActions/commands/ForcebanCmd.ts +++ b/backend/src/plugins/ModActions/commands/ForcebanCmd.ts @@ -1,16 +1,15 @@ import { Snowflake } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { canActOn, hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { resolveMember, resolveUser } from "../../../utils"; +import { DAYS, MINUTES, resolveMember, resolveUser } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { ignoreEvent } from "../functions/ignoreEvent"; import { isBanned } from "../functions/isBanned"; import { IgnoredEventType, modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; const opts = { mod: ct.member({ option: true }), @@ -70,7 +69,7 @@ export const ForcebanCmd = modActionsCmd({ try { // FIXME: Use banUserId()? await pluginData.guild.bans.create(user.id as Snowflake, { - days: 1, + deleteMessageSeconds: (1 * DAYS) / MINUTES, reason: reason ?? undefined, }); } catch { diff --git a/backend/src/plugins/ModActions/commands/MassBanCmd.ts b/backend/src/plugins/ModActions/commands/MassBanCmd.ts index 8f0482e9..afef9f1f 100644 --- a/backend/src/plugins/ModActions/commands/MassBanCmd.ts +++ b/backend/src/plugins/ModActions/commands/MassBanCmd.ts @@ -1,18 +1,17 @@ -import { Snowflake, TextChannel } from "discord.js"; -import { waitForReply } from "knub/dist/helpers"; +import { Snowflake } from "discord.js"; +import { waitForReply } from "knub/helpers"; import { performance } from "perf_hooks"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; import { humanizeDurationShort } from "../../../humanizeDurationShort"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { MINUTES, noop } from "../../../utils"; +import { DAYS, MINUTES, noop, SECONDS } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { ignoreEvent } from "../functions/ignoreEvent"; import { IgnoredEventType, modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export const MassbanCmd = modActionsCmd({ trigger: "massban", @@ -34,7 +33,7 @@ export const MassbanCmd = modActionsCmd({ // Ask for ban reason (cleaner this way instead of trying to cram it into the args) msg.channel.send("Ban reason? `cancel` to cancel"); - const banReasonReply = await waitForReply(pluginData.client, msg.channel as TextChannel, msg.author.id); + const banReasonReply = await waitForReply(pluginData.client, msg.channel, msg.author.id); if (!banReasonReply || !banReasonReply.content || banReasonReply.content.toLowerCase().trim() === "cancel") { sendErrorMessage(pluginData, msg.channel, "Cancelled"); return; @@ -95,8 +94,8 @@ export const MassbanCmd = modActionsCmd({ pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, userId, 30 * MINUTES); await pluginData.guild.bans.create(userId as Snowflake, { - days: deleteDays, - reason: banReason ?? undefined, + deleteMessageSeconds: (deleteDays * DAYS) / SECONDS, + reason: banReason, }); await casesPlugin.createCase({ diff --git a/backend/src/plugins/ModActions/commands/MassUnbanCmd.ts b/backend/src/plugins/ModActions/commands/MassUnbanCmd.ts index 83044c85..c5ce37ac 100644 --- a/backend/src/plugins/ModActions/commands/MassUnbanCmd.ts +++ b/backend/src/plugins/ModActions/commands/MassUnbanCmd.ts @@ -1,16 +1,15 @@ -import { Snowflake, TextChannel } from "discord.js"; -import { waitForReply } from "knub/dist/helpers"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { Snowflake } from "discord.js"; +import { waitForReply } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { ignoreEvent } from "../functions/ignoreEvent"; import { isBanned } from "../functions/isBanned"; import { IgnoredEventType, modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export const MassunbanCmd = modActionsCmd({ trigger: "massunban", @@ -32,7 +31,7 @@ export const MassunbanCmd = modActionsCmd({ // Ask for unban reason (cleaner this way instead of trying to cram it into the args) msg.channel.send("Unban reason? `cancel` to cancel"); - const unbanReasonReply = await waitForReply(pluginData.client, msg.channel as TextChannel, msg.author.id); + const unbanReasonReply = await waitForReply(pluginData.client, msg.channel, msg.author.id); if (!unbanReasonReply || !unbanReasonReply.content || unbanReasonReply.content.toLowerCase().trim() === "cancel") { sendErrorMessage(pluginData, msg.channel, "Cancelled"); return; diff --git a/backend/src/plugins/ModActions/commands/MassmuteCmd.ts b/backend/src/plugins/ModActions/commands/MassmuteCmd.ts index ecd696df..acdd880f 100644 --- a/backend/src/plugins/ModActions/commands/MassmuteCmd.ts +++ b/backend/src/plugins/ModActions/commands/MassmuteCmd.ts @@ -1,14 +1,13 @@ -import { Snowflake, TextChannel } from "discord.js"; -import { waitForReply } from "knub/dist/helpers"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { Snowflake } from "discord.js"; +import { waitForReply } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { MutesPlugin } from "../../../plugins/Mutes/MutesPlugin"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export const MassmuteCmd = modActionsCmd({ trigger: "massmute", @@ -30,7 +29,7 @@ export const MassmuteCmd = modActionsCmd({ // Ask for mute reason msg.channel.send("Mute reason? `cancel` to cancel"); - const muteReasonReceived = await waitForReply(pluginData.client, msg.channel as TextChannel, msg.author.id); + const muteReasonReceived = await waitForReply(pluginData.client, msg.channel, msg.author.id); if ( !muteReasonReceived || !muteReasonReceived.content || diff --git a/backend/src/plugins/ModActions/commands/NoteCmd.ts b/backend/src/plugins/ModActions/commands/NoteCmd.ts index 569831a8..ad183ba1 100644 --- a/backend/src/plugins/ModActions/commands/NoteCmd.ts +++ b/backend/src/plugins/ModActions/commands/NoteCmd.ts @@ -1,13 +1,11 @@ -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; -import { LogType } from "../../../data/LogType"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveUser } from "../../../utils"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export const NoteCmd = modActionsCmd({ trigger: "note", diff --git a/backend/src/plugins/ModActions/commands/UnbanCmd.ts b/backend/src/plugins/ModActions/commands/UnbanCmd.ts index 2c9bbfd3..cda36959 100644 --- a/backend/src/plugins/ModActions/commands/UnbanCmd.ts +++ b/backend/src/plugins/ModActions/commands/UnbanCmd.ts @@ -1,16 +1,15 @@ import { Snowflake } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; +import { clearExpiringTempban } from "../../../data/loops/expiringTempbansLoop"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveUser } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { formatReasonWithAttachments } from "../functions/formatReasonWithAttachments"; import { ignoreEvent } from "../functions/ignoreEvent"; import { IgnoredEventType, modActionsCmd } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { clearExpiringTempban } from "../../../data/loops/expiringTempbansLoop"; const opts = { mod: ct.member({ option: true }), diff --git a/backend/src/plugins/ModActions/commands/WarnCmd.ts b/backend/src/plugins/ModActions/commands/WarnCmd.ts index 03d79b76..c15c82ca 100644 --- a/backend/src/plugins/ModActions/commands/WarnCmd.ts +++ b/backend/src/plugins/ModActions/commands/WarnCmd.ts @@ -1,4 +1,3 @@ -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { CaseTypes } from "../../../data/CaseTypes"; import { canActOn, hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; @@ -94,7 +93,7 @@ export const WarnCmd = modActionsCmd({ ppId: mod.id !== msg.author.id ? msg.author.id : undefined, reason, }, - retryPromptChannel: msg.channel as TextChannel, + retryPromptChannel: msg.channel, }); if (warnResult.status === "failed") { diff --git a/backend/src/plugins/ModActions/events/CreateBanCaseOnManualBanEvt.ts b/backend/src/plugins/ModActions/events/CreateBanCaseOnManualBanEvt.ts index 148ebd5b..929c1e25 100644 --- a/backend/src/plugins/ModActions/events/CreateBanCaseOnManualBanEvt.ts +++ b/backend/src/plugins/ModActions/events/CreateBanCaseOnManualBanEvt.ts @@ -1,15 +1,14 @@ -import { GuildAuditLogs, User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { AuditLogEvent, User } from "discord.js"; import { CaseTypes } from "../../../data/CaseTypes"; import { Case } from "../../../data/entities/Case"; -import { LogType } from "../../../data/LogType"; import { resolveUser, UnknownUser } from "../../../utils"; +import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { clearIgnoredEvents } from "../functions/clearIgnoredEvents"; import { isEventIgnored } from "../functions/isEventIgnored"; import { IgnoredEventType, modActionsEvt } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; /** * Create a BAN case automatically when a user is banned manually. @@ -24,7 +23,11 @@ export const CreateBanCaseOnManualBanEvt = modActionsEvt({ return; } - const relevantAuditLogEntry = await findMatchingAuditLogEntry(pluginData.guild, "MEMBER_BAN_ADD", user.id); + const relevantAuditLogEntry = await findMatchingAuditLogEntry( + pluginData.guild, + AuditLogEvent.MemberBanAdd, + user.id, + ); const casesPlugin = pluginData.getPlugin(CasesPlugin); diff --git a/backend/src/plugins/ModActions/events/CreateKickCaseOnManualKickEvt.ts b/backend/src/plugins/ModActions/events/CreateKickCaseOnManualKickEvt.ts index b2a7f822..d03d3763 100644 --- a/backend/src/plugins/ModActions/events/CreateKickCaseOnManualKickEvt.ts +++ b/backend/src/plugins/ModActions/events/CreateKickCaseOnManualKickEvt.ts @@ -1,16 +1,14 @@ -import { GuildAuditLogs, User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { AuditLogEvent, User } from "discord.js"; import { CaseTypes } from "../../../data/CaseTypes"; import { Case } from "../../../data/entities/Case"; -import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { resolveUser, UnknownUser } from "../../../utils"; +import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { clearIgnoredEvents } from "../functions/clearIgnoredEvents"; import { isEventIgnored } from "../functions/isEventIgnored"; import { IgnoredEventType, modActionsEvt } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; /** * Create a KICK case automatically when a user is kicked manually. @@ -24,7 +22,7 @@ export const CreateKickCaseOnManualKickEvt = modActionsEvt({ return; } - const kickAuditLogEntry = await findMatchingAuditLogEntry(pluginData.guild, "MEMBER_KICK", member.id); + const kickAuditLogEntry = await findMatchingAuditLogEntry(pluginData.guild, AuditLogEvent.MemberKick, member.id); let mod: User | UnknownUser | null = null; let createdCase: Case | null = null; diff --git a/backend/src/plugins/ModActions/events/CreateUnbanCaseOnManualUnbanEvt.ts b/backend/src/plugins/ModActions/events/CreateUnbanCaseOnManualUnbanEvt.ts index 3498f834..e6e8fbb3 100644 --- a/backend/src/plugins/ModActions/events/CreateUnbanCaseOnManualUnbanEvt.ts +++ b/backend/src/plugins/ModActions/events/CreateUnbanCaseOnManualUnbanEvt.ts @@ -1,15 +1,13 @@ -import { GuildAuditLogs, User } from "discord.js"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { AuditLogEvent, User } from "discord.js"; import { CaseTypes } from "../../../data/CaseTypes"; import { Case } from "../../../data/entities/Case"; -import { LogType } from "../../../data/LogType"; import { resolveUser, UnknownUser } from "../../../utils"; +import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { clearIgnoredEvents } from "../functions/clearIgnoredEvents"; import { isEventIgnored } from "../functions/isEventIgnored"; import { IgnoredEventType, modActionsEvt } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { findMatchingAuditLogEntry } from "../../../utils/findMatchingAuditLogEntry"; /** * Create an UNBAN case automatically when a user is unbanned manually. @@ -24,7 +22,11 @@ export const CreateUnbanCaseOnManualUnbanEvt = modActionsEvt({ return; } - const relevantAuditLogEntry = await findMatchingAuditLogEntry(pluginData.guild, "MEMBER_BAN_REMOVE", user.id); + const relevantAuditLogEntry = await findMatchingAuditLogEntry( + pluginData.guild, + AuditLogEvent.MemberBanRemove, + user.id, + ); const casesPlugin = pluginData.getPlugin(CasesPlugin); diff --git a/backend/src/plugins/ModActions/events/PostAlertOnMemberJoinEvt.ts b/backend/src/plugins/ModActions/events/PostAlertOnMemberJoinEvt.ts index 39a11c25..d64ec08b 100644 --- a/backend/src/plugins/ModActions/events/PostAlertOnMemberJoinEvt.ts +++ b/backend/src/plugins/ModActions/events/PostAlertOnMemberJoinEvt.ts @@ -1,5 +1,4 @@ -import { Permissions, Snowflake, TextChannel } from "discord.js"; -import { LogType } from "../../../data/LogType"; +import { PermissionsBitField, Snowflake, TextChannel } from "discord.js"; import { resolveMember } from "../../../utils"; import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions"; import { LogsPlugin } from "../../Logs/LogsPlugin"; @@ -39,7 +38,7 @@ export const PostAlertOnMemberJoinEvt = modActionsEvt({ const botMember = await resolveMember(pluginData.client, pluginData.guild, pluginData.client.user!.id); const botPerms = alertChannel.permissionsFor(botMember ?? pluginData.client.user!.id); - if (!hasDiscordPermissions(botPerms, Permissions.FLAGS.SEND_MESSAGES)) { + if (!hasDiscordPermissions(botPerms, PermissionsBitField.Flags.SendMessages)) { logs.logBotAlert({ body: `Missing "Send Messages" permissions for the \`alert_channel\` configured in \`mod_actions\`: \`${alertChannelId}\``, }); diff --git a/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts b/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts index 92178edc..bd7c3f3d 100644 --- a/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualKickMemberCmd.ts @@ -1,9 +1,9 @@ -import { GuildMember, TextChannel, ThreadChannel } from "discord.js"; +import { GuildMember, GuildTextBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { hasPermission } from "knub/dist/helpers"; +import { hasPermission } from "knub/helpers"; import { LogType } from "../../../data/LogType"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { errorMessage, resolveMember, resolveUser } from "../../../utils"; +import { DAYS, errorMessage, resolveMember, resolveUser, SECONDS } from "../../../utils"; import { IgnoredEventType, ModActionsPluginType } from "../types"; import { formatReasonWithAttachments } from "./formatReasonWithAttachments"; import { ignoreEvent } from "./ignoreEvent"; @@ -19,7 +19,7 @@ export async function actualKickMemberCmd( reason: string; mod: GuildMember; notify?: string; - "notify-channel"?: TextChannel | ThreadChannel; + "notify-channel"?: GuildTextBasedChannel; clean?: boolean; }, ) { @@ -82,7 +82,7 @@ export async function actualKickMemberCmd( ignoreEvent(pluginData, IgnoredEventType.Ban, memberToKick.id); try { - await memberToKick.ban({ days: 1, reason: "kick -clean" }); + await memberToKick.ban({ deleteMessageSeconds: (1 * DAYS) / SECONDS, reason: "kick -clean" }); } catch { sendErrorMessage(pluginData, msg.channel, "Failed to ban the user to clean messages (-clean)"); } diff --git a/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts b/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts index 6a750fd5..fce169b7 100644 --- a/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualMuteUserCmd.ts @@ -1,4 +1,4 @@ -import { GuildMember, Message, TextChannel, ThreadChannel, User } from "discord.js"; +import { GuildMember, GuildTextBasedChannel, Message, User } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import { logger } from "../../../logger"; @@ -24,7 +24,7 @@ export async function actualMuteUserCmd( reason?: string; mod: GuildMember; notify?: string; - "notify-channel"?: TextChannel | ThreadChannel; + "notify-channel"?: GuildTextBasedChannel; }, ) { // The moderator who did the action is the message author or, if used, the specified -mod @@ -33,7 +33,7 @@ export async function actualMuteUserCmd( if (args.mod) { if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "You don't have permission to use -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } @@ -51,7 +51,7 @@ export async function actualMuteUserCmd( try { contactMethods = readContactMethodsFromArgs(args); } catch (e) { - sendErrorMessage(pluginData, msg.channel as TextChannel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); return; } @@ -65,9 +65,9 @@ export async function actualMuteUserCmd( }); } catch (e) { if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not mute the user: no mute role set in config"); + sendErrorMessage(pluginData, msg.channel, "Could not mute the user: no mute role set in config"); } else if (isDiscordAPIError(e) && e.code === 10007) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not mute the user: unknown member"); + sendErrorMessage(pluginData, msg.channel, "Could not mute the user: unknown member"); } else { logger.error(`Failed to mute user ${user.id}: ${e.stack}`); if (user.id == null) { @@ -75,14 +75,14 @@ export async function actualMuteUserCmd( // tslint:disable-next-line:no-console console.trace("[DEBUG] Null user.id for mute"); } - sendErrorMessage(pluginData, msg.channel as TextChannel, "Could not mute the user"); + sendErrorMessage(pluginData, msg.channel, "Could not mute the user"); } return; } // Confirm the action to the moderator - let response; + let response: string; if (args.time) { if (muteResult.updatedExistingMute) { response = asSingleLine(` @@ -110,5 +110,5 @@ export async function actualMuteUserCmd( } if (muteResult.notifyResult.text) response += ` (${muteResult.notifyResult.text})`; - sendSuccessMessage(pluginData, msg.channel as TextChannel, response); + sendSuccessMessage(pluginData, msg.channel, response); } diff --git a/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts b/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts index 46ab7be6..ee1557eb 100644 --- a/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualUnmuteUserCmd.ts @@ -1,4 +1,4 @@ -import { GuildMember, Message, TextChannel, User } from "discord.js"; +import { GuildMember, Message, User } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import { MutesPlugin } from "../../../plugins/Mutes/MutesPlugin"; @@ -19,7 +19,7 @@ export async function actualUnmuteCmd( if (args.mod) { if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg, channelId: msg.channel.id }))) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "You don't have permission to use -mod"); + sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod"); return; } @@ -37,7 +37,7 @@ export async function actualUnmuteCmd( }); if (!result) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "User is not muted!"); + sendErrorMessage(pluginData, msg.channel, "User is not muted!"); return; } @@ -46,7 +46,7 @@ export async function actualUnmuteCmd( const timeUntilUnmute = args.time && humanizeDuration(args.time); sendSuccessMessage( pluginData, - msg.channel as TextChannel, + msg.channel, asSingleLine(` Unmuting **${user.tag}** in ${timeUntilUnmute} (Case #${result.case.case_number}) @@ -55,7 +55,7 @@ export async function actualUnmuteCmd( } else { sendSuccessMessage( pluginData, - msg.channel as TextChannel, + msg.channel, asSingleLine(` Unmuted **${user.tag}** (Case #${result.case.case_number}) diff --git a/backend/src/plugins/ModActions/functions/banUserId.ts b/backend/src/plugins/ModActions/functions/banUserId.ts index 9833ce2c..6a9dc7d5 100644 --- a/backend/src/plugins/ModActions/functions/banUserId.ts +++ b/backend/src/plugins/ModActions/functions/banUserId.ts @@ -1,25 +1,26 @@ import { DiscordAPIError, Snowflake, User } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; +import { registerExpiringTempban } from "../../../data/loops/expiringTempbansLoop"; import { logger } from "../../../logger"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; import { createUserNotificationError, + DAYS, notifyUser, resolveUser, - stripObjectToScalars, + SECONDS, ucfirst, UserNotificationResult, } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { BanOptions, BanResult, IgnoredEventType, ModActionsPluginType } from "../types"; import { getDefaultContactMethods } from "./getDefaultContactMethods"; import { ignoreEvent } from "./ignoreEvent"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { registerExpiringTempban } from "../../../data/loops/expiringTempbansLoop"; /** * Ban the specified user id, whether or not they're actually on the server at the time. Generates a case. @@ -87,7 +88,7 @@ export async function banUserId( try { const deleteMessageDays = Math.min(7, Math.max(0, banOptions.deleteMessageDays ?? 1)); await pluginData.guild.bans.create(userId as Snowflake, { - days: deleteMessageDays, + deleteMessageSeconds: (deleteMessageDays * DAYS) / SECONDS, reason: reason ?? undefined, }); } catch (e) { diff --git a/backend/src/plugins/ModActions/functions/clearTempban.ts b/backend/src/plugins/ModActions/functions/clearTempban.ts index 00ef5849..25807df8 100644 --- a/backend/src/plugins/ModActions/functions/clearTempban.ts +++ b/backend/src/plugins/ModActions/functions/clearTempban.ts @@ -5,14 +5,14 @@ import moment from "moment-timezone"; import { LogType } from "src/data/LogType"; import { logger } from "src/logger"; import { CaseTypes } from "../../../data/CaseTypes"; -import { resolveUser, SECONDS } from "../../../utils"; +import { Tempban } from "../../../data/entities/Tempban"; +import { resolveUser } from "../../../utils"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { IgnoredEventType, ModActionsPluginType } from "../types"; import { formatReasonWithAttachments } from "./formatReasonWithAttachments"; import { ignoreEvent } from "./ignoreEvent"; import { isBanned } from "./isBanned"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { Tempban } from "../../../data/entities/Tempban"; export async function clearTempban(pluginData: GuildPluginData, tempban: Tempban) { if (!(await isBanned(pluginData, tempban.user_id))) { diff --git a/backend/src/plugins/ModActions/functions/formatReasonWithAttachments.ts b/backend/src/plugins/ModActions/functions/formatReasonWithAttachments.ts index 77194a3f..3fd92ee8 100644 --- a/backend/src/plugins/ModActions/functions/formatReasonWithAttachments.ts +++ b/backend/src/plugins/ModActions/functions/formatReasonWithAttachments.ts @@ -1,6 +1,6 @@ -import { MessageAttachment } from "discord.js"; +import { Attachment } from "discord.js"; -export function formatReasonWithAttachments(reason: string, attachments: MessageAttachment[]) { +export function formatReasonWithAttachments(reason: string, attachments: Attachment[]) { const attachmentUrls = attachments.map((a) => a.url); return ((reason || "") + " " + attachmentUrls.join(" ")).trim(); } diff --git a/backend/src/plugins/ModActions/functions/isBanned.ts b/backend/src/plugins/ModActions/functions/isBanned.ts index 57ea8bf4..839adf8d 100644 --- a/backend/src/plugins/ModActions/functions/isBanned.ts +++ b/backend/src/plugins/ModActions/functions/isBanned.ts @@ -1,6 +1,5 @@ -import { Permissions, Snowflake } from "discord.js"; +import { PermissionsBitField, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; -import { LogType } from "../../../data/LogType"; import { isDiscordAPIError, isDiscordHTTPError, SECONDS, sleep } from "../../../utils"; import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions"; import { LogsPlugin } from "../../Logs/LogsPlugin"; @@ -12,7 +11,7 @@ export async function isBanned( timeout: number = 5 * SECONDS, ): Promise { const botMember = pluginData.guild.members.cache.get(pluginData.client.user!.id); - if (botMember && !hasDiscordPermissions(botMember.permissions, Permissions.FLAGS.BAN_MEMBERS)) { + if (botMember && !hasDiscordPermissions(botMember.permissions, PermissionsBitField.Flags.BanMembers)) { pluginData.getPlugin(LogsPlugin).logBotAlert({ body: `Missing "Ban Members" permission to check for existing bans`, }); diff --git a/backend/src/plugins/ModActions/functions/kickMember.ts b/backend/src/plugins/ModActions/functions/kickMember.ts index d70722a1..89559364 100644 --- a/backend/src/plugins/ModActions/functions/kickMember.ts +++ b/backend/src/plugins/ModActions/functions/kickMember.ts @@ -1,15 +1,15 @@ import { GuildMember } from "discord.js"; import { GuildPluginData } from "knub"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; import { LogType } from "../../../data/LogType"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; import { createUserNotificationError, notifyUser, resolveUser, ucfirst, UserNotificationResult } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { IgnoredEventType, KickOptions, KickResult, ModActionsPluginType } from "../types"; import { getDefaultContactMethods } from "./getDefaultContactMethods"; import { ignoreEvent } from "./ignoreEvent"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; /** * Kick the specified server member. Generates a case. diff --git a/backend/src/plugins/ModActions/functions/readContactMethodsFromArgs.ts b/backend/src/plugins/ModActions/functions/readContactMethodsFromArgs.ts index 7d601508..a9503333 100644 --- a/backend/src/plugins/ModActions/functions/readContactMethodsFromArgs.ts +++ b/backend/src/plugins/ModActions/functions/readContactMethodsFromArgs.ts @@ -1,9 +1,9 @@ -import { TextChannel, ThreadChannel } from "discord.js"; +import { GuildTextBasedChannel } from "discord.js"; import { disableUserNotificationStrings, UserNotificationMethod } from "../../../utils"; export function readContactMethodsFromArgs(args: { notify?: string; - "notify-channel"?: TextChannel | ThreadChannel; + "notify-channel"?: GuildTextBasedChannel; }): null | UserNotificationMethod[] { if (args.notify) { if (args.notify === "dm") { diff --git a/backend/src/plugins/ModActions/functions/updateCase.ts b/backend/src/plugins/ModActions/functions/updateCase.ts index 2a1974be..ac6b65af 100644 --- a/backend/src/plugins/ModActions/functions/updateCase.ts +++ b/backend/src/plugins/ModActions/functions/updateCase.ts @@ -1,11 +1,10 @@ -import { Message, TextChannel } from "discord.js"; +import { Message } from "discord.js"; import { CaseTypes } from "../../../data/CaseTypes"; import { Case } from "../../../data/entities/Case"; -import { LogType } from "../../../data/LogType"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { formatReasonWithAttachments } from "./formatReasonWithAttachments"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { formatReasonWithAttachments } from "./formatReasonWithAttachments"; export async function updateCase(pluginData, msg: Message, args) { let theCase: Case | undefined; @@ -16,12 +15,12 @@ export async function updateCase(pluginData, msg: Message, args) { } if (!theCase) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Case not found"); + sendErrorMessage(pluginData, msg.channel, "Case not found"); return; } if (!args.note && msg.attachments.size === 0) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Text or attachment required"); + sendErrorMessage(pluginData, msg.channel, "Text or attachment required"); return; } @@ -41,5 +40,5 @@ export async function updateCase(pluginData, msg: Message, args) { note, }); - sendSuccessMessage(pluginData, msg.channel as TextChannel, `Case \`#${theCase.case_number}\` updated`); + sendSuccessMessage(pluginData, msg.channel, `Case \`#${theCase.case_number}\` updated`); } diff --git a/backend/src/plugins/ModActions/functions/warnMember.ts b/backend/src/plugins/ModActions/functions/warnMember.ts index e0f22c8f..c5357ee1 100644 --- a/backend/src/plugins/ModActions/functions/warnMember.ts +++ b/backend/src/plugins/ModActions/functions/warnMember.ts @@ -1,15 +1,14 @@ import { GuildMember, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; -import { LogType } from "../../../data/LogType"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; import { createUserNotificationError, notifyUser, resolveUser, ucfirst, UserNotificationResult } from "../../../utils"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { waitForButtonConfirm } from "../../../utils/waitForInteraction"; import { CasesPlugin } from "../../Cases/CasesPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { ModActionsPluginType, WarnOptions, WarnResult } from "../types"; import { getDefaultContactMethods } from "./getDefaultContactMethods"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export async function warnMember( pluginData: GuildPluginData, diff --git a/backend/src/plugins/ModActions/types.ts b/backend/src/plugins/ModActions/types.ts index 16e8e15e..a0341b12 100644 --- a/backend/src/plugins/ModActions/types.ts +++ b/backend/src/plugins/ModActions/types.ts @@ -1,7 +1,7 @@ -import { TextChannel } from "discord.js"; +import { GuildTextBasedChannel } from "discord.js"; import { EventEmitter } from "events"; import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { Case } from "../../data/entities/Case"; import { GuildCases } from "../../data/GuildCases"; import { GuildLogs } from "../../data/GuildLogs"; @@ -11,8 +11,6 @@ import { Queue } from "../../Queue"; import { tNullable, UserNotificationMethod, UserNotificationResult } from "../../utils"; import { CaseArgs } from "../Cases/types"; -import Timeout = NodeJS.Timeout; - export const ConfigSchema = t.type({ dm_on_warn: t.boolean, dm_on_kick: t.boolean, @@ -129,7 +127,7 @@ export type WarnMemberNotifyRetryCallback = () => boolean | Promise; export interface WarnOptions { caseArgs?: Partial | null; contactMethods?: UserNotificationMethod[] | null; - retryPromptChannel?: TextChannel | null; + retryPromptChannel?: GuildTextBasedChannel | null; isAutomodAction?: boolean; } @@ -149,5 +147,5 @@ export interface BanOptions { export type ModActionType = "note" | "warn" | "mute" | "unmute" | "kick" | "ban" | "unban"; -export const modActionsCmd = typedGuildCommand(); -export const modActionsEvt = typedGuildEventListener(); +export const modActionsCmd = guildPluginMessageCommand(); +export const modActionsEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Mutes/MutesPlugin.ts b/backend/src/plugins/Mutes/MutesPlugin.ts index eba6b40f..5797eed1 100644 --- a/backend/src/plugins/Mutes/MutesPlugin.ts +++ b/backend/src/plugins/Mutes/MutesPlugin.ts @@ -2,9 +2,10 @@ import { GuildMember, Snowflake } from "discord.js"; import { EventEmitter } from "events"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildCases } from "../../data/GuildCases"; +import { onGuildEvent } from "../../data/GuildEvents"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildMutes } from "../../data/GuildMutes"; -import { mapToPublicFn } from "../../pluginUtils"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { CasesPlugin } from "../Cases/CasesPlugin"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -13,15 +14,13 @@ import { ClearMutesCmd } from "./commands/ClearMutesCmd"; import { ClearMutesWithoutRoleCmd } from "./commands/ClearMutesWithoutRoleCmd"; import { MutesCmd } from "./commands/MutesCmd"; import { ClearActiveMuteOnMemberBanEvt } from "./events/ClearActiveMuteOnMemberBanEvt"; -import { ClearActiveMuteOnRoleRemovalEvt } from "./events/ClearActiveMuteOnRoleRemovalEvt"; import { ReapplyActiveMuteOnJoinEvt } from "./events/ReapplyActiveMuteOnJoinEvt"; +import { clearMute } from "./functions/clearMute"; import { muteUser } from "./functions/muteUser"; import { offMutesEvent } from "./functions/offMutesEvent"; import { onMutesEvent } from "./functions/onMutesEvent"; import { unmuteUser } from "./functions/unmuteUser"; import { ConfigSchema, MutesPluginType } from "./types"; -import { onGuildEvent } from "../../data/GuildEvents"; -import { clearMute } from "./functions/clearMute"; const defaultOptions = { config: { @@ -66,14 +65,15 @@ export const MutesPlugin = zeppelinGuildPlugin()({ showInDocs: true, info: { prettyName: "Mutes", + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, dependencies: () => [CasesPlugin, LogsPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ MutesCmd, ClearBannedMutesCmd, ClearMutesWithoutRoleCmd, @@ -105,22 +105,26 @@ export const MutesPlugin = zeppelinGuildPlugin()({ }, beforeLoad(pluginData) { - pluginData.state.mutes = GuildMutes.getGuildInstance(pluginData.guild.id); - pluginData.state.cases = GuildCases.getGuildInstance(pluginData.guild.id); - pluginData.state.serverLogs = new GuildLogs(pluginData.guild.id); - pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id); + const { state, guild } = pluginData; - pluginData.state.events = new EventEmitter(); + state.mutes = GuildMutes.getGuildInstance(guild.id); + state.cases = GuildCases.getGuildInstance(guild.id); + state.serverLogs = new GuildLogs(guild.id); + state.archives = GuildArchives.getGuildInstance(guild.id); + + state.events = new EventEmitter(); }, afterLoad(pluginData) { - pluginData.state.unregisterGuildEventListener = onGuildEvent(pluginData.guild.id, "expiredMute", (mute) => - clearMute(pluginData, mute), - ); + const { state, guild } = pluginData; + + state.unregisterGuildEventListener = onGuildEvent(guild.id, "expiredMute", (mute) => clearMute(pluginData, mute)); }, beforeUnload(pluginData) { - pluginData.state.unregisterGuildEventListener?.(); - pluginData.state.events.removeAllListeners(); + const { state, guild } = pluginData; + + state.unregisterGuildEventListener?.(); + state.events.removeAllListeners(); }, }); diff --git a/backend/src/plugins/Mutes/commands/ClearBannedMutesCmd.ts b/backend/src/plugins/Mutes/commands/ClearBannedMutesCmd.ts index cd18bf0f..29f9dc28 100644 --- a/backend/src/plugins/Mutes/commands/ClearBannedMutesCmd.ts +++ b/backend/src/plugins/Mutes/commands/ClearBannedMutesCmd.ts @@ -1,4 +1,4 @@ -import { Snowflake, User } from "discord.js"; +import { Snowflake } from "discord.js"; import { sendSuccessMessage } from "../../../pluginUtils"; import { mutesCmd } from "../types"; diff --git a/backend/src/plugins/Mutes/commands/MutesCmd.ts b/backend/src/plugins/Mutes/commands/MutesCmd.ts index 5c4e9d3a..699b8b11 100644 --- a/backend/src/plugins/Mutes/commands/MutesCmd.ts +++ b/backend/src/plugins/Mutes/commands/MutesCmd.ts @@ -1,4 +1,11 @@ -import { GuildMember, MessageActionRow, MessageButton, MessageComponentInteraction, Snowflake } from "discord.js"; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + GuildMember, + MessageComponentInteraction, + Snowflake, +} from "discord.js"; import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { humanizeDurationShort } from "../../../humanizeDurationShort"; @@ -192,13 +199,12 @@ export const MutesCmd = mutesCmd({ if (totalPages > 1) { const idMod = `${listMessage.id}:muteList`; - const buttons: MessageButton[] = []; + const buttons = [ + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setEmoji("⬅").setCustomId(`previousButton:${idMod}`), + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setEmoji("➡").setCustomId(`nextButton:${idMod}`), + ] satisfies ButtonBuilder[]; - buttons.push(new MessageButton().setStyle("SECONDARY").setEmoji("⬅").setCustomId(`previousButton:${idMod}`)); - - buttons.push(new MessageButton().setStyle("SECONDARY").setEmoji("➡").setCustomId(`nextButton:${idMod}`)); - - const row = new MessageActionRow().addComponents(buttons); + const row = new ActionRowBuilder().addComponents(buttons); await listMessage.edit({ components: [row] }); const collector = listMessage.createMessageComponentCollector({ time: stopCollectionDebounce }); @@ -207,6 +213,7 @@ export const MutesCmd = mutesCmd({ if (msg.author.id !== interaction.user.id) { interaction .reply({ content: `You are not permitted to use these buttons.`, ephemeral: true }) + // tslint:disable-next-line no-console .catch((err) => console.trace(err.message)); } else { collector.resetTimer(); diff --git a/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts b/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts index 20611e45..8c8c5396 100644 --- a/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts +++ b/backend/src/plugins/Mutes/events/ReapplyActiveMuteOnJoinEvt.ts @@ -1,9 +1,7 @@ import { Snowflake } from "discord.js"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; import { memberRolesLock } from "../../../utils/lockNameHelpers"; -import { mutesEvt } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { mutesEvt } from "../types"; /** * Reapply active mutes on join diff --git a/backend/src/plugins/Mutes/functions/clearMute.ts b/backend/src/plugins/Mutes/functions/clearMute.ts index e7167474..e4a28c92 100644 --- a/backend/src/plugins/Mutes/functions/clearMute.ts +++ b/backend/src/plugins/Mutes/functions/clearMute.ts @@ -1,11 +1,11 @@ +import { GuildMember } from "discord.js"; +import { GuildPluginData } from "knub"; import { Mute } from "../../../data/entities/Mute"; +import { clearExpiringMute } from "../../../data/loops/expiringMutesLoop"; import { resolveMember, verboseUserMention } from "../../../utils"; import { memberRolesLock } from "../../../utils/lockNameHelpers"; import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { GuildPluginData } from "knub"; import { MutesPluginType } from "../types"; -import { clearExpiringMute } from "../../../data/loops/expiringMutesLoop"; -import { GuildMember } from "discord.js"; export async function clearMute( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Mutes/functions/muteUser.ts b/backend/src/plugins/Mutes/functions/muteUser.ts index 3956be8a..8677c162 100644 --- a/backend/src/plugins/Mutes/functions/muteUser.ts +++ b/backend/src/plugins/Mutes/functions/muteUser.ts @@ -1,9 +1,10 @@ -import { Snowflake, TextChannel, User } from "discord.js"; +import { Snowflake, User } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; import { Case } from "../../../data/entities/Case"; +import { Mute } from "../../../data/entities/Mute"; +import { registerExpiringMute } from "../../../data/loops/expiringMutesLoop"; import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter"; @@ -16,10 +17,9 @@ import { UserNotificationResult, } from "../../../utils"; import { muteLock } from "../../../utils/lockNameHelpers"; +import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CasesPlugin } from "../../Cases/CasesPlugin"; import { MuteOptions, MutesPluginType } from "../types"; -import { Mute } from "../../../data/entities/Mute"; -import { registerExpiringMute } from "../../../data/loops/expiringMutesLoop"; /** * TODO: Clean up this function @@ -186,7 +186,7 @@ export async function muteUser( const channel = config.message_channel ? pluginData.guild.channels.cache.get(config.message_channel as Snowflake) : null; - if (useChannel && channel?.isText()) { + if (useChannel && channel?.isTextBased()) { contactMethods.push({ type: "channel", channel }); } } diff --git a/backend/src/plugins/Mutes/functions/unmuteUser.ts b/backend/src/plugins/Mutes/functions/unmuteUser.ts index a3c55b08..00a7123a 100644 --- a/backend/src/plugins/Mutes/functions/unmuteUser.ts +++ b/backend/src/plugins/Mutes/functions/unmuteUser.ts @@ -1,17 +1,14 @@ import { Snowflake } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; -import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; -import { LogType } from "../../../data/LogType"; import { resolveMember, resolveUser } from "../../../utils"; -import { memberRolesLock } from "../../../utils/lockNameHelpers"; import { CasesPlugin } from "../../Cases/CasesPlugin"; import { CaseArgs } from "../../Cases/types"; -import { MutesPluginType, UnmuteResult } from "../types"; -import { memberHasMutedRole } from "./memberHasMutedRole"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { MutesPluginType, UnmuteResult } from "../types"; import { clearMute } from "./clearMute"; +import { memberHasMutedRole } from "./memberHasMutedRole"; export async function unmuteUser( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Mutes/types.ts b/backend/src/plugins/Mutes/types.ts index b612b029..9fe1e575 100644 --- a/backend/src/plugins/Mutes/types.ts +++ b/backend/src/plugins/Mutes/types.ts @@ -1,7 +1,7 @@ import { GuildMember } from "discord.js"; import { EventEmitter } from "events"; import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { Case } from "../../data/entities/Case"; import { Mute } from "../../data/entities/Mute"; import { GuildArchives } from "../../data/GuildArchives"; @@ -11,8 +11,6 @@ import { GuildMutes } from "../../data/GuildMutes"; import { tNullable, UserNotificationMethod, UserNotificationResult } from "../../utils"; import { CaseArgs } from "../Cases/types"; -import Timeout = NodeJS.Timeout; - export const ConfigSchema = t.type({ mute_role: tNullable(t.string), move_to_voice_channel: tNullable(t.string), @@ -79,5 +77,5 @@ export interface MuteOptions { isAutomodAction?: boolean; } -export const mutesCmd = typedGuildCommand(); -export const mutesEvt = typedGuildEventListener(); +export const mutesCmd = guildPluginMessageCommand(); +export const mutesEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/NameHistory/NameHistoryPlugin.ts b/backend/src/plugins/NameHistory/NameHistoryPlugin.ts index 0efb5fe3..9a9948c8 100644 --- a/backend/src/plugins/NameHistory/NameHistoryPlugin.ts +++ b/backend/src/plugins/NameHistory/NameHistoryPlugin.ts @@ -1,10 +1,10 @@ import { PluginOptions } from "knub"; import { GuildNicknameHistory } from "../../data/GuildNicknameHistory"; import { UsernameHistory } from "../../data/UsernameHistory"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { Queue } from "../../Queue"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { NamesCmd } from "./commands/NamesCmd"; -import { ChannelJoinEvt, MessageCreateEvt } from "./events/UpdateNameEvts"; import { ConfigSchema, NameHistoryPluginType } from "./types"; const defaultOptions: PluginOptions = { @@ -25,11 +25,11 @@ export const NameHistoryPlugin = zeppelinGuildPlugin()({ name: "name_history", showInDocs: false, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ NamesCmd, ], diff --git a/backend/src/plugins/NameHistory/commands/NamesCmd.ts b/backend/src/plugins/NameHistory/commands/NamesCmd.ts index fcb8c242..81e896f5 100644 --- a/backend/src/plugins/NameHistory/commands/NamesCmd.ts +++ b/backend/src/plugins/NameHistory/commands/NamesCmd.ts @@ -1,5 +1,5 @@ import { Snowflake } from "discord.js"; -import { createChunkedMessage, disableCodeBlocks } from "knub/dist/helpers"; +import { createChunkedMessage, disableCodeBlocks } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { NICKNAME_RETENTION_PERIOD } from "../../../data/cleanup/nicknames"; import { MAX_NICKNAME_ENTRIES_PER_USER } from "../../../data/GuildNicknameHistory"; diff --git a/backend/src/plugins/NameHistory/types.ts b/backend/src/plugins/NameHistory/types.ts index 8102b59f..f2c81db1 100644 --- a/backend/src/plugins/NameHistory/types.ts +++ b/backend/src/plugins/NameHistory/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildNicknameHistory } from "../../data/GuildNicknameHistory"; import { UsernameHistory } from "../../data/UsernameHistory"; import { Queue } from "../../Queue"; @@ -18,5 +18,5 @@ export interface NameHistoryPluginType extends BasePluginType { }; } -export const nameHistoryCmd = typedGuildCommand(); -export const nameHistoryEvt = typedGuildEventListener(); +export const nameHistoryCmd = guildPluginMessageCommand(); +export const nameHistoryEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Persist/PersistPlugin.ts b/backend/src/plugins/Persist/PersistPlugin.ts index 3ba19fc3..8b53d9f2 100644 --- a/backend/src/plugins/Persist/PersistPlugin.ts +++ b/backend/src/plugins/Persist/PersistPlugin.ts @@ -1,6 +1,7 @@ import { PluginOptions } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildPersistedData } from "../../data/GuildPersistedData"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -25,10 +26,11 @@ export const PersistPlugin = zeppelinGuildPlugin()({ Re-apply roles or nicknames for users when they rejoin the server. Mute roles are re-applied automatically, this plugin is not required for that. `), + configSchema: ConfigSchema, }, dependencies: () => [LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore diff --git a/backend/src/plugins/Persist/events/LoadDataEvt.ts b/backend/src/plugins/Persist/events/LoadDataEvt.ts index 699fb703..ef97c616 100644 --- a/backend/src/plugins/Persist/events/LoadDataEvt.ts +++ b/backend/src/plugins/Persist/events/LoadDataEvt.ts @@ -1,7 +1,5 @@ -import { GuildMemberEditData, Permissions } from "discord.js"; +import { GuildMemberEditOptions, PermissionFlagsBits } from "discord.js"; import intersection from "lodash.intersection"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; import { canAssignRole } from "../../../utils/canAssignRole"; import { getMissingPermissions } from "../../../utils/getMissingPermissions"; import { memberRolesLock } from "../../../utils/lockNameHelpers"; @@ -9,7 +7,7 @@ import { missingPermissionError } from "../../../utils/missingPermissionError"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { persistEvt } from "../types"; -const p = Permissions.FLAGS; +const p = PermissionFlagsBits; export const LoadDataEvt = persistEvt({ event: "guildMemberAdd", @@ -26,15 +24,17 @@ export const LoadDataEvt = persistEvt({ return; } - const toRestore: GuildMemberEditData = {}; + const toRestore: GuildMemberEditOptions = { + reason: "Restored upon rejoin", + }; const config = await pluginData.config.getForMember(member); const restoredData: string[] = []; // Check permissions const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; let requiredPermissions = 0n; - if (config.persist_nicknames) requiredPermissions |= p.MANAGE_NICKNAMES; - if (config.persisted_roles) requiredPermissions |= p.MANAGE_ROLES; + if (config.persist_nicknames) requiredPermissions |= p.ManageNicknames; + if (config.persisted_roles) requiredPermissions |= p.ManageRoles; const missingPermissions = getMissingPermissions(me.permissions, requiredPermissions); if (missingPermissions) { pluginData.getPlugin(LogsPlugin).logBotAlert({ @@ -73,7 +73,7 @@ export const LoadDataEvt = persistEvt({ } if (restoredData.length) { - await member.edit(toRestore, "Restored upon rejoin"); + await member.edit(toRestore); await pluginData.state.persistedData.clear(member.id); pluginData.getPlugin(LogsPlugin).logMemberRestore({ diff --git a/backend/src/plugins/Persist/types.ts b/backend/src/plugins/Persist/types.ts index 8475f4be..8ab258b7 100644 --- a/backend/src/plugins/Persist/types.ts +++ b/backend/src/plugins/Persist/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildPersistedData } from "../../data/GuildPersistedData"; @@ -19,4 +19,4 @@ export interface PersistPluginType extends BasePluginType { }; } -export const persistEvt = typedGuildEventListener(); +export const persistEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Phisherman/PhishermanPlugin.ts b/backend/src/plugins/Phisherman/PhishermanPlugin.ts index 4b8a9d5e..e8f74ce3 100644 --- a/backend/src/plugins/Phisherman/PhishermanPlugin.ts +++ b/backend/src/plugins/Phisherman/PhishermanPlugin.ts @@ -1,16 +1,10 @@ -import { PluginOptions, typedGuildCommand } from "knub"; -import { GuildPingableRoles } from "../../data/GuildPingableRoles"; +import { PluginOptions } from "knub"; +import { hasPhishermanMasterAPIKey, phishermanApiKeyIsValid } from "../../data/Phisherman"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; -import { ConfigSchema, PhishermanPluginType } from "./types"; -import { - getPhishermanDomainInfo, - hasPhishermanMasterAPIKey, - phishermanApiKeyIsValid, - reportTrackedDomainsToPhisherman, -} from "../../data/Phisherman"; -import { mapToPublicFn } from "../../pluginUtils"; import { getDomainInfo } from "./functions/getDomainInfo"; import { pluginInfo } from "./info"; +import { ConfigSchema, PhishermanPluginType } from "./types"; const defaultOptions: PluginOptions = { config: { @@ -24,7 +18,7 @@ export const PhishermanPlugin = zeppelinGuildPlugin()({ showInDocs: true, info: pluginInfo, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore @@ -33,6 +27,8 @@ export const PhishermanPlugin = zeppelinGuildPlugin()({ }, async beforeLoad(pluginData) { + const { state } = pluginData; + pluginData.state.validApiKey = null; if (!hasPhishermanMasterAPIKey()) { @@ -49,7 +45,7 @@ export const PhishermanPlugin = zeppelinGuildPlugin()({ return false; }); if (isValid) { - pluginData.state.validApiKey = apiKey; + state.validApiKey = apiKey; } } }, diff --git a/backend/src/plugins/Phisherman/functions/getDomainInfo.ts b/backend/src/plugins/Phisherman/functions/getDomainInfo.ts index 8f61bd41..2714ec01 100644 --- a/backend/src/plugins/Phisherman/functions/getDomainInfo.ts +++ b/backend/src/plugins/Phisherman/functions/getDomainInfo.ts @@ -1,7 +1,7 @@ import { GuildPluginData } from "knub"; -import { PhishermanPluginType } from "../types"; -import { PhishermanDomainInfo } from "../../../data/types/phisherman"; import { getPhishermanDomainInfo, phishermanDomainIsSafe, trackPhishermanCaughtDomain } from "../../../data/Phisherman"; +import { PhishermanDomainInfo } from "../../../data/types/phisherman"; +import { PhishermanPluginType } from "../types"; export async function getDomainInfo( pluginData: GuildPluginData, diff --git a/backend/src/plugins/Phisherman/info.ts b/backend/src/plugins/Phisherman/info.ts index 5c1ea94f..826da43d 100644 --- a/backend/src/plugins/Phisherman/info.ts +++ b/backend/src/plugins/Phisherman/info.ts @@ -1,5 +1,6 @@ import { trimPluginDescription } from "../../utils"; import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint"; +import { ConfigSchema } from "./types"; export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = { prettyName: "Phisherman", @@ -38,4 +39,5 @@ export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = { clean: true ~~~ `), + configSchema: ConfigSchema, }; diff --git a/backend/src/plugins/PingableRoles/PingableRolesPlugin.ts b/backend/src/plugins/PingableRoles/PingableRolesPlugin.ts index e1492f9e..331410f9 100644 --- a/backend/src/plugins/PingableRoles/PingableRolesPlugin.ts +++ b/backend/src/plugins/PingableRoles/PingableRolesPlugin.ts @@ -1,9 +1,9 @@ import { PluginOptions } from "knub"; import { GuildPingableRoles } from "../../data/GuildPingableRoles"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { PingableRoleDisableCmd } from "./commands/PingableRoleDisableCmd"; import { PingableRoleEnableCmd } from "./commands/PingableRoleEnableCmd"; -import { MessageCreateDisablePingableEvt, TypingEnablePingableEvt } from "./events/ChangePingableEvts"; import { ConfigSchema, PingableRolesPluginType } from "./types"; const defaultOptions: PluginOptions = { @@ -25,13 +25,14 @@ export const PingableRolesPlugin = zeppelinGuildPlugin( showInDocs: true, info: { prettyName: "Pingable roles", + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ PingableRoleEnableCmd, PingableRoleDisableCmd, ], diff --git a/backend/src/plugins/PingableRoles/events/ChangePingableEvts.ts b/backend/src/plugins/PingableRoles/events/ChangePingableEvts.ts index 8153f094..f91b7264 100644 --- a/backend/src/plugins/PingableRoles/events/ChangePingableEvts.ts +++ b/backend/src/plugins/PingableRoles/events/ChangePingableEvts.ts @@ -10,7 +10,7 @@ export const TypingEnablePingableEvt = pingableRolesEvt({ async listener(meta) { const pluginData = meta.pluginData; - const channel = meta.args.channel; + const channel = meta.args.typing.channel; const pingableRoles = await getPingableRolesForChannel(pluginData, channel.id); if (pingableRoles.length === 0) return; diff --git a/backend/src/plugins/PingableRoles/types.ts b/backend/src/plugins/PingableRoles/types.ts index cc479a99..8c942459 100644 --- a/backend/src/plugins/PingableRoles/types.ts +++ b/backend/src/plugins/PingableRoles/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { PingableRole } from "../../data/entities/PingableRole"; import { GuildPingableRoles } from "../../data/GuildPingableRoles"; @@ -18,5 +18,5 @@ export interface PingableRolesPluginType extends BasePluginType { }; } -export const pingableRolesCmd = typedGuildCommand(); -export const pingableRolesEvt = typedGuildEventListener(); +export const pingableRolesCmd = guildPluginMessageCommand(); +export const pingableRolesEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/PingableRoles/utils/disablePingableRoles.ts b/backend/src/plugins/PingableRoles/utils/disablePingableRoles.ts index 8730b588..b417ecdf 100644 --- a/backend/src/plugins/PingableRoles/utils/disablePingableRoles.ts +++ b/backend/src/plugins/PingableRoles/utils/disablePingableRoles.ts @@ -11,11 +11,6 @@ export function disablePingableRoles( const role = pluginData.guild.roles.cache.get(pingableRole.role_id as Snowflake); if (!role) continue; - role.edit( - { - mentionable: false, - }, - "Disable pingable role", - ); + role.setMentionable(false, "Disable pingable role"); } } diff --git a/backend/src/plugins/PingableRoles/utils/enablePingableRoles.ts b/backend/src/plugins/PingableRoles/utils/enablePingableRoles.ts index a7c40e78..7aec4df4 100644 --- a/backend/src/plugins/PingableRoles/utils/enablePingableRoles.ts +++ b/backend/src/plugins/PingableRoles/utils/enablePingableRoles.ts @@ -11,11 +11,6 @@ export function enablePingableRoles( const role = pluginData.guild.roles.cache.get(pingableRole.role_id as Snowflake); if (!role) continue; - role.edit( - { - mentionable: true, - }, - "Enable pingable role", - ); + role.setMentionable(true, "Enable pingable role"); } } diff --git a/backend/src/plugins/Post/PostPlugin.ts b/backend/src/plugins/Post/PostPlugin.ts index cc32bbbf..09d069a2 100644 --- a/backend/src/plugins/Post/PostPlugin.ts +++ b/backend/src/plugins/Post/PostPlugin.ts @@ -1,7 +1,10 @@ import { PluginOptions } from "knub"; +import { onGuildEvent } from "../../data/GuildEvents"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildScheduledPosts } from "../../data/GuildScheduledPosts"; +import { makeIoTsConfigParser } from "../../pluginUtils"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { EditCmd } from "./commands/EditCmd"; @@ -12,8 +15,6 @@ import { ScheduledPostsDeleteCmd } from "./commands/ScheduledPostsDeleteCmd"; import { ScheduledPostsListCmd } from "./commands/ScheduledPostsListCmd"; import { ScheduledPostsShowCmd } from "./commands/ScheduledPostsShowCmd"; import { ConfigSchema, PostPluginType } from "./types"; -import { LogsPlugin } from "../Logs/LogsPlugin"; -import { onGuildEvent } from "../../data/GuildEvents"; import { postScheduledPost } from "./util/postScheduledPost"; const defaultOptions: PluginOptions = { @@ -35,14 +36,15 @@ export const PostPlugin = zeppelinGuildPlugin()({ showInDocs: true, info: { prettyName: "Post", + configSchema: ConfigSchema, }, dependencies: () => [TimeAndDatePlugin, LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ PostCmd, PostEmbedCmd, EditCmd, @@ -61,12 +63,16 @@ export const PostPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - pluginData.state.unregisterGuildEventListener = onGuildEvent(pluginData.guild.id, "scheduledPost", (post) => + const { state, guild } = pluginData; + + state.unregisterGuildEventListener = onGuildEvent(guild.id, "scheduledPost", (post) => postScheduledPost(pluginData, post), ); }, beforeUnload(pluginData) { - pluginData.state.unregisterGuildEventListener?.(); + const { state } = pluginData; + + state.unregisterGuildEventListener?.(); }, }); diff --git a/backend/src/plugins/Post/commands/EditCmd.ts b/backend/src/plugins/Post/commands/EditCmd.ts index 317bb1ec..47ae07d7 100644 --- a/backend/src/plugins/Post/commands/EditCmd.ts +++ b/backend/src/plugins/Post/commands/EditCmd.ts @@ -1,4 +1,3 @@ -import { Snowflake, TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { postCmd } from "../types"; diff --git a/backend/src/plugins/Post/commands/EditEmbedCmd.ts b/backend/src/plugins/Post/commands/EditEmbedCmd.ts index c0d1d30e..7e446c69 100644 --- a/backend/src/plugins/Post/commands/EditEmbedCmd.ts +++ b/backend/src/plugins/Post/commands/EditEmbedCmd.ts @@ -1,4 +1,4 @@ -import { MessageEmbed, Snowflake, TextChannel } from "discord.js"; +import { APIEmbed } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { trimLines } from "../../../utils"; @@ -42,7 +42,7 @@ export const EditEmbedCmd = postCmd({ return; } - const embed = (targetMessage.embeds![0] ?? { fields: [] }) as MessageEmbed; + const embed = (targetMessage.embeds![0] ?? { fields: [] }) as APIEmbed; if (args.title) embed.title = args.title; if (content) embed.description = formatContent(content); if (color) embed.color = color; diff --git a/backend/src/plugins/Post/commands/PostEmbedCmd.ts b/backend/src/plugins/Post/commands/PostEmbedCmd.ts index e822cd3b..e605402b 100644 --- a/backend/src/plugins/Post/commands/PostEmbedCmd.ts +++ b/backend/src/plugins/Post/commands/PostEmbedCmd.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions } from "discord.js"; +import { APIEmbed } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { isValidEmbed, trimLines } from "../../../utils"; @@ -46,7 +46,7 @@ export const PostEmbedCmd = postCmd({ } } - let embed: MessageEmbedOptions = {}; + let embed: APIEmbed = {}; if (args.title) embed.title = args.title; if (color) embed.color = color; diff --git a/backend/src/plugins/Post/commands/ScheduledPostsDeleteCmd.ts b/backend/src/plugins/Post/commands/ScheduledPostsDeleteCmd.ts index 2a08344f..78becae8 100644 --- a/backend/src/plugins/Post/commands/ScheduledPostsDeleteCmd.ts +++ b/backend/src/plugins/Post/commands/ScheduledPostsDeleteCmd.ts @@ -1,8 +1,8 @@ import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { clearUpcomingScheduledPost } from "../../../data/loops/upcomingScheduledPostsLoop"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { sorter } from "../../../utils"; import { postCmd } from "../types"; -import { clearUpcomingScheduledPost } from "../../../data/loops/upcomingScheduledPostsLoop"; export const ScheduledPostsDeleteCmd = postCmd({ trigger: ["scheduled_posts delete", "scheduled_posts d"], diff --git a/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts b/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts index a0a88766..c118440f 100644 --- a/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts +++ b/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts @@ -1,4 +1,4 @@ -import { Util } from "discord.js"; +import { escapeCodeBlock } from "discord.js"; import humanizeDuration from "humanize-duration"; import moment from "moment-timezone"; import { createChunkedMessage, DBDateFormat, deactivateMentions, sorter, trimLines } from "../../../utils"; @@ -26,7 +26,7 @@ export const ScheduledPostsListCmd = postCmd({ const isTruncated = previewText.length > SCHEDULED_POST_PREVIEW_TEXT_LENGTH; - previewText = Util.escapeCodeBlock(deactivateMentions(previewText)) + previewText = escapeCodeBlock(deactivateMentions(previewText)) .replace(/\s+/g, " ") .slice(0, SCHEDULED_POST_PREVIEW_TEXT_LENGTH); diff --git a/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts b/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts index c1a92510..990bfbae 100644 --- a/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts +++ b/backend/src/plugins/Post/commands/ScheduledPostsShowCmd.ts @@ -1,4 +1,3 @@ -import { TextChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { sorter } from "../../../utils"; @@ -22,6 +21,6 @@ export const ScheduledPostsShowCmd = postCmd({ return; } - postMessage(pluginData, msg.channel as TextChannel, post.content, post.attachments, post.enable_mentions); + postMessage(pluginData, msg.channel, post.content, post.attachments, post.enable_mentions); }, }); diff --git a/backend/src/plugins/Post/types.ts b/backend/src/plugins/Post/types.ts index 493e8444..815b19b8 100644 --- a/backend/src/plugins/Post/types.ts +++ b/backend/src/plugins/Post/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand } from "knub"; +import { BasePluginType, guildPluginMessageCommand } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildScheduledPosts } from "../../data/GuildScheduledPosts"; @@ -20,4 +20,4 @@ export interface PostPluginType extends BasePluginType { }; } -export const postCmd = typedGuildCommand(); +export const postCmd = guildPluginMessageCommand(); diff --git a/backend/src/plugins/Post/util/actualPostCmd.ts b/backend/src/plugins/Post/util/actualPostCmd.ts index a63b1312..dbd0e2b2 100644 --- a/backend/src/plugins/Post/util/actualPostCmd.ts +++ b/backend/src/plugins/Post/util/actualPostCmd.ts @@ -1,17 +1,15 @@ -import { Channel, GuildTextBasedChannel, Message, NewsChannel, TextChannel, ThreadChannel } from "discord.js"; +import { GuildTextBasedChannel, Message } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; +import { registerUpcomingScheduledPost } from "../../../data/loops/upcomingScheduledPostsLoop"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { DBDateFormat, errorMessage, MINUTES, StrictMessageContent } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { PostPluginType } from "../types"; import { parseScheduleTime } from "./parseScheduleTime"; import { postMessage } from "./postMessage"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { registerUpcomingScheduledPost } from "../../../data/loops/upcomingScheduledPostsLoop"; const MIN_REPEAT_TIME = 5 * MINUTES; const MAX_REPEAT_TIME = Math.pow(2, 32); @@ -30,7 +28,7 @@ export async function actualPostCmd( "repeat-times"?: number; } = {}, ) { - if (!targetChannel.isText()) { + if (!targetChannel.isTextBased()) { msg.channel.send(errorMessage("Specified channel is not a text-based channel")); return; } @@ -42,19 +40,11 @@ export async function actualPostCmd( if (opts.repeat) { if (opts.repeat < MIN_REPEAT_TIME) { - sendErrorMessage( - pluginData, - msg.channel as TextChannel, - `Minimum time for -repeat is ${humanizeDuration(MIN_REPEAT_TIME)}`, - ); + sendErrorMessage(pluginData, msg.channel, `Minimum time for -repeat is ${humanizeDuration(MIN_REPEAT_TIME)}`); return; } if (opts.repeat > MAX_REPEAT_TIME) { - sendErrorMessage( - pluginData, - msg.channel as TextChannel, - `Max time for -repeat is ${humanizeDuration(MAX_REPEAT_TIME)}`, - ); + sendErrorMessage(pluginData, msg.channel, `Max time for -repeat is ${humanizeDuration(MAX_REPEAT_TIME)}`); return; } } @@ -65,7 +55,7 @@ export async function actualPostCmd( // Schedule the post to be posted later postAt = await parseScheduleTime(pluginData, msg.author.id, opts.schedule); if (!postAt) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Invalid schedule time"); + sendErrorMessage(pluginData, msg.channel, "Invalid schedule time"); return; } } else if (opts.repeat) { @@ -82,17 +72,17 @@ export async function actualPostCmd( // Invalid time if (!repeatUntil) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Invalid time specified for -repeat-until"); + sendErrorMessage(pluginData, msg.channel, "Invalid time specified for -repeat-until"); return; } if (repeatUntil.isBefore(moment.utc())) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "You can't set -repeat-until in the past"); + sendErrorMessage(pluginData, msg.channel, "You can't set -repeat-until in the past"); return; } if (repeatUntil.isAfter(MAX_REPEAT_UNTIL)) { sendErrorMessage( pluginData, - msg.channel as TextChannel, + msg.channel, "Unfortunately, -repeat-until can only be at most 100 years into the future. Maybe 99 years would be enough?", ); return; @@ -100,26 +90,18 @@ export async function actualPostCmd( } else if (opts["repeat-times"]) { repeatTimes = opts["repeat-times"]; if (repeatTimes <= 0) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "-repeat-times must be 1 or more"); + sendErrorMessage(pluginData, msg.channel, "-repeat-times must be 1 or more"); return; } } if (repeatUntil && repeatTimes) { - sendErrorMessage( - pluginData, - msg.channel as TextChannel, - "You can only use one of -repeat-until or -repeat-times at once", - ); + sendErrorMessage(pluginData, msg.channel, "You can only use one of -repeat-until or -repeat-times at once"); return; } if (opts.repeat && !repeatUntil && !repeatTimes) { - sendErrorMessage( - pluginData, - msg.channel as TextChannel, - "You must specify -repeat-until or -repeat-times for repeated messages", - ); + sendErrorMessage(pluginData, msg.channel, "You must specify -repeat-until or -repeat-times for repeated messages"); return; } @@ -134,7 +116,7 @@ export async function actualPostCmd( // Save schedule/repeat information in DB if (postAt) { if (postAt < moment.utc()) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Post can't be scheduled to be posted in the past"); + sendErrorMessage(pluginData, msg.channel, "Post can't be scheduled to be posted in the past"); return; } @@ -210,6 +192,6 @@ export async function actualPostCmd( } if (targetChannel.id !== msg.channel.id || opts.schedule || opts.repeat) { - sendSuccessMessage(pluginData, msg.channel as TextChannel, successMessage); + sendSuccessMessage(pluginData, msg.channel, successMessage); } } diff --git a/backend/src/plugins/Post/util/postMessage.ts b/backend/src/plugins/Post/util/postMessage.ts index 4a10f809..8fb5ad8c 100644 --- a/backend/src/plugins/Post/util/postMessage.ts +++ b/backend/src/plugins/Post/util/postMessage.ts @@ -1,12 +1,4 @@ -import { - GuildTextBasedChannel, - Message, - MessageAttachment, - MessageOptions, - NewsChannel, - TextChannel, - ThreadChannel, -} from "discord.js"; +import { Attachment, GuildTextBasedChannel, Message, MessageCreateOptions } from "discord.js"; import fs from "fs"; import { GuildPluginData } from "knub"; import { downloadFile } from "../../../utils"; @@ -18,8 +10,8 @@ const fsp = fs.promises; export async function postMessage( pluginData: GuildPluginData, channel: GuildTextBasedChannel, - content: MessageOptions, - attachments: MessageAttachment[] = [], + content: MessageCreateOptions, + attachments: Attachment[] = [], enableMentions: boolean = false, ): Promise { if (typeof content === "string") { diff --git a/backend/src/plugins/Post/util/postScheduledPost.ts b/backend/src/plugins/Post/util/postScheduledPost.ts index 824f5021..e4a3e1e0 100644 --- a/backend/src/plugins/Post/util/postScheduledPost.ts +++ b/backend/src/plugins/Post/util/postScheduledPost.ts @@ -1,13 +1,13 @@ -import { Snowflake, TextChannel, User } from "discord.js"; +import { Snowflake, User } from "discord.js"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { logger } from "../../../logger"; -import { DBDateFormat, verboseChannelMention, verboseUserMention } from "../../../utils"; -import { PostPluginType } from "../types"; -import { postMessage } from "./postMessage"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; import { ScheduledPost } from "../../../data/entities/ScheduledPost"; import { registerUpcomingScheduledPost } from "../../../data/loops/upcomingScheduledPostsLoop"; +import { logger } from "../../../logger"; +import { DBDateFormat, verboseChannelMention, verboseUserMention } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { PostPluginType } from "../types"; +import { postMessage } from "./postMessage"; export async function postScheduledPost(pluginData: GuildPluginData, post: ScheduledPost) { // First, update the scheduled post or delete it from the database *before* we try posting it. @@ -45,7 +45,7 @@ export async function postScheduledPost(pluginData: GuildPluginData( info: { prettyName: "Reaction roles", legacy: "Consider using the [Role buttons](/docs/plugins/role_buttons) plugin instead.", + configSchema: ConfigSchema, }, dependencies: () => [LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ RefreshReactionRolesCmd, ClearReactionRolesCmd, InitReactionRolesCmd, @@ -79,8 +81,10 @@ export const ReactionRolesPlugin = zeppelinGuildPlugin( }, beforeUnload(pluginData) { - if (pluginData.state.autoRefreshTimeout) { - clearTimeout(pluginData.state.autoRefreshTimeout); + const { state, guild } = pluginData; + + if (state.autoRefreshTimeout) { + clearTimeout(state.autoRefreshTimeout); } }, }); diff --git a/backend/src/plugins/ReactionRoles/types.ts b/backend/src/plugins/ReactionRoles/types.ts index 412d798a..19ba38c6 100644 --- a/backend/src/plugins/ReactionRoles/types.ts +++ b/backend/src/plugins/ReactionRoles/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildReactionRoles } from "../../data/GuildReactionRoles"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { Queue } from "../../Queue"; @@ -43,5 +43,5 @@ export interface ReactionRolesPluginType extends BasePluginType { }; } -export const reactionRolesCmd = typedGuildCommand(); -export const reactionRolesEvt = typedGuildEventListener(); +export const reactionRolesCmd = guildPluginMessageCommand(); +export const reactionRolesEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts b/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts index 78c681c2..6ba6945b 100644 --- a/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts +++ b/backend/src/plugins/ReactionRoles/util/addMemberPendingRoleChange.ts @@ -31,12 +31,7 @@ export async function addMemberPendingRoleChange( } try { - await member.edit( - { - roles: Array.from(newRoleIds.values()), - }, - "Reaction roles", - ); + await member.roles.set(Array.from(newRoleIds.values()), "Reaction roles"); } catch (e) { logger.warn(`Failed to apply role changes to ${member.user.tag} (${member.id}): ${e.message}`); } diff --git a/backend/src/plugins/ReactionRoles/util/applyReactionRoleReactionsToMessage.ts b/backend/src/plugins/ReactionRoles/util/applyReactionRoleReactionsToMessage.ts index 4d41ba42..94de8631 100644 --- a/backend/src/plugins/ReactionRoles/util/applyReactionRoleReactionsToMessage.ts +++ b/backend/src/plugins/ReactionRoles/util/applyReactionRoleReactionsToMessage.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { ReactionRole } from "../../../data/entities/ReactionRole"; import { isDiscordAPIError, sleep } from "../../../utils"; @@ -16,15 +16,15 @@ export async function applyReactionRoleReactionsToMessage( messageId: string, reactionRoles: ReactionRole[], ): Promise { - const channel = pluginData.guild.channels.cache.get(channelId as Snowflake) as TextChannel; - if (!channel) return; + const channel = pluginData.guild.channels.cache.get(channelId as Snowflake); + if (!channel?.isTextBased()) return; const errors: string[] = []; const logs = pluginData.getPlugin(LogsPlugin); let targetMessage; try { - targetMessage = await channel.messages.fetch(messageId, { force: true }); + targetMessage = await channel.messages.fetch({ message: messageId, force: true }); } catch (e) { if (isDiscordAPIError(e)) { if (e.code === 10008) { diff --git a/backend/src/plugins/Reminders/RemindersPlugin.ts b/backend/src/plugins/Reminders/RemindersPlugin.ts index 62f6b29a..128e945c 100644 --- a/backend/src/plugins/Reminders/RemindersPlugin.ts +++ b/backend/src/plugins/Reminders/RemindersPlugin.ts @@ -1,13 +1,14 @@ import { PluginOptions } from "knub"; +import { onGuildEvent } from "../../data/GuildEvents"; import { GuildReminders } from "../../data/GuildReminders"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { RemindCmd } from "./commands/RemindCmd"; import { RemindersCmd } from "./commands/RemindersCmd"; import { RemindersDeleteCmd } from "./commands/RemindersDeleteCmd"; -import { ConfigSchema, RemindersPluginType } from "./types"; -import { onGuildEvent } from "../../data/GuildEvents"; import { postReminder } from "./functions/postReminder"; +import { ConfigSchema, RemindersPluginType } from "./types"; const defaultOptions: PluginOptions = { config: { @@ -28,14 +29,15 @@ export const RemindersPlugin = zeppelinGuildPlugin()({ showInDocs: true, info: { prettyName: "Reminders", + configSchema: ConfigSchema, }, dependencies: () => [TimeAndDatePlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ RemindCmd, RemindersCmd, RemindersDeleteCmd, @@ -50,13 +52,17 @@ export const RemindersPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - pluginData.state.unregisterGuildEventListener = onGuildEvent(pluginData.guild.id, "reminder", (reminder) => + const { state, guild } = pluginData; + + state.unregisterGuildEventListener = onGuildEvent(guild.id, "reminder", (reminder) => postReminder(pluginData, reminder), ); }, beforeUnload(pluginData) { - pluginData.state.unregisterGuildEventListener?.(); - pluginData.state.unloaded = true; + const { state } = pluginData; + + state.unregisterGuildEventListener?.(); + state.unloaded = true; }, }); diff --git a/backend/src/plugins/Reminders/commands/RemindCmd.ts b/backend/src/plugins/Reminders/commands/RemindCmd.ts index 122c9e3d..c4655771 100644 --- a/backend/src/plugins/Reminders/commands/RemindCmd.ts +++ b/backend/src/plugins/Reminders/commands/RemindCmd.ts @@ -1,11 +1,11 @@ import humanizeDuration from "humanize-duration"; import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { registerUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { convertDelayStringToMS, messageLink } from "../../../utils"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { remindersCmd } from "../types"; -import { registerUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop"; export const RemindCmd = remindersCmd({ trigger: ["remind", "remindme", "reminder"], diff --git a/backend/src/plugins/Reminders/commands/RemindersDeleteCmd.ts b/backend/src/plugins/Reminders/commands/RemindersDeleteCmd.ts index 0690b434..25713a4f 100644 --- a/backend/src/plugins/Reminders/commands/RemindersDeleteCmd.ts +++ b/backend/src/plugins/Reminders/commands/RemindersDeleteCmd.ts @@ -1,8 +1,8 @@ import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { clearUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { sorter } from "../../../utils"; import { remindersCmd } from "../types"; -import { clearUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop"; export const RemindersDeleteCmd = remindersCmd({ trigger: ["reminders delete", "reminders d"], diff --git a/backend/src/plugins/Reminders/functions/postReminder.ts b/backend/src/plugins/Reminders/functions/postReminder.ts index d96ad7b6..86afbf08 100644 --- a/backend/src/plugins/Reminders/functions/postReminder.ts +++ b/backend/src/plugins/Reminders/functions/postReminder.ts @@ -1,15 +1,14 @@ +import { HTTPError, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; -import { RemindersPluginType } from "../types"; -import { Reminder } from "../../../data/entities/Reminder"; -import { DiscordAPIError, HTTPError, Snowflake, TextChannel } from "discord.js"; +import { disableLinkPreviews } from "knub/helpers"; import moment from "moment-timezone"; -import { disableLinkPreviews } from "knub/dist/helpers"; -import { DBDateFormat, isDiscordHTTPError, SECONDS } from "../../../utils"; -import humanizeDuration from "humanize-duration"; +import { Reminder } from "../../../data/entities/Reminder"; +import { DBDateFormat } from "../../../utils"; +import { RemindersPluginType } from "../types"; export async function postReminder(pluginData: GuildPluginData, reminder: Reminder) { const channel = pluginData.guild.channels.cache.get(reminder.channel_id as Snowflake); - if (channel && (channel.isText() || channel.isThread())) { + if (channel && (channel.isTextBased() || channel.isThread())) { try { // Only show created at date if one exists if (moment.utc(reminder.created_at).isValid()) { @@ -34,7 +33,7 @@ export async function postReminder(pluginData: GuildPluginData= 500) { + if (err instanceof HTTPError && err.status >= 500) { // If we get a server error, try again later return; } diff --git a/backend/src/plugins/Reminders/types.ts b/backend/src/plugins/Reminders/types.ts index be9f3fdb..5bc896ba 100644 --- a/backend/src/plugins/Reminders/types.ts +++ b/backend/src/plugins/Reminders/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand } from "knub"; +import { BasePluginType, guildPluginMessageCommand } from "knub"; import { GuildReminders } from "../../data/GuildReminders"; export const ConfigSchema = t.type({ @@ -20,4 +20,4 @@ export interface RemindersPluginType extends BasePluginType { }; } -export const remindersCmd = typedGuildCommand(); +export const remindersCmd = guildPluginMessageCommand(); diff --git a/backend/src/plugins/RoleButtons/RoleButtonsPlugin.ts b/backend/src/plugins/RoleButtons/RoleButtonsPlugin.ts index f7fc72ea..4dc3d345 100644 --- a/backend/src/plugins/RoleButtons/RoleButtonsPlugin.ts +++ b/backend/src/plugins/RoleButtons/RoleButtonsPlugin.ts @@ -1,20 +1,19 @@ -import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; -import { ConfigSchema, RoleButtonsPluginType } from "./types"; -import { mapToPublicFn } from "../../pluginUtils"; -import { LogsPlugin } from "../Logs/LogsPlugin"; -import { applyAllRoleButtons } from "./functions/applyAllRoleButtons"; +import * as t from "io-ts"; import { GuildRoleButtons } from "../../data/GuildRoleButtons"; +import { StrictValidationError, validate } from "../../validatorUtils"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin"; -import { StrictValidationError } from "../../validatorUtils"; +import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; +import { resetButtonsCmd } from "./commands/resetButtons"; import { onButtonInteraction } from "./events/buttonInteraction"; -import { pluginInfo } from "./info"; +import { applyAllRoleButtons } from "./functions/applyAllRoleButtons"; import { createButtonComponents } from "./functions/createButtonComponents"; import { TooManyComponentsError } from "./functions/TooManyComponentsError"; -import { resetButtonsCmd } from "./commands/resetButtons"; +import { pluginInfo } from "./info"; +import { ConfigSchema, RoleButtonsPluginType } from "./types"; export const RoleButtonsPlugin = zeppelinGuildPlugin()({ name: "role_buttons", - configSchema: ConfigSchema, info: pluginInfo, showInDocs: true, @@ -33,11 +32,10 @@ export const RoleButtonsPlugin = zeppelinGuildPlugin()({ ], }, - configPreprocessor(options) { + configParser(input) { // Auto-fill "name" property for buttons based on the object key - const buttonsArray = Array.isArray(options.config?.buttons) ? options.config.buttons : []; const seenMessages = new Set(); - for (const [name, buttonsConfig] of Object.entries(options.config?.buttons ?? {})) { + for (const [name, buttonsConfig] of Object.entries((input as any).buttons ?? {})) { if (name.length > 16) { throw new StrictValidationError(["Name for role buttons can be at most 16 characters long"]); } @@ -67,14 +65,19 @@ export const RoleButtonsPlugin = zeppelinGuildPlugin()({ } } - return options; + const error = validate(ConfigSchema, input); + if (error) { + throw error; + } + + return input as t.TypeOf; }, dependencies: () => [LogsPlugin, RoleManagerPlugin], events: [onButtonInteraction], - commands: [resetButtonsCmd], + messageCommands: [resetButtonsCmd], beforeLoad(pluginData) { pluginData.state.roleButtons = GuildRoleButtons.getGuildInstance(pluginData.guild.id); diff --git a/backend/src/plugins/RoleButtons/commands/resetButtons.ts b/backend/src/plugins/RoleButtons/commands/resetButtons.ts index 0445e5b3..cca8e643 100644 --- a/backend/src/plugins/RoleButtons/commands/resetButtons.ts +++ b/backend/src/plugins/RoleButtons/commands/resetButtons.ts @@ -1,10 +1,10 @@ -import { typedGuildCommand } from "knub"; -import { RoleButtonsPluginType } from "../types"; +import { guildPluginMessageCommand } from "knub"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { applyAllRoleButtons } from "../functions/applyAllRoleButtons"; +import { RoleButtonsPluginType } from "../types"; -export const resetButtonsCmd = typedGuildCommand()({ +export const resetButtonsCmd = guildPluginMessageCommand()({ trigger: "role_buttons reset", description: "In case of issues, you can run this command to have Zeppelin 'forget' about specific role buttons and re-apply them. This will also repost the message, if not targeting an existing message.", diff --git a/backend/src/plugins/RoleButtons/events/buttonInteraction.ts b/backend/src/plugins/RoleButtons/events/buttonInteraction.ts index 579ea7e9..6fcc27e6 100644 --- a/backend/src/plugins/RoleButtons/events/buttonInteraction.ts +++ b/backend/src/plugins/RoleButtons/events/buttonInteraction.ts @@ -1,11 +1,11 @@ -import { typedGuildEventListener } from "knub"; -import { RoleButtonsPluginType, TRoleButtonOption } from "../types"; -import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin"; import { GuildMember } from "discord.js"; -import { getAllRolesInButtons } from "../functions/getAllRolesInButtons"; +import { guildPluginEventListener } from "knub"; import { parseCustomId } from "../../../utils/parseCustomId"; +import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin"; +import { getAllRolesInButtons } from "../functions/getAllRolesInButtons"; +import { RoleButtonsPluginType, TRoleButtonOption } from "../types"; -export const onButtonInteraction = typedGuildEventListener()({ +export const onButtonInteraction = guildPluginEventListener()({ event: "interactionCreate", async listener({ pluginData, args }) { if (!args.interaction.isButton()) { @@ -28,6 +28,7 @@ export const onButtonInteraction = typedGuildEventListener console.trace(err.message)); return; } @@ -46,6 +47,7 @@ export const onButtonInteraction = typedGuildEventListener console.trace(err.message)); } else { rolesToAdd.push(option.role_id); @@ -63,6 +65,7 @@ export const onButtonInteraction = typedGuildEventListener console.trace(err.message)); } diff --git a/backend/src/plugins/RoleButtons/functions/applyAllRoleButtons.ts b/backend/src/plugins/RoleButtons/functions/applyAllRoleButtons.ts index e956377b..bb50f3d5 100644 --- a/backend/src/plugins/RoleButtons/functions/applyAllRoleButtons.ts +++ b/backend/src/plugins/RoleButtons/functions/applyAllRoleButtons.ts @@ -1,6 +1,6 @@ +import { createHash } from "crypto"; import { GuildPluginData } from "knub"; import { RoleButtonsPluginType } from "../types"; -import { createHash } from "crypto"; import { applyRoleButtons } from "./applyRoleButtons"; export async function applyAllRoleButtons(pluginData: GuildPluginData) { diff --git a/backend/src/plugins/RoleButtons/functions/applyRoleButtons.ts b/backend/src/plugins/RoleButtons/functions/applyRoleButtons.ts index 66e9666e..3570ebd0 100644 --- a/backend/src/plugins/RoleButtons/functions/applyRoleButtons.ts +++ b/backend/src/plugins/RoleButtons/functions/applyRoleButtons.ts @@ -1,14 +1,10 @@ +import { Message, MessageCreateOptions, MessageEditOptions } from "discord.js"; import { GuildPluginData } from "knub"; -import { RoleButtonsPluginType, TRoleButtonsConfigItem } from "../types"; -import { isSnowflake, snowflakeRegex } from "../../../utils"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { Message, MessageButton, MessageEditOptions, MessageOptions, Snowflake } from "discord.js"; import { RoleButtonsItem } from "../../../data/entities/RoleButtonsItem"; -import { buildCustomId } from "../../../utils/buildCustomId"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { RoleButtonsPluginType, TRoleButtonsConfigItem } from "../types"; import { createButtonComponents } from "./createButtonComponents"; -const channelMessageRegex = new RegExp(`^(${snowflakeRegex.source})-(${snowflakeRegex.source})$`); - export async function applyRoleButtons( pluginData: GuildPluginData, configItem: TRoleButtonsConfigItem, @@ -19,7 +15,7 @@ export async function applyRoleButtons( // Remove existing role buttons, if any if (existingSavedButtons?.channel_id) { const existingChannel = await pluginData.guild.channels.fetch(configItem.message.channel_id).catch(() => null); - const existingMessage = await (existingChannel?.isText() && + const existingMessage = await (existingChannel?.isTextBased() && existingChannel.messages.fetch(existingSavedButtons.message_id).catch(() => null)); if (existingMessage && existingMessage.components.length) { await existingMessage.edit({ @@ -32,7 +28,7 @@ export async function applyRoleButtons( if ("message_id" in configItem.message) { // channel id + message id: apply role buttons to existing message const channel = await pluginData.guild.channels.fetch(configItem.message.channel_id).catch(() => null); - const messageCandidate = await (channel?.isText() && + const messageCandidate = await (channel?.isTextBased() && channel.messages.fetch(configItem.message.message_id).catch(() => null)); if (!messageCandidate) { pluginData.getPlugin(LogsPlugin).logBotAlert({ @@ -55,10 +51,12 @@ export async function applyRoleButtons( } const channel = await pluginData.guild.channels.fetch(configItem.message.channel_id).catch(() => null); - if (channel && (!channel.isText || typeof channel.isText !== "function")) { + if (channel && (!channel.isTextBased || typeof channel.isTextBased !== "function")) { + // FIXME: Probably not relevant anymore? + // tslint:disable-next-line no-console console.log("wtf", pluginData.guild?.id, configItem.message.channel_id); } - if (!channel || !channel?.isText?.()) { + if (!channel || !channel?.isTextBased()) { pluginData.getPlugin(LogsPlugin).logBotAlert({ body: `Text channel not found for role_buttons/${configItem.name}`, }); @@ -88,7 +86,7 @@ export async function applyRoleButtons( if (!candidateMessage) { try { - candidateMessage = await channel.send(configItem.message.content as string | MessageOptions); + candidateMessage = await channel.send(configItem.message.content as string | MessageCreateOptions); } catch (err) { pluginData.getPlugin(LogsPlugin).logBotAlert({ body: `Error while posting message for role_buttons/${configItem.name}: ${String(err)}`, diff --git a/backend/src/plugins/RoleButtons/functions/convertButtonStyleStringToEnum.ts b/backend/src/plugins/RoleButtons/functions/convertButtonStyleStringToEnum.ts new file mode 100644 index 00000000..18486372 --- /dev/null +++ b/backend/src/plugins/RoleButtons/functions/convertButtonStyleStringToEnum.ts @@ -0,0 +1,17 @@ +import { ButtonStyle } from "discord.js"; +import { TRoleButtonOption } from "../types.js"; + +export function convertButtonStyleStringToEnum(input: TRoleButtonOption["style"]): ButtonStyle | null | undefined { + switch (input) { + case "PRIMARY": + return ButtonStyle.Primary; + case "SECONDARY": + return ButtonStyle.Secondary; + case "SUCCESS": + return ButtonStyle.Success; + case "DANGER": + return ButtonStyle.Danger; + default: + return input; + } +} diff --git a/backend/src/plugins/RoleButtons/functions/createButtonComponents.ts b/backend/src/plugins/RoleButtons/functions/createButtonComponents.ts index 05736484..fb287f39 100644 --- a/backend/src/plugins/RoleButtons/functions/createButtonComponents.ts +++ b/backend/src/plugins/RoleButtons/functions/createButtonComponents.ts @@ -1,23 +1,22 @@ -import { MessageActionRow, MessageButton, Snowflake } from "discord.js"; -import { chunkArray } from "../../../utils"; -import { RoleButtonsPluginType, TRoleButtonOption, TRoleButtonsConfigItem } from "../types"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { buildCustomId } from "../../../utils/buildCustomId"; -import { GuildPluginData } from "knub"; +import { TRoleButtonsConfigItem } from "../types"; +import { convertButtonStyleStringToEnum } from "./convertButtonStyleStringToEnum.js"; import { TooManyComponentsError } from "./TooManyComponentsError"; -export function createButtonComponents(configItem: TRoleButtonsConfigItem): MessageActionRow[] { - const rows: MessageActionRow[] = []; +export function createButtonComponents(configItem: TRoleButtonsConfigItem): Array> { + const rows: Array> = []; - let currentRow = new MessageActionRow(); + let currentRow = new ActionRowBuilder(); for (const [index, option] of configItem.options.entries()) { if (currentRow.components.length === 5 || (currentRow.components.length > 0 && option.start_new_row)) { rows.push(currentRow); - currentRow = new MessageActionRow(); + currentRow = new ActionRowBuilder(); } - const button = new MessageButton() + const button = new ButtonBuilder() .setLabel(option.label ?? "") - .setStyle(option.style ?? "PRIMARY") + .setStyle(convertButtonStyleStringToEnum(option.style) ?? ButtonStyle.Primary) .setCustomId(buildCustomId("roleButtons", { name: configItem.name, index })); if (option.emoji) { diff --git a/backend/src/plugins/RoleButtons/info.ts b/backend/src/plugins/RoleButtons/info.ts index 6f64806e..d63fb17c 100644 --- a/backend/src/plugins/RoleButtons/info.ts +++ b/backend/src/plugins/RoleButtons/info.ts @@ -1,5 +1,6 @@ import { trimPluginDescription } from "../../utils"; import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint"; +import { ConfigSchema } from "./types"; export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = { prettyName: "Role buttons", @@ -77,4 +78,5 @@ export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = { ... # See above for examples for options ~~~ `), + configSchema: ConfigSchema, }; diff --git a/backend/src/plugins/RoleButtons/types.ts b/backend/src/plugins/RoleButtons/types.ts index e7b4096d..f5792f36 100644 --- a/backend/src/plugins/RoleButtons/types.ts +++ b/backend/src/plugins/RoleButtons/types.ts @@ -1,7 +1,8 @@ +import { ButtonStyle } from "discord.js"; import * as t from "io-ts"; import { BasePluginType } from "knub"; -import { tMessageContent, tNullable } from "../../utils"; import { GuildRoleButtons } from "../../data/GuildRoleButtons"; +import { tMessageContent, tNullable } from "../../utils"; const RoleButtonOption = t.type({ role_id: t.string, @@ -10,6 +11,12 @@ const RoleButtonOption = t.type({ // https://discord.js.org/#/docs/discord.js/v13/typedef/MessageButtonStyle style: tNullable( t.union([ + t.literal(ButtonStyle.Primary), + t.literal(ButtonStyle.Secondary), + t.literal(ButtonStyle.Success), + t.literal(ButtonStyle.Danger), + + // The following are deprecated t.literal("PRIMARY"), t.literal("SECONDARY"), t.literal("SUCCESS"), diff --git a/backend/src/plugins/RoleManager/RoleManagerPlugin.ts b/backend/src/plugins/RoleManager/RoleManagerPlugin.ts index c6d398f1..878b2cd4 100644 --- a/backend/src/plugins/RoleManager/RoleManagerPlugin.ts +++ b/backend/src/plugins/RoleManager/RoleManagerPlugin.ts @@ -1,20 +1,20 @@ -import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; -import { ConfigSchema, RoleManagerPluginType } from "./types"; import { GuildRoleQueue } from "../../data/GuildRoleQueue"; -import { mapToPublicFn } from "../../pluginUtils"; -import { addRole } from "./functions/addRole"; -import { removeRole } from "./functions/removeRole"; -import { addPriorityRole } from "./functions/addPriorityRole"; -import { removePriorityRole } from "./functions/removePriorityRole"; -import { runRoleAssignmentLoop } from "./functions/runRoleAssignmentLoop"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { LogsPlugin } from "../Logs/LogsPlugin"; +import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; +import { addPriorityRole } from "./functions/addPriorityRole"; +import { addRole } from "./functions/addRole"; +import { removePriorityRole } from "./functions/removePriorityRole"; +import { removeRole } from "./functions/removeRole"; +import { runRoleAssignmentLoop } from "./functions/runRoleAssignmentLoop"; +import { ConfigSchema, RoleManagerPluginType } from "./types"; export const RoleManagerPlugin = zeppelinGuildPlugin()({ name: "role_manager", - configSchema: ConfigSchema, showInDocs: false, dependencies: () => [LogsPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), public: { addRole: mapToPublicFn(addRole), @@ -24,8 +24,10 @@ export const RoleManagerPlugin = zeppelinGuildPlugin()({ }, beforeLoad(pluginData) { - pluginData.state.roleQueue = GuildRoleQueue.getGuildInstance(pluginData.guild.id); - pluginData.state.pendingRoleAssignmentPromise = Promise.resolve(); + const { state, guild } = pluginData; + + state.roleQueue = GuildRoleQueue.getGuildInstance(guild.id); + state.pendingRoleAssignmentPromise = Promise.resolve(); }, afterLoad(pluginData) { @@ -33,7 +35,9 @@ export const RoleManagerPlugin = zeppelinGuildPlugin()({ }, async afterUnload(pluginData) { - pluginData.state.abortRoleAssignmentLoop = true; - await pluginData.state.pendingRoleAssignmentPromise; + const { state } = pluginData; + + state.abortRoleAssignmentLoop = true; + await state.pendingRoleAssignmentPromise; }, }); diff --git a/backend/src/plugins/RoleManager/functions/addPriorityRole.ts b/backend/src/plugins/RoleManager/functions/addPriorityRole.ts index a6aab5cb..2c9534f6 100644 --- a/backend/src/plugins/RoleManager/functions/addPriorityRole.ts +++ b/backend/src/plugins/RoleManager/functions/addPriorityRole.ts @@ -1,6 +1,6 @@ import { GuildPluginData } from "knub"; -import { RoleManagerPluginType } from "../types"; import { PRIORITY_ROLE_PRIORITY } from "../constants"; +import { RoleManagerPluginType } from "../types"; import { runRoleAssignmentLoop } from "./runRoleAssignmentLoop"; export async function addPriorityRole( diff --git a/backend/src/plugins/RoleManager/functions/removePriorityRole.ts b/backend/src/plugins/RoleManager/functions/removePriorityRole.ts index 1b120659..4192a762 100644 --- a/backend/src/plugins/RoleManager/functions/removePriorityRole.ts +++ b/backend/src/plugins/RoleManager/functions/removePriorityRole.ts @@ -1,6 +1,6 @@ import { GuildPluginData } from "knub"; -import { RoleManagerPluginType } from "../types"; import { PRIORITY_ROLE_PRIORITY } from "../constants"; +import { RoleManagerPluginType } from "../types"; import { runRoleAssignmentLoop } from "./runRoleAssignmentLoop"; export async function removePriorityRole( diff --git a/backend/src/plugins/RoleManager/functions/runRoleAssignmentLoop.ts b/backend/src/plugins/RoleManager/functions/runRoleAssignmentLoop.ts index 2ed7c2ea..2c62295e 100644 --- a/backend/src/plugins/RoleManager/functions/runRoleAssignmentLoop.ts +++ b/backend/src/plugins/RoleManager/functions/runRoleAssignmentLoop.ts @@ -1,8 +1,8 @@ import { GuildPluginData } from "knub"; -import { RoleManagerPluginType } from "../types"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { logger } from "../../../logger"; import { RoleQueueItem } from "../../../data/entities/RoleQueueItem"; +import { logger } from "../../../logger"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { RoleManagerPluginType } from "../types"; const ROLE_ASSIGNMENTS_PER_BATCH = 20; diff --git a/backend/src/plugins/RoleManager/types.ts b/backend/src/plugins/RoleManager/types.ts index d3adc30b..54f5033a 100644 --- a/backend/src/plugins/RoleManager/types.ts +++ b/backend/src/plugins/RoleManager/types.ts @@ -1,6 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand } from "knub"; -import { GuildLogs } from "../../data/GuildLogs"; +import { BasePluginType } from "knub"; import { GuildRoleQueue } from "../../data/GuildRoleQueue"; export const ConfigSchema = t.type({}); diff --git a/backend/src/plugins/Roles/RolesPlugin.ts b/backend/src/plugins/Roles/RolesPlugin.ts index c643b6d1..c173ddd1 100644 --- a/backend/src/plugins/Roles/RolesPlugin.ts +++ b/backend/src/plugins/Roles/RolesPlugin.ts @@ -1,13 +1,14 @@ import { PluginOptions } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { AddRoleCmd } from "./commands/AddRoleCmd"; import { MassAddRoleCmd } from "./commands/MassAddRoleCmd"; import { MassRemoveRoleCmd } from "./commands/MassRemoveRoleCmd"; import { RemoveRoleCmd } from "./commands/RemoveRoleCmd"; import { ConfigSchema, RolesPluginType } from "./types"; -import { LogsPlugin } from "../Logs/LogsPlugin"; const defaultOptions: PluginOptions = { config: { @@ -39,14 +40,15 @@ export const RolesPlugin = zeppelinGuildPlugin()({ description: trimPluginDescription(` Enables authorised users to add and remove whitelisted roles with a command. `), + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, dependencies: () => [LogsPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ AddRoleCmd, RemoveRoleCmd, MassAddRoleCmd, diff --git a/backend/src/plugins/Roles/commands/AddRoleCmd.ts b/backend/src/plugins/Roles/commands/AddRoleCmd.ts index 23229ca5..49c21d23 100644 --- a/backend/src/plugins/Roles/commands/AddRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/AddRoleCmd.ts @@ -1,11 +1,10 @@ import { GuildChannel } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { LogType } from "../../../data/LogType"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveRoleId, verboseUserMention } from "../../../utils"; -import { rolesCmd } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { rolesCmd } from "../types"; export const AddRoleCmd = rolesCmd({ trigger: "addrole", diff --git a/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts b/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts index 0b09535c..4c3f1ab6 100644 --- a/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/MassAddRoleCmd.ts @@ -1,12 +1,11 @@ import { GuildMember } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { canActOn, sendErrorMessage } from "../../../pluginUtils"; import { resolveMember, resolveRoleId, successMessage } from "../../../utils"; -import { rolesCmd } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { rolesCmd } from "../types"; export const MassAddRoleCmd = rolesCmd({ trigger: "massaddrole", diff --git a/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts b/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts index e5cedc42..2bc4cfe3 100644 --- a/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/MassRemoveRoleCmd.ts @@ -1,12 +1,11 @@ import { GuildMember } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { canActOn, sendErrorMessage } from "../../../pluginUtils"; import { resolveMember, resolveRoleId, successMessage } from "../../../utils"; -import { rolesCmd } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { rolesCmd } from "../types"; export const MassRemoveRoleCmd = rolesCmd({ trigger: "massremoverole", diff --git a/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts b/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts index 63a843c8..589b9980 100644 --- a/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts +++ b/backend/src/plugins/Roles/commands/RemoveRoleCmd.ts @@ -1,11 +1,10 @@ import { GuildChannel } from "discord.js"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { LogType } from "../../../data/LogType"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { resolveRoleId, verboseUserMention } from "../../../utils"; -import { rolesCmd } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { rolesCmd } from "../types"; export const RemoveRoleCmd = rolesCmd({ trigger: "removerole", diff --git a/backend/src/plugins/Roles/types.ts b/backend/src/plugins/Roles/types.ts index e8c614a6..346dc562 100644 --- a/backend/src/plugins/Roles/types.ts +++ b/backend/src/plugins/Roles/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand } from "knub"; +import { BasePluginType, guildPluginMessageCommand } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; export const ConfigSchema = t.type({ @@ -16,4 +16,4 @@ export interface RolesPluginType extends BasePluginType { }; } -export const rolesCmd = typedGuildCommand(); +export const rolesCmd = guildPluginMessageCommand(); diff --git a/backend/src/plugins/SelfGrantableRoles/SelfGrantableRolesPlugin.ts b/backend/src/plugins/SelfGrantableRoles/SelfGrantableRolesPlugin.ts index 82396cb9..9a2c7dd1 100644 --- a/backend/src/plugins/SelfGrantableRoles/SelfGrantableRolesPlugin.ts +++ b/backend/src/plugins/SelfGrantableRoles/SelfGrantableRolesPlugin.ts @@ -1,5 +1,7 @@ +import * as t from "io-ts"; import { CooldownManager, PluginOptions } from "knub"; import { trimPluginDescription } from "../../utils"; +import { validate } from "../../validatorUtils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { RoleAddCmd } from "./commands/RoleAddCmd"; import { RoleHelpCmd } from "./commands/RoleHelpCmd"; @@ -17,9 +19,6 @@ export const SelfGrantableRolesPlugin = zeppelinGuildPlugin { - const config = options.config; - for (const [key, entry] of Object.entries(config.entries)) { + configParser: (input) => { + const entries = (input as any).entries; + for (const [key, entry] of Object.entries(entries)) { // Apply default entry config - config.entries[key] = { ...defaultSelfGrantableRoleEntry, ...entry }; + entries[key] = { ...defaultSelfGrantableRoleEntry, ...entry }; // Normalize alias names if (entry.roles) { - for (const [roleId, aliases] of Object.entries(entry.roles)) { + for (const [roleId, aliases] of Object.entries(entry.roles)) { entry.roles[roleId] = aliases.map((a) => a.toLowerCase()); } } } - return { ...options, config }; + const error = validate(ConfigSchema, input); + if (error) { + throw error; + } + + return input as t.TypeOf; }, + defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ RoleHelpCmd, RoleRemoveCmd, RoleAddCmd, diff --git a/backend/src/plugins/SelfGrantableRoles/types.ts b/backend/src/plugins/SelfGrantableRoles/types.ts index dbebcf9f..08d665df 100644 --- a/backend/src/plugins/SelfGrantableRoles/types.ts +++ b/backend/src/plugins/SelfGrantableRoles/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, CooldownManager, typedGuildCommand } from "knub"; +import { BasePluginType, CooldownManager, guildPluginMessageCommand } from "knub"; const RoleMap = t.record(t.string, t.array(t.string)); @@ -31,4 +31,4 @@ export interface SelfGrantableRolesPluginType extends BasePluginType { }; } -export const selfGrantableRolesCmd = typedGuildCommand(); +export const selfGrantableRolesCmd = guildPluginMessageCommand(); diff --git a/backend/src/plugins/Slowmode/SlowmodePlugin.ts b/backend/src/plugins/Slowmode/SlowmodePlugin.ts index abd0d884..a79ca25a 100644 --- a/backend/src/plugins/Slowmode/SlowmodePlugin.ts +++ b/backend/src/plugins/Slowmode/SlowmodePlugin.ts @@ -2,6 +2,7 @@ import { PluginOptions } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildSlowmodes } from "../../data/GuildSlowmodes"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { SECONDS } from "../../utils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -40,6 +41,7 @@ export const SlowmodePlugin = zeppelinGuildPlugin()({ showInDocs: true, info: { prettyName: "Slowmode", + configSchema: ConfigSchema, }, // prettier-ignore @@ -47,11 +49,11 @@ export const SlowmodePlugin = zeppelinGuildPlugin()({ LogsPlugin, ], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ SlowmodeDisableCmd, SlowmodeClearCmd, SlowmodeListCmd, @@ -79,7 +81,9 @@ export const SlowmodePlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn); - clearInterval(pluginData.state.clearInterval); + const { state, guild } = pluginData; + + state.savedMessages.events.off("create", state.onMessageCreateFn); + clearInterval(state.clearInterval); }, }); diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts index c9a31ddd..cf434386 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts @@ -1,5 +1,4 @@ -import { Util } from "discord.js"; -import { ChannelTypeStrings } from "src/types"; +import { ChannelType, escapeInlineCode } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { asSingleLine } from "../../../utils"; @@ -39,7 +38,7 @@ export const SlowmodeClearCmd = slowmodeCmd({ } try { - if (args.channel.type === ChannelTypeStrings.TEXT) { + if (args.channel.type === ChannelType.GuildText) { await clearBotSlowmodeFromUserId(pluginData, args.channel, args.user.id, args.force); } else { sendErrorMessage( @@ -58,7 +57,7 @@ export const SlowmodeClearCmd = slowmodeCmd({ msg.channel, asSingleLine(` Failed to clear slowmode from **${args.user.tag}** in <#${args.channel.id}>: - \`${Util.escapeInlineCode(e.message)}\` + \`${escapeInlineCode(e.message)}\` `), ); return; diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeGetCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeGetCmd.ts index e1302464..9583d433 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeGetCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeGetCmd.ts @@ -1,4 +1,3 @@ -import { TextChannel } from "discord.js"; import humanizeDuration from "humanize-duration"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { slowmodeCmd } from "../types"; @@ -13,7 +12,7 @@ export const SlowmodeGetCmd = slowmodeCmd({ }, async run({ message: msg, args, pluginData }) { - const channel = args.channel || (msg.channel as TextChannel); + const channel = args.channel || msg.channel; let currentSlowmode = channel.rateLimitPerUser; let isNative = true; diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeListCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeListCmd.ts index 58e2bffd..0fe32f84 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeListCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeListCmd.ts @@ -1,6 +1,6 @@ import { GuildChannel, TextChannel } from "discord.js"; import humanizeDuration from "humanize-duration"; -import { createChunkedMessage } from "knub/dist/helpers"; +import { createChunkedMessage } from "knub/helpers"; import { errorMessage } from "../../../utils"; import { slowmodeCmd } from "../types"; diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts index 192c2f43..44d5c111 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts @@ -1,6 +1,5 @@ -import { Permissions, TextChannel, ThreadChannel, Util } from "discord.js"; +import { escapeInlineCode, PermissionsBitField } from "discord.js"; import humanizeDuration from "humanize-duration"; -import { ChannelTypeStrings } from "src/types"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { asSingleLine, DAYS, HOURS, MINUTES } from "../../../utils"; @@ -23,7 +22,6 @@ export const SlowmodeSetCmd = slowmodeCmd({ permission: "can_manage", source: "guild", - // prettier-ignore signature: [ { time: ct.delay(), @@ -35,11 +33,16 @@ export const SlowmodeSetCmd = slowmodeCmd({ time: ct.delay(), mode: ct.string({ option: true, shortcut: "m" }), - } + }, ], async run({ message: msg, args, pluginData }) { - const channel: TextChannel | ThreadChannel = args.channel || msg.channel; + const channel = args.channel || msg.channel; + + if (!channel.isTextBased() || channel.isThread()) { + sendErrorMessage(pluginData, msg.channel, "Slowmode can only be set on non-thread text-based channels"); + return; + } if (args.time === 0) { // Workaround until we can call SlowmodeDisableCmd from here @@ -89,7 +92,7 @@ export const SlowmodeSetCmd = slowmodeCmd({ if (mode === "native") { const missingPermissions = getMissingPermissions( - channelPermissions ?? new Permissions(), + channelPermissions ?? new PermissionsBitField(), NATIVE_SLOWMODE_PERMISSIONS, ); if (missingPermissions) { @@ -104,7 +107,7 @@ export const SlowmodeSetCmd = slowmodeCmd({ if (mode === "bot") { const missingPermissions = getMissingPermissions( - channelPermissions ?? new Permissions(), + channelPermissions ?? new PermissionsBitField(), BOT_SLOWMODE_PERMISSIONS, ); if (missingPermissions) { @@ -123,25 +126,21 @@ export const SlowmodeSetCmd = slowmodeCmd({ if (mode === "native") { // If there is an existing bot-maintained slowmode, disable that first const existingBotSlowmode = await pluginData.state.slowmodes.getChannelSlowmode(channel.id); - if (existingBotSlowmode && channel.type === ChannelTypeStrings.TEXT) { + if (existingBotSlowmode && channel.isTextBased()) { await disableBotSlowmodeForChannel(pluginData, channel); } // Set native slowmode try { - await channel.edit({ - rateLimitPerUser: rateLimitSeconds, - }); + await channel.setRateLimitPerUser(rateLimitSeconds); } catch (e) { - sendErrorMessage(pluginData, msg.channel, `Failed to set native slowmode: ${Util.escapeInlineCode(e.message)}`); + sendErrorMessage(pluginData, msg.channel, `Failed to set native slowmode: ${escapeInlineCode(e.message)}`); return; } } else { // If there is an existing native slowmode, disable that first if (channel.rateLimitPerUser) { - await channel.edit({ - rateLimitPerUser: 0, - }); + await channel.setRateLimitPerUser(0); } // Set bot-maintained slowmode @@ -149,7 +148,7 @@ export const SlowmodeSetCmd = slowmodeCmd({ // Update cache const slowmode = await pluginData.state.slowmodes.getChannelSlowmode(channel.id); - await pluginData.state.channelSlowmodeCache.set(channel.id, slowmode ?? null); + pluginData.state.channelSlowmodeCache.set(channel.id, slowmode ?? null); } const humanizedSlowmodeTime = humanizeDuration(args.time); diff --git a/backend/src/plugins/Slowmode/requiredPermissions.ts b/backend/src/plugins/Slowmode/requiredPermissions.ts index ea9065be..1a8ac41d 100644 --- a/backend/src/plugins/Slowmode/requiredPermissions.ts +++ b/backend/src/plugins/Slowmode/requiredPermissions.ts @@ -1,8 +1,8 @@ -import { Permissions } from "discord.js"; +import { PermissionsBitField } from "discord.js"; -const p = Permissions.FLAGS; +const p = PermissionsBitField.Flags; -export const NATIVE_SLOWMODE_PERMISSIONS = p.VIEW_CHANNEL | p.MANAGE_CHANNELS; -export const BOT_SLOWMODE_PERMISSIONS = p.VIEW_CHANNEL | p.MANAGE_ROLES | p.MANAGE_MESSAGES; -export const BOT_SLOWMODE_CLEAR_PERMISSIONS = p.VIEW_CHANNEL | p.MANAGE_ROLES; -export const BOT_SLOWMODE_DISABLE_PERMISSIONS = p.VIEW_CHANNEL | p.MANAGE_ROLES; +export const NATIVE_SLOWMODE_PERMISSIONS = p.ViewChannel | p.ManageChannels; +export const BOT_SLOWMODE_PERMISSIONS = p.ViewChannel | p.ManageRoles | p.ManageMessages; +export const BOT_SLOWMODE_CLEAR_PERMISSIONS = p.ViewChannel | p.ManageRoles; +export const BOT_SLOWMODE_DISABLE_PERMISSIONS = p.ViewChannel | p.ManageRoles; diff --git a/backend/src/plugins/Slowmode/types.ts b/backend/src/plugins/Slowmode/types.ts index 21f0de69..61b468b3 100644 --- a/backend/src/plugins/Slowmode/types.ts +++ b/backend/src/plugins/Slowmode/types.ts @@ -1,9 +1,9 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; +import { SlowmodeChannel } from "../../data/entities/SlowmodeChannel"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildSlowmodes } from "../../data/GuildSlowmodes"; -import { SlowmodeChannel } from "../../data/entities/SlowmodeChannel"; export const ConfigSchema = t.type({ use_native_slowmode: t.boolean, @@ -27,5 +27,5 @@ export interface SlowmodePluginType extends BasePluginType { }; } -export const slowmodeCmd = typedGuildCommand(); -export const slowmodeEvt = typedGuildEventListener(); +export const slowmodeCmd = guildPluginMessageCommand(); +export const slowmodeEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Slowmode/util/actualDisableSlowmodeCmd.ts b/backend/src/plugins/Slowmode/util/actualDisableSlowmodeCmd.ts index 09565bae..2bc00bed 100644 --- a/backend/src/plugins/Slowmode/util/actualDisableSlowmodeCmd.ts +++ b/backend/src/plugins/Slowmode/util/actualDisableSlowmodeCmd.ts @@ -1,4 +1,4 @@ -import { Message, TextChannel } from "discord.js"; +import { Message } from "discord.js"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { noop } from "../../../utils"; import { getMissingChannelPermissions } from "../../../utils/getMissingChannelPermissions"; @@ -11,7 +11,7 @@ export async function actualDisableSlowmodeCmd(msg: Message, args, pluginData) { const hasNativeSlowmode = args.channel.rateLimitPerUser; if (!botSlowmode && hasNativeSlowmode === 0) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "Channel is not on slowmode!"); + sendErrorMessage(pluginData, msg.channel, "Channel is not on slowmode!"); return; } @@ -20,7 +20,7 @@ export async function actualDisableSlowmodeCmd(msg: Message, args, pluginData) { if (missingPermissions) { sendErrorMessage( pluginData, - msg.channel as TextChannel, + msg.channel, `Unable to disable slowmode. ${missingPermissionError(missingPermissions)}`, ); return; @@ -43,11 +43,11 @@ export async function actualDisableSlowmodeCmd(msg: Message, args, pluginData) { if (failedUsers.length) { sendSuccessMessage( pluginData, - msg.channel as TextChannel, + msg.channel, `Slowmode disabled! Failed to clear slowmode from the following users:\n\n<@!${failedUsers.join(">\n<@!")}>`, ); } else { - sendSuccessMessage(pluginData, msg.channel as TextChannel, "Slowmode disabled!"); + sendSuccessMessage(pluginData, msg.channel, "Slowmode disabled!"); initMsg.delete().catch(noop); } } diff --git a/backend/src/plugins/Slowmode/util/applyBotSlowmodeToUserId.ts b/backend/src/plugins/Slowmode/util/applyBotSlowmodeToUserId.ts index 3f4f0c45..27da9d72 100644 --- a/backend/src/plugins/Slowmode/util/applyBotSlowmodeToUserId.ts +++ b/backend/src/plugins/Slowmode/util/applyBotSlowmodeToUserId.ts @@ -1,25 +1,27 @@ -import { GuildChannel, Snowflake, TextChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { isDiscordAPIError, UnknownUser, verboseChannelMention, verboseUserMention } from "../../../utils"; -import { SlowmodePluginType } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { SlowmodePluginType } from "../types"; export async function applyBotSlowmodeToUserId( pluginData: GuildPluginData, - channel: GuildChannel & TextChannel, + channel: GuildTextBasedChannel, userId: string, ) { + // FIXME: Is there a better way to do this? + if (channel.isThread()) return; + // Deny sendMessage permission from the user. If there are existing permission overwrites, take those into account. const existingOverride = channel.permissionOverwrites?.resolve(userId as Snowflake); try { pluginData.state.serverLogs.ignoreLog(LogType.CHANNEL_UPDATE, channel.id, 5 * 1000); if (existingOverride) { - await existingOverride.edit({ SEND_MESSAGES: false }); + await existingOverride.edit({ SendMessages: false }); } else { - await channel.permissionOverwrites?.create(userId as Snowflake, { SEND_MESSAGES: false }, { type: 1 }); + await channel.permissionOverwrites?.create(userId as Snowflake, { SendMessages: false }, { type: 1 }); } } catch (e) { const user = await pluginData.client.users.fetch(userId as Snowflake).catch(() => new UnknownUser({ id: userId })); diff --git a/backend/src/plugins/Slowmode/util/clearBotSlowmodeFromUserId.ts b/backend/src/plugins/Slowmode/util/clearBotSlowmodeFromUserId.ts index b86862ae..f76c924a 100644 --- a/backend/src/plugins/Slowmode/util/clearBotSlowmodeFromUserId.ts +++ b/backend/src/plugins/Slowmode/util/clearBotSlowmodeFromUserId.ts @@ -1,11 +1,11 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { AnyThreadChannel, GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { LogType } from "../../../data/LogType"; import { SlowmodePluginType } from "../types"; export async function clearBotSlowmodeFromUserId( pluginData: GuildPluginData, - channel: TextChannel, + channel: Exclude, userId: string, force = false, ) { diff --git a/backend/src/plugins/Slowmode/util/clearExpiredSlowmodes.ts b/backend/src/plugins/Slowmode/util/clearExpiredSlowmodes.ts index 4dcfe634..d2f840fa 100644 --- a/backend/src/plugins/Slowmode/util/clearExpiredSlowmodes.ts +++ b/backend/src/plugins/Slowmode/util/clearExpiredSlowmodes.ts @@ -1,12 +1,10 @@ import { GuildChannel, Snowflake, TextChannel } from "discord.js"; import { GuildPluginData } from "knub"; -import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; import { logger } from "../../../logger"; import { UnknownUser, verboseChannelMention, verboseUserMention } from "../../../utils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { SlowmodePluginType } from "../types"; import { clearBotSlowmodeFromUserId } from "./clearBotSlowmodeFromUserId"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export async function clearExpiredSlowmodes(pluginData: GuildPluginData) { const expiredSlowmodeUsers = await pluginData.state.slowmodes.getExpiredSlowmodeUsers(); diff --git a/backend/src/plugins/Slowmode/util/disableBotSlowmodeForChannel.ts b/backend/src/plugins/Slowmode/util/disableBotSlowmodeForChannel.ts index 7b273c1a..d3292901 100644 --- a/backend/src/plugins/Slowmode/util/disableBotSlowmodeForChannel.ts +++ b/backend/src/plugins/Slowmode/util/disableBotSlowmodeForChannel.ts @@ -1,11 +1,11 @@ -import { TextChannel } from "discord.js"; +import { AnyThreadChannel, GuildTextBasedChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { SlowmodePluginType } from "../types"; import { clearBotSlowmodeFromUserId } from "./clearBotSlowmodeFromUserId"; export async function disableBotSlowmodeForChannel( pluginData: GuildPluginData, - channel: TextChannel, + channel: Exclude, ) { // Disable channel slowmode await pluginData.state.slowmodes.deleteChannelSlowmode(channel.id); diff --git a/backend/src/plugins/Slowmode/util/onMessageCreate.ts b/backend/src/plugins/Slowmode/util/onMessageCreate.ts index cfe1af95..a83c315b 100644 --- a/backend/src/plugins/Slowmode/util/onMessageCreate.ts +++ b/backend/src/plugins/Slowmode/util/onMessageCreate.ts @@ -1,6 +1,7 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { ChannelType, GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; +import { SlowmodeChannel } from "../../../data/entities/SlowmodeChannel"; import { hasPermission } from "../../../pluginUtils"; import { resolveMember } from "../../../utils"; import { getMissingChannelPermissions } from "../../../utils/getMissingChannelPermissions"; @@ -10,13 +11,12 @@ import { LogsPlugin } from "../../Logs/LogsPlugin"; import { BOT_SLOWMODE_PERMISSIONS } from "../requiredPermissions"; import { SlowmodePluginType } from "../types"; import { applyBotSlowmodeToUserId } from "./applyBotSlowmodeToUserId"; -import { SlowmodeChannel } from "../../../data/entities/SlowmodeChannel"; export async function onMessageCreate(pluginData: GuildPluginData, msg: SavedMessage) { if (msg.is_bot) return; - const channel = pluginData.guild.channels.cache.get(msg.channel_id as Snowflake) as TextChannel; - if (!channel) return; + const channel = pluginData.guild.channels.cache.get(msg.channel_id as Snowflake) as GuildTextBasedChannel; + if (!channel?.isTextBased() || channel.type === ChannelType.GuildStageVoice) return; // Don't apply slowmode if the lock was interrupted earlier (e.g. the message was caught by word filters) const thisMsgLock = await pluginData.locks.acquire(messageLock(msg)); diff --git a/backend/src/plugins/Spam/SpamPlugin.ts b/backend/src/plugins/Spam/SpamPlugin.ts index 84182993..e4935ca3 100644 --- a/backend/src/plugins/Spam/SpamPlugin.ts +++ b/backend/src/plugins/Spam/SpamPlugin.ts @@ -3,6 +3,7 @@ import { GuildArchives } from "../../data/GuildArchives"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildMutes } from "../../data/GuildMutes"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -52,11 +53,11 @@ export const SpamPlugin = zeppelinGuildPlugin()({ For more advanced spam filtering, check out the Automod plugin! `), legacy: true, + configSchema: ConfigSchema, }, dependencies: () => [LogsPlugin], - - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore @@ -87,7 +88,9 @@ export const SpamPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn); - clearInterval(pluginData.state.expiryInterval); + const { state, guild } = pluginData; + + state.savedMessages.events.off("create", state.onMessageCreateFn); + clearInterval(state.expiryInterval); }, }); diff --git a/backend/src/plugins/Spam/types.ts b/backend/src/plugins/Spam/types.ts index 68697f02..1e561477 100644 --- a/backend/src/plugins/Spam/types.ts +++ b/backend/src/plugins/Spam/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener } from "knub"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildMutes } from "../../data/GuildMutes"; @@ -77,4 +77,4 @@ export interface SpamPluginType extends BasePluginType { }; } -export const spamEvt = typedGuildEventListener(); +export const spamEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts b/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts index ab95f5b3..57ee49eb 100644 --- a/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts +++ b/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts @@ -1,7 +1,6 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake, TextChannel } from "discord.js"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; @@ -142,7 +141,9 @@ export async function logAndDetectMessageSpam( clearRecentUserActions(pluginData, type, savedMessage.user_id, savedMessage.channel_id); // Generate a log from the detected messages - const channel = pluginData.guild.channels.cache.get(savedMessage.channel_id as Snowflake); + const channel = pluginData.guild.channels.cache.get( + savedMessage.channel_id as Snowflake, + ) as GuildTextBasedChannel; const archiveUrl = await saveSpamArchives(pluginData, uniqueMessages); // Create a case diff --git a/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts b/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts index 4a1256d1..7bda3cc6 100644 --- a/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts +++ b/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts @@ -1,7 +1,5 @@ import { GuildPluginData } from "knub"; -import { memberToTemplateSafeMember } from "../../../utils/templateSafeObjects"; import { CaseTypes } from "../../../data/CaseTypes"; -import { LogType } from "../../../data/LogType"; import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin"; import { MutesPlugin } from "../../../plugins/Mutes/MutesPlugin"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; diff --git a/backend/src/plugins/Starboard/StarboardPlugin.ts b/backend/src/plugins/Starboard/StarboardPlugin.ts index d6a60a11..c4bb4ead 100644 --- a/backend/src/plugins/Starboard/StarboardPlugin.ts +++ b/backend/src/plugins/Starboard/StarboardPlugin.ts @@ -1,8 +1,10 @@ +import * as t from "io-ts"; import { PluginOptions } from "knub"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildStarboardMessages } from "../../data/GuildStarboardMessages"; import { GuildStarboardReactions } from "../../data/GuildStarboardReactions"; import { trimPluginDescription } from "../../utils"; +import { validate } from "../../validatorUtils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { MigratePinsCmd } from "./commands/MigratePinsCmd"; import { StarboardReactionAddEvt } from "./events/StarboardReactionAddEvt"; @@ -30,9 +32,6 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ name: "starboard", showInDocs: true, - configSchema: ConfigSchema, - defaultOptions, - info: { prettyName: "Starboard", description: trimPluginDescription(` @@ -42,13 +41,13 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ ### Note on emojis To specify emoji in the config, you need to use the emoji's "raw form". To obtain this, post the emoji with a backslash in front of it. - + - Example with a default emoji: "\:star:" => "⭐" - Example with a custom emoji: "\:mrvnSmile:" => "<:mrvnSmile:543000534102310933>" ### Basic starboard Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226). - + ~~~yml starboard: config: @@ -57,10 +56,10 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ channel_id: "604342689038729226" stars_required: 5 ~~~ - + ### Basic starboard with custom color Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226), with the given color (0x87CEEB). - + ~~~yml starboard: config: @@ -70,10 +69,10 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ stars_required: 5 color: 0x87CEEB ~~~ - + ### Custom star emoji This is identical to the basic starboard above, but accepts two emoji: the regular star and a custom :mrvnSmile: emoji - + ~~~yml starboard: config: @@ -83,10 +82,10 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ star_emoji: ["⭐", "<:mrvnSmile:543000534102310933>"] stars_required: 5 ~~~ - + ### Limit starboard to a specific channel This is identical to the basic starboard above, but only works from a specific channel (473087035574321152). - + ~~~yml starboard: config: @@ -105,7 +104,7 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ ### Limit starboard to a specific level (and above) This is identical to the basic starboard above, but only works for a specific level (>=50). - + ~~~yml starboard: config: @@ -122,28 +121,36 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ enabled: true ~~~ `), + configSchema: ConfigSchema, }, - configPreprocessor(options) { - if (options.config?.boards) { - for (const [name, opts] of Object.entries(options.config.boards)) { - options.config.boards[name] = Object.assign({}, defaultStarboardOpts, options.config.boards[name]); + configParser(input) { + const boards = (input as any).boards; + if (boards) { + for (const [name, opts] of Object.entries(boards)) { + boards[name] = Object.assign({}, defaultStarboardOpts, boards[name]); } } - return options; + const error = validate(ConfigSchema, input); + if (error) { + throw error; + } + + return input as t.TypeOf; }, + defaultOptions, // prettier-ignore - commands: [ - MigratePinsCmd, + messageCommands: [ + MigratePinsCmd, ], // prettier-ignore events: [ - StarboardReactionAddEvt, - StarboardReactionRemoveEvt, - StarboardReactionRemoveAllEvt, + StarboardReactionAddEvt, + StarboardReactionRemoveEvt, + StarboardReactionRemoveAllEvt, ], beforeLoad(pluginData) { @@ -162,6 +169,8 @@ export const StarboardPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - pluginData.state.savedMessages.events.off("delete", pluginData.state.onMessageDeleteFn); + const { state, guild } = pluginData; + + state.savedMessages.events.off("delete", state.onMessageDeleteFn); }, }); diff --git a/backend/src/plugins/Starboard/types.ts b/backend/src/plugins/Starboard/types.ts index 4e1f54eb..223501e3 100644 --- a/backend/src/plugins/Starboard/types.ts +++ b/backend/src/plugins/Starboard/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildStarboardMessages } from "../../data/GuildStarboardMessages"; import { GuildStarboardReactions } from "../../data/GuildStarboardReactions"; @@ -44,5 +44,5 @@ export interface StarboardPluginType extends BasePluginType { }; } -export const starboardCmd = typedGuildCommand(); -export const starboardEvt = typedGuildEventListener(); +export const starboardCmd = guildPluginMessageCommand(); +export const starboardEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts b/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts index 053389db..84e338f4 100644 --- a/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts +++ b/backend/src/plugins/Starboard/util/createStarboardEmbedFromMessage.ts @@ -21,14 +21,14 @@ export function createStarboardEmbedFromMessage( name: msg.author.tag, }, fields: [], - timestamp: msg.createdTimestamp, + timestamp: msg.createdAt.toISOString(), }; if (color != null) { embed.color = color; } - embed.author.icon_url = msg.author.displayAvatarURL({ dynamic: true }); + embed.author.icon_url = msg.author.displayAvatarURL(); // The second condition here checks for messages with only an image link that is then embedded. // The message content in that case is hidden by the Discord client, so we hide it here too. diff --git a/backend/src/plugins/Starboard/util/removeMessageFromStarboard.ts b/backend/src/plugins/Starboard/util/removeMessageFromStarboard.ts index 6535852f..cb62432e 100644 --- a/backend/src/plugins/Starboard/util/removeMessageFromStarboard.ts +++ b/backend/src/plugins/Starboard/util/removeMessageFromStarboard.ts @@ -1,3 +1,4 @@ +import { ChannelType } from "discord.js"; import { GuildPluginData } from "knub"; import { StarboardMessage } from "../../../data/entities/StarboardMessage"; import { noop } from "../../../utils"; @@ -12,7 +13,7 @@ export async function removeMessageFromStarboard( // this code is now Almeida-certified and no longer ugly :ok_hand: :cake: const channel = pluginData.client.channels.cache.find((c) => c.id === msg.starboard_channel_id); - if (!channel?.isText()) return; + if (channel?.type !== ChannelType.GuildText) return; const message = await channel.messages.fetch(msg.starboard_message_id).catch(noop); if (!message?.deletable) return; await message.delete().catch(noop); diff --git a/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts b/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts index ed2bd3f9..af614705 100644 --- a/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts +++ b/backend/src/plugins/Starboard/util/saveMessageToStarboard.ts @@ -1,4 +1,4 @@ -import { Message, MessageEmbedOptions, Snowflake, TextChannel } from "discord.js"; +import { APIEmbed, Message, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { StarboardPluginType, TStarboardOpts } from "../types"; import { createStarboardEmbedFromMessage } from "./createStarboardEmbedFromMessage"; @@ -10,12 +10,12 @@ export async function saveMessageToStarboard( starboard: TStarboardOpts, ) { const channel = pluginData.guild.channels.cache.get(starboard.channel_id as Snowflake); - if (!channel) return; + if (!channel?.isTextBased()) return; const starCount = (await pluginData.state.starboardReactions.getAllReactionsForMessageId(msg.id)).length; const embed = createStarboardEmbedFromMessage(msg, Boolean(starboard.copy_full_embed), starboard.color); embed.fields!.push(createStarboardPseudoFooterForMessage(starboard, msg, starboard.star_emoji![0], starCount)); - const starboardMessage = await (channel as TextChannel).send({ embeds: [embed as MessageEmbedOptions] }); + const starboardMessage = await channel.send({ embeds: [embed as APIEmbed] }); await pluginData.state.starboardMessages.createStarboardMessage(channel.id, msg.id, starboardMessage.id); } diff --git a/backend/src/plugins/Tags/TagsPlugin.ts b/backend/src/plugins/Tags/TagsPlugin.ts index c2cc6a1d..2e0a612b 100644 --- a/backend/src/plugins/Tags/TagsPlugin.ts +++ b/backend/src/plugins/Tags/TagsPlugin.ts @@ -1,14 +1,16 @@ import { Snowflake } from "discord.js"; import humanizeDuration from "humanize-duration"; +import * as t from "io-ts"; import { PluginOptions } from "knub"; import moment from "moment-timezone"; -import { StrictValidationError } from "src/validatorUtils"; +import { StrictValidationError, validate } from "src/validatorUtils"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildTags } from "../../data/GuildTags"; import { mapToPublicFn } from "../../pluginUtils"; import { convertDelayStringToMS, trimPluginDescription } from "../../utils"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { TagCreateCmd } from "./commands/TagCreateCmd"; @@ -16,14 +18,13 @@ import { TagDeleteCmd } from "./commands/TagDeleteCmd"; import { TagEvalCmd } from "./commands/TagEvalCmd"; import { TagListCmd } from "./commands/TagListCmd"; import { TagSourceCmd } from "./commands/TagSourceCmd"; +import { generateTemplateMarkdown } from "./docs"; +import { TemplateFunctions } from "./templateFunctions"; import { ConfigSchema, TagsPluginType } from "./types"; import { findTagByName } from "./util/findTagByName"; import { onMessageCreate } from "./util/onMessageCreate"; import { onMessageDelete } from "./util/onMessageDelete"; import { renderTagBody } from "./util/renderTagBody"; -import { LogsPlugin } from "../Logs/LogsPlugin"; -import { generateTemplateMarkdown } from "./docs"; -import { TemplateFunctions } from "./templateFunctions"; const defaultOptions: PluginOptions = { config: { @@ -68,17 +69,17 @@ export const TagsPlugin = zeppelinGuildPlugin()({ You use them by adding a \`{}\` on your tag. Here are the functions you can use in your tags: - + ${generateTemplateMarkdown(TemplateFunctions)} `), + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, dependencies: () => [LogsPlugin], defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ TagEvalCmd, TagDeleteCmd, TagListCmd, @@ -96,17 +97,19 @@ export const TagsPlugin = zeppelinGuildPlugin()({ findTagByName: mapToPublicFn(findTagByName), }, - configPreprocessor(options) { - if (options.config.delete_with_command && options.config.auto_delete_command) { + configParser(_input) { + const input = _input as any; + + if (input.delete_with_command && input.auto_delete_command) { throw new StrictValidationError([ `Cannot have both (global) delete_with_command and global_delete_invoke enabled`, ]); } // Check each category for conflicting options - if (options.config?.categories) { - for (const [name, opts] of Object.entries(options.config.categories)) { - const cat = options.config.categories[name]; + if (input.categories) { + for (const [name, opts] of Object.entries(input.categories)) { + const cat = input.categories[name]; if (cat.delete_with_command && cat.auto_delete_command) { throw new StrictValidationError([ `Cannot have both (category specific) delete_with_command and category_delete_invoke enabled at `, @@ -115,7 +118,12 @@ export const TagsPlugin = zeppelinGuildPlugin()({ } } - return options; + const error = validate(ConfigSchema, input); + if (error) { + throw error; + } + + return input as t.TypeOf; }, beforeLoad(pluginData) { @@ -130,7 +138,7 @@ export const TagsPlugin = zeppelinGuildPlugin()({ }, afterLoad(pluginData) { - const { state, guild } = pluginData; + const { state } = pluginData; state.onMessageCreateFn = (msg) => onMessageCreate(pluginData, msg); state.savedMessages.events.on("create", state.onMessageCreateFn); @@ -277,6 +285,8 @@ export const TagsPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn); + const { state, guild } = pluginData; + + state.savedMessages.events.off("create", state.onMessageCreateFn); }, }); diff --git a/backend/src/plugins/Tags/commands/TagEvalCmd.ts b/backend/src/plugins/Tags/commands/TagEvalCmd.ts index bf9c3f1f..55659d30 100644 --- a/backend/src/plugins/Tags/commands/TagEvalCmd.ts +++ b/backend/src/plugins/Tags/commands/TagEvalCmd.ts @@ -1,7 +1,8 @@ -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; +import { MessageCreateOptions } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { TemplateParseError } from "../../../templateFormatter"; +import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { tagsCmd } from "../types"; import { renderTagBody } from "../util/renderTagBody"; @@ -15,7 +16,7 @@ export const TagEvalCmd = tagsCmd({ async run({ message: msg, args, pluginData }) { try { - const rendered = await renderTagBody( + const rendered = (await renderTagBody( pluginData, args.body, [], @@ -24,7 +25,7 @@ export const TagEvalCmd = tagsCmd({ user: userToTemplateSafeUser(msg.member.user), }, { member: msg.member }, - ); + )) as MessageCreateOptions; if (!rendered.content && !rendered.embeds?.length) { sendErrorMessage(pluginData, msg.channel, "Evaluation resulted in an empty text"); diff --git a/backend/src/plugins/Tags/commands/TagListCmd.ts b/backend/src/plugins/Tags/commands/TagListCmd.ts index aaa42ec4..59d63dee 100644 --- a/backend/src/plugins/Tags/commands/TagListCmd.ts +++ b/backend/src/plugins/Tags/commands/TagListCmd.ts @@ -1,7 +1,7 @@ +import escapeStringRegexp from "escape-string-regexp"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { createChunkedMessage } from "../../../utils"; import { tagsCmd } from "../types"; -import escapeStringRegexp from "escape-string-regexp"; export const TagListCmd = tagsCmd({ trigger: ["tag list", "tags", "taglist"], diff --git a/backend/src/plugins/Tags/types.ts b/backend/src/plugins/Tags/types.ts index 7c0ce10c..3a7eb2b3 100644 --- a/backend/src/plugins/Tags/types.ts +++ b/backend/src/plugins/Tags/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; @@ -68,5 +68,5 @@ export interface TemplateFunction { examples?: string[]; } -export const tagsCmd = typedGuildCommand(); -export const tagsEvt = typedGuildEventListener(); +export const tagsCmd = guildPluginMessageCommand(); +export const tagsEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Tags/util/findTagByName.ts b/backend/src/plugins/Tags/util/findTagByName.ts index 4ec61ec5..363006ff 100644 --- a/backend/src/plugins/Tags/util/findTagByName.ts +++ b/backend/src/plugins/Tags/util/findTagByName.ts @@ -1,6 +1,5 @@ import * as t from "io-ts"; -import { GuildPluginData } from "knub"; -import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; +import { ExtendedMatchParams, GuildPluginData } from "knub"; import { Tag, TagsPluginType } from "../types"; export async function findTagByName( diff --git a/backend/src/plugins/Tags/util/matchAndRenderTagFromString.ts b/backend/src/plugins/Tags/util/matchAndRenderTagFromString.ts index db436067..7c781289 100644 --- a/backend/src/plugins/Tags/util/matchAndRenderTagFromString.ts +++ b/backend/src/plugins/Tags/util/matchAndRenderTagFromString.ts @@ -1,7 +1,6 @@ import { GuildMember } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; -import { GuildPluginData } from "knub"; -import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; +import { ExtendedMatchParams, GuildPluginData } from "knub"; import { StrictMessageContent } from "../../../utils"; import { TagsPluginType, TTagCategory } from "../types"; import { renderTagFromString } from "./renderTagFromString"; diff --git a/backend/src/plugins/Tags/util/onMessageCreate.ts b/backend/src/plugins/Tags/util/onMessageCreate.ts index 052c6395..ba5a14da 100644 --- a/backend/src/plugins/Tags/util/onMessageCreate.ts +++ b/backend/src/plugins/Tags/util/onMessageCreate.ts @@ -2,13 +2,12 @@ import { Snowflake, TextChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { erisAllowedMentionsToDjsMentionOptions } from "src/utils/erisAllowedMentionsToDjsMentionOptions"; import { SavedMessage } from "../../../data/entities/SavedMessage"; -import { LogType } from "../../../data/LogType"; import { convertDelayStringToMS, resolveMember, tStrictMessageContent } from "../../../utils"; import { messageIsEmpty } from "../../../utils/messageIsEmpty"; import { validate } from "../../../validatorUtils"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; import { TagsPluginType } from "../types"; import { matchAndRenderTagFromString } from "./matchAndRenderTagFromString"; -import { LogsPlugin } from "../../Logs/LogsPlugin"; export async function onMessageCreate(pluginData: GuildPluginData, msg: SavedMessage) { if (msg.is_bot) return; @@ -17,8 +16,8 @@ export async function onMessageCreate(pluginData: GuildPluginData delete the response as well const commandMsgResponse = await pluginData.state.tags.findResponseByCommandMessageId(msg.id); if (commandMsgResponse) { - const channel = pluginData.guild.channels.cache.get(msg.channelId) as TextChannel; - if (!channel) return; - await pluginData.state.tags.deleteResponseByCommandMessageId(msg.id); await channel.messages.delete(commandMsgResponse.response_message_id).catch(noop); return; @@ -21,9 +18,6 @@ export const onMessageDelete = typedGuildEventListener({ // Response was deleted -> delete the command message as well const responseMsgResponse = await pluginData.state.tags.findResponseByResponseMessageId(msg.id); if (responseMsgResponse) { - const channel = pluginData.guild.channels.cache.get(msg.channelId) as TextChannel; - if (!channel) return; - await pluginData.state.tags.deleteResponseByResponseMessageId(msg.id); await channel.messages.delete(responseMsgResponse.command_message_id).catch(noop); return; diff --git a/backend/src/plugins/Tags/util/renderTagBody.ts b/backend/src/plugins/Tags/util/renderTagBody.ts index 5174e7b1..36103c7e 100644 --- a/backend/src/plugins/Tags/util/renderTagBody.ts +++ b/backend/src/plugins/Tags/util/renderTagBody.ts @@ -1,5 +1,4 @@ -import { GuildPluginData } from "knub"; -import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; +import { ExtendedMatchParams, GuildPluginData } from "knub"; import { renderTemplate, TemplateSafeValue, TemplateSafeValueContainer } from "../../../templateFormatter"; import { renderRecursively, StrictMessageContent } from "../../../utils"; import { TagsPluginType, TTag } from "../types"; diff --git a/backend/src/plugins/Tags/util/renderTagFromString.ts b/backend/src/plugins/Tags/util/renderTagFromString.ts index b482741d..84d1759f 100644 --- a/backend/src/plugins/Tags/util/renderTagFromString.ts +++ b/backend/src/plugins/Tags/util/renderTagFromString.ts @@ -1,9 +1,9 @@ import { GuildMember } from "discord.js"; import { GuildPluginData } from "knub"; import { parseArguments } from "knub-command-manager"; -import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { TemplateParseError } from "../../../templateFormatter"; import { StrictMessageContent, validateAndParseMessageContent } from "../../../utils"; +import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { TagsPluginType, TTag } from "../types"; import { renderTagBody } from "./renderTagBody"; diff --git a/backend/src/plugins/TimeAndDate/TimeAndDatePlugin.ts b/backend/src/plugins/TimeAndDate/TimeAndDatePlugin.ts index 2f0d6cdf..f4d5ded0 100644 --- a/backend/src/plugins/TimeAndDate/TimeAndDatePlugin.ts +++ b/backend/src/plugins/TimeAndDate/TimeAndDatePlugin.ts @@ -1,6 +1,6 @@ import { PluginOptions } from "knub"; import { GuildMemberTimezones } from "../../data/GuildMemberTimezones"; -import { mapToPublicFn } from "../../pluginUtils"; +import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils"; import { trimPluginDescription } from "../../utils"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { ResetTimezoneCmd } from "./commands/ResetTimezoneCmd"; @@ -39,13 +39,14 @@ export const TimeAndDatePlugin = zeppelinGuildPlugin()({ description: trimPluginDescription(` Allows controlling the displayed time/date formats and timezones `), + configSchema: ConfigSchema, }, - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ ResetTimezoneCmd, SetTimezoneCmd, ViewTimezoneCmd, @@ -60,6 +61,8 @@ export const TimeAndDatePlugin = zeppelinGuildPlugin()({ }, beforeLoad(pluginData) { - pluginData.state.memberTimezones = GuildMemberTimezones.getGuildInstance(pluginData.guild.id); + const { state, guild } = pluginData; + + state.memberTimezones = GuildMemberTimezones.getGuildInstance(guild.id); }, }); diff --git a/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts b/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts index d88a0740..90d00b3b 100644 --- a/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts +++ b/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts @@ -1,4 +1,4 @@ -import { Util } from "discord.js"; +import { escapeInlineCode } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { trimLines } from "../../../utils"; @@ -20,7 +20,7 @@ export const SetTimezoneCmd = timeAndDateCmd({ pluginData, message.channel, trimLines(` - Invalid timezone: \`${Util.escapeInlineCode(args.timezone)}\` + Invalid timezone: \`${escapeInlineCode(args.timezone)}\` Zeppelin uses timezone locations rather than specific timezone names. See the **TZ database name** column at for a list of valid options. `), diff --git a/backend/src/plugins/TimeAndDate/types.ts b/backend/src/plugins/TimeAndDate/types.ts index c0144bfd..102d620b 100644 --- a/backend/src/plugins/TimeAndDate/types.ts +++ b/backend/src/plugins/TimeAndDate/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand } from "knub"; +import { BasePluginType, guildPluginMessageCommand } from "knub"; import { GuildMemberTimezones } from "../../data/GuildMemberTimezones"; import { tNullable, tPartialDictionary } from "../../utils"; import { tValidTimezone } from "../../utils/tValidTimezone"; @@ -19,4 +19,4 @@ export interface TimeAndDatePluginType extends BasePluginType { }; } -export const timeAndDateCmd = typedGuildCommand(); +export const timeAndDateCmd = guildPluginMessageCommand(); diff --git a/backend/src/plugins/UsernameSaver/UsernameSaverPlugin.ts b/backend/src/plugins/UsernameSaver/UsernameSaverPlugin.ts index 3014350c..a14ef791 100644 --- a/backend/src/plugins/UsernameSaver/UsernameSaverPlugin.ts +++ b/backend/src/plugins/UsernameSaver/UsernameSaverPlugin.ts @@ -1,5 +1,6 @@ import * as t from "io-ts"; import { UsernameHistory } from "../../data/UsernameHistory"; +import { makeIoTsConfigParser } from "../../pluginUtils"; import { Queue } from "../../Queue"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { MessageCreateUpdateUsernameEvt, VoiceChannelJoinUpdateUsernameEvt } from "./events/UpdateUsernameEvts"; @@ -9,7 +10,7 @@ export const UsernameSaverPlugin = zeppelinGuildPlugin( name: "username_saver", showInDocs: false, - configSchema: t.type({}), + configParser: makeIoTsConfigParser(t.type({})), // prettier-ignore events: [ @@ -18,7 +19,7 @@ export const UsernameSaverPlugin = zeppelinGuildPlugin( ], beforeLoad(pluginData) { - const { state, guild } = pluginData; + const { state } = pluginData; state.usernameHistory = new UsernameHistory(); state.updateQueue = new Queue(); diff --git a/backend/src/plugins/UsernameSaver/types.ts b/backend/src/plugins/UsernameSaver/types.ts index 9a4a6b04..70fc3f12 100644 --- a/backend/src/plugins/UsernameSaver/types.ts +++ b/backend/src/plugins/UsernameSaver/types.ts @@ -1,4 +1,4 @@ -import { BasePluginType, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener } from "knub"; import { UsernameHistory } from "../../data/UsernameHistory"; import { Queue } from "../../Queue"; @@ -9,4 +9,4 @@ export interface UsernameSaverPluginType extends BasePluginType { }; } -export const usernameSaverEvt = typedGuildEventListener(); +export const usernameSaverEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/Utility/UtilityPlugin.ts b/backend/src/plugins/Utility/UtilityPlugin.ts index 5a3e6636..7f5b2d7e 100644 --- a/backend/src/plugins/Utility/UtilityPlugin.ts +++ b/backend/src/plugins/Utility/UtilityPlugin.ts @@ -1,12 +1,13 @@ -import { GuildMember, MessageEmbedOptions, Snowflake } from "discord.js"; +import { GuildMember, Snowflake } from "discord.js"; import { PluginOptions } from "knub"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildCases } from "../../data/GuildCases"; import { GuildLogs } from "../../data/GuildLogs"; import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { Supporters } from "../../data/Supporters"; -import { sendSuccessMessage } from "../../pluginUtils"; +import { makeIoTsConfigParser, sendSuccessMessage } from "../../pluginUtils"; import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { ModActionsPlugin } from "../ModActions/ModActionsPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -42,7 +43,6 @@ import { hasPermission } from "./functions/hasPermission"; import { activeReloads } from "./guildReloads"; import { refreshMembersIfNeeded } from "./refreshMembers"; import { ConfigSchema, UtilityPluginType } from "./types"; -import { LogsPlugin } from "../Logs/LogsPlugin"; const defaultOptions: PluginOptions = { config: { @@ -117,14 +117,15 @@ export const UtilityPlugin = zeppelinGuildPlugin()({ showInDocs: true, info: { prettyName: "Utility", + configSchema: ConfigSchema, }, dependencies: () => [TimeAndDatePlugin, ModActionsPlugin, LogsPlugin], - configSchema: ConfigSchema, + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore - commands: [ + messageCommands: [ SearchCmd, BanSearchCmd, UserInfoCmd, diff --git a/backend/src/plugins/Utility/commands/AboutCmd.ts b/backend/src/plugins/Utility/commands/AboutCmd.ts index 369c1201..3e6443ef 100644 --- a/backend/src/plugins/Utility/commands/AboutCmd.ts +++ b/backend/src/plugins/Utility/commands/AboutCmd.ts @@ -1,13 +1,13 @@ -import { GuildChannel, MessageOptions } from "discord.js"; +import { APIEmbed, GuildChannel } from "discord.js"; import humanizeDuration from "humanize-duration"; import LCL from "last-commit-log"; +import { shuffle } from "lodash"; import moment from "moment-timezone"; import { rootDir } from "../../../paths"; import { getCurrentUptime } from "../../../uptime"; -import { multiSorter, resolveMember, sorter } from "../../../utils"; +import { resolveMember, sorter } from "../../../utils"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { utilityCmd } from "../types"; -import { shuffle } from "lodash"; export const AboutCmd = utilityCmd({ trigger: "about", @@ -59,20 +59,16 @@ export const AboutCmd = utilityCmd({ ); loadedPlugins.sort(); - const aboutContent: MessageOptions = { - embeds: [ + const aboutEmbed: APIEmbed = { + title: `About ${pluginData.client.user!.username}`, + fields: [ { - title: `About ${pluginData.client.user!.username}`, - fields: [ - { - name: "Status", - value: basicInfoRows.map(([label, value]) => `${label}: **${value}**`).join("\n"), - }, - { - name: `Loaded plugins on this server (${loadedPlugins.length})`, - value: loadedPlugins.join(", "), - }, - ], + name: "Status", + value: basicInfoRows.map(([label, value]) => `${label}: **${value}**`).join("\n"), + }, + { + name: `Loaded plugins on this server (${loadedPlugins.length})`, + value: loadedPlugins.join(", "), }, ], }; @@ -86,7 +82,7 @@ export const AboutCmd = utilityCmd({ .map((s, i) => (i % 2 === 0 ? `**${s.name}**` : `__${s.name}__`)) .join(" "); - aboutContent.embeds![0].fields!.push({ + aboutEmbed.fields!.push({ name: "Zeppelin supporters 🎉", value: "These amazing people have supported Zeppelin development by pledging on [Patreon](https://www.patreon.com/zeppelinbot):\n\n" + @@ -102,14 +98,14 @@ export const AboutCmd = utilityCmd({ botRoles = botRoles.filter((r) => r.color); // Filter to those with a color botRoles.sort(sorter("position", "DESC")); // Sort by position (highest first) if (botRoles.length) { - aboutContent.embeds![0].color = botRoles[0].color; + aboutEmbed.color = botRoles[0].color; } // Use the bot avatar as the embed image if (pluginData.client.user!.avatarURL()) { - aboutContent.embeds![0].thumbnail = { url: pluginData.client.user!.avatarURL()! }; + aboutEmbed.thumbnail = { url: pluginData.client.user!.avatarURL()! }; } - msg.channel.send(aboutContent); + msg.channel.send({ embeds: [aboutEmbed] }); }, }); diff --git a/backend/src/plugins/Utility/commands/AvatarCmd.ts b/backend/src/plugins/Utility/commands/AvatarCmd.ts index d503bc97..4db271f3 100644 --- a/backend/src/plugins/Utility/commands/AvatarCmd.ts +++ b/backend/src/plugins/Utility/commands/AvatarCmd.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions } from "discord.js"; +import { APIEmbed, ImageFormat } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { UnknownUser } from "../../../utils"; @@ -16,9 +16,9 @@ export const AvatarCmd = utilityCmd({ async run({ message: msg, args, pluginData }) { const user = args.user || msg.author; if (!(user instanceof UnknownUser)) { - const embed: MessageEmbedOptions = { + const embed: APIEmbed = { image: { - url: user.displayAvatarURL({ dynamic: true, format: "png", size: 2048 }), + url: user.displayAvatarURL({ extension: ImageFormat.PNG, size: 2048 }), }, title: `Avatar of ${user.tag}:`, }; diff --git a/backend/src/plugins/Utility/commands/CleanCmd.ts b/backend/src/plugins/Utility/commands/CleanCmd.ts index 6abbb481..673d7e12 100644 --- a/backend/src/plugins/Utility/commands/CleanCmd.ts +++ b/backend/src/plugins/Utility/commands/CleanCmd.ts @@ -3,13 +3,13 @@ import { GuildPluginData } from "knub"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; +import { humanizeDurationShort } from "../../../humanizeDurationShort"; import { ModActionsPlugin } from "../../../plugins/ModActions/ModActionsPlugin"; import { getBaseUrl, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { allowTimeout } from "../../../RegExpRunner"; import { DAYS, getInviteCodesInString, noop, SECONDS } from "../../../utils"; -import { utilityCmd, UtilityPluginType } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; -import { humanizeDurationShort } from "../../../humanizeDurationShort"; +import { utilityCmd, UtilityPluginType } from "../types"; const MAX_CLEAN_COUNT = 150; const MAX_CLEAN_TIME = 1 * DAYS; @@ -81,7 +81,7 @@ export async function cleanCmd(pluginData: GuildPluginData, a } const targetChannel = args.channel ? pluginData.guild.channels.cache.get(args.channel as Snowflake) : msg.channel; - if (!targetChannel?.isText()) { + if (!targetChannel?.isTextBased()) { sendErrorMessage(pluginData, msg.channel, `Invalid channel specified`); return; } diff --git a/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts b/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts index 7a60eda6..5c89f3eb 100644 --- a/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts +++ b/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts @@ -1,7 +1,7 @@ import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; -import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; import { getCustomEmojiId } from "../functions/getCustomEmojiId"; +import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; import { utilityCmd } from "../types"; export const EmojiInfoCmd = utilityCmd({ diff --git a/backend/src/plugins/Utility/commands/HelpCmd.ts b/backend/src/plugins/Utility/commands/HelpCmd.ts index b03d3aa4..7e3391fd 100644 --- a/backend/src/plugins/Utility/commands/HelpCmd.ts +++ b/backend/src/plugins/Utility/commands/HelpCmd.ts @@ -1,5 +1,4 @@ -import { LoadedGuildPlugin } from "knub"; -import { PluginCommandDefinition } from "knub/dist/commands/commandUtils"; +import { LoadedGuildPlugin, PluginCommandDefinition } from "knub"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { createChunkedMessage } from "../../../utils"; import { utilityCmd } from "../types"; @@ -24,7 +23,7 @@ export const HelpCmd = utilityCmd({ const guildData = pluginData.getKnubInstance().getLoadedGuild(pluginData.guild.id)!; for (const plugin of guildData.loadedPlugins.values()) { - const registeredCommands = plugin.pluginData.commands.getAll(); + const registeredCommands = plugin.pluginData.messageCommands.getAll(); for (const registeredCommand of registeredCommands) { for (const trigger of registeredCommand.originalTriggers) { const strTrigger = typeof trigger === "string" ? trigger : trigger.source; diff --git a/backend/src/plugins/Utility/commands/InfoCmd.ts b/backend/src/plugins/Utility/commands/InfoCmd.ts index 5dcf3456..63d0ae81 100644 --- a/backend/src/plugins/Utility/commands/InfoCmd.ts +++ b/backend/src/plugins/Utility/commands/InfoCmd.ts @@ -1,21 +1,21 @@ import { Snowflake } from "discord.js"; -import { getChannelId, getRoleId } from "knub/dist/utils"; +import { getChannelId, getRoleId } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; import { isValidSnowflake, noop, parseInviteCodeInput, resolveInvite, resolveUser } from "../../../utils"; -import { getUserInfoEmbed } from "../functions/getUserInfoEmbed"; import { canReadChannel } from "../../../utils/canReadChannel"; import { resolveMessageTarget } from "../../../utils/resolveMessageTarget"; import { getChannelInfoEmbed } from "../functions/getChannelInfoEmbed"; +import { getCustomEmojiId } from "../functions/getCustomEmojiId"; +import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; import { getGuildPreview } from "../functions/getGuildPreview"; import { getInviteInfoEmbed } from "../functions/getInviteInfoEmbed"; import { getMessageInfoEmbed } from "../functions/getMessageInfoEmbed"; import { getRoleInfoEmbed } from "../functions/getRoleInfoEmbed"; -import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; -import { getCustomEmojiId } from "../functions/getCustomEmojiId"; -import { utilityCmd } from "../types"; import { getServerInfoEmbed } from "../functions/getServerInfoEmbed"; import { getSnowflakeInfoEmbed } from "../functions/getSnowflakeInfoEmbed"; +import { getUserInfoEmbed } from "../functions/getUserInfoEmbed"; +import { utilityCmd } from "../types"; export const InfoCmd = utilityCmd({ trigger: "info", diff --git a/backend/src/plugins/Utility/commands/JumboCmd.ts b/backend/src/plugins/Utility/commands/JumboCmd.ts index d07855c1..bc9f70ab 100644 --- a/backend/src/plugins/Utility/commands/JumboCmd.ts +++ b/backend/src/plugins/Utility/commands/JumboCmd.ts @@ -1,6 +1,6 @@ -import { MessageAttachment } from "discord.js"; -import fs from "fs"; import photon from "@silvia-odwyer/photon-node"; +import { AttachmentBuilder } from "discord.js"; +import fs from "fs"; import twemoji from "twemoji"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; @@ -50,7 +50,7 @@ export const JumboCmd = utilityCmd({ const emojiRegex = new RegExp(`(<.*:).*:(\\d+)`); const results = emojiRegex.exec(args.emoji); let extension = ".png"; - let file: MessageAttachment | undefined; + let file: AttachmentBuilder | undefined; if (!isEmoji(args.emoji)) { sendErrorMessage(pluginData, msg.channel, "Invalid emoji"); @@ -65,10 +65,10 @@ export const JumboCmd = utilityCmd({ url += `${results[2]}${extension}`; if (extension === ".png") { const image = resizeBuffer(await getBufferFromUrl(url), size, size); - file = new MessageAttachment(image, `emoji${extension}`); + file = new AttachmentBuilder(image, { name: `emoji${extension}` }); } else { const image = await getBufferFromUrl(url); - file = new MessageAttachment(image, `emoji${extension}`); + file = new AttachmentBuilder(image, { name: `emoji${extension}` }); } } else { let url = `${twemoji.base}${twemoji.size}/${twemoji.convert.toCodePoint(args.emoji)}${twemoji.ext}`; @@ -87,7 +87,7 @@ export const JumboCmd = utilityCmd({ return; } - file = new MessageAttachment(image, "emoji.png"); + file = new AttachmentBuilder(image, { name: "emoji.png" }); } msg.channel.send({ files: [file] }); diff --git a/backend/src/plugins/Utility/commands/NicknameCmd.ts b/backend/src/plugins/Utility/commands/NicknameCmd.ts index f689a6af..9821ef32 100644 --- a/backend/src/plugins/Utility/commands/NicknameCmd.ts +++ b/backend/src/plugins/Utility/commands/NicknameCmd.ts @@ -1,7 +1,7 @@ -import { Util } from "discord.js"; +import { escapeBold } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { errorMessage } from "../../../utils"; import { canActOn, sendSuccessMessage } from "../../../pluginUtils"; +import { errorMessage } from "../../../utils"; import { utilityCmd } from "../types"; export const NicknameCmd = utilityCmd({ @@ -20,7 +20,7 @@ export const NicknameCmd = utilityCmd({ if (!args.member.nickname) { msg.channel.send(`<@!${args.member.id}> does not have a nickname`); } else { - msg.channel.send(`The nickname of <@!${args.member.id}> is **${Util.escapeBold(args.member.nickname)}**`); + msg.channel.send(`The nickname of <@!${args.member.id}> is **${escapeBold(args.member.nickname)}**`); } return; } diff --git a/backend/src/plugins/Utility/commands/SourceCmd.ts b/backend/src/plugins/Utility/commands/SourceCmd.ts index b5e4e802..54c24647 100644 --- a/backend/src/plugins/Utility/commands/SourceCmd.ts +++ b/backend/src/plugins/Utility/commands/SourceCmd.ts @@ -1,4 +1,3 @@ -import { Snowflake } from "discord.js"; import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { getBaseUrl, sendErrorMessage } from "../../../pluginUtils"; diff --git a/backend/src/plugins/Utility/commands/VcdisconnectCmd.ts b/backend/src/plugins/Utility/commands/VcdisconnectCmd.ts index def3201c..1cfc305b 100644 --- a/backend/src/plugins/Utility/commands/VcdisconnectCmd.ts +++ b/backend/src/plugins/Utility/commands/VcdisconnectCmd.ts @@ -1,14 +1,8 @@ import { VoiceChannel } from "discord.js"; -import { - channelToTemplateSafeChannel, - memberToTemplateSafeMember, - userToTemplateSafeUser, -} from "../../../utils/templateSafeObjects"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { LogType } from "../../../data/LogType"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { utilityCmd } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { utilityCmd } from "../types"; export const VcdisconnectCmd = utilityCmd({ trigger: ["vcdisconnect", "vcdisc", "vcdc", "vckick", "vck"], diff --git a/backend/src/plugins/Utility/commands/VcmoveCmd.ts b/backend/src/plugins/Utility/commands/VcmoveCmd.ts index 935fa0b1..bc0481fc 100644 --- a/backend/src/plugins/Utility/commands/VcmoveCmd.ts +++ b/backend/src/plugins/Utility/commands/VcmoveCmd.ts @@ -1,16 +1,9 @@ -import { Snowflake, VoiceChannel } from "discord.js"; -import { - channelToTemplateSafeChannel, - memberToTemplateSafeMember, - userToTemplateSafeUser, -} from "../../../utils/templateSafeObjects"; +import { ChannelType, Snowflake, VoiceChannel } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { LogType } from "../../../data/LogType"; import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; import { channelMentionRegex, isSnowflake, simpleClosestStringMatch } from "../../../utils"; -import { utilityCmd } from "../types"; -import { ChannelTypeStrings } from "../../../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { utilityCmd } from "../types"; export const VcmoveCmd = utilityCmd({ trigger: "vcmove", @@ -48,7 +41,7 @@ export const VcmoveCmd = utilityCmd({ } else { // Search string -> find closest matching voice channel name const voiceChannels = [...pluginData.guild.channels.cache.values()].filter( - (c): c is VoiceChannel => c.type === ChannelTypeStrings.VOICE, + (c): c is VoiceChannel => c.type === ChannelType.GuildVoice, ); const closestMatch = simpleClosestStringMatch(args.channel, voiceChannels, (ch) => ch.name); if (!closestMatch) { @@ -127,7 +120,7 @@ export const VcmoveAllCmd = utilityCmd({ } else { // Search string -> find closest matching voice channel name const voiceChannels = [...pluginData.guild.channels.cache.values()].filter( - (c): c is VoiceChannel => c.type === ChannelTypeStrings.VOICE, + (c): c is VoiceChannel => c.type === ChannelType.GuildVoice, ); const closestMatch = simpleClosestStringMatch(args.channel, voiceChannels, (ch) => ch.name); if (!closestMatch) { diff --git a/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts b/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts index 5e8e916a..d1c2dd0f 100644 --- a/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts @@ -1,8 +1,7 @@ -import { MessageEmbedOptions, Snowflake, StageChannel, ThreadChannel, VoiceChannel } from "discord.js"; +import { APIEmbed, ChannelType, Snowflake, StageChannel, VoiceChannel } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { ChannelTypeStrings } from "src/types"; import { EmbedWith, formatNumber, MINUTES, preEmbedPadding, trimLines, verboseUserMention } from "../../../utils"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { UtilityPluginType } from "../types"; @@ -17,14 +16,16 @@ const STAGE_CHANNEL_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/839930647711186995/stage-channel.png"; const PUBLIC_THREAD_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/870343055855738921/public-thread.png"; -const PRIVATE_THREAD_UCON = +const PRIVATE_THREAD_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/870343402447839242/private-thread.png"; +const FORUM_CHANNEL_ICON = + "https://cdn.discordapp.com/attachments/740650744830623756/1091681253364875294/forum-channel-icon.png"; export async function getChannelInfoEmbed( pluginData: GuildPluginData, channelId: string, requestMemberId?: string, -): Promise { +): Promise { const channel = pluginData.guild.channels.cache.get(channelId as Snowflake); if (!channel) { return null; @@ -36,25 +37,28 @@ export async function getChannelInfoEmbed( const icon = { - [ChannelTypeStrings.VOICE]: VOICE_CHANNEL_ICON, - [ChannelTypeStrings.NEWS]: ANNOUNCEMENT_CHANNEL_ICON, - [ChannelTypeStrings.STAGE]: STAGE_CHANNEL_ICON, - [ChannelTypeStrings.PUBLIC_THREAD]: PUBLIC_THREAD_ICON, - [ChannelTypeStrings.PRIVATE_THREAD]: PRIVATE_THREAD_UCON, - }[channel.type] || TEXT_CHANNEL_ICON; + [ChannelType.GuildVoice]: VOICE_CHANNEL_ICON, + [ChannelType.GuildAnnouncement]: ANNOUNCEMENT_CHANNEL_ICON, + [ChannelType.GuildStageVoice]: STAGE_CHANNEL_ICON, + [ChannelType.PublicThread]: PUBLIC_THREAD_ICON, + [ChannelType.PrivateThread]: PRIVATE_THREAD_ICON, + [ChannelType.AnnouncementThread]: PUBLIC_THREAD_ICON, + [ChannelType.GuildForum]: FORUM_CHANNEL_ICON, + }[channel.type] ?? TEXT_CHANNEL_ICON; const channelType = { - [ChannelTypeStrings.TEXT]: "Text channel", - [ChannelTypeStrings.VOICE]: "Voice channel", - [ChannelTypeStrings.CATEGORY]: "Category", - [ChannelTypeStrings.NEWS]: "Announcement channel", - [ChannelTypeStrings.STORE]: "Store channel", - [ChannelTypeStrings.STAGE]: "Stage channel", - [ChannelTypeStrings.PUBLIC_THREAD]: "Public Thread channel", - [ChannelTypeStrings.PRIVATE_THREAD]: "Private Thread channel", - [ChannelTypeStrings.NEWS_THREAD]: "News Thread channel", - }[channel.type] || "Channel"; + [ChannelType.GuildText]: "Text channel", + [ChannelType.GuildVoice]: "Voice channel", + [ChannelType.GuildCategory]: "Category channel", + [ChannelType.GuildAnnouncement]: "Announcement channel", + [ChannelType.GuildStageVoice]: "Stage channel", + [ChannelType.PublicThread]: "Public Thread channel", + [ChannelType.PrivateThread]: "Private Thread channel", + [ChannelType.AnnouncementThread]: "News Thread channel", + [ChannelType.GuildDirectory]: "Hub channel", + [ChannelType.GuildForum]: "Forum channel", + }[channel.type] ?? "Channel"; embed.author = { name: `${channelType}: ${channel.name}`, @@ -63,9 +67,9 @@ export async function getChannelInfoEmbed( let channelName = `#${channel.name}`; if ( - channel.type === ChannelTypeStrings.VOICE || - channel.type === ChannelTypeStrings.CATEGORY || - channel.type === ChannelTypeStrings.STAGE + channel.type === ChannelType.GuildVoice || + channel.type === ChannelType.GuildCategory || + channel.type === ChannelType.GuildStageVoice ) { channelName = channel.name; } @@ -81,7 +85,7 @@ export async function getChannelInfoEmbed( round: true, }); - const showMention = channel.type !== ChannelTypeStrings.CATEGORY; + const showMention = channel.type !== ChannelType.GuildCategory; embed.fields.push({ name: preEmbedPadding + "Channel information", @@ -94,11 +98,11 @@ export async function getChannelInfoEmbed( `), }); - if (channel.type === ChannelTypeStrings.VOICE || channel.type === ChannelTypeStrings.STAGE) { + if (channel.type === ChannelType.GuildVoice || channel.type === ChannelType.GuildStageVoice) { const voiceMembers = Array.from((channel as VoiceChannel | StageChannel).members.values()); const muted = voiceMembers.filter((vm) => vm.voice.mute || vm.voice.selfMute); const deafened = voiceMembers.filter((vm) => vm.voice.deaf || vm.voice.selfDeaf); - const voiceOrStage = channel.type === ChannelTypeStrings.VOICE ? "Voice" : "Stage"; + const voiceOrStage = channel.type === ChannelType.GuildVoice ? "Voice" : "Stage"; embed.fields.push({ name: preEmbedPadding + `${voiceOrStage} information`, @@ -110,13 +114,13 @@ export async function getChannelInfoEmbed( }); } - if (channel.type === ChannelTypeStrings.CATEGORY) { + if (channel.type === ChannelType.GuildCategory) { const textChannels = pluginData.guild.channels.cache.filter( - (ch) => ch.parentId === channel.id && ch.type !== ChannelTypeStrings.VOICE, + (ch) => ch.parentId === channel.id && ch.type !== ChannelType.GuildVoice, ); const voiceChannels = pluginData.guild.channels.cache.filter( (ch) => - ch.parentId === channel.id && (ch.type === ChannelTypeStrings.VOICE || ch.type === ChannelTypeStrings.STAGE), + ch.parentId === channel.id && (ch.type === ChannelType.GuildVoice || ch.type === ChannelType.GuildStageVoice), ); embed.fields.push({ @@ -128,14 +132,13 @@ export async function getChannelInfoEmbed( }); } - if (channel.type === ChannelTypeStrings.PRIVATE_THREAD || channel.type === ChannelTypeStrings.PUBLIC_THREAD) { - const thread = channel as ThreadChannel; - const parentChannelName = thread.parent?.name ?? `<#${thread.parentId}>`; - const memberCount = thread.memberCount ?? thread.members.cache.size; - const owner = await thread.fetchOwner().catch(() => null); + if (channel.isThread()) { + const parentChannelName = channel.parent?.name ?? `<#${channel.parentId}>`; + const memberCount = channel.memberCount ?? channel.members.cache.size; + const owner = await channel.fetchOwner().catch(() => null); const ownerMention = owner?.user ? verboseUserMention(owner.user) : "Unknown#0000"; const humanizedArchiveTime = `Archive duration: **${humanizeDuration( - (thread.autoArchiveDuration ?? 0) * MINUTES, + (channel.autoArchiveDuration ?? 0) * MINUTES, )}**`; embed.fields.push({ @@ -144,7 +147,7 @@ export async function getChannelInfoEmbed( Parent channel: **#${parentChannelName}** Member count: **${memberCount}** Thread creator: ${ownerMention} - ${thread.archived ? "Archived: **True**" : humanizedArchiveTime}`), + ${channel.archived ? "Archived: **True**" : humanizedArchiveTime}`), }); } diff --git a/backend/src/plugins/Utility/functions/getEmojiInfoEmbed.ts b/backend/src/plugins/Utility/functions/getEmojiInfoEmbed.ts index a97d5310..01001e2d 100644 --- a/backend/src/plugins/Utility/functions/getEmojiInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getEmojiInfoEmbed.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions } from "discord.js"; +import { APIEmbed } from "discord.js"; import { GuildPluginData } from "knub"; import { EmbedWith, preEmbedPadding, trimLines } from "../../../utils"; import { UtilityPluginType } from "../types"; @@ -6,22 +6,21 @@ import { UtilityPluginType } from "../types"; export async function getEmojiInfoEmbed( pluginData: GuildPluginData, emojiId: string, -): Promise { +): Promise { const emoji = pluginData.guild.emojis.cache.find((e) => e.id === emojiId); if (!emoji) { return null; } - const embed: EmbedWith<"fields"> = { + const embed: EmbedWith<"fields" | "author"> = { fields: [], + author: { + name: `Emoji: ${emoji.name}`, + icon_url: emoji.url, + }, }; - embed.author = { - name: `Emoji: ${emoji.name}`, - icon_url: `https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? "gif" : "png"}?v=1`, - }; - - embed.fields.push({ + embed.fields!.push({ name: preEmbedPadding + "Emoji information", value: trimLines(` Name: **${emoji.name}** diff --git a/backend/src/plugins/Utility/functions/getInviteInfoEmbed.ts b/backend/src/plugins/Utility/functions/getInviteInfoEmbed.ts index a0a987e7..1c8fb80c 100644 --- a/backend/src/plugins/Utility/functions/getInviteInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getInviteInfoEmbed.ts @@ -1,8 +1,6 @@ -import { MessageEmbedOptions } from "discord.js"; +import { APIEmbed, ChannelType } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; -import moment from "moment-timezone"; -import { ChannelTypeStrings } from "src/types"; import { EmbedWith, formatNumber, @@ -20,7 +18,7 @@ import { UtilityPluginType } from "../types"; export async function getInviteInfoEmbed( pluginData: GuildPluginData, inviteCode: string, -): Promise { +): Promise { let invite = await resolveInvite(pluginData.client, inviteCode, true); if (!invite) { return null; @@ -45,7 +43,6 @@ export async function getInviteInfoEmbed( } const serverCreatedAtTimestamp = snowflakeToTimestamp(invite.guild.id); - const serverCreatedAt = moment.utc(serverCreatedAtTimestamp, "x"); const serverAge = humanizeDuration(Date.now() - serverCreatedAtTimestamp, { largest: 2, round: true, @@ -67,10 +64,9 @@ export async function getInviteInfoEmbed( }); if (invite.channel) { const channelName = - invite.channel.type === ChannelTypeStrings.VOICE ? `🔉 ${invite.channel.name}` : `#${invite.channel.name}`; + invite.channel.type === ChannelType.GuildVoice ? `🔉 ${invite.channel.name}` : `#${invite.channel.name}`; const channelCreatedAtTimestamp = snowflakeToTimestamp(invite.channel.id); - const channelCreatedAt = moment.utc(channelCreatedAtTimestamp, "x"); const channelAge = humanizeDuration(Date.now() - channelCreatedAtTimestamp, { largest: 2, round: true, @@ -82,7 +78,7 @@ export async function getInviteInfoEmbed( Created: **${channelAge} ago** `); - if (invite.channel.type !== ChannelTypeStrings.VOICE) { + if (invite.channel.type !== ChannelType.GuildVoice) { channelInfo += `\nMention: <#${invite.channel.id}>`; } @@ -114,14 +110,15 @@ export async function getInviteInfoEmbed( invite = invite as GroupDMInvite; embed.author = { - name: invite.channel.name ? `Group DM invite: ${invite.channel.name}` : `Group DM invite`, + name: invite.channel!.name ? `Group DM invite: ${invite.channel!.name}` : `Group DM invite`, url: `https://discord.gg/${invite.code}`, }; // FIXME pending invite re-think /*if (invite.channel.icon) { embed.author.icon_url = `https://cdn.discordapp.com/channel-icons/${invite.channel.id}/${invite.channel.icon}.png?size=256`; - }*/ const channelCreatedAtTimestamp = snowflakeToTimestamp(invite.channel.id); - const channelCreatedAt = moment.utc(channelCreatedAtTimestamp, "x"); + }*/ + + const channelCreatedAtTimestamp = snowflakeToTimestamp(invite.channel!.id); const channelAge = humanizeDuration(Date.now() - channelCreatedAtTimestamp, { largest: 2, round: true, @@ -130,8 +127,8 @@ export async function getInviteInfoEmbed( embed.fields.push({ name: preEmbedPadding + "Group DM information", value: trimLines(` - Name: ${invite.channel.name ? `**${invite.channel.name}**` : `_Unknown_`} - ID: \`${invite.channel.id}\` + Name: ${invite.channel!.name ? `**${invite.channel!.name}**` : `_Unknown_`} + ID: \`${invite.channel!.id}\` Created: **${channelAge} ago** Members: **${formatNumber((invite as any).memberCount)}** `), diff --git a/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts b/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts index 9d3c6d68..2e97999c 100644 --- a/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts @@ -1,9 +1,7 @@ -import { MessageEmbedOptions, Snowflake, TextChannel } from "discord.js"; +import { APIEmbed, MessageType, Snowflake, TextChannel } from "discord.js"; import humanizeDuration from "humanize-duration"; -import { GuildPluginData } from "knub"; -import { getDefaultPrefix } from "knub/dist/commands/commandUtils"; +import { getDefaultMessageCommandPrefix, GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { MessageTypeStrings } from "src/types"; import { chunkMessageLines, EmbedWith, messageLink, preEmbedPadding, trimEmptyLines, trimLines } from "../../../utils"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { UtilityPluginType } from "../types"; @@ -15,7 +13,7 @@ export async function getMessageInfoEmbed( channelId: string, messageId: string, requestMemberId?: string, -): Promise { +): Promise { const message = await (pluginData.guild.channels.resolve(channelId as Snowflake) as TextChannel).messages .fetch(messageId as Snowflake) .catch(() => null); @@ -25,13 +23,12 @@ export async function getMessageInfoEmbed( const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin); - const embed: EmbedWith<"fields"> = { + const embed: EmbedWith<"fields" | "author"> = { fields: [], - }; - - embed.author = { - name: `Message: ${message.id}`, - icon_url: MESSAGE_ICON, + author: { + name: `Message: ${message.id}`, + icon_url: MESSAGE_ICON, + }, }; const createdAt = moment.utc(message.createdAt, "x"); @@ -58,17 +55,17 @@ export async function getMessageInfoEmbed( const type = { - [MessageTypeStrings.DEFAULT]: "Regular message", - [MessageTypeStrings.PINS_ADD]: "System message", - [MessageTypeStrings.GUILD_MEMBER_JOIN]: "System message", - [MessageTypeStrings.USER_PREMIUM_GUILD_SUBSCRIPTION]: "System message", - [MessageTypeStrings.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1]: "System message", - [MessageTypeStrings.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2]: "System message", - [MessageTypeStrings.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3]: "System message", - [MessageTypeStrings.CHANNEL_FOLLOW_ADD]: "System message", - [MessageTypeStrings.GUILD_DISCOVERY_DISQUALIFIED]: "System message", - [MessageTypeStrings.GUILD_DISCOVERY_REQUALIFIED]: "System message", - }[message.type] || "Unknown"; + [MessageType.Default]: "Regular message", + [MessageType.ChannelPinnedMessage]: "System message", + [MessageType.UserJoin]: "System message", + [MessageType.GuildBoost]: "System message", + [MessageType.GuildBoostTier1]: "System message", + [MessageType.GuildBoostTier2]: "System message", + [MessageType.GuildBoostTier3]: "System message", + [MessageType.ChannelFollowAdd]: "System message", + [MessageType.GuildDiscoveryDisqualified]: "System message", + [MessageType.GuildDiscoveryRequalified]: "System message", + }[message.type] ?? "Unknown"; embed.fields.push({ name: preEmbedPadding + "Message information", @@ -138,7 +135,7 @@ export async function getMessageInfoEmbed( } if (message.embeds.length) { - const prefix = pluginData.fullConfig.prefix || getDefaultPrefix(pluginData.client); + const prefix = pluginData.fullConfig.prefix || getDefaultMessageCommandPrefix(pluginData.client); embed.fields.push({ name: preEmbedPadding + "Embeds", value: `Message contains an embed, use \`${prefix}source\` to see the embed source`, diff --git a/backend/src/plugins/Utility/functions/getRoleInfoEmbed.ts b/backend/src/plugins/Utility/functions/getRoleInfoEmbed.ts index f58f61be..5acd8c72 100644 --- a/backend/src/plugins/Utility/functions/getRoleInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getRoleInfoEmbed.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions, Permissions, Role } from "discord.js"; +import { APIEmbed, PermissionFlagsBits, Role } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; @@ -13,18 +13,16 @@ export async function getRoleInfoEmbed( pluginData: GuildPluginData, role: Role, requestMemberId?: string, -): Promise { - const embed: EmbedWith<"fields"> = { +): Promise { + const embed: EmbedWith<"fields" | "author" | "color"> = { fields: [], + author: { + name: `Role: ${role.name}`, + icon_url: MENTION_ICON, + }, + color: role.color, }; - embed.author = { - name: `Role: ${role.name}`, - icon_url: MENTION_ICON, - }; - - embed.color = role.color; - const createdAt = moment.utc(role.createdAt, "x"); const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin); const tzCreatedAt = requestMemberId @@ -36,8 +34,8 @@ export async function getRoleInfoEmbed( round: true, }); - const rolePerms = role.permissions.has(Permissions.FLAGS.ADMINISTRATOR) - ? [PERMISSION_NAMES.ADMINISTRATOR] + const rolePerms = role.permissions.has(PermissionFlagsBits.Administrator) + ? [PERMISSION_NAMES.Administrator] : role.permissions.toArray().map((p) => PERMISSION_NAMES[p]); // -1 because of the @everyone role diff --git a/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts b/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts index 03d7212d..c6b7bbd3 100644 --- a/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts @@ -1,8 +1,7 @@ -import { MessageEmbedOptions, PremiumTier, Snowflake } from "discord.js"; +import { APIEmbed, ChannelType, GuildPremiumTier, Snowflake } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; -import { ChannelTypeStrings } from "../../../types"; import { EmbedWith, formatNumber, @@ -19,18 +18,11 @@ import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { UtilityPluginType } from "../types"; import { getGuildPreview } from "./getGuildPreview"; -const PremiumTiers: Record = { - NONE: 0, - TIER_1: 1, - TIER_2: 2, - TIER_3: 3, -}; - export async function getServerInfoEmbed( pluginData: GuildPluginData, serverId: string, requestMemberId?: string, -): Promise { +): Promise { const thisServer = serverId === pluginData.guild.id ? pluginData.guild : null; const [restGuild, guildPreview] = await Promise.all([ thisServer @@ -54,7 +46,7 @@ export async function getServerInfoEmbed( embed.author = { name: `Server: ${(guildPreview || restGuild)!.name}`, - iconURL: (guildPreview || restGuild)!.iconURL() ?? undefined, + icon_url: (guildPreview || restGuild)!.iconURL() ?? undefined, }; // BASIC INFORMATION @@ -90,10 +82,10 @@ export async function getServerInfoEmbed( }); // IMAGE LINKS - const iconUrl = `[Link](${(restGuild || guildPreview)!.iconURL({ dynamic: true, format: "png", size: 2048 })})`; - const bannerUrl = restGuild?.banner ? `[Link](${restGuild.bannerURL({ format: "png", size: 2048 })})` : "None"; + const iconUrl = `[Link](${(restGuild || guildPreview)!.iconURL({ size: 2048 })})`; + const bannerUrl = restGuild?.banner ? `[Link](${restGuild.bannerURL({ size: 2048 })})` : "None"; const splashUrl = (restGuild || guildPreview)!.splash - ? `[Link](${(restGuild || guildPreview)!.splashURL({ format: "png", size: 2048 })})` + ? `[Link](${(restGuild || guildPreview)!.splashURL({ size: 2048 })})` : "None"; embed.fields.push( @@ -162,9 +154,14 @@ export async function getServerInfoEmbed( // CHANNEL COUNTS if (thisServer) { const totalChannels = thisServer.channels.cache.size; - const categories = thisServer.channels.cache.filter((channel) => channel.type === ChannelTypeStrings.CATEGORY); - const textChannels = thisServer.channels.cache.filter((channel) => channel.type === ChannelTypeStrings.TEXT); - const voiceChannels = thisServer.channels.cache.filter((channel) => channel.type === ChannelTypeStrings.VOICE); + const categories = thisServer.channels.cache.filter((channel) => channel.type === ChannelType.GuildCategory); + const textChannels = thisServer.channels.cache.filter((channel) => channel.type === ChannelType.GuildText); + const voiceChannels = thisServer.channels.cache.filter((channel) => channel.type === ChannelType.GuildVoice); + const threadChannels = thisServer.channels.cache.filter((channel) => channel.isThread()); + const announcementChannels = thisServer.channels.cache.filter( + (channel) => channel.type === ChannelType.GuildAnnouncement, + ); + const stageChannels = thisServer.channels.cache.filter((channel) => channel.type === ChannelType.GuildStageVoice); embed.fields.push({ name: preEmbedPadding + "Channels", @@ -172,8 +169,10 @@ export async function getServerInfoEmbed( value: trimLines(` Total: **${totalChannels}** / 500 Categories: **${categories.size}** - Text: **${textChannels.size}** + Text: **${textChannels.size}** (**${threadChannels.size} threads**) + Announcement: **${announcementChannels.size}** Voice: **${voiceChannels.size}** + Stage: **${stageChannels.size}** `), }); } @@ -186,22 +185,20 @@ export async function getServerInfoEmbed( } if (restGuild) { - const premiumTierValue = PremiumTiers[restGuild.premiumTier]; - const maxEmojis = { - 0: 50, - 1: 100, - 2: 150, - 3: 250, - }[premiumTierValue] ?? 50; + [GuildPremiumTier.None]: 50, + [GuildPremiumTier.Tier1]: 100, + [GuildPremiumTier.Tier2]: 150, + [GuildPremiumTier.Tier3]: 250, + }[restGuild.premiumTier] ?? 50; const maxStickers = { - 0: 0, - 1: 15, - 2: 30, - 3: 60, - }[premiumTierValue] ?? 0; + [GuildPremiumTier.None]: 0, + [GuildPremiumTier.Tier1]: 15, + [GuildPremiumTier.Tier2]: 30, + [GuildPremiumTier.Tier3]: 60, + }[restGuild.premiumTier] ?? 0; otherStats.push(`Emojis: **${restGuild.emojis.cache.size}** / ${maxEmojis * 2}`); otherStats.push(`Stickers: **${restGuild.stickers.cache.size}** / ${maxStickers}`); @@ -211,9 +208,7 @@ export async function getServerInfoEmbed( } if (thisServer) { - otherStats.push( - `Boosts: **${thisServer.premiumSubscriptionCount ?? 0}** (level ${PremiumTiers[thisServer.premiumTier]})`, - ); + otherStats.push(`Boosts: **${thisServer.premiumSubscriptionCount ?? 0}** (level ${thisServer.premiumTier})`); } embed.fields.push({ diff --git a/backend/src/plugins/Utility/functions/getSnowflakeInfoEmbed.ts b/backend/src/plugins/Utility/functions/getSnowflakeInfoEmbed.ts index 3ef1777f..901df722 100644 --- a/backend/src/plugins/Utility/functions/getSnowflakeInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getSnowflakeInfoEmbed.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions } from "discord.js"; +import { APIEmbed } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; @@ -14,14 +14,13 @@ export async function getSnowflakeInfoEmbed( snowflake: string, showUnknownWarning = false, requestMemberId?: string, -): Promise { - const embed: EmbedWith<"fields"> = { +): Promise { + const embed: EmbedWith<"fields" | "author"> = { fields: [], - }; - - embed.author = { - name: `Snowflake: ${snowflake}`, - icon_url: SNOWFLAKE_ICON, + author: { + name: `Snowflake: ${snowflake}`, + icon_url: SNOWFLAKE_ICON, + }, }; if (showUnknownWarning) { diff --git a/backend/src/plugins/Utility/functions/getUserInfoEmbed.ts b/backend/src/plugins/Utility/functions/getUserInfoEmbed.ts index c4922bed..4b9e2724 100644 --- a/backend/src/plugins/Utility/functions/getUserInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getUserInfoEmbed.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions, Role } from "discord.js"; +import { APIEmbed } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; @@ -28,7 +28,7 @@ export async function getUserInfoEmbed( userId: string, compact = false, requestMemberId?: string, -): Promise { +): Promise { const user = await resolveUser(pluginData.client, userId); if (!user || user instanceof UnknownUser) { return null; diff --git a/backend/src/plugins/Utility/search.ts b/backend/src/plugins/Utility/search.ts index 440652b5..4340ad23 100644 --- a/backend/src/plugins/Utility/search.ts +++ b/backend/src/plugins/Utility/search.ts @@ -1,17 +1,16 @@ import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, GuildMember, Message, - MessageActionRow, - MessageButton, MessageComponentInteraction, - Permissions, + PermissionsBitField, Snowflake, - TextChannel, User, } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; -import { GuildPluginData } from "knub"; -import { ArgsFromSignatureOrArray } from "knub/dist/commands/commandUtils"; +import { ArgsFromSignatureOrArray, GuildPluginData } from "knub"; import moment from "moment-timezone"; import { getBaseUrl, sendErrorMessage } from "../../pluginUtils"; import { allowTimeout, RegExpRunner } from "../../RegExpRunner"; @@ -116,12 +115,12 @@ export async function displaySearch( } } catch (e) { if (e instanceof SearchError) { - sendErrorMessage(pluginData, msg.channel as TextChannel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); return; } if (e instanceof InvalidRegexError) { - sendErrorMessage(pluginData, msg.channel as TextChannel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); return; } @@ -129,7 +128,7 @@ export async function displaySearch( } if (searchResult.totalResults === 0) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "No results found"); + sendErrorMessage(pluginData, msg.channel, "No results found"); return; } @@ -169,23 +168,21 @@ export async function displaySearch( // Set up pagination reactions if needed. The reactions are cleared after a timeout. if (searchResult.totalResults > perPage) { const idMod = `${searchMsg.id}:${moment.utc().valueOf()}`; - const buttons: MessageButton[] = []; - - buttons.push( - new MessageButton() - .setStyle("SECONDARY") + const buttons: ButtonBuilder[] = [ + new ButtonBuilder() + .setStyle(ButtonStyle.Secondary) .setEmoji("⬅") .setCustomId(`previousButton:${idMod}`) .setDisabled(currentPage === 1), - new MessageButton() - .setStyle("SECONDARY") + new ButtonBuilder() + .setStyle(ButtonStyle.Secondary) .setEmoji("➡") .setCustomId(`nextButton:${idMod}`) .setDisabled(currentPage === searchResult.lastPage), - new MessageButton().setStyle("SECONDARY").setEmoji("🔄").setCustomId(`reloadButton:${idMod}`), - ); + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setEmoji("🔄").setCustomId(`reloadButton:${idMod}`), + ]; - const row = new MessageActionRow().addComponents(buttons); + const row = new ActionRowBuilder().addComponents(buttons); await searchMsg.edit({ content: result, components: [row] }); const collector = searchMsg.createMessageComponentCollector({ time: 2 * MINUTES }); @@ -194,6 +191,7 @@ export async function displaySearch( if (msg.author.id !== interaction.user.id) { interaction .reply({ content: `You are not permitted to use these buttons.`, ephemeral: true }) + // tslint:disable-next-line no-console .catch((err) => console.trace(err.message)); } else { if (interaction.customId === `previousButton:${idMod}` && currentPage > 1) { @@ -261,12 +259,12 @@ export async function archiveSearch( } } catch (e) { if (e instanceof SearchError) { - sendErrorMessage(pluginData, msg.channel as TextChannel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); return; } if (e instanceof InvalidRegexError) { - sendErrorMessage(pluginData, msg.channel as TextChannel, e.message); + sendErrorMessage(pluginData, msg.channel, e.message); return; } @@ -274,7 +272,7 @@ export async function archiveSearch( } if (results.totalResults === 0) { - sendErrorMessage(pluginData, msg.channel as TextChannel, "No results found"); + sendErrorMessage(pluginData, msg.channel, "No results found"); return; } @@ -430,7 +428,7 @@ async function performBanSearch( perPage = SEARCH_RESULTS_PER_PAGE, ): Promise<{ results: User[]; totalResults: number; page: number; lastPage: number; from: number; to: number }> { const member = pluginData.guild.members.cache.get(pluginData.client.user!.id); - if (member && !hasDiscordPermissions(member.permissions, Permissions.FLAGS.BAN_MEMBERS)) { + if (member && !hasDiscordPermissions(member.permissions, PermissionsBitField.Flags.BanMembers)) { throw new SearchError(`Unable to search bans: missing "Ban Members" permission`); } diff --git a/backend/src/plugins/Utility/types.ts b/backend/src/plugins/Utility/types.ts index 15f6e925..134c0f48 100644 --- a/backend/src/plugins/Utility/types.ts +++ b/backend/src/plugins/Utility/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand } from "knub"; import { GuildArchives } from "../../data/GuildArchives"; import { GuildCases } from "../../data/GuildCases"; import { GuildLogs } from "../../data/GuildLogs"; @@ -52,5 +52,5 @@ export interface UtilityPluginType extends BasePluginType { }; } -export const utilityCmd = typedGuildCommand(); -export const utilityEvt = typedGuildEventListener(); +export const utilityCmd = guildPluginMessageCommand(); +export const utilityEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/WelcomeMessage/WelcomeMessagePlugin.ts b/backend/src/plugins/WelcomeMessage/WelcomeMessagePlugin.ts index 1bbcdb45..e007b968 100644 --- a/backend/src/plugins/WelcomeMessage/WelcomeMessagePlugin.ts +++ b/backend/src/plugins/WelcomeMessage/WelcomeMessagePlugin.ts @@ -1,9 +1,10 @@ import { PluginOptions } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; +import { makeIoTsConfigParser } from "../../pluginUtils"; +import { LogsPlugin } from "../Logs/LogsPlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; import { SendWelcomeMessageEvt } from "./events/SendWelcomeMessageEvt"; import { ConfigSchema, WelcomeMessagePluginType } from "./types"; -import { LogsPlugin } from "../Logs/LogsPlugin"; const defaultOptions: PluginOptions = { config: { @@ -18,10 +19,11 @@ export const WelcomeMessagePlugin = zeppelinGuildPlugin [LogsPlugin], + configParser: makeIoTsConfigParser(ConfigSchema), defaultOptions, // prettier-ignore diff --git a/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts b/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts index 772f27a9..fac26322 100644 --- a/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts +++ b/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts @@ -1,16 +1,14 @@ import { Snowflake, TextChannel } from "discord.js"; +import { renderTemplate, TemplateParseError, TemplateSafeValueContainer } from "../../../templateFormatter"; +import { createChunkedMessage, verboseChannelMention, verboseUserMention } from "../../../utils"; +import { sendDM } from "../../../utils/sendDM"; import { - channelToTemplateSafeChannel, guildToTemplateSafeGuild, memberToTemplateSafeMember, userToTemplateSafeUser, } from "../../../utils/templateSafeObjects"; -import { LogType } from "../../../data/LogType"; -import { renderTemplate, TemplateParseError, TemplateSafeValueContainer } from "../../../templateFormatter"; -import { createChunkedMessage, stripObjectToScalars, verboseChannelMention, verboseUserMention } from "../../../utils"; -import { sendDM } from "../../../utils/sendDM"; -import { welcomeMessageEvt } from "../types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { welcomeMessageEvt } from "../types"; export const SendWelcomeMessageEvt = welcomeMessageEvt({ event: "guildMemberAdd", diff --git a/backend/src/plugins/WelcomeMessage/types.ts b/backend/src/plugins/WelcomeMessage/types.ts index c4a995da..682cfe39 100644 --- a/backend/src/plugins/WelcomeMessage/types.ts +++ b/backend/src/plugins/WelcomeMessage/types.ts @@ -1,5 +1,5 @@ import * as t from "io-ts"; -import { BasePluginType, typedGuildEventListener } from "knub"; +import { BasePluginType, guildPluginEventListener } from "knub"; import { GuildLogs } from "../../data/GuildLogs"; import { tNullable } from "../../utils"; @@ -18,4 +18,4 @@ export interface WelcomeMessagePluginType extends BasePluginType { }; } -export const welcomeMessageEvt = typedGuildEventListener(); +export const welcomeMessageEvt = guildPluginEventListener(); diff --git a/backend/src/plugins/ZeppelinPluginBlueprint.ts b/backend/src/plugins/ZeppelinPluginBlueprint.ts index bb8cc70d..165c67b5 100644 --- a/backend/src/plugins/ZeppelinPluginBlueprint.ts +++ b/backend/src/plugins/ZeppelinPluginBlueprint.ts @@ -1,16 +1,13 @@ import * as t from "io-ts"; import { BasePluginType, + globalPlugin, GlobalPluginBlueprint, GlobalPluginData, + guildPlugin, GuildPluginBlueprint, GuildPluginData, - typedGlobalPlugin, - typedGuildPlugin, } from "knub"; -import { PluginOptions } from "knub/dist/config/configTypes"; -import { Awaitable } from "knub/dist/utils"; -import { getPluginConfigPreprocessor } from "../pluginUtils"; import { TMarkdown } from "../types"; /** @@ -19,41 +16,30 @@ import { TMarkdown } from "../types"; export interface ZeppelinGuildPluginBlueprint = GuildPluginData> extends GuildPluginBlueprint { - configSchema: t.TypeC; showInDocs?: boolean; - info?: { prettyName: string; description?: TMarkdown; usageGuide?: TMarkdown; configurationGuide?: TMarkdown; legacy?: boolean | string; + configSchema?: t.Type; }; - - configPreprocessor?: ( - options: PluginOptions, - strict?: boolean, - ) => Awaitable>; } -export function zeppelinGuildPlugin( - blueprint: TBlueprint, -): TBlueprint & { configPreprocessor: ZeppelinGuildPluginBlueprint["configPreprocessor"] }; +export function zeppelinGuildPlugin(blueprint: TBlueprint): TBlueprint; export function zeppelinGuildPlugin(): < TBlueprint extends ZeppelinGuildPluginBlueprint>, >( blueprint: TBlueprint, -) => TBlueprint & { - configPreprocessor: ZeppelinGuildPluginBlueprint>["configPreprocessor"]; -}; +) => TBlueprint; export function zeppelinGuildPlugin(...args) { if (args.length) { - const blueprint = typedGuildPlugin( - ...(args as Parameters), + const blueprint = guildPlugin( + ...(args as Parameters), ) as unknown as ZeppelinGuildPluginBlueprint; - blueprint.configPreprocessor = getPluginConfigPreprocessor(blueprint, blueprint.configPreprocessor); return blueprint; } else { return zeppelinGuildPlugin as (name, blueprint) => ZeppelinGuildPluginBlueprint; @@ -65,30 +51,23 @@ export function zeppelinGuildPlugin(...args) { */ export interface ZeppelinGlobalPluginBlueprint - extends GlobalPluginBlueprint> { - configSchema: t.TypeC; - configPreprocessor?: (options: PluginOptions, strict?: boolean) => Awaitable>; -} + extends GlobalPluginBlueprint> {} export function zeppelinGlobalPlugin( blueprint: TBlueprint, -): TBlueprint & { configPreprocessor: ZeppelinGlobalPluginBlueprint["configPreprocessor"] }; +): TBlueprint; export function zeppelinGlobalPlugin(): < TBlueprint extends ZeppelinGlobalPluginBlueprint, >( blueprint: TBlueprint, -) => TBlueprint & { - configPreprocessor: ZeppelinGlobalPluginBlueprint["configPreprocessor"]; -}; +) => TBlueprint; export function zeppelinGlobalPlugin(...args) { if (args.length) { - const blueprint = typedGlobalPlugin( - ...(args as Parameters), + const blueprint = globalPlugin( + ...(args as Parameters), ) as unknown as ZeppelinGlobalPluginBlueprint; - // @ts-ignore FIXME: Check the types here - blueprint.configPreprocessor = getPluginConfigPreprocessor(blueprint, blueprint.configPreprocessor); return blueprint; } else { return zeppelinGlobalPlugin as (name, blueprint) => ZeppelinGlobalPluginBlueprint; diff --git a/backend/src/plugins/availablePlugins.ts b/backend/src/plugins/availablePlugins.ts index d47df12f..ab8bfc1e 100644 --- a/backend/src/plugins/availablePlugins.ts +++ b/backend/src/plugins/availablePlugins.ts @@ -12,6 +12,7 @@ import { CustomEventsPlugin } from "./CustomEvents/CustomEventsPlugin"; import { GuildAccessMonitorPlugin } from "./GuildAccessMonitor/GuildAccessMonitorPlugin"; import { GuildConfigReloaderPlugin } from "./GuildConfigReloader/GuildConfigReloaderPlugin"; import { GuildInfoSaverPlugin } from "./GuildInfoSaver/GuildInfoSaverPlugin"; +import { InternalPosterPlugin } from "./InternalPoster/InternalPosterPlugin"; import { LocateUserPlugin } from "./LocateUser/LocateUserPlugin"; import { LogsPlugin } from "./Logs/LogsPlugin"; import { MessageSaverPlugin } from "./MessageSaver/MessageSaverPlugin"; @@ -19,10 +20,13 @@ import { ModActionsPlugin } from "./ModActions/ModActionsPlugin"; import { MutesPlugin } from "./Mutes/MutesPlugin"; import { NameHistoryPlugin } from "./NameHistory/NameHistoryPlugin"; import { PersistPlugin } from "./Persist/PersistPlugin"; +import { PhishermanPlugin } from "./Phisherman/PhishermanPlugin"; import { PingableRolesPlugin } from "./PingableRoles/PingableRolesPlugin"; import { PostPlugin } from "./Post/PostPlugin"; import { ReactionRolesPlugin } from "./ReactionRoles/ReactionRolesPlugin"; import { RemindersPlugin } from "./Reminders/RemindersPlugin"; +import { RoleButtonsPlugin } from "./RoleButtons/RoleButtonsPlugin"; +import { RoleManagerPlugin } from "./RoleManager/RoleManagerPlugin"; import { RolesPlugin } from "./Roles/RolesPlugin"; import { SelfGrantableRolesPlugin } from "./SelfGrantableRoles/SelfGrantableRolesPlugin"; import { SlowmodePlugin } from "./Slowmode/SlowmodePlugin"; @@ -34,10 +38,6 @@ import { UsernameSaverPlugin } from "./UsernameSaver/UsernameSaverPlugin"; import { UtilityPlugin } from "./Utility/UtilityPlugin"; import { WelcomeMessagePlugin } from "./WelcomeMessage/WelcomeMessagePlugin"; import { ZeppelinGlobalPluginBlueprint, ZeppelinGuildPluginBlueprint } from "./ZeppelinPluginBlueprint"; -import { PhishermanPlugin } from "./Phisherman/PhishermanPlugin"; -import { InternalPosterPlugin } from "./InternalPoster/InternalPosterPlugin"; -import { RoleManagerPlugin } from "./RoleManager/RoleManagerPlugin"; -import { RoleButtonsPlugin } from "./RoleButtons/RoleButtonsPlugin"; // prettier-ignore export const guildPlugins: Array> = [ diff --git a/backend/src/profiler.ts b/backend/src/profiler.ts index b9d0222f..e0b03f20 100644 --- a/backend/src/profiler.ts +++ b/backend/src/profiler.ts @@ -1,5 +1,6 @@ -import { Profiler } from "knub/dist/Profiler"; +import type { Knub } from "knub"; +type Profiler = Knub["profiler"]; let profiler: Profiler | null = null; export function getProfiler() { diff --git a/backend/src/types.ts b/backend/src/types.ts index 6f24a7fd..45974aee 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -1,7 +1,7 @@ import * as t from "io-ts"; import { BaseConfig, Knub } from "knub"; -export interface ZeppelinGuildConfig extends BaseConfig { +export interface ZeppelinGuildConfig extends BaseConfig { success_emoji?: string; error_emoji?: string; @@ -26,7 +26,7 @@ export const ZeppelinGuildConfigSchema = t.type({ }); export const PartialZeppelinGuildConfigSchema = t.partial(ZeppelinGuildConfigSchema.props); -export interface ZeppelinGlobalConfig extends BaseConfig { +export interface ZeppelinGlobalConfig extends BaseConfig { url: string; owners?: string[]; } @@ -37,7 +37,7 @@ export const ZeppelinGlobalConfigSchema = t.type({ plugins: t.record(t.string, t.unknown), }); -export type TZeppelinKnub = Knub; +export type TZeppelinKnub = Knub; /** * Wrapper for the string type that indicates the text will be parsed as Markdown later @@ -64,40 +64,3 @@ export interface CommandInfo { [key: string]: TMarkdown; }; } - -export enum ChannelTypeStrings { - TEXT = "GUILD_TEXT", - DM = "DM", - VOICE = "GUILD_VOICE", - GROUP = "GROUP_DM", - CATEGORY = "GUILD_CATEGORY", - NEWS = "GUILD_NEWS", - STORE = "GUILD_STORE", - NEWS_THREAD = "GUILD_NEWS_THREAD", - PUBLIC_THREAD = "GUILD_PUBLIC_THREAD", - PRIVATE_THREAD = "GUILD_PRIVATE_THREAD", - STAGE = "GUILD_STAGE_VOICE", - UNKNOWN = "UNKNOWN", -} - -export enum MessageTypeStrings { - "DEFAULT", - "RECIPIENT_ADD", - "RECIPIENT_REMOVE", - "CALL", - "CHANNEL_NAME_CHANGE", - "CHANNEL_ICON_CHANGE", - "PINS_ADD", - "GUILD_MEMBER_JOIN", - "USER_PREMIUM_GUILD_SUBSCRIPTION", - "USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1", - "USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2", - "USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3", - "CHANNEL_FOLLOW_ADD", - "GUILD_DISCOVERY_DISQUALIFIED", - "GUILD_DISCOVERY_REQUALIFIED", - "GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING", - "GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING", - "REPLY", - "APPLICATION_COMMAND", -} diff --git a/backend/src/utils.ts b/backend/src/utils.ts index e90c38e0..396493d4 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -1,11 +1,13 @@ import { + APIEmbed, + ChannelType, Client, - Constants, DiscordAPIError, + EmbedData, + EmbedType, Emoji, + escapeCodeBlock, Guild, - GuildAuditLogs, - GuildAuditLogsEntry, GuildBasedChannel, GuildChannel, GuildMember, @@ -14,19 +16,14 @@ import { InviteGuild, LimitedCollection, Message, - MessageAttachment, - MessageEmbed, - MessageEmbedOptions, + MessageCreateOptions, MessageMentionOptions, - MessageOptions, PartialChannelData, PartialMessage, Snowflake, Sticker, - TextChannel, - ThreadChannel, + TextBasedChannel, User, - Util, } from "discord.js"; import emojiRegex from "emoji-regex"; import { either } from "fp-ts/lib/Either"; @@ -37,18 +34,17 @@ import humanizeDuration from "humanize-duration"; import * as t from "io-ts"; import { isEqual } from "lodash"; import moment from "moment-timezone"; +import { performance } from "perf_hooks"; import tlds from "tlds"; import tmp from "tmp"; import { URL } from "url"; +import { z, ZodError } from "zod"; import { ISavedMessageAttachmentData, SavedMessage } from "./data/entities/SavedMessage"; +import { getProfiler } from "./profiler"; import { SimpleCache } from "./SimpleCache"; -import { ChannelTypeStrings } from "./types"; import { sendDM } from "./utils/sendDM"; import { waitForButtonConfirm } from "./utils/waitForInteraction"; import { decodeAndValidateStrict, StrictValidationError } from "./validatorUtils"; -import { z, ZodError } from "zod"; -import { getProfiler } from "./profiler"; -import { performance } from "perf_hooks"; const fsp = fs.promises; @@ -179,7 +175,7 @@ function tDeepPartialProp(prop: any) { } } -export function getScalarDifference( +export function getScalarDifference( base: T, object: T, ignoreKeys: string[] = [], @@ -258,7 +254,7 @@ export function nonNullish(v: V): v is NonNullable { export type GuildInvite = Invite & { guild: InviteGuild | Guild }; export type GroupDMInvite = Invite & { channel: PartialChannelData; - type: typeof Constants.ChannelTypes.GROUP_DM; + type: typeof ChannelType.GroupDM; }; /** @@ -392,8 +388,7 @@ export const zEmbedInput = z.object({ .nullable(), }); -export type EmbedWith = MessageEmbedOptions & - Pick, T>; +export type EmbedWith = APIEmbed & Pick, T>; export const zStrictMessageContent = z.object({ content: z.string().optional(), @@ -406,7 +401,7 @@ export type ZStrictMessageContent = z.infer; export type StrictMessageContent = { content?: string; tts?: boolean; - embeds?: MessageEmbedOptions[]; + embeds?: APIEmbed[]; }; export const tStrictMessageContent = t.type({ @@ -573,7 +568,7 @@ export function convertMSToDelayString(ms: number): string { return result; } -export function successMessage(str, emoji = "<:zep_check:650361014180904971>") { +export function successMessage(str: string, emoji = "<:zep_check:650361014180904971>") { return emoji ? `${emoji} ${str}` : str; } @@ -648,7 +643,7 @@ interface MatchedURL extends URL { } export function getUrlsInString(str: string, onlyUnique = false): MatchedURL[] { - let matches = str.match(urlRegex) || []; + let matches = [...(str.match(urlRegex) ?? [])]; if (onlyUnique) { matches = unique(matches); } @@ -953,7 +948,7 @@ export function chunkMessageLines(str: string, maxChunkLength = 1990): string[] } export async function createChunkedMessage( - channel: TextChannel | ThreadChannel | User, + channel: TextBasedChannel | User, messageText: string, allowedMentions?: MessageMentionOptions, ) { @@ -1403,17 +1398,21 @@ export async function resolveStickerId(bot: Client, id: Snowflake): Promise { +export async function confirm( + channel: GuildTextBasedChannel, + userId: string, + content: MessageCreateOptions, +): Promise { return waitForButtonConfirm(channel, content, { restrictToId: userId }); } export function messageSummary(msg: SavedMessage) { // Regular text content - let result = "```\n" + (msg.data.content ? Util.escapeCodeBlock(msg.data.content) : "") + "```"; + let result = "```\n" + (msg.data.content ? escapeCodeBlock(msg.data.content) : "") + "```"; // Rich embed - const richEmbed = (msg.data.embeds || []).find((e) => (e as MessageEmbed).type === "rich"); - if (richEmbed) result += "Embed:```" + Util.escapeCodeBlock(JSON.stringify(richEmbed)) + "```"; + const richEmbed = (msg.data.embeds || []).find((e) => (e as EmbedData).type === EmbedType.Rich); + if (richEmbed) result += "Embed:```" + escapeCodeBlock(JSON.stringify(richEmbed)) + "```"; // Attachments if (msg.data.attachments && msg.data.attachments.length) { @@ -1442,9 +1441,9 @@ export function verboseUserName(user: User | UnknownUser): string { return `**${user.tag}** (\`${user.id}\`)`; } -export function verboseChannelMention(channel: GuildChannel | ThreadChannel): string { +export function verboseChannelMention(channel: GuildBasedChannel): string { const plainTextName = - channel.type === ChannelTypeStrings.VOICE || channel.type === ChannelTypeStrings.STAGE + channel.type === ChannelType.GuildVoice || channel.type === ChannelType.GuildStageVoice ? channel.name : `#${channel.name}`; return `<#${channel.id}> (**${plainTextName}**, \`${channel.id}\`)`; @@ -1578,7 +1577,7 @@ export function isGuildInvite(invite: Invite): invite is GuildInvite { } export function isGroupDMInvite(invite: Invite): invite is GroupDMInvite { - return invite.guild == null && invite.channel?.type === ChannelTypeStrings.GROUP; + return invite.guild == null && invite.channel?.type === ChannelType.GroupDM; } export function inviteHasCounts(invite: Invite): invite is Invite { diff --git a/backend/src/utils/MessageBuffer.ts b/backend/src/utils/MessageBuffer.ts index 17022357..7cef009c 100644 --- a/backend/src/utils/MessageBuffer.ts +++ b/backend/src/utils/MessageBuffer.ts @@ -1,6 +1,6 @@ import { StrictMessageContent } from "../utils"; -import Timeout = NodeJS.Timeout; import { calculateEmbedSize } from "./calculateEmbedSize"; +import Timeout = NodeJS.Timeout; type ConsumeFn = (part: StrictMessageContent) => void; diff --git a/backend/src/utils/async.ts b/backend/src/utils/async.ts index 5479436a..196067be 100644 --- a/backend/src/utils/async.ts +++ b/backend/src/utils/async.ts @@ -1,4 +1,4 @@ -import { Awaitable } from "knub/dist/utils"; +import { Awaitable } from "./typeUtils"; export async function asyncReduce( arr: T[], diff --git a/backend/src/utils/calculateEmbedSize.ts b/backend/src/utils/calculateEmbedSize.ts index 124aa03b..4a9cd1a0 100644 --- a/backend/src/utils/calculateEmbedSize.ts +++ b/backend/src/utils/calculateEmbedSize.ts @@ -1,4 +1,4 @@ -import { MessageEmbedOptions } from "discord.js"; +import { APIEmbed, EmbedData } from "discord.js"; function sumStringLengthsRecursively(obj: any): number { if (obj == null) return 0; @@ -12,6 +12,6 @@ function sumStringLengthsRecursively(obj: any): number { return 0; } -export function calculateEmbedSize(embed: MessageEmbedOptions): number { +export function calculateEmbedSize(embed: APIEmbed | EmbedData): number { return sumStringLengthsRecursively(embed); } diff --git a/backend/src/utils/canAssignRole.ts b/backend/src/utils/canAssignRole.ts index a88a61e9..96ec92e4 100644 --- a/backend/src/utils/canAssignRole.ts +++ b/backend/src/utils/canAssignRole.ts @@ -1,9 +1,9 @@ -import { Guild, GuildMember, Permissions, Role, Snowflake } from "discord.js"; +import { Guild, GuildMember, PermissionsBitField, Role, Snowflake } from "discord.js"; import { getMissingPermissions } from "./getMissingPermissions"; import { hasDiscordPermissions } from "./hasDiscordPermissions"; export function canAssignRole(guild: Guild, member: GuildMember, roleId: string) { - if (getMissingPermissions(member.permissions, Permissions.FLAGS.MANAGE_ROLES)) { + if (getMissingPermissions(member.permissions, PermissionsBitField.Flags.ManageRoles)) { return false; } @@ -18,7 +18,7 @@ export function canAssignRole(guild: Guild, member: GuildMember, roleId: string) const memberRoles = member.roles.cache; const highestRoleWithManageRoles = memberRoles.reduce((highest, role) => { - if (!hasDiscordPermissions(role.permissions, Permissions.FLAGS.MANAGE_ROLES)) return highest; + if (!hasDiscordPermissions(role.permissions, PermissionsBitField.Flags.ManageRoles)) return highest; if (highest == null) return role; if (role.position > highest.position) return role; return highest; diff --git a/backend/src/utils/createPaginatedMessage.ts b/backend/src/utils/createPaginatedMessage.ts index e711c620..18e8f3d0 100644 --- a/backend/src/utils/createPaginatedMessage.ts +++ b/backend/src/utils/createPaginatedMessage.ts @@ -1,19 +1,19 @@ import { Client, Message, + MessageCreateOptions, MessageEditOptions, - MessageOptions, MessageReaction, PartialMessageReaction, PartialUser, - TextChannel, + TextBasedChannel, User, } from "discord.js"; -import { Awaitable } from "knub/dist/utils"; import { MINUTES, noop } from "../utils"; +import { Awaitable } from "./typeUtils"; import Timeout = NodeJS.Timeout; -export type LoadPageFn = (page: number) => Awaitable; +export type LoadPageFn = (page: number) => Awaitable; export interface PaginateMessageOpts { timeout: number; @@ -27,7 +27,7 @@ const defaultOpts: PaginateMessageOpts = { export async function createPaginatedMessage( client: Client, - channel: TextChannel | User, + channel: TextBasedChannel | User, totalPages: number, loadPageFn: LoadPageFn, opts: Partial = {}, diff --git a/backend/src/utils/crypt.ts b/backend/src/utils/crypt.ts index d6208f1c..026892ec 100644 --- a/backend/src/utils/crypt.ts +++ b/backend/src/utils/crypt.ts @@ -1,7 +1,6 @@ -import { spawn, Worker, Pool } from "threads"; -import type { CryptFns } from "./cryptWorker"; -import { MINUTES } from "../utils"; +import { Pool, spawn, Worker } from "threads"; import { env } from "../env"; +import { MINUTES } from "../utils"; const pool = Pool(() => spawn(new Worker("./cryptWorker"), { timeout: 10 * MINUTES }), 8); diff --git a/backend/src/utils/easyProfiler.ts b/backend/src/utils/easyProfiler.ts index b26c68b4..28918526 100644 --- a/backend/src/utils/easyProfiler.ts +++ b/backend/src/utils/easyProfiler.ts @@ -1,6 +1,8 @@ -import { Profiler } from "knub/dist/Profiler"; +import type { Knub } from "knub"; import { performance } from "perf_hooks"; -import { SECONDS, noop } from "../utils"; +import { noop, SECONDS } from "../utils"; + +type Profiler = Knub["profiler"]; let _profilingEnabled = false; diff --git a/backend/src/utils/filterObject.ts b/backend/src/utils/filterObject.ts index 0456ebcb..6cea499c 100644 --- a/backend/src/utils/filterObject.ts +++ b/backend/src/utils/filterObject.ts @@ -6,7 +6,7 @@ type FilterResult = { * Filter an object's properties based on its values and keys * @return New object with filtered properties */ -export function filterObject( +export function filterObject( object: T, filterFn: (value: T[K], key: K) => boolean, ): FilterResult { diff --git a/backend/src/utils/findMatchingAuditLogEntry.ts b/backend/src/utils/findMatchingAuditLogEntry.ts index fcce7a76..70e8e426 100644 --- a/backend/src/utils/findMatchingAuditLogEntry.ts +++ b/backend/src/utils/findMatchingAuditLogEntry.ts @@ -1,4 +1,4 @@ -import { Guild, GuildAuditLogsAction, GuildAuditLogsEntry } from "discord.js"; +import { AuditLogEvent, Guild, GuildAuditLogsEntry } from "discord.js"; import { SECONDS, sleep } from "../utils"; const BATCH_DEBOUNCE_TIME = 2 * SECONDS; @@ -19,7 +19,7 @@ const batches = new Map(); */ export async function findMatchingAuditLogEntry( guild: Guild, - action?: GuildAuditLogsAction, + action?: AuditLogEvent, targetId?: string, ): Promise { let candidates: GuildAuditLogsEntry[]; @@ -49,7 +49,8 @@ export async function findMatchingAuditLogEntry( const _candidates = Array.from(result?.entries.values() ?? []); batches.delete(guild.id); - resolve(_candidates); + // TODO: Figure out the type + resolve(_candidates as any); }), join() { batch._waitUntil = Date.now() + BATCH_DEBOUNCE_TIME; diff --git a/backend/src/utils/getGuildPrefix.ts b/backend/src/utils/getGuildPrefix.ts index 763a5032..8623dc09 100644 --- a/backend/src/utils/getGuildPrefix.ts +++ b/backend/src/utils/getGuildPrefix.ts @@ -1,6 +1,5 @@ -import { GuildPluginData } from "knub"; -import { getDefaultPrefix } from "knub/dist/commands/commandUtils"; +import { getDefaultMessageCommandPrefix, GuildPluginData } from "knub"; export function getGuildPrefix(pluginData: GuildPluginData) { - return pluginData.fullConfig.prefix || getDefaultPrefix(pluginData.client); + return pluginData.fullConfig.prefix || getDefaultMessageCommandPrefix(pluginData.client); } diff --git a/backend/src/utils/getMissingPermissions.ts b/backend/src/utils/getMissingPermissions.ts index 3b1adf45..14ac7e03 100644 --- a/backend/src/utils/getMissingPermissions.ts +++ b/backend/src/utils/getMissingPermissions.ts @@ -1,4 +1,4 @@ -import { Permissions } from "discord.js"; +import { PermissionsBitField } from "discord.js"; /** * @param resolvedPermissions A Permission object from e.g. GuildChannel#permissionsFor() or Member#permission @@ -6,13 +6,13 @@ import { Permissions } from "discord.js"; * @return Bitmask of missing permissions */ export function getMissingPermissions( - resolvedPermissions: Permissions | Readonly, + resolvedPermissions: PermissionsBitField | Readonly, requiredPermissions: number | bigint, ): bigint { const allowedPermissions = resolvedPermissions; const nRequiredPermissions = requiredPermissions; - if (Boolean(allowedPermissions.bitfield & Permissions.FLAGS.ADMINISTRATOR)) { + if (Boolean(allowedPermissions.bitfield & PermissionsBitField.Flags.Administrator)) { return BigInt(0); } diff --git a/backend/src/utils/getPermissionNames.ts b/backend/src/utils/getPermissionNames.ts index bc05ee64..e0dd105c 100644 --- a/backend/src/utils/getPermissionNames.ts +++ b/backend/src/utils/getPermissionNames.ts @@ -1,11 +1,11 @@ -import { Permissions } from "discord.js"; +import { PermissionsBitField } from "discord.js"; const permissionNumberToName: Map = new Map(); const ignoredPermissionConstants = ["all", "allGuild", "allText", "allVoice"]; -for (const key in Permissions.FLAGS) { +for (const key in PermissionsBitField.Flags) { if (ignoredPermissionConstants.includes(key)) continue; - permissionNumberToName.set(BigInt(Permissions.FLAGS[key]), key); + permissionNumberToName.set(BigInt(PermissionsBitField.Flags[key]), key); } /** diff --git a/backend/src/utils/hasDiscordPermissions.ts b/backend/src/utils/hasDiscordPermissions.ts index fbf81506..e56b4fbb 100644 --- a/backend/src/utils/hasDiscordPermissions.ts +++ b/backend/src/utils/hasDiscordPermissions.ts @@ -1,18 +1,18 @@ -import { Permissions, PermissionString } from "discord.js"; +import { PermissionsBitField } from "discord.js"; /** * @param resolvedPermissions A Permission object from e.g. GuildChannel#permissionsOf() or Member#permission * @param requiredPermissions Bitmask of required permissions */ export function hasDiscordPermissions( - resolvedPermissions: Permissions | Readonly | null, + resolvedPermissions: PermissionsBitField | Readonly | null, requiredPermissions: number | bigint, ) { if (resolvedPermissions == null) { return false; } - if (resolvedPermissions.has(Permissions.FLAGS.ADMINISTRATOR)) { + if (resolvedPermissions.has(PermissionsBitField.Flags.Administrator)) { return true; } diff --git a/backend/src/utils/idToTimestamp.ts b/backend/src/utils/idToTimestamp.ts index 10b964a5..53a1fa03 100644 --- a/backend/src/utils/idToTimestamp.ts +++ b/backend/src/utils/idToTimestamp.ts @@ -1,6 +1,6 @@ import { Snowflake, SnowflakeUtil } from "discord.js"; -export function idToTimestamp(id: string) { +export function idToTimestamp(id: string): string | null { if (typeof id === "number") return null; - return SnowflakeUtil.deconstruct(id as Snowflake).timestamp; + return SnowflakeUtil.deconstruct(id as Snowflake).timestamp.toString(); } diff --git a/backend/src/utils/isDmChannel.ts b/backend/src/utils/isDmChannel.ts index ad1a5781..089c5058 100644 --- a/backend/src/utils/isDmChannel.ts +++ b/backend/src/utils/isDmChannel.ts @@ -1,6 +1,5 @@ -import { Channel, DMChannel } from "discord.js"; -import { ChannelTypeStrings } from "src/types"; +import type { Channel, DMChannel } from "discord.js"; export function isDmChannel(channel: Channel): channel is DMChannel { - return channel.type === ChannelTypeStrings.DM || channel.type === ChannelTypeStrings.GROUP; + return channel.isDMBased(); } diff --git a/backend/src/utils/isGuildChannel.ts b/backend/src/utils/isGuildChannel.ts index fa5a5199..b2b224bc 100644 --- a/backend/src/utils/isGuildChannel.ts +++ b/backend/src/utils/isGuildChannel.ts @@ -1,5 +1,5 @@ -import { Channel, GuildChannel } from "discord.js"; +import type { Channel, GuildBasedChannel } from "discord.js"; -export function isGuildChannel(channel: Channel): channel is GuildChannel { - return channel.type.startsWith("GUILD_"); +export function isGuildChannel(channel: Channel): channel is GuildBasedChannel { + return "guild" in channel && channel.guild !== null; } diff --git a/backend/src/utils/isThreadChannel.ts b/backend/src/utils/isThreadChannel.ts index 6a1060df..b0829d77 100644 --- a/backend/src/utils/isThreadChannel.ts +++ b/backend/src/utils/isThreadChannel.ts @@ -1,10 +1,5 @@ -import { Channel, ThreadChannel } from "discord.js"; -import { ChannelTypeStrings } from "src/types"; +import type { AnyThreadChannel, Channel } from "discord.js"; -export function isThreadChannel(channel: Channel): channel is ThreadChannel { - return ( - channel.type === ChannelTypeStrings.NEWS_THREAD || - channel.type === ChannelTypeStrings.PUBLIC_THREAD || - channel.type === ChannelTypeStrings.PRIVATE_THREAD - ); +export function isThreadChannel(channel: Channel): channel is AnyThreadChannel { + return channel.isThread(); } diff --git a/backend/src/utils/messageHasContent.ts b/backend/src/utils/messageHasContent.ts index b70918be..0db5931e 100644 --- a/backend/src/utils/messageHasContent.ts +++ b/backend/src/utils/messageHasContent.ts @@ -1,4 +1,5 @@ -import { MessageOptions } from "discord.js"; +import { MessageCreateOptions } from "discord.js"; +import { StrictMessageContent } from "../utils.js"; function embedHasContent(embed: any) { for (const [key, value] of Object.entries(embed)) { @@ -18,7 +19,7 @@ function embedHasContent(embed: any) { return false; } -export function messageHasContent(content: string | MessageOptions): boolean { +export function messageHasContent(content: string | MessageCreateOptions | StrictMessageContent): boolean { if (typeof content === "string") { return content.trim() !== ""; } diff --git a/backend/src/utils/messageIsEmpty.ts b/backend/src/utils/messageIsEmpty.ts index d1b1f117..4721f7eb 100644 --- a/backend/src/utils/messageIsEmpty.ts +++ b/backend/src/utils/messageIsEmpty.ts @@ -1,6 +1,7 @@ -import { MessageOptions } from "discord.js"; +import { MessageCreateOptions } from "discord.js"; +import { StrictMessageContent } from "../utils.js"; import { messageHasContent } from "./messageHasContent"; -export function messageIsEmpty(content: string | MessageOptions): boolean { +export function messageIsEmpty(content: string | MessageCreateOptions | StrictMessageContent): boolean { return !messageHasContent(content); } diff --git a/backend/src/utils/permissionNames.ts b/backend/src/utils/permissionNames.ts index 433fcb98..43e39291 100644 --- a/backend/src/utils/permissionNames.ts +++ b/backend/src/utils/permissionNames.ts @@ -1,48 +1,46 @@ -import { PermissionFlags } from "discord.js"; +import type { PermissionFlagsBits } from "discord.js"; import { EMPTY_CHAR } from "../utils"; -export const PERMISSION_NAMES: Record = { - ADD_REACTIONS: "Add Reactions", - ADMINISTRATOR: "Administrator", - ATTACH_FILES: "Attach Files", - BAN_MEMBERS: "Ban Members", - CHANGE_NICKNAME: "Change Nickname", - CONNECT: "Connect", - CREATE_INSTANT_INVITE: "Create Invite", - CREATE_PRIVATE_THREADS: "Create Private Threads", - CREATE_PUBLIC_THREADS: "Create Public Threads", - DEAFEN_MEMBERS: "Deafen Members", - EMBED_LINKS: "Embed Links", - KICK_MEMBERS: "Kick Members", - MANAGE_CHANNELS: "Manage Channels", - MANAGE_EMOJIS_AND_STICKERS: "Manage Emojis and Stickers", - MANAGE_GUILD: "Manage Server", - MANAGE_MESSAGES: "Manage Messages", - MANAGE_NICKNAMES: "Manage Nicknames", - MANAGE_ROLES: "Manage Roles", - MANAGE_THREADS: "Manage Threads", - MANAGE_WEBHOOKS: "Manage Webhooks", - MENTION_EVERYONE: `Mention @${EMPTY_CHAR}everyone, @${EMPTY_CHAR}here, and All Roles`, - MOVE_MEMBERS: "Move Members", - MUTE_MEMBERS: "Mute Members", - PRIORITY_SPEAKER: "Priority Speaker", - READ_MESSAGE_HISTORY: "Read Message History", - REQUEST_TO_SPEAK: "Request to Speak", - SEND_MESSAGES: "Send Messages", - SEND_MESSAGES_IN_THREADS: "Send Messages in Threads", - SEND_TTS_MESSAGES: "Send Text-To-Speech Messages", - SPEAK: "Speak", - START_EMBEDDED_ACTIVITIES: "Start Embedded Activities", - STREAM: "Video", - USE_APPLICATION_COMMANDS: "Use Application Commands", - USE_EXTERNAL_EMOJIS: "Use External Emoji", - USE_EXTERNAL_STICKERS: "Use External Stickers", - USE_PRIVATE_THREADS: "Use Private Threads", - USE_PUBLIC_THREADS: "Use Public Threads", - USE_VAD: "Use Voice Activity", - VIEW_AUDIT_LOG: "View Audit Log", - VIEW_CHANNEL: "View Channels", - VIEW_GUILD_INSIGHTS: "View Guild Insights", - MODERATE_MEMBERS: "Moderate Members", - MANAGE_EVENTS: "Manage Events", -}; +export const PERMISSION_NAMES = { + AddReactions: "Add Reactions", + Administrator: "Administrator", + AttachFiles: "Attach Files", + BanMembers: "Ban Members", + ChangeNickname: "Change Nickname", + Connect: "Connect", + CreateInstantInvite: "Create Invite", + CreatePrivateThreads: "Create Private Threads", + CreatePublicThreads: "Create Public Threads", + DeafenMembers: "Deafen Members", + EmbedLinks: "Embed Links", + KickMembers: "Kick Members", + ManageChannels: "Manage Channels", + ManageEmojisAndStickers: "Manage Emojis and Stickers", + ManageGuild: "Manage Server", + ManageMessages: "Manage Messages", + ManageNicknames: "Manage Nicknames", + ManageRoles: "Manage Roles", + ManageThreads: "Manage Threads", + ManageWebhooks: "Manage Webhooks", + MentionEveryone: `Mention @${EMPTY_CHAR}everyone, @${EMPTY_CHAR}here, and All Roles`, + MoveMembers: "Move Members", + MuteMembers: "Mute Members", + PrioritySpeaker: "Priority Speaker", + ReadMessageHistory: "Read Message History", + RequestToSpeak: "Request to Speak", + SendMessages: "Send Messages", + SendMessagesInThreads: "Send Messages in Threads", + SendTTSMessages: "Send Text-To-Speech Messages", + Speak: "Speak", + UseEmbeddedActivities: "Start Embedded Activities", + Stream: "Video", + UseApplicationCommands: "Use Application Commands", + UseExternalEmojis: "Use External Emoji", + UseExternalStickers: "Use External Stickers", + UseVAD: "Use Voice Activity", + ViewAuditLog: "View Audit Log", + ViewChannel: "View Channels", + ViewGuildInsights: "View Guild Insights", + ModerateMembers: "Moderate Members", + ManageEvents: "Manage Events", +} as const satisfies Record; diff --git a/backend/src/utils/readChannelPermissions.ts b/backend/src/utils/readChannelPermissions.ts index 16dae413..643b9c9d 100644 --- a/backend/src/utils/readChannelPermissions.ts +++ b/backend/src/utils/readChannelPermissions.ts @@ -1,9 +1,10 @@ -import { Permissions } from "discord.js"; +import { PermissionsBitField } from "discord.js"; /** * Bitmask of permissions required to read messages in a channel */ -export const readChannelPermissions = Permissions.FLAGS.VIEW_CHANNEL | Permissions.FLAGS.READ_MESSAGE_HISTORY; +export const readChannelPermissions = + PermissionsBitField.Flags.ViewChannel | PermissionsBitField.Flags.ReadMessageHistory; /** * Bitmask of permissions required to read messages in a channel (bigint) diff --git a/backend/src/utils/resolveChannelIds.ts b/backend/src/utils/resolveChannelIds.ts index bc7493e9..7f3bf138 100644 --- a/backend/src/utils/resolveChannelIds.ts +++ b/backend/src/utils/resolveChannelIds.ts @@ -1,7 +1,4 @@ -import { ChannelType } from "discord-api-types/v9"; import { CategoryChannel, Channel } from "discord.js"; -import { ChannelTypes } from "discord.js/typings/enums"; -import { ChannelTypeStrings } from "src/types"; import { isDmChannel } from "./isDmChannel"; import { isGuildChannel } from "./isGuildChannel"; import { isThreadChannel } from "./isThreadChannel"; diff --git a/backend/src/utils/resolveMessageTarget.ts b/backend/src/utils/resolveMessageTarget.ts index 4b24b779..1c7e7c3c 100644 --- a/backend/src/utils/resolveMessageTarget.ts +++ b/backend/src/utils/resolveMessageTarget.ts @@ -1,4 +1,4 @@ -import { GuildTextBasedChannel, Snowflake, TextChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { getChannelIdFromMessageId } from "../data/getChannelIdFromMessageId"; import { isSnowflake } from "../utils"; @@ -47,7 +47,7 @@ export async function resolveMessageTarget(pluginData: GuildPluginData, val } const channel = pluginData.guild.channels.resolve(result.channelId as Snowflake); - if (!channel?.isText()) { + if (!channel?.isTextBased()) { return null; } diff --git a/backend/src/utils/templateSafeObjects.ts b/backend/src/utils/templateSafeObjects.ts index d57e1fca..05e3f08f 100644 --- a/backend/src/utils/templateSafeObjects.ts +++ b/backend/src/utils/templateSafeObjects.ts @@ -1,7 +1,7 @@ import { Emoji, Guild, - GuildChannel, + GuildBasedChannel, GuildMember, Message, PartialGuildMember, @@ -9,12 +9,12 @@ import { Snowflake, StageInstance, Sticker, - ThreadChannel, + StickerFormatType, User, } from "discord.js"; -import { UnknownUser } from "src/utils"; import { GuildPluginData } from "knub"; -import { TemplateSafeValueContainer, TypedTemplateSafeValueContainer } from "../templateFormatter"; +import { UnknownUser } from "src/utils"; +import { Case } from "../data/entities/Case"; import { ISavedMessageAttachmentData, ISavedMessageData, @@ -22,7 +22,7 @@ import { ISavedMessageStickerData, SavedMessage, } from "../data/entities/SavedMessage"; -import { Case } from "../data/entities/Case"; +import { TemplateSafeValueContainer, TypedTemplateSafeValueContainer } from "../templateFormatter"; type InputProps = Omit< { @@ -239,7 +239,7 @@ export function userToTemplateSafeUser(user: User | UnknownUser): TemplateSafeUs discriminator: user.discriminator, mention: `<@${user.id}>`, tag: user.tag, - avatarURL: user.displayAvatarURL?.({ dynamic: true }), + avatarURL: user.displayAvatarURL?.(), bot: user.bot, createdAt: user.createdTimestamp, }); @@ -268,7 +268,7 @@ export function memberToTemplateSafeMember(member: GuildMember | PartialGuildMem }); } -export function channelToTemplateSafeChannel(channel: GuildChannel | ThreadChannel): TemplateSafeChannel { +export function channelToTemplateSafeChannel(channel: GuildBasedChannel): TemplateSafeChannel { return new TemplateSafeChannel({ id: channel.id, name: channel.name, @@ -305,9 +305,9 @@ export function stickerToTemplateSafeSticker(sticker: Sticker): TemplateSafeStic packId: sticker.packId ?? undefined, name: sticker.name, description: sticker.description ?? "", - tags: sticker.tags?.join(", ") ?? "", + tags: sticker.tags ?? "", format: sticker.format, - animated: sticker.format === "PNG" ? false : true, + animated: sticker.format === StickerFormatType.PNG ? false : true, url: sticker.url, }); } @@ -448,7 +448,7 @@ export function messageToTemplateSafeMessage(message: Message): TemplateSafeMess id: message.id, content: message.content, author: userToTemplateSafeUser(message.author), - channel: channelToTemplateSafeChannel(message.channel as GuildChannel | ThreadChannel), + channel: channelToTemplateSafeChannel(message.channel as GuildBasedChannel), }); } diff --git a/backend/src/utils/typeUtils.ts b/backend/src/utils/typeUtils.ts index 26bc121e..7c8b4cbd 100644 --- a/backend/src/utils/typeUtils.ts +++ b/backend/src/utils/typeUtils.ts @@ -8,3 +8,9 @@ export declare type WithRequiredProps = T & { // https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/ export type Awaited = T extends PromiseLike ? Awaited : T; + +export type Awaitable = T | Promise; + +export type DeepMutable = { + -readonly [P in keyof T]: DeepMutable; +}; diff --git a/backend/src/utils/validateNoObjectAliases.ts b/backend/src/utils/validateNoObjectAliases.ts index 3666274d..76039d6e 100644 --- a/backend/src/utils/validateNoObjectAliases.ts +++ b/backend/src/utils/validateNoObjectAliases.ts @@ -1,5 +1,3 @@ -import { Not } from "../utils"; - const scalarTypes = ["string", "number", "boolean", "bigint"]; export class ObjectAliasError extends Error {} diff --git a/backend/src/utils/waitForInteraction.ts b/backend/src/utils/waitForInteraction.ts index ece841ca..70688047 100644 --- a/backend/src/utils/waitForInteraction.ts +++ b/backend/src/utils/waitForInteraction.ts @@ -1,23 +1,31 @@ -import { MessageActionRow, MessageButton, MessageComponentInteraction, MessageOptions, TextChannel } from "discord.js"; -import { noop } from "knub/dist/utils"; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + GuildTextBasedChannel, + MessageActionRowComponentBuilder, + MessageComponentInteraction, + MessageCreateOptions, +} from "discord.js"; import moment from "moment"; import uuidv4 from "uuid/v4"; +import { noop } from "../utils"; export async function waitForButtonConfirm( - channel: TextChannel, - toPost: MessageOptions, + channel: GuildTextBasedChannel, + toPost: MessageCreateOptions, options?: WaitForOptions, ): Promise { return new Promise(async (resolve) => { const idMod = `${channel.guild.id}-${moment.utc().valueOf()}`; - const row = new MessageActionRow().addComponents([ - new MessageButton() - .setStyle("SUCCESS") + const row = new ActionRowBuilder().addComponents([ + new ButtonBuilder() + .setStyle(ButtonStyle.Success) .setLabel(options?.confirmText || "Confirm") .setCustomId(`confirmButton:${idMod}:${uuidv4()}`), - new MessageButton() - .setStyle("DANGER") + new ButtonBuilder() + .setStyle(ButtonStyle.Danger) .setLabel(options?.cancelText || "Cancel") .setCustomId(`cancelButton:${idMod}:${uuidv4()}`), ]); @@ -29,6 +37,7 @@ export async function waitForButtonConfirm( if (options?.restrictToId && options.restrictToId !== interaction.user.id) { interaction .reply({ content: `You are not permitted to use these buttons.`, ephemeral: true }) + // tslint:disable-next-line no-console .catch((err) => console.trace(err.message)); } else { if (interaction.customId.startsWith(`confirmButton:${idMod}:`)) { @@ -41,7 +50,7 @@ export async function waitForButtonConfirm( } }); collector.on("end", () => { - if (!message.deleted) message.delete().catch(noop); + if (message.deletable) message.delete().catch(noop); resolve(false); }); }); diff --git a/backend/tsconfig.json b/backend/tsconfig.json index cde827e4..04e9bf4d 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -1,13 +1,13 @@ { "compilerOptions": { - "moduleResolution": "node", - "module": "commonjs", + "moduleResolution": "NodeNext", + "module": "NodeNext", "noImplicitAny": false, "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, - "target": "es2020", - "lib": ["esnext"], + "target": "es2022", + "lib": ["es2022"], "baseUrl": ".", "resolveJsonModule": true, "esModuleInterop": true, diff --git a/dashboard/src/components/docs/Plugin.vue b/dashboard/src/components/docs/Plugin.vue index 68c15f26..ab88c46b 100644 --- a/dashboard/src/components/docs/Plugin.vue +++ b/dashboard/src/components/docs/Plugin.vue @@ -45,10 +45,10 @@ - -
-

Commands

-
+
+

Message Commands

+

@@ -309,8 +309,9 @@ }, hasUsageInfo() { if (!this.data) return true; - if (this.data.commands.length) return true; - if (this.data.info.usageGuide) return true; + if (this.data.messageCommands?.length) return true; + if (this.data.slashCommands?.length) return true; + if (this.data.info?.usageGuide) return true; return false; }, }), diff --git a/dashboard/src/init-vue.ts b/dashboard/src/init-vue.ts index 614b5609..1c1cbbd8 100644 --- a/dashboard/src/init-vue.ts +++ b/dashboard/src/init-vue.ts @@ -4,11 +4,11 @@ import Vue from "vue"; import hljs from "highlight.js/lib/highlight.js"; import hljsYaml from "highlight.js/lib/languages/yaml.js"; -import VueHighlightJS from "vue-highlightjs"; import "highlight.js/styles/ocean.css"; +import VueHighlightJS from "vue-highlightjs"; -import { RootStore } from "./store"; import { router } from "./routes"; +import { RootStore } from "./store"; import "./directives/trim-indents"; diff --git a/dashboard/src/routes.ts b/dashboard/src/routes.ts index e442f615..15ac3d96 100644 --- a/dashboard/src/routes.ts +++ b/dashboard/src/routes.ts @@ -1,5 +1,5 @@ import Vue from "vue"; -import VueRouter, { RouteConfig } from "vue-router"; +import VueRouter from "vue-router"; import { authGuard, authRedirectGuard, loginCallbackGuard } from "./auth"; Vue.use(VueRouter); diff --git a/dashboard/src/store/auth.ts b/dashboard/src/store/auth.ts index 457284b3..ff2de198 100644 --- a/dashboard/src/store/auth.ts +++ b/dashboard/src/store/auth.ts @@ -1,5 +1,5 @@ -import { get, post } from "../api"; -import { ActionTree, Module } from "vuex"; +import { Module } from "vuex"; +import { post } from "../api"; import { AuthState, IntervalType, RootState } from "./types"; // Refresh auth every 15 minutes diff --git a/dashboard/src/store/docs.ts b/dashboard/src/store/docs.ts index 8aaa4cfe..842cd1a1 100644 --- a/dashboard/src/store/docs.ts +++ b/dashboard/src/store/docs.ts @@ -1,5 +1,5 @@ -import { get } from "../api"; import { Module } from "vuex"; +import { get } from "../api"; import { DocsState, RootState } from "./types"; export const DocsStore: Module = { @@ -34,8 +34,8 @@ export const DocsStore: Module = { if (state.plugins[name]) return; const data = await get(`docs/plugins/${name}`); - if (data && data.commands) { - data.commands.sort((a, b) => { + if (data && data.messageCommands) { + data.messageCommands.sort((a, b) => { const aName = (Array.isArray(a.trigger) ? a.trigger[0] : a.trigger).toLowerCase(); const bName = (Array.isArray(b.trigger) ? b.trigger[0] : b.trigger).toLowerCase(); if (aName > bName) return 1; diff --git a/dashboard/src/store/guilds.ts b/dashboard/src/store/guilds.ts index bfdbe71b..0064aace 100644 --- a/dashboard/src/store/guilds.ts +++ b/dashboard/src/store/guilds.ts @@ -1,8 +1,7 @@ -import { get, post } from "../api"; -import { Module } from "vuex"; -import { GuildState, LoadStatus, RootState } from "./types"; -import { ApiPermissions } from "@shared/apiPermissions"; import Vue from "vue"; +import { Module } from "vuex"; +import { get, post } from "../api"; +import { GuildState, LoadStatus, RootState } from "./types"; export const GuildStore: Module = { namespaced: true, diff --git a/dashboard/src/store/index.ts b/dashboard/src/store/index.ts index c4aadd83..6c7b3792 100644 --- a/dashboard/src/store/index.ts +++ b/dashboard/src/store/index.ts @@ -3,10 +3,10 @@ import Vuex, { Store } from "vuex"; Vue.use(Vuex); -import { RootState } from "./types"; import { AuthStore } from "./auth"; -import { GuildStore } from "./guilds"; import { DocsStore } from "./docs"; +import { GuildStore } from "./guilds"; +import { RootState } from "./types"; export const RootStore = new Vuex.Store({ modules: { diff --git a/dashboard/src/store/staff.ts b/dashboard/src/store/staff.ts index c6855598..293d2888 100644 --- a/dashboard/src/store/staff.ts +++ b/dashboard/src/store/staff.ts @@ -1,5 +1,5 @@ -import { get, post } from "../api"; import { Module } from "vuex"; +import { get } from "../api"; import { RootState, StaffState } from "./types"; export const StaffStore: Module = { diff --git a/dashboard/src/store/types.ts b/dashboard/src/store/types.ts index c7e2987b..ddecc560 100644 --- a/dashboard/src/store/types.ts +++ b/dashboard/src/store/types.ts @@ -54,7 +54,8 @@ export interface ThinDocsPlugin { } export interface DocsPlugin extends ThinDocsPlugin { - commands: any[]; + messageCommands: any[]; + slashCommands: any[]; defaultOptions: any; configSchema?: string; info: { diff --git a/dashboard/webpack.config.js b/dashboard/webpack.config.js index 2ae878ca..5d6bc5cb 100644 --- a/dashboard/webpack.config.js +++ b/dashboard/webpack.config.js @@ -3,6 +3,7 @@ const VueLoaderPlugin = require("vue-loader/lib/plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const DotenvPlugin = require("dotenv-webpack"); const merge = require("webpack-merge"); +const webpack = require("webpack"); const targetDir = path.normalize(path.join(__dirname, "dist")); @@ -162,6 +163,8 @@ let config = { }, }; +if (process.env.NODE_ENV === "web") config.plugins.push(new webpack.EnvironmentPlugin(["NODE_ENV", "API_URL"])); + if (process.env.NODE_ENV === "production") { config = merge(config, { mode: "production", diff --git a/package-lock.json b/package-lock.json index 0ef2489d..6a7256a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,16 +7,14 @@ "": { "name": "@zeppelin/zeppelin", "version": "0.0.1", - "dependencies": { - "knub": "^30.0.0-beta.39" - }, "devDependencies": { "husky": "^3.0.9", "lint-staged": "^9.4.2", - "prettier": "^2.4.0", + "prettier": "^2.8.4", + "prettier-plugin-organize-imports": "^3.2.2", "tslint": "^5.13.1", "tslint-config-prettier": "^1.18.0", - "typescript": "4.4" + "typescript": "~4.9.5" } }, "node_modules/@babel/code-frame": { @@ -39,48 +37,6 @@ "js-tokens": "^4.0.0" } }, - "node_modules/@discordjs/builders": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz", - "integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==", - "dependencies": { - "@sindresorhus/is": "^4.0.1", - "discord-api-types": "^0.22.0", - "ow": "^0.27.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">=14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@discordjs/builders/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/@discordjs/collection": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz", - "integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -128,26 +84,6 @@ "node": ">=6" } }, - "node_modules/@sapphire/async-queue": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz", - "integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==", - "engines": { - "node": ">=14", - "npm": ">=6" - } - }, - "node_modules/@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -174,7 +110,8 @@ "node_modules/@types/node": { "version": "12.12.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.5.tgz", - "integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==" + "integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==", + "dev": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.0", @@ -182,14 +119,6 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, - "node_modules/@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -260,11 +189,6 @@ "node": ">=8" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -413,17 +337,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -508,14 +421,6 @@ "node": ">=8" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/diff": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", @@ -537,55 +442,6 @@ "node": ">=8" } }, - "node_modules/discord-api-types": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz", - "integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/discord.js": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz", - "integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==", - "dependencies": { - "@discordjs/builders": "^0.5.0", - "@discordjs/collection": "^0.2.1", - "@discordjs/form-data": "^3.0.1", - "@sapphire/async-queue": "^1.1.4", - "@types/ws": "^7.4.7", - "discord-api-types": "^0.22.0", - "node-fetch": "^2.6.1", - "ws": "^7.5.1" - }, - "engines": { - "node": ">=16.6.0", - "npm": ">=7.0.0" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, "node_modules/elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -1064,33 +920,6 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "node_modules/knub": { - "version": "30.0.0-beta.39", - "resolved": "https://registry.npmjs.org/knub/-/knub-30.0.0-beta.39.tgz", - "integrity": "sha512-L9RYkqh7YcWfw0ZXdGrKEZru/J+mkiyn+8vi1xCvjEdKMPdq4Gov/SG4suajMFhhX3RXdvh8BoE/3gbR2cq4xA==", - "dependencies": { - "discord-api-types": "^0.22.0", - "discord.js": "^13.0.1", - "knub-command-manager": "^9.1.0", - "ts-essentials": "^6.0.7" - } - }, - "node_modules/knub-command-manager": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/knub-command-manager/-/knub-command-manager-9.1.0.tgz", - "integrity": "sha512-pEtpWElbBoTRSL8kWSPRrTIuTIdvYGkP/wzOn77cieumC02adfwEt1Cc09HFvVT4ib35nf1y31oul36csaG7Vg==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - } - }, - "node_modules/knub-command-manager/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -1397,11 +1226,6 @@ "node": ">=8" } }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, "node_modules/log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -1456,25 +1280,6 @@ "node": ">=8" } }, - "node_modules/mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", - "dependencies": { - "mime-db": "1.49.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -1527,14 +1332,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "engines": { - "node": "4.x || >=6.0.0" - } - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1616,44 +1413,6 @@ "opencollective-postinstall": "index.js" } }, - "node_modules/ow": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz", - "integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==", - "dependencies": { - "@sindresorhus/is": "^4.0.1", - "callsites": "^3.1.0", - "dot-prop": "^6.0.1", - "lodash.isequal": "^4.5.0", - "type-fest": "^1.2.1", - "vali-date": "^1.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ow/node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ow/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -1794,15 +1553,38 @@ } }, "node_modules/prettier": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz", - "integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", "dev": true, "bin": { "prettier": "bin-prettier.js" }, "engines": { "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.2.tgz", + "integrity": "sha512-e97lE6odGSiHonHJMTYC0q0iLXQyw0u5z/PJpvP/3vRy6/Zi9kLBwFAbEGjDzIowpjQv8b+J04PDamoUSQbzGA==", + "dev": true, + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } } }, "node_modules/pump": { @@ -2147,19 +1929,6 @@ "node": ">=8.0" } }, - "node_modules/ts-essentials": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-6.0.7.tgz", - "integrity": "sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==", - "peerDependencies": { - "typescript": ">=3.7.0" - } - }, - "node_modules/ts-mixer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz", - "integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ==" - }, "node_modules/tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -2230,9 +1999,10 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2241,14 +2011,6 @@ "node": ">=4.2.0" } }, - "node_modules/vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -2332,26 +2094,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "node_modules/ws": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } } }, "dependencies": { @@ -2375,40 +2117,6 @@ "js-tokens": "^4.0.0" } }, - "@discordjs/builders": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz", - "integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==", - "requires": { - "@sindresorhus/is": "^4.0.1", - "discord-api-types": "^0.22.0", - "ow": "^0.27.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@discordjs/collection": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz", - "integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==" - }, - "@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -2444,16 +2152,6 @@ "any-observable": "^0.3.0" } }, - "@sapphire/async-queue": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz", - "integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==" - }, - "@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" - }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -2480,7 +2178,8 @@ "@types/node": { "version": "12.12.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.5.tgz", - "integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==" + "integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==", + "dev": true }, "@types/normalize-package-data": { "version": "2.4.0", @@ -2488,14 +2187,6 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, - "@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "requires": { - "@types/node": "*" - } - }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -2548,11 +2239,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2671,14 +2357,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -2753,11 +2431,6 @@ "slash": "^3.0.0" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, "diff": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", @@ -2773,41 +2446,6 @@ "path-type": "^4.0.0" } }, - "discord-api-types": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz", - "integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==" - }, - "discord.js": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz", - "integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==", - "requires": { - "@discordjs/builders": "^0.5.0", - "@discordjs/collection": "^0.2.1", - "@discordjs/form-data": "^3.0.1", - "@sapphire/async-queue": "^1.1.4", - "@types/ws": "^7.4.7", - "discord-api-types": "^0.22.0", - "node-fetch": "^2.6.1", - "ws": "^7.5.1" - } - }, - "dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "requires": { - "is-obj": "^2.0.0" - }, - "dependencies": { - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - } - } - }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -3181,32 +2819,6 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "knub": { - "version": "30.0.0-beta.39", - "resolved": "https://registry.npmjs.org/knub/-/knub-30.0.0-beta.39.tgz", - "integrity": "sha512-L9RYkqh7YcWfw0ZXdGrKEZru/J+mkiyn+8vi1xCvjEdKMPdq4Gov/SG4suajMFhhX3RXdvh8BoE/3gbR2cq4xA==", - "requires": { - "discord-api-types": "^0.22.0", - "discord.js": "^13.0.1", - "knub-command-manager": "^9.1.0", - "ts-essentials": "^6.0.7" - } - }, - "knub-command-manager": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/knub-command-manager/-/knub-command-manager-9.1.0.tgz", - "integrity": "sha512-pEtpWElbBoTRSL8kWSPRrTIuTIdvYGkP/wzOn77cieumC02adfwEt1Cc09HFvVT4ib35nf1y31oul36csaG7Vg==", - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - } - } - }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -3446,11 +3058,6 @@ "p-locate": "^4.1.0" } }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -3493,19 +3100,6 @@ "picomatch": "^2.0.5" } }, - "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" - }, - "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", - "requires": { - "mime-db": "1.49.0" - } - }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3550,11 +3144,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -3618,31 +3207,6 @@ "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", "dev": true }, - "ow": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz", - "integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==", - "requires": { - "@sindresorhus/is": "^4.0.1", - "callsites": "^3.1.0", - "dot-prop": "^6.0.1", - "lodash.isequal": "^4.5.0", - "type-fest": "^1.2.1", - "vali-date": "^1.0.0" - }, - "dependencies": { - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } - } - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -3747,11 +3311,18 @@ } }, "prettier": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz", - "integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", "dev": true }, + "prettier-plugin-organize-imports": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.2.tgz", + "integrity": "sha512-e97lE6odGSiHonHJMTYC0q0iLXQyw0u5z/PJpvP/3vRy6/Zi9kLBwFAbEGjDzIowpjQv8b+J04PDamoUSQbzGA==", + "dev": true, + "requires": {} + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -4022,17 +3593,6 @@ "is-number": "^7.0.0" } }, - "ts-essentials": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-6.0.7.tgz", - "integrity": "sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==", - "requires": {} - }, - "ts-mixer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz", - "integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ==" - }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -4082,14 +3642,10 @@ "dev": true }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==" - }, - "vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -4158,12 +3714,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "ws": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "requires": {} } } } diff --git a/package.json b/package.json index 47abab49..5091aa24 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,11 @@ "devDependencies": { "husky": "^3.0.9", "lint-staged": "^9.4.2", - "prettier": "^2.4.0", + "prettier": "^2.8.4", + "prettier-plugin-organize-imports": "^3.2.2", "tslint": "^5.13.1", "tslint-config-prettier": "^1.18.0", - "typescript": "4.4" + "typescript": "~4.9.5" }, "husky": { "hooks": { @@ -27,8 +28,5 @@ "prettier --write", "git add" ] - }, - "dependencies": { - "knub": "^30.0.0-beta.39" } } diff --git a/presetup-configurator/src/App.tsx b/presetup-configurator/src/App.tsx index 54e9e365..b0eb1e1f 100644 --- a/presetup-configurator/src/App.tsx +++ b/presetup-configurator/src/App.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Configurator } from "./Configurator"; import "./App.css"; +import { Configurator } from "./Configurator"; export function App() { return ( diff --git a/presetup-configurator/src/Configurator.tsx b/presetup-configurator/src/Configurator.tsx index 02fd9d7d..8a8da679 100644 --- a/presetup-configurator/src/Configurator.tsx +++ b/presetup-configurator/src/Configurator.tsx @@ -1,8 +1,8 @@ +import yaml from "js-yaml"; import React, { useEffect, useState } from "react"; +import "./Configurator.css"; import { LevelEntry, Levels } from "./Levels"; import { LogChannel, LogChannels } from "./LogChannels"; -import yaml from "js-yaml"; -import "./Configurator.css"; export function Configurator() { const [prefix, setPrefix] = useState("!"); diff --git a/presetup-configurator/src/Levels.tsx b/presetup-configurator/src/Levels.tsx index cb1cc8a9..81f860c7 100644 --- a/presetup-configurator/src/Levels.tsx +++ b/presetup-configurator/src/Levels.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; const LEVEL_ADMIN = 100; const LEVEL_MODERATOR = 50; diff --git a/presetup-configurator/src/LogChannels.tsx b/presetup-configurator/src/LogChannels.tsx index 82a6a59d..40c0ca0f 100644 --- a/presetup-configurator/src/LogChannels.tsx +++ b/presetup-configurator/src/LogChannels.tsx @@ -1,4 +1,4 @@ -import React, { SetStateAction, useState } from "react"; +import React, { SetStateAction } from "react"; import "./LogChannels.css"; const LOG_TYPES = { diff --git a/shared/src/apiPermissions.test.ts b/shared/src/apiPermissions.test.ts index 98d8752b..365e4f08 100644 --- a/shared/src/apiPermissions.test.ts +++ b/shared/src/apiPermissions.test.ts @@ -1,5 +1,5 @@ -import { ApiPermissions, hasPermission } from "./apiPermissions"; import test from "ava"; +import { ApiPermissions, hasPermission } from "./apiPermissions"; test("Directly granted permissions match", (t) => { t.is(hasPermission(new Set([ApiPermissions.ManageAccess]), ApiPermissions.ManageAccess), true); diff --git a/shared/tsconfig.json b/shared/tsconfig.json index 4b02fab0..c5c16316 100644 --- a/shared/tsconfig.json +++ b/shared/tsconfig.json @@ -1,13 +1,13 @@ { "compilerOptions": { - "moduleResolution": "node", - "module": "commonjs", + "moduleResolution": "NodeNext", + "module": "NodeNext", "noImplicitAny": false, "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, - "target": "es2018", - "lib": ["esnext"], + "target": "es2022", + "lib": ["es2022"], "baseUrl": "src", "resolveJsonModule": true, "esModuleInterop": true,