From cda0ccae86f5678e9c558f81fcda841e1b27fdc2 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:51:27 -0500 Subject: [PATCH 1/4] feat(Util): backport `escapeMarkdown` PRs to v13 Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- src/util/Util.js | 72 +++++++++++++++++++++++++++++++++++++++++++++- typings/index.d.ts | 12 +++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/util/Util.js b/src/util/Util.js index 5ff5c6bea969..cf422d3f29a0 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -124,6 +124,11 @@ class Util extends null { * @property {boolean} [spoiler=true] Whether to escape spoilers or not * @property {boolean} [codeBlockContent=true] Whether to escape text inside code blocks or not * @property {boolean} [inlineCodeContent=true] Whether to escape text inside inline code or not + * @property {boolean} [escape=true] Whether to escape escape characters or not + * @property {boolean} [heading=false] Whether to escape headings or not + * @property {boolean} [bulletedList=false] Whether to escape bulleted lists or not + * @property {boolean} [numberedList=false] Whether to escape numbered lists or not + * @property {boolean} [maskedLink=false] Whether to escape masked links or not */ /** @@ -144,6 +149,11 @@ class Util extends null { spoiler = true, codeBlockContent = true, inlineCodeContent = true, + escape = true, + heading = false, + bulletedList = false, + numberedList = false, + maskedLink = false, } = {}, ) { if (!codeBlockContent) { @@ -159,6 +169,11 @@ class Util extends null { strikethrough, spoiler, inlineCodeContent, + escape, + heading, + bulletedList, + numberedList, + maskedLink, }); }) .join(codeBlock ? '\\`\\`\\`' : '```'); @@ -175,6 +190,11 @@ class Util extends null { underline, strikethrough, spoiler, + escape, + heading, + bulletedList, + numberedList, + maskedLink, }); }) .join(inlineCode ? '\\`' : '`'); @@ -186,6 +206,11 @@ class Util extends null { if (underline) text = Util.escapeUnderline(text); if (strikethrough) text = Util.escapeStrikethrough(text); if (spoiler) text = Util.escapeSpoiler(text); + if (escape) text = Util.escapeEscape(text); + if (heading) text = Util.escapeHeading(text); + if (bulletedList) text = Util.escapeBulletedList(text); + if (numberedList) text = Util.escapeNumberedList(text); + if (maskedLink) text = Util.escapeMaskedLink(text); return text; } @@ -204,7 +229,7 @@ class Util extends null { * @returns {string} */ static escapeInlineCode(text) { - return text.replace(/(?<=^|[^`])`(?=[^`]|$)/g, '\\`'); + return text.replace(/(?<=^|[^`])``?(?=[^`]|$)/g, match => (match.length === 2 ? '\\`\\`' : '\\`')); } /** @@ -269,6 +294,51 @@ class Util extends null { return text.replaceAll('||', '\\|\\|'); } + /** + * Escapes escape characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeEscape(text) { + return text.replaceAll('\\', '\\\\'); + } + + /** + * Escapes heading characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeHeading(text) { + return text.replaceAll(/^( {0,2}- +)?(#{1,3} )/gm, '$1\\$2'); + } + + /** + * Escapes bulleted list characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeBulletedList(text) { + return text.replaceAll(/^( *)-( +)/gm, '$1\\-$2'); + } + + /** + * Escapes numbered list characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeNumberedList(text) { + return text.replaceAll(/^( *\d+)\./gm, '$1\\.'); + } + + /** + * Escapes masked link characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ + static escapeMaskedLink(text) { + return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$0'); + } + /** * @typedef {Object} FetchRecommendedShardsOptions * @property {number} [guildsPerShard=1000] Number of guilds assigned per shard diff --git a/typings/index.d.ts b/typings/index.d.ts index 065486ce4725..1c8f35b208b3 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2625,6 +2625,11 @@ export class Util extends null { public static escapeUnderline(text: string): string; public static escapeStrikethrough(text: string): string; public static escapeSpoiler(text: string): string; + public static escapeEscape(text: string): string; + public static escapeHeading(text: string): string; + public static escapeBulletedList(text: string): string; + public static escapeNumberedList(text: string): string; + public static escapeMaskedLink(text: string): string; public static cleanCodeBlockContent(text: string): string; public static fetchRecommendedShards(token: string, options?: FetchRecommendedShardsOptions): Promise; public static flatten(obj: unknown, ...props: Record[]): unknown; @@ -4650,8 +4655,13 @@ export interface EscapeMarkdownOptions { underline?: boolean; strikethrough?: boolean; spoiler?: boolean; - inlineCodeContent?: boolean; codeBlockContent?: boolean; + inlineCodeContent?: boolean; + escape?: boolean; + heading?: boolean; + bulletedList?: boolean; + numberedList?: boolean; + maskedLink?: boolean; } export type ExplicitContentFilterLevel = keyof typeof ExplicitContentFilterLevels; From ffd878c0fdbcf6d38faefff9920c8d7250634999 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Wed, 5 Oct 2022 11:40:28 -0500 Subject: [PATCH 2/4] fix: lists bulleted with `*` Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- src/util/Util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/Util.js b/src/util/Util.js index cf422d3f29a0..d8df6bdec492 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -309,7 +309,7 @@ class Util extends null { * @returns {string} */ static escapeHeading(text) { - return text.replaceAll(/^( {0,2}- +)?(#{1,3} )/gm, '$1\\$2'); + return text.replaceAll(/^( {0,2}[*-] +)?(#{1,3} )/gm, '$1\\$2'); } /** @@ -318,7 +318,7 @@ class Util extends null { * @returns {string} */ static escapeBulletedList(text) { - return text.replaceAll(/^( *)-( +)/gm, '$1\\-$2'); + return text.replaceAll(/^( *)[*-]( +)/gm, '$1\\-$2'); } /** From d3c85921ec7f557339198a8786c45ebc2b102919 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Thu, 6 Oct 2022 11:34:26 -0500 Subject: [PATCH 3/4] tests(escapeMarkdown): add tests Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- src/util/Util.js | 30 +++++++++++------------ test/escapeMarkdown.test.js | 47 +++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/util/Util.js b/src/util/Util.js index d8df6bdec492..658864fff163 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -115,20 +115,20 @@ class Util extends null { /** * Options used to escape markdown. * @typedef {Object} EscapeMarkdownOptions - * @property {boolean} [codeBlock=true] Whether to escape code blocks or not - * @property {boolean} [inlineCode=true] Whether to escape inline code or not - * @property {boolean} [bold=true] Whether to escape bolds or not - * @property {boolean} [italic=true] Whether to escape italics or not - * @property {boolean} [underline=true] Whether to escape underlines or not - * @property {boolean} [strikethrough=true] Whether to escape strikethroughs or not - * @property {boolean} [spoiler=true] Whether to escape spoilers or not - * @property {boolean} [codeBlockContent=true] Whether to escape text inside code blocks or not - * @property {boolean} [inlineCodeContent=true] Whether to escape text inside inline code or not - * @property {boolean} [escape=true] Whether to escape escape characters or not - * @property {boolean} [heading=false] Whether to escape headings or not - * @property {boolean} [bulletedList=false] Whether to escape bulleted lists or not - * @property {boolean} [numberedList=false] Whether to escape numbered lists or not - * @property {boolean} [maskedLink=false] Whether to escape masked links or not + * @property {boolean} [codeBlock=true] Whether to escape code blocks + * @property {boolean} [inlineCode=true] Whether to escape inline code + * @property {boolean} [bold=true] Whether to escape bolds + * @property {boolean} [italic=true] Whether to escape italics + * @property {boolean} [underline=true] Whether to escape underlines + * @property {boolean} [strikethrough=true] Whether to escape strikethroughs + * @property {boolean} [spoiler=true] Whether to escape spoilers + * @property {boolean} [codeBlockContent=true] Whether to escape text inside code blocks + * @property {boolean} [inlineCodeContent=true] Whether to escape text inside inline code + * @property {boolean} [escape=true] Whether to escape escape characters + * @property {boolean} [heading=false] Whether to escape headings + * @property {boolean} [bulletedList=false] Whether to escape bulleted lists + * @property {boolean} [numberedList=false] Whether to escape numbered lists + * @property {boolean} [maskedLink=false] Whether to escape masked links */ /** @@ -336,7 +336,7 @@ class Util extends null { * @returns {string} */ static escapeMaskedLink(text) { - return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$0'); + return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$&'); } /** diff --git a/test/escapeMarkdown.test.js b/test/escapeMarkdown.test.js index 4c7ca180fe21..bdffdc71f3cc 100644 --- a/test/escapeMarkdown.test.js +++ b/test/escapeMarkdown.test.js @@ -5,6 +5,8 @@ const Util = require('../src/util/Util'); const testString = "`_Behold!_`\n||___~~***```js\n`use strict`;\nrequire('discord.js');```***~~___||"; +const testStringForums = + '# Title\n## Subtitle\n### Subsubtitle\n- Bullet list\n - # Title with bullet\n * Subbullet\n1. Number list\n 1. Sub number list'; describe('escapeCodeblock', () => { test('shared', () => { @@ -88,12 +90,53 @@ describe('escapeSpoiler', () => { "`_Behold!_`\n\\|\\|___~~***```js\n`use strict`;\nrequire('discord.js');```***~~___\\|\\|", ); }); - test('basic', () => { expect(Util.escapeSpoiler('||test||')).toBe('\\|\\|test\\|\\|'); }); }); +describe('escapeHeading', () => { + test('shared', () => { + expect(Util.escapeHeading(testStringForums)).toBe( + '\\# Title\n\\## Subtitle\n\\### Subsubtitle\n- Bullet list\n - \\# Title with bullet\n * Subbullet\n1. Number list\n 1. Sub number list', + ); + }); + + test('basic', () => { + expect(Util.escapeHeading('# test')).toBe('\\# test'); + }); +}); + +describe('escapeBulletedList', () => { + test('shared', () => { + expect(Util.escapeBulletedList(testStringForums)).toBe( + '# Title\n## Subtitle\n### Subsubtitle\n\\- Bullet list\n \\- # Title with bullet\n \\* Subbullet\n1. Number list\n 1. Sub number list', + ); + }); + + test('basic', () => { + expect(Util.escapeBulletedList('- test')).toBe('\\- test'); + }); +}); + +describe('escapeNumberedList', () => { + test('shared', () => { + expect(Util.escapeNumberedList(testStringForums)).toBe( + '# Title\n## Subtitle\n### Subsubtitle\n- Bullet list\n - # Title with bullet\n * Subbullet\n1\\. Number list\n 1\\. Sub number list', + ); + }); + + test('basic', () => { + expect(Util.escapeNumberedList('1. test')).toBe('1\\. test'); + }); +}); + +describe('escapeMaskedLink', () => { + test('basic', () => { + expect(Util.escapeMaskedLink('[test](https://discord.js.org)')).toBe('\\[test](https://discord.js.org)'); + }); +}); + describe('escapeMarkdown', () => { test('shared', () => { expect(Util.escapeMarkdown(testString)).toBe( @@ -176,7 +219,7 @@ describe('escapeMarkdown', () => { ); }); - test('edge-case odd number of fenses with no code block content', () => { + test('edge-case odd number of fences with no code block content', () => { expect( Util.escapeMarkdown('**foo** ```**bar**``` **fizz** ``` **buzz**', { codeBlock: false, From c853dd6b1ee0f6796f77eb510f426213a9636779 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Fri, 7 Oct 2022 08:28:19 -0500 Subject: [PATCH 4/4] Update escapeMarkdown.test.js --- test/escapeMarkdown.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/escapeMarkdown.test.js b/test/escapeMarkdown.test.js index bdffdc71f3cc..878df9f7deca 100644 --- a/test/escapeMarkdown.test.js +++ b/test/escapeMarkdown.test.js @@ -90,6 +90,7 @@ describe('escapeSpoiler', () => { "`_Behold!_`\n\\|\\|___~~***```js\n`use strict`;\nrequire('discord.js');```***~~___\\|\\|", ); }); + test('basic', () => { expect(Util.escapeSpoiler('||test||')).toBe('\\|\\|test\\|\\|'); });