Renamed Update event to OnUpdate, returning Task

(to gracefully handle async exceptions)
This commit is contained in:
Wizou 2022-07-29 15:24:18 +02:00
parent 6977641b2d
commit 668b19e3e8
8 changed files with 32 additions and 29 deletions

View file

@ -312,14 +312,15 @@ finally
<a name="updates"></a>
### Monitor all Telegram events happening for the user
This is done through the `client.Update` callback event.
This is done through the `client.OnUpdate` callback event.
Your event handler implementation can either return `Task.CompletedTask` or be an `async Task` method.
See [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
<a name="monitor-msg"></a>
### Monitor new messages being posted in chats
You have to handle `client.Update` events containing an `UpdateNewMessage`.
You have to handle `client.OnUpdate` events containing an `UpdateNewMessage`.
See the `DisplayMessage` method in [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
@ -336,7 +337,7 @@ See [Examples/Program_DownloadSavedMedia.cs](Examples/Program_DownloadSavedMedia
<a name="collect-access-hash"></a>
### Collect Access Hash and save them for later use
You can automate the collection of `access_hash` for the various resources obtained in response to API calls or Update events,
You can automate the collection of `access_hash` for the various resources obtained in response to API calls or Updates,
so that you don't have to remember them by yourself or ask the API about them each time.
This is done by activating the experimental `client.CollectAccessHash` system.

View file

@ -15,10 +15,10 @@ namespace WTelegramClientTest
Console.WriteLine("The program will download photos/medias from messages you send/forward to yourself (Saved Messages)");
using var client = new WTelegram.Client(Environment.GetEnvironmentVariable);
var user = await client.LoginUserIfNeeded();
client.Update += Client_Update;
client.OnUpdate += Client_OnUpdate;
Console.ReadKey();
async void Client_Update(IObject arg)
async Task Client_OnUpdate(IObject arg)
{
if (arg is not Updates { updates: var updates } upd) return;
foreach (var update in updates)

View file

@ -30,7 +30,7 @@ namespace WTelegramClientTest
Client = new WTelegram.Client(store.Length == 0 ? null : Environment.GetEnvironmentVariable, store);
using (Client)
{
Client.Update += Client_Update;
Client.OnUpdate += Client_OnUpdate;
My = await Client.LoginUserIfNeeded();
Console.WriteLine($"We are logged-in as {My.username ?? My.first_name + " " + My.last_name} (id {My.id})");
var dialogs = await Client.Messages_GetAllDialogs();
@ -39,7 +39,7 @@ namespace WTelegramClientTest
}
}
private static async void Client_Update(IObject arg)
private static async Task Client_OnUpdate(IObject arg)
{
if (arg is not UpdatesBase updates) return;
updates.CollectUsersChats(Users, Chats);

View file

@ -20,7 +20,7 @@ namespace WTelegramClientTest
Client = new WTelegram.Client(Environment.GetEnvironmentVariable);
using (Client)
{
Client.Update += Client_Update;
Client.OnUpdate += Client_OnUpdate;
My = await Client.LoginUserIfNeeded();
Users[My.id] = My;
// Note: on login, Telegram may sends a bunch of updates/messages that happened in the past and were not acknowledged
@ -32,9 +32,10 @@ namespace WTelegramClientTest
}
}
private static void Client_Update(IObject arg)
// in this example, we're not using async/await, so we just return Task.CompletedTask
private static Task Client_OnUpdate(IObject arg)
{
if (arg is not UpdatesBase updates) return;
if (arg is not UpdatesBase updates) return Task.CompletedTask;
updates.CollectUsersChats(Users, Chats);
foreach (var update in updates.UpdateList)
switch (update)
@ -52,6 +53,7 @@ namespace WTelegramClientTest
case UpdateUserPhoto uup: Console.WriteLine($"{User(uup.user_id)} has changed profile photo"); break;
default: Console.WriteLine(update.GetType().Name); break; // there are much more update types than the above cases
}
return Task.CompletedTask;
}
private static void DisplayMessage(MessageBase messageBase, bool edit = false)

4
FAQ.md
View file

@ -134,7 +134,7 @@ Here are some advices from [another similar library](https://github.com/gotd/td/
Some additional advices from me:
5. Avoid repetitive polling or repetitive sequence of actions/requests: Save the initial results of your queries, and update those results when you're informed of a change through `Update` events.
5. Avoid repetitive polling or repetitive sequence of actions/requests: Save the initial results of your queries, and update those results when you're informed of a change through `OnUpdate` events.
6. If a phone number is brand new, it will be closely monitored by Telegram for abuse, and it can even already be considered a bad user due to bad behavior from the previous owner of that phone number (which may happens often with VoIP or other easy-to-buy-online numbers, so expect fast ban)
7. You may want to use your new phone number account with an official Telegram client and act like a normal user for some time (some weeks/months), before using it for automation with WTelegramClient.
8. When creating a new API ID/Hash, I recommend you use your own phone number with long history of normal Telegram usage, rather than a brand new phone number with short history.
@ -191,7 +191,7 @@ If Telegram servers decide to shutdown this secondary connection, it's not an is
This should be transparent and pending API calls should automatically be resent upon reconnection.
You can choose to increase `MaxAutoReconnects` if it happens too often because your Internet connection is unstable.
3) If you reach `MaxAutoReconnects` disconnections, then the **Update** event handler will receive a `ReactorError` object to notify you of the problem,
3) If you reach `MaxAutoReconnects` disconnections, then the **OnUpdate** event handler will receive a `ReactorError` object to notify you of the problem.
and pending API calls throw the network IOException.
In this case, the recommended action would be to dispose the client and recreate one

View file

@ -130,7 +130,7 @@ In the API, Telegram uses some terms/classnames that can be confusing as they di
# Other things to know
The Client class also offers an `Update` event that is triggered when Telegram servers sends Updates (like new messages or status), independently of your API requests. See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs)
The Client class also offers an `OnUpdate` event that is triggered when Telegram servers sends Updates (like new messages or status), independently of your API requests. See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs)
An invalid API request can result in a `RpcException` being raised, reflecting the [error code and status text](https://revgram.github.io/errors.html) of the problem.

View file

@ -189,7 +189,7 @@ namespace WTelegram
else
updates = await this.Messages_SendMedia(peer, media, text, random_id, entities: entities,
reply_to_msg_id: reply_to_msg_id == 0 ? null : reply_to_msg_id, schedule_date: schedule_date == default ? null : schedule_date);
OnUpdate(updates);
RaiseUpdate(updates);
int msgId = -1;
foreach (var update in updates.UpdateList)
{
@ -282,7 +282,7 @@ namespace WTelegram
if (entities != null) lastMedia.flags = InputSingleMedia.Flags.has_entities;
var updates = await this.Messages_SendMultiMedia(peer, multiMedia, reply_to_msg_id: reply_to_msg_id, schedule_date: schedule_date);
OnUpdate(updates);
RaiseUpdate(updates);
var msgIds = new int[medias.Length];
var result = new Message[medias.Length];
foreach (var update in updates.UpdateList)

View file

@ -25,8 +25,8 @@ namespace WTelegram
public partial class Client : IDisposable
{
/// <summary>This event will be called when unsollicited updates/messages are sent by Telegram servers</summary>
/// <remarks>See <see href="https://github.com/wiz0u/WTelegramClient/tree/master/Examples/Program_ListenUpdate.cs">Examples/Program_ListenUpdate.cs</see> for how to use this</remarks>
public event Action<IObject> Update;
/// <remarks>Make your handler <see langword="async"/>, or return <see cref="Task.CompletedTask"></see><br/>See <see href="https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs">Examples/Program_ListenUpdate.cs</see> for how to use this</remarks>
public event Func<IObject, Task> OnUpdate;
/// <summary>Used to create a TcpClient connected to the given address/port, or throw an exception on failure</summary>
public TcpFactory TcpHandler { get; set; } = DefaultTcpHandler;
public delegate Task<TcpClient> TcpFactory(string host, int port);
@ -150,7 +150,7 @@ namespace WTelegram
public static void LoadPublicKey(string pem) => Encryption.LoadPublicKey(pem);
/// <summary>Builds a structure that is used to validate a 2FA password</summary>
/// <param name="accountPassword">Password validation configuration. You can obtain this though an Update event as part of the login process</param>
/// <param name="accountPassword">Password validation configuration. You can obtain this via <c>Account_GetPassword</c> or through OnUpdate as part of the login process</param>
/// <param name="password">The password to validate</param>
public static Task<InputCheckPasswordSRP> InputCheckPassword(Account_Password accountPassword, string password)
=> Check2FA(accountPassword, () => Task.FromResult(password));
@ -321,7 +321,7 @@ namespace WTelegram
if (IsMainDC)
{
var updatesState = await this.Updates_GetState(); // this call reenables incoming Updates
OnUpdate(updatesState);
RaiseUpdate(updatesState);
}
}
else
@ -329,7 +329,7 @@ namespace WTelegram
}
catch
{
OnUpdate(reactorError);
RaiseUpdate(reactorError);
lock (_pendingRpcs) // abort all pending requests
{
foreach (var rpc in _pendingRpcs.Values)
@ -633,7 +633,7 @@ namespace WTelegram
rpc.tcs.SetException(new ApplicationException($"BadMsgNotification {badMsgNotification.error_code}"));
}
else
OnUpdate(obj);
RaiseUpdate(obj);
break;
default:
if (_bareRpc != null)
@ -648,7 +648,7 @@ namespace WTelegram
else
Helpers.Log(4, $"Received a {obj.GetType()} incompatible with expected bare {rpc?.type}");
}
OnUpdate(obj);
RaiseUpdate(obj);
break;
}
@ -658,19 +658,19 @@ namespace WTelegram
if (rpc != null)
rpc.tcs.SetResult(result);
else
OnUpdate(obj);
RaiseUpdate(obj);
}
}
private void OnUpdate(IObject obj)
private async void RaiseUpdate(IObject obj)
{
try
{
Update?.Invoke(obj);
await OnUpdate?.Invoke(obj);
}
catch (Exception ex)
{
Helpers.Log(4, $"{nameof(Update)} callback on {obj.GetType().Name} raised {ex}");
Helpers.Log(4, $"{nameof(OnUpdate)}({obj?.GetType().Name}) raised {ex}");
}
}
@ -943,7 +943,7 @@ namespace WTelegram
{
resent:
var timeout = DateTime.UtcNow + TimeSpan.FromSeconds(sentCode.timeout);
OnUpdate(sentCode);
RaiseUpdate(sentCode);
Helpers.Log(3, $"A verification code has been sent via {sentCode.type.GetType().Name[17..]}");
for (int retry = 1; authorization == null; retry++)
try
@ -973,7 +973,7 @@ namespace WTelegram
try
{
var accountPassword = await this.Account_GetPassword();
OnUpdate(accountPassword);
RaiseUpdate(accountPassword);
var checkPasswordSRP = await Check2FA(accountPassword, () => ConfigAsync("password"));
authorization = await this.Auth_CheckPassword(checkPasswordSRP);
}
@ -992,7 +992,7 @@ namespace WTelegram
if (authorization is Auth_AuthorizationSignUpRequired signUpRequired)
{
var waitUntil = DateTime.UtcNow.AddSeconds(3);
OnUpdate(signUpRequired); // give caller the possibility to read and accept TOS
RaiseUpdate(signUpRequired); // give caller the possibility to read and accept TOS
var first_name = Config("first_name");
var last_name = Config("last_name");
var wait = waitUntil - DateTime.UtcNow;