diff --git a/packages/html-parser/README.md b/packages/html-parser/README.md index 98d4ecfd..4f1b953a 100644 --- a/packages/html-parser/README.md +++ b/packages/html-parser/README.md @@ -13,17 +13,14 @@ API ([documented here](https://core.telegram.org/bots/api#html-style)) ```typescript import { TelegramClient } from '@mtcute/client' -import { HtmlMessageEntityParser } from '@mtcute/html-parser' +import { HtmlMessageEntityParser, html } from '@mtcute/html-parser' const tg = new TelegramClient({ ... }) tg.registerParseMode(new HtmlMessageEntityParser()) tg.sendText( 'me', - 'Hello, me! Updates from the feed:\n' + - HtmlMessageEntityParser.escape( - await getUpdatesFromFeed() - ) + html`Hello, me! Updates from the feed:\n${await getUpdatesFromFeed()}` ) ``` @@ -94,4 +91,13 @@ bold _and_** _italic_ Escaping in this parser works exactly the same as in `htmlparser2`. This means that you can keep `<>&` symbols as-is in some cases. However, when dealing with user input, it is always -better to use [`HtmlMessageEntityParser.escape`](./classes/htmlmessageentityparser.html#escape) +better to use [`HtmlMessageEntityParser.escape`](./classes/htmlmessageentityparser.html#escape) or, even better, +`html` helper: + +```typescript +import { html } from '@mtcute/html-parser' + +const username = 'Boris <&>' +const text = html`Hi, ${username}!` +console.log(text) // Hi, Boris &lt;&amp;&gt;! +``` diff --git a/packages/html-parser/src/index.ts b/packages/html-parser/src/index.ts index 33b02022..e9e93d80 100644 --- a/packages/html-parser/src/index.ts +++ b/packages/html-parser/src/index.ts @@ -5,6 +5,22 @@ import bigInt from 'big-integer' const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ +/** + * Tagged template based helper for escaping entities in HTML + * + * @example + * ```typescript + * const escaped = html`${user.displayName}` + * ``` + */ +export function html(strings: TemplateStringsArray, ...sub: string[]): string { + let str = '' + sub.forEach((it, idx) => { + str += strings[idx] + HtmlMessageEntityParser.escape(it) + }) + return str + strings[strings.length - 1] +} + export namespace HtmlMessageEntityParser { /** * Syntax highlighter function used in {@link HtmlMessageEntityParser.unparse} diff --git a/packages/html-parser/tests/html-parser.spec.ts b/packages/html-parser/tests/html-parser.spec.ts index c8f92a9f..df4db8d3 100644 --- a/packages/html-parser/tests/html-parser.spec.ts +++ b/packages/html-parser/tests/html-parser.spec.ts @@ -1,7 +1,7 @@ import { describe, it } from 'mocha' import { expect } from 'chai' import { tl } from '@mtcute/tl' -import { HtmlMessageEntityParser } from '../src' +import { HtmlMessageEntityParser, html } from '../src' import { MessageEntity } from '@mtcute/client' import bigInt from 'big-integer' @@ -450,4 +450,15 @@ describe('HtmlMessageEntityParser', () => { test('link link', [], 'link link') }) }) + + describe('template', () => { + it('should work as a tagged template literal', () => { + const unsafeString = '<&>' + + expect(html`${unsafeString}`).eq('<&>') + expect(html`${unsafeString} text`).eq('<&> text') + expect(html`text ${unsafeString}`).eq('text <&>') + expect(html`${unsafeString}`).eq('<&>') + }) + }) }) diff --git a/packages/markdown-parser/README.md b/packages/markdown-parser/README.md index 6b853bf1..78690c20 100644 --- a/packages/markdown-parser/README.md +++ b/packages/markdown-parser/README.md @@ -12,17 +12,14 @@ This package implements formatting syntax similar to Markdown (CommonMark) but s ```typescript import { TelegramClient } from '@mtcute/client' -import { MarkdownMessageEntityParser } from '@mtcute/markdown-parser' +import { MarkdownMessageEntityParser, md } from '@mtcute/markdown-parser' const tg = new TelegramClient({ ... }) tg.registerParseMode(new MarkdownMessageEntityParser()) tg.sendText( 'me', - 'Hello, **me**! Updates from the feed:\n' + - MarkdownMessageEntityParser.escape( - await getUpdatesFromFeed() - ) + md`Hello, **me**! Updates from the feed:\n${await getUpdatesFromFeed()}` ) ``` @@ -126,7 +123,7 @@ like `"\\_\\_not italic\\_\\_`. > **Note**: backslash itself must be escaped like this: ` \\ ` (double backslash). > > This will look pretty bad in real code, so use escaping only when really needed, and use -> [`MarkdownMessageEntityParser.escape`](./classes/markdownmessageentityparser.html#escape) or +> [`MarkdownMessageEntityParser.escape`](./classes/markdownmessageentityparser.html#escape) or `md` or > other parse modes (like HTML one provided by [`@mtcute/html-parser`](../html-parser/index.html))) instead. > In theory, you could escape every single non-markup character, but why would you want to do that 😜 diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index a63855ad..e4426219 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -13,6 +13,22 @@ const TAG_PRE = '```' const TO_BE_ESCAPED = /[*_\-~`[\\\]]/g +/** + * Tagged template based helper for escaping entities in Markdown + * + * @example + * ```typescript + * const escaped = md`**${user.displayName}**` + * ``` + */ +export function md(strings: TemplateStringsArray, ...sub: string[]): string { + let str = '' + sub.forEach((it, idx) => { + str += strings[idx] + MarkdownMessageEntityParser.escape(it) + }) + return str + strings[strings.length - 1] +} + /** * Markdown MessageEntity parser. * diff --git a/packages/markdown-parser/tests/markdown-parser.spec.ts b/packages/markdown-parser/tests/markdown-parser.spec.ts index 6d91d2d2..99f4a5b1 100644 --- a/packages/markdown-parser/tests/markdown-parser.spec.ts +++ b/packages/markdown-parser/tests/markdown-parser.spec.ts @@ -2,7 +2,7 @@ import { describe, it } from 'mocha' import { expect } from 'chai' import { tl } from '@mtcute/tl' import { MessageEntity } from '@mtcute/client' -import { MarkdownMessageEntityParser } from '../src' +import { MarkdownMessageEntityParser, md } from '../src' import bigInt from 'big-integer' const createEntity = ( @@ -647,4 +647,15 @@ describe('MarkdownMessageEntityParser', () => { }) }) }) + + describe('template', () => { + it('should work as a tagged template literal', () => { + const unsafeString = '__[]__' + + expect(md`${unsafeString}`).eq('\\_\\_\\[\\]\\_\\_') + expect(md`${unsafeString} **text**`).eq('\\_\\_\\[\\]\\_\\_ **text**') + expect(md`**text** ${unsafeString}`).eq('**text** \\_\\_\\[\\]\\_\\_') + expect(md`**${unsafeString}**`).eq('**\\_\\_\\[\\]\\_\\_**') + }) + }) })