diff --git a/EXAMPLES.md b/EXAMPLES.md
index ac2daf1..d00c4b5 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -365,7 +365,7 @@ await Task.Delay(5000);
```csharp
// • Sending a message with custom emojies in Markdown to ourself:
var text = "Vicksy says Hi! ";
-var entities = client.MarkdownToEntities(ref text, premium: true);
+var entities = client.MarkdownToEntities(ref text);
await client.SendMessageAsync(InputPeer.Self, text, entities: entities);
// also available in HTML: 👋
diff --git a/src/Services.cs b/src/Services.cs
index 7d452f2..ebf7733 100644
--- a/src/Services.cs
+++ b/src/Services.cs
@@ -131,10 +131,9 @@ namespace TL
/// Converts a Markdown text into the (plain text + entities) format used by Telegram messages
/// not used anymore, you can pass null
/// [in] The Markdown text
[out] The same (plain) text, stripped of all Markdown notation
- /// Generate premium entities if any
/// Dictionary used for tg://user?id= notation
/// The array of formatting entities that you can pass (along with the plain text) to SendMessageAsync or SendMediaAsync
- public static MessageEntity[] MarkdownToEntities(this Client _, ref string text, bool premium = false, IReadOnlyDictionary users = null)
+ public static MessageEntity[] MarkdownToEntities(this Client _, ref string text, IReadOnlyDictionary users = null)
{
var entities = new List();
MessageEntityBlockquote lastBlockQuote = null;
@@ -217,12 +216,18 @@ namespace TL
else if (c == ')') break;
}
textUrl.url = sb.ToString(offset + 2, offset2 - offset - 3);
+ sb.Remove(offset, offset2 - offset);
if (textUrl.url.StartsWith("tg://user?id=") && long.TryParse(textUrl.url[13..], out var id) && users?.GetValueOrDefault(id)?.access_hash is long hash)
entities[lastIndex] = new InputMessageEntityMentionName { offset = textUrl.offset, length = textUrl.length, user_id = new InputUser(id, hash) };
- else if ((textUrl.url.StartsWith("tg://emoji?id=") || textUrl.url.StartsWith("emoji?id=")) && long.TryParse(textUrl.url[(textUrl.url.IndexOf('=') + 1)..], out id))
- if (premium) entities[lastIndex] = new MessageEntityCustomEmoji { offset = textUrl.offset, length = textUrl.length, document_id = id };
- else entities.RemoveAt(lastIndex);
- sb.Remove(offset, offset2 - offset);
+ else if (textUrl.url.StartsWith("tg://emoji?id=") && long.TryParse(textUrl.url[14..], out id))
+ entities[lastIndex] = new MessageEntityCustomEmoji { offset = textUrl.offset, length = textUrl.length, document_id = id };
+ else if (textUrl.url.StartsWith("tg://time?unix=") && textUrl.url.IndexOf("&format=", 15) is { } idxFormat)
+ entities[lastIndex] = new MessageEntityFormattedDate
+ {
+ offset = textUrl.offset, length = textUrl.length,
+ date = new DateTime((long.Parse(idxFormat < 0 ? textUrl.url[15..] : textUrl.url[15..idxFormat]) + 62135596800L) * 10000000, DateTimeKind.Utc),
+ flags = idxFormat < 0 ? 0 : HtmlText.DateFlags(textUrl.url[(idxFormat + 8)..])
+ };
break;
}
}
@@ -264,9 +269,8 @@ namespace TL
/// Client, used only for getting current user ID in case of InputMessageEntityMentionName+InputUserSelf
/// The plain text, typically obtained from
/// The array of formatting entities, typically obtained from
- /// Convert premium entities (might lead to non-standard markdown)
/// The message text with MarkdownV2 formattings
- public static string EntitiesToMarkdown(this Client client, string message, MessageEntity[] entities, bool premium = false)
+ public static string EntitiesToMarkdown(this Client client, string message, MessageEntity[] entities)
{
if (entities == null || entities.Length == 0) return Escape(message);
var closings = new List<(int offset, string md)>();
@@ -301,8 +305,9 @@ namespace TL
else if (nextEntity is InputMessageEntityMentionName imemn)
closing.md = $"](tg://user?id={imemn.user_id.UserId ?? client.UserId})";
else if (nextEntity is MessageEntityCustomEmoji mecu)
- if (premium) closing.md = $"](tg://emoji?id={mecu.document_id})";
- else continue;
+ closing.md = $"](tg://emoji?id={mecu.document_id})";
+ else if (nextEntity is MessageEntityFormattedDate mefd)
+ closing.md = $"](tg://time?unix={((DateTimeOffset)mefd.date).ToUnixTimeSeconds()}{(mefd.flags == 0 ? null : $"&format={HtmlText.DateFormat(mefd.flags)}")})";
}
else if (nextEntity is MessageEntityBlockquote mebq)
{ inBlockQuote = true; if (lastCh is not '\n' and not '\0') md = "\n>";
@@ -343,6 +348,7 @@ namespace TL
[typeof(MessageEntitySpoiler)] = "||",
[typeof(MessageEntityCustomEmoji)] = "![",
[typeof(MessageEntityBlockquote)] = ">",
+ [typeof(MessageEntityFormattedDate)] = "![",
};
/// Insert backslashes in front of Markdown reserved characters
@@ -372,10 +378,9 @@ namespace TL
/// Converts an HTML-formatted text into the (plain text + entities) format used by Telegram messages
/// not used anymore, you can pass null
/// [in] The HTML-formatted text
[out] The same (plain) text, stripped of all HTML tags
- /// Generate premium entities if any
/// Dictionary used for tg://user?id= notation
/// The array of formatting entities that you can pass (along with the plain text) to SendMessageAsync or SendMediaAsync
- public static MessageEntity[] HtmlToEntities(this Client _, ref string text, bool premium = false, IReadOnlyDictionary users = null)
+ public static MessageEntity[] HtmlToEntities(this Client _, ref string text, IReadOnlyDictionary users = null)
{
var entities = new List();
var sb = new StringBuilder(text);
@@ -419,6 +424,7 @@ namespace TL
case "code": ProcessEntity(); break;
case "pre": ProcessEntity(); break;
case "tg-emoji" when closing: ProcessEntity(); break;
+ case "tg-time" when closing: ProcessEntity(); break;
case "blockquote": ProcessEntity(); break;
case "blockquote expandable":
entities.Add(new MessageEntityBlockquote { offset = offset, length = -1, flags = MessageEntityBlockquote.Flags.collapsed });
@@ -448,8 +454,15 @@ namespace TL
if (entities.LastOrDefault(e => e.length == -1) is MessageEntityPre prevEntity)
prevEntity.language = tag[21..^1];
}
- else if (premium && (tag.StartsWith("tg-emoji emoji-id=\"") || tag.StartsWith("tg-emoji emoji-id='")))
- entities.Add(new MessageEntityCustomEmoji { offset = offset, length = -1, document_id = long.Parse(tag[(tag.IndexOf('=') + 2)..^1]) });
+ else if (tag.StartsWith("tg-emoji emoji-id=\"") || tag.StartsWith("tg-emoji emoji-id='"))
+ entities.Add(new MessageEntityCustomEmoji { offset = offset, length = -1, document_id = long.Parse(tag[19..^1]) });
+ else if ((tag.StartsWith("tg-time unix=\"") || tag.StartsWith("tg-time unix='")) && (end = tag.IndexOf(tag[13], 14)) > 0)
+ entities.Add(new MessageEntityFormattedDate
+ {
+ offset = offset, length = -1,
+ date = new DateTime((long.Parse(tag[14..end]) + 62135596800L) * 10000000, DateTimeKind.Utc),
+ flags = string.Compare(tag, end + 1, " format=", 0, 8) == 0 ? DateFlags(tag[(end + 10)..^1]) : 0
+ });
break;
}
@@ -486,9 +499,8 @@ namespace TL
/// Client, used only for getting current user ID in case of InputMessageEntityMentionName+InputUserSelf
/// The plain text, typically obtained from
/// The array of formatting entities, typically obtained from
- /// Convert premium entities
/// The message text with HTML formatting tags
- public static string EntitiesToHtml(this Client client, string message, MessageEntity[] entities, bool premium = false)
+ public static string EntitiesToHtml(this Client client, string message, MessageEntity[] entities)
{
if (entities == null || entities.Length == 0) return Escape(message);
var closings = new List<(int offset, string tag)>();
@@ -519,8 +531,7 @@ namespace TL
tag = $"";
}
else if (nextEntity is MessageEntityCustomEmoji mecu)
- if (premium) tag = $"";
- else continue;
+ tag = $"";
else if (nextEntity is MessageEntityPre mep && !string.IsNullOrEmpty(mep.language))
{
closing.Item2 = "";
@@ -528,6 +539,8 @@ namespace TL
}
else if (nextEntity is MessageEntityBlockquote { flags: MessageEntityBlockquote.Flags.collapsed })
tag = "";
+ else if (nextEntity is MessageEntityFormattedDate mefd)
+ tag = $"";
else
tag = $"<{tag}>";
int index = ~closings.BinarySearch(closing, Comparer<(int, string)>.Create((x, y) => x.Item1.CompareTo(y.Item1) | 1));
@@ -559,6 +572,7 @@ namespace TL
[typeof(MessageEntitySpoiler)] = "tg-spoiler",
[typeof(MessageEntityCustomEmoji)] = "tg-emoji",
[typeof(MessageEntityBlockquote)] = "blockquote",
+ [typeof(MessageEntityFormattedDate)] = "tg-time",
};
/// Replace special HTML characters with their &xx; equivalent
@@ -566,5 +580,15 @@ namespace TL
/// The HTML-safe text, ready to be used in HtmlToEntities without problems
public static string Escape(string text)
=> text?.Replace("&", "&").Replace("<", "<").Replace(">", ">");
+
+ internal static string DateFormat(MessageEntityFormattedDate.Flags flags) => flags.HasFlag(MessageEntityFormattedDate.Flags.relative) ? "r" :
+ ((flags & MessageEntityFormattedDate.Flags.day_of_week) != 0 ? "w" : "") +
+ ((flags & MessageEntityFormattedDate.Flags.short_date) != 0 ? "d" : "") +
+ ((flags & MessageEntityFormattedDate.Flags.long_date) != 0 ? "D" : "") +
+ ((flags & MessageEntityFormattedDate.Flags.short_time) != 0 ? "t" : "") +
+ ((flags & MessageEntityFormattedDate.Flags.long_time) != 0 ? "T" : "");
+
+ internal static MessageEntityFormattedDate.Flags DateFlags(string format)
+ => (MessageEntityFormattedDate.Flags)format.Sum(c => 1 << "rtTdDw".IndexOf(c));
}
}