UNPKG

dograma

Version:

NodeJS/Browser MTProto API Telegram client library,

235 lines (215 loc) 8.07 kB
import { Parser } from "htmlparser2"; import { Handler } from "htmlparser2/lib/Parser"; import { Api } from "../tl"; import { helpers } from "../index"; class HTMLToTelegramParser implements Handler { text: string; entities: Api.TypeMessageEntity[]; private readonly _buildingEntities: Map<string, Api.TypeMessageEntity>; private readonly _openTags: string[]; private readonly _openTagsMeta: (string | undefined)[]; constructor() { this.text = ""; this.entities = []; this._buildingEntities = new Map<string, Api.TypeMessageEntity>(); this._openTags = []; this._openTagsMeta = []; } onopentag( name: string, attributes: { [s: string]: string; } ) { /* * This fires when a new tag is opened. * * If you don't need an aggregated `attributes` object, * have a look at the `onopentagname` and `onattribute` events. */ this._openTags.unshift(name); this._openTagsMeta.unshift(undefined); let EntityType; const args: any = {}; if (name == "strong" || name == "b") { EntityType = Api.MessageEntityBold; } else if (name == "spoiler") { EntityType = Api.MessageEntitySpoiler; } else if (name == "em" || name == "i") { EntityType = Api.MessageEntityItalic; } else if (name == "u") { EntityType = Api.MessageEntityUnderline; } else if (name == "del" || name == "s") { EntityType = Api.MessageEntityStrike; } else if (name == "blockquote") { EntityType = Api.MessageEntityBlockquote; } else if (name == "code") { const pre = this._buildingEntities.get("pre"); if (pre && pre instanceof Api.MessageEntityPre) { try { pre.language = attributes.class.slice( "language-".length, attributes.class.length ); } catch (e) { // no language block } } else { EntityType = Api.MessageEntityCode; } } else if (name == "pre") { EntityType = Api.MessageEntityPre; args["language"] = ""; } else if (name == "a") { let url: string | undefined = attributes.href; if (!url) { return; } if (url.startsWith("mailto:")) { url = url.slice("mailto:".length, url.length); EntityType = Api.MessageEntityEmail; } else { EntityType = Api.MessageEntityTextUrl; args["url"] = url; url = undefined; } this._openTagsMeta.shift(); this._openTagsMeta.unshift(url); } if (EntityType && !this._buildingEntities.has(name)) { this._buildingEntities.set( name, new EntityType({ offset: this.text.length, length: 0, ...args, }) ); } } ontext(text: string) { const previousTag = this._openTags.length > 0 ? this._openTags[0] : ""; if (previousTag == "a") { const url = this._openTagsMeta[0]; if (url) { text = url; } } for (let [tag, entity] of this._buildingEntities) { entity.length += text.length; } this.text += text; } onclosetag(tagname: string) { this._openTagsMeta.shift(); this._openTags.shift(); const entity = this._buildingEntities.get(tagname); if (entity) { this._buildingEntities.delete(tagname); this.entities.push(entity); } } onattribute( name: string, value: string, quote?: string | undefined | null ): void {} oncdataend(): void {} oncdatastart(): void {} oncomment(data: string): void {} oncommentend(): void {} onend(): void {} onerror(error: Error): void {} onopentagname(name: string): void {} onparserinit(parser: Parser): void {} onprocessinginstruction(name: string, data: string): void {} onreset(): void {} } export class HTMLParser { static parse(html: string): [string, Api.TypeMessageEntity[]] { if (!html) { return [html, []]; } const handler = new HTMLToTelegramParser(); const parser = new Parser(handler); parser.write(html); parser.end(); const text = helpers.stripText(handler.text, handler.entities); return [text, handler.entities]; } static unparse( text: string, entities: Api.TypeMessageEntity[] | undefined, _offset: number = 0, _length?: number ): string { if (!text || !entities || !entities.length) { return text; } if (_length == undefined) { _length = text.length; } const html = []; let lastOffset = 0; for (let i = 0; i < entities.length; i++) { const entity = entities[i]; if (entity.offset >= _offset + _length) { break; } let relativeOffset = entity.offset - _offset; if (relativeOffset > lastOffset) { html.push(text.slice(lastOffset, relativeOffset)); } else if (relativeOffset < lastOffset) { continue; } let skipEntity = false; let length = entity.length; let entityText = this.unparse( text.slice(relativeOffset, relativeOffset + length), entities.slice(i + 1, entities.length), entity.offset, length ); if (entity instanceof Api.MessageEntityBold) { html.push(`<strong>${entityText}</strong>`); } else if (entity instanceof Api.MessageEntitySpoiler) { html.push(`<spoiler>${entityText}</spoiler>`); } else if (entity instanceof Api.MessageEntityItalic) { html.push(`<em>${entityText}</em>`); } else if (entity instanceof Api.MessageEntityCode) { html.push(`<code>${entityText}</code>`); } else if (entity instanceof Api.MessageEntityUnderline) { html.push(`<u>${entityText}</u>`); } else if (entity instanceof Api.MessageEntityStrike) { html.push(`<del>${entityText}</del>`); } else if (entity instanceof Api.MessageEntityBlockquote) { html.push(`<blockquote>${entityText}</blockquote>`); } else if (entity instanceof Api.MessageEntityPre) { if (entity.language) { html.push(`<pre> <code class="language-${entity.language}"> ${entityText} </code> </pre>`); } else { html.push(`<pre></pre><code>${entityText}</code><pre>`); } } else if (entity instanceof Api.MessageEntityEmail) { html.push(`<a href="mailto:${entityText}">${entityText}</a>`); } else if (entity instanceof Api.MessageEntityUrl) { html.push(`<a href="${entityText}">${entityText}</a>`); } else if (entity instanceof Api.MessageEntityTextUrl) { html.push(`<a href="${entity.url}">${entityText}</a>`); } else if (entity instanceof Api.MessageEntityMentionName) { html.push( `<a href="tg://user?id=${entity.userId}">${entityText}</a>` ); } else { skipEntity = true; } lastOffset = relativeOffset + (skipEntity ? 0 : length); } html.push(text.slice(lastOffset, text.length)); return html.join(""); } }