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