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 <&>!
+```
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('**\\_\\_\\[\\]\\_\\_**')
+ })
+ })
})