Provide X number (if any) and generic message in RpcException. Retry only once on -503 error.

This commit is contained in:
Wizou 2022-04-11 12:08:17 +02:00
parent e6a1dbb24d
commit 796b49546e
7 changed files with 42 additions and 29 deletions

2
.github/dev.yml vendored
View file

@ -2,7 +2,7 @@ pr: none
trigger:
- master
name: 2.2.2-dev.$(Rev:r)
name: 2.3.1-dev.$(Rev:r)
pool:
vmImage: ubuntu-latest

2
.github/release.yml vendored
View file

@ -1,7 +1,7 @@
pr: none
trigger: none
name: 2.2.$(Rev:r)
name: 2.3.$(Rev:r)
pool:
vmImage: ubuntu-latest

View file

@ -362,10 +362,9 @@ namespace WTelegram
{
fileBase = await client.Upload_GetFile(fileLocation, offset, FilePartSize);
}
catch (RpcException ex) when (ex.Code == 303 && ex.Message.StartsWith("FILE_MIGRATE_"))
catch (RpcException ex) when (ex.Code == 303 && ex.Message == "FILE_MIGRATE_X")
{
var dcId = int.Parse(ex.Message[13..]);
client = await GetClientForDC(dcId, true);
client = await GetClientForDC(ex.X, true);
fileBase = await client.Upload_GetFile(fileLocation, offset, FilePartSize);
}
catch (RpcException ex) when (ex.Code == 400 && ex.Message == "OFFSET_INVALID")

View file

@ -1088,41 +1088,40 @@ namespace WTelegram
}
}
internal async Task<X> InvokeBare<X>(IMethod<X> request)
internal async Task<T> InvokeBare<T>(IMethod<T> request)
{
if (_bareRpc != null) throw new ApplicationException("A bare request is already undergoing");
_bareRpc = new Rpc { type = typeof(X) };
_bareRpc = new Rpc { type = typeof(T) };
await SendAsync(request, false, _bareRpc);
return (X)await _bareRpc.Task;
return (T)await _bareRpc.Task;
}
/// <summary>Call the given TL method <i>(You shouldn't need to use this method directly)</i></summary>
/// <typeparam name="X">Expected type of the returned object</typeparam>
/// <typeparam name="T">Expected type of the returned object</typeparam>
/// <param name="query">TL method structure</param>
/// <returns>Wait for the reply and return the resulting object, or throws an RpcException if an error was replied</returns>
public async Task<X> Invoke<X>(IMethod<X> query)
public async Task<T> Invoke<T>(IMethod<T> query)
{
retry:
var rpc = new Rpc { type = typeof(X) };
await SendAsync(query, true, rpc);
bool got503 = false;
retry:
var rpc = new Rpc { type = typeof(T) };
await SendAsync(query, true, rpc);
var result = await rpc.Task;
switch (result)
{
case X resultX: return resultX;
case T resultT: return resultT;
case RpcError rpcError:
int number;
if (rpcError.error_code == 303 && ((number = rpcError.error_message.IndexOf("_MIGRATE_")) > 0))
var x = rpcError.ParseX();
if (rpcError.error_code == 303 && rpcError.error_message.EndsWith("_MIGRATE_X"))
{
if (!rpcError.error_message.StartsWith("FILE_"))
if (rpcError.error_message != "FILE_MIGRATE_X")
{
number = int.Parse(rpcError.error_message[(number + 9)..]);
// this is a hack to migrate _dcSession in-place (staying in same Client):
Session.DCSession dcSession;
lock (_session)
dcSession = GetOrCreateDCSession(number, _dcSession.DataCenter.flags);
dcSession = GetOrCreateDCSession(x, _dcSession.DataCenter.flags);
Reset(false, false);
_session.MainDC = number;
_session.MainDC = x;
_dcSession.Client = null;
_dcSession = dcSession;
_dcSession.Client = this;
@ -1130,12 +1129,11 @@ namespace WTelegram
goto retry;
}
}
else if (rpcError.error_code == 420 && ((number = rpcError.error_message.IndexOf("_WAIT_")) > 0))
else if (rpcError.error_code == 420 && rpcError.error_message.EndsWith("_WAIT_X"))
{
number = int.Parse(rpcError.error_message[(number + 6)..]);
if (number <= FloodRetryThreshold)
if (x <= FloodRetryThreshold)
{
await Task.Delay(number * 1000);
await Task.Delay(x * 1000);
goto retry;
}
}
@ -1149,11 +1147,11 @@ namespace WTelegram
_session.UserId = 0; // force a full login authorization flow, next time
lock (_session) _session.Save();
}
throw new RpcException(rpcError.error_code, rpcError.error_message);
throw new RpcException(rpcError.error_code, rpcError.error_message, x);
case ReactorError:
goto retry;
default:
throw new ApplicationException($"{query.GetType().Name} call got a result of type {result.GetType().Name} instead of {typeof(X).Name}");
throw new ApplicationException($"{query.GetType().Name} call got a result of type {result.GetType().Name} instead of {typeof(T).Name}");
}
}
}

View file

@ -32,8 +32,6 @@ namespace TL
/// <summary>Accumulate users/chats found in this structure in your dictionaries, ignoring <see href="https://core.telegram.org/api/min">Min constructors</see> when the full object is already stored</summary>
/// <param name="structure">The structure having a <c>users</c></param>
/// <param name="users"></param>
/// <param name="chats"></param>
public static void CollectUsersChats(this IPeerResolver structure, Dictionary<long, User> users, Dictionary<long, ChatBase> chats)
=> structure.UserOrChat(new CollectorPeer { _users = users, _chats = chats });
}

View file

@ -198,6 +198,22 @@ namespace TL
{
public int error_code;
public string error_message;
public int ParseX() // ⚠ Method replace number in error_message with a X
{
for (int index = error_message.Length - 1; index > 0 && (index = error_message.LastIndexOf('_', index - 1)) >= 0;)
{
if (error_message[index + 1] is >= '0' and <= '9')
{
int end = ++index;
do end++; while (end < error_message.Length && error_message[end] is >= '0' and <= '9');
var x = int.Parse(error_message[index..end]);
error_message = $"{error_message[0..index]}X{error_message[end..]}";
return x;
}
}
return -1;
}
}
public abstract class RpcDropAnswer : IObject { }

View file

@ -31,7 +31,9 @@ namespace TL
public class RpcException : Exception
{
public readonly int Code;
public RpcException(int code, string message) : base(message) => Code = code;
/// <summary>The value of X in the message, -1 if no variable X was found</summary>
public readonly int X;
public RpcException(int code, string message, int x = -1) : base(message) { Code = code; X = x; }
public override string ToString() { var str = base.ToString(); return str.Insert(str.IndexOf(':') + 1, " " + Code); }
}