Renamed OnUpdate => OnUpdates (with temporary compatibility shim)

This commit is contained in:
Wizou 2024-03-29 16:42:58 +01:00
parent 270a7d89e6
commit 3d224afb23
12 changed files with 49 additions and 52 deletions

View file

@ -187,7 +187,7 @@ Notes:
<a name="updates"></a>
## Monitor all Telegram events happening for the user
This is done through the `client.OnUpdate` callback event.
This is done through the `client.OnUpdates` callback event.
Your event handler implementation can either return `Task.CompletedTask` or be an `async Task` method.
See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23).
@ -195,7 +195,7 @@ See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient
<a name="monitor-msg"></a>
## Monitor new messages being posted in chats in real-time
You have to handle `client.OnUpdate` events containing an `UpdateNewMessage`.
You have to handle `client.OnUpdates` events containing an `UpdateNewMessage`.
See the `HandleMessage` method in [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23).
@ -453,7 +453,7 @@ finally
## Collect Users/Chats description structures and access hash
Many API calls return a structure with a `users` and a `chats` field at the root of the structure.
This is also the case for updates passed to `client.OnUpdate`.
This is also the case for updates passed to `client.OnUpdates`.
These two dictionaries give details *(including access hash)* about the various users/chats that will be typically referenced in subobjects deeper in the structure,
typically in the form of a `Peer` object or a `user_id`/`chat_id` field.
@ -473,7 +473,7 @@ private Dictionary<long, ChatBase> _chats = new();
var dialogs = await client.Messages_GetAllDialogs();
dialogs.CollectUsersChats(_users, _chats);
private async Task OnUpdate(UpdatesBase updates)
private async Task OnUpdates(UpdatesBase updates)
{
updates.CollectUsersChats(_users, _chats);
...

View file

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TL;
@ -15,10 +13,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.OnUpdate += Client_OnUpdate;
client.OnUpdates += Client_OnUpdates;
Console.ReadKey();
async Task Client_OnUpdate(UpdatesBase updates)
async Task Client_OnUpdates(UpdatesBase updates)
{
foreach (var update in updates.UpdateList)
{

View file

@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using TL;

View file

@ -30,7 +30,7 @@ namespace WTelegramClientTest
Client = new WTelegram.Client(store.Length == 0 ? null : Environment.GetEnvironmentVariable, store);
using (Client)
{
Client.OnUpdate += Client_OnUpdate;
Client.OnUpdates += Client_OnUpdates;
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 Task Client_OnUpdate(UpdatesBase updates)
private static async Task Client_OnUpdates(UpdatesBase updates)
{
updates.CollectUsersChats(Users, Chats);
foreach (var update in updates.UpdateList)

View file

@ -20,7 +20,7 @@ namespace WTelegramClientTest
Client = new WTelegram.Client(Environment.GetEnvironmentVariable);
using (Client)
{
Client.OnUpdate += Client_OnUpdate;
Client.OnUpdates += Client_OnUpdates;
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
@ -33,7 +33,7 @@ namespace WTelegramClientTest
}
// if not using async/await, we could just return Task.CompletedTask
private static async Task Client_OnUpdate(UpdatesBase updates)
private static async Task Client_OnUpdates(UpdatesBase updates)
{
updates.CollectUsersChats(Users, Chats);
if (updates is UpdateShortMessage usm && !Users.ContainsKey(usm.user_id))

View file

@ -1,6 +1,5 @@
using System;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TL;
namespace WTelegramClientTest
@ -28,7 +27,7 @@ namespace WTelegramClientTest
private static async Task CreateAndConnect()
{
Client = new WTelegram.Client(Environment.GetEnvironmentVariable);
Client.OnUpdate += Client_OnUpdate;
Client.OnUpdates += Client_OnUpdates;
Client.OnOther += Client_OnOther;
var my = await Client.LoginUserIfNeeded();
Console.WriteLine($"We are logged-in as " + my);
@ -61,7 +60,7 @@ namespace WTelegramClientTest
Console.WriteLine("Other: " + arg.GetType().Name);
}
private static Task Client_OnUpdate(UpdatesBase updates)
private static Task Client_OnUpdates(UpdatesBase updates)
{
foreach (var update in updates.UpdateList)
Console.WriteLine(update.GetType().Name);

View file

@ -25,7 +25,7 @@ namespace WTelegramClientTest
AppDomain.CurrentDomain.ProcessExit += (s, e) => { Secrets.Dispose(); Client.Dispose(); };
SelectActiveChat();
Client.OnUpdate += Client_OnUpdate;
Client.OnUpdates += Client_OnUpdates;
var myself = await Client.LoginUserIfNeeded();
Users[myself.id] = myself;
Console.WriteLine($"We are logged-in as {myself}");
@ -76,7 +76,7 @@ Type a command, or a message to send to the active secret chat:");
} while (true);
}
private static async Task Client_OnUpdate(UpdatesBase updates)
private static async Task Client_OnUpdates(UpdatesBase updates)
{
updates.CollectUsersChats(Users, Chats);
foreach (var update in updates.UpdateList)

4
FAQ.md
View file

@ -143,7 +143,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 `OnUpdate` 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 `OnUpdates` events.
6. Don't buy fake user accounts/sessions and don't extract api_id/hash/authkey/sessions from official clients, this is [specifically forbidden by API TOS](https://core.telegram.org/api/terms#2-transparency). You must use your own api_id and create your own sessions associated with it.
7. 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 happen often with VoIP or other easy-to-buy-online numbers, so expect fast ban)
8. 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.
@ -261,7 +261,7 @@ The following choices were made while implementing Secret Chats in WTelegramClie
If for some reason, we received them in incorrect order, messages are kept in memory until the requested missing messages are obtained.
If those missing messages are never obtained during the session, incoming messages might get stuck and lost.
- SecretChats file data is only valid for the current user, so make sure to pick the right file *(or a new file name)* if you change logged-in user.
- If you want to accept incoming Secret Chats request only from specific user, you must check it in OnUpdate before:
- If you want to accept incoming Secret Chats request only from specific user, you must check it in OnUpdates before:
`await Secrets.HandleUpdate(ue, ue.chat is EncryptedChatRequested ecr && ecr.admin_id == EXPECTED_USER_ID);`
- As recommended, new encryption keys are negotiated every 100 sent/received messages or after one week.
If remote client doesn't complete this negotiation before reaching 200 messages, the Secret Chat is aborted.

View file

@ -164,7 +164,7 @@ See [FAQ #4](https://wiz0u.github.io/WTelegramClient/FAQ#access-hash) to learn m
# Other things to know
The Client class also offers `OnUpdate` and `OnOther` events that are triggered when Telegram servers sends Updates (like new messages or status) or other notifications, independently of your API requests.
The Client class also offers `OnUpdates` and `OnOther` events that are triggered when Telegram servers sends Updates (like new messages or status) or other notifications, independently of your API requests.
See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23) and [Examples/Program_ReactorError.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ReactorError.cs?ts=4#L32)
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

@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Sockets;
@ -25,11 +24,13 @@ namespace WTelegram
{
/// <summary>This event will be called when unsollicited updates/messages are sent by Telegram servers</summary>
/// <remarks>Make your handler <see langword="async"/>, or return <see cref="Task.CompletedTask"/> or <see langword="null"/><br/>See <see href="https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23">Examples/Program_ListenUpdate.cs</see> for how to use this</remarks>
public event Func<UpdatesBase, Task> OnUpdate;
public event Func<UpdatesBase, Task> OnUpdates;
[Obsolete("This event was renamed OnUpdates (plural)")]
public event Func<UpdatesBase, Task> OnUpdate { add { OnUpdates += value; } remove { OnUpdates -= value; } }
/// <summary>This event is called for other types of notifications (login states, reactor errors, ...)</summary>
public event Func<IObject, Task> OnOther;
/// <summary>Use this handler to intercept Updates that resulted from your own API calls</summary>
public event Func<UpdatesBase, Task> OnOwnUpdate;
public event Func<UpdatesBase, Task> OnOwnUpdates;
/// <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);
@ -355,13 +356,13 @@ namespace WTelegram
if (IsMainDC)
{
var updatesState = await this.Updates_GetState(); // this call reenables incoming Updates
RaiseUpdate(updatesState);
RaiseUpdates(updatesState);
}
}
catch
{
if (IsMainDC)
RaiseUpdate(reactorError);
RaiseUpdates(reactorError);
lock (_pendingRpcs) // abort all pending requests
{
foreach (var rpc in _pendingRpcs.Values)
@ -583,11 +584,11 @@ namespace WTelegram
else
{
Helpers.Log(1, $" → {result?.GetType().Name,-37} #{(short)msgId.GetHashCode():X4}");
if (OnOwnUpdate != null)
if (OnOwnUpdates != null)
if (result is UpdatesBase updates)
RaiseOwnUpdate(updates);
RaiseOwnUpdates(updates);
else if (result is Messages_AffectedMessages affected)
RaiseOwnUpdate(new UpdateShort { update = new UpdateAffectedMessages { affected = affected }, date = MsgIdToStamp(_lastRecvMsgId) });
RaiseOwnUpdates(new UpdateShort { update = new UpdateAffectedMessages { affected = affected }, date = MsgIdToStamp(_lastRecvMsgId) });
}
rpc.tcs.SetResult(result);
@ -611,8 +612,8 @@ namespace WTelegram
else
{
result = reader.ReadTLObject(ctorNb);
if (OnOwnUpdate != null && result is UpdatesBase updates)
RaiseOwnUpdate(updates);
if (OnOwnUpdates != null && result is UpdatesBase updates)
RaiseOwnUpdates(updates);
}
var typeName = result?.GetType().Name;
@ -673,7 +674,7 @@ namespace WTelegram
break;
case Pong pong:
SetResult(pong.msg_id, pong);
RaiseUpdate(pong);
RaiseUpdates(pong);
break;
case FutureSalts futureSalts:
SetResult(futureSalts.req_msg_id, futureSalts);
@ -733,10 +734,10 @@ namespace WTelegram
rpc.tcs.SetException(new WTException($"BadMsgNotification {badMsgNotification.error_code}"));
}
else
RaiseUpdate(badMsgNotification);
RaiseUpdates(badMsgNotification);
break;
default:
RaiseUpdate(obj);
RaiseUpdates(obj);
break;
}
@ -746,32 +747,32 @@ namespace WTelegram
if (rpc != null)
rpc.tcs.SetResult(result);
else
RaiseUpdate(obj);
RaiseUpdates(obj);
}
}
private async void RaiseUpdate(IObject obj)
private async void RaiseUpdates(IObject obj)
{
try
{
var task = obj is UpdatesBase updates ? OnUpdate?.Invoke(updates) : OnOther?.Invoke(obj);
var task = obj is UpdatesBase updates ? OnUpdates?.Invoke(updates) : OnOther?.Invoke(obj);
if (task != null) await task;
}
catch (Exception ex)
{
Helpers.Log(4, $"{nameof(OnUpdate)}({obj?.GetType().Name}) raised {ex}");
Helpers.Log(4, $"{nameof(OnUpdates)}({obj?.GetType().Name}) raised {ex}");
}
}
private async void RaiseOwnUpdate(UpdatesBase updates)
private async void RaiseOwnUpdates(UpdatesBase updates)
{
try
{
await OnOwnUpdate(updates);
await OnOwnUpdates(updates);
}
catch (Exception ex)
{
Helpers.Log(4, $"{nameof(OnOwnUpdate)}({updates.GetType().Name}) raised {ex}");
Helpers.Log(4, $"{nameof(OnOwnUpdates)}({updates.GetType().Name}) raised {ex}");
}
}
@ -1050,7 +1051,7 @@ namespace WTelegram
if (self.id == long.Parse(botToken.Split(':')[0]))
{
_session.UserId = _dcSession.UserId = self.id;
RaiseUpdate(self);
RaiseUpdates(self);
return User = self;
}
Helpers.Log(3, $"Current logged user {self.id} mismatched bot_token. Logging out and in...");
@ -1089,7 +1090,7 @@ namespace WTelegram
self.phone == string.Concat((phone_number = Config("phone_number")).Where(char.IsDigit)))
{
_session.UserId = _dcSession.UserId = self.id;
RaiseUpdate(self);
RaiseUpdates(self);
return User = self;
}
var mismatch = $"Current logged user {self.id} mismatched user_id or phone_number";
@ -1124,7 +1125,7 @@ namespace WTelegram
{
phone_code_hash = setupSentCode.phone_code_hash;
Helpers.Log(3, "A login email is required");
RaiseUpdate(sentCodeBase);
RaiseUpdates(sentCodeBase);
var email = _config("email");
if (string.IsNullOrEmpty(email))
sentCodeBase = await this.Auth_ResendCode(phone_number, phone_code_hash);
@ -1135,7 +1136,7 @@ namespace WTelegram
{
var sentEmail = await this.Account_SendVerifyEmailCode(purpose, email);
Helpers.Log(3, "An email verification code has been sent to " + sentEmail.email_pattern);
RaiseUpdate(sentEmail);
RaiseUpdates(sentEmail);
}
Account_EmailVerified verified = null;
for (int retry = 1; verified == null; retry++)
@ -1166,7 +1167,7 @@ namespace WTelegram
phone_code_hash = sentCode.phone_code_hash;
var timeout = DateTime.UtcNow + TimeSpan.FromSeconds(sentCode.timeout);
Helpers.Log(3, $"A verification code has been sent via {sentCode.type.GetType().Name[17..]}");
RaiseUpdate(sentCode);
RaiseUpdates(sentCode);
if (sentCode.type is Auth_SentCodeTypeFirebaseSms firebaseSms)
{
var token = await ConfigAsync("firebase");
@ -1210,7 +1211,7 @@ namespace WTelegram
try
{
var accountPassword = await this.Account_GetPassword();
RaiseUpdate(accountPassword);
RaiseUpdates(accountPassword);
var checkPasswordSRP = await Check2FA(accountPassword, () => ConfigAsync("password"));
authorization = await this.Auth_CheckPassword(checkPasswordSRP);
}
@ -1230,7 +1231,7 @@ namespace WTelegram
if (authorization is Auth_AuthorizationSignUpRequired signUpRequired)
{
var waitUntil = DateTime.UtcNow.AddSeconds(3);
RaiseUpdate(signUpRequired); // give caller the possibility to read and accept TOS
RaiseUpdates(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;
@ -1257,7 +1258,7 @@ namespace WTelegram
throw new WTException("Failed to get Authorization: " + authorization.GetType().Name);
_session.UserId = _dcSession.UserId = self.id;
lock (_session) _session.Save();
RaiseUpdate(self);
RaiseUpdates(self);
return User = self;
}

View file

@ -21,7 +21,7 @@ namespace WTelegram
int RemoteLayer { get; }
}
[TLDef(0xFEFEFEFE)]
[TLDef(0xFEFEFEFE)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles")]
internal sealed partial class SecretChat : IObject, ISecretChat
{
[Flags] public enum Flags : uint { requestChat = 1, renewKey = 2, acceptKey = 4, originator = 8, commitKey = 16 }
@ -170,7 +170,7 @@ namespace WTelegram
return chat_id;
}
/// <summary>Processes the <see cref="UpdateEncryption"/> you received from Telegram (<see cref="Client.OnUpdate"/>).</summary>
/// <summary>Processes the <see cref="UpdateEncryption"/> you received from Telegram (<see cref="Client.OnUpdates"/>).</summary>
/// <param name="update">If update.chat is <see cref="EncryptedChatRequested"/>, you might want to first make sure you want to accept this secret chat initiated by user <see cref="EncryptedChatRequested.admin_id"/></param>
/// <param name="acceptChatRequests">Incoming requests for secret chats are automatically: accepted (<see langword="true"/>), rejected (<see langword="false"/>) or ignored (<see langword="null"/>)</param>
/// <returns><see langword="true"/> if the update was handled successfully</returns>

View file

@ -396,7 +396,7 @@ namespace TL
public static implicit operator byte[](Int256 int256) => int256.raw;
}
public sealed partial class UpdateAffectedMessages : Update // auto-generated for OnOwnUpdate in case of such API call result
public sealed partial class UpdateAffectedMessages : Update // auto-generated for OnOwnUpdates in case of such API call result
{
public Messages_AffectedMessages affected;
public override (long, int, int) GetMBox() => (0, affected.pts, affected.pts_count);