From 30d48616311c7340cb6bda97985f50c58ec41cac Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Tue, 4 Oct 2022 08:56:36 -0500 Subject: [PATCH 1/4] feat(Util): escape more markdown characters Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- packages/discord.js/src/util/Util.js | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/packages/discord.js/src/util/Util.js b/packages/discord.js/src/util/Util.js index 04d0992f5e2e..974d0ae860b9 100644 --- a/packages/discord.js/src/util/Util.js +++ b/packages/discord.js/src/util/Util.js @@ -65,6 +65,11 @@ function flatten(obj, ...props) { * @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 */ /** @@ -85,6 +90,11 @@ function escapeMarkdown( spoiler = true, codeBlockContent = true, inlineCodeContent = true, + escape = true, + heading = false, + bulletedList = false, + numberedList = false, + maskedLink = false, } = {}, ) { if (!codeBlockContent) { @@ -100,6 +110,11 @@ function escapeMarkdown( strikethrough, spoiler, inlineCodeContent, + escape, + heading, + bulletedList, + numberedList, + maskedLink, }); }) .join(codeBlock ? '\\`\\`\\`' : '```'); @@ -116,6 +131,11 @@ function escapeMarkdown( underline, strikethrough, spoiler, + escape, + heading, + bulletedList, + numberedList, + maskedLink, }); }) .join(inlineCode ? '\\`' : '`'); @@ -127,6 +147,11 @@ function escapeMarkdown( if (underline) text = escapeUnderline(text); if (strikethrough) text = escapeStrikethrough(text); if (spoiler) text = escapeSpoiler(text); + if (escape) text = escapeEscape(text); + if (heading) text = escapeHeading(text); + if (bulletedList) text = escapeBulletedList(text); + if (numberedList) text = escapeNumberedList(text); + if (maskedLink) text = escapeMaskedLink(text); return text; } @@ -210,6 +235,51 @@ function escapeSpoiler(text) { return text.replaceAll('||', '\\|\\|'); } +/** + * Escapes escape characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ +function escapeEscape(text) { + return text.replaceAll('\\', '\\\\'); +} + +/** + * Escapes heading characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ +function 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} + */ +function escapeBulletedList(text) { + return text.replaceAll(/^( *)-( +)/gm, '$1\\-$2'); +} + +/** + * Escapes numbered list characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ +function escapeNumberedList(text) { + return text.replaceAll(/^( *\d+)\./gm, '$1\\.'); +} + +/** + * Escapes masked link characters in a string. + * @param {string} text Content to escape + * @returns {string} + */ +function escapeMaskedLink(text) { + return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$0'); +} + /** * @typedef {Object} FetchRecommendedShardCountOptions * @property {number} [guildsPerShard=1000] Number of guilds assigned per shard From 4f87b135c89d0a55372089258d25d15ec5c5abf9 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:08:20 -0500 Subject: [PATCH 2/4] types(EscapeMarkdownOptions): update types Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- packages/discord.js/typings/index.d.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 93d44f01b713..07d6672762f4 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -2737,6 +2737,11 @@ export function escapeItalic(text: string): string; export function escapeUnderline(text: string): string; export function escapeStrikethrough(text: string): string; export function escapeSpoiler(text: string): string; +export function escapeEscape(text: string): string; +export function escapeHeading(text: string): string; +export function escapeBulletedList(text: string): string; +export function escapeNumberedList(text: string): string; +export function escapeMaskedLink(text: string): string; export function cleanCodeBlockContent(text: string): string; export function fetchRecommendedShardCount(token: string, options?: FetchRecommendedShardCountOptions): Promise; export function flatten(obj: unknown, ...props: Record[]): unknown; @@ -4659,8 +4664,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 interface FetchApplicationCommandOptions extends BaseFetchOptions { From 549ee36958d0d0bec895f82cf2b14ece8a1e7238 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Wed, 5 Oct 2022 11:43:23 -0500 Subject: [PATCH 3/4] fix: lists bulleted with `*` Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- packages/discord.js/src/util/Util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/discord.js/src/util/Util.js b/packages/discord.js/src/util/Util.js index 974d0ae860b9..27389ff85747 100644 --- a/packages/discord.js/src/util/Util.js +++ b/packages/discord.js/src/util/Util.js @@ -250,7 +250,7 @@ function escapeEscape(text) { * @returns {string} */ function escapeHeading(text) { - return text.replaceAll(/^( {0,2}- +)?(#{1,3} )/gm, '$1\\$2'); + return text.replaceAll(/^( {0,2}[*-] +)?(#{1,3} )/gm, '$1\\$2'); } /** @@ -259,7 +259,7 @@ function escapeHeading(text) { * @returns {string} */ function escapeBulletedList(text) { - return text.replaceAll(/^( *)-( +)/gm, '$1\\-$2'); + return text.replaceAll(/^( *)[*-]( +)/gm, '$1\\-$2'); } /** From a7bb52b6b586899e90762582a71eeb1d360d0155 Mon Sep 17 00:00:00 2001 From: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> Date: Thu, 6 Oct 2022 11:08:27 -0500 Subject: [PATCH 4/4] tests(escapeMarkdown): add tests Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com> --- packages/discord.js/src/util/Util.js | 30 ++++++------ .../discord.js/test/escapeMarkdown.test.js | 46 ++++++++++++++++++- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/packages/discord.js/src/util/Util.js b/packages/discord.js/src/util/Util.js index 27389ff85747..c5f5d1b52f13 100644 --- a/packages/discord.js/src/util/Util.js +++ b/packages/discord.js/src/util/Util.js @@ -56,20 +56,20 @@ function flatten(obj, ...props) { /** * 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 */ /** @@ -277,7 +277,7 @@ function escapeNumberedList(text) { * @returns {string} */ function escapeMaskedLink(text) { - return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$0'); + return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$&'); } /** diff --git a/packages/discord.js/test/escapeMarkdown.test.js b/packages/discord.js/test/escapeMarkdown.test.js index 316cfdf9625c..246efce19982 100644 --- a/packages/discord.js/test/escapeMarkdown.test.js +++ b/packages/discord.js/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', () => { @@ -94,6 +96,48 @@ describe('escapeSpoiler', () => { }); }); +describe('escapeHeading', () => { + test('shared', () => { + expect(Util.escapeHeading(testStringForums)).toEqual( + '\\# 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')).toEqual('\\# test'); + }); +}); + +describe('escapeBulletedList', () => { + test('shared', () => { + expect(Util.escapeBulletedList(testStringForums)).toEqual( + '# 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')).toEqual('\\- test'); + }); +}); + +describe('escapeNumberedList', () => { + test('shared', () => { + expect(Util.escapeNumberedList(testStringForums)).toEqual( + '# 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')).toEqual('1\\. test'); + }); +}); + +describe('escapeMaskedLink', () => { + test('basic', () => { + expect(Util.escapeMaskedLink('[test](https://discord.js.org)')).toEqual('\\[test](https://discord.js.org)'); + }); +}); + describe('escapeMarkdown', () => { test('shared', () => { expect(Util.escapeMarkdown(testString)).toEqual( @@ -176,7 +220,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,