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 , attributes ) { this . _openTags . unshift ( name ) ; this . _openTagsMeta . unshift ( undefined ) ; let EntityType ; const args = { } ; if ( name == "strong" || name == "b" ) { EntityType = Api . MessageEntityBold ; } 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 ) { } } else { EntityType = Api . MessageEntityCode ; } } else if ( name == "pre" ) { EntityType = Api . MessageEntityPre ; args [ "language" ] = "" ; } else if ( name == "a" ) { let url = 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 ) { 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 ) { this . _openTagsMeta . shift ( ) ; this . _openTags . shift ( ) ; const entity = this . _buildingEntities . get ( tagname ) ; if ( entity ) { this . _buildingEntities . delete ( tagname ) ; this . entities . push ( entity ) ; } } onattribute ( name , value , quote ? ) { } oncdataend ( ) { } oncdatastart ( ) { } oncomment ( data ) { } oncommentend ( ) { } onend ( ) { } onerror ( error ) { } onopentagname ( name ) { } onparserinit ( parser ) { } onprocessinginstruction ( name , data ) { } onreset ( ) { } } export class HTMLParser { static parse ( html ) { 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 , entities , _offset = 0 , _length ? ) { 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 . MessageEntityItalic ) { html . push ( `<em>${ entityText }</em>` ) ; } else if ( entity instanceof Api . MessageEntityBold ) { html . push ( `<strong>${ entityText }</strong>` ) ; } 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 ( "" ) ; } }