diff --git a/EXAMPLES.md b/EXAMPLES.md index 55d5768..5e7ca9d 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -238,6 +238,7 @@ var inputMedias = new List photoFromTelegram, // PhotoBase has implicit conversion to InputMediaPhoto new InputMediaUploadedPhoto { file = uploadedFile }, new InputMediaPhotoExternal { url = photoUrl }, + //or Document, InputMediaDocument, InputMediaUploadedDocument, InputMediaDocumentExternal... }; await client.SendAlbumAsync(InputPeer.Self, inputMedias, "My first album"); ``` diff --git a/src/Client.Helpers.cs b/src/Client.Helpers.cs index c0ac2f1..db7a17d 100644 --- a/src/Client.Helpers.cs +++ b/src/Client.Helpers.cs @@ -769,7 +769,7 @@ namespace WTelegram } /// Return chat and message details based on a Message Link (URL) - /// Message Link, like https://t.me/c/1234567890/1234 or https://t.me/channelname/1234 + /// Message Link, like https://t.me/c/1234567890/1234 or t.me/channelname/1234 /// previously collected chats, to prevent unnecessary ResolveUsername /// Structure containing the message, chat and user details /// If link is for private group (t.me/c/..), user must have joined that group @@ -778,17 +778,28 @@ namespace WTelegram int start = url.IndexOf("//"); start = url.IndexOf('/', start + 2) + 1; int slash = url.IndexOf('/', start + 2); - if (start == 0 || slash == -1) throw new ArgumentException("Invalid URL"); - int end = url.IndexOfAny(UrlSeparator, slash + 1); + int msgStart = slash + 1; + int end = url.IndexOfAny(UrlSeparator, msgStart); if (end == -1) end = url.Length; - int msgId = int.Parse(url[(slash + 1)..end]); + else if (url[end] == '/' && char.IsDigit(url[msgStart]) && url.Length > end + 1 && char.IsDigit(url[end + 1])) + { + end = url.IndexOfAny(UrlSeparator, msgStart = end + 1); + if (end == -1) end = url.Length; + } + if (start == 0 || slash == -1 || end <= slash + 1 || !char.IsDigit(url[msgStart])) throw new ArgumentException("Invalid URL"); + int msgId = int.Parse(url[msgStart..end]); ChatBase chat; if (url[start] is 'c' or 'C' && url[start + 1] == '/') { long chatId = long.Parse(url[(start + 2)..slash]); - var mc = await this.Channels_GetChannels(new InputChannel(chatId, 0)); - if (!mc.chats.TryGetValue(chatId, out chat)) - throw new WTException($"Channel {chatId} not found"); + if (chats?.TryGetValue(chatId, out chat) != true) + { + var mc = await this.Channels_GetChannels(new InputChannel(chatId, 0)); + if (!mc.chats.TryGetValue(chatId, out chat)) + throw new WTException($"Channel {chatId} not found"); + else if (chats != null) + chats[chatId] = chat; + } } else chat = await CachedOrResolveUsername(url[start..slash], chats); diff --git a/src/Client.cs b/src/Client.cs index b35b3b9..2b3447c 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -522,7 +522,7 @@ namespace WTelegram for (int i = 0; i < count; i++) { var msg = array[i] = new _Message(reader.ReadInt64(), reader.ReadInt32(), null) { bytes = reader.ReadInt32() }; - if ((msg.seqno & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msg.msg_id); + if ((msg.seq_no & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msg.msg_id); var pos = reader.BaseStream.Position; try { @@ -535,7 +535,7 @@ namespace WTelegram else { var obj = msg.body = reader.ReadTLObject(ctorNb); - Helpers.Log(1, $" → {obj.GetType().Name,-38} {MsgIdToStamp(msg.msg_id):u} {((msg.seqno & 1) != 0 ? "" : "(svc)")} {((msg.msg_id & 2) == 0 ? "" : "NAR")}"); + Helpers.Log(1, $" → {obj.GetType().Name,-38} {MsgIdToStamp(msg.msg_id):u} {((msg.seq_no & 1) != 0 ? "" : "(svc)")} {((msg.msg_id & 2) == 0 ? "" : "NAR")}"); } } catch (Exception ex) diff --git a/src/Session.cs b/src/Session.cs index 7211e84..d5d0b82 100644 --- a/src/Session.cs +++ b/src/Session.cs @@ -7,7 +7,7 @@ using System.Net; using System.Security.Cryptography; using System.Text.Json; -//( Don't change this code to lower the security. That's contrary to Telegram recommended practices. Read the official API documentation. +// Don't change this code to lower the security. It's following Telegram security recommendations https://corefork.telegram.org/mtproto/description namespace WTelegram { @@ -81,7 +81,7 @@ namespace WTelegram public DateTime SessionStart => _sessionStart; private readonly DateTime _sessionStart = DateTime.UtcNow; - private readonly SHA256 _sha256 = SHA256.Create(); // put + private readonly SHA256 _sha256 = SHA256.Create(); private Stream _store; private byte[] _reuseKey; // used only if AES Encryptor.CanReuseTransform = false (Mono) private byte[] _encrypted = new byte[16]; @@ -95,7 +95,7 @@ namespace WTelegram _store.Dispose(); _encryptor.Dispose(); _jsonWriter.Dispose(); - _jsonStream.Dispose(); // this + _jsonStream.Dispose(); } internal static Session LoadOrCreate(Stream store, byte[] rgbKey) @@ -107,7 +107,7 @@ namespace WTelegram var length = (int)store.Length; if (length > 0) { - var input = new byte[length]; // code + var input = new byte[length]; if (store.Read(input, 0, length) != length) throw new WTException($"Can't read session block ({store.Position}, {length})"); using var sha256 = SHA256.Create(); @@ -141,7 +141,7 @@ namespace WTelegram int encryptedLen = 64 + (utf8JsonLen & ~15); lock (_store) // while updating _encrypted buffer and writing to store { - if (encryptedLen > _encrypted.Length) // back + if (encryptedLen > _encrypted.Length) Array.Copy(_encrypted, _encrypted = new byte[encryptedLen + 256], 16); _encryptor.TransformBlock(_sha256.ComputeHash(utf8Json, 0, utf8JsonLen), 0, 32, _encrypted, 16); _encryptor.TransformBlock(utf8Json, 0, encryptedLen - 64, _encrypted, 48); @@ -192,7 +192,6 @@ namespace WTelegram } } - // QWxp couldn't be bothered to write such a simple SessionStore, so here it is: internal class ActionStore : MemoryStream { private readonly Action _save; diff --git a/src/TL.Extensions.cs b/src/TL.Extensions.cs index aef2c56..96fb1af 100644 --- a/src/TL.Extensions.cs +++ b/src/TL.Extensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -39,9 +40,13 @@ namespace TL public static void CollectUsersChats(this IPeerResolver structure, IDictionary users, IDictionary chats) => structure.UserOrChat(new CollectorPeer { _users = users, _chats = chats }); + [EditorBrowsable(EditorBrowsableState.Never)] public static Task Messages_GetChats(this Client _) => throw new WTException("The method you're looking for is Messages_GetAllChats"); + [EditorBrowsable(EditorBrowsableState.Never)] public static Task Channels_GetChannels(this Client _) => throw new WTException("The method you're looking for is Messages_GetAllChats"); + [EditorBrowsable(EditorBrowsableState.Never)] public static Task Users_GetUsers(this Client _) => throw new WTException("The method you're looking for is Messages_GetAllDialogs"); + [EditorBrowsable(EditorBrowsableState.Never)] public static Task Messages_GetMessages(this Client _) => throw new WTException("If you want to get all messages from a chat, use method Messages_GetHistory"); } diff --git a/src/TL.cs b/src/TL.cs index 60d84ba..77e8087 100644 --- a/src/TL.cs +++ b/src/TL.cs @@ -198,10 +198,10 @@ namespace TL foreach (var msg in messages) { writer.Write(msg.msg_id); - writer.Write(msg.seqno); + writer.Write(msg.seq_no); var patchPos = writer.BaseStream.Position; writer.Write(0); // patched below - if ((msg.seqno & 1) != 0) + if ((msg.seq_no & 1) != 0) WTelegram.Helpers.Log(1, $" → {msg.body.GetType().Name.TrimEnd('_'),-38} #{(short)msg.msg_id.GetHashCode():X4}"); else WTelegram.Helpers.Log(1, $" → {msg.body.GetType().Name.TrimEnd('_'),-38}"); @@ -372,11 +372,11 @@ namespace TL } [TLDef(0x5BB8E511)] //message#5bb8e511 msg_id:long seqno:int bytes:int body:Object = Message - public class _Message + public class _Message : IObject { - public _Message(long msgId, int seqNo, IObject obj) { msg_id = msgId; seqno = seqNo; body = obj; } + public _Message(long msgId, int seqNo, IObject obj) { msg_id = msgId; seq_no = seqNo; body = obj; } public long msg_id; - public int seqno; + public int seq_no; public int bytes; public IObject body; }