Handle BadMsgNotification 32/33 by renewing session

This commit is contained in:
Wizou 2021-12-04 00:14:15 +01:00
parent bafe3c56bd
commit e1132f653b
5 changed files with 48 additions and 29 deletions

6
FAQ.md
View file

@ -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.

View file

@ -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.

View file

@ -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}");
var tcs = PullPendingRequest(badMsgNotification.bad_msg_id).tcs;
if (tcs != null)
switch (badMsgNotification.error_code)
{
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<object> 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)

View file

@ -12,7 +12,7 @@ namespace WTelegram
public static Action<int, string> Log { get; set; } = DefaultLogger;
/// <summary>For serializing indented Json with fields included</summary>
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<K, V>(this Dictionary<K, V> dictionary, K key) where V : new()
=> dictionary.TryGetValue(key, out V value) ? value : dictionary[key] = new V();

View file

@ -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;