updated docs (and reordered Examples.md)

This commit is contained in:
Wizou 2022-11-01 18:44:01 +01:00
parent d64c5c0c1e
commit b902b33558
6 changed files with 277 additions and 276 deletions

View file

@ -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).
<a name="logging"></a>
### 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)
<a name="msg-by-name"></a>
### 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.*
<a name="msg-by-phone"></a>
### 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.*
<a name="markdown"></a>
<a name="html"></a>
### 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))*
<a name="list-dialogs"></a>
### 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).
<a name="list-chats"></a>
### 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)
<a name="list-members"></a>
### 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*
<a name="history"></a>
### 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);`
<a name="updates"></a>
### 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).
<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`.
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.
<a name="download"></a>
### 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)
<a name="upload"></a>
### 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);
```
<a name="album"></a>
### 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<Message>().Select(m => m.media).OfType<MessageMediaPhoto>().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<InputMedia>
{
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*
<a name="forward"></a><a name="copy"></a>
### 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);
```
<a name="schedule-msg"></a>
### 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);
```
<a name="fun"></a>
### 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);
```
<a name="list-chats"></a>
### List all chats (groups/channels NOT users) that we joined and send a message to one
<a name="reaction"></a><a name="pinned"></a><a name="custom_emoji"></a>
### 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: <tg-emoji id="5190875290439525089">👋</tg-emoji>
<a name="list-dialogs"></a>
### 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))
// • 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
{
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).
<a name="schedule-msg"></a>
### 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);
```
<a name="upload"></a>
### 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);
```
<a name="album"></a>
### 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<Message>().Select(m => m.media).OfType<MessageMediaPhoto>().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<InputMedia>
{
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;
<a name="list-members"></a>
### 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<InputMessagesFilterPinned>(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*
<a name="join-channel"></a>
### 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);
```
<a name="history"></a>
### Get all messages (history) from a chat
<a name="msg-by-phone"></a>
### 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);`
<a name="updates"></a>
### 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).
<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`.
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.*
<a name="contacts"></a>
### Retrieve the current user's contacts list
@ -348,14 +438,6 @@ finally
}
```
<a name="download"></a>
### 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)
<a name="collect-access-hash"></a>
### 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*
<a name="logging"></a>
### 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)
<a name="2FA"></a>
### 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)
<a name="reaction"></a><a name="pinned"></a><a name="custom_emoji"></a>
### 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: <tg-emoji id="5190875290439525089">👋</tg-emoji>
// • 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<InputMessagesFilterPinned>(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*
<a name="forward"></a><a name="copy"></a>
### 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);
```
<a name="e2e"></a><a name="secrets"></a>
### Send/receive end-to-end encrypted messages & files in Secret Chats

View file

@ -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.

View file

@ -322,8 +322,8 @@ 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)
{
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
@ -341,9 +341,6 @@ namespace WTelegram
RaiseUpdate(updatesState);
}
}
else
throw;
}
catch
{
RaiseUpdate(reactorError);

View file

@ -2268,24 +2268,24 @@ namespace TL
/// <summary>Location of a certain size of a picture <para>See <a href="https://corefork.telegram.org/type/PhotoSize"/></para> <para>Derived classes: <see cref="PhotoSizeEmpty"/>, <see cref="PhotoSize"/>, <see cref="PhotoCachedSize"/>, <see cref="PhotoStrippedSize"/>, <see cref="PhotoSizeProgressive"/>, <see cref="PhotoPathSize"/></para></summary>
public abstract partial class PhotoSizeBase : IObject
{
/// <summary>Thumbnail type (see. <see cref="PhotoSize"/>)</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public virtual string Type { get; }
}
/// <summary>Empty constructor. Image with this thumbnail is unavailable. <para>See <a href="https://corefork.telegram.org/constructor/photoSizeEmpty"/></para></summary>
[TLDef(0x0E17E23C)]
public partial class PhotoSizeEmpty : PhotoSizeBase
{
/// <summary>Thumbnail type (see. <see cref="PhotoSize"/>)</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public string type;
/// <summary>Thumbnail type (see. <see cref="PhotoSize"/>)</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public override string Type => type;
}
/// <summary>Image description. <para>See <a href="https://corefork.telegram.org/constructor/photoSize"/></para></summary>
[TLDef(0x75C78E60)]
public partial class PhotoSize : PhotoSizeBase
{
/// <summary>Thumbnail type</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public string type;
/// <summary>Image width</summary>
public int w;
@ -2294,7 +2294,7 @@ namespace TL
/// <summary>File size</summary>
public int size;
/// <summary>Thumbnail type</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public override string Type => type;
}
/// <summary>Description of an image and its content. <para>See <a href="https://corefork.telegram.org/constructor/photoCachedSize"/></para></summary>
@ -2329,7 +2329,7 @@ namespace TL
[TLDef(0xFA3EFB95)]
public partial class PhotoSizeProgressive : PhotoSizeBase
{
/// <summary>Photosize type</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Photosize type »</a></summary>
public string type;
/// <summary>Photo width</summary>
public int w;
@ -2338,7 +2338,7 @@ namespace TL
/// <summary>Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image.</summary>
public int[] sizes;
/// <summary>Photosize type</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Photosize type »</a></summary>
public override string Type => type;
}
/// <summary>Messages with animated stickers can have a compressed svg (&lt; 300 bytes) to show the outline of the sticker before fetching the actual lottie animation. <para>See <a href="https://corefork.telegram.org/constructor/photoPathSize"/></para></summary>
@ -13151,9 +13151,9 @@ namespace TL
public long bot_id;
/// <summary>Attachment menu item name</summary>
public string short_name;
/// <summary>List of peer types where this attachment should be shown</summary>
/// <summary>List of dialog types where this attachment menu entry should be shown</summary>
public AttachMenuPeerType[] peer_types;
/// <summary>Attachment menu icon</summary>
/// <summary>List of platform-specific static icons and animations to use for the attachment menu button</summary>
public AttachMenuBotIcon[] icons;
[Flags] public enum Flags : uint
@ -13292,9 +13292,9 @@ namespace TL
{
///<summary>The bot attachment menu entry is available in the chat with the bot that offers it</summary>
SameBotPM = 0x7D6BE90E,
///<summary>The bot attachment menu entry is available in private chats with other bots</summary>
///<summary>The bot attachment menu entry is available in private chats with other bots (excluding the bot that offers the current attachment menu)</summary>
BotPM = 0xC32BFA1A,
///<summary>The bot attachment menu entry is available in private chats with other users</summary>
///<summary>The bot attachment menu entry is available in private chats with other users (not bots)</summary>
PM = 0xF146D31F,
///<summary>The bot attachment menu entry is available in <a href="https://corefork.telegram.org/api/channel">groups and supergroups</a></summary>
Chat = 0x0509113F,

View file

@ -3214,7 +3214,7 @@ namespace TL
/// <param name="bot">Bot that owns the <a href="https://corefork.telegram.org/api/bots/webapps">web app</a></param>
/// <param name="url"><a href="https://corefork.telegram.org/api/bots/webapps">Web app URL</a></param>
/// <param name="start_param">If the web app was opened from the attachment menu using a <a href="https://corefork.telegram.org/api/links#bot-attachment-menu-links">attachment menu deep link</a>, <c>start_param</c> should contain the <c>data</c> from the <c>startattach</c> parameter.</param>
/// <param name="theme_params">Theme parameters for the web app</param>
/// <param name="theme_params"><a href="https://corefork.telegram.org/api/bots/webapps#theme-parameters">Theme parameters »</a></param>
/// <param name="platform">Short name of the application; 0-64 English letters, digits, and underscores</param>
/// <param name="reply_to_msg_id">Whether the inline message that will be sent by the bot on behalf of the user once the web app interaction is <a href="https://corefork.telegram.org/method/messages.sendWebViewResultMessage">terminated</a> should be sent in reply to this message ID.</param>
/// <param name="send_as">Open the web app as the specified peer, sending the resulting the message as the specified peer.</param>
@ -3253,7 +3253,7 @@ namespace TL
/// <summary>Open a <a href="https://corefork.telegram.org/api/bots/webapps">bot web app</a>. <para>See <a href="https://corefork.telegram.org/method/messages.requestSimpleWebView"/></para></summary>
/// <param name="bot">Bot that owns the webapp</param>
/// <param name="url">Web app URL</param>
/// <param name="theme_params">Theme parameters</param>
/// <param name="theme_params"><a href="https://corefork.telegram.org/api/bots/webapps#theme-parameters">Theme parameters »</a></param>
/// <param name="platform">Short name of the application; 0-64 English letters, digits, and underscores</param>
public static Task<SimpleWebViewResult> Messages_RequestSimpleWebView(this Client client, InputUserBase bot, string url, string platform, DataJSON theme_params = null)
=> client.Invoke(new Messages_RequestSimpleWebView

View file

@ -672,7 +672,7 @@ namespace TL
[TLDef(0x77BFB61B)]
public partial class PhotoSize : PhotoSizeBase
{
/// <summary>Thumbnail type</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public string type;
public FileLocationBase location;
/// <summary>Image width</summary>
@ -682,7 +682,7 @@ namespace TL
/// <summary>File size</summary>
public int size;
/// <summary>Thumbnail type</summary>
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public override string Type => type;
}
/// <summary>Description of an image and its content. <para>See <a href="https://corefork.telegram.org/constructor/photoCachedSize"/></para></summary>