Support premium emojies in Html/Markdown helpers

This commit is contained in:
Wizou 2022-09-19 22:28:12 +02:00
parent faf8ab3fd0
commit 9523ca4036
4 changed files with 46 additions and 29 deletions

2
.github/dev.yml vendored
View file

@ -2,7 +2,7 @@ pr: none
trigger:
- master
name: 2.6.4-dev.$(Rev:r)
name: 3.0.0-dev.$(Rev:r)
pool:
vmImage: ubuntu-latest

2
.github/release.yml vendored
View file

@ -1,7 +1,7 @@
pr: none
trigger: none
name: 2.6.$(Rev:r)
name: 3.0.0
pool:
vmImage: ubuntu-latest

View file

@ -115,7 +115,7 @@ namespace WTelegram
Path.GetDirectoryName(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar)))
?? AppDomain.CurrentDomain.BaseDirectory, "WTelegram.session"),
#if DEBUG
"server_address" => "149.154.167.40:443", // Test DC 2
"server_address" => "149.154.167.40:443", // Test DC 2
#else
"server_address" => "149.154.167.50:443", // DC 2
#endif
@ -176,7 +176,7 @@ namespace WTelegram
}
catch { }
_cts?.Cancel();
_sendSemaphore = new(0); // initially taken, first released during DoConnectAsync
_sendSemaphore = new(0); // initially taken, first released during DoConnectAsync
try
{
_reactorTask?.Wait(1000);
@ -394,7 +394,7 @@ namespace WTelegram
var msgId = reader.ReadInt64(); // int64 message_id
var seqno = reader.ReadInt32(); // int32 msg_seqno
var length = reader.ReadInt32(); // int32 message_data_length
if (length < 0 || length % 4 != 0) throw new ApplicationException($"Invalid message_data_length: {length}");
if (decrypted_data.Length - 32 - length is < 12 or > 1024) throw new ApplicationException($"Invalid message padding length: {decrypted_data.Length - 32}-{length}");
if (sessionId != _dcSession.Id) throw new ApplicationException($"Unexpected session ID: {sessionId} != {_dcSession.Id}");
@ -943,6 +943,7 @@ namespace WTelegram
if (sentCode.type is Auth_SentCodeTypeSetUpEmailRequired setupEmail)
{
Helpers.Log(3, "A login email is required");
RaiseUpdate(sentCode);
var email = _config("email");
if (string.IsNullOrEmpty(email))
sentCode = await this.Auth_ResendCode(phone_number, sentCode.phone_code_hash);
@ -1104,11 +1105,11 @@ namespace WTelegram
if (_dcSession.AuthKeyID == 0) // send unencrypted message
{
if (_bareRpc == null) throw new ApplicationException($"Shouldn't send a {msg.GetType().Name} unencrypted");
writer.Write(0L); // int64 auth_key_id = 0 (Unencrypted)
writer.Write(msgId); // int64 message_id
writer.Write(0); // int32 message_data_length (to be patched)
writer.Write(0L); // int64 auth_key_id = 0 (Unencrypted)
writer.Write(msgId); // int64 message_id
writer.Write(0); // int32 message_data_length (to be patched)
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name.TrimEnd('_')}...");
writer.WriteTLObject(msg); // bytes message_data
writer.WriteTLObject(msg); // bytes message_data
BinaryPrimitives.WriteInt32LittleEndian(memStream.GetBuffer().AsSpan(20), (int)memStream.Length - 24); // patch message_data_length
}
else
@ -1116,19 +1117,19 @@ namespace WTelegram
using var clearStream = new MemoryStream(1024);
using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8);
clearWriter.Write(_dcSession.AuthKey, 88, 32);
clearWriter.Write(_dcSession.Salt); // int64 salt
clearWriter.Write(_dcSession.Id); // int64 session_id
clearWriter.Write(msgId); // int64 message_id
clearWriter.Write(seqno); // int32 msg_seqno
clearWriter.Write(0); // int32 message_data_length (to be patched)
clearWriter.Write(_dcSession.Salt); // int64 salt
clearWriter.Write(_dcSession.Id); // int64 session_id
clearWriter.Write(msgId); // int64 message_id
clearWriter.Write(seqno); // int32 msg_seqno
clearWriter.Write(0); // int32 message_data_length (to be patched)
if ((seqno & 1) != 0)
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name.TrimEnd('_'),-40} #{(short)msgId.GetHashCode():X4}");
else
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name.TrimEnd('_'),-40} {MsgIdToStamp(msgId):u} (svc)");
clearWriter.WriteTLObject(msg); // bytes message_data
clearWriter.WriteTLObject(msg); // bytes message_data
int clearLength = (int)clearStream.Length - 32; // length before padding (= 32 + message_data_length)
int padding = (0x7FFFFFF0 - clearLength) % 16;
padding += _random.Next(1, 64) * 16; // MTProto 2.0 padding must be between 12..1024 with total length divisible by 16
padding += _random.Next(1, 64) * 16; // MTProto 2.0 padding must be between 12..1024 with total length divisible by 16
clearStream.SetLength(32 + clearLength + padding);
byte[] clearBuffer = clearStream.GetBuffer();
BinaryPrimitives.WriteInt32LittleEndian(clearBuffer.AsSpan(60), clearLength - 32); // patch message_data_length
@ -1137,9 +1138,9 @@ namespace WTelegram
const int msgKeyOffset = 8; // msg_key = middle 128-bits of SHA256(authkey_part+plaintext+padding)
byte[] encrypted_data = EncryptDecryptMessage(clearBuffer.AsSpan(32, clearLength + padding), true, _dcSession.AuthKey, msgKeyLarge, msgKeyOffset, _sha256);
writer.Write(_dcSession.AuthKeyID); // int64 auth_key_id
writer.Write(msgKeyLarge, msgKeyOffset, 16); // int128 msg_key
writer.Write(encrypted_data); // bytes encrypted_data
writer.Write(_dcSession.AuthKeyID); // int64 auth_key_id
writer.Write(msgKeyLarge, msgKeyOffset, 16); // int128 msg_key
writer.Write(encrypted_data); // bytes encrypted_data
}
if (_paddedMode) // Padded intermediate mode => append random padding
{

View file

@ -41,8 +41,9 @@ namespace TL
/// <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>
/// <param name="client">Client, used for getting access_hash for <c>tg://user?id=</c> URLs</param>
/// <param name="text">[in] The Markdown text<br/>[out] The same (plain) text, stripped of all Markdown notation</param>
/// <param name="premium">Generate premium entities if any</param>
/// <returns>The array of formatting entities that you can pass (along with the plain text) to <see cref="WTelegram.Client.SendMessageAsync">SendMessageAsync</see> or <see cref="WTelegram.Client.SendMediaAsync">SendMediaAsync</see></returns>
public static MessageEntity[] MarkdownToEntities(this WTelegram.Client client, ref string text)
public static MessageEntity[] MarkdownToEntities(this WTelegram.Client client, ref string text, bool premium = false)
{
var entities = new List<MessageEntity>();
var sb = new StringBuilder(text);
@ -109,8 +110,11 @@ namespace TL
else if (c == ')') break;
}
textUrl.url = sb.ToString(offset + 2, offset2 - offset - 3);
if (textUrl.url.StartsWith("tg://user?id=") && long.TryParse(textUrl.url[13..], out var user_id) && client.GetAccessHashFor<User>(user_id) is long hash)
entities[lastIndex] = new InputMessageEntityMentionName { offset = textUrl.offset, length = textUrl.length, user_id = new InputUser(user_id, hash) };
if (textUrl.url.StartsWith("tg://user?id=") && long.TryParse(textUrl.url[13..], out var id) && client.GetAccessHashFor<User>(id) is long hash)
entities[lastIndex] = new InputMessageEntityMentionName { offset = textUrl.offset, length = textUrl.length, user_id = new InputUser(id, hash) };
else if (textUrl.url.StartsWith("emoji?id=") && long.TryParse(textUrl.url[9..], 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);
break;
}
@ -137,8 +141,9 @@ namespace TL
/// <param name="client">Client, used only for getting current user ID in case of <c>InputMessageEntityMentionName+InputUserSelf</c></param>
/// <param name="message">The plain text, typically obtained from <see cref="TL.Message.message"/></param>
/// <param name="entities">The array of formatting entities, typically obtained from <see cref="TL.Message.entities"/></param>
/// <param name="premium">Convert premium entities (might lead to non-standard markdown)</param>
/// <returns>The message text with MarkdownV2 formattings</returns>
public static string EntitiesToMarkdown(this WTelegram.Client client, string message, MessageEntity[] entities)
public static string EntitiesToMarkdown(this WTelegram.Client client, string message, MessageEntity[] entities, bool premium = false)
{
if (entities == null || entities.Length == 0) return Escape(message);
var closings = new List<(int offset, string md)>();
@ -155,7 +160,7 @@ namespace TL
closings.RemoveAt(0);
}
if (i == sb.Length) break;
while (offset == nextEntity?.offset)
for (; offset == nextEntity?.offset; nextEntity = ++entityIndex < entities.Length ? entities[entityIndex] : null)
{
if (entityToMD.TryGetValue(nextEntity.GetType(), out var md))
{
@ -168,6 +173,9 @@ namespace TL
closing.md = $"](tg://user?id={memn.user_id})";
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 = $"](emoji?id={mecu.document_id})";
else continue;
}
else if (nextEntity is MessageEntityPre mep)
md = $"```{mep.language}\n";
@ -176,7 +184,6 @@ namespace TL
if (i > 0 && md[0] == '_' && sb[i - 1] == '_') md = '\r' + md;
sb.Insert(i, md); i += md.Length;
}
nextEntity = ++entityIndex < entities.Length ? entities[entityIndex] : null;
}
switch (sb[i])
{
@ -201,6 +208,7 @@ namespace TL
[typeof(MessageEntityUnderline)] = "__",
[typeof(MessageEntityStrike)] = "~",
[typeof(MessageEntitySpoiler)] = "||",
[typeof(MessageEntityCustomEmoji)] = "[",
};
/// <summary>Insert backslashes in front of Markdown reserved characters</summary>
@ -229,8 +237,9 @@ namespace TL
/// <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>
/// <param name="client">Client, used for getting access_hash for <c>tg://user?id=</c> URLs</param>
/// <param name="text">[in] The HTML-formatted text<br/>[out] The same (plain) text, stripped of all HTML tags</param>
/// <param name="premium">Generate premium entities if any</param>
/// <returns>The array of formatting entities that you can pass (along with the plain text) to <see cref="WTelegram.Client.SendMessageAsync">SendMessageAsync</see> or <see cref="WTelegram.Client.SendMediaAsync">SendMediaAsync</see></returns>
public static MessageEntity[] HtmlToEntities(this WTelegram.Client client, ref string text)
public static MessageEntity[] HtmlToEntities(this WTelegram.Client client, ref string text, bool premium = false)
{
var entities = new List<MessageEntity>();
var sb = new StringBuilder(text);
@ -271,6 +280,7 @@ namespace TL
case "tg-spoiler": ProcessEntity<MessageEntitySpoiler>(); break;
case "code": ProcessEntity<MessageEntityCode>(); break;
case "pre": ProcessEntity<MessageEntityPre>(); break;
case "tg-emoji" when closing: ProcessEntity<MessageEntityCustomEmoji>(); break;
default:
if (closing)
{
@ -294,6 +304,8 @@ namespace TL
if (entities.LastOrDefault(e => e.length == -1) is MessageEntityPre prevEntity)
prevEntity.language = tag[21..^1];
}
else if (premium && tag.StartsWith("tg-emoji id=\""))
entities.Add(new MessageEntityCustomEmoji { offset = offset, length = -1, document_id = long.Parse(tag[13..^1]) });
break;
}
@ -316,8 +328,9 @@ namespace TL
/// <param name="client">Client, used only for getting current user ID in case of <c>InputMessageEntityMentionName+InputUserSelf</c></param>
/// <param name="message">The plain text, typically obtained from <see cref="TL.Message.message"/></param>
/// <param name="entities">The array of formatting entities, typically obtained from <see cref="TL.Message.entities"/></param>
/// <param name="premium">Convert premium entities</param>
/// <returns>The message text with HTML formatting tags</returns>
public static string EntitiesToHtml(this WTelegram.Client client, string message, MessageEntity[] entities)
public static string EntitiesToHtml(this WTelegram.Client client, string message, MessageEntity[] entities, bool premium = false)
{
if (entities == null || entities.Length == 0) return Escape(message);
var closings = new List<(int offset, string tag)>();
@ -333,7 +346,7 @@ namespace TL
closings.RemoveAt(0);
}
if (i == sb.Length) break;
while (offset == nextEntity?.offset)
for (; offset == nextEntity?.offset; nextEntity = ++entityIndex < entities.Length ? entities[entityIndex] : null)
{
if (entityToTag.TryGetValue(nextEntity.GetType(), out var tag))
{
@ -347,6 +360,9 @@ namespace TL
else if (nextEntity is InputMessageEntityMentionName imemn)
tag = $"<a href=\"tg://user?id={imemn.user_id.UserId ?? client.UserId}\">";
}
else if (nextEntity is MessageEntityCustomEmoji mecu)
if (premium) tag = $"<tg-emoji id=\"{mecu.document_id}\">";
else continue;
else if (nextEntity is MessageEntityPre mep && !string.IsNullOrEmpty(mep.language))
{
closing.Item2 = "</code></pre>";
@ -358,7 +374,6 @@ namespace TL
closings.Insert(index, closing);
sb.Insert(i, tag); i += tag.Length;
}
nextEntity = ++entityIndex < entities.Length ? entities[entityIndex] : null;
}
switch (sb[i])
{
@ -382,6 +397,7 @@ namespace TL
[typeof(MessageEntityUnderline)] = "u",
[typeof(MessageEntityStrike)] = "s",
[typeof(MessageEntitySpoiler)] = "tg-spoiler",
[typeof(MessageEntityCustomEmoji)] = "tg-emoji",
};
/// <summary>Replace special HTML characters with their &amp;xx; equivalent</summary>