From 9ed51fd9962ea2d8c73d02c08bb890a3def69700 Mon Sep 17 00:00:00 2001 From: teidesu <86301490+teidesu@users.noreply.github.com> Date: Fri, 2 Jul 2021 21:28:30 +0300 Subject: [PATCH] fix: made .mention() work with tagged templates --- packages/client/src/client.ts | 2 +- packages/client/src/index.ts | 1 - packages/client/src/methods/_imports.ts | 6 ++---- .../src/methods/parse-modes/_initialize.ts | 2 +- .../src/methods/parse-modes/parse-modes.ts | 3 +-- packages/client/src/types/index.ts | 1 + .../src/{parser/index.ts => types/parser.ts} | 16 ++++++++++++++++ packages/client/src/types/peers/chat.ts | 7 ++++--- packages/client/src/types/peers/user.ts | 7 ++++--- packages/html-parser/src/index.ts | 7 ++++--- packages/html-parser/tests/html-parser.spec.ts | 14 +++++++++++++- packages/markdown-parser/src/index.ts | 6 ++++-- .../tests/markdown-parser.spec.ts | 13 ++++++++++++- 13 files changed, 63 insertions(+), 22 deletions(-) rename packages/client/src/{parser/index.ts => types/parser.ts} (82%) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 1726bdf3..350baa7e 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -157,7 +157,6 @@ import { setProfilePhoto } from './methods/users/set-profile-photo' import { unblockUser } from './methods/users/unblock-user' import { updateProfile } from './methods/users/update-profile' import { updateUsername } from './methods/users/update-username' -import { IMessageEntityParser } from './parser' import { Readable } from 'stream' import { ArrayWithTotal, @@ -171,6 +170,7 @@ import { Dialog, FileDownloadParameters, GameHighScore, + IMessageEntityParser, InputFileLike, InputInlineResult, InputMediaLike, diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 3821566d..2e6020d6 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -6,7 +6,6 @@ export { } from '@mtcute/core' export * from '@mtcute/tl/errors' -export * from './parser' export * from './types' export * from './client' export * from './utils/peer-utils' diff --git a/packages/client/src/methods/_imports.ts b/packages/client/src/methods/_imports.ts index ca0691c9..163f24b0 100644 --- a/packages/client/src/methods/_imports.ts +++ b/packages/client/src/methods/_imports.ts @@ -1,8 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -// @copy -import { IMessageEntityParser } from '../parser' - // @copy import { Readable } from 'stream' @@ -41,7 +38,8 @@ import { ArrayWithTotal, BotCommands, MessageMedia, - RawDocument + RawDocument, + IMessageEntityParser } from '../types' // @copy diff --git a/packages/client/src/methods/parse-modes/_initialize.ts b/packages/client/src/methods/parse-modes/_initialize.ts index 8ae8141f..26091b07 100644 --- a/packages/client/src/methods/parse-modes/_initialize.ts +++ b/packages/client/src/methods/parse-modes/_initialize.ts @@ -1,4 +1,4 @@ -import { IMessageEntityParser } from '../../parser' +import { IMessageEntityParser } from '../../types' import { TelegramClient } from '../../client' // @extension diff --git a/packages/client/src/methods/parse-modes/parse-modes.ts b/packages/client/src/methods/parse-modes/parse-modes.ts index e0e6a6cb..74c87ef2 100644 --- a/packages/client/src/methods/parse-modes/parse-modes.ts +++ b/packages/client/src/methods/parse-modes/parse-modes.ts @@ -1,6 +1,5 @@ import { TelegramClient } from '../../client' -import { IMessageEntityParser } from '../../parser' -import { MtCuteError } from '../../types' +import { MtCuteError, IMessageEntityParser } from '../../types' /** * Register a given {@link IMessageEntityParser} as a parse mode diff --git a/packages/client/src/types/index.ts b/packages/client/src/types/index.ts index cd84527f..97d11489 100644 --- a/packages/client/src/types/index.ts +++ b/packages/client/src/types/index.ts @@ -7,5 +7,6 @@ export * from './peers' export * from './misc' export * from './errors' +export * from './parser' export { MaybeDynamic, ArrayWithTotal } from './utils' export { MaybeAsync, PartialExcept, PartialOnly } from '@mtcute/core' diff --git a/packages/client/src/parser/index.ts b/packages/client/src/types/parser.ts similarity index 82% rename from packages/client/src/parser/index.ts rename to packages/client/src/types/parser.ts index f07c9f67..a228231d 100644 --- a/packages/client/src/parser/index.ts +++ b/packages/client/src/types/parser.ts @@ -38,3 +38,19 @@ export interface IMessageEntityParser { */ unparse(text: string, entities: ReadonlyArray): string } + +/** + * Raw string that will not be escaped when passing + * to tagged template helpers (like `html` and `md`) + */ +export class RawString { + raw!: true + + constructor (readonly value: string) {} + + toString(): string { + return this.value + } +} + +RawString.prototype.raw = true diff --git a/packages/client/src/types/peers/chat.ts b/packages/client/src/types/peers/chat.ts index 9a9d40ba..fedc985b 100644 --- a/packages/client/src/types/peers/chat.ts +++ b/packages/client/src/types/peers/chat.ts @@ -8,6 +8,7 @@ import { makeInspectable } from '../utils' import { ChatsIndex, InputPeerLike, User, UsersIndex } from './index' import { ChatLocation } from './chat-location' import { InputMediaLike } from '../media' +import { RawString } from '../parser' export namespace Chat { /** @@ -548,7 +549,7 @@ export class Chat { * msg.replyText(`Hello, ${msg.chat.mention()`) * ``` */ - mention(text?: string | null, parseMode?: string | null): string { + mention(text?: string | null, parseMode?: string | null): string | RawString { if (this.user) return this.user.mention(text, parseMode) if (!text && this.username) { @@ -560,7 +561,7 @@ export class Chat { if (!parseMode) parseMode = this.client['_defaultParseMode'] - return this.client.getParseMode(parseMode).unparse(text, [ + return new RawString(this.client.getParseMode(parseMode).unparse(text, [ { raw: undefined as any, type: 'text_link', @@ -568,7 +569,7 @@ export class Chat { length: text.length, url: `https://t.me/${this.username}` }, - ]) + ])) } /** diff --git a/packages/client/src/types/peers/user.ts b/packages/client/src/types/peers/user.ts index 8e081a6c..0fc85556 100644 --- a/packages/client/src/types/peers/user.ts +++ b/packages/client/src/types/peers/user.ts @@ -5,6 +5,7 @@ import { MtCuteArgumentError } from '../errors' import { makeInspectable } from '../utils' import { assertTypeIs } from '../../utils/type-assertion' import { InputMediaLike } from '../media' +import { RawString } from '../parser' export namespace User { /** @@ -290,7 +291,7 @@ export class User { * msg.replyText(`Hello, ${msg.sender.mention()`) * ``` */ - mention(text?: string | null, parseMode?: string | null): string { + mention(text?: string | null, parseMode?: string | null): string | RawString { if (!text && this.username) { return `@${this.username}` } @@ -298,7 +299,7 @@ export class User { if (!text) text = this.displayName if (!parseMode) parseMode = this.client['_defaultParseMode'] - return this.client.getParseMode(parseMode).unparse(text, [ + return new RawString(this.client.getParseMode(parseMode).unparse(text, [ { raw: undefined as any, type: 'text_mention', @@ -306,7 +307,7 @@ export class User { length: text.length, userId: this.id, }, - ]) + ])) } /** diff --git a/packages/html-parser/src/index.ts b/packages/html-parser/src/index.ts index e9e93d80..374fa91e 100644 --- a/packages/html-parser/src/index.ts +++ b/packages/html-parser/src/index.ts @@ -1,4 +1,4 @@ -import type { IMessageEntityParser, MessageEntity } from '@mtcute/client' +import type { IMessageEntityParser, MessageEntity, RawString } from '@mtcute/client' import { tl } from '@mtcute/tl' import { Parser } from 'htmlparser2' import bigInt from 'big-integer' @@ -13,10 +13,11 @@ const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&| * const escaped = html`${user.displayName}` * ``` */ -export function html(strings: TemplateStringsArray, ...sub: string[]): string { +export function html(strings: TemplateStringsArray, ...sub: (string | RawString)[]): string { let str = '' sub.forEach((it, idx) => { - str += strings[idx] + HtmlMessageEntityParser.escape(it) + if (typeof it === 'string') it = HtmlMessageEntityParser.escape(it) + str += strings[idx] + it }) return str + strings[strings.length - 1] } diff --git a/packages/html-parser/tests/html-parser.spec.ts b/packages/html-parser/tests/html-parser.spec.ts index df4db8d3..33a1c5b5 100644 --- a/packages/html-parser/tests/html-parser.spec.ts +++ b/packages/html-parser/tests/html-parser.spec.ts @@ -2,7 +2,7 @@ import { describe, it } from 'mocha' import { expect } from 'chai' import { tl } from '@mtcute/tl' import { HtmlMessageEntityParser, html } from '../src' -import { MessageEntity } from '@mtcute/client' +import { MessageEntity, RawString } from '@mtcute/client' import bigInt from 'big-integer' const createEntity = ( @@ -460,5 +460,17 @@ describe('HtmlMessageEntityParser', () => { expect(html`text ${unsafeString}`).eq('text <&>') expect(html`${unsafeString}`).eq('<&>') }) + + it('should skip with RawString', () => { + const unsafeString2 = '<&>' + const unsafeString = new RawString('<&>') + + expect(html`${unsafeString}`).eq('<&>') + expect(html`${unsafeString} ${unsafeString2}`).eq('<&> <&>') + expect(html`${unsafeString} text`).eq('<&> text') + expect(html`text ${unsafeString}`).eq('text <&>') + expect(html`${unsafeString}`).eq('<&>') + expect(html`${unsafeString} ${unsafeString2}`).eq('<&> <&>') + }) }) }) diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index e4426219..a4bc927c 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -1,6 +1,7 @@ import type { IMessageEntityParser, MessageEntity } from '@mtcute/client' import { tl } from '@mtcute/tl' import bigInt from 'big-integer' +import { RawString } from '@mtcute/client' const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ @@ -21,10 +22,11 @@ const TO_BE_ESCAPED = /[*_\-~`[\\\]]/g * const escaped = md`**${user.displayName}**` * ``` */ -export function md(strings: TemplateStringsArray, ...sub: string[]): string { +export function md(strings: TemplateStringsArray, ...sub: (string | RawString)[]): string { let str = '' sub.forEach((it, idx) => { - str += strings[idx] + MarkdownMessageEntityParser.escape(it) + if (typeof it === 'string') it = MarkdownMessageEntityParser.escape(it as string) + str += strings[idx] + it }) return str + strings[strings.length - 1] } diff --git a/packages/markdown-parser/tests/markdown-parser.spec.ts b/packages/markdown-parser/tests/markdown-parser.spec.ts index 99f4a5b1..a2fc2529 100644 --- a/packages/markdown-parser/tests/markdown-parser.spec.ts +++ b/packages/markdown-parser/tests/markdown-parser.spec.ts @@ -1,7 +1,7 @@ import { describe, it } from 'mocha' import { expect } from 'chai' import { tl } from '@mtcute/tl' -import { MessageEntity } from '@mtcute/client' +import { MessageEntity, RawString } from '@mtcute/client' import { MarkdownMessageEntityParser, md } from '../src' import bigInt from 'big-integer' @@ -657,5 +657,16 @@ describe('MarkdownMessageEntityParser', () => { expect(md`**text** ${unsafeString}`).eq('**text** \\_\\_\\[\\]\\_\\_') expect(md`**${unsafeString}**`).eq('**\\_\\_\\[\\]\\_\\_**') }) + + it('should skip with RawString', () => { + const unsafeString2 = '__[]__' + const unsafeString = new RawString('__[]__') + + expect(md`${unsafeString}`).eq('__[]__') + expect(md`${unsafeString} ${unsafeString2}`).eq('__[]__ \\_\\_\\[\\]\\_\\_') + expect(md`${unsafeString} **text**`).eq('__[]__ **text**') + expect(md`**text** ${unsafeString}`).eq('**text** __[]__') + expect(md`**${unsafeString} ${unsafeString2}**`).eq('**__[]__ \\_\\_\\[\\]\\_\\_**') + }) }) })