diff --git a/packages/client/src/types/messages/message-entity.ts b/packages/client/src/types/messages/message-entity.ts index 53fb648d..1556eecb 100644 --- a/packages/client/src/types/messages/message-entity.ts +++ b/packages/client/src/types/messages/message-entity.ts @@ -17,6 +17,7 @@ const entityToType: Partial< messageEntityPhone: 'phone_number', messageEntityPre: 'pre', messageEntityStrike: 'strikethrough', + messageEntitySpoiler: 'spoiler', messageEntityTextUrl: 'text_link', messageEntityUnderline: 'underline', messageEntityUrl: 'url', @@ -54,6 +55,7 @@ export namespace MessageEntity { | 'italic' | 'underline' | 'strikethrough' + | 'spoiler' | 'code' | 'pre' | 'text_link' diff --git a/packages/html-parser/README.md b/packages/html-parser/README.md index 41b904df..35db69d6 100644 --- a/packages/html-parser/README.md +++ b/packages/html-parser/README.md @@ -47,6 +47,7 @@ Inline entities are entities that are in-line with other text. We support these | Italic | `text` | _text_ | | Underline | `text` | text | | Strikethrough | `text` | ~~text~~ | +| Spoiler | `text` | N/A | | Monospace (code) | `text` | `text` | | Text link | `Google` | [Google](https://google.com) | | Text mention | `Name` | N/A | diff --git a/packages/html-parser/src/index.ts b/packages/html-parser/src/index.ts index e609083f..cba150c2 100644 --- a/packages/html-parser/src/index.ts +++ b/packages/html-parser/src/index.ts @@ -178,6 +178,13 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { language: attribs.language ?? '', } break + case 'spoiler': + entity = { + _: 'messageEntitySpoiler', + offset: plainText.length, + length: 0, + } + break case 'a': { let url = attribs.href if (!url) return @@ -343,22 +350,25 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { break case 'code': case 'pre': - case 'blockquote': html.push( `<${type}${ - type === 'pre' && entity.language + entity.language ? ` language="${entity.language}"` : '' }>${ this._syntaxHighlighter && entity.language ? this._syntaxHighlighter( entityText, - entity.language! + entity.language ) : entityText }` ) break + case 'blockquote': + case 'spoiler': + html.push(`<${type}>${entityText}`) + break case 'email': html.push( `${entityText}` diff --git a/packages/html-parser/tests/html-parser.spec.ts b/packages/html-parser/tests/html-parser.spec.ts index 7725cddb..89312b67 100644 --- a/packages/html-parser/tests/html-parser.spec.ts +++ b/packages/html-parser/tests/html-parser.spec.ts @@ -59,15 +59,16 @@ describe('HtmlMessageEntityParser', () => { ) }) - it('should handle ,
, 
tags', () => { + it('should handle ,
, 
, tags', () => { test( - 'plain code pre blockquote plain', + 'plain code pre blockquote spoiler plain', [ createEntity('messageEntityCode', 6, 4), createEntity('messageEntityPre', 11, 3), createEntity('messageEntityBlockquote', 15, 10), + createEntity('messageEntitySpoiler', 26, 7), ], - 'plain code
pre
blockquote
plain' + 'plain code
pre
blockquote
spoiler plain' ) }) @@ -309,15 +310,16 @@ describe('HtmlMessageEntityParser', () => { ) }) - it('should handle ,
, 
tags', () => { + it('should handle ,
, 
, tags', () => { test( - 'plain code
pre
blockquote
plain', + 'plain code
pre
blockquote
spoiler plain', [ createEntity('messageEntityCode', 6, 4), createEntity('messageEntityPre', 11, 3, { language: '' }), createEntity('messageEntityBlockquote', 15, 10), + createEntity('messageEntitySpoiler', 26, 7), ], - 'plain code pre blockquote plain' + 'plain code pre blockquote spoiler plain' ) }) diff --git a/packages/markdown-parser/README.md b/packages/markdown-parser/README.md index 78690c20..b3887665 100644 --- a/packages/markdown-parser/README.md +++ b/packages/markdown-parser/README.md @@ -35,6 +35,7 @@ Supported entities: - Italic, tag is `__` - Underline, tag is `--` (_NON-STANDARD_) - Strikethrough, tag is `~~` +- Spoiler, tag is `||` (_NON-STANDARD_) - Code (monospaced font), tag is ` - Note that escaping text works differently inside code, see below. @@ -44,16 +45,17 @@ Supported entities: > > This eliminates a lot of confusion, like: `_bold_` → _bold_, `**italic**` → **italic** -| Code | Result (visual) | Result (as HTML) -|---|---|---| -| `**bold**` | **bold** | `bold` -| `__italic__` | __italic__ | `italic` -| `--underline` | underline | `underline` -| `~~strikethrough~~` | ~~strikethrough~~ | `strikethrough` -| `*whatever*` | \*whatever\* | `*whatever*` -| `_whatever_` | \_whatever\_ | `_whatever_` -| \`hello world\` | `hello world` | `hello world` -| \`__text__\` | `__text__` | `__text__` +| Code | Result (visual) | Result (as HTML) | +|----------------------------------------------|-------------------|------------------------------| +| `**bold**` | **bold** | `bold` | +| `__italic__` | __italic__ | `italic` | +| `--underline--` | underline | `underline` | +| `~~strikethrough~~` | ~~strikethrough~~ | `strikethrough` | +| ||spoiler|| | N/A | `spoiler` | +| `*whatever*` | \*whatever\* | `*whatever*` | +| `_whatever_` | \_whatever\_ | `_whatever_` | +| \`hello world\` | `hello world` | `hello world` | +| \`__text__\` | `__text__` | `__text__` | ### Pre @@ -62,11 +64,11 @@ Pre represents a single block of code, optionally with a language. This entity starts with \`\`\` (triple backtick), optionally followed with language name and a must be followed with a line break, and ends with \`\`\` (triple backtick), optionally preceded with a line break. -| Code | Result (visual) | Result (as HTML) -|---|---|---| -|
\`\`\`
hello
\`\`\`
| `hello` | `
hello
` -|
\`\`\`
hello\`\`\`
| `hello` | `
hello
` -|
\`\`\`javascript
const a = ``
\`\`\`
| const a = `` |
<pre language="javascript">
const a = ``
</pre>
+| Code | Result (visual) | Result (as HTML) | +|--------------------------------------------------------------------|---------------------------|---------------------------------------------------------------------------------------------| +|
\`\`\`
hello
\`\`\`
| `hello` | `
hello
` | +|
\`\`\`
hello\`\`\`
| `hello` | `
hello
` | +|
\`\`\`javascript
const a = ``
\`\`\`
| const a = `` |
<pre language="javascript">
const a = ``
</pre>
| ### Links @@ -87,13 +89,13 @@ where `1234567` is the ID of the user you want to mention > where `abc` is user's access hash written as a base-16 *unsigned* integer. > Order of the parameters does matter, i.e. `tg://user?hash=abc&id=1234567` will not be processed as expected. -| Code | Result (visual) | Result (as HTML) -|---|---|---| -| `[Google](https://google.com)` | [Google](https://google.com) | `Google` -| `[__Google__](https://google.com)` | [_Google_](https://google.com) | `Google` -| `[empty link]()` | empty link | `empty link` -| `[empty link]` | [empty link] | `[empty link]` -| `[User](tg://user?id=1234567)` | N/A | N/A +| Code | Result (visual) | Result (as HTML) | +|------------------------------------|--------------------------------|--------------------------------------------------| +| `[Google](https://google.com)` | [Google](https://google.com) | `Google` | +| `[__Google__](https://google.com)` | [_Google_](https://google.com) | `Google` | +| `[empty link]()` | empty link | `empty link` | +| `[empty link]` | [empty link] | `[empty link]` | +| `[User](tg://user?id=1234567)` | N/A | N/A | ### Nested and overlapping entities @@ -103,10 +105,10 @@ code) can be overlapped. Since inline entities are only defined by their tag, and nesting same entities doesn't make sense, you can think of the tags just as start/end markers, and not in terms of nesting. -| Code | Result (visual) | Result (as HTML) -|---|---|---| -| `**Welcome back, __User__!**` | **Welcome back, _User_!** | `Welcome back, User!` -| `**bold __and** italic__` | **bold _and_** _italic_ | `bold and italic` +| Code | Result (visual) | Result (as HTML) | +|-------------------------------|---------------------------|----------------------------------------| +| `**Welcome back, __User__!**` | **Welcome back, _User_!** | `Welcome back, User!` | +| `**bold __and** italic__` | **bold _and_** _italic_ | `bold and italic` | ## Escaping @@ -128,10 +130,10 @@ like `"\\_\\_not italic\\_\\_`. > In theory, you could escape every single non-markup character, but why would you want to do that 😜 -| Code | Result (visual) | Result (as HTML) -|---|---|---| -| `\_\_not italic\_\_` | \_\_not italic\_\_ | `__not italic__` -| `__italic \_ text__` | _italic \_ text_ | `italic _ text ` -| \`__not italic__\` | `__not italic__` | `__not italic__` -| C:\\\\Users\\\\Guest | C:\Users\Guest | `C:\Users\Guest` -| \`var a = \\\`hello\\\`\` | var a = \`hello\` | <code>var a = \`hello\`</code> +| Code | Result (visual) | Result (as HTML) | +|----------------------------------------|--------------------------------|---------------------------------------------------------| +| `\_\_not italic\_\_` | \_\_not italic\_\_ | `__not italic__` | +| `__italic \_ text__` | _italic \_ text_ | `italic _ text ` | +| \`__not italic__\` | `__not italic__` | `__not italic__` | +| C:\\\\Users\\\\Guest | C:\Users\Guest | `C:\Users\Guest` | +| \`var a = \\\`hello\\\`\` | var a = \`hello\` | <code>var a = \`hello\`</code> | diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index d5f5590b..4c25ac14 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -10,10 +10,11 @@ const TAG_BOLD = '**' const TAG_ITALIC = '__' const TAG_UNDERLINE = '--' const TAG_STRIKE = '~~' +const TAG_SPOILER = '||' const TAG_CODE = '`' const TAG_PRE = '```' -const TO_BE_ESCAPED = /[*_\-~`[\\\]]/g +const TO_BE_ESCAPED = /[*_\-~`[\\\]|]/g /** * Tagged template based helper for escaping entities in Markdown @@ -248,7 +249,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { if (c === text[pos + 1]) { // maybe (?) start or end of an entity - let type: 'Italic' | 'Bold' | 'Underline' | 'Strike' | null = + let type: 'Italic' | 'Bold' | 'Underline' | 'Strike' | 'Spoiler' | null = null switch (c) { case '_': @@ -263,6 +264,9 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { case '~': type = 'Strike' break + case '|': + type = 'Spoiler' + break } if (type) { @@ -352,6 +356,9 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { case 'strikethrough': startTag = endTag = TAG_STRIKE break + case 'spoiler': + startTag = endTag = TAG_SPOILER + break case 'code': startTag = endTag = TAG_CODE break diff --git a/packages/markdown-parser/tests/markdown-parser.spec.ts b/packages/markdown-parser/tests/markdown-parser.spec.ts index 325db202..8041e417 100644 --- a/packages/markdown-parser/tests/markdown-parser.spec.ts +++ b/packages/markdown-parser/tests/markdown-parser.spec.ts @@ -51,16 +51,17 @@ describe('MarkdownMessageEntityParser', () => { test('some text', [], 'some text') }) - it('should handle bold, italic, underline and strikethrough', () => { + it('should handle bold, italic, underline, strikethrough and spoiler', () => { test( - 'plain bold italic underline strikethrough plain', + 'plain bold italic underline strikethrough spoiler plain', [ createEntity('messageEntityBold', 6, 4), createEntity('messageEntityItalic', 11, 6), createEntity('messageEntityUnderline', 18, 9), createEntity('messageEntityStrike', 28, 13), + createEntity('messageEntitySpoiler', 42, 7), ], - 'plain **bold** __italic__ --underline-- ~~strikethrough~~ plain' + 'plain **bold** __italic__ --underline-- ~~strikethrough~~ ||spoiler|| plain' ) }) @@ -294,16 +295,17 @@ describe('MarkdownMessageEntityParser', () => { } } - it('should handle bold, italic, underline and strikethrough', () => { + it('should handle bold, italic, underline, spoiler and strikethrough', () => { test( - 'plain **bold** __italic__ --underline-- ~~strikethrough~~ plain', + 'plain **bold** __italic__ --underline-- ~~strikethrough~~ ||spoiler|| plain', [ createEntity('messageEntityBold', 6, 4), createEntity('messageEntityItalic', 11, 6), createEntity('messageEntityUnderline', 18, 9), createEntity('messageEntityStrike', 28, 13), + createEntity('messageEntitySpoiler', 42, 7), ], - 'plain bold italic underline strikethrough plain' + 'plain bold italic underline strikethrough spoiler plain' ) })