diff --git a/backend/src/commandTypes.ts b/backend/src/commandTypes.ts index 29a69d11..e3f1ece3 100644 --- a/backend/src/commandTypes.ts +++ b/backend/src/commandTypes.ts @@ -1,11 +1,9 @@ -import { GuildChannel, GuildMember, Snowflake, User } from "discord.js"; +import { GuildChannel, GuildMember, Snowflake, Util, User } from "discord.js"; import { baseCommandParameterTypeHelpers, baseTypeConverters, CommandContext, TypeConversionError } from "knub"; import { createTypeHelper } from "knub-command-manager"; import { channelMentionRegex, convertDelayStringToMS, - disableCodeBlocks, - disableInlineCode, isValidSnowflake, resolveMember, resolveUser, @@ -32,7 +30,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 \`${disableCodeBlocks(value)}\` was not found`); + throw new TypeConversionError(`User \`${Util.escapeCodeBlock(value)}\` was not found`); } return result; }, @@ -40,7 +38,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: \`${disableCodeBlocks(value)}\``); + throw new TypeConversionError(`Invalid user: \`${Util.escapeCodeBlock(value)}\``); } return result; }, @@ -53,7 +51,7 @@ export const commandTypes = { const result = await resolveMember(context.pluginData.client, context.message.channel.guild, value); if (result == null) { throw new TypeConversionError( - `Member \`${disableCodeBlocks(value)}\` was not found or they have left the server`, + `Member \`${Util.escapeCodeBlock(value)}\` was not found or they have left the server`, ); } return result; @@ -64,7 +62,7 @@ export const commandTypes = { const result = await resolveMessageTarget(context.pluginData, value); if (!result) { - throw new TypeConversionError(`Unknown message \`${disableInlineCode(value)}\``); + throw new TypeConversionError(`Unknown message \`${Util.escapeInlineCode(value)}\``); } return result; @@ -84,20 +82,20 @@ export const commandTypes = { return value as Snowflake; } - throw new TypeConversionError(`Could not parse ID: \`${disableInlineCode(value)}\``); + throw new TypeConversionError(`Could not parse ID: \`${Util.escapeInlineCode(value)}\``); }, regex(value: string, context: CommandContext): RegExp { try { return inputPatternToRegExp(value); } catch (e) { - throw new TypeConversionError(`Could not parse RegExp: \`${disableInlineCode(e.message)}\``); + throw new TypeConversionError(`Could not parse RegExp: \`${Util.escapeInlineCode(e.message)}\``); } }, timezone(value: string) { if (!isValidTimezone(value)) { - throw new TypeConversionError(`Invalid timezone: ${disableInlineCode(value)}`); + throw new TypeConversionError(`Invalid timezone: ${Util.escapeInlineCode(value)}`); } return value; diff --git a/backend/src/plugins/Automod/triggers/matchAttachmentType.ts b/backend/src/plugins/Automod/triggers/matchAttachmentType.ts index d69cc9a8..ba8e4e59 100644 --- a/backend/src/plugins/Automod/triggers/matchAttachmentType.ts +++ b/backend/src/plugins/Automod/triggers/matchAttachmentType.ts @@ -1,6 +1,6 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { Snowflake, TextChannel, Util } from "discord.js"; import * as t from "io-ts"; -import { asSingleLine, disableInlineCode, messageSummary, verboseChannelMention } from "../../../utils"; +import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils"; import { automodTrigger } from "../helpers"; interface MatchResultType { @@ -73,7 +73,7 @@ export const MatchAttachmentTypeTrigger = automodTrigger()({ return ( asSingleLine(` - Matched attachment type \`${disableInlineCode(matchResult.extra.matchedType)}\` + Matched attachment type \`${Util.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 193a184c..2c70387c 100644 --- a/backend/src/plugins/Automod/triggers/matchLinks.ts +++ b/backend/src/plugins/Automod/triggers/matchLinks.ts @@ -1,7 +1,8 @@ +import { Util } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; import * as t from "io-ts"; import { allowTimeout } from "../../../RegExpRunner"; -import { disableInlineCode, getUrlsInString, tNullable } from "../../../utils"; +import { getUrlsInString, tNullable } from "../../../utils"; import { TRegex } from "../../../validatorUtils"; import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary"; import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage"; @@ -129,6 +130,6 @@ export const MatchLinksTrigger = automodTrigger()({ renderMatchInformation({ pluginData, contexts, matchResult }) { const partialSummary = getTextMatchPartialSummary(pluginData, matchResult.extra.type, contexts[0]); - return `Matched link \`${disableInlineCode(matchResult.extra.link)}\` in ${partialSummary}`; + return `Matched link \`${Util.escapeInlineCode(matchResult.extra.link)}\` in ${partialSummary}`; }, }); diff --git a/backend/src/plugins/Automod/triggers/matchRegex.ts b/backend/src/plugins/Automod/triggers/matchRegex.ts index 5d585f08..3e0b2e38 100644 --- a/backend/src/plugins/Automod/triggers/matchRegex.ts +++ b/backend/src/plugins/Automod/triggers/matchRegex.ts @@ -1,6 +1,6 @@ +import { Util } from "discord.js"; import * as t from "io-ts"; import { allowTimeout } from "../../../RegExpRunner"; -import { disableInlineCode } from "../../../utils"; import { normalizeText } from "../../../utils/normalizeText"; import { stripMarkdown } from "../../../utils/stripMarkdown"; import { TRegex } from "../../../validatorUtils"; @@ -72,6 +72,6 @@ export const MatchRegexTrigger = automodTrigger()({ renderMatchInformation({ pluginData, contexts, matchResult }) { const partialSummary = getTextMatchPartialSummary(pluginData, matchResult.extra.type, contexts[0]); - return `Matched regex \`${disableInlineCode(matchResult.extra.pattern)}\` in ${partialSummary}`; + return `Matched regex \`${Util.escapeInlineCode(matchResult.extra.pattern)}\` in ${partialSummary}`; }, }); diff --git a/backend/src/plugins/Automod/triggers/matchWords.ts b/backend/src/plugins/Automod/triggers/matchWords.ts index 2a011546..387f2208 100644 --- a/backend/src/plugins/Automod/triggers/matchWords.ts +++ b/backend/src/plugins/Automod/triggers/matchWords.ts @@ -1,6 +1,6 @@ +import { Util } from "discord.js"; import escapeStringRegexp from "escape-string-regexp"; import * as t from "io-ts"; -import { disableInlineCode } from "../../../utils"; import { normalizeText } from "../../../utils/normalizeText"; import { stripMarkdown } from "../../../utils/stripMarkdown"; import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary"; @@ -89,6 +89,6 @@ export const MatchWordsTrigger = automodTrigger()({ renderMatchInformation({ pluginData, contexts, matchResult }) { const partialSummary = getTextMatchPartialSummary(pluginData, matchResult.extra.type, contexts[0]); - return `Matched word \`${disableInlineCode(matchResult.extra.word)}\` in ${partialSummary}`; + return `Matched word \`${Util.escapeInlineCode(matchResult.extra.word)}\` in ${partialSummary}`; }, }); diff --git a/backend/src/plugins/Logs/LogsPlugin.ts b/backend/src/plugins/Logs/LogsPlugin.ts index e61d0857..d4285de8 100644 --- a/backend/src/plugins/Logs/LogsPlugin.ts +++ b/backend/src/plugins/Logs/LogsPlugin.ts @@ -7,7 +7,6 @@ import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { LogType } from "../../data/LogType"; import { logger } from "../../logger"; import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners"; -import { disableCodeBlocks } from "../../utils"; import { CasesPlugin } from "../Cases/CasesPlugin"; import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin"; import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint"; @@ -37,6 +36,7 @@ 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"; const defaultOptions: PluginOptions = { config: { @@ -147,7 +147,7 @@ export const LogsPlugin = zeppelinGuildPlugin()({ The following regex has taken longer than ${timeoutMs}ms for ${failedTimes} times and has been temporarily disabled: `.trim() + "\n```" + - disableCodeBlocks(regexSource) + + Util.escapeCodeBlock(regexSource) + "```", }); }; diff --git a/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts b/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts index 65e76041..7ac59698 100644 --- a/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts +++ b/backend/src/plugins/Post/commands/ScheduledPostsListCmd.ts @@ -1,13 +1,7 @@ +import { Util } from "discord.js"; import humanizeDuration from "humanize-duration"; import moment from "moment-timezone"; -import { - createChunkedMessage, - DBDateFormat, - deactivateMentions, - disableCodeBlocks, - sorter, - trimLines, -} from "../../../utils"; +import { createChunkedMessage, DBDateFormat, deactivateMentions, sorter, trimLines } from "../../../utils"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { postCmd } from "../types"; @@ -33,7 +27,7 @@ export const ScheduledPostsListCmd = postCmd({ const isTruncated = previewText.length > SCHEDULED_POST_PREVIEW_TEXT_LENGTH; - previewText = disableCodeBlocks(deactivateMentions(previewText)) + previewText = Util.escapeCodeBlock(deactivateMentions(previewText)) .replace(/\s+/g, " ") .slice(0, SCHEDULED_POST_PREVIEW_TEXT_LENGTH); @@ -61,7 +55,7 @@ export const ScheduledPostsListCmd = postCmd({ const finalMessage = trimLines(` ${postLines.join("\n")} - + Use \`scheduled_posts \` to view a scheduled post in full Use \`scheduled_posts delete \` to delete a scheduled post `); diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts index 75835e6b..c9a31ddd 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeClearCmd.ts @@ -1,7 +1,8 @@ +import { Util } from "discord.js"; import { ChannelTypeStrings } from "src/types"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { asSingleLine, disableInlineCode } from "../../../utils"; +import { asSingleLine } from "../../../utils"; import { getMissingChannelPermissions } from "../../../utils/getMissingChannelPermissions"; import { missingPermissionError } from "../../../utils/missingPermissionError"; import { BOT_SLOWMODE_CLEAR_PERMISSIONS } from "../requiredPermissions"; @@ -57,7 +58,7 @@ export const SlowmodeClearCmd = slowmodeCmd({ msg.channel, asSingleLine(` Failed to clear slowmode from **${args.user.tag}** in <#${args.channel.id}>: - \`${disableInlineCode(e.message)}\` + \`${Util.escapeInlineCode(e.message)}\` `), ); return; diff --git a/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts b/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts index 22bc74d7..3b316c6f 100644 --- a/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts +++ b/backend/src/plugins/Slowmode/commands/SlowmodeSetCmd.ts @@ -1,9 +1,9 @@ -import { Permissions, TextChannel, ThreadChannel } from "discord.js"; +import { Permissions, TextChannel, ThreadChannel, Util } 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, disableInlineCode, HOURS, MINUTES } from "../../../utils"; +import { asSingleLine, DAYS, HOURS, MINUTES } from "../../../utils"; import { getMissingPermissions } from "../../../utils/getMissingPermissions"; import { missingPermissionError } from "../../../utils/missingPermissionError"; import { BOT_SLOWMODE_PERMISSIONS, NATIVE_SLOWMODE_PERMISSIONS } from "../requiredPermissions"; @@ -133,7 +133,7 @@ export const SlowmodeSetCmd = slowmodeCmd({ rateLimitPerUser: rateLimitSeconds, }); } catch (e) { - sendErrorMessage(pluginData, msg.channel, `Failed to set native slowmode: ${disableInlineCode(e.message)}`); + sendErrorMessage(pluginData, msg.channel, `Failed to set native slowmode: ${Util.escapeInlineCode(e.message)}`); return; } } else { diff --git a/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts b/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts index 1786bd34..d88a0740 100644 --- a/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts +++ b/backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts @@ -1,6 +1,7 @@ +import { Util } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils"; -import { disableInlineCode, trimLines } from "../../../utils"; +import { trimLines } from "../../../utils"; import { parseFuzzyTimezone } from "../../../utils/parseFuzzyTimezone"; import { timeAndDateCmd } from "../types"; @@ -19,7 +20,7 @@ export const SetTimezoneCmd = timeAndDateCmd({ pluginData, message.channel, trimLines(` - Invalid timezone: \`${disableInlineCode(args.timezone)}\` + Invalid timezone: \`${Util.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/utils.ts b/backend/src/utils.ts index a2ea3005..a3dcec39 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -22,6 +22,7 @@ import { TextChannel, ThreadChannel, User, + Util, } from "discord.js"; import emojiRegex from "emoji-regex"; import { either } from "fp-ts/lib/Either"; @@ -785,21 +786,6 @@ export function deactivateMentions(content: string): string { return content.replace(/@/g, "@\u200b"); } -/** - * Disable inline code in the given string by replacing backticks/grave accents with acute accents - * FIXME: Find a better way that keeps the grave accents? Can't use the code block approach here since it's just 1 character. - */ -export function disableInlineCode(content: string): string { - return content.replace(/`/g, "\u00b4"); -} - -/** - * Disable code blocks in the given string by adding invisible unicode characters between backticks - */ -export function disableCodeBlocks(content: string): string { - return content.replace(/`/g, "`\u200b"); -} - export function useMediaUrls(content: string): string { return content.replace(/cdn\.discord(app)?\.com/g, "media.discordapp.net"); } @@ -1336,11 +1322,11 @@ export async function confirm(channel: TextChannel, userId: string, content: Mes export function messageSummary(msg: SavedMessage) { // Regular text content - let result = "```\n" + (msg.data.content ? disableCodeBlocks(msg.data.content) : "") + "```"; + let result = "```\n" + (msg.data.content ? Util.escapeCodeBlock(msg.data.content) : "") + "```"; // Rich embed const richEmbed = (msg.data.embeds || []).find(e => (e as MessageEmbed).type === "rich"); - if (richEmbed) result += "Embed:```" + disableCodeBlocks(JSON.stringify(richEmbed)) + "```"; + if (richEmbed) result += "Embed:```" + Util.escapeCodeBlock(JSON.stringify(richEmbed)) + "```"; // Attachments if (msg.data.attachments) {