mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Renamed Update event to OnUpdate, returning Task
(to gracefully handle async exceptions)
This commit is contained in:
parent
6977641b2d
commit
668b19e3e8
|
|
@ -312,14 +312,15 @@ finally
|
||||||
<a name="updates"></a>
|
<a name="updates"></a>
|
||||||
### Monitor all Telegram events happening for the user
|
### 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).
|
See [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
|
||||||
|
|
||||||
<a name="monitor-msg"></a>
|
<a name="monitor-msg"></a>
|
||||||
### Monitor new messages being posted in chats
|
### 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).
|
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>
|
<a name="collect-access-hash"></a>
|
||||||
### Collect Access Hash and save them for later use
|
### 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.
|
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.
|
This is done by activating the experimental `client.CollectAccessHash` system.
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ namespace WTelegramClientTest
|
||||||
Console.WriteLine("The program will download photos/medias from messages you send/forward to yourself (Saved Messages)");
|
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);
|
using var client = new WTelegram.Client(Environment.GetEnvironmentVariable);
|
||||||
var user = await client.LoginUserIfNeeded();
|
var user = await client.LoginUserIfNeeded();
|
||||||
client.Update += Client_Update;
|
client.OnUpdate += Client_OnUpdate;
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
|
|
||||||
async void Client_Update(IObject arg)
|
async Task Client_OnUpdate(IObject arg)
|
||||||
{
|
{
|
||||||
if (arg is not Updates { updates: var updates } upd) return;
|
if (arg is not Updates { updates: var updates } upd) return;
|
||||||
foreach (var update in updates)
|
foreach (var update in updates)
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace WTelegramClientTest
|
||||||
Client = new WTelegram.Client(store.Length == 0 ? null : Environment.GetEnvironmentVariable, store);
|
Client = new WTelegram.Client(store.Length == 0 ? null : Environment.GetEnvironmentVariable, store);
|
||||||
using (Client)
|
using (Client)
|
||||||
{
|
{
|
||||||
Client.Update += Client_Update;
|
Client.OnUpdate += Client_OnUpdate;
|
||||||
My = await Client.LoginUserIfNeeded();
|
My = await Client.LoginUserIfNeeded();
|
||||||
Console.WriteLine($"We are logged-in as {My.username ?? My.first_name + " " + My.last_name} (id {My.id})");
|
Console.WriteLine($"We are logged-in as {My.username ?? My.first_name + " " + My.last_name} (id {My.id})");
|
||||||
var dialogs = await Client.Messages_GetAllDialogs();
|
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;
|
if (arg is not UpdatesBase updates) return;
|
||||||
updates.CollectUsersChats(Users, Chats);
|
updates.CollectUsersChats(Users, Chats);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace WTelegramClientTest
|
||||||
Client = new WTelegram.Client(Environment.GetEnvironmentVariable);
|
Client = new WTelegram.Client(Environment.GetEnvironmentVariable);
|
||||||
using (Client)
|
using (Client)
|
||||||
{
|
{
|
||||||
Client.Update += Client_Update;
|
Client.OnUpdate += Client_OnUpdate;
|
||||||
My = await Client.LoginUserIfNeeded();
|
My = await Client.LoginUserIfNeeded();
|
||||||
Users[My.id] = My;
|
Users[My.id] = My;
|
||||||
// Note: on login, Telegram may sends a bunch of updates/messages that happened in the past and were not acknowledged
|
// 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);
|
updates.CollectUsersChats(Users, Chats);
|
||||||
foreach (var update in updates.UpdateList)
|
foreach (var update in updates.UpdateList)
|
||||||
switch (update)
|
switch (update)
|
||||||
|
|
@ -52,6 +53,7 @@ namespace WTelegramClientTest
|
||||||
case UpdateUserPhoto uup: Console.WriteLine($"{User(uup.user_id)} has changed profile photo"); break;
|
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
|
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)
|
private static void DisplayMessage(MessageBase messageBase, bool edit = false)
|
||||||
|
|
|
||||||
4
FAQ.md
4
FAQ.md
|
|
@ -134,7 +134,7 @@ Here are some advices from [another similar library](https://github.com/gotd/td/
|
||||||
|
|
||||||
Some additional advices from me:
|
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)
|
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.
|
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.
|
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.
|
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.
|
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.
|
and pending API calls throw the network IOException.
|
||||||
In this case, the recommended action would be to dispose the client and recreate one
|
In this case, the recommended action would be to dispose the client and recreate one
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ In the API, Telegram uses some terms/classnames that can be confusing as they di
|
||||||
|
|
||||||
# Other things to know
|
# 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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ namespace WTelegram
|
||||||
else
|
else
|
||||||
updates = await this.Messages_SendMedia(peer, media, text, random_id, entities: entities,
|
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);
|
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;
|
int msgId = -1;
|
||||||
foreach (var update in updates.UpdateList)
|
foreach (var update in updates.UpdateList)
|
||||||
{
|
{
|
||||||
|
|
@ -282,7 +282,7 @@ namespace WTelegram
|
||||||
if (entities != null) lastMedia.flags = InputSingleMedia.Flags.has_entities;
|
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);
|
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 msgIds = new int[medias.Length];
|
||||||
var result = new Message[medias.Length];
|
var result = new Message[medias.Length];
|
||||||
foreach (var update in updates.UpdateList)
|
foreach (var update in updates.UpdateList)
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ namespace WTelegram
|
||||||
public partial class Client : IDisposable
|
public partial class Client : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>This event will be called when unsollicited updates/messages are sent by Telegram servers</summary>
|
/// <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>
|
/// <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 Action<IObject> Update;
|
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>
|
/// <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 TcpFactory TcpHandler { get; set; } = DefaultTcpHandler;
|
||||||
public delegate Task<TcpClient> TcpFactory(string host, int port);
|
public delegate Task<TcpClient> TcpFactory(string host, int port);
|
||||||
|
|
@ -150,7 +150,7 @@ namespace WTelegram
|
||||||
public static void LoadPublicKey(string pem) => Encryption.LoadPublicKey(pem);
|
public static void LoadPublicKey(string pem) => Encryption.LoadPublicKey(pem);
|
||||||
|
|
||||||
/// <summary>Builds a structure that is used to validate a 2FA password</summary>
|
/// <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>
|
/// <param name="password">The password to validate</param>
|
||||||
public static Task<InputCheckPasswordSRP> InputCheckPassword(Account_Password accountPassword, string password)
|
public static Task<InputCheckPasswordSRP> InputCheckPassword(Account_Password accountPassword, string password)
|
||||||
=> Check2FA(accountPassword, () => Task.FromResult(password));
|
=> Check2FA(accountPassword, () => Task.FromResult(password));
|
||||||
|
|
@ -321,7 +321,7 @@ namespace WTelegram
|
||||||
if (IsMainDC)
|
if (IsMainDC)
|
||||||
{
|
{
|
||||||
var updatesState = await this.Updates_GetState(); // this call reenables incoming Updates
|
var updatesState = await this.Updates_GetState(); // this call reenables incoming Updates
|
||||||
OnUpdate(updatesState);
|
RaiseUpdate(updatesState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -329,7 +329,7 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
OnUpdate(reactorError);
|
RaiseUpdate(reactorError);
|
||||||
lock (_pendingRpcs) // abort all pending requests
|
lock (_pendingRpcs) // abort all pending requests
|
||||||
{
|
{
|
||||||
foreach (var rpc in _pendingRpcs.Values)
|
foreach (var rpc in _pendingRpcs.Values)
|
||||||
|
|
@ -633,7 +633,7 @@ namespace WTelegram
|
||||||
rpc.tcs.SetException(new ApplicationException($"BadMsgNotification {badMsgNotification.error_code}"));
|
rpc.tcs.SetException(new ApplicationException($"BadMsgNotification {badMsgNotification.error_code}"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
OnUpdate(obj);
|
RaiseUpdate(obj);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (_bareRpc != null)
|
if (_bareRpc != null)
|
||||||
|
|
@ -648,7 +648,7 @@ namespace WTelegram
|
||||||
else
|
else
|
||||||
Helpers.Log(4, $"Received a {obj.GetType()} incompatible with expected bare {rpc?.type}");
|
Helpers.Log(4, $"Received a {obj.GetType()} incompatible with expected bare {rpc?.type}");
|
||||||
}
|
}
|
||||||
OnUpdate(obj);
|
RaiseUpdate(obj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -658,19 +658,19 @@ namespace WTelegram
|
||||||
if (rpc != null)
|
if (rpc != null)
|
||||||
rpc.tcs.SetResult(result);
|
rpc.tcs.SetResult(result);
|
||||||
else
|
else
|
||||||
OnUpdate(obj);
|
RaiseUpdate(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUpdate(IObject obj)
|
private async void RaiseUpdate(IObject obj)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Update?.Invoke(obj);
|
await OnUpdate?.Invoke(obj);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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:
|
resent:
|
||||||
var timeout = DateTime.UtcNow + TimeSpan.FromSeconds(sentCode.timeout);
|
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..]}");
|
Helpers.Log(3, $"A verification code has been sent via {sentCode.type.GetType().Name[17..]}");
|
||||||
for (int retry = 1; authorization == null; retry++)
|
for (int retry = 1; authorization == null; retry++)
|
||||||
try
|
try
|
||||||
|
|
@ -973,7 +973,7 @@ namespace WTelegram
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var accountPassword = await this.Account_GetPassword();
|
var accountPassword = await this.Account_GetPassword();
|
||||||
OnUpdate(accountPassword);
|
RaiseUpdate(accountPassword);
|
||||||
var checkPasswordSRP = await Check2FA(accountPassword, () => ConfigAsync("password"));
|
var checkPasswordSRP = await Check2FA(accountPassword, () => ConfigAsync("password"));
|
||||||
authorization = await this.Auth_CheckPassword(checkPasswordSRP);
|
authorization = await this.Auth_CheckPassword(checkPasswordSRP);
|
||||||
}
|
}
|
||||||
|
|
@ -992,7 +992,7 @@ namespace WTelegram
|
||||||
if (authorization is Auth_AuthorizationSignUpRequired signUpRequired)
|
if (authorization is Auth_AuthorizationSignUpRequired signUpRequired)
|
||||||
{
|
{
|
||||||
var waitUntil = DateTime.UtcNow.AddSeconds(3);
|
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 first_name = Config("first_name");
|
||||||
var last_name = Config("last_name");
|
var last_name = Config("last_name");
|
||||||
var wait = waitUntil - DateTime.UtcNow;
|
var wait = waitUntil - DateTime.UtcNow;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue