diff --git a/EXAMPLES.md b/EXAMPLES.md
index 654e117..f62efda 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -95,7 +95,7 @@ foreach (Dialog dialog in dialogs.dialogs)
Notes:
- 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](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs#L20).
+- See also the `Main` method in [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L20).
- To retrieve the dialog information about a specific [peer](README.md#terminology), use `client.Messages_GetPeerDialogs(inputPeer)`
@@ -114,7 +114,7 @@ Notes:
- 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](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_GetAllChats.cs#L32)
+- You can find a longer version of this method call in [Examples/Program_GetAllChats.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_GetAllChats.cs?ts=4#L32)
## List the members from a chat
@@ -189,14 +189,14 @@ Notes:
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](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs#L23).
+See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23).
## 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](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs#L23).
+See the `DisplayMessage` method in [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23).
You can filter specific chats the message are posted in, by looking at the `Message.peer_id` field.
@@ -206,7 +206,7 @@ You can filter specific chats the message are posted in, by looking at the `Mess
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](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_DownloadSavedMedia.cs#L31) that download all media files you forward to yourself (Saved Messages)
+See [Examples/Program_DownloadSavedMedia.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_DownloadSavedMedia.cs?ts=4#L31) that download all media files you forward to yourself (Saved Messages)
## Upload a media file and post it with caption to a chat
@@ -323,10 +323,10 @@ await Task.Delay(5000);
## 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 text = "Vicksy says Hi! ";
var entities = client.MarkdownToEntities(ref text, premium: true);
await client.SendMessageAsync(InputPeer.Self, text, entities: entities);
-// also available in HTML: 👋
+// also available in HTML: 👋
// • Fetch all available standard emoji reactions
var all_emoji = await client.Messages_GetAvailableReactions();
@@ -448,7 +448,7 @@ You can automate the collection of `access_hash` for the various resources obtai
so that you don't have to remember them by yourself or ask the API about them each time.
This is done by activating the experimental `client.CollectAccessHash` system.
-See [Examples/Program_CollectAccessHash.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_CollectAccessHash.cs#L22) for how to enable it, and save/restore them for later use.
+See [Examples/Program_CollectAccessHash.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_CollectAccessHash.cs?ts=4#L22) for how to enable it, and save/restore them for later use.
## Use a proxy or MTProxy to connect to Telegram
@@ -509,14 +509,14 @@ you can choose to store the session data somewhere else, like in a database.
The WTelegram.Client constructor takes an optional `sessionStore` parameter to allow storing sessions in an alternate manner.
Use it to pass a custom Stream-derived class that will **read** (first initial call to Length & Read) and **store** (subsequent Writes) session data to database.
-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)
+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?ts=4#L61)
## Send/receive end-to-end encrypted messages & files in Secret Chats
This can be done easily using the helper class `WTelegram.SecretChats` offering methods to manage/encrypt/decrypt secret chats & encrypted messages/files.
-You can view a full working example at [Examples/Program_SecretChats.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_SecretChats.cs#L11).
+You can view a full working example at [Examples/Program_SecretChats.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_SecretChats.cs?ts=4#L11).
Secret Chats have been tested successfully with Telegram Android & iOS official clients.
You can also check our [FAQ for more implementation details](FAQ.md#14-secret-chats-implementation-details).
\ No newline at end of file
diff --git a/FAQ.md b/FAQ.md
index 4d388e9..2363f45 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -243,7 +243,7 @@ and hosted online on any [VPS Hosting](https://www.google.com/search?q=vps+hosti
Pure WebApp hosts might not be adequate as they will recycle (stop) your app if there is no incoming HTTP requests.
There are many cheap VPS Hosting offers available, for example Heroku:
-See [Examples/Program_Heroku.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_Heroku.cs#L9) for such an implementation and the steps to host/deploy it.
+See [Examples/Program_Heroku.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_Heroku.cs?ts=4#L9) for such an implementation and the steps to host/deploy it.
## 14. Secret Chats implementation details
diff --git a/README.md b/README.md
index 7aacdc3..028de8d 100644
--- a/README.md
+++ b/README.md
@@ -156,23 +156,23 @@ and in the [Examples subdirectory](https://github.com/wiz0u/WTelegramClient/tree
# Terminology in Telegram Client API
In the API, Telegram uses some terms/classnames that can be confusing as they differ from the terms shown to end-users:
-- `Channel` : A (large or public) chat group *(sometimes called [supergroup](https://corefork.telegram.org/api/channel#supergroups))*
+- `Channel`: A (large or public) chat group *(sometimes called [supergroup](https://corefork.telegram.org/api/channel#supergroups))*,
or a [broadcast channel](https://corefork.telegram.org/api/channel#channels) (the `broadcast` flag differentiate those)
-- `Chat` : A private [basic chat group](https://corefork.telegram.org/api/channel#basic-groups) with less than 200 members
+- `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` *(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.
+- **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.
See [FAQ #4](https://wiz0u.github.io/WTelegramClient/FAQ#access-hash) to learn more about it.
-- DC (DataCenter) : There are a few datacenters depending on where in the world the user (or an uploaded media file) is from.
-- Session or Authorization : Pairing between a device and a phone number. You can have several active sessions for the same phone number.
+- **DC** (DataCenter): There are a few datacenters depending on where in the world the user (or an uploaded media file) is from.
+- **Session** or **Authorization**: Pairing between a device and a phone number. You can have several active sessions for the same phone number.
# Other things to know
The Client class also offers an `OnUpdate` event that is triggered when Telegram servers sends Updates (like new messages or status), independently of your API requests.
-See [Examples/Program_ListenUpdates.cs](https://wiz0u.github.io/WTelegramClient/Examples/Program_ListenUpdates.cs)
+See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L23)
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.
diff --git a/src/Client.Helpers.cs b/src/Client.Helpers.cs
index 30da842..f9adc37 100644
--- a/src/Client.Helpers.cs
+++ b/src/Client.Helpers.cs
@@ -25,7 +25,7 @@ namespace WTelegram
/// Retrieve the access_hash associated with this id (for a TL class) if it was collected
/// This requires to be set to first.
- /// See Examples/Program_CollectAccessHash.cs for how to use this
+ /// See Examples/Program_CollectAccessHash.cs for how to use this
/// a TL object class. For example User, Channel or Photo
public long GetAccessHashFor(long id) where T : IObject
{
diff --git a/src/Client.cs b/src/Client.cs
index af5234e..7918d1f 100644
--- a/src/Client.cs
+++ b/src/Client.cs
@@ -24,7 +24,7 @@ namespace WTelegram
public partial class Client : IDisposable
{
/// This event will be called when unsollicited updates/messages are sent by Telegram servers
- /// Make your handler , or return or
See Examples/Program_ListenUpdate.cs for how to use this
+ /// Make your handler , or return or
See Examples/Program_ListenUpdate.cs for how to use this
public event Func OnUpdate;
/// Used to create a TcpClient connected to the given address/port, or throw an exception on failure
public TcpFactory TcpHandler { get; set; } = DefaultTcpHandler;
diff --git a/src/TL.Extensions.cs b/src/TL.Extensions.cs
index f028131..fb18172 100644
--- a/src/TL.Extensions.cs
+++ b/src/TL.Extensions.cs
@@ -91,6 +91,9 @@ namespace TL
else
ProcessEntity();
break;
+ case '!' when offset + 1 < sb.Length && sb[offset + 1] == '[':
+ sb.Remove(offset, 1);
+ goto case '[';
case '[':
entities.Add(new MessageEntityTextUrl { offset = offset, length = -1 });
sb.Remove(offset, 1);
@@ -112,7 +115,7 @@ namespace TL
textUrl.url = sb.ToString(offset + 2, offset2 - offset - 3);
if (textUrl.url.StartsWith("tg://user?id=") && long.TryParse(textUrl.url[13..], out var id) && client.GetAccessHashFor(id) is long hash)
entities[lastIndex] = new InputMessageEntityMentionName { offset = textUrl.offset, length = textUrl.length, user_id = new InputUser(id, hash) };
- else if (textUrl.url.StartsWith("emoji?id=") && long.TryParse(textUrl.url[9..], out id))
+ else if ((textUrl.url.StartsWith("tg://emoji?id=") || textUrl.url.StartsWith("emoji?id=")) && long.TryParse(textUrl.url[(textUrl.url.IndexOf('=') + 1)..], out id))
if (premium) entities[lastIndex] = new MessageEntityCustomEmoji { offset = textUrl.offset, length = textUrl.length, document_id = id };
else entities.RemoveAt(lastIndex);
sb.Remove(offset, offset2 - offset);
@@ -165,7 +168,7 @@ namespace TL
if (entityToMD.TryGetValue(nextEntity.GetType(), out var md))
{
var closing = (nextEntity.offset + nextEntity.length, md);
- if (md[0] == '[')
+ if (md[0] is '[' or '!')
{
if (nextEntity is MessageEntityTextUrl metu)
closing.md = $"]({metu.url.Replace("\\", "\\\\").Replace(")", "\\)").Replace(">", "%3E")})";
@@ -174,7 +177,7 @@ namespace TL
else if (nextEntity is InputMessageEntityMentionName imemn)
closing.md = $"](tg://user?id={imemn.user_id.UserId ?? client.UserId})";
else if (nextEntity is MessageEntityCustomEmoji mecu)
- if (premium) closing.md = $"](emoji?id={mecu.document_id})";
+ if (premium) closing.md = $"](tg://emoji?id={mecu.document_id})";
else continue;
}
else if (nextEntity is MessageEntityPre mep)
@@ -208,7 +211,7 @@ namespace TL
[typeof(MessageEntityUnderline)] = "__",
[typeof(MessageEntityStrike)] = "~",
[typeof(MessageEntitySpoiler)] = "||",
- [typeof(MessageEntityCustomEmoji)] = "[",
+ [typeof(MessageEntityCustomEmoji)] = "![",
};
/// Insert backslashes in front of Markdown reserved characters
@@ -304,8 +307,8 @@ namespace TL
if (entities.LastOrDefault(e => e.length == -1) is MessageEntityPre prevEntity)
prevEntity.language = tag[21..^1];
}
- else if (premium && tag.StartsWith("tg-emoji id=\""))
- entities.Add(new MessageEntityCustomEmoji { offset = offset, length = -1, document_id = long.Parse(tag[13..^1]) });
+ else if (premium && (tag.StartsWith("tg-emoji emoji-id=\"") || tag.StartsWith("tg-emoji id=\"")))
+ entities.Add(new MessageEntityCustomEmoji { offset = offset, length = -1, document_id = long.Parse(tag[(tag.IndexOf('=') + 2)..^1]) });
break;
}
@@ -361,7 +364,7 @@ namespace TL
tag = $"";
}
else if (nextEntity is MessageEntityCustomEmoji mecu)
- if (premium) tag = $"";
+ if (premium) tag = $"";
else continue;
else if (nextEntity is MessageEntityPre mep && !string.IsNullOrEmpty(mep.language))
{