From 39f03ed78f2e6302cc408a52522e22850215de94 Mon Sep 17 00:00:00 2001 From: Wizou Date: Sat, 14 Aug 2021 08:55:30 +0200 Subject: [PATCH] Support for FLOOD_WAIT_X --- README.md | 13 ++++++++++--- src/Client.cs | 33 ++++++++++++++++++++++++--------- src/Helpers.TL.cs | 7 +++++++ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2e98aee..3bce2ff 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Its `int` argument is the log severity, compatible with the classic [LogLevel en :information_source: The Telegram API makes extensive usage of base and derived classes, so be ready to use the various syntaxes C# offer to check/cast base classes into the more useful derived classes (`is`, `as`, `case DerivedType` ) To find which derived classes are available for a given base class, the fastest is to check our [TL.Schema.cs](src/TL.Schema.cs) source file as they are listed in groups. +Intellisense tooltips on API structures/methods will also display a web link to the adequate Telegram documentation page. The Telegram [API object classes](https://core.telegram.org/schema) are defined in the `TL` namespace, and the [API functions](https://core.telegram.org/methods) are available as async methods of `Client`. @@ -102,14 +103,20 @@ await client.SendMessageAsync(target, "Hello, World"); An invalid API request can result in a RpcException being raised, reflecting the [error code and status text](https://core.telegram.org/api/errors) of the problem. -Beyond the TL async methods, the Client class offers a few other methods to simplify the sending of files, medias or messages. - The other configuration items that you can override include: **session_pathname, server_address, device_model, system_version, app_version, system_lang_code, lang_pack, lang_code** +Optional API parameters have a default value of `null` when unset. Passing `null` for a required string/array is the same as *empty* (0-length). Required API parameters/fields can sometimes be set to 0 or `null` when unused (check API documentation). + +I've added several useful converters or implicit cast to various API object so that they are more easy to manipulate. + +Beyond the TL async methods, the Client class offers a few other methods to simplify the sending of files, medias or messages. + For the moment, this library requires .NET 5.0 minimum. # Development status -The library is already well usable for many scenarios involving automated steps based on API requests/responses. +The library is usable for most scenarios including (sequential or parallel) automated steps based on API requests/responses, or real-time monitoring of incoming Updates/messages. Secret chats have not been tested yet. + +Developers feedback are welcome in the Telegram channel [@WTelegramClient](https://t.me/WTelegramClient) Here are the main expected developments: - [x] Encrypt session file diff --git a/src/Client.cs b/src/Client.cs index 804f692..c328317 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -422,22 +422,31 @@ namespace WTelegram lock (_pendingRequests) if (_pendingRequests.TryGetValue(msgId, out request)) _pendingRequests.Remove(msgId); + object result; if (request.type != null) { - var result = reader.ReadTLValue(request.type); - Helpers.Log(1, $" → {result?.GetType().Name,-47} #{(short)msgId.GetHashCode():X4}"); + result = reader.ReadTLValue(request.type); + Log(1, ""); Task.Run(() => request.tcs.SetResult(result)); // to avoid deadlock, see https://blog.stephencleary.com/2012/12/dont-block-in-asynchronous-code.html return new RpcResult { req_msg_id = msgId, result = result }; } else { - var result = reader.ReadTLObject(); + result = reader.ReadTLObject(); if (_session.MsgIdToStamp(msgId) >= _session.SessionStart) - Helpers.Log(4, $" → {result?.GetType().Name,-47} for unknown msgId #{(short)msgId.GetHashCode():X4}"); + Log(4, "for unknown msgId "); else - Helpers.Log(1, $" → {result?.GetType().Name,-47} for past msgId #{(short)msgId.GetHashCode():X4}"); + Log(1, "for past msgId "); return new RpcResult { req_msg_id = msgId, result = result }; } + + void Log(int level, string msgIdprefix) + { + if (result is RpcError rpcError) + Helpers.Log(4, $" → RpcError {rpcError.error_code,3} {rpcError.error_message,-34} {msgIdprefix}#{(short)msgId.GetHashCode():X4}"); + else + Helpers.Log(level, $" → {result?.GetType().Name,-47} {msgIdprefix}#{(short)msgId.GetHashCode():X4}"); + } } public class RpcException : Exception @@ -467,11 +476,17 @@ namespace WTelegram { case X resultX: return resultX; case RpcError rpcError: - int migrateDC; - if (rpcError.error_code == 303 && ((migrateDC = rpcError.error_message.IndexOf("_MIGRATE_")) > 0)) + int number; + if (rpcError.error_code == 303 && ((number = rpcError.error_message.IndexOf("_MIGRATE_")) > 0)) { - migrateDC = int.Parse(rpcError.error_message[(migrateDC + 9)..]); - await MigrateDCAsync(migrateDC); + number = int.Parse(rpcError.error_message[(number + 9)..]); + await MigrateDCAsync(number); + goto retry; + } + else if (rpcError.error_code == 420 && ((number = rpcError.error_message.IndexOf("_WAIT_")) > 0)) + { + number = int.Parse(rpcError.error_message[(number + 6)..]); + await Task.Delay(number * 1000); goto retry; } else diff --git a/src/Helpers.TL.cs b/src/Helpers.TL.cs index 69b9fc4..551c84f 100644 --- a/src/Helpers.TL.cs +++ b/src/Helpers.TL.cs @@ -13,6 +13,7 @@ { public abstract int ID { get; } public abstract string Title { get; } + public abstract bool IsBanned(ChatBannedRights.Flags flags = 0); protected abstract InputPeer ToInputPeer(); public static implicit operator InputPeer(ChatBase chat) => chat.ToInputPeer(); } @@ -20,24 +21,29 @@ { public override int ID => id; public override string Title => null; + public override bool IsBanned(ChatBannedRights.Flags flags = 0) => true; protected override InputPeer ToInputPeer() => InputPeer.Empty; } partial class Chat { public override int ID => id; public override string Title => title; + /// returns true if you're banned of any of these rights + public override bool IsBanned(ChatBannedRights.Flags flags = 0) => ((default_banned_rights?.flags ?? 0) & flags) != 0; protected override InputPeer ToInputPeer() => new InputPeerChat { chat_id = id }; } partial class ChatForbidden { public override int ID => id; public override string Title => title; + public override bool IsBanned(ChatBannedRights.Flags flags = 0) => true; protected override InputPeer ToInputPeer() => new InputPeerChat { chat_id = id }; } partial class Channel { public override int ID => id; public override string Title => title; + public override bool IsBanned(ChatBannedRights.Flags flags = 0) => ((banned_rights?.flags ?? 0) & flags) != 0 || ((default_banned_rights?.flags ?? 0) & flags) != 0; protected override InputPeer ToInputPeer() => new InputPeerChannel { channel_id = id, access_hash = access_hash }; public static implicit operator InputChannel(Channel channel) => new() { channel_id = channel.id, access_hash = channel.access_hash }; } @@ -45,6 +51,7 @@ { public override int ID => id; public override string Title => title; + public override bool IsBanned(ChatBannedRights.Flags flags = 0) => true; protected override InputPeer ToInputPeer() => new InputPeerChannel { channel_id = id, access_hash = access_hash }; }