diff --git a/FAQ.md b/FAQ.md index 6d474a2..4dedeb4 100644 --- a/FAQ.md +++ b/FAQ.md @@ -32,9 +32,9 @@ This might require adding a reference *(and `using`)* to the Microsoft.VisualBas #### 4. I get the error `CHAT_ID_INVALID` or `CHANNEL_INVALID` -First, you should distinguish between Chat, Channel and Group/Supergroup: See Terminology in [ReadMe](README.md#Terminology-in-Telegram-Client-API) -Most chat groups are in fact not a simple `Chat` but rather a (super)group (= a `Channel` without the `broadcast` flag) -Some TL methods only applies to simple Chat, and some only applies to Channels. +First, you should distinguish between Chat, Channel and Group/Supergroup: See Terminology in [ReadMe](README.md#Terminology-in-Telegram-Client-API). +Common chat groups are usually not a simple `Chat` but rather a supergroup: a `Channel` without the `broadcast` flag +Some TL methods only applies to simple `Chat`, and some only applies to `Channel`. A simple `Chat` can be queried using their `chat_id` only. But a `Channel` must be queried using an `InputChannel` or `InputPeerChannel` object that specifies the `channel_id` as well as an `access_hash` that proves you are allowed to access it. diff --git a/README.md b/README.md index d6441bd..7ecb0e6 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ await client.SendMessageAsync(target, "Hello, World"); # Terminology in Telegram Client API In the API, Telegram uses some terms/classnames that can be confusing as they differ from the terms shown to end-users: -- `Channel` : A (large) chat group *(sometimes called supergroup)* or a broadcast channel (the `broadcast` flag differenciate those) -- `Chat` : A private simple chat group with few people (it may be migrated to a supergroup/channel when it doesn't fit anymore) -- Chats : In plural, it means either `Chat` or `Channel` +- `Channel` : A (large or public) chat group *(sometimes called supergroup)* or a broadcast channel (the `broadcast` flag differenciate those) +- `Chat` : A private simple chat group with less than 200 members (it may be migrated to a supergroup `Channel` with a new ID when it gets bigger or public, in which case the old `Chat` will still exist but be `deactivated`) +- chats : In plural or general meaning, it means either `Chat` or `Channel` - `Peer` : Either a `Chat`, `Channel` or a private chat with a `User` - Dialog : The current status of a chat with a `Peer` *(draft, last message, unread count, pinned...)* - DC (DataCenter) : There are a few datacenters depending on where in the world the user (or an uploaded media file) is from. diff --git a/src/Client.cs b/src/Client.cs index 45963cb..e3ca2ea 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -837,19 +837,6 @@ namespace WTelegram if (msgCopy?.orig_message?.body != null) await HandleMessageAsync(msgCopy.orig_message.body); break; - case BadServerSalt badServerSalt: - _dcSession.Salt = badServerSalt.new_server_salt; - if (badServerSalt.bad_msg_id == _dcSession.LastSentMsgId) - { - var newMsgId = await SendAsync(_lastSentMsg, true); - lock (_pendingRequests) - if (_pendingRequests.TryGetValue(badServerSalt.bad_msg_id, out var t)) - { - _pendingRequests.Remove(badServerSalt.bad_msg_id); - _pendingRequests[newMsgId] = t; - } - } - break; case TL.Methods.Ping ping: _ = SendAsync(new Pong { msg_id = _lastRecvMsgId, ping_id = ping.ping_id }, false); break; @@ -864,17 +851,48 @@ namespace WTelegram case MsgsAck msgsAck: break; // we don't do anything with these, for now case BadMsgNotification badMsgNotification: + await _sendSemaphore.WaitAsync(); + bool retryLast = badMsgNotification.bad_msg_id == _dcSession.LastSentMsgId; + var lastSentMsg = _lastSentMsg; + _sendSemaphore.Release(); + Helpers.Log(4, $"BadMsgNotification {badMsgNotification.error_code} for msg #{(short)badMsgNotification.bad_msg_id.GetHashCode():X4}"); + switch (badMsgNotification.error_code) { - Helpers.Log(4, $"BadMsgNotification {badMsgNotification.error_code} for msg #{(short)badMsgNotification.bad_msg_id.GetHashCode():X4}"); - var tcs = PullPendingRequest(badMsgNotification.bad_msg_id).tcs; - if (tcs != null) - { - if (_bareRequest == badMsgNotification.bad_msg_id) _bareRequest = 0; - tcs.SetException(new ApplicationException($"BadMsgNotification {badMsgNotification.error_code}")); - } - else - OnUpdate(obj); + case 32: // msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno) + case 33: // msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno) + if (_dcSession.Seqno <= 1) + retryLast = false; + else + { + Reset(false, false); + _dcSession.Renew(); + await ConnectAsync(); + } + break; + case 48: // incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it) + _dcSession.Salt = ((BadServerSalt)badMsgNotification).new_server_salt; + break; + default: + retryLast = false; + break; } + if (retryLast) + { + var newMsgId = await SendAsync(lastSentMsg, true); + lock (_pendingRequests) + if (_pendingRequests.TryGetValue(badMsgNotification.bad_msg_id, out var t)) + { + _pendingRequests.Remove(badMsgNotification.bad_msg_id); + _pendingRequests[newMsgId] = t; + } + } + else if (PullPendingRequest(badMsgNotification.bad_msg_id).tcs is TaskCompletionSource tcs) + { + if (_bareRequest == badMsgNotification.bad_msg_id) _bareRequest = 0; + tcs.SetException(new ApplicationException($"BadMsgNotification {badMsgNotification.error_code}")); + } + else + OnUpdate(obj); break; default: if (_bareRequest != 0) diff --git a/src/Helpers.cs b/src/Helpers.cs index d4a74ff..306037c 100644 --- a/src/Helpers.cs +++ b/src/Helpers.cs @@ -12,7 +12,7 @@ namespace WTelegram public static Action Log { get; set; } = DefaultLogger; /// For serializing indented Json with fields included - public static readonly JsonSerializerOptions JsonOptions = new() { IncludeFields = true, WriteIndented = true }; + public static readonly JsonSerializerOptions JsonOptions = new() { IncludeFields = true, WriteIndented = true, IgnoreReadOnlyProperties = true }; public static V GetOrCreate(this Dictionary dictionary, K key) where V : new() => dictionary.TryGetValue(key, out V value) ? value : dictionary[key] = new V(); diff --git a/src/Session.cs b/src/Session.cs index 89a2082..9fe35fd 100644 --- a/src/Session.cs +++ b/src/Session.cs @@ -33,6 +33,7 @@ namespace WTelegram internal Client Client; internal int DcID => DataCenter?.id ?? 0; internal IPEndPoint EndPoint => DataCenter == null ? null : new(IPAddress.Parse(DataCenter.ip_address), DataCenter.port); + internal void Renew() { Helpers.Log(3, $"Renewing session on DC {DcID}..."); Id = Helpers.RandomLong(); Seqno = 0; LastSentMsgId = 0; } } public DateTime SessionStart => _sessionStart;