diff --git a/EXAMPLES.md b/EXAMPLES.md
index e57d7fb..59bf859 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -21,6 +21,29 @@ and avoid calling the same methods (like `Messages_GetAllChats`) repetitively.
ℹ️ WTelegramClient covers 100% of Telegram Client API, much more than the examples below: check the [full API methods list](https://corefork.telegram.org/methods)!
More examples can also be found in the [Examples folder](Examples) and in answers to [StackOverflow questions](https://stackoverflow.com/questions/tagged/wtelegramclient).
+
+### Change logging settings
+By default, WTelegramClient logs are displayed on the Console screen.
+If you are not in a Console app or don't want the logs on screen, you can redirect them as you prefer:
+
+```csharp
+// • Log to file in replacement of default Console screen logging, using this static variable:
+static StreamWriter WTelegramLogs = new StreamWriter("WTelegram.log", true, Encoding.UTF8) { AutoFlush = true };
+...
+WTelegram.Helpers.Log = (lvl, str) => WTelegramLogs.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{"TDIWE!"[lvl]}] {str}");
+
+// • Log to VS Output debugging pane in addition (+=) to the default Console screen logging:
+WTelegram.Helpers.Log += (lvl, str) => System.Diagnostics.Debug.WriteLine(str);
+
+// • In ASP.NET service, you will typically send logs to an ILogger:
+WTelegram.Helpers.Log = (lvl, str) => _logger.Log((LogLevel)lvl, str);
+
+// • Disable logging (THIS IS NOT RECOMMENDED as you won't be able to diagnose any upcoming problem):
+WTelegram.Helpers.Log = (lvl, str) => { };
+```
+
+The `lvl` argument correspond to standard [LogLevel values](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel#fields)
+
### Send a message to someone by @username
```csharp
@@ -30,15 +53,6 @@ await client.SendMessageAsync(resolved, "/start");
*Note: This also works if the @username points to a channel/group, but you must already have joined that channel before sending a message to it.
If the username is invalid/unused, the API call raises an RpcException.*
-
-### Send a message to someone by phone number
-```csharp
-var contacts = await client.Contacts_ImportContacts(new[] { new InputPhoneContact { phone = "+PHONENUMBER" } });
-if (contacts.imported.Length > 0)
- await client.SendMessageAsync(contacts.users[contacts.imported[0].user_id], "Hello!");
-```
-*Note: To prevent spam, Telegram may restrict your ability to add new phone numbers.*
-
### Convert message to/from HTML or Markdown, and send it to ourself (Saved Messages)
@@ -63,6 +77,196 @@ text2 = client.EntitiesToMarkdown(sent2.message, sent2.entities);
See [HTML formatting style](https://core.telegram.org/bots/api/#html-style) and [MarkdownV2 formatting style](https://core.telegram.org/bots/api/#markdownv2-style) for details.
*Note: For the `tg://user?id=` notation to work, that user's access hash must have been collected first ([see below](#collect-access-hash))*
+
+### List all dialogs (chats/groups/channels/user chat) we are currently in
+```csharp
+var dialogs = await client.Messages_GetAllDialogs();
+foreach (Dialog dialog in dialogs.dialogs)
+{
+ switch (dialogs.UserOrChat(dialog))
+ {
+ case User user when user.IsActive: Console.WriteLine("User " + user); break;
+ case ChatBase chat when chat.IsActive: Console.WriteLine(chat); break;
+ }
+ //var latestMsg = dialogs.messages.FirstOrDefault(m => m.Peer.ID == dialog.Peer.ID && m.ID == dialog.TopMessage);
+}
+```
+
+*Note: the lists returned by Messages_GetAllDialogs contains the `access_hash` for those chats and users.*
+See also the `Main` method in [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
+
+
+### List all chats (groups/channels NOT users) that we joined and send a message to one
+```csharp
+var chats = await client.Messages_GetAllChats();
+foreach (var (id, chat) in chats.chats)
+ if (chat.IsActive)
+ Console.WriteLine($"{id} : {chat}");
+Console.Write("Choose a chat ID to send a message to: ");
+long chatId = long.Parse(Console.ReadLine());
+await client.SendMessageAsync(chats.chats[chatId], "Hello, World");
+```
+Notes:
+- This list does not include discussions with other users. For this, you need to use [Messages_GetAllDialogs](#list-dialogs).
+- The list returned by Messages_GetAllChats contains the `access_hash` for those chats. Read [FAQ #4](FAQ.md#access-hash) about this.
+- If a basic chat group has been migrated to a supergroup, you may find both the old `Chat` and a `Channel` with different IDs in the `chats.chats` result,
+but the old `Chat` will be marked with flag [deactivated] and should not be used anymore. See [Terminology in ReadMe](README.md#terminology).
+- You can find a longer version of this method call in [Examples/Program_GetAllChats.cs](Examples/Program_GetAllChats.cs)
+
+
+### List the members from a chat
+For a basic Chat: *(see Terminology in [ReadMe](README.md#terminology))*
+```csharp
+var chatFull = await client.Messages_GetFullChat(1234567890); // the chat we want
+foreach (var (id, user) in chatFull.users)
+ Console.WriteLine(user);
+```
+
+For a Channel/Group:
+```csharp
+var chats = await client.Messages_GetAllChats();
+var channel = (Channel)chats.chats[1234567890]; // the channel we want
+for (int offset = 0; ;)
+{
+ var participants = await client.Channels_GetParticipants(channel, null, offset);
+ foreach (var (id, user) in participants.users)
+ Console.WriteLine(user);
+ offset += participants.participants.Length;
+ if (offset >= participants.count || participants.participants.Length == 0) break;
+}
+```
+
+For big Channel/Group, Telegram servers might limit the number of members you can obtain with the normal above method.
+In this case, you can use the following helper method, but it can take several minutes to complete:
+```csharp
+var chats = await client.Messages_GetAllChats();
+var channel = (Channel)chats.chats[1234567890]; // the channel we want
+var participants = await client.Channels_GetAllParticipants(channel);
+```
+
+You can use specific filters, for example to list only the channel owner/admins:
+```csharp
+var participants = await client.Channels_GetParticipants(channel, filter: new ChannelParticipantsAdmins());
+foreach (var participant in participants.participants) // This is the correct way to enumerate the result
+{
+ var user = participants.users[participant.UserID];
+ if (participant is ChannelParticipantCreator cpc) Console.WriteLine($"{user} is the owner '{cpc.rank}'");
+ else if (participant is ChannelParticipantAdmin cpa) Console.WriteLine($"{user} is admin '{cpa.rank}'");
+}
+```
+*Note: It is not possible to list only the Deleted Accounts. Those will be automatically removed by Telegram from your group after a while*
+
+
+### Fetch all messages (history) from a chat
+```csharp
+var chats = await client.Messages_GetAllChats();
+InputPeer peer = chats.chats[1234567890]; // the chat we want
+for (int offset_id = 0; ;)
+{
+ var messages = await client.Messages_GetHistory(peer, offset_id);
+ if (messages.Messages.Length == 0) break;
+ foreach (var msgBase in messages.Messages)
+ {
+ var from = messages.UserOrChat(msgBase.From ?? msgBase.Peer); // from can be User/Chat/Channel
+ if (msgBase is Message msg)
+ Console.WriteLine($"{from}> {msg.message} {msg.media}");
+ else if (msgBase is MessageService ms)
+ Console.WriteLine($"{from} [{ms.action.GetType().Name[13..]}]");
+ }
+ offset_id = messages.Messages[^1].ID;
+}
+```
+Notes:
+- To stop at a specific msg ID, use Messages_GetHistory `min_id` argument. For example, `min_id: dialog.read_inbox_max_id`
+- To mark the message history as read, use: `await client.ReadHistory(peer);`
+
+
+### Monitor all Telegram events happening for the user
+
+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).
+
+
+### Monitor new messages being posted in chats in real-time
+
+You have to handle `client.OnUpdate` events containing an `UpdateNewMessage`.
+
+See the `DisplayMessage` method in [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
+
+You can filter specific chats the message are posted in, by looking at the `Message.peer_id` field.
+
+
+### Downloading photos, medias, files
+
+This is done using the helper method `client.DownloadFileAsync(file, outputStream)`
+that simplifies the download of a photo/document/file once you get a reference to its location *(through updates or API calls)*.
+
+See [Examples/Program_DownloadSavedMedia.cs](Examples/Program_DownloadSavedMedia.cs) that download all media files you forward to yourself (Saved Messages)
+
+
+### Upload a media file and post it with caption to a chat
+```csharp
+const int ChatId = 1234567890; // the chat we want
+const string Filepath = @"C:\...\photo.jpg";
+
+var chats = await client.Messages_GetAllChats();
+InputPeer peer = chats.chats[ChatId];
+var inputFile = await client.UploadFileAsync(Filepath);
+await client.SendMediaAsync(peer, "Here is the photo", inputFile);
+```
+
+
+### Send a grouped media album using photos from various sources
+```csharp
+// Photo 1 already on Telegram: latest photo found in the user's Saved Messages
+var history = await client.Messages_GetHistory(InputPeer.Self);
+PhotoBase photoFromTelegram = history.Messages.OfType().Select(m => m.media).OfType().First().photo;
+// Photo 2 uploaded now from our computer:
+var uploadedFile = await client.UploadFileAsync(@"C:\Pictures\flower.jpg");
+// Photo 3 specified by external url:
+const string photoUrl = "https://picsum.photos/310/200.jpg";
+
+var inputMedias = new List
+{
+ photoFromTelegram, // PhotoBase has implicit conversion to InputMediaPhoto
+ new InputMediaUploadedPhoto { file = uploadedFile },
+ new InputMediaPhotoExternal() { url = photoUrl },
+};
+await client.SendAlbumAsync(InputPeer.Self, inputMedias, "My first album");
+```
+*Note: Don't mix Photos and file Documents in your album, it doesn't work well*
+
+
+### Forward or copy a message to another chat
+```csharp
+// Determine which chats and message to forward/copy
+var chats = await client.Messages_GetAllChats();
+var from_chat = chats.chats[1234567890]; // source chat
+var to_chat = chats.chats[1234567891]; // destination chat
+var history = await client.Messages_GetHistory(from_chat, limit: 1);
+var msg = history.Messages[0] as Message; // last message of source chat
+
+// • Forward the message (only the source message id is necessary)
+await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat);
+
+// • Copy the message without the "Forwarded" header (only the source message id is necessary)
+await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat, drop_author: true);
+
+// • Alternative solution to copy the message (the full message is needed)
+await client.SendMessageAsync(to_chat, msg.message, msg.media?.ToInputMedia(), entities: msg.entities);
+```
+
+
+### Schedule a message to be sent to a chat
+```csharp
+var chats = await client.Messages_GetAllChats();
+InputPeer peer = chats.chats[1234567890]; // the chat we want
+DateTime when = DateTime.UtcNow.AddMinutes(3);
+await client.SendMessageAsync(peer, "This will be posted in 3 minutes", schedule_date: when);
+```
+
### Fun with stickers, GIFs, dice, and animated emojies
```csharp
@@ -112,123 +316,41 @@ var typing = await client.Messages_SetTyping(InputPeer.Self, new SendMessageEmoj
await Task.Delay(5000);
```
-
-### List all chats (groups/channels NOT users) that we joined and send a message to one
+
+### Fun with custom emojies and reactions on pinned messages
```csharp
-var chats = await client.Messages_GetAllChats();
-foreach (var (id, chat) in chats.chats)
- if (chat.IsActive)
- Console.WriteLine($"{id} : {chat}");
-Console.Write("Choose a chat ID to send a message to: ");
-long chatId = long.Parse(Console.ReadLine());
-await client.SendMessageAsync(chats.chats[chatId], "Hello, World");
-```
-Notes:
-- This list does not include discussions with other users. For this, you need to use [Messages_GetAllDialogs](#list-dialogs).
-- The list returned by Messages_GetAllChats contains the `access_hash` for those chats. Read [FAQ #4](FAQ.md#access-hash) about this.
-- If a basic chat group has been migrated to a supergroup, you may find both the old `Chat` and a `Channel` with different IDs in the `chats.chats` result,
-but the old `Chat` will be marked with flag [deactivated] and should not be used anymore. See [Terminology in ReadMe](README.md#terminology).
-- You can find a longer version of this method call in [Examples/Program_GetAllChats.cs](Examples/Program_GetAllChats.cs)
+// • Sending a message with custom emojies in Markdown to ourself:
+var text = "Vicksy says Hi! [👋](emoji?id=5190875290439525089)";
+var entities = client.MarkdownToEntities(ref text, premium: true);
+await client.SendMessageAsync(InputPeer.Self, text, entities: entities);
+// also available in HTML: 👋
-
-### List all dialogs (chats/groups/channels/user chat) we are currently in
-```csharp
-var dialogs = await client.Messages_GetAllDialogs();
-foreach (Dialog dialog in dialogs.dialogs)
- switch (dialogs.UserOrChat(dialog))
- {
- case User user when user.IsActive: Console.WriteLine("User " + user); break;
- case ChatBase chat when chat.IsActive: Console.WriteLine(chat); break;
- }
-```
-
-*Note: the lists returned by Messages_GetAllDialogs contains the `access_hash` for those chats and users.*
-See also the `Main` method in [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
-
-
-### Schedule a message to be sent to a chat
-```csharp
-var chats = await client.Messages_GetAllChats();
-InputPeer peer = chats.chats[1234567890]; // the chat we want
-DateTime when = DateTime.UtcNow.AddMinutes(3);
-await client.SendMessageAsync(peer, "This will be posted in 3 minutes", schedule_date: when);
-```
-
-
-### Upload a media file and post it with caption to a chat
-```csharp
-const int ChatId = 1234567890; // the chat we want
-const string Filepath = @"C:\...\photo.jpg";
+// • Fetch all available standard emoji reactions
+var all_emoji = await client.Messages_GetAvailableReactions();
var chats = await client.Messages_GetAllChats();
-InputPeer peer = chats.chats[ChatId];
-var inputFile = await client.UploadFileAsync(Filepath);
-await client.SendMediaAsync(peer, "Here is the photo", inputFile);
-```
+var chat = chats.chats[1234567890]; // the chat we want
-
-### Send a grouped media album using photos from various sources
-```csharp
-// Photo 1 already on Telegram: latest photo found in the user's Saved Messages
-var history = await client.Messages_GetHistory(InputPeer.Self);
-PhotoBase photoFromTelegram = history.Messages.OfType().Select(m => m.media).OfType().First().photo;
-// Photo 2 uploaded now from our computer:
-var uploadedFile = await client.UploadFileAsync(@"C:\Pictures\flower.jpg");
-// Photo 3 specified by external url:
-const string photoUrl = "https://picsum.photos/310/200.jpg";
-
-var inputMedias = new List
+// • Check reactions available in this chat
+var full = await client.GetFullChat(chat);
+Reaction reaction = full.full_chat.AvailableReactions switch
{
- photoFromTelegram, // PhotoBase has implicit conversion to InputMediaPhoto
- new InputMediaUploadedPhoto { file = uploadedFile },
- new InputMediaPhotoExternal() { url = photoUrl },
+ ChatReactionsSome some => some.reactions[0], // only some reactions are allowed => pick the first
+ ChatReactionsAll all => // all reactions are allowed in this chat
+ all.flags.HasFlag(ChatReactionsAll.Flags.allow_custom) && client.User.flags.HasFlag(TL.User.Flags.premium)
+ ? new ReactionCustomEmoji { document_id = 5190875290439525089 } // we can use custom emoji reactions here
+ : new ReactionEmoji { emoticon = all_emoji.reactions[0].reaction }, // else, pick the first standard emoji reaction
+ _ => null // reactions are not allowed in this chat
};
-await client.SendAlbumAsync(InputPeer.Self, inputMedias, "My first album");
-```
-*Note: Don't mix Photos and file Documents in your album, it doesn't work well*
+if (reaction == null) return;
-
-### Get all members from a chat
-For a basic Chat: *(see Terminology in [ReadMe](README.md#terminology))*
-```csharp
-var chatFull = await client.Messages_GetFullChat(1234567890); // the chat we want
-foreach (var (id, user) in chatFull.users)
- Console.WriteLine(user);
+// • Send the selected reaction on the last 2 pinned messages
+var messages = await client.Messages_Search(chat, limit: 2);
+foreach (var msg in messages.Messages)
+ await client.Messages_SendReaction(chat, msg.ID, reaction: new[] { reaction });
```
+*Note: you can find custom emoji document IDs via API methods like [Messages_GetFeaturedEmojiStickers](https://corefork.telegram.org/method/messages.getFeaturedEmojiStickers) or inspecting incoming messages. Access hash is not required*
-For a Channel/Group:
-```csharp
-var chats = await client.Messages_GetAllChats();
-var channel = (Channel)chats.chats[1234567890]; // the channel we want
-for (int offset = 0; ;)
-{
- var participants = await client.Channels_GetParticipants(channel, null, offset);
- foreach (var (id, user) in participants.users)
- Console.WriteLine(user);
- offset += participants.participants.Length;
- if (offset >= participants.count || participants.participants.Length == 0) break;
-}
-```
-
-For big Channel/Group, Telegram servers might limit the number of members you can obtain with the normal above method.
-In this case, you can use the following helper method, but it can take several minutes to complete:
-```csharp
-var chats = await client.Messages_GetAllChats();
-var channel = (Channel)chats.chats[1234567890]; // the channel we want
-var participants = await client.Channels_GetAllParticipants(channel);
-```
-
-If you only need to list the channel owner/admins, you can use specific filter:
-```csharp
-var participants = await client.Channels_GetParticipants(channel, filter: new ChannelParticipantsAdmins());
-foreach (var participant in participants.participants) // This is the correct way to enumerate the result
-{
- var user = participants.users[participant.UserID];
- if (participant is ChannelParticipantCreator cpc) Console.WriteLine($"{user} is the owner '{cpc.rank}'");
- else if (participant is ChannelParticipantAdmin cpa) Console.WriteLine($"{user} is admin '{cpa.rank}'");
-}
-```
-*Note: It is not possible to list only the Deleted Accounts. Those will be automatically removed by Telegram from your group after a while*
### Join a channel/group by their public name or invite link
@@ -278,46 +400,14 @@ await client.Messages_DeleteExportedChatInvite(chat, invite.link);
await client.DeleteChatUser(chat, user);
```
-
-### Get all messages (history) from a chat
+
+### Send a message to someone by phone number
```csharp
-var chats = await client.Messages_GetAllChats();
-InputPeer peer = chats.chats[1234567890]; // the chat we want
-for (int offset_id = 0; ;)
-{
- var messages = await client.Messages_GetHistory(peer, offset_id);
- if (messages.Messages.Length == 0) break;
- foreach (var msgBase in messages.Messages)
- {
- var from = messages.UserOrChat(msgBase.From ?? msgBase.Peer); // from can be User/Chat/Channel
- if (msgBase is Message msg)
- Console.WriteLine($"{from}> {msg.message} {msg.media}");
- else if (msgBase is MessageService ms)
- Console.WriteLine($"{from} [{ms.action.GetType().Name[13..]}]");
- }
- offset_id = messages.Messages[^1].ID;
-}
+var contacts = await client.Contacts_ImportContacts(new[] { new InputPhoneContact { phone = "+PHONENUMBER" } });
+if (contacts.imported.Length > 0)
+ await client.SendMessageAsync(contacts.users[contacts.imported[0].user_id], "Hello!");
```
-Notes:
-- To stop at a specific msg ID, use Messages_GetHistory `min_id` argument. For example, `min_id: dialog.read_inbox_max_id`
-- To mark the message history as read, use: `await client.ReadHistory(peer);`
-
-
-### Monitor all Telegram events happening for the user
-
-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).
-
-
-### Monitor new messages being posted in chats in real-time
-
-You have to handle `client.OnUpdate` events containing an `UpdateNewMessage`.
-
-See the `DisplayMessage` method in [Examples/Program_ListenUpdates.cs](Examples/Program_ListenUpdates.cs).
-
-You can filter specific chats the message are posted in, by looking at the `Message.peer_id` field.
+*Note: Don't use this method too much. To prevent spam, Telegram may restrict your ability to add new phone numbers.*
### Retrieve the current user's contacts list
@@ -348,14 +438,6 @@ finally
}
```
-
-### Downloading photos, medias, files
-
-This is done using the helper method `client.DownloadFileAsync(file, outputStream)`
-that simplify the download of a photo/document/file once you get a reference to its location *(through updates or API calls)*.
-
-See [Examples/Program_DownloadSavedMedia.cs](Examples/Program_DownloadSavedMedia.cs) that download all media files you forward to yourself (Saved Messages)
-
### Collect Access Hash and save them for later use
@@ -397,29 +479,6 @@ If your Telegram client is already connected to such MTPROTO proxy, you can also
*Note: WTelegramClient always uses transport obfuscation when connecting to Telegram servers, even without MTProxy*
-
-### Change logging settings
-By default, WTelegramClient logs are displayed on the Console screen.
-If you are not in a Console app or don't want the logs on screen, you can redirect them as you prefer:
-
-```csharp
-// • Log to file in replacement of default Console screen logging, using this static variable:
-static StreamWriter WTelegramLogs = new StreamWriter("WTelegram.log", true, Encoding.UTF8) { AutoFlush = true };
-...
-WTelegram.Helpers.Log = (lvl, str) => WTelegramLogs.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{"TDIWE!"[lvl]}] {str}");
-
-// • Log to VS Output debugging pane in addition (+=) to the default Console screen logging:
-WTelegram.Helpers.Log += (lvl, str) => System.Diagnostics.Debug.WriteLine(str);
-
-// • In ASP.NET service, you will typically send logs to an ILogger:
-WTelegram.Helpers.Log = (lvl, str) => _logger.Log((LogLevel)lvl, str);
-
-// • Disable logging (THIS IS NOT RECOMMENDED as you won't be able to diagnose any upcoming problem):
-WTelegram.Helpers.Log = (lvl, str) => { };
-```
-
-The `lvl` argument correspond to standard [LogLevel values](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel#fields)
-
### Change 2FA password
```csharp
@@ -449,61 +508,6 @@ Use it to pass a custom Stream-derived class that will **read** (first initial c
You can find an example for such custom session store in [Examples/Program_Heroku.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_Heroku.cs#L61)
-
-### Fun with custom emojies and reactions on pinned messages
-```csharp
-// • Sending a message with custom emojies in Markdown to ourself:
-var text = "Vicksy says Hi! [👋](emoji?id=5190875290439525089)";
-var entities = client.MarkdownToEntities(ref text, premium: true);
-await client.SendMessageAsync(InputPeer.Self, text, entities: entities);
-// also available in HTML: 👋
-
-// • Fetch all available standard emoji reactions
-var all_emoji = await client.Messages_GetAvailableReactions();
-
-var chats = await client.Messages_GetAllChats();
-var chat = chats.chats[1234567890]; // the chat we want
-
-// • Check reactions available in this chat
-var full = await client.GetFullChat(chat);
-Reaction reaction = full.full_chat.AvailableReactions switch
-{
- ChatReactionsSome some => some.reactions[0], // only some reactions are allowed => pick the first
- ChatReactionsAll all => // all reactions are allowed in this chat
- all.flags.HasFlag(ChatReactionsAll.Flags.allow_custom) && client.User.flags.HasFlag(TL.User.Flags.premium)
- ? new ReactionCustomEmoji { document_id = 5190875290439525089 } // we can use custom emoji reactions here
- : new ReactionEmoji { emoticon = all_emoji.reactions[0].reaction }, // else, pick the first standard emoji reaction
- _ => null // reactions are not allowed in this chat
-};
-if (reaction == null) return;
-
-// • Send the selected reaction on the last 2 pinned messages
-var messages = await client.Messages_Search(chat, limit: 2);
-foreach (var msg in messages.Messages)
- await client.Messages_SendReaction(chat, msg.ID, reaction: new[] { reaction });
-```
-*Note: you can find custom emoji document IDs via API methods like [Messages_GetFeaturedEmojiStickers](https://corefork.telegram.org/method/messages.getFeaturedEmojiStickers) or inspecting incoming messages. Access hash is not required*
-
-
-### Forward or copy a message to another chat
-```csharp
-// Determine which chats and message to forward/copy
-var chats = await client.Messages_GetAllChats();
-var from_chat = chats.chats[1234567890]; // source chat
-var to_chat = chats.chats[1234567891]; // destination chat
-var history = await client.Messages_GetHistory(from_chat, limit: 1);
-var msg = history.Messages[0] as Message; // last message of source chat
-
-// • Forward the message (only the source message id is necessary)
-await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat);
-
-// • Copy the message without the "Forwarded" header (only the source message id is necessary)
-await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat, drop_author: true);
-
-// • Alternative solution to copy the message (the full message is needed)
-await client.SendMessageAsync(to_chat, msg.message, msg.media?.ToInputMedia(), entities: msg.entities);
-```
-
### Send/receive end-to-end encrypted messages & files in Secret Chats
diff --git a/README.md b/README.md
index e8a0773..439e09d 100644
--- a/README.md
+++ b/README.md
@@ -163,7 +163,7 @@ or a [broadcast channel](https://corefork.telegram.org/api/channel#channels) (th
- `Chat` : A private [basic chat group](https://corefork.telegram.org/api/channel#basic-groups) with less than 200 members
(it may be migrated to a supergroup `Channel` with a new ID when it gets bigger or public, in which case the old `Chat` will still exist but will be `deactivated`)
**⚠️ Most chat groups you see are really of type `Channel`, not `Chat`!**
-- chats : In plural or general meaning, it means either `Chat` or `Channel`
+- chats : In plural or general meaning, it means either `Chat` or `Channel` *(therefore, no private user discussions)*
- `Peer` : Either a `Chat`, a `Channel` or a `User`
- Dialog : Status of chat with a `Peer` *(draft, last message, unread count, pinned...)*. It represents each line from your Telegram chat list.
- Access Hash : Telegram requires you to provide a specific `access_hash` for users, channels, and other resources before interacting with them.
diff --git a/src/Client.cs b/src/Client.cs
index de73394..7386ae7 100644
--- a/src/Client.cs
+++ b/src/Client.cs
@@ -322,27 +322,24 @@ namespace WTelegram
if (!IsMainDC && _pendingRpcs.Count <= 1 && ex is ApplicationException { Message: ConnectionShutDown } or IOException { InnerException: SocketException })
if (_pendingRpcs.Values.FirstOrDefault() is not Rpc rpc || rpc.type == typeof(Pong))
_reactorReconnects = 0;
- if (_reactorReconnects != 0)
- {
- await Task.Delay(5000);
- if (_networkStream == null) return; // Dispose has been called in-between
- await ConnectAsync(); // start a new reactor after 5 secs
- lock (_pendingRpcs) // retry all pending requests
- {
- foreach (var rpc in _pendingRpcs.Values)
- rpc.tcs.SetResult(reactorError); // this leads to a retry (see Invoke method)
- _pendingRpcs.Clear();
- _bareRpc = null;
- }
- // TODO: implement an Updates gaps handling system? https://core.telegram.org/api/updates
- if (IsMainDC)
- {
- var updatesState = await this.Updates_GetState(); // this call reenables incoming Updates
- RaiseUpdate(updatesState);
- }
- }
- else
+ if (_reactorReconnects == 0)
throw;
+ await Task.Delay(5000);
+ if (_networkStream == null) return; // Dispose has been called in-between
+ await ConnectAsync(); // start a new reactor after 5 secs
+ lock (_pendingRpcs) // retry all pending requests
+ {
+ foreach (var rpc in _pendingRpcs.Values)
+ rpc.tcs.SetResult(reactorError); // this leads to a retry (see Invoke method)
+ _pendingRpcs.Clear();
+ _bareRpc = null;
+ }
+ // TODO: implement an Updates gaps handling system? https://core.telegram.org/api/updates
+ if (IsMainDC)
+ {
+ var updatesState = await this.Updates_GetState(); // this call reenables incoming Updates
+ RaiseUpdate(updatesState);
+ }
}
catch
{
diff --git a/src/TL.Schema.cs b/src/TL.Schema.cs
index df0ec23..a827e48 100644
--- a/src/TL.Schema.cs
+++ b/src/TL.Schema.cs
@@ -2268,24 +2268,24 @@ namespace TL
/// Location of a certain size of a picture See Derived classes: , , , , ,
public abstract partial class PhotoSizeBase : IObject
{
- /// Thumbnail type (see. )
+ /// Thumbnail type »
public virtual string Type { get; }
}
/// Empty constructor. Image with this thumbnail is unavailable. See
[TLDef(0x0E17E23C)]
public partial class PhotoSizeEmpty : PhotoSizeBase
{
- /// Thumbnail type (see. )
+ /// Thumbnail type »
public string type;
- /// Thumbnail type (see. )
+ /// Thumbnail type »
public override string Type => type;
}
/// Image description. See
[TLDef(0x75C78E60)]
public partial class PhotoSize : PhotoSizeBase
{
- /// Thumbnail type
+ /// Thumbnail type »
public string type;
/// Image width
public int w;
@@ -2294,7 +2294,7 @@ namespace TL
/// File size
public int size;
- /// Thumbnail type
+ /// Thumbnail type »
public override string Type => type;
}
/// Description of an image and its content. See
@@ -2329,7 +2329,7 @@ namespace TL
[TLDef(0xFA3EFB95)]
public partial class PhotoSizeProgressive : PhotoSizeBase
{
- /// Photosize type
+ /// Photosize type »
public string type;
/// Photo width
public int w;
@@ -2338,7 +2338,7 @@ namespace TL
/// Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image.
public int[] sizes;
- /// Photosize type
+ /// Photosize type »
public override string Type => type;
}
/// Messages with animated stickers can have a compressed svg (< 300 bytes) to show the outline of the sticker before fetching the actual lottie animation. See
@@ -13151,9 +13151,9 @@ namespace TL
public long bot_id;
/// Attachment menu item name
public string short_name;
- /// List of peer types where this attachment should be shown
+ /// List of dialog types where this attachment menu entry should be shown
public AttachMenuPeerType[] peer_types;
- /// Attachment menu icon
+ /// List of platform-specific static icons and animations to use for the attachment menu button
public AttachMenuBotIcon[] icons;
[Flags] public enum Flags : uint
@@ -13292,9 +13292,9 @@ namespace TL
{
///The bot attachment menu entry is available in the chat with the bot that offers it
SameBotPM = 0x7D6BE90E,
- ///The bot attachment menu entry is available in private chats with other bots
+ ///The bot attachment menu entry is available in private chats with other bots (excluding the bot that offers the current attachment menu)
BotPM = 0xC32BFA1A,
- ///The bot attachment menu entry is available in private chats with other users
+ ///The bot attachment menu entry is available in private chats with other users (not bots)
PM = 0xF146D31F,
///The bot attachment menu entry is available in groups and supergroups
Chat = 0x0509113F,
diff --git a/src/TL.SchemaFuncs.cs b/src/TL.SchemaFuncs.cs
index c0b70d1..43f7f50 100644
--- a/src/TL.SchemaFuncs.cs
+++ b/src/TL.SchemaFuncs.cs
@@ -3214,7 +3214,7 @@ namespace TL
/// Bot that owns the web app
/// Web app URL
/// If the web app was opened from the attachment menu using a attachment menu deep link, start_param should contain the data from the startattach parameter.
- /// Theme parameters for the web app
+ /// Theme parameters »
/// Short name of the application; 0-64 English letters, digits, and underscores
/// Whether the inline message that will be sent by the bot on behalf of the user once the web app interaction is terminated should be sent in reply to this message ID.
/// Open the web app as the specified peer, sending the resulting the message as the specified peer.
@@ -3253,7 +3253,7 @@ namespace TL
/// Open a bot web app. See
/// Bot that owns the webapp
/// Web app URL
- /// Theme parameters
+ /// Theme parameters »
/// Short name of the application; 0-64 English letters, digits, and underscores
public static Task Messages_RequestSimpleWebView(this Client client, InputUserBase bot, string url, string platform, DataJSON theme_params = null)
=> client.Invoke(new Messages_RequestSimpleWebView
diff --git a/src/TL.Secret.cs b/src/TL.Secret.cs
index 8c810c8..1cf43a6 100644
--- a/src/TL.Secret.cs
+++ b/src/TL.Secret.cs
@@ -672,7 +672,7 @@ namespace TL
[TLDef(0x77BFB61B)]
public partial class PhotoSize : PhotoSizeBase
{
- /// Thumbnail type
+ /// Thumbnail type »
public string type;
public FileLocationBase location;
/// Image width
@@ -682,7 +682,7 @@ namespace TL
/// File size
public int size;
- /// Thumbnail type
+ /// Thumbnail type »
public override string Type => type;
}
/// Description of an image and its content. See