@@ -131,10 +131,9 @@ public static class Markdown
131131 /// <summary>Converts a <a href="https://core.telegram.org/bots/api/#markdownv2-style">Markdown text</a> into the (plain text + entities) format used by Telegram messages</summary>
132132 /// <param name="_">not used anymore, you can pass null</param>
133133 /// <param name="text">[in] The Markdown text<br/>[out] The same (plain) text, stripped of all Markdown notation</param>
134- /// <param name="premium">Generate premium entities if any</param>
135134 /// <param name="users">Dictionary used for <c>tg://user?id=</c> notation</param>
136135 /// <returns>The array of formatting entities that you can pass (along with the plain text) to <see cref="Client.SendMessageAsync">SendMessageAsync</see> or <see cref="Client.SendMediaAsync">SendMediaAsync</see></returns>
137- public static MessageEntity [ ] MarkdownToEntities ( this Client _ , ref string text , bool premium = false , IReadOnlyDictionary < long , User > users = null )
136+ public static MessageEntity [ ] MarkdownToEntities ( this Client _ , ref string text , IReadOnlyDictionary < long , User > users = null )
138137 {
139138 var entities = new List < MessageEntity > ( ) ;
140139 MessageEntityBlockquote lastBlockQuote = null ;
@@ -217,12 +216,18 @@ public static MessageEntity[] MarkdownToEntities(this Client _, ref string text,
217216 else if ( c == ')' ) break ;
218217 }
219218 textUrl . url = sb . ToString ( offset + 2 , offset2 - offset - 3 ) ;
219+ sb . Remove ( offset , offset2 - offset ) ;
220220 if ( textUrl . url . StartsWith ( "tg://user?id=" ) && long . TryParse ( textUrl . url [ 13 ..] , out var id ) && users ? . GetValueOrDefault ( id ) ? . access_hash is long hash )
221221 entities [ lastIndex ] = new InputMessageEntityMentionName { offset = textUrl . offset , length = textUrl . length , user_id = new InputUser ( id , hash ) } ;
222- else if ( ( textUrl . url . StartsWith ( "tg://emoji?id=" ) || textUrl . url . StartsWith ( "emoji?id=" ) ) && long . TryParse ( textUrl . url [ ( textUrl . url . IndexOf ( '=' ) + 1 ) ..] , out id ) )
223- if ( premium ) entities [ lastIndex ] = new MessageEntityCustomEmoji { offset = textUrl . offset , length = textUrl . length , document_id = id } ;
224- else entities . RemoveAt ( lastIndex ) ;
225- sb . Remove ( offset , offset2 - offset ) ;
222+ else if ( textUrl . url . StartsWith ( "tg://emoji?id=" ) && long . TryParse ( textUrl . url [ 14 ..] , out id ) )
223+ entities [ lastIndex ] = new MessageEntityCustomEmoji { offset = textUrl . offset , length = textUrl . length , document_id = id } ;
224+ else if ( textUrl . url . StartsWith ( "tg://time?unix=" ) && textUrl . url . IndexOf ( "&format=" , 15 ) is { } idxFormat )
225+ entities [ lastIndex ] = new MessageEntityFormattedDate
226+ {
227+ offset = textUrl . offset , length = textUrl . length ,
228+ date = new DateTime ( ( long . Parse ( idxFormat < 0 ? textUrl . url [ 15 ..] : textUrl . url [ 15 ..idxFormat ] ) + 62135596800L ) * 10000000 , DateTimeKind . Utc ) ,
229+ flags = idxFormat < 0 ? 0 : HtmlText . DateFlags ( textUrl . url [ ( idxFormat + 8 ) ..] )
230+ } ;
226231 break ;
227232 }
228233 }
@@ -264,9 +269,8 @@ void CloseBlockQuote()
264269 /// <param name="client">Client, used only for getting current user ID in case of <c>InputMessageEntityMentionName+InputUserSelf</c></param>
265270 /// <param name="message">The plain text, typically obtained from <see cref="Message.message"/></param>
266271 /// <param name="entities">The array of formatting entities, typically obtained from <see cref="Message.entities"/></param>
267- /// <param name="premium">Convert premium entities (might lead to non-standard markdown)</param>
268272 /// <returns>The message text with MarkdownV2 formattings</returns>
269- public static string EntitiesToMarkdown ( this Client client , string message , MessageEntity [ ] entities , bool premium = false )
273+ public static string EntitiesToMarkdown ( this Client client , string message , MessageEntity [ ] entities )
270274 {
271275 if ( entities == null || entities . Length == 0 ) return Escape ( message ) ;
272276 var closings = new List < ( int offset , string md ) > ( ) ;
@@ -301,8 +305,9 @@ public static string EntitiesToMarkdown(this Client client, string message, Mess
301305 else if ( nextEntity is InputMessageEntityMentionName imemn )
302306 closing . md = $ "](tg://user?id={ imemn . user_id . UserId ?? client . UserId } )";
303307 else if ( nextEntity is MessageEntityCustomEmoji mecu )
304- if ( premium ) closing . md = $ "](tg://emoji?id={ mecu . document_id } )";
305- else continue ;
308+ closing . md = $ "](tg://emoji?id={ mecu . document_id } )";
309+ else if ( nextEntity is MessageEntityFormattedDate mefd )
310+ closing . md = $ "](tg://time?unix={ ( ( DateTimeOffset ) mefd . date ) . ToUnixTimeSeconds ( ) } { ( mefd . flags == 0 ? null : $ "&format={ HtmlText . DateFormat ( mefd . flags ) } ") } )";
306311 }
307312 else if ( nextEntity is MessageEntityBlockquote mebq )
308313 { inBlockQuote = true ; if ( lastCh is not '\n ' and not '\0 ' ) md = "\n >" ;
@@ -343,6 +348,7 @@ public static string EntitiesToMarkdown(this Client client, string message, Mess
343348 [ typeof ( MessageEntitySpoiler ) ] = "||" ,
344349 [ typeof ( MessageEntityCustomEmoji ) ] = "![" ,
345350 [ typeof ( MessageEntityBlockquote ) ] = ">" ,
351+ [ typeof ( MessageEntityFormattedDate ) ] = "![" ,
346352 } ;
347353
348354 /// <summary>Insert backslashes in front of Markdown reserved characters</summary>
@@ -372,10 +378,9 @@ public static class HtmlText
372378 /// <summary>Converts an <a href="https://core.telegram.org/bots/api/#html-style">HTML-formatted text</a> into the (plain text + entities) format used by Telegram messages</summary>
373379 /// <param name="_">not used anymore, you can pass null</param>
374380 /// <param name="text">[in] The HTML-formatted text<br/>[out] The same (plain) text, stripped of all HTML tags</param>
375- /// <param name="premium">Generate premium entities if any</param>
376381 /// <param name="users">Dictionary used for <c>tg://user?id=</c> notation</param>
377382 /// <returns>The array of formatting entities that you can pass (along with the plain text) to <see cref="Client.SendMessageAsync">SendMessageAsync</see> or <see cref="Client.SendMediaAsync">SendMediaAsync</see></returns>
378- public static MessageEntity [ ] HtmlToEntities ( this Client _ , ref string text , bool premium = false , IReadOnlyDictionary < long , User > users = null )
383+ public static MessageEntity [ ] HtmlToEntities ( this Client _ , ref string text , IReadOnlyDictionary < long , User > users = null )
379384 {
380385 var entities = new List < MessageEntity > ( ) ;
381386 var sb = new StringBuilder ( text ) ;
@@ -419,6 +424,7 @@ public static MessageEntity[] HtmlToEntities(this Client _, ref string text, boo
419424 case "code" : ProcessEntity < MessageEntityCode > ( ) ; break ;
420425 case "pre" : ProcessEntity < MessageEntityPre > ( ) ; break ;
421426 case "tg-emoji" when closing : ProcessEntity < MessageEntityCustomEmoji > ( ) ; break ;
427+ case "tg-time" when closing : ProcessEntity < MessageEntityFormattedDate > ( ) ; break ;
422428 case "blockquote" : ProcessEntity < MessageEntityBlockquote > ( ) ; break ;
423429 case "blockquote expandable" :
424430 entities . Add ( new MessageEntityBlockquote { offset = offset , length = - 1 , flags = MessageEntityBlockquote . Flags . collapsed } ) ;
@@ -448,8 +454,15 @@ public static MessageEntity[] HtmlToEntities(this Client _, ref string text, boo
448454 if ( entities . LastOrDefault ( e => e . length == - 1 ) is MessageEntityPre prevEntity )
449455 prevEntity . language = tag [ 21 ..^ 1 ] ;
450456 }
451- else if ( premium && ( tag . StartsWith ( "tg-emoji emoji-id=\" " ) || tag . StartsWith ( "tg-emoji emoji-id='" ) ) )
452- entities . Add ( new MessageEntityCustomEmoji { offset = offset , length = - 1 , document_id = long . Parse ( tag [ ( tag . IndexOf ( '=' ) + 2 ) ..^ 1 ] ) } ) ;
457+ else if ( tag . StartsWith ( "tg-emoji emoji-id=\" " ) || tag . StartsWith ( "tg-emoji emoji-id='" ) )
458+ entities . Add ( new MessageEntityCustomEmoji { offset = offset , length = - 1 , document_id = long . Parse ( tag [ 19 ..^ 1 ] ) } ) ;
459+ else if ( ( tag . StartsWith ( "tg-time unix=\" " ) || tag . StartsWith ( "tg-time unix='" ) ) && ( end = tag . IndexOf ( tag [ 13 ] , 14 ) ) > 0 )
460+ entities . Add ( new MessageEntityFormattedDate
461+ {
462+ offset = offset , length = - 1 ,
463+ date = new DateTime ( ( long . Parse ( tag [ 14 ..end ] ) + 62135596800L ) * 10000000 , DateTimeKind . Utc ) ,
464+ flags = string . Compare ( tag , end + 1 , " format=" , 0 , 8 ) == 0 ? DateFlags ( tag [ ( end + 10 ) ..^ 1 ] ) : 0
465+ } ) ;
453466 break ;
454467 }
455468
@@ -486,9 +499,8 @@ internal static void FixUps(StringBuilder sb, List<MessageEntity> entities)
486499 /// <param name="client">Client, used only for getting current user ID in case of <c>InputMessageEntityMentionName+InputUserSelf</c></param>
487500 /// <param name="message">The plain text, typically obtained from <see cref="Message.message"/></param>
488501 /// <param name="entities">The array of formatting entities, typically obtained from <see cref="Message.entities"/></param>
489- /// <param name="premium">Convert premium entities</param>
490502 /// <returns>The message text with HTML formatting tags</returns>
491- public static string EntitiesToHtml ( this Client client , string message , MessageEntity [ ] entities , bool premium = false )
503+ public static string EntitiesToHtml ( this Client client , string message , MessageEntity [ ] entities )
492504 {
493505 if ( entities == null || entities . Length == 0 ) return Escape ( message ) ;
494506 var closings = new List < ( int offset , string tag ) > ( ) ;
@@ -519,15 +531,16 @@ public static string EntitiesToHtml(this Client client, string message, MessageE
519531 tag = $ "<a href=\" tg://user?id={ imemn . user_id . UserId ?? client . UserId } \" >";
520532 }
521533 else if ( nextEntity is MessageEntityCustomEmoji mecu )
522- if ( premium ) tag = $ "<tg-emoji emoji-id=\" { mecu . document_id } \" >";
523- else continue ;
534+ tag = $ "<tg-emoji emoji-id=\" { mecu . document_id } \" >";
524535 else if ( nextEntity is MessageEntityPre mep && ! string . IsNullOrEmpty ( mep . language ) )
525536 {
526537 closing . Item2 = "</code></pre>" ;
527538 tag = $ "<pre><code class=\" language-{ mep . language } \" >";
528539 }
529540 else if ( nextEntity is MessageEntityBlockquote { flags : MessageEntityBlockquote . Flags . collapsed } )
530541 tag = "<blockquote expandable>" ;
542+ else if ( nextEntity is MessageEntityFormattedDate mefd )
543+ tag = $ "<tg-time unix=\" { ( ( DateTimeOffset ) mefd . date ) . ToUnixTimeSeconds ( ) } \" { ( mefd . flags == 0 ? null : $ " format=\" { DateFormat ( mefd . flags ) } \" ") } >";
531544 else
532545 tag = $ "<{ tag } >";
533546 int index = ~ closings . BinarySearch ( closing , Comparer < ( int , string ) > . Create ( ( x , y ) => x . Item1 . CompareTo ( y . Item1 ) | 1 ) ) ;
@@ -559,12 +572,23 @@ public static string EntitiesToHtml(this Client client, string message, MessageE
559572 [ typeof ( MessageEntitySpoiler ) ] = "tg-spoiler" ,
560573 [ typeof ( MessageEntityCustomEmoji ) ] = "tg-emoji" ,
561574 [ typeof ( MessageEntityBlockquote ) ] = "blockquote" ,
575+ [ typeof ( MessageEntityFormattedDate ) ] = "tg-time" ,
562576 } ;
563577
564578 /// <summary>Replace special HTML characters with their &xx; equivalent</summary>
565579 /// <param name="text">The text to make HTML-safe</param>
566580 /// <returns>The HTML-safe text, ready to be used in <see cref="HtmlToEntities">HtmlToEntities</see> without problems</returns>
567581 public static string Escape ( string text )
568582 => text ? . Replace ( "&" , "&" ) . Replace ( "<" , "<" ) . Replace ( ">" , ">" ) ;
583+
584+ internal static string DateFormat ( MessageEntityFormattedDate . Flags flags ) => flags . HasFlag ( MessageEntityFormattedDate . Flags . relative ) ? "r" :
585+ ( ( flags & MessageEntityFormattedDate . Flags . day_of_week ) != 0 ? "w" : "" ) +
586+ ( ( flags & MessageEntityFormattedDate . Flags . short_date ) != 0 ? "d" : "" ) +
587+ ( ( flags & MessageEntityFormattedDate . Flags . long_date ) != 0 ? "D" : "" ) +
588+ ( ( flags & MessageEntityFormattedDate . Flags . short_time ) != 0 ? "t" : "" ) +
589+ ( ( flags & MessageEntityFormattedDate . Flags . long_time ) != 0 ? "T" : "" ) ;
590+
591+ internal static MessageEntityFormattedDate . Flags DateFlags ( string format )
592+ => ( MessageEntityFormattedDate . Flags ) format . Sum ( c => 1 << "rtTdDw" . IndexOf ( c ) ) ;
569593 }
570594}
0 commit comments