diff --git a/.nvmrc b/.nvmrc index d925544b..b6a7d89c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.6 +16 diff --git a/backend/package-lock.json b/backend/package-lock.json index 1647e84c..6fc0547c 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,8 +13,8 @@ "cors": "^2.8.5", "cross-env": "^5.2.0", "deep-diff": "^1.0.2", - "discord-api-types": "^0.31.0", - "discord.js": "^13.6.0", + "discord-api-types": "^0.33.1", + "discord.js": "^13.8.0", "dotenv": "^4.0.0", "emoji-regex": "^8.0.0", "erlpack": "github:discord/erlpack", @@ -124,23 +124,25 @@ } }, "node_modules/@discordjs/builders": { - "version": "0.11.0", - "license": "Apache-2.0", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.14.0.tgz", + "integrity": "sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==", "dependencies": { - "@sindresorhus/is": "^4.2.0", - "discord-api-types": "^0.26.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.1", - "zod": "^3.11.6" + "@sapphire/shapeshift": "^3.1.0", + "@sindresorhus/is": "^4.6.0", + "discord-api-types": "^0.33.3", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.1", + "tslib": "^2.4.0" }, "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" + "node": ">=16.9.0" } }, "node_modules/@discordjs/builders/node_modules/@sindresorhus/is": { "version": "4.6.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "engines": { "node": ">=10" }, @@ -148,23 +150,17 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@discordjs/builders/node_modules/discord-api-types": { - "version": "0.26.1", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/@discordjs/builders/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@discordjs/collection": { - "version": "0.4.0", - "license": "Apache-2.0", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", + "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==", "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" + "node": ">=16.9.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -207,6 +203,15 @@ "npm": ">=7.0.0" } }, + "node_modules/@sapphire/shapeshift": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.1.0.tgz", + "integrity": "sha512-PkxFXd3QJ1qAPS05Dy2UkVGYPm/asF1Ugt2Xyzmv4DHzO3+G7l+873C4XFFcJ9M5Je+eCMC7SSifgPTSur5QuA==", + "engines": { + "node": ">=v15.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.1", "license": "Apache-2.0" @@ -344,8 +349,9 @@ "license": "MIT" }, "node_modules/@types/node-fetch": { - "version": "2.5.12", - "license": "MIT", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", "dependencies": { "@types/node": "*", "form-data": "^3.0.0" @@ -1693,35 +1699,30 @@ } }, "node_modules/discord-api-types": { - "version": "0.31.0", - "license": "MIT" + "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==" }, "node_modules/discord.js": { - "version": "13.6.0", - "license": "Apache-2.0", + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.8.0.tgz", + "integrity": "sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==", "dependencies": { - "@discordjs/builders": "^0.11.0", - "@discordjs/collection": "^0.4.0", - "@sapphire/async-queue": "^1.1.9", - "@types/node-fetch": "^2.5.12", - "@types/ws": "^8.2.2", - "discord-api-types": "^0.26.0", + "@discordjs/builders": "^0.14.0", + "@discordjs/collection": "^0.7.0", + "@sapphire/async-queue": "^1.3.1", + "@types/node-fetch": "^2.6.1", + "@types/ws": "^8.5.3", + "discord-api-types": "^0.33.3", "form-data": "^4.0.0", "node-fetch": "^2.6.1", - "ws": "^8.4.0" + "ws": "^8.7.0" }, "engines": { "node": ">=16.6.0", "npm": ">=7.0.0" } }, - "node_modules/discord.js/node_modules/discord-api-types": { - "version": "0.26.1", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/discord.js/node_modules/form-data": { "version": "4.0.0", "license": "MIT", @@ -1941,6 +1942,11 @@ "node": ">= 0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fast-diff": { "version": "1.2.0", "dev": true, @@ -4506,7 +4512,8 @@ }, "node_modules/ts-mixer": { "version": "6.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", + "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" }, "node_modules/tsc-watch": { "version": "5.0.2", @@ -5106,8 +5113,9 @@ } }, "node_modules/ws": { - "version": "8.5.0", - "license": "MIT", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "engines": { "node": ">=10.0.0" }, @@ -5305,28 +5313,34 @@ } }, "@discordjs/builders": { - "version": "0.11.0", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.14.0.tgz", + "integrity": "sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==", "requires": { - "@sindresorhus/is": "^4.2.0", - "discord-api-types": "^0.26.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.1", - "zod": "^3.11.6" + "@sapphire/shapeshift": "^3.1.0", + "@sindresorhus/is": "^4.6.0", + "discord-api-types": "^0.33.3", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.1", + "tslib": "^2.4.0" }, "dependencies": { "@sindresorhus/is": { - "version": "4.6.0" - }, - "discord-api-types": { - "version": "0.26.1" + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" }, "tslib": { - "version": "2.3.1" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" } } }, "@discordjs/collection": { - "version": "0.4.0" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", + "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==" }, "@nodelib/fs.scandir": { "version": "2.1.3", @@ -5351,6 +5365,11 @@ "@sapphire/async-queue": { "version": "1.3.1" }, + "@sapphire/shapeshift": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.1.0.tgz", + "integrity": "sha512-PkxFXd3QJ1qAPS05Dy2UkVGYPm/asF1Ugt2Xyzmv4DHzO3+G7l+873C4XFFcJ9M5Je+eCMC7SSifgPTSur5QuA==" + }, "@silvia-odwyer/photon-node": { "version": "0.3.1" }, @@ -5462,7 +5481,9 @@ "version": "14.0.14" }, "@types/node-fetch": { - "version": "2.5.12", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", "requires": { "@types/node": "*", "form-data": "^3.0.0" @@ -6346,25 +6367,26 @@ } }, "discord-api-types": { - "version": "0.31.0" + "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==" }, "discord.js": { - "version": "13.6.0", + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.8.0.tgz", + "integrity": "sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==", "requires": { - "@discordjs/builders": "^0.11.0", - "@discordjs/collection": "^0.4.0", - "@sapphire/async-queue": "^1.1.9", - "@types/node-fetch": "^2.5.12", - "@types/ws": "^8.2.2", - "discord-api-types": "^0.26.0", + "@discordjs/builders": "^0.14.0", + "@discordjs/collection": "^0.7.0", + "@sapphire/async-queue": "^1.3.1", + "@types/node-fetch": "^2.6.1", + "@types/ws": "^8.5.3", + "discord-api-types": "^0.33.3", "form-data": "^4.0.0", "node-fetch": "^2.6.1", - "ws": "^8.4.0" + "ws": "^8.7.0" }, "dependencies": { - "discord-api-types": { - "version": "0.26.1" - }, "form-data": { "version": "4.0.0", "requires": { @@ -6425,7 +6447,7 @@ }, "erlpack": { "version": "git+ssh://git@github.com/discord/erlpack.git#3b793a333dd3f6a140b9168ea91e9fa9660753ce", - "from": "erlpack@github:discord/erlpack.git", + "from": "erlpack@github:discord/erlpack", "requires": { "bindings": "^1.5.0", "nan": "^2.15.0" @@ -6513,6 +6535,11 @@ "vary": "~1.1.2" } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "fast-diff": { "version": "1.2.0", "dev": true @@ -8117,7 +8144,9 @@ "requires": {} }, "ts-mixer": { - "version": "6.0.1" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", + "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" }, "tsc-watch": { "version": "5.0.2", @@ -8491,7 +8520,9 @@ } }, "ws": { - "version": "8.5.0", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", + "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", "requires": {} }, "xdg-basedir": { diff --git a/backend/package.json b/backend/package.json index fbdac67f..4761ec2e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,8 +28,8 @@ "cors": "^2.8.5", "cross-env": "^5.2.0", "deep-diff": "^1.0.2", - "discord-api-types": "^0.31.0", - "discord.js": "^13.6.0", + "discord-api-types": "^0.33.1", + "discord.js": "^13.8.0", "dotenv": "^4.0.0", "emoji-regex": "^8.0.0", "erlpack": "github:discord/erlpack", diff --git a/backend/src/commandTypes.ts b/backend/src/commandTypes.ts index e3f1ece3..75c6225c 100644 --- a/backend/src/commandTypes.ts +++ b/backend/src/commandTypes.ts @@ -1,4 +1,4 @@ -import { GuildChannel, GuildMember, Snowflake, Util, User } from "discord.js"; +import { GuildChannel, GuildMember, Snowflake, Util, User, GuildTextBasedChannel } from "discord.js"; import { baseCommandParameterTypeHelpers, baseTypeConverters, CommandContext, TypeConversionError } from "knub"; import { createTypeHelper } from "knub-command-manager"; import { @@ -14,6 +14,8 @@ 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, @@ -100,6 +102,11 @@ export const commandTypes = { return value; }, + + guildTextBasedChannel(value: string, context: CommandContext) { + // FIXME: Remove once Knub's types have been fixed + return baseTypeConverters.textChannel(value, context) as GuildTextBasedChannel; + }, }; export const commandTypeHelpers = { @@ -113,4 +120,5 @@ export const commandTypeHelpers = { anyId: createTypeHelper>(commandTypes.anyId), regex: createTypeHelper(commandTypes.regex), timezone: createTypeHelper(commandTypes.timezone), + guildTextBasedChannel: createTypeHelper(commandTypes.guildTextBasedChannel), }; diff --git a/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts b/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts index 1ab5e123..17b377b2 100644 --- a/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts +++ b/backend/src/plugins/AutoReactions/commands/NewAutoReactionsCmd.ts @@ -15,7 +15,7 @@ export const NewAutoReactionsCmd = autoReactionsCmd({ usage: "!auto_reactions 629990160477585428 👍 👎", signature: { - channel: ct.channel(), + channel: ct.guildTextBasedChannel(), reactions: ct.string({ rest: true }), }, @@ -23,7 +23,7 @@ export const NewAutoReactionsCmd = autoReactionsCmd({ const finalReactions: string[] = []; const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; - const missingPermissions = getMissingChannelPermissions(me, args.channel as GuildChannel, requiredPermissions); + const missingPermissions = getMissingChannelPermissions(me, args.channel, requiredPermissions); if (missingPermissions) { sendErrorMessage( pluginData, diff --git a/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts b/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts index a7d762a2..a959cdcf 100644 --- a/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts +++ b/backend/src/plugins/AutoReactions/events/AddReactionsEvt.ts @@ -1,4 +1,4 @@ -import { GuildChannel, Permissions } from "discord.js"; +import { GuildChannel, GuildTextBasedChannel, Permissions } from "discord.js"; import { LogType } from "../../../data/LogType"; import { isDiscordAPIError } from "../../../utils"; import { getMissingChannelPermissions } from "../../../utils/getMissingChannelPermissions"; @@ -16,13 +16,21 @@ export const AddReactionsEvt = autoReactionsEvt({ allowSelf: true, async listener({ pluginData, args: { message } }) { + const channel = (await message.guild?.channels.fetch(message.channelId)) as + | GuildTextBasedChannel + | null + | undefined; + if (!channel) { + return; + } + let autoReaction: AutoReaction | null = null; - const lock = await pluginData.locks.acquire(`auto-reactions-${message.channel.id}`); - if (pluginData.state.cache.has(message.channel.id)) { - autoReaction = pluginData.state.cache.get(message.channel.id) ?? null; + const lock = await pluginData.locks.acquire(`auto-reactions-${channel.id}`); + if (pluginData.state.cache.has(channel.id)) { + autoReaction = pluginData.state.cache.get(channel.id) ?? null; } else { - autoReaction = (await pluginData.state.autoReactions.getForChannel(message.channel.id)) ?? null; - pluginData.state.cache.set(message.channel.id, autoReaction); + autoReaction = (await pluginData.state.autoReactions.getForChannel(channel.id)) ?? null; + pluginData.state.cache.set(channel.id, autoReaction); } lock.unlock(); @@ -32,17 +40,11 @@ export const AddReactionsEvt = autoReactionsEvt({ const me = pluginData.guild.members.cache.get(pluginData.client.user!.id)!; if (me) { - const missingPermissions = getMissingChannelPermissions( - me, - message.channel as GuildChannel, - readChannelPermissions | p.ADD_REACTIONS, - ); + const missingPermissions = getMissingChannelPermissions(me, channel, readChannelPermissions | p.ADD_REACTIONS); if (missingPermissions) { const logs = pluginData.getPlugin(LogsPlugin); logs.logBotAlert({ - body: `Cannot apply auto-reactions in <#${message.channel.id}>. ${missingPermissionError( - missingPermissions, - )}`, + body: `Cannot apply auto-reactions in <#${channel.id}>. ${missingPermissionError(missingPermissions)}`, }); return; } @@ -56,11 +58,11 @@ export const AddReactionsEvt = autoReactionsEvt({ const logs = pluginData.getPlugin(LogsPlugin); if (e.code === 10008) { logs.logBotAlert({ - body: `Could not apply auto-reactions in <#${message.channel.id}> for message \`${message.id}\`. Make sure nothing is deleting the message before the reactions are applied.`, + body: `Could not apply auto-reactions in <#${channel.id}> for message \`${message.id}\`. Make sure nothing is deleting the message before the reactions are applied.`, }); } else { logs.logBotAlert({ - body: `Could not apply auto-reactions in <#${message.channel.id}> for message \`${message.id}\`. Error code ${e.code}.`, + body: `Could not apply auto-reactions in <#${channel.id}> for message \`${message.id}\`. Error code ${e.code}.`, }); } diff --git a/backend/src/plugins/Automod/actions/alert.ts b/backend/src/plugins/Automod/actions/alert.ts index 7c96dea5..8bfce04d 100644 --- a/backend/src/plugins/Automod/actions/alert.ts +++ b/backend/src/plugins/Automod/actions/alert.ts @@ -38,7 +38,7 @@ export const AlertAction = automodAction({ const channel = pluginData.guild.channels.cache.get(actionConfig.channel as Snowflake); const logs = pluginData.getPlugin(LogsPlugin); - if (channel && channel instanceof TextChannel) { + if (channel?.isText()) { const text = actionConfig.text; const theMessageLink = contexts[0].message && messageLink(pluginData.guild.id, contexts[0].message.channel_id, contexts[0].message.id); diff --git a/backend/src/plugins/Automod/actions/reply.ts b/backend/src/plugins/Automod/actions/reply.ts index 52aa491d..865ef127 100644 --- a/backend/src/plugins/Automod/actions/reply.ts +++ b/backend/src/plugins/Automod/actions/reply.ts @@ -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 instanceof TextChannel || channel instanceof ThreadChannel; + return channel?.isText(); }); const contextsByChannelId = contextsWithTextChannels.reduce((map: Map, context) => { diff --git a/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts b/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts index 5ac49703..9576d149 100644 --- a/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts +++ b/backend/src/plugins/Automod/functions/resolveActionContactMethods.ts @@ -19,7 +19,7 @@ export function resolveActionContactMethods( } const channel = pluginData.guild.channels.cache.get(actionConfig.notifyChannel as Snowflake); - if (!(channel instanceof TextChannel || channel instanceof ThreadChannel)) { + if (!channel?.isText()) { throw new RecoverablePluginError(ERRORS.INVALID_USER_NOTIFICATION_CHANNEL); } diff --git a/backend/src/plugins/Censor/util/censorMessage.ts b/backend/src/plugins/Censor/util/censorMessage.ts index a9549974..6375da79 100644 --- a/backend/src/plugins/Censor/util/censorMessage.ts +++ b/backend/src/plugins/Censor/util/censorMessage.ts @@ -1,4 +1,4 @@ -import { BaseGuildTextChannel, Snowflake, TextChannel, ThreadChannel } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, Snowflake, TextChannel, ThreadChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; import { SavedMessage } from "../../../data/entities/SavedMessage"; @@ -22,9 +22,7 @@ export async function censorMessage( } const user = await resolveUser(pluginData.client, savedMessage.user_id); - const channel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake)! as - | BaseGuildTextChannel - | ThreadChannel; + const channel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake)! as GuildTextBasedChannel; pluginData.getPlugin(LogsPlugin).logCensor({ user, diff --git a/backend/src/plugins/Counters/commands/ViewCounterCmd.ts b/backend/src/plugins/Counters/commands/ViewCounterCmd.ts index 26a2fe7c..9a4cbca5 100644 --- a/backend/src/plugins/Counters/commands/ViewCounterCmd.ts +++ b/backend/src/plugins/Counters/commands/ViewCounterCmd.ts @@ -20,17 +20,17 @@ export const ViewCounterCmd = typedGuildCommand()({ }, { counterName: ct.string(), - channel: ct.textChannel(), + channel: ct.guildTextBasedChannel(), }, { counterName: ct.string(), - channel: ct.textChannel(), + channel: ct.guildTextBasedChannel(), user: ct.resolvedUser(), }, { counterName: ct.string(), user: ct.resolvedUser(), - channel: ct.textChannel(), + channel: ct.guildTextBasedChannel(), }, ], @@ -68,7 +68,7 @@ export const ViewCounterCmd = typedGuildCommand()({ } const potentialChannel = pluginData.guild.channels.resolve(reply.content as Snowflake); - if (!potentialChannel || !(potentialChannel instanceof TextChannel)) { + if (!potentialChannel?.isText()) { sendErrorMessage(pluginData, message.channel, "Channel is not a text channel, cancelling"); return; } diff --git a/backend/src/plugins/InternalPoster/functions/editMessage.ts b/backend/src/plugins/InternalPoster/functions/editMessage.ts index 79165a6d..a0fc7169 100644 --- a/backend/src/plugins/InternalPoster/functions/editMessage.ts +++ b/backend/src/plugins/InternalPoster/functions/editMessage.ts @@ -1,4 +1,11 @@ -import { Message, MessageOptions, NewsChannel, TextChannel, WebhookClient } from "discord.js"; +import { + Message, + MessageEditOptions, + NewsChannel, + TextChannel, + WebhookClient, + WebhookEditMessageOptions, +} from "discord.js"; import { GuildPluginData } from "knub"; import { InternalPosterPluginType } from "../types"; import { isDiscordAPIError, noop } from "../../../utils"; @@ -9,7 +16,7 @@ import { isDiscordAPIError, noop } from "../../../utils"; export async function editMessage( pluginData: GuildPluginData, message: Message, - content: MessageOptions, + content: MessageEditOptions & WebhookEditMessageOptions, ): Promise { if (!(message.channel instanceof TextChannel || message.channel instanceof NewsChannel)) { return; diff --git a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts index cc582495..aea1523c 100644 --- a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts +++ b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookClientForChannel.ts @@ -1,11 +1,11 @@ import { GuildPluginData } from "knub"; import { InternalPosterPluginType } from "../types"; -import { NewsChannel, TextChannel, WebhookClient } from "discord.js"; -import { getOrCreateWebhookForChannel } from "./getOrCreateWebhookForChannel"; +import { WebhookClient } from "discord.js"; +import { getOrCreateWebhookForChannel, WebhookableChannel } from "./getOrCreateWebhookForChannel"; export async function getOrCreateWebhookClientForChannel( pluginData: GuildPluginData, - channel: TextChannel | NewsChannel, + channel: WebhookableChannel, ): Promise { if (!pluginData.state.webhookClientCache.has(channel.id)) { const webhookInfo = await getOrCreateWebhookForChannel(pluginData, channel); diff --git a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts index 8acd2887..9785271e 100644 --- a/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts +++ b/backend/src/plugins/InternalPoster/functions/getOrCreateWebhookForChannel.ts @@ -1,17 +1,20 @@ import { GuildPluginData } from "knub"; import { InternalPosterPluginType } from "../types"; -import { NewsChannel, Permissions, TextChannel } from "discord.js"; +import { AnyChannel, GuildChannel, MessageManager, NewsChannel, Permissions, TextChannel } from "discord.js"; import { isDiscordAPIError } from "../../../utils"; type WebhookInfo = [id: string, token: string]; +export type WebhookableChannel = Extract any }>; + +export function channelIsWebhookable(channel: AnyChannel): channel is WebhookableChannel { + return "createWebhook" in channel; +} + export async function getOrCreateWebhookForChannel( pluginData: GuildPluginData, - channel: TextChannel | NewsChannel, + channel: WebhookableChannel, ): Promise { - // tslint:disable-next-line:no-console FIXME: Here for debugging purposes - console.log(`getOrCreateWebhookForChannel(${channel.id})`); - // Database cache const fromDb = await pluginData.state.webhooks.findByChannelId(channel.id); if (fromDb) { diff --git a/backend/src/plugins/InternalPoster/functions/sendMessage.ts b/backend/src/plugins/InternalPoster/functions/sendMessage.ts index bc52ac05..283cc3dc 100644 --- a/backend/src/plugins/InternalPoster/functions/sendMessage.ts +++ b/backend/src/plugins/InternalPoster/functions/sendMessage.ts @@ -1,7 +1,7 @@ -import { Message, MessageOptions, NewsChannel, TextChannel, WebhookClient } from "discord.js"; +import { GuildTextBasedChannel, Message, MessageOptions, NewsChannel, TextChannel, WebhookClient } from "discord.js"; import { GuildPluginData } from "knub"; import { InternalPosterPluginType } from "../types"; -import { getOrCreateWebhookForChannel } from "./getOrCreateWebhookForChannel"; +import { channelIsWebhookable, getOrCreateWebhookForChannel } from "./getOrCreateWebhookForChannel"; import { APIMessage } from "discord-api-types"; import { isDiscordAPIError } from "../../../utils"; import { getOrCreateWebhookClientForChannel } from "./getOrCreateWebhookClientForChannel"; @@ -12,7 +12,7 @@ export type InternalPosterMessageResult = { }; async function sendDirectly( - channel: TextChannel | NewsChannel, + channel: GuildTextBasedChannel, content: MessageOptions, ): Promise { return channel.send(content).then((message) => ({ @@ -26,17 +26,26 @@ async function sendDirectly( */ export async function sendMessage( pluginData: GuildPluginData, - channel: TextChannel | NewsChannel, + channel: GuildTextBasedChannel, content: MessageOptions, ): Promise { return pluginData.state.queue.add(async () => { - const webhookClient = await getOrCreateWebhookClientForChannel(pluginData, channel); + let webhookClient: WebhookClient | null = null; + let threadId: string | undefined; + if (channelIsWebhookable(channel)) { + webhookClient = await getOrCreateWebhookClientForChannel(pluginData, channel); + } else if (channel.isThread() && channelIsWebhookable(channel.parent!)) { + webhookClient = await getOrCreateWebhookClientForChannel(pluginData, channel.parent!); + threadId = channel.id; + } + if (!webhookClient) { return sendDirectly(channel, content); } return webhookClient .send({ + threadId, ...content, ...(pluginData.client.user && { username: pluginData.client.user.username, @@ -50,7 +59,7 @@ export async function sendMessage( .catch(async (err) => { // Unknown Webhook if (isDiscordAPIError(err) && err.code === 10015) { - await pluginData.state.webhooks.delete(webhookClient.id); + await pluginData.state.webhooks.delete(webhookClient!.id); pluginData.state.webhookClientCache.delete(channel.id); // Fallback to regular message for this log message diff --git a/backend/src/plugins/Logs/logFunctions/logCensor.ts b/backend/src/plugins/Logs/logFunctions/logCensor.ts index 2385273f..b0131657 100644 --- a/backend/src/plugins/Logs/logFunctions/logCensor.ts +++ b/backend/src/plugins/Logs/logFunctions/logCensor.ts @@ -3,7 +3,7 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, @@ -15,7 +15,7 @@ import { deactivateMentions, disableCodeBlocks } from "knub/dist/helpers"; interface LogCensorData { user: User | UnknownUser; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; reason: string; message: SavedMessage; } diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts b/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts index 1b752866..6a2d642c 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDelete.ts @@ -3,7 +3,7 @@ import { FORMAT_NO_TIMESTAMP, LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, @@ -16,7 +16,7 @@ import { UnknownUser, useMediaUrls } from "../../../utils"; interface LogMessageDeleteData { user: User | UnknownUser; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; message: SavedMessage; } diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts index 6b980951..ed49d3e4 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBare.ts @@ -3,12 +3,12 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel } from "discord.js"; import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; interface LogMessageDeleteBareData { messageId: string; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; } export function logMessageDeleteBare(pluginData: GuildPluginData, data: LogMessageDeleteBareData) { diff --git a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts index 01f38279..b57b526b 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageDeleteBulk.ts @@ -3,13 +3,13 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel } from "discord.js"; import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects"; interface LogMessageDeleteBulkData { count: number; authorIds: string[]; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; archiveUrl: string; } diff --git a/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts b/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts index a77ce103..3aee8dbe 100644 --- a/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts +++ b/backend/src/plugins/Logs/logFunctions/logMessageEdit.ts @@ -3,7 +3,7 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, savedMessageToTemplateSafeSavedMessage, @@ -14,7 +14,7 @@ import { UnknownUser } from "../../../utils"; interface LogMessageEditData { user: User | UnknownUser; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; before: SavedMessage; after: SavedMessage; } diff --git a/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts b/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts index 52aa83ca..3000a906 100644 --- a/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logPostedScheduledMessage.ts @@ -3,12 +3,12 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; interface LogPostedScheduledMessageData { author: User; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; messageId: string; } diff --git a/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts b/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts index d17b5a55..7502b6ce 100644 --- a/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logRepeatedMessage.ts @@ -3,12 +3,12 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; interface LogRepeatedMessageData { author: User; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; datetime: string; date: string; time: string; diff --git a/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts b/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts index 27408798..54315e6f 100644 --- a/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logScheduledMessage.ts @@ -3,12 +3,12 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; interface LogScheduledMessageData { author: User; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; datetime: string; date: string; time: string; diff --git a/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts b/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts index 55d24df3..03272f5f 100644 --- a/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts +++ b/backend/src/plugins/Logs/logFunctions/logScheduledRepeatedMessage.ts @@ -3,12 +3,12 @@ import { LogsPluginType } from "../types"; import { LogType } from "../../../data/LogType"; import { log } from "../util/log"; import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter"; -import { BaseGuildTextChannel, ThreadChannel, User } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js"; import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; interface LogScheduledRepeatedMessageData { author: User; - channel: BaseGuildTextChannel | ThreadChannel; + channel: GuildTextBasedChannel; datetime: string; date: string; time: string; diff --git a/backend/src/plugins/Logs/util/log.ts b/backend/src/plugins/Logs/util/log.ts index 37fca40a..f95e1f07 100644 --- a/backend/src/plugins/Logs/util/log.ts +++ b/backend/src/plugins/Logs/util/log.ts @@ -83,7 +83,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 || !(channel instanceof TextChannel)) continue; + if (!channel?.isText()) 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; diff --git a/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts b/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts index 1dec0101..36ad96f1 100644 --- a/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts +++ b/backend/src/plugins/Logs/util/onMessageDeleteBulk.ts @@ -1,4 +1,4 @@ -import { BaseGuildTextChannel, Snowflake, ThreadChannel } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, Snowflake, ThreadChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { SavedMessage } from "../../../data/entities/SavedMessage"; import { LogType } from "../../../data/LogType"; @@ -12,9 +12,9 @@ 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 efd92d28..13d611ed 100644 --- a/backend/src/plugins/Logs/util/onMessageUpdate.ts +++ b/backend/src/plugins/Logs/util/onMessageUpdate.ts @@ -1,4 +1,4 @@ -import { BaseGuildTextChannel, MessageEmbed, Snowflake, ThreadChannel } from "discord.js"; +import { BaseGuildTextChannel, GuildTextBasedChannel, MessageEmbed, Snowflake, ThreadChannel } from "discord.js"; import { GuildPluginData } from "knub"; import cloneDeep from "lodash.clonedeep"; import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; @@ -49,9 +49,7 @@ export async function onMessageUpdate( } const user = await resolveUser(pluginData.client, savedMessage.user_id); - const channel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake)! as - | BaseGuildTextChannel - | ThreadChannel; + const channel = pluginData.guild.channels.resolve(savedMessage.channel_id as Snowflake)! as GuildTextBasedChannel; logMessageEdit(pluginData, { user, diff --git a/backend/src/plugins/Mutes/functions/muteUser.ts b/backend/src/plugins/Mutes/functions/muteUser.ts index 68908e30..3956be8a 100644 --- a/backend/src/plugins/Mutes/functions/muteUser.ts +++ b/backend/src/plugins/Mutes/functions/muteUser.ts @@ -183,9 +183,10 @@ export async function muteUser( } const useChannel = existingMute ? config.message_on_update : config.message_on_mute; - const channel = - config.message_channel && pluginData.guild.channels.cache.get(config.message_channel as Snowflake); - if (useChannel && channel instanceof TextChannel) { + const channel = config.message_channel + ? pluginData.guild.channels.cache.get(config.message_channel as Snowflake) + : null; + if (useChannel && channel?.isText()) { contactMethods.push({ type: "channel", channel }); } } diff --git a/backend/src/plugins/Post/util/actualPostCmd.ts b/backend/src/plugins/Post/util/actualPostCmd.ts index 054f655c..a63b1312 100644 --- a/backend/src/plugins/Post/util/actualPostCmd.ts +++ b/backend/src/plugins/Post/util/actualPostCmd.ts @@ -1,4 +1,4 @@ -import { Channel, Message, NewsChannel, TextChannel, ThreadChannel } from "discord.js"; +import { Channel, GuildTextBasedChannel, Message, NewsChannel, TextChannel, ThreadChannel } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import moment from "moment-timezone"; @@ -20,7 +20,7 @@ const MAX_REPEAT_UNTIL = moment.utc().add(100, "years"); export async function actualPostCmd( pluginData: GuildPluginData, msg: Message, - targetChannel: Channel, + targetChannel: GuildTextBasedChannel, content: StrictMessageContent, opts: { "enable-mentions"?: boolean; @@ -30,12 +30,8 @@ export async function actualPostCmd( "repeat-times"?: number; } = {}, ) { - if ( - !(targetChannel instanceof TextChannel) && - !(targetChannel instanceof NewsChannel) && - !(targetChannel instanceof ThreadChannel) - ) { - msg.channel.send(errorMessage("Specified channel is not a text channel, announcement channel, or thread")); + if (!targetChannel.isText()) { + msg.channel.send(errorMessage("Specified channel is not a text-based channel")); return; } diff --git a/backend/src/plugins/Post/util/postMessage.ts b/backend/src/plugins/Post/util/postMessage.ts index 0395867c..4a10f809 100644 --- a/backend/src/plugins/Post/util/postMessage.ts +++ b/backend/src/plugins/Post/util/postMessage.ts @@ -1,4 +1,12 @@ -import { Message, MessageAttachment, MessageOptions, NewsChannel, TextChannel, ThreadChannel } from "discord.js"; +import { + GuildTextBasedChannel, + Message, + MessageAttachment, + MessageOptions, + NewsChannel, + TextChannel, + ThreadChannel, +} from "discord.js"; import fs from "fs"; import { GuildPluginData } from "knub"; import { downloadFile } from "../../../utils"; @@ -9,7 +17,7 @@ const fsp = fs.promises; export async function postMessage( pluginData: GuildPluginData, - channel: TextChannel | NewsChannel | ThreadChannel, + channel: GuildTextBasedChannel, content: MessageOptions, attachments: MessageAttachment[] = [], enableMentions: boolean = false, diff --git a/backend/src/plugins/Reminders/RemindersPlugin.ts b/backend/src/plugins/Reminders/RemindersPlugin.ts index 67a97035..62f6b29a 100644 --- a/backend/src/plugins/Reminders/RemindersPlugin.ts +++ b/backend/src/plugins/Reminders/RemindersPlugin.ts @@ -56,7 +56,7 @@ export const RemindersPlugin = zeppelinGuildPlugin()({ }, beforeUnload(pluginData) { - pluginData.state.unregisterGuildEventListener(); + pluginData.state.unregisterGuildEventListener?.(); pluginData.state.unloaded = true; }, }); diff --git a/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts b/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts index 9d050b3f..5e8e916a 100644 --- a/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getChannelInfoEmbed.ts @@ -70,13 +70,13 @@ export async function getChannelInfoEmbed( channelName = channel.name; } - const createdAt = moment.utc(channel.createdAt, "x"); + const createdAt = moment.utc(channel.createdAt!, "x"); const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin); const tzCreatedAt = requestMemberId ? await timeAndDate.inMemberTz(requestMemberId, createdAt) : timeAndDate.inGuildTz(createdAt); const prettyCreatedAt = tzCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime")); - const channelAge = humanizeDuration(Date.now() - channel.createdTimestamp, { + const channelAge = humanizeDuration(Date.now() - channel.createdTimestamp!, { largest: 2, round: true, }); @@ -134,8 +134,9 @@ export async function getChannelInfoEmbed( const memberCount = thread.memberCount ?? thread.members.cache.size; const owner = await thread.fetchOwner().catch(() => null); const ownerMention = owner?.user ? verboseUserMention(owner.user) : "Unknown#0000"; - const autoArchiveDuration = thread.autoArchiveDuration === "MAX" ? 10080 : thread.autoArchiveDuration; // TODO: Boost level check - const humanizedArchiveTime = `Archive duration: **${humanizeDuration((autoArchiveDuration ?? 0) * MINUTES)}**`; + const humanizedArchiveTime = `Archive duration: **${humanizeDuration( + (thread.autoArchiveDuration ?? 0) * MINUTES, + )}**`; embed.fields.push({ name: preEmbedPadding + "Thread information", diff --git a/backend/src/utils.ts b/backend/src/utils.ts index fb505743..e90c38e0 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -6,8 +6,10 @@ import { Guild, GuildAuditLogs, GuildAuditLogsEntry, + GuildBasedChannel, GuildChannel, GuildMember, + GuildTextBasedChannel, Invite, InviteGuild, LimitedCollection, @@ -1085,7 +1087,7 @@ export type CustomEmoji = { id: string; } & Emoji; -export type UserNotificationMethod = { type: "dm" } | { type: "channel"; channel: TextChannel | ThreadChannel }; +export type UserNotificationMethod = { type: "dm" } | { type: "channel"; channel: GuildTextBasedChannel }; export const disableUserNotificationStrings = ["no", "none", "off"]; diff --git a/backend/src/utils/canReadChannel.ts b/backend/src/utils/canReadChannel.ts index 94268c4b..043a72d2 100644 --- a/backend/src/utils/canReadChannel.ts +++ b/backend/src/utils/canReadChannel.ts @@ -1,8 +1,8 @@ -import { GuildChannel, GuildMember } from "discord.js"; +import { GuildMember, GuildTextBasedChannel } from "discord.js"; import { getMissingChannelPermissions } from "./getMissingChannelPermissions"; import { readChannelPermissions } from "./readChannelPermissions"; -export function canReadChannel(channel: GuildChannel, member: GuildMember) { +export function canReadChannel(channel: GuildTextBasedChannel, member: GuildMember) { // Not missing permissions required to read the channel = can read channel return !getMissingChannelPermissions(member, channel, readChannelPermissions); } diff --git a/backend/src/utils/getMissingChannelPermissions.ts b/backend/src/utils/getMissingChannelPermissions.ts index a1a7be9a..25f1e9e7 100644 --- a/backend/src/utils/getMissingChannelPermissions.ts +++ b/backend/src/utils/getMissingChannelPermissions.ts @@ -1,4 +1,4 @@ -import { GuildChannel, GuildMember, ThreadChannel } from "discord.js"; +import { GuildMember, GuildTextBasedChannel } from "discord.js"; import { getMissingPermissions } from "./getMissingPermissions"; /** @@ -7,7 +7,7 @@ import { getMissingPermissions } from "./getMissingPermissions"; */ export function getMissingChannelPermissions( member: GuildMember, - channel: GuildChannel | ThreadChannel, + channel: GuildTextBasedChannel, requiredPermissions: number | bigint, ): bigint { const memberChannelPermissions = channel.permissionsFor(member.id); diff --git a/backend/src/utils/resolveMessageTarget.ts b/backend/src/utils/resolveMessageTarget.ts index a7c3043c..4b24b779 100644 --- a/backend/src/utils/resolveMessageTarget.ts +++ b/backend/src/utils/resolveMessageTarget.ts @@ -1,4 +1,4 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { GuildTextBasedChannel, Snowflake, TextChannel } from "discord.js"; import { GuildPluginData } from "knub"; import { getChannelIdFromMessageId } from "../data/getChannelIdFromMessageId"; import { isSnowflake } from "../utils"; @@ -7,7 +7,7 @@ const channelAndMessageIdRegex = /^(\d+)[\-\/](\d+)$/; const messageLinkRegex = /^https:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/\d+\/(\d+)\/(\d+)$/i; export interface MessageTarget { - channel: TextChannel; + channel: GuildTextBasedChannel; messageId: string; } @@ -47,7 +47,7 @@ export async function resolveMessageTarget(pluginData: GuildPluginData, val } const channel = pluginData.guild.channels.resolve(result.channelId as Snowflake); - if (!channel || !(channel instanceof TextChannel)) { + if (!channel?.isText()) { return null; }