diff --git a/EXAMPLES.md b/EXAMPLES.md index 933e51e..c871113 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -12,7 +12,7 @@ await client.LoginUserIfNeeded(); In this case, environment variables are used for configuration so make sure to go to your **Project Properties > Debug > Environment variables** -and add at least these variables with adequate value: **api_id, api_hash, phone_number** +and add at least these variables with adequate values: **api_id, api_hash, phone_number** Remember that these are just simple example codes that you should adjust to your needs. In real production code, you might want to properly test the success of each operation or handle exceptions, @@ -453,25 +453,30 @@ These two dictionaries give details about the various users/chats that will be t typically in the form of a `Peer` object or a `user_id` field. In such case, the root structure inherits the `IPeerResolver` interface, and you can use the `UserOrChat(peer)` method to resolve a `Peer` -into either a `User` or `ChatBase` (`Chat`,`Channel`...) description structure *(depending what kind of peer it was describing)* +into either a `User` or `ChatBase` (`Chat`,`Channel`...) description structure *(depending on the kind of peer it was describing)* You can also use the `CollectUsersChats` helper method to collect these 2 fields into 2 aggregate dictionaries to remember details *(including access hashes)* about all the users/chats you've encountered so far. -Example of usage for `CollectUsersChats`: +Example of usage: ```csharp -static Dictionary _users = new(); -static Dictionary _chats = new(); +private Dictionary _users = new(); +private Dictionary _chats = new(); ... var dialogs = await client.Messages_GetAllDialogs(); dialogs.CollectUsersChats(_users, _chats); -... -private static async Task OnUpdate(IObject arg) + +private async Task OnUpdate(IObject arg) { if (arg is not UpdatesBase updates) return; updates.CollectUsersChats(_users, _chats); ... } + +// example of UserOrChat usage: +var firstPeer = dialogs.UserOrChat(dialogs.dialogs[0].Peer); +if (firstPeer is User firstUser) Console.WriteLine($"First dialog is with user {firstUser}"); +else if (firstPeer is ChatBase firstChat) Console.WriteLine($"First dialog is {firstChat}"); ``` *Note: If you need to save/restore those dictionaries between runs of your program, it's up to you to serialize their content to disk* diff --git a/Examples/WinForms_app.zip b/Examples/WinForms_app.zip index b92e832..e04d6b7 100644 Binary files a/Examples/WinForms_app.zip and b/Examples/WinForms_app.zip differ diff --git a/FAQ.md b/FAQ.md index 1c1d382..42b6f8e 100644 --- a/FAQ.md +++ b/FAQ.md @@ -50,28 +50,32 @@ calling `client.Login(...)` as the user provides the requested configuration ele You can download such full example apps [for WinForms](Examples/WinForms_app.zip) and [for ASP.NET](Examples/ASPnet_webapp.zip) -## 4. How to use IDs? Where to get the access_hash? Why the error `CHANNEL_INVALID` or `USER_ID_INVALID`? +## 4. How to use IDs and access_hash? Why the error `CHANNEL_INVALID` or `USER_ID_INVALID`? -Having only the ID is **not enough**: An `access_hash` is required by Telegram when dealing with a channel, user, photo, document, etc... -This serves as a proof that the logged-in user is entitled to access it (otherwise, anybody with the ID could access it) +⚠️ In Telegram Client API *(contrary to Bot API)*, you **cannot** interact with channels/users/etc. with only their IDs. + +You also need to obtain their `access_hash` which is specific to the resource you want to access AND to the currently logged-in user. +This serves as a proof that the logged-in user is entitled to access that channel/user/photo/document/... +(otherwise, anybody with the ID could access it) > A small private `Chat` don't need an access_hash and can be queried using their `chat_id` only. However most common chat groups are not `Chat` but a `Channel` supergroup (without the `broadcast` flag). See [Terminology in ReadMe](README.md#terminology). Some TL methods only applies to private `Chat`, some only applies to `Channel` and some to both. The `access_hash` must usually be provided within the `Input...` structure you pass in argument to an API method (`InputPeer`, `InputChannel`, `InputUser`, etc...). -You obtain the `access_hash` through **description structures** like `Channel`, `User`, `Photo`, `Document` that you receive through updates or when you query them through API methods like `Messages_GetAllChats`, `Messages_GetAllDialogs`, `Contacts_ResolveUsername`, etc... -*(if you have a `Peer` object, you can convert it to a `User`/`Channel`/`Chat` via the `UserOrChat` helper from the root class that contained the peer)* -Once you obtained the description structure, there are 3 methods for building your `Input...` request structure: -* **Recommended:** If you take a look at the **description structure** base class `ChatBase/UserBase`, -you will see that they have conversion implicit operators or methods that can create the `Input...` structure for you automatically. -So you can just pass that structure you already have, in place of the `Input...` argument, it will work! -* Alternatively, you can manually create the `Input...` structure yourself by extracting the `access_hash` from the **description structure** -* If you have enabled the [CollectAccessHash system](EXAMPLES.md#collect-access-hash) at the start of your session, it will have collected the `access_hash` automatically when you obtained the description structure. -You can then retrieve it with `client.GetAccessHashFor(id)` +You obtain the `access_hash` through TL **description structures** like `Channel`, `User`, `Photo`, `Document` that you receive through updates +or when you query them through API methods like `Messages_GetAllChats`, `Messages_GetAllDialogs`, `Contacts_ResolveUsername`, etc... -⚠️ *An `access_hash` obtained from a User/Channel structure with flag `min` may not be usable for most requests. See [Min constructors](https://core.telegram.org/api/min).* +You can use the [`UserOrChat` and `CollectUsersChats` methods](EXAMPLES.md#collect-users-chats) to help you in obtaining/collecting +the description structures you receive via API calls or updates. + +Once you obtained the description structure, there are 2 methods for building your `Input...` request structure: +* **Recommended:** Just pass that description structure you already have, in place of the `Input...` argument, it will work! +*The implicit conversion operators on base classes like `ChatBase/UserBase` will create the `Input...` structure for you automatically.* +* Alternatively, you can manually create the `Input...` structure yourself by extracting the `access_hash` from the description structure + +*Note: An `access_hash` obtained from a User/Channel structure with flag `min` may not be usable for most requests. See [Min constructors](https://core.telegram.org/api/min).* ## 5. I need to test a feature that has been recently developed but seems not available in my program diff --git a/README.md b/README.md index e40d404..4a2b470 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Its `int` argument is the log severity, compatible with the [LogLevel enum](http Since version 3.0.0, a new approach to login/configuration has been added. Some people might find it easier to deal with: ```csharp -WTelegram.Client client = new WTelegram.Client(YOUR_API_ID, "YOUR_API_HASH"); +WTelegram.Client client = new WTelegram.Client(YOUR_API_ID, "YOUR_API_HASH"); // this constructor doesn't need a Config method await DoLogin("+12025550156"); // initial call with user's phone_number async Task DoLogin(string loginInfo) // (add this method to your code)