refactor: improved typings for MessageEntity

This commit is contained in:
alina 🌸 2023-10-06 04:53:19 +03:00
parent 5600f292f7
commit 75021648eb
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
14 changed files with 184 additions and 180 deletions

View file

@ -1,5 +1,3 @@
import { isPresent } from '@mtcute/core/utils'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { InputPeerLike, MessageEntity } from '../../types' import { InputPeerLike, MessageEntity } from '../../types'
@ -28,5 +26,5 @@ export async function translateMessage(
toLang: toLanguage, toLang: toLanguage,
}) })
return [res.result[0].text, res.result[0].entities.map((it) => MessageEntity._parse(it)).filter(isPresent)] return [res.result[0].text, res.result[0].entities.map((it) => new MessageEntity(it, res.result[0].text))]
} }

View file

@ -1,5 +1,4 @@
import { tl } from '@mtcute/core' import { tl } from '@mtcute/core'
import { isPresent } from '@mtcute/core/utils'
import { makeInspectable } from '../../utils' import { makeInspectable } from '../../utils'
import { MessageEntity } from '../messages' import { MessageEntity } from '../messages'
@ -37,7 +36,7 @@ export class TermsOfService {
* Terms of Service entities text * Terms of Service entities text
*/ */
get entities(): ReadonlyArray<MessageEntity> { get entities(): ReadonlyArray<MessageEntity> {
return (this._entities ??= this.tos.entities.map((it) => MessageEntity._parse(it)).filter(isPresent)) return (this._entities ??= this.tos.entities.map((it) => new MessageEntity(it, this.tos.text)))
} }
} }

View file

@ -152,8 +152,7 @@ export class Poll {
if (this.results.solutionEntities?.length) { if (this.results.solutionEntities?.length) {
for (const ent of this.results.solutionEntities) { for (const ent of this.results.solutionEntities) {
const parsed = MessageEntity._parse(ent) this._entities.push(new MessageEntity(ent, this.results.solution))
if (parsed) this._entities.push(parsed)
} }
} }
} }
@ -168,9 +167,9 @@ export class Poll {
* @param parseMode Parse mode to use (`null` for default) * @param parseMode Parse mode to use (`null` for default)
*/ */
unparseSolution(parseMode?: string | null): string | null { unparseSolution(parseMode?: string | null): string | null {
if (!this.solution) return null if (!this.results?.solutionEntities) return null
return this.client.getParseMode(parseMode).unparse(this.solution, this.solutionEntities!) return this.client.getParseMode(parseMode).unparse(this.results.solution!, this.results.solutionEntities)
} }
/** /**

View file

@ -48,8 +48,7 @@ export class DraftMessage {
if (this.raw.entities?.length) { if (this.raw.entities?.length) {
for (const ent of this.raw.entities) { for (const ent of this.raw.entities) {
const parsed = MessageEntity._parse(ent) this._entities.push(new MessageEntity(ent, this.raw.message))
if (parsed) this._entities.push(parsed)
} }
} }
} }

View file

@ -2,34 +2,13 @@ import { tl } from '@mtcute/core'
import { makeInspectable } from '../../utils' import { makeInspectable } from '../../utils'
const entityToType: Partial<Record<tl.TypeMessageEntity['_'], MessageEntityType>> = {
messageEntityBlockquote: 'blockquote',
messageEntityBold: 'bold',
messageEntityBotCommand: 'bot_command',
messageEntityCashtag: 'cashtag',
messageEntityCode: 'code',
messageEntityEmail: 'email',
messageEntityHashtag: 'hashtag',
messageEntityItalic: 'italic',
messageEntityMention: 'mention',
messageEntityMentionName: 'text_mention',
messageEntityPhone: 'phone_number',
messageEntityPre: 'pre',
messageEntityStrike: 'strikethrough',
messageEntitySpoiler: 'spoiler',
messageEntityTextUrl: 'text_link',
messageEntityUnderline: 'underline',
messageEntityUrl: 'url',
messageEntityCustomEmoji: 'emoji',
}
/** /**
* Type of the entity. Can be: * Params of the entity. `.kind` can be:
* - 'mention': `@username`. * - 'mention': `@username`.
* - 'hashtag': `#hashtag`. * - 'hashtag': `#hashtag`.
* - 'cashtag': `$USD`. * - 'cashtag': `$USD`.
* - 'bot_command': `/start`. * - 'bot_command': `/start`.
* - 'url': `https://example.com` (see {@link MessageEntity.url}). * - 'url': `https://example.com`
* - 'email': `example@example.com`. * - 'email': `example@example.com`.
* - 'phone_number': `+42000`. * - 'phone_number': `+42000`.
* - 'bold': **bold text**. * - 'bold': **bold text**.
@ -37,96 +16,145 @@ const entityToType: Partial<Record<tl.TypeMessageEntity['_'], MessageEntityType>
* - 'underline': <u>underlined</u> text. * - 'underline': <u>underlined</u> text.
* - 'strikethrough': <s>strikethrough</s> text. * - 'strikethrough': <s>strikethrough</s> text.
* - 'code': `monospaced` string. * - 'code': `monospaced` string.
* - 'pre': `monospaced` block (see {@link MessageEntity.language}). * - 'pre': `monospaced` block. `.language` contains the language of the block (if available).
* - 'text_link': for clickable text URLs. * - 'text_link': for clickable text URLs.
* - 'text_mention': for users without usernames (see {@link MessageEntity.user} below). * - 'text_mention': for user mention by name. `.userId` contains the ID of the mentioned user.
* - 'blockquote': A blockquote * - 'blockquote': A blockquote
* - 'emoji': A custom emoji * - 'emoji': A custom emoji. `.emojiId` contains the emoji ID.
*/ */
export type MessageEntityType = export type MessageEntityParams =
| 'mention' | {
| 'hashtag' kind:
| 'cashtag' | 'mention'
| 'bot_command' | 'hashtag'
| 'url' | 'cashtag'
| 'email' | 'bot_command'
| 'phone_number' | 'url'
| 'bold' | 'email'
| 'italic' | 'phone_number'
| 'underline' | 'bold'
| 'strikethrough' | 'italic'
| 'spoiler' | 'underline'
| 'code' | 'strikethrough'
| 'pre' | 'spoiler'
| 'text_link' | 'code'
| 'text_mention' | 'blockquote'
| 'blockquote' | 'bank_card'
| 'emoji' | 'unknown'
}
| { kind: 'pre'; language?: string }
| { kind: 'text_link'; url: string }
| { kind: 'text_mention'; userId: number }
| { kind: 'emoji'; emojiId: tl.Long }
/**
* Kind of the entity. For more information, see {@link MessageEntityParams}
*/
export type MessageEntityKind = MessageEntityParams['kind']
/** /**
* One special entity in a text message (like mention, hashtag, URL, etc.) * One special entity in a text message (like mention, hashtag, URL, etc.)
*/ */
export class MessageEntity<Type extends MessageEntityType = MessageEntityType> { export class MessageEntity {
/** constructor(readonly raw: tl.TypeMessageEntity, readonly _text?: string) {}
* Underlying raw TL object
*/
readonly raw!: tl.TypeMessageEntity
/**
* Type of the entity. See {@link MessageEntity.Type} for a list of possible values
*/
readonly type!: MessageEntityType
/** /**
* Offset in UTF-16 code units to the start of the entity. * Offset in UTF-16 code units to the start of the entity.
* *
* Since JS strings are UTF-16, you can use this as-is * Since JS strings are UTF-16, you can use this as-is
*/ */
readonly offset!: number get offset() {
return this.raw.offset
}
/** /**
* Length of the entity in UTF-16 code units. * Length of the entity in UTF-16 code units.
* *
* Since JS strings are UTF-16, you can use this as-is * Since JS strings are UTF-16, you can use this as-is
*/ */
readonly length!: number get length() {
return this.raw.length
}
/** /**
* When `type=text_link`, contains the URL that would be opened if user taps on the text * Kind of the entity (see {@link MessageEntityParams})
*/ */
readonly url?: Type extends 'text_link' ? string : never get kind(): MessageEntityKind {
return this.params.kind
}
private _params?: MessageEntityParams
/** /**
* When `type=text_mention`, contains the ID of the user mentioned. * Params of the entity
*/ */
readonly userId?: number get params(): MessageEntityParams {
if (this._params) return this._params
/** switch (this.raw._) {
* When `type=pre`, contains the programming language of the entity text case 'messageEntityMention':
*/ return (this._params = { kind: 'mention' })
readonly language?: string case 'messageEntityHashtag':
return (this._params = { kind: 'hashtag' })
/** case 'messageEntityCashtag':
* When `type=emoji`, ID of the custom emoji. return (this._params = { kind: 'cashtag' })
* The emoji itself must be loaded separately (and presumably cached) case 'messageEntityBotCommand':
* using {@link TelegramClient#getCustomEmojis} return (this._params = { kind: 'bot_command' })
*/ case 'messageEntityUrl':
readonly emojiId?: tl.Long return (this._params = { kind: 'url' })
case 'messageEntityEmail':
static _parse(obj: tl.TypeMessageEntity): MessageEntity | null { return (this._params = { kind: 'email' })
const type = entityToType[obj._] case 'messageEntityPhone':
if (!type) return null return (this._params = { kind: 'phone_number' })
case 'messageEntityBold':
return { return (this._params = { kind: 'bold' })
raw: obj, case 'messageEntityItalic':
type, return (this._params = { kind: 'italic' })
offset: obj.offset, case 'messageEntityUnderline':
length: obj.length, return (this._params = { kind: 'underline' })
url: obj._ === 'messageEntityTextUrl' ? obj.url : undefined, case 'messageEntityStrike':
userId: obj._ === 'messageEntityMentionName' ? obj.userId : undefined, return (this._params = { kind: 'strikethrough' })
language: obj._ === 'messageEntityPre' ? obj.language : undefined, case 'messageEntitySpoiler':
emojiId: obj._ === 'messageEntityCustomEmoji' ? obj.documentId : undefined, return (this._params = { kind: 'spoiler' })
case 'messageEntityCode':
return (this._params = { kind: 'code' })
case 'messageEntityPre':
return (this._params = { kind: 'pre', language: this.raw.language })
case 'messageEntityTextUrl':
return (this._params = { kind: 'text_link', url: this.raw.url })
case 'messageEntityMentionName':
return (this._params = { kind: 'text_mention', userId: this.raw.userId })
case 'messageEntityBlockquote':
return (this._params = { kind: 'blockquote' })
case 'messageEntityCustomEmoji':
return (this._params = { kind: 'emoji', emojiId: this.raw.documentId })
case 'messageEntityBankCard':
return (this._params = { kind: 'bank_card' })
} }
return (this._params = { kind: 'unknown' })
}
/**
* Text contained in this entity.
*
* > **Note**: This does not take into account that entities may overlap,
* > and is only useful for simple cases.
*/
get text(): string {
if (!this._text) return ''
return this._text.slice(this.raw.offset, this.raw.offset + this.raw.length)
}
/**
* Checks if this entity is of the given type, and adjusts the type accordingly.
* @param kind
* @returns
*/
is<const T extends MessageEntityKind>(
kind: T,
): this is MessageEntity & { content: Extract<MessageEntityParams, { kind: T }>; kind: T } {
return this.params.kind === kind
} }
} }

View file

@ -404,8 +404,7 @@ export class Message {
if (this.raw._ === 'message' && this.raw.entities?.length) { if (this.raw._ === 'message' && this.raw.entities?.length) {
for (const ent of this.raw.entities) { for (const ent of this.raw.entities) {
const parsed = MessageEntity._parse(ent) this._entities.push(new MessageEntity(ent, this.raw.message))
if (parsed) this._entities.push(parsed)
} }
} }
} }
@ -572,7 +571,9 @@ export class Message {
* @param parseMode Parse mode to use (`null` for default) * @param parseMode Parse mode to use (`null` for default)
*/ */
unparse(parseMode?: string | null): string { unparse(parseMode?: string | null): string {
return this.client.getParseMode(parseMode).unparse(this.text, this.entities) if (this.raw._ === 'messageService') return ''
return this.client.getParseMode(parseMode).unparse(this.text, this.raw.entities ?? [])
} }
/** /**

View file

@ -1,13 +1,10 @@
import { tl } from '@mtcute/core' import { tl } from '@mtcute/core'
import { MessageEntity } from '../types'
/** /**
* Interface describing a message entity parser. * Interface describing a message entity parser.
*
* mtcute comes with HTML parser inside `@mtcute/html-parser` * mtcute comes with HTML parser inside `@mtcute/html-parser`
* and MarkdownV2 parser inside `@mtcute/markdown-parser`, * and Markdown parser inside `@mtcute/markdown-parser`.
* implemented similar to how they are described
* in the [Bot API documentation](https://core.telegram.org/bots/api#formatting-options).
* *
* You are also free to implement your own parser and register it with * You are also free to implement your own parser and register it with
* {@link TelegramClient.registerParseMode}. * {@link TelegramClient.registerParseMode}.
@ -35,7 +32,7 @@ export interface IMessageEntityParser {
* @param text Plain text * @param text Plain text
* @param entities Message entities that should be added to the text * @param entities Message entities that should be added to the text
*/ */
unparse(text: string, entities: ReadonlyArray<MessageEntity>): string unparse(text: string, entities: ReadonlyArray<tl.TypeMessageEntity>): string
} }
/** /**

View file

@ -588,9 +588,7 @@ export class Chat {
return new FormattedString( return new FormattedString(
this.client.getParseMode(parseMode).unparse(text, [ this.client.getParseMode(parseMode).unparse(text, [
{ {
// eslint-disable-next-line _: 'messageEntityTextUrl',
raw: undefined as any,
type: 'text_link',
offset: 0, offset: 0,
length: text.length, length: text.length,
url: `https://t.me/${this.username}`, url: `https://t.me/${this.username}`,

View file

@ -359,9 +359,7 @@ export class User {
return new FormattedString( return new FormattedString(
this.client.getParseMode(parseMode).unparse(text, [ this.client.getParseMode(parseMode).unparse(text, [
{ {
// eslint-disable-next-line _: 'messageEntityMentionName',
raw: undefined as any,
type: 'text_mention',
offset: 0, offset: 0,
length: text.length, length: text.length,
userId: this.raw.id, userId: this.raw.id,
@ -415,9 +413,7 @@ export class User {
return new FormattedString( return new FormattedString(
this.client.getParseMode(parseMode).unparse(text, [ this.client.getParseMode(parseMode).unparse(text, [
{ {
// eslint-disable-next-line _: 'messageEntityTextUrl',
raw: undefined as any,
type: 'text_link',
offset: 0, offset: 0,
length: text.length, length: text.length,
url: `tg://user?id=${this.id}&hash=${this.raw.accessHash.toString(16)}`, url: `tg://user?id=${this.id}&hash=${this.raw.accessHash.toString(16)}`,

View file

@ -100,8 +100,7 @@ export class Story {
if (this.raw.entities?.length) { if (this.raw.entities?.length) {
for (const ent of this.raw.entities) { for (const ent of this.raw.entities) {
const parsed = MessageEntity._parse(ent) this._entities.push(new MessageEntity(ent, this.raw.caption))
if (parsed) this._entities.push(parsed)
} }
} }
} }

View file

@ -1,7 +1,7 @@
import { Parser } from 'htmlparser2' import { Parser } from 'htmlparser2'
import Long from 'long' import Long from 'long'
import type { FormattedString, IMessageEntityParser, MessageEntity, tl } from '@mtcute/client' import type { FormattedString, IMessageEntityParser, tl } from '@mtcute/client'
const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/
@ -276,14 +276,14 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
return [plainText.replace(/\u00A0/g, ' '), entities] return [plainText.replace(/\u00A0/g, ' '), entities]
} }
unparse(text: string, entities: ReadonlyArray<MessageEntity>): string { unparse(text: string, entities: ReadonlyArray<tl.TypeMessageEntity>): string {
return this._unparse(text, entities) return this._unparse(text, entities)
} }
// internal function that uses recursion to correctly process nested & overlapping entities // internal function that uses recursion to correctly process nested & overlapping entities
private _unparse( private _unparse(
text: string, text: string,
entities: ReadonlyArray<MessageEntity>, entities: ReadonlyArray<tl.TypeMessageEntity>,
entitiesOffset = 0, entitiesOffset = 0,
offset = 0, offset = 0,
length = text.length, length = text.length,
@ -334,59 +334,59 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
const substr = text.substr(relativeOffset, length) const substr = text.substr(relativeOffset, length)
if (!substr) continue if (!substr) continue
const type = entity.type const type = entity._
let entityText let entityText
if (type === 'pre') { if (type === 'messageEntityPre') {
entityText = substr entityText = substr
} else { } else {
entityText = this._unparse(substr, entities, i + 1, offset + relativeOffset, length) entityText = this._unparse(substr, entities, i + 1, offset + relativeOffset, length)
} }
switch (type) { switch (type) {
case 'bold': case 'messageEntityBold':
case 'italic': case 'messageEntityItalic':
case 'underline': case 'messageEntityUnderline':
case 'strikethrough': case 'messageEntityStrike':
html.push(`<${type[0]}>${entityText}</${type[0]}>`) case 'messageEntityCode':
case 'messageEntityBlockquote':
case 'messageEntitySpoiler':
{
const tag = (
{
messageEntityBold: 'b',
messageEntityItalic: 'i',
messageEntityUnderline: 'u',
messageEntityStrike: 's',
messageEntityCode: 'code',
messageEntityBlockquote: 'blockquote',
messageEntitySpoiler: 'spoiler',
} as const
)[type]
html.push(`<${tag}>${entityText}</${tag}>`)
}
break break
case 'code': case 'messageEntityPre':
case 'pre':
html.push( html.push(
`<${type}${entity.language ? ` language="${entity.language}"` : ''}>${ `<pre${entity.language ? ` language="${entity.language}"` : ''}>${
this._syntaxHighlighter && entity.language ? this._syntaxHighlighter && entity.language ?
this._syntaxHighlighter(entityText, entity.language) : this._syntaxHighlighter(entityText, entity.language) :
entityText entityText
}</${type}>`, }</pre>`,
) )
break break
case 'blockquote': case 'messageEntityEmail':
case 'spoiler':
html.push(`<${type}>${entityText}</${type}>`)
break
case 'email':
html.push(`<a href="mailto:${entityText}">${entityText}</a>`) html.push(`<a href="mailto:${entityText}">${entityText}</a>`)
break break
case 'url': case 'messageEntityUrl':
html.push(`<a href="${entityText}">${entityText}</a>`) html.push(`<a href="${entityText}">${entityText}</a>`)
break break
case 'text_link': case 'messageEntityTextUrl':
html.push( html.push(`<a href="${HtmlMessageEntityParser.escape(entity.url, true)}">${entityText}</a>`)
`<a href="${HtmlMessageEntityParser.escape(
// todo improve typings
entity.url!,
true,
)}">${entityText}</a>`,
)
break break
case 'text_mention': case 'messageEntityMentionName':
html.push( html.push(`<a href="tg://user?id=${entity.userId}">${entityText}</a>`)
// todo improve typings
`<a href="tg://user?id=${entity.userId!}">${entityText}</a>`,
)
break break
default: default:
skip = true skip = true

View file

@ -2,8 +2,7 @@ import { expect } from 'chai'
import Long from 'long' import Long from 'long'
import { describe, it } from 'mocha' import { describe, it } from 'mocha'
import { FormattedString, MessageEntity, tl } from '@mtcute/client' import { FormattedString, tl } from '@mtcute/client'
import { isPresent } from '@mtcute/client/utils'
import { html, HtmlMessageEntityParser } from '../src' import { html, HtmlMessageEntityParser } from '../src'
@ -21,16 +20,12 @@ const createEntity = <T extends tl.TypeMessageEntity['_']>(
} as tl.TypeMessageEntity } as tl.TypeMessageEntity
} }
const createEntities = (entities: tl.TypeMessageEntity[]): MessageEntity[] => {
return entities.map((it) => MessageEntity._parse(it)).filter(isPresent)
}
describe('HtmlMessageEntityParser', () => { describe('HtmlMessageEntityParser', () => {
const parser = new HtmlMessageEntityParser() const parser = new HtmlMessageEntityParser()
describe('unparse', () => { describe('unparse', () => {
const test = (text: string, entities: tl.TypeMessageEntity[], expected: string, _parser = parser): void => { const test = (text: string, entities: tl.TypeMessageEntity[], expected: string, _parser = parser): void => {
expect(_parser.unparse(text, createEntities(entities))).eq(expected) expect(_parser.unparse(text, entities)).eq(expected)
} }
it('should return the same text if there are no entities or text', () => { it('should return the same text if there are no entities or text', () => {

View file

@ -1,6 +1,6 @@
import Long from 'long' import Long from 'long'
import type { FormattedString, IMessageEntityParser, MessageEntity, tl } from '@mtcute/client' import type { FormattedString, IMessageEntityParser, tl } from '@mtcute/client'
const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/
const EMOJI_REGEX = /^tg:\/\/emoji\?id=(-?\d+)/ const EMOJI_REGEX = /^tg:\/\/emoji\?id=(-?\d+)/
@ -303,7 +303,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
return [result, entities] return [result, entities]
} }
unparse(text: string, entities: ReadonlyArray<MessageEntity>): string { unparse(text: string, entities: ReadonlyArray<tl.TypeMessageEntity>): string {
// keep track of positions of inserted escape symbols // keep track of positions of inserted escape symbols
const escaped: number[] = [] const escaped: number[] = []
text = text.replace(TO_BE_ESCAPED, (s, pos: number) => { text = text.replace(TO_BE_ESCAPED, (s, pos: number) => {
@ -317,7 +317,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
const insert: InsertLater[] = [] const insert: InsertLater[] = []
for (const entity of entities) { for (const entity of entities) {
const type = entity.type const type = entity._
let start = entity.offset let start = entity.offset
let end = start + entity.length let end = start + entity.length
@ -345,25 +345,25 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
let endTag: string let endTag: string
switch (type) { switch (type) {
case 'bold': case 'messageEntityBold':
startTag = endTag = TAG_BOLD startTag = endTag = TAG_BOLD
break break
case 'italic': case 'messageEntityItalic':
startTag = endTag = TAG_ITALIC startTag = endTag = TAG_ITALIC
break break
case 'underline': case 'messageEntityUnderline':
startTag = endTag = TAG_UNDERLINE startTag = endTag = TAG_UNDERLINE
break break
case 'strikethrough': case 'messageEntityStrike':
startTag = endTag = TAG_STRIKE startTag = endTag = TAG_STRIKE
break break
case 'spoiler': case 'messageEntitySpoiler':
startTag = endTag = TAG_SPOILER startTag = endTag = TAG_SPOILER
break break
case 'code': case 'messageEntityCode':
startTag = endTag = TAG_CODE startTag = endTag = TAG_CODE
break break
case 'pre': case 'messageEntityPre':
startTag = TAG_PRE startTag = TAG_PRE
if (entity.language) { if (entity.language) {
@ -373,17 +373,17 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
startTag += '\n' startTag += '\n'
endTag = '\n' + TAG_PRE endTag = '\n' + TAG_PRE
break break
case 'text_link': case 'messageEntityTextUrl':
startTag = '[' startTag = '['
endTag = `](${entity.url})` endTag = `](${entity.url})`
break break
case 'text_mention': case 'messageEntityMentionName':
startTag = '[' startTag = '['
endTag = `](tg://user?id=${entity.userId})` endTag = `](tg://user?id=${entity.userId})`
break break
case 'emoji': case 'messageEntityCustomEmoji':
startTag = '[' startTag = '['
endTag = `](tg://emoji?id=${entity.emojiId!.toString()})` endTag = `](tg://emoji?id=${entity.documentId.toString()})`
break break
default: default:
continue continue

View file

@ -2,8 +2,7 @@ import { expect } from 'chai'
import Long from 'long' import Long from 'long'
import { describe, it } from 'mocha' import { describe, it } from 'mocha'
import { FormattedString, MessageEntity, tl } from '@mtcute/client' import { FormattedString, tl } from '@mtcute/client'
import { isPresent } from '@mtcute/client/utils'
import { MarkdownMessageEntityParser, md } from '../src' import { MarkdownMessageEntityParser, md } from '../src'
@ -21,10 +20,6 @@ const createEntity = <T extends tl.TypeMessageEntity['_']>(
} as tl.TypeMessageEntity // idc really, its not that important } as tl.TypeMessageEntity // idc really, its not that important
} }
const createEntities = (entities: tl.TypeMessageEntity[]): MessageEntity[] => {
return entities.map((it) => MessageEntity._parse(it)).filter(isPresent)
}
describe('MarkdownMessageEntityParser', () => { describe('MarkdownMessageEntityParser', () => {
const parser = new MarkdownMessageEntityParser() const parser = new MarkdownMessageEntityParser()
@ -35,7 +30,7 @@ describe('MarkdownMessageEntityParser', () => {
expected: string | string[], expected: string | string[],
_parser = parser, _parser = parser,
): void => { ): void => {
const result = _parser.unparse(text, createEntities(entities)) const result = _parser.unparse(text, entities)
if (Array.isArray(expected)) { if (Array.isArray(expected)) {
expect(expected).to.include(result) expect(expected).to.include(result)