feat: parse mode template literals override default/passed parse mode
This commit is contained in:
parent
be8ffe5b5b
commit
d611f91f19
20 changed files with 123 additions and 83 deletions
|
@ -171,6 +171,7 @@ import {
|
|||
ChatsIndex,
|
||||
Dialog,
|
||||
FileDownloadParameters,
|
||||
FormattedString,
|
||||
GameHighScore,
|
||||
IMessageEntityParser,
|
||||
InputFileLike,
|
||||
|
@ -1829,7 +1830,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*
|
||||
* When `media` is passed, `media.caption` is used instead
|
||||
*/
|
||||
text?: string
|
||||
text?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse entities before sending
|
||||
|
@ -1890,7 +1891,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*
|
||||
* When `media` is passed, `media.caption` is used instead
|
||||
*/
|
||||
text?: string
|
||||
text?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse entities before sending
|
||||
|
@ -1998,7 +1999,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* You can either pass `caption` or `captionMedia`, passing both will
|
||||
* result in an error
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Optionally, a media caption for your forwarded message(s).
|
||||
|
@ -2384,7 +2385,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
/**
|
||||
* New message caption (only used for media)
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse `text` entities before sending
|
||||
|
@ -2546,7 +2547,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* Can be used, for example. when using File IDs
|
||||
* or when using existing InputMedia objects.
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Override entities for `media`.
|
||||
|
@ -2636,7 +2637,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
sendText(
|
||||
chatId: InputPeerLike,
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: {
|
||||
/**
|
||||
* Message to reply to. Either a message object or message ID.
|
||||
|
@ -2785,10 +2786,9 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* mode is also set as default.
|
||||
*
|
||||
* @param parseMode Parse mode to register
|
||||
* @param name (default: `parseMode.name`) Parse mode name. By default is taken from the object.
|
||||
* @throws MtCuteError When the parse mode with a given name is already registered.
|
||||
*/
|
||||
registerParseMode(parseMode: IMessageEntityParser, name?: string): void
|
||||
registerParseMode(parseMode: IMessageEntityParser): void
|
||||
/**
|
||||
* Unregister a parse mode by its name.
|
||||
* Will silently fail if given parse mode does not exist.
|
||||
|
|
|
@ -39,7 +39,8 @@ import {
|
|||
BotCommands,
|
||||
MessageMedia,
|
||||
RawDocument,
|
||||
IMessageEntityParser
|
||||
IMessageEntityParser,
|
||||
FormattedString
|
||||
} from '../types'
|
||||
|
||||
// @copy
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { BotKeyboard, InputMediaLike, ReplyMarkup } from '../../types'
|
||||
import { BotKeyboard, FormattedString, InputMediaLike, ReplyMarkup } from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ export async function editInlineMessage(
|
|||
*
|
||||
* When `media` is passed, `media.caption` is used instead
|
||||
*/
|
||||
text?: string
|
||||
text?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse entities before sending
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
BotKeyboard,
|
||||
BotKeyboard, FormattedString,
|
||||
InputMediaLike,
|
||||
InputPeerLike,
|
||||
Message,
|
||||
|
@ -26,7 +26,7 @@ export async function editMessage(
|
|||
*
|
||||
* When `media` is passed, `media.caption` is used instead
|
||||
*/
|
||||
text?: string
|
||||
text?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse entities before sending
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
FormattedString,
|
||||
InputMediaLike,
|
||||
InputPeerLike,
|
||||
Message,
|
||||
|
@ -74,7 +75,7 @@ export async function forwardMessages(
|
|||
* You can either pass `caption` or `captionMedia`, passing both will
|
||||
* result in an error
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Optionally, a media caption for your forwarded message(s).
|
||||
|
@ -139,7 +140,7 @@ export async function forwardMessages(
|
|||
* You can either pass `caption` or `captionMedia`, passing both will
|
||||
* result in an error
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Optionally, a media caption for your forwarded message(s).
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
import { TelegramClient } from '../../client'
|
||||
import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||
import { FormattedString, MtCuteError } from '../../types'
|
||||
|
||||
const empty: [string, undefined] = ['', undefined]
|
||||
|
||||
/** @internal */
|
||||
export async function _parseEntities(
|
||||
this: TelegramClient,
|
||||
text?: string,
|
||||
text?: string | FormattedString,
|
||||
mode?: string | null,
|
||||
entities?: tl.TypeMessageEntity[]
|
||||
): Promise<[string, tl.TypeMessageEntity[] | undefined]> {
|
||||
|
@ -15,12 +16,22 @@ export async function _parseEntities(
|
|||
return empty
|
||||
}
|
||||
|
||||
if (typeof text === 'object') {
|
||||
mode = text.mode
|
||||
text = text.value
|
||||
}
|
||||
|
||||
if (!entities) {
|
||||
if (mode === undefined) {
|
||||
mode = this._defaultParseMode
|
||||
}
|
||||
// either explicitly disabled or no available parser
|
||||
if (!mode) return [text, []]
|
||||
|
||||
if (!(mode in this._parseModes)) {
|
||||
throw new MtCuteError(`Parse mode ${mode} is not registered.`)
|
||||
}
|
||||
|
||||
;[text, entities] = await this._parseModes[mode].parse(text)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Message, ReplyMarkup } from '../../types'
|
||||
import { InputPeerLike, Message, FormattedString, ReplyMarkup } from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { MessageNotFoundError } from '@mtcute/tl/errors'
|
||||
|
||||
|
@ -45,7 +45,7 @@ export async function sendCopy(
|
|||
/**
|
||||
* New message caption (only used for media)
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse `text` entities before sending
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
BotKeyboard,
|
||||
BotKeyboard, FormattedString,
|
||||
InputMediaLike,
|
||||
InputPeerLike,
|
||||
Message, MtCuteArgumentError,
|
||||
|
@ -37,7 +37,7 @@ export async function sendMedia(
|
|||
* Can be used, for example. when using File IDs
|
||||
* or when using existing InputMedia objects.
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Override entities for `media`.
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
UsersIndex,
|
||||
MtCuteTypeAssertionError,
|
||||
ChatsIndex,
|
||||
MtCuteArgumentError,
|
||||
MtCuteArgumentError, FormattedString,
|
||||
} from '../../types'
|
||||
import { getMarkedPeerId, MessageNotFoundError } from '@mtcute/core'
|
||||
import { createDummyUpdate } from '../../utils/updates-utils'
|
||||
|
@ -30,7 +30,7 @@ import { createDummyUpdate } from '../../utils/updates-utils'
|
|||
export async function sendText(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: {
|
||||
/**
|
||||
* Message to reply to. Either a message object or message ID.
|
||||
|
|
|
@ -7,15 +7,15 @@ import { MtCuteError, IMessageEntityParser } from '../../types'
|
|||
* mode is also set as default.
|
||||
*
|
||||
* @param parseMode Parse mode to register
|
||||
* @param name Parse mode name. By default is taken from the object.
|
||||
* @throws MtCuteError When the parse mode with a given name is already registered.
|
||||
* @internal
|
||||
*/
|
||||
export function registerParseMode(
|
||||
this: TelegramClient,
|
||||
parseMode: IMessageEntityParser,
|
||||
name: string = parseMode.name
|
||||
parseMode: IMessageEntityParser
|
||||
): void {
|
||||
const name = parseMode.name
|
||||
|
||||
if (name in this._parseModes) {
|
||||
throw new MtCuteError(
|
||||
`Parse mode ${name} is already registered. Unregister it first!`
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
InputMediaGeoLive,
|
||||
InputMediaVenue,
|
||||
} from '../../media'
|
||||
import { FormattedString } from '../../parser'
|
||||
|
||||
/**
|
||||
* Inline message containing only text
|
||||
|
@ -17,7 +18,7 @@ export interface InputInlineMessageText {
|
|||
/**
|
||||
* Text of the message
|
||||
*/
|
||||
text: string
|
||||
text: string | FormattedString
|
||||
|
||||
/**
|
||||
* Text markup entities.
|
||||
|
@ -46,7 +47,7 @@ export interface InputInlineMessageMedia {
|
|||
/**
|
||||
* Caption for the media
|
||||
*/
|
||||
text?: string
|
||||
text?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Caption markup entities.
|
||||
|
|
|
@ -2,6 +2,7 @@ import { InputFileLike } from '../files'
|
|||
import { tl } from '@mtcute/tl'
|
||||
import { Venue } from './venue'
|
||||
import { MaybeArray } from '@mtcute/core'
|
||||
import { FormattedString } from '../parser'
|
||||
|
||||
interface BaseInputMedia {
|
||||
/**
|
||||
|
@ -12,7 +13,7 @@ interface BaseInputMedia {
|
|||
/**
|
||||
* Caption of the media
|
||||
*/
|
||||
caption?: string
|
||||
caption?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Caption entities of the media.
|
||||
|
@ -517,7 +518,7 @@ export interface InputMediaQuiz extends Omit<InputMediaPoll, 'type'> {
|
|||
/**
|
||||
* Explanation of the quiz solution
|
||||
*/
|
||||
solution?: string
|
||||
solution?: string | FormattedString
|
||||
|
||||
/**
|
||||
* Format entities for `solution`.
|
||||
|
|
|
@ -12,6 +12,7 @@ import { makeInspectable } from '../utils'
|
|||
import { InputMediaLike, WebPage } from '../media'
|
||||
import { _messageActionFromTl, MessageAction } from './message-action'
|
||||
import { _messageMediaFromTl, MessageMedia } from './message-media'
|
||||
import { FormattedString } from '../parser'
|
||||
|
||||
/**
|
||||
* A message or a service message
|
||||
|
@ -557,7 +558,7 @@ export class Message {
|
|||
* @param params
|
||||
*/
|
||||
answerText(
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: Parameters<TelegramClient['sendText']>[2]
|
||||
): ReturnType<TelegramClient['sendText']> {
|
||||
return this.client.sendText(this.chat.inputPeer, text, params)
|
||||
|
@ -602,7 +603,7 @@ export class Message {
|
|||
* @param params
|
||||
*/
|
||||
replyText(
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: Parameters<TelegramClient['sendText']>[2]
|
||||
): ReturnType<TelegramClient['sendText']> {
|
||||
if (!params) params = {}
|
||||
|
@ -657,7 +658,7 @@ export class Message {
|
|||
* @param params
|
||||
*/
|
||||
commentText(
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: Parameters<TelegramClient['sendText']>[2]
|
||||
): ReturnType<TelegramClient['sendText']> {
|
||||
if (this.chat.type !== 'channel') {
|
||||
|
@ -790,7 +791,7 @@ export class Message {
|
|||
* @link TelegramClient.editMessage
|
||||
*/
|
||||
editText(
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: Omit<Parameters<TelegramClient['editMessage']>[2], 'text'>
|
||||
): Promise<Message> {
|
||||
return this.edit({
|
||||
|
|
|
@ -13,9 +13,7 @@ import { MessageEntity } from '../types'
|
|||
*/
|
||||
export interface IMessageEntityParser {
|
||||
/**
|
||||
* Default name for the parser.
|
||||
*
|
||||
* Used when registering the parser as a fallback value for `name`
|
||||
* Parser name, which will be used when registering it.
|
||||
*/
|
||||
name: string
|
||||
|
||||
|
@ -29,9 +27,9 @@ export interface IMessageEntityParser {
|
|||
parse(text: string): [string, tl.TypeMessageEntity[]]
|
||||
|
||||
/**
|
||||
* Add formating to the text given the plain text and the entities.
|
||||
* Add formatting to the text given the plain text and the entities.
|
||||
*
|
||||
* **Note** that `unparse(parse(text)) === text` is not always true!
|
||||
* > **Note**: `unparse(parse(text)) === text` is not always true!
|
||||
*
|
||||
* @param text Plain text
|
||||
* @param entities Message entities that should be added to the text
|
||||
|
@ -43,14 +41,14 @@ export interface IMessageEntityParser {
|
|||
* 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) {}
|
||||
export class FormattedString {
|
||||
/**
|
||||
* @param value Value that the string holds
|
||||
* @param mode Name of the parse mode used
|
||||
*/
|
||||
constructor (readonly value: string, readonly mode?: string) {}
|
||||
|
||||
toString(): string {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
|
||||
RawString.prototype.raw = true
|
||||
|
|
|
@ -8,7 +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'
|
||||
import { FormattedString } from '../parser'
|
||||
|
||||
export namespace Chat {
|
||||
/**
|
||||
|
@ -552,7 +552,7 @@ export class Chat {
|
|||
* msg.replyText(`Hello, ${msg.chat.mention()`)
|
||||
* ```
|
||||
*/
|
||||
mention(text?: string | null, parseMode?: string | null): string | RawString {
|
||||
mention(text?: string | null, parseMode?: string | null): string | FormattedString {
|
||||
if (this.user) return this.user.mention(text, parseMode)
|
||||
|
||||
if (text === undefined && this.username) {
|
||||
|
@ -564,7 +564,7 @@ export class Chat {
|
|||
|
||||
if (!parseMode) parseMode = this.client['_defaultParseMode']
|
||||
|
||||
return new RawString(this.client.getParseMode(parseMode).unparse(text, [
|
||||
return new FormattedString(this.client.getParseMode(parseMode).unparse(text, [
|
||||
{
|
||||
raw: undefined as any,
|
||||
type: 'text_link',
|
||||
|
@ -628,7 +628,7 @@ export class Chat {
|
|||
* @param params
|
||||
*/
|
||||
sendText(
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: Parameters<TelegramClient['sendText']>[2]
|
||||
): ReturnType<TelegramClient['sendText']> {
|
||||
return this.client.sendText(this.inputPeer, text, params)
|
||||
|
|
|
@ -5,7 +5,7 @@ import { MtCuteArgumentError } from '../errors'
|
|||
import { makeInspectable } from '../utils'
|
||||
import { assertTypeIs } from '../../utils/type-assertion'
|
||||
import { InputMediaLike } from '../media'
|
||||
import { RawString } from '../parser'
|
||||
import { FormattedString } from '../parser'
|
||||
|
||||
export namespace User {
|
||||
/**
|
||||
|
@ -294,7 +294,7 @@ export class User {
|
|||
* msg.replyText(`Hello, ${msg.sender.mention()`)
|
||||
* ```
|
||||
*/
|
||||
mention(text?: string | null, parseMode?: string | null): string | RawString {
|
||||
mention(text?: string | null, parseMode?: string | null): string | FormattedString {
|
||||
if (text === undefined && this.username) {
|
||||
return `@${this.username}`
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ export class User {
|
|||
if (!text) text = this.displayName
|
||||
if (!parseMode) parseMode = this.client['_defaultParseMode']
|
||||
|
||||
return new RawString(this.client.getParseMode(parseMode).unparse(text, [
|
||||
return new FormattedString(this.client.getParseMode(parseMode).unparse(text, [
|
||||
{
|
||||
raw: undefined as any,
|
||||
type: 'text_mention',
|
||||
|
@ -369,7 +369,7 @@ export class User {
|
|||
* @param params
|
||||
*/
|
||||
sendText(
|
||||
text: string,
|
||||
text: string | FormattedString,
|
||||
params?: Parameters<TelegramClient['sendText']>[2]
|
||||
): ReturnType<TelegramClient['sendText']> {
|
||||
return this.client.sendText(this.inputPeer, text, params)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { IMessageEntityParser, MessageEntity, RawString } from '@mtcute/client'
|
||||
import type { IMessageEntityParser, MessageEntity, FormattedString } from '@mtcute/client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { Parser } from 'htmlparser2'
|
||||
import bigInt from 'big-integer'
|
||||
|
@ -13,13 +13,18 @@ const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|
|
|||
* const escaped = html`<b>${user.displayName}</b>`
|
||||
* ```
|
||||
*/
|
||||
export function html(strings: TemplateStringsArray, ...sub: (string | RawString)[]): string {
|
||||
export function html(strings: TemplateStringsArray, ...sub: (string | FormattedString)[]): FormattedString {
|
||||
let str = ''
|
||||
sub.forEach((it, idx) => {
|
||||
if (typeof it === 'string') it = HtmlMessageEntityParser.escape(it)
|
||||
else {
|
||||
if (it.mode && it.mode !== 'html') throw new Error(`Incompatible parse mode: ${it.mode}`)
|
||||
it = it.value
|
||||
}
|
||||
|
||||
str += strings[idx] + it
|
||||
})
|
||||
return str + strings[strings.length - 1]
|
||||
return { value: str + strings[strings.length - 1], mode: 'html' }
|
||||
}
|
||||
|
||||
export namespace HtmlMessageEntityParser {
|
||||
|
|
|
@ -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, RawString } from '@mtcute/client'
|
||||
import { MessageEntity, FormattedString } from '@mtcute/client'
|
||||
import bigInt from 'big-integer'
|
||||
|
||||
const createEntity = <T extends tl.TypeMessageEntity['_']>(
|
||||
|
@ -455,22 +455,30 @@ describe('HtmlMessageEntityParser', () => {
|
|||
it('should work as a tagged template literal', () => {
|
||||
const unsafeString = '<&>'
|
||||
|
||||
expect(html`${unsafeString}`).eq('<&>')
|
||||
expect(html`${unsafeString} <b>text</b>`).eq('<&> <b>text</b>')
|
||||
expect(html`<b>text</b> ${unsafeString}`).eq('<b>text</b> <&>')
|
||||
expect(html`<b>${unsafeString}</b>`).eq('<b><&></b>')
|
||||
expect(html`${unsafeString}`.value).eq('<&>')
|
||||
expect(html`${unsafeString} <b>text</b>`.value).eq('<&> <b>text</b>')
|
||||
expect(html`<b>text</b> ${unsafeString}`.value).eq('<b>text</b> <&>')
|
||||
expect(html`<b>${unsafeString}</b>`.value).eq('<b><&></b>')
|
||||
})
|
||||
|
||||
it('should skip with RawString', () => {
|
||||
it('should skip with FormattedString', () => {
|
||||
const unsafeString2 = '<&>'
|
||||
const unsafeString = new RawString('<&>')
|
||||
const unsafeString = new FormattedString('<&>')
|
||||
|
||||
expect(html`${unsafeString}`).eq('<&>')
|
||||
expect(html`${unsafeString} ${unsafeString2}`).eq('<&> <&>')
|
||||
expect(html`${unsafeString} <b>text</b>`).eq('<&> <b>text</b>')
|
||||
expect(html`<b>text</b> ${unsafeString}`).eq('<b>text</b> <&>')
|
||||
expect(html`<b>${unsafeString}</b>`).eq('<b><&></b>')
|
||||
expect(html`<b>${unsafeString} ${unsafeString2}</b>`).eq('<b><&> <&></b>')
|
||||
expect(html`${unsafeString}`.value).eq('<&>')
|
||||
expect(html`${unsafeString} ${unsafeString2}`.value).eq('<&> <&>')
|
||||
expect(html`${unsafeString} <b>text</b>`.value).eq('<&> <b>text</b>')
|
||||
expect(html`<b>text</b> ${unsafeString}`.value).eq('<b>text</b> <&>')
|
||||
expect(html`<b>${unsafeString}</b>`.value).eq('<b><&></b>')
|
||||
expect(html`<b>${unsafeString} ${unsafeString2}</b>`.value).eq('<b><&> <&></b>')
|
||||
})
|
||||
|
||||
it('should error with incompatible FormattedString', () => {
|
||||
const unsafeString = new FormattedString('<&>', 'html')
|
||||
const unsafeString2 = new FormattedString('<&>', 'some-other-mode')
|
||||
|
||||
expect(() => html`${unsafeString}`.value).not.throw(Error)
|
||||
expect(() => html`${unsafeString2}`.value).throw(Error)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { IMessageEntityParser, MessageEntity } from '@mtcute/client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import bigInt from 'big-integer'
|
||||
import { RawString } from '@mtcute/client'
|
||||
import { FormattedString } from '@mtcute/client'
|
||||
|
||||
const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/
|
||||
|
||||
|
@ -22,13 +22,18 @@ const TO_BE_ESCAPED = /[*_\-~`[\\\]]/g
|
|||
* const escaped = md`**${user.displayName}**`
|
||||
* ```
|
||||
*/
|
||||
export function md(strings: TemplateStringsArray, ...sub: (string | RawString)[]): string {
|
||||
export function md(strings: TemplateStringsArray, ...sub: (string | FormattedString)[]): FormattedString {
|
||||
let str = ''
|
||||
sub.forEach((it, idx) => {
|
||||
if (typeof it === 'string') it = MarkdownMessageEntityParser.escape(it as string)
|
||||
else {
|
||||
if (it.mode && it.mode !== 'markdown') throw new Error(`Incompatible parse mode: ${it.mode}`)
|
||||
it = it.value
|
||||
}
|
||||
|
||||
str += strings[idx] + it
|
||||
})
|
||||
return str + strings[strings.length - 1]
|
||||
return { value: str + strings[strings.length - 1], mode: 'markdown' }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, it } from 'mocha'
|
||||
import { expect } from 'chai'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { MessageEntity, RawString } from '@mtcute/client'
|
||||
import { MessageEntity, FormattedString } from '@mtcute/client'
|
||||
import { MarkdownMessageEntityParser, md } from '../src'
|
||||
import bigInt from 'big-integer'
|
||||
|
||||
|
@ -652,21 +652,29 @@ describe('MarkdownMessageEntityParser', () => {
|
|||
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('**\\_\\_\\[\\]\\_\\_**')
|
||||
expect(md`${unsafeString}`.value).eq('\\_\\_\\[\\]\\_\\_')
|
||||
expect(md`${unsafeString} **text**`.value).eq('\\_\\_\\[\\]\\_\\_ **text**')
|
||||
expect(md`**text** ${unsafeString}`.value).eq('**text** \\_\\_\\[\\]\\_\\_')
|
||||
expect(md`**${unsafeString}**`.value).eq('**\\_\\_\\[\\]\\_\\_**')
|
||||
})
|
||||
|
||||
it('should skip with RawString', () => {
|
||||
it('should skip with FormattedString', () => {
|
||||
const unsafeString2 = '__[]__'
|
||||
const unsafeString = new RawString('__[]__')
|
||||
const unsafeString = new FormattedString('__[]__')
|
||||
|
||||
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('**__[]__ \\_\\_\\[\\]\\_\\_**')
|
||||
expect(md`${unsafeString}`.value).eq('__[]__')
|
||||
expect(md`${unsafeString} ${unsafeString2}`.value).eq('__[]__ \\_\\_\\[\\]\\_\\_')
|
||||
expect(md`${unsafeString} **text**`.value).eq('__[]__ **text**')
|
||||
expect(md`**text** ${unsafeString}`.value).eq('**text** __[]__')
|
||||
expect(md`**${unsafeString} ${unsafeString2}**`.value).eq('**__[]__ \\_\\_\\[\\]\\_\\_**')
|
||||
})
|
||||
|
||||
it('should error with incompatible FormattedString', () => {
|
||||
const unsafeString = new FormattedString('<&>', 'markdown')
|
||||
const unsafeString2 = new FormattedString('<&>', 'some-other-mode')
|
||||
|
||||
expect(() => md`${unsafeString}`.value).not.throw(Error)
|
||||
expect(() => md`${unsafeString2}`.value).throw(Error)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue