diff --git a/.github/dev.yml b/.github/dev.yml
index ebcdc85..691c3e1 100644
--- a/.github/dev.yml
+++ b/.github/dev.yml
@@ -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
diff --git a/.github/release.yml b/.github/release.yml
index b6de330..de94957 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -1,7 +1,7 @@
pr: none
trigger: none
-name: 2.6.$(Rev:r)
+name: 3.0.0
pool:
vmImage: ubuntu-latest
diff --git a/src/Client.cs b/src/Client.cs
index 48d22c1..68099fb 100644
--- a/src/Client.cs
+++ b/src/Client.cs
@@ -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
{
diff --git a/src/TL.Extensions.cs b/src/TL.Extensions.cs
index 1ceae5b..f028131 100644
--- a/src/TL.Extensions.cs
+++ b/src/TL.Extensions.cs
@@ -41,8 +41,9 @@ namespace TL
/// Converts a Markdown text into the (plain text + entities) format used by Telegram messages
/// Client, used for getting access_hash for tg://user?id= URLs
/// [in] The Markdown text
[out] The same (plain) text, stripped of all Markdown notation
+ /// Generate premium entities if any
/// The array of formatting entities that you can pass (along with the plain text) to SendMessageAsync or SendMediaAsync
- 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();
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_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(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
/// 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 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)] = "[",
};
/// Insert backslashes in front of Markdown reserved characters
@@ -229,8 +237,9 @@ namespace TL
/// Converts an HTML-formatted text into the (plain text + entities) format used by Telegram messages
/// Client, used for getting access_hash for tg://user?id= URLs
/// [in] The HTML-formatted text
[out] The same (plain) text, stripped of all HTML tags
+ /// Generate premium entities if any
/// The array of formatting entities that you can pass (along with the plain text) to SendMessageAsync or SendMediaAsync
- 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();
var sb = new StringBuilder(text);
@@ -271,6 +280,7 @@ namespace TL
case "tg-spoiler": ProcessEntity(); break;
case "code": ProcessEntity(); break;
case "pre": ProcessEntity(); break;
+ case "tg-emoji" when closing: ProcessEntity(); 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
/// 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 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 = $"";
}
+ else if (nextEntity is MessageEntityCustomEmoji mecu)
+ if (premium) tag = $"";
+ else continue;
else if (nextEntity is MessageEntityPre mep && !string.IsNullOrEmpty(mep.language))
{
closing.Item2 = "";
@@ -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",
};
/// Replace special HTML characters with their &xx; equivalent