mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Compare commits
28 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfc8e0e1b5 | ||
|
|
e923d65d53 | ||
|
|
4ad2f0a212 | ||
|
|
30bc536ebc | ||
|
|
d6fdcab440 | ||
|
|
9ec2f31f72 | ||
|
|
4ccfddd22e | ||
|
|
9693037ef2 | ||
|
|
40bcf69bfb | ||
|
|
4875f75774 | ||
|
|
2e95576be5 | ||
|
|
48d005b605 | ||
|
|
a5323eaa86 | ||
|
|
610d059b4c | ||
|
|
3f1036a559 | ||
|
|
4578dea3a3 | ||
|
|
253249e06a | ||
|
|
eb52dccfa7 | ||
|
|
a9bbdb9fc4 | ||
|
|
5f411d45f9 | ||
|
|
e16e39bfba | ||
|
|
7faa3873f8 | ||
|
|
d9e4b7cc0f | ||
|
|
30a982b0ac | ||
|
|
e9543a690b | ||
|
|
a8fa32dfd5 | ||
|
|
56ba15bc13 | ||
|
|
a3f41330b5 |
33
.github/workflows/dev.yml
vendored
33
.github/workflows/dev.yml
vendored
|
|
@ -12,14 +12,16 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
id-token: write # enable GitHub OIDC token issuance for this job
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 100
|
||||
fetch-depth: 30
|
||||
- name: Determine version
|
||||
run: |
|
||||
git fetch --depth=100 --tags
|
||||
git fetch --depth=30 --tags
|
||||
DESCR_TAG=$(git describe --tags)
|
||||
COMMITS=${DESCR_TAG#*-}
|
||||
COMMITS=${COMMITS%-*}
|
||||
|
|
@ -29,24 +31,37 @@ jobs:
|
|||
if [[ "$RELEASE_VERSION" > "$NEXT_VERSION" ]] then VERSION=$RELEASE_VERSION; else VERSION=$NEXT_VERSION; fi
|
||||
echo Last tag: $LAST_TAG · Next version: $NEXT_VERSION · Release version: $RELEASE_VERSION · Build version: $VERSION
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
# - name: Setup .NET
|
||||
# uses: actions/setup-dotnet@v4
|
||||
# with:
|
||||
# dotnet-version: 8.0.x
|
||||
- name: Pack
|
||||
run: dotnet pack $PROJECT_PATH --configuration $CONFIGURATION -p:Version=$VERSION "-p:ReleaseNotes=\"$RELEASE_NOTES\"" --output packages
|
||||
run: |
|
||||
RELEASE_NOTES=${RELEASE_NOTES//$'\n'/%0A}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//\"/%22}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//,/%2C}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//;/%3B}
|
||||
dotnet pack $PROJECT_PATH --configuration $CONFIGURATION -p:Version=$VERSION -p:ReleaseNotes="$RELEASE_NOTES" --output packages
|
||||
# - name: Upload artifact
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: packages
|
||||
# path: packages/*.nupkg
|
||||
|
||||
- name: NuGet login (OIDC → temp API key)
|
||||
uses: NuGet/login@v1
|
||||
id: login
|
||||
with:
|
||||
user: ${{ secrets.NUGET_USER }}
|
||||
- name: Nuget push
|
||||
run: dotnet nuget push packages/*.nupkg --api-key ${{secrets.NUGETAPIKEY}} --skip-duplicate --source https://api.nuget.org/v3/index.json
|
||||
run: dotnet nuget push packages/*.nupkg --api-key ${{steps.login.outputs.NUGET_API_KEY}} --skip-duplicate --source https://api.nuget.org/v3/index.json
|
||||
|
||||
- name: Deployment Notification
|
||||
env:
|
||||
JSON: |
|
||||
{
|
||||
"status": "success", "complete": true, "commitMessage": ${{ toJSON(github.event.head_commit.message) }},
|
||||
"status": "success", "complete": true, "commitMessage": ${{ toJSON(env.RELEASE_NOTES) }},
|
||||
"message": "{ \"commitId\": \"${{ github.sha }}\", \"buildNumber\": \"${{ env.VERSION }}\", \"repoName\": \"${{ github.repository }}\"}"
|
||||
}
|
||||
run: |
|
||||
|
|
|
|||
23
.github/workflows/release.yml
vendored
23
.github/workflows/release.yml
vendored
|
|
@ -22,6 +22,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # For git tag
|
||||
id-token: write # enable GitHub OIDC token issuance for this job
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -37,19 +38,35 @@ jobs:
|
|||
if [[ "$RELEASE_VERSION" > "$NEXT_VERSION" ]] then VERSION=$RELEASE_VERSION; else VERSION=$NEXT_VERSION; fi
|
||||
echo Last tag: $LAST_TAG · Next version: $NEXT_VERSION · Release version: $RELEASE_VERSION · Build version: $VERSION
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
- name: Pack
|
||||
run: dotnet pack $PROJECT_PATH --configuration $CONFIGURATION -p:Version=$VERSION "-p:ReleaseNotes=\"$RELEASE_NOTES\"" --output packages
|
||||
run: |
|
||||
RELEASE_NOTES=${RELEASE_NOTES//|/%0A}
|
||||
RELEASE_NOTES=${RELEASE_NOTES// - /%0A- }
|
||||
RELEASE_NOTES=${RELEASE_NOTES// /%0A%0A}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//$'\n'/%0A}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//\"/%22}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//,/%2C}
|
||||
RELEASE_NOTES=${RELEASE_NOTES//;/%3B}
|
||||
dotnet pack $PROJECT_PATH --configuration $CONFIGURATION -p:Version=$VERSION -p:ReleaseNotes="$RELEASE_NOTES" --output packages
|
||||
# - name: Upload artifact
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: packages
|
||||
# path: packages/*.nupkg
|
||||
|
||||
- name: NuGet login (OIDC → temp API key)
|
||||
uses: NuGet/login@v1
|
||||
id: login
|
||||
with:
|
||||
user: ${{ secrets.NUGET_USER }}
|
||||
- name: Nuget push
|
||||
run: dotnet nuget push packages/*.nupkg --api-key ${{secrets.NUGETAPIKEY}} --skip-duplicate --source https://api.nuget.org/v3/index.json
|
||||
run: dotnet nuget push packages/*.nupkg --api-key ${{steps.login.outputs.NUGET_API_KEY}} --skip-duplicate --source https://api.nuget.org/v3/index.json
|
||||
|
||||
- name: Git tag
|
||||
run: |
|
||||
git tag $VERSION
|
||||
|
|
@ -58,7 +75,7 @@ jobs:
|
|||
env:
|
||||
JSON: |
|
||||
{
|
||||
"status": "success", "complete": true, "commitMessage": ${{ toJSON(github.event.head_commit.message) }},
|
||||
"status": "success", "complete": true, "commitMessage": ${{ toJSON(env.RELEASE_NOTES) }},
|
||||
"message": "{ \"commitId\": \"${{ github.sha }}\", \"buildNumber\": \"${{ env.VERSION }}\", \"repoName\": \"${{ github.repository }}\"}"
|
||||
}
|
||||
run: |
|
||||
|
|
|
|||
3
.github/workflows/telegram-api.yml
vendored
3
.github/workflows/telegram-api.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
if: contains(github.event.issue.labels.*.name, 'telegram api')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/support-requests@v3.0.0
|
||||
- uses: dessant/support-requests@v4
|
||||
with:
|
||||
support-label: 'telegram api'
|
||||
issue-comment: >
|
||||
|
|
@ -26,3 +26,4 @@ jobs:
|
|||
|
||||
If the above links didn't answer your problem, [click here to ask your question on **StackOverflow**](https://stackoverflow.com/questions/ask?tags=c%23+wtelegramclient+telegram-api) so the whole community can help and benefit.
|
||||
close-issue: true
|
||||
issue-close-reason: 'not planned'
|
||||
|
|
|
|||
37
EXAMPLES.md
37
EXAMPLES.md
|
|
@ -210,7 +210,7 @@ that simplifies the download of a photo/document/file once you get a reference t
|
|||
|
||||
See [Examples/Program_DownloadSavedMedia.cs](https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_DownloadSavedMedia.cs?ts=4#L28) that download all media files you forward to yourself (Saved Messages)
|
||||
|
||||
_Note: To abort an ongoing download, you can throw an exception via the `progress` callback argument._
|
||||
_Note: To abort an ongoing download, you can throw an exception via the `progress` callback argument. Example: `(t,s) => ct.ThrowIfCancellationRequested()`_
|
||||
|
||||
<a name="upload"></a>
|
||||
## Upload a media file and post it with caption to a chat
|
||||
|
|
@ -224,6 +224,41 @@ var inputFile = await client.UploadFileAsync(Filepath);
|
|||
await client.SendMediaAsync(peer, "Here is the photo", inputFile);
|
||||
```
|
||||
|
||||
<a name="upload-video"></a>
|
||||
## Upload a streamable video with optional custom thumbnail
|
||||
```csharp
|
||||
var chats = await client.Messages_GetAllChats();
|
||||
InputPeer peer = chats.chats[1234567890]; // the chat we want
|
||||
const string videoPath = @"C:\...\video.mp4";
|
||||
const string thumbnailPath = @"C:\...\thumbnail.jpg";
|
||||
|
||||
// Extract video information using FFMpegCore or similar library
|
||||
var mediaInfo = await FFmpeg.GetMediaInfo(videoPath);
|
||||
var videoStream = mediaInfo.VideoStreams.FirstOrDefault();
|
||||
int width = videoStream?.Width ?? 0;
|
||||
int height = videoStream?.Height ?? 0;
|
||||
int duration = (int)mediaInfo.Duration.TotalSeconds;
|
||||
|
||||
// Upload video file
|
||||
var inputFile = await Client.UploadFileAsync(videoPath);
|
||||
|
||||
// Prepare InputMedia structure with video attributes
|
||||
var media = new InputMediaUploadedDocument(inputFile, "video/mp4",
|
||||
new DocumentAttributeVideo { w = width, h = height, duration = duration,
|
||||
flags = DocumentAttributeVideo.Flags.supports_streaming });
|
||||
if (thumbnailPath != null)
|
||||
{
|
||||
// upload custom thumbnail and complete InputMedia structure
|
||||
var inputThumb = await client.UploadFileAsync(thumbnailPath);
|
||||
media.thumb = inputThumb;
|
||||
media.flags |= InputMediaUploadedDocument.Flags.has_thumb;
|
||||
}
|
||||
|
||||
// Send the media message
|
||||
await client.SendMessageAsync(peer, "caption", media);
|
||||
```
|
||||
*Note: This example requires FFMpegCore NuGet package for video metadata extraction. You can also manually set width, height, and duration if you know the video properties.*
|
||||
|
||||
<a name="album"></a>
|
||||
## Send a grouped media album using photos from various sources
|
||||
```csharp
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[](https://corefork.telegram.org/methods)
|
||||
[](https://corefork.telegram.org/methods)
|
||||
[](https://www.nuget.org/packages/WTelegramClient/)
|
||||
[](https://www.nuget.org/packages/WTelegramClient/absoluteLatest)
|
||||
[](https://buymeacoffee.com/wizou)
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
This library allows you to connect to Telegram and control a user programmatically (or a bot, but [WTelegramBot](https://www.nuget.org/packages/WTelegramBot) is much easier for that).
|
||||
All the Telegram Client APIs (MTProto) are supported so you can do everything the user could do with a full Telegram GUI client.
|
||||
|
||||
Library was developed solely by one unemployed guy. [Donations are welcome](https://buymeacoffee.com/wizou).
|
||||
Library was developed solely by one unemployed guy. [Donations](https://buymeacoffee.com/wizou) or [Patreon memberships are welcome](https://patreon.com/wizou).
|
||||
|
||||
This ReadMe is a **quick but important tutorial** to learn the fundamentals about this library. Please read it all.
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class MTProtoGenerator : IIncrementalGenerator
|
|||
var nullables = LoadNullables(layer);
|
||||
var namespaces = new Dictionary<string, Dictionary<string, string>>(); // namespace,class,methods
|
||||
var tableTL = new StringBuilder();
|
||||
var methodsTL = new StringBuilder();
|
||||
var source = new StringBuilder();
|
||||
source
|
||||
.AppendLine("using System;")
|
||||
|
|
@ -46,6 +47,9 @@ public class MTProtoGenerator : IIncrementalGenerator
|
|||
tableTL
|
||||
.AppendLine("\t\tpublic static readonly Dictionary<uint, Func<BinaryReader, IObject>> Table = new()")
|
||||
.AppendLine("\t\t{");
|
||||
methodsTL
|
||||
.AppendLine("\t\tpublic static readonly Dictionary<uint, Func<BinaryReader, IObject>> Methods = new()")
|
||||
.AppendLine("\t\t{");
|
||||
|
||||
foreach (var classDecl in unit.classes)
|
||||
{
|
||||
|
|
@ -79,15 +83,20 @@ public class MTProtoGenerator : IIncrementalGenerator
|
|||
ns = symbol.ContainingNamespace.ToString();
|
||||
name = symbol.Name;
|
||||
if (!namespaces.TryGetValue(ns, out var classes)) namespaces[ns] = classes = [];
|
||||
if (name is "_Message" or "RpcResult" or "MsgCopy")
|
||||
if (name is "_Message" or "MsgCopy")
|
||||
{
|
||||
classes[name] = "\t\tpublic void WriteTL(BinaryWriter writer) => throw new NotSupportedException();";
|
||||
continue;
|
||||
}
|
||||
if (id == 0x3072CFA1) // GzipPacked
|
||||
tableTL.AppendLine($"\t\t\t[0x{id:X8}] = reader => (IObject)reader.ReadTLGzipped(typeof(IObject)),");
|
||||
else if (name != "Null" && (ns != "TL.Methods" || name == "Ping"))
|
||||
else if (name != "Null")
|
||||
{
|
||||
if (ns == "TL.Methods")
|
||||
methodsTL.AppendLine($"\t\t\t[0x{id:X8}] = {(ns == "TL" ? "" : ns + '.')}{name}{(symbol.IsGenericType ? "<object>" : "")}.ReadTL,");
|
||||
if (ns != "TL.Methods" || name == "Ping")
|
||||
tableTL.AppendLine($"\t\t\t[0x{id:X8}] = {(ns == "TL" ? "" : ns + '.')}{name}.ReadTL,");
|
||||
}
|
||||
var override_ = symbol.BaseType == object_ ? "" : "override ";
|
||||
if (name == "Messages_AffectedMessages") override_ = "virtual ";
|
||||
//if (symbol.Constructors[0].IsImplicitlyDeclared)
|
||||
|
|
@ -167,26 +176,35 @@ public class MTProtoGenerator : IIncrementalGenerator
|
|||
writeTl.AppendLine($"writer.WriteTLMessages({member.Name});");
|
||||
break;
|
||||
case "TL.IObject": case "TL.IMethod<X>":
|
||||
readTL.AppendLine($"r.{member.Name} = {(memberType == "TL.IObject" ? "" : $"({memberType})")}reader.ReadTLObject();");
|
||||
readTL.AppendLine($"r.{member.Name} = {(memberType == "TL.IObject" ? "reader.ReadTLObject()" : "reader.ReadTLMethod<X>()")};");
|
||||
writeTl.AppendLine($"{member.Name}.WriteTL(writer);");
|
||||
break;
|
||||
case "System.Collections.Generic.Dictionary<long, TL.User>":
|
||||
readTL.AppendLine($"r.{member.Name} = reader.ReadTLDictionary<User>();");
|
||||
writeTl.AppendLine($"writer.WriteTLVector({member.Name}.Values.ToArray());");
|
||||
writeTl.AppendLine($"writer.WriteTLVector({member.Name}?.Values.ToArray());");
|
||||
break;
|
||||
case "System.Collections.Generic.Dictionary<long, TL.ChatBase>":
|
||||
readTL.AppendLine($"r.{member.Name} = reader.ReadTLDictionary<ChatBase>();");
|
||||
writeTl.AppendLine($"writer.WriteTLVector({member.Name}.Values.ToArray());");
|
||||
writeTl.AppendLine($"writer.WriteTLVector({member.Name}?.Values.ToArray());");
|
||||
break;
|
||||
case "object":
|
||||
readTL.AppendLine($"r.{member.Name} = reader.ReadTLObject();");
|
||||
writeTl.AppendLine($"writer.WriteTLValue({member.Name}, {member.Name}.GetType());");
|
||||
break;
|
||||
default:
|
||||
if (member.Type is IArrayTypeSymbol arrayType)
|
||||
{
|
||||
if (name is "FutureSalts")
|
||||
{
|
||||
readTL.AppendLine($"r.{member.Name} = reader.ReadTLRawVector<{memberType.Substring(0, memberType.Length - 2)}>(0x0949D9DC).ToArray();");
|
||||
writeTl.AppendLine($"writer.WriteTLRawVector({member.Name}, 16);");
|
||||
}
|
||||
else
|
||||
{
|
||||
readTL.AppendLine($"r.{member.Name} = reader.ReadTLVector<{memberType.Substring(0, memberType.Length - 2)}>();");
|
||||
writeTl.AppendLine($"writer.WriteTLVector({member.Name});");
|
||||
}
|
||||
}
|
||||
else if (member.Type.BaseType.SpecialType == SpecialType.System_Enum)
|
||||
{
|
||||
readTL.AppendLine($"r.{member.Name} = ({memberType})reader.ReadUInt32();");
|
||||
|
|
@ -213,7 +231,8 @@ public class MTProtoGenerator : IIncrementalGenerator
|
|||
foreach (var nullable in nullables)
|
||||
tableTL.AppendLine($"\t\t\t[0x{nullable.Value:X8}] = null,");
|
||||
tableTL.AppendLine("\t\t};");
|
||||
namespaces["TL"]["Layer"] = tableTL.ToString();
|
||||
methodsTL.AppendLine("\t\t};");
|
||||
namespaces["TL"]["Layer"] = tableTL.ToString() + methodsTL.ToString();
|
||||
foreach (var namesp in namespaces)
|
||||
{
|
||||
source.Append("namespace ").AppendLine(namesp.Key).Append('{');
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ namespace WTelegram
|
|||
{
|
||||
partial class Client
|
||||
{
|
||||
#region Client TL Helpers
|
||||
/// <summary>Used to indicate progression of file download/upload</summary>
|
||||
/// <param name="transmitted">transmitted bytes</param>
|
||||
/// <param name="totalSize">total size of file in bytes, or 0 if unknown</param>
|
||||
|
|
@ -36,9 +35,10 @@ namespace WTelegram
|
|||
var client = await GetClientForDC(-_dcSession.DcID, true);
|
||||
using (stream)
|
||||
{
|
||||
const long SMALL_FILE_MAX_SIZE = 10 << 20;
|
||||
bool hasLength = stream.CanSeek;
|
||||
long transmitted = 0, length = hasLength ? stream.Length : -1;
|
||||
bool isBig = !hasLength || length >= 10 * 1024 * 1024;
|
||||
bool isBig = !hasLength || length > SMALL_FILE_MAX_SIZE;
|
||||
int file_total_parts = hasLength ? (int)((length - 1) / FilePartSize) + 1 : -1;
|
||||
long file_id = Helpers.RandomLong();
|
||||
int file_part = 0, read;
|
||||
|
|
@ -94,19 +94,19 @@ namespace WTelegram
|
|||
/// <summary>Search messages in chat with <see href="https://corefork.telegram.org/type/MessagesFilter">filter</see> and text <para>See <a href="https://corefork.telegram.org/method/messages.search"/></para></summary>
|
||||
/// <typeparam name="T">See <see cref="MessagesFilter"/> for a list of possible filter types</typeparam>
|
||||
/// <param name="peer">User or chat, histories with which are searched, or <see langword="null"/> constructor for global search</param>
|
||||
/// <param name="text">Text search request</param>
|
||||
/// <param name="q">Text search request</param>
|
||||
/// <param name="offset_id">Only return messages starting from the specified message ID</param>
|
||||
/// <param name="limit"><a href="https://corefork.telegram.org/api/offsets">Number of results to return</a></param>
|
||||
public Task<Messages_MessagesBase> Messages_Search<T>(InputPeer peer, string text = null, int offset_id = 0, int limit = int.MaxValue) where T : MessagesFilter, new()
|
||||
=> this.Messages_Search(peer, text, new T(), offset_id: offset_id, limit: limit);
|
||||
public Task<Messages_MessagesBase> Messages_Search<T>(InputPeer peer, string q = null, int offset_id = 0, int limit = int.MaxValue) where T : MessagesFilter, new()
|
||||
=> this.Messages_Search(peer, q, new T(), offset_id: offset_id, limit: limit);
|
||||
|
||||
/// <summary>Search messages globally with <see href="https://corefork.telegram.org/type/MessagesFilter">filter</see> and text <para>See <a href="https://corefork.telegram.org/method/messages.searchGlobal"/></para></summary>
|
||||
/// <typeparam name="T">See <see cref="MessagesFilter"/> for a list of possible filter types</typeparam>
|
||||
/// <param name="text">Text search request</param>
|
||||
/// <param name="q">Query</param>
|
||||
/// <param name="offset_id">Only return messages starting from the specified message ID</param>
|
||||
/// <param name="limit"><a href="https://corefork.telegram.org/api/offsets">Number of results to return</a></param>
|
||||
public Task<Messages_MessagesBase> Messages_SearchGlobal<T>(string text = null, int offset_id = 0, int limit = int.MaxValue) where T : MessagesFilter, new()
|
||||
=> this.Messages_SearchGlobal(text, new T(), offset_id: offset_id, limit: limit);
|
||||
public Task<Messages_MessagesBase> Messages_SearchGlobal<T>(string q = null, int offset_id = 0, int limit = int.MaxValue) where T : MessagesFilter, new()
|
||||
=> this.Messages_SearchGlobal(q, new T(), offset_id: offset_id, limit: limit);
|
||||
|
||||
/// <summary>Helper method to send a media message more easily</summary>
|
||||
/// <param name="peer">Destination of message (chat group, channel, user chat, etc..) </param>
|
||||
|
|
@ -361,6 +361,18 @@ namespace WTelegram
|
|||
return thumbSize == null ? document.mime_type : "image/" + fileType;
|
||||
}
|
||||
|
||||
/// <summary>Download a document from Telegram into the outputStream</summary>
|
||||
/// <param name="document">The document to download</param>
|
||||
/// <param name="outputStream">Stream to write the file content to. This method does not close/dispose the stream</param>
|
||||
/// <param name="videoSize">A specific size/version of the animated photo. Use <c>photo.LargestVideoSize</c> to download the largest version of the animated photo</param>
|
||||
/// <param name="progress">(optional) Callback for tracking the progression of the transfer</param>
|
||||
/// <returns>MIME type of the document/thumbnail</returns>
|
||||
public async Task<Storage_FileType> DownloadFileAsync(Document document, Stream outputStream, VideoSize videoSize, ProgressCallback progress = null)
|
||||
{
|
||||
var fileLocation = document.ToFileLocation(videoSize);
|
||||
return await DownloadFileAsync(fileLocation, outputStream, document.dc_id, videoSize.size, progress);
|
||||
}
|
||||
|
||||
/// <summary>Download a file from Telegram into the outputStream</summary>
|
||||
/// <param name="fileLocation">Telegram file identifier, typically obtained with a .ToFileLocation() call</param>
|
||||
/// <param name="outputStream">Stream to write file content to. This method does not close/dispose the stream</param>
|
||||
|
|
@ -535,18 +547,20 @@ namespace WTelegram
|
|||
case Messages_DialogsSlice mds:
|
||||
var dialogList = new List<DialogBase>();
|
||||
var messageList = new List<MessageBase>();
|
||||
while (dialogs.Dialogs.Length != 0)
|
||||
int skip = 0;
|
||||
while (dialogs.Dialogs.Length > skip)
|
||||
{
|
||||
dialogList.AddRange(dialogs.Dialogs);
|
||||
dialogList.AddRange(skip == 0 ? dialogs.Dialogs : dialogs.Dialogs[skip..]);
|
||||
messageList.AddRange(dialogs.Messages);
|
||||
skip = 0;
|
||||
int last = dialogs.Dialogs.Length - 1;
|
||||
var lastDialog = dialogs.Dialogs[last];
|
||||
retryDate:
|
||||
var lastPeer = dialogs.UserOrChat(lastDialog).ToInputPeer();
|
||||
var lastMsgId = lastDialog.TopMessage;
|
||||
retryDate:
|
||||
var lastDate = dialogs.Messages.LastOrDefault(m => m.Peer.ID == lastDialog.Peer.ID && m.ID == lastDialog.TopMessage)?.Date ?? default;
|
||||
if (lastDate == default)
|
||||
if (--last < 0) break; else { lastDialog = dialogs.Dialogs[last]; goto retryDate; }
|
||||
if (--last < 0) break; else { ++skip; lastDialog = dialogs.Dialogs[last]; goto retryDate; }
|
||||
dialogs = await this.Messages_GetDialogs(lastDate, lastMsgId, lastPeer, folder_id: folder_id);
|
||||
if (dialogs is not Messages_Dialogs md) break;
|
||||
foreach (var (key, value) in md.chats) mds.chats[key] = value;
|
||||
|
|
@ -638,18 +652,18 @@ namespace WTelegram
|
|||
}
|
||||
|
||||
/// <summary>Helper simplified method: Get all <a href="https://corefork.telegram.org/api/forum">topics of a forum</a> <para>See <a href="https://corefork.telegram.org/method/channels.getForumTopics"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/channels.getForumTopics#possible-errors">details</a>)</para></summary>
|
||||
/// <param name="channel">Supergroup</param>
|
||||
/// <param name="peer">Supergroup or Bot peer</param>
|
||||
/// <param name="q">Search query</param>
|
||||
public async Task<Messages_ForumTopics> Channels_GetAllForumTopics(InputChannelBase channel, string q = null)
|
||||
public async Task<Messages_ForumTopics> Channels_GetAllForumTopics(InputPeer peer, string q = null)
|
||||
{
|
||||
var result = await this.Channels_GetForumTopics(channel, offset_date: DateTime.MaxValue, q: q);
|
||||
var result = await this.Messages_GetForumTopics(peer, offset_date: DateTime.MaxValue, q: q);
|
||||
if (result.topics.Length < result.count)
|
||||
{
|
||||
var topics = result.topics.ToList();
|
||||
var messages = result.messages.ToList();
|
||||
while (true)
|
||||
{
|
||||
var more_topics = await this.Channels_GetForumTopics(channel, messages[^1].Date, messages[^1].ID, topics[^1].ID);
|
||||
var more_topics = await this.Messages_GetForumTopics(peer, messages[^1].Date, messages[^1].ID, topics[^1].ID);
|
||||
if (more_topics.topics.Length == 0) break;
|
||||
topics.AddRange(more_topics.topics);
|
||||
messages.AddRange(more_topics.messages);
|
||||
|
|
@ -909,6 +923,28 @@ namespace WTelegram
|
|||
}
|
||||
return chat;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>Receive updates for a given group/channel until cancellation is requested.</summary>
|
||||
/// <param name="channel">Group/channel to monitor without joining</param>
|
||||
/// <param name="ct">Cancel token to stop the monitoring</param>
|
||||
/// <remarks>After cancelling, you may still receive updates for a few more seconds</remarks>
|
||||
public async void OpenChat(InputChannel channel, CancellationToken ct)
|
||||
{
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token, ct);
|
||||
try
|
||||
{
|
||||
while (!cts.IsCancellationRequested)
|
||||
{
|
||||
var diff = await this.Updates_GetChannelDifference(channel, null, 1, 1, true);
|
||||
var timeout = diff.Timeout * 1000;
|
||||
await Task.Delay(timeout != 0 ? timeout : 30000, cts.Token);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
Console.WriteLine($"An exception occured for OpenChat {channel.channel_id}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,10 +185,6 @@ namespace WTelegram
|
|||
return Console.ReadLine();
|
||||
}
|
||||
|
||||
/// <summary>Load a specific Telegram server public key</summary>
|
||||
/// <param name="pem">A string starting with <c>-----BEGIN RSA PUBLIC KEY-----</c></param>
|
||||
public static void LoadPublicKey(string pem) => Encryption.LoadPublicKey(pem);
|
||||
|
||||
/// <summary>Builds a structure that is used to validate a 2FA password</summary>
|
||||
/// <param name="accountPassword">Password validation configuration. You can obtain this via <c>Account_GetPassword</c> or through OnOther as part of the login process</param>
|
||||
/// <param name="password">The password to validate</param>
|
||||
|
|
@ -277,9 +273,8 @@ namespace WTelegram
|
|||
|
||||
private Session.DCSession GetOrCreateDCSession(int dcId, DcOption.Flags flags)
|
||||
{
|
||||
if (_session.DCSessions.TryGetValue(dcId, out var dcSession) && dcSession.AuthKey != null)
|
||||
if (dcSession.Client != null || dcSession.DataCenter.flags == flags)
|
||||
return dcSession; // if we have already a session with this DC and we are connected or it is a perfect match, use it
|
||||
if (_session.DCSessions.TryGetValue(dcId, out var dcSession) && dcSession.Client != null)
|
||||
return dcSession; // we have already a connected session with this DC, use it
|
||||
if (dcSession == null && _session.DCSessions.TryGetValue(-dcId, out dcSession) && dcSession.AuthKey != null)
|
||||
{
|
||||
// we have already negociated an AuthKey with this DC
|
||||
|
|
@ -295,9 +290,10 @@ namespace WTelegram
|
|||
dcId = Math.Abs(dcId);
|
||||
}
|
||||
var dcOptions = GetDcOptions(Math.Abs(dcId), flags);
|
||||
var dcOption = dcOptions.FirstOrDefault() ?? throw new WTException($"Could not find adequate dc_option for DC {dcId}");
|
||||
var dcOption = dcOptions.FirstOrDefault();
|
||||
dcSession ??= new(); // create new session only if not already existing
|
||||
dcSession.DataCenter = dcOption;
|
||||
if (dcOption != null) dcSession.DataCenter = dcOption;
|
||||
else if (dcSession.DataCenter == null) throw new WTException($"Could not find adequate dc_option for DC {dcId}");
|
||||
return _session.DCSessions[dcId] = dcSession;
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +354,7 @@ namespace WTelegram
|
|||
if (await stream.FullReadAsync(data, 4, ct) != 4)
|
||||
throw new WTException(ConnectionShutDown);
|
||||
#if OBFUSCATION
|
||||
_recvCtr.EncryptDecrypt(data, 4);
|
||||
_recvCtr.EncryptDecrypt(data.AsSpan(0, 4));
|
||||
#endif
|
||||
int payloadLen = BinaryPrimitives.ReadInt32LittleEndian(data);
|
||||
if (payloadLen <= 0)
|
||||
|
|
@ -370,7 +366,7 @@ namespace WTelegram
|
|||
if (await stream.FullReadAsync(data, payloadLen, ct) != payloadLen)
|
||||
throw new WTException("Could not read frame data : Connection shut down");
|
||||
#if OBFUSCATION
|
||||
_recvCtr.EncryptDecrypt(data, payloadLen);
|
||||
_recvCtr.EncryptDecrypt(data.AsSpan(0, payloadLen));
|
||||
#endif
|
||||
obj = ReadFrame(data, payloadLen);
|
||||
}
|
||||
|
|
@ -593,7 +589,7 @@ namespace WTelegram
|
|||
{
|
||||
var msg = new _Message(reader.ReadInt64(), reader.ReadInt32(), null) { bytes = reader.ReadInt32() };
|
||||
messages.Add(msg);
|
||||
if ((msg.seq_no & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msg.msg_id);
|
||||
if ((msg.seqno & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msg.msg_id);
|
||||
var pos = reader.BaseStream.Position;
|
||||
try
|
||||
{
|
||||
|
|
@ -606,7 +602,7 @@ namespace WTelegram
|
|||
else
|
||||
{
|
||||
var obj = msg.body = reader.ReadTLObject(ctorNb);
|
||||
Helpers.Log(1, $" → {obj.GetType().Name,-38} {MsgIdToStamp(msg.msg_id):u} {((msg.seq_no & 1) != 0 ? "" : "(svc)")} {((msg.msg_id & 2) == 0 ? "" : "NAR")}");
|
||||
Helpers.Log(1, $" → {obj.GetType().Name,-38} {MsgIdToStamp(msg.msg_id):u} {((msg.seqno & 1) != 0 ? "" : "(svc)")} {((msg.msg_id & 2) == 0 ? "" : "NAR")}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -1129,7 +1125,7 @@ namespace WTelegram
|
|||
{
|
||||
try
|
||||
{
|
||||
var users = await this.Users_GetUsers(InputUser.Self); // this calls also reenable incoming Updates
|
||||
var users = await this.Users_GetUsers(InputUser.Self); // this call also reenable incoming Updates
|
||||
var self = users[0] as User;
|
||||
if (self.id == long.Parse(botToken.Split(':')[0]))
|
||||
{
|
||||
|
|
@ -1238,10 +1234,13 @@ namespace WTelegram
|
|||
if (verified is Account_EmailVerifiedLogin verifiedLogin) // (it should always be)
|
||||
sentCodeBase = verifiedLogin.sent_code;
|
||||
}
|
||||
RaiseUpdates(sentCodeBase);
|
||||
}
|
||||
resent:
|
||||
if (sentCodeBase is Auth_SentCodeSuccess success)
|
||||
authorization = success.authorization;
|
||||
else if (sentCodeBase is Auth_SentCodePaymentRequired paymentRequired)
|
||||
throw new WTException("Auth_SentCodePaymentRequired unsupported");
|
||||
else if (sentCodeBase is Auth_SentCode sentCode)
|
||||
{
|
||||
phone_code_hash = sentCode.phone_code_hash;
|
||||
|
|
@ -1410,7 +1409,7 @@ namespace WTelegram
|
|||
public User LoginAlreadyDone(Auth_AuthorizationBase authorization)
|
||||
{
|
||||
if (authorization is not Auth_Authorization { user: User self })
|
||||
throw new WTException("Failed to get Authorization: " + authorization.GetType().Name);
|
||||
throw new WTException("Failed to get Authorization: " + authorization?.GetType().Name);
|
||||
_session.UserId = _dcSession.UserId = self.id;
|
||||
lock (_session) _session.Save();
|
||||
RaiseUpdates(self);
|
||||
|
|
@ -1478,7 +1477,7 @@ namespace WTelegram
|
|||
writer.Write(0L); // int64 auth_key_id = 0 (Unencrypted)
|
||||
writer.Write(msgId); // int64 message_id
|
||||
writer.Write(0); // int32 message_data_length (to be patched)
|
||||
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name.TrimEnd('_')}...");
|
||||
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name.TrimEnd('_')}");
|
||||
writer.WriteTLObject(msg); // bytes message_data
|
||||
BinaryPrimitives.WriteInt32LittleEndian(memStream.GetBuffer().AsSpan(20), (int)memStream.Length - 24); // patch message_data_length
|
||||
}
|
||||
|
|
@ -1523,7 +1522,7 @@ namespace WTelegram
|
|||
int frameLength = (int)memStream.Length;
|
||||
BinaryPrimitives.WriteInt32LittleEndian(buffer, frameLength - 4); // patch payload_len with correct value
|
||||
#if OBFUSCATION
|
||||
_sendCtr?.EncryptDecrypt(buffer, frameLength);
|
||||
_sendCtr?.EncryptDecrypt(buffer.AsSpan(0, frameLength));
|
||||
#endif
|
||||
if (_networkStream != null)
|
||||
await _networkStream.WriteAsync(buffer, 0, frameLength);
|
||||
|
|
@ -1630,6 +1629,16 @@ namespace WTelegram
|
|||
got503 = true;
|
||||
goto retry;
|
||||
}
|
||||
else if (code == 401 && !IsMainDC && message is "SESSION_REVOKED" or "AUTH_KEY_UNREGISTERED") // need to renegociate alt-DC auth
|
||||
{
|
||||
lock (_session)
|
||||
{
|
||||
_session.DCSessions.Remove(_dcSession.DcID);
|
||||
if (_session.MainDC != -_dcSession.DcID) _session.DCSessions.Remove(-_dcSession.DcID);
|
||||
_session.Save();
|
||||
}
|
||||
await DisposeAsync();
|
||||
}
|
||||
else if (code == 400 && message == "CONNECTION_NOT_INITED")
|
||||
{
|
||||
await InitConnection();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using static WTelegram.Compat;
|
|||
|
||||
namespace WTelegram
|
||||
{
|
||||
internal static class Encryption
|
||||
public static class Encryption
|
||||
{
|
||||
private static readonly Dictionary<long, RSAPublicKey> PublicKeys = [];
|
||||
internal static readonly RandomNumberGenerator RNG = RandomNumberGenerator.Create();
|
||||
|
|
@ -94,7 +94,7 @@ namespace WTelegram
|
|||
if (serverDHparams is not ServerDHParamsOk serverDHparamsOk) throw new WTException("not server_DH_params_ok");
|
||||
if (serverDHparamsOk.nonce != nonce) throw new WTException("Nonce mismatch");
|
||||
if (serverDHparamsOk.server_nonce != resPQ.server_nonce) throw new WTException("Server Nonce mismatch");
|
||||
var (tmp_aes_key, tmp_aes_iv) = ConstructTmpAESKeyIV(resPQ.server_nonce, pqInnerData.new_nonce);
|
||||
var (tmp_aes_key, tmp_aes_iv) = ConstructTmpAESKeyIV(sha1, resPQ.server_nonce, pqInnerData.new_nonce);
|
||||
var answer = AES_IGE_EncryptDecrypt(serverDHparamsOk.encrypted_answer, tmp_aes_key, tmp_aes_iv, false);
|
||||
|
||||
using var answerReader = new BinaryReader(new MemoryStream(answer));
|
||||
|
|
@ -163,8 +163,9 @@ namespace WTelegram
|
|||
session.AuthKey = authKey;
|
||||
session.Salt = BinaryPrimitives.ReadInt64LittleEndian(pqInnerData.new_nonce.raw) ^ BinaryPrimitives.ReadInt64LittleEndian(resPQ.server_nonce.raw);
|
||||
session.OldSalt = session.Salt;
|
||||
}
|
||||
|
||||
(byte[] key, byte[] iv) ConstructTmpAESKeyIV(TL.Int128 server_nonce, Int256 new_nonce)
|
||||
public static (byte[] key, byte[] iv) ConstructTmpAESKeyIV(SHA1 sha1, TL.Int128 server_nonce, Int256 new_nonce)
|
||||
{
|
||||
byte[] tmp_aes_key = new byte[32], tmp_aes_iv = new byte[32];
|
||||
sha1.TransformBlock(new_nonce, 0, 32, null, 0);
|
||||
|
|
@ -183,7 +184,6 @@ namespace WTelegram
|
|||
sha1.Initialize();
|
||||
return (tmp_aes_key, tmp_aes_iv);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CheckGoodPrime(BigInteger p, int g)
|
||||
{
|
||||
|
|
@ -237,6 +237,8 @@ namespace WTelegram
|
|||
throw new WTException("g^a or g^b is not between 2^{2048-64} and dh_prime - 2^{2048-64}");
|
||||
}
|
||||
|
||||
/// <summary>Load a specific Telegram server public key</summary>
|
||||
/// <param name="pem">A string starting with <c>-----BEGIN RSA PUBLIC KEY-----</c></param>
|
||||
public static void LoadPublicKey(string pem)
|
||||
{
|
||||
using var rsa = RSA.Create();
|
||||
|
|
@ -245,10 +247,7 @@ namespace WTelegram
|
|||
var rsaParam = rsa.ExportParameters(false);
|
||||
if (rsaParam.Modulus[0] == 0) rsaParam.Modulus = rsaParam.Modulus[1..];
|
||||
var publicKey = new RSAPublicKey { n = rsaParam.Modulus, e = rsaParam.Exponent };
|
||||
using var memStream = new MemoryStream(280);
|
||||
using (var writer = new BinaryWriter(memStream))
|
||||
writer.WriteTLObject(publicKey);
|
||||
var bareData = memStream.ToArray();
|
||||
var bareData = publicKey.ToBytes();
|
||||
var fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(bareData, 4, bareData.Length - 4).AsSpan(12)); // 64 lower-order bits of SHA1
|
||||
PublicKeys[fingerprint] = publicKey;
|
||||
Helpers.Log(1, $"Loaded a public key with fingerprint {fingerprint:X}");
|
||||
|
|
@ -276,7 +275,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
-----END RSA PUBLIC KEY-----");
|
||||
}
|
||||
|
||||
internal static byte[] EncryptDecryptMessage(Span<byte> input, bool encrypt, int x, byte[] authKey, byte[] msgKey, int msgKeyOffset, SHA256 sha256)
|
||||
public static byte[] EncryptDecryptMessage(Span<byte> input, bool encrypt, int x, byte[] authKey, byte[] msgKey, int msgKeyOffset, SHA256 sha256)
|
||||
{
|
||||
// first, construct AES key & IV
|
||||
byte[] aes_key = new byte[32], aes_iv = new byte[32];
|
||||
|
|
@ -297,7 +296,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
return AES_IGE_EncryptDecrypt(input, aes_key, aes_iv, encrypt);
|
||||
}
|
||||
|
||||
internal static byte[] AES_IGE_EncryptDecrypt(Span<byte> input, byte[] aes_key, byte[] aes_iv, bool encrypt)
|
||||
public static byte[] AES_IGE_EncryptDecrypt(Span<byte> input, byte[] aes_key, byte[] aes_iv, bool encrypt)
|
||||
{
|
||||
if (input.Length % 16 != 0) throw new WTException("AES_IGE input size not divisible by 16");
|
||||
|
||||
|
|
@ -305,8 +304,8 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
var output = new byte[input.Length];
|
||||
var prevBytes = (byte[])aes_iv.Clone();
|
||||
var span = MemoryMarshal.Cast<byte, long>(input);
|
||||
var sout = MemoryMarshal.Cast<byte, long>(output);
|
||||
var prev = MemoryMarshal.Cast<byte, long>(prevBytes);
|
||||
var sout = MemoryMarshal.Cast<byte, long>(output.AsSpan());
|
||||
var prev = MemoryMarshal.Cast<byte, long>(prevBytes.AsSpan());
|
||||
if (!encrypt) { (prev[2], prev[0]) = (prev[0], prev[2]); (prev[3], prev[1]) = (prev[1], prev[3]); }
|
||||
for (int i = 0, count = input.Length / 8; i < count;)
|
||||
{
|
||||
|
|
@ -319,7 +318,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
}
|
||||
|
||||
#if OBFUSCATION
|
||||
internal sealed class AesCtr(byte[] key, byte[] ivec) : IDisposable
|
||||
public sealed class AesCtr(byte[] key, byte[] ivec) : IDisposable
|
||||
{
|
||||
readonly ICryptoTransform _encryptor = AesECB.CreateEncryptor(key, null);
|
||||
readonly byte[] _ecount = new byte[16];
|
||||
|
|
@ -327,9 +326,9 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
|
||||
public void Dispose() => _encryptor.Dispose();
|
||||
|
||||
public void EncryptDecrypt(byte[] buffer, int length)
|
||||
public void EncryptDecrypt(Span<byte> buffer)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
if (_num == 0)
|
||||
{
|
||||
|
|
@ -373,7 +372,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
var sendCtr = new AesCtr(sendKey, sendIV);
|
||||
var recvCtr = new AesCtr(recvKey, recvIV);
|
||||
var encrypted = (byte[])preamble.Clone();
|
||||
sendCtr.EncryptDecrypt(encrypted, 64);
|
||||
sendCtr.EncryptDecrypt(encrypted);
|
||||
for (int i = 56; i < 64; i++)
|
||||
preamble[i] = encrypted[i];
|
||||
return (sendCtr, recvCtr, preamble);
|
||||
|
|
@ -557,7 +556,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
|||
{
|
||||
count = count + 15 & ~15;
|
||||
var span = MemoryMarshal.Cast<byte, long>(buffer.AsSpan(offset, count));
|
||||
var prev = MemoryMarshal.Cast<byte, long>(_prevBytes);
|
||||
var prev = MemoryMarshal.Cast<byte, long>(_prevBytes.AsSpan());
|
||||
for (offset = 0, count /= 8; offset < count;)
|
||||
{
|
||||
prev[0] ^= span[offset]; prev[1] ^= span[offset + 1];
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ namespace TL
|
|||
_users[user.id] = user;
|
||||
else
|
||||
{ // update previously full user from min user:
|
||||
// see https://github.com/tdlib/td/blob/master/td/telegram/UserManager.cpp#L2689
|
||||
// and https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/data/data_session.cpp#L515
|
||||
const User.Flags updated_flags = (User.Flags)0x5DAFE000;
|
||||
const User.Flags2 updated_flags2 = (User.Flags2)0x711;
|
||||
// tdlib updated flags: deleted | bot | bot_chat_history | bot_nochats | verified | bot_inline_geo
|
||||
|
|
@ -53,7 +55,7 @@ namespace TL
|
|||
if (user.lang_code != null)
|
||||
prevUser.lang_code = user.lang_code; // tdlib: updated if present ; tdesktop: ignored
|
||||
prevUser.emoji_status = user.emoji_status; // tdlib/tdesktop: updated
|
||||
prevUser.usernames = user.usernames; // tdlib: not updated ; tdesktop: updated
|
||||
//prevUser.usernames = user.usernames; // tdlib/tdesktop: not updated
|
||||
if (user.stories_max_id > 0)
|
||||
prevUser.stories_max_id = user.stories_max_id; // tdlib: updated if > 0 ; tdesktop: not updated
|
||||
prevUser.color = user.color; // tdlib/tdesktop: updated
|
||||
|
|
@ -68,8 +70,13 @@ namespace TL
|
|||
foreach (var chat in chats)
|
||||
if (chat is not Channel channel)
|
||||
_chats[chat.ID] = chat;
|
||||
else if (!channel.flags.HasFlag(Channel.Flags.min) || !_chats.TryGetValue(channel.id, out var prevChat) || prevChat is not Channel prevChannel || prevChannel.flags.HasFlag(Channel.Flags.min))
|
||||
else if (!_chats.TryGetValue(channel.id, out var prevChat) || prevChat is not Channel prevChannel)
|
||||
_chats[channel.id] = channel;
|
||||
else if (!channel.flags.HasFlag(Channel.Flags.min) || prevChannel.flags.HasFlag(Channel.Flags.min))
|
||||
{
|
||||
if (channel.participants_count == 0) channel.participants_count = prevChannel.participants_count; // non-min channel can lack this info
|
||||
_chats[channel.id] = channel;
|
||||
}
|
||||
else
|
||||
{ // update previously full channel from min channel:
|
||||
const Channel.Flags updated_flags = (Channel.Flags)0x7FDC0BE0;
|
||||
|
|
@ -376,15 +383,15 @@ namespace TL
|
|||
end = offset + 1;
|
||||
if (end < sb.Length && sb[end] == '#') end++;
|
||||
while (end < sb.Length && sb[end] is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '0' and <= '9') end++;
|
||||
if (end >= sb.Length || sb[end] != ';') break;
|
||||
var html = HttpUtility.HtmlDecode(sb.ToString(offset, end - offset + 1));
|
||||
var html = HttpUtility.HtmlDecode(end >= sb.Length || sb[end] != ';'
|
||||
? sb.ToString(offset, end - offset) + ";" : sb.ToString(offset, ++end - offset));
|
||||
if (html.Length == 1)
|
||||
{
|
||||
sb[offset] = html[0];
|
||||
sb.Remove(++offset, end - offset + 1);
|
||||
sb.Remove(++offset, end - offset);
|
||||
}
|
||||
else
|
||||
offset = end + 1;
|
||||
offset = end;
|
||||
}
|
||||
else if (c == '<')
|
||||
{
|
||||
|
|
@ -401,6 +408,7 @@ namespace TL
|
|||
case "u": case "ins": ProcessEntity<MessageEntityUnderline>(); break;
|
||||
case "s": case "strike": case "del": ProcessEntity<MessageEntityStrike>(); break;
|
||||
case "span class=\"tg-spoiler\"":
|
||||
case "span class='tg-spoiler'":
|
||||
case "span" when closing:
|
||||
case "tg-spoiler": ProcessEntity<MessageEntitySpoiler>(); break;
|
||||
case "code": ProcessEntity<MessageEntityCode>(); break;
|
||||
|
|
@ -420,7 +428,8 @@ namespace TL
|
|||
prevEntity.length = offset - prevEntity.offset;
|
||||
}
|
||||
}
|
||||
else if (tag.StartsWith("a href=\"") && tag[^1] == '"')
|
||||
else if ((tag[^1] == '"' && tag.StartsWith("a href=\""))
|
||||
|| (tag[^1] == '\'' && tag.StartsWith("a href='")))
|
||||
{
|
||||
tag = HttpUtility.HtmlDecode(tag[8..^1]);
|
||||
if (tag.StartsWith("tg://user?id=") && long.TryParse(tag[13..], out var user_id) && users?.GetValueOrDefault(user_id)?.access_hash is long hash)
|
||||
|
|
@ -428,12 +437,13 @@ namespace TL
|
|||
else
|
||||
entities.Add(new MessageEntityTextUrl { offset = offset, length = -1, url = tag });
|
||||
}
|
||||
else if (tag.StartsWith("code class=\"language-") && tag[^1] == '"')
|
||||
else if ((tag[^1] == '"' && tag.StartsWith("code class=\"language-"))
|
||||
|| (tag[^1] == '\'' && tag.StartsWith("code class='language-")))
|
||||
{
|
||||
if (entities.LastOrDefault(e => e.length == -1) is MessageEntityPre prevEntity)
|
||||
prevEntity.language = tag[21..^1];
|
||||
}
|
||||
else if (premium && (tag.StartsWith("tg-emoji emoji-id=\"") || tag.StartsWith("tg-emoji id=\"")))
|
||||
else if (premium && (tag.StartsWith("tg-emoji emoji-id=\"") || tag.StartsWith("tg-emoji emoji-id='")))
|
||||
entities.Add(new MessageEntityCustomEmoji { offset = offset, length = -1, document_id = long.Parse(tag[(tag.IndexOf('=') + 2)..^1]) });
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,15 +109,13 @@ namespace TL
|
|||
public Int128 new_nonce_hash3;
|
||||
}
|
||||
|
||||
public enum DestroyAuthKeyRes : uint
|
||||
{
|
||||
///<summary>See <a href="https://corefork.telegram.org/constructor/destroy_auth_key_ok"/></summary>
|
||||
Ok = 0xF660E1D4,
|
||||
///<summary>See <a href="https://corefork.telegram.org/constructor/destroy_auth_key_none"/></summary>
|
||||
None = 0x0A9F2259,
|
||||
///<summary>See <a href="https://corefork.telegram.org/constructor/destroy_auth_key_fail"/></summary>
|
||||
Fail = 0xEA109B13,
|
||||
}
|
||||
public abstract partial class DestroyAuthKeyRes : IObject { }
|
||||
[TLDef(0xF660E1D4)] //destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes
|
||||
public sealed partial class DestroyAuthKeyOk : DestroyAuthKeyRes { }
|
||||
[TLDef(0x0A9F2259)] //destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes
|
||||
public sealed partial class DestroyAuthKeyNone : DestroyAuthKeyRes { }
|
||||
[TLDef(0xEA109B13)] //destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes
|
||||
public sealed partial class DestroyAuthKeyFail : DestroyAuthKeyRes { }
|
||||
|
||||
[TLDef(0x62D6B459)] //msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck
|
||||
public sealed partial class MsgsAck : IObject
|
||||
|
|
@ -327,12 +325,12 @@ namespace TL
|
|||
});
|
||||
|
||||
public static Task<DestroyAuthKeyRes> DestroyAuthKey(this Client client)
|
||||
=> client.InvokeBare(new DestroyAuthKey
|
||||
=> client.Invoke(new DestroyAuthKey
|
||||
{
|
||||
});
|
||||
|
||||
public static Task<RpcDropAnswer> RpcDropAnswer(this Client client, long req_msg_id)
|
||||
=> client.InvokeBare(new Methods.RpcDropAnswer
|
||||
=> client.Invoke(new Methods.RpcDropAnswer
|
||||
{
|
||||
req_msg_id = req_msg_id,
|
||||
});
|
||||
|
|
@ -357,7 +355,7 @@ namespace TL
|
|||
});
|
||||
|
||||
public static Task<DestroySessionRes> DestroySession(this Client client, long session_id)
|
||||
=> client.InvokeBare(new DestroySession
|
||||
=> client.Invoke(new DestroySession
|
||||
{
|
||||
session_id = session_id,
|
||||
});
|
||||
|
|
|
|||
1505
src/TL.Schema.cs
1505
src/TL.Schema.cs
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,7 @@
|
|||
namespace TL
|
||||
{
|
||||
#pragma warning disable IDE1006, CS1574
|
||||
/// <summary>Object describes the contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessage"/></para></summary>
|
||||
/// <summary>Object describes the contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessage"/></para> <para>Derived classes: <see cref="DecryptedMessage"/>, <see cref="DecryptedMessageService"/></para></summary>
|
||||
public abstract partial class DecryptedMessageBase : IObject
|
||||
{
|
||||
/// <summary>Flags, see <a href="https://corefork.telegram.org/mtproto/TL-combinators#conditional-fields">TL conditional fields</a> (added in layer 45)</summary>
|
||||
|
|
@ -24,11 +24,12 @@ namespace TL
|
|||
public virtual long ReplyToRandom => default;
|
||||
/// <summary>Random group ID, assigned by the author of message.<br/>Multiple encrypted messages with a photo attached and with the same group ID indicate an <a href="https://corefork.telegram.org/api/files#albums-grouped-media">album or grouped media</a> (parameter added in layer 45)</summary>
|
||||
public virtual long Grouped => default;
|
||||
/// <summary>Random bytes, removed in layer 17.</summary>
|
||||
public virtual byte[] RandomBytes => default;
|
||||
public virtual DecryptedMessageAction Action => default;
|
||||
}
|
||||
|
||||
/// <summary>Object describes media contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessageMedia"/></para></summary>
|
||||
/// <summary>Object describes media contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessageMedia"/></para> <para>Derived classes: <see cref="DecryptedMessageMediaPhoto"/>, <see cref="DecryptedMessageMediaVideo"/>, <see cref="DecryptedMessageMediaGeoPoint"/>, <see cref="DecryptedMessageMediaContact"/>, <see cref="DecryptedMessageMediaDocument"/>, <see cref="DecryptedMessageMediaAudio"/>, <see cref="DecryptedMessageMediaExternalDocument"/>, <see cref="DecryptedMessageMediaVenue"/>, <see cref="DecryptedMessageMediaWebPage"/></para></summary>
|
||||
/// <remarks>a <see langword="null"/> value means <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaEmpty">decryptedMessageMediaEmpty</a></remarks>
|
||||
public abstract partial class DecryptedMessageMedia : IObject
|
||||
{
|
||||
|
|
@ -36,14 +37,17 @@ namespace TL
|
|||
internal virtual (long size, byte[] key, byte[] iv) SizeKeyIV { get => default; set => throw new WTelegram.WTException("Incompatible DecryptedMessageMedia"); }
|
||||
}
|
||||
|
||||
/// <summary>Object describes the action to which a service message is linked. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessageAction"/></para></summary>
|
||||
/// <summary>Object describes the action to which a service message is linked. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessageAction"/></para> <para>Derived classes: <see cref="DecryptedMessageActionSetMessageTTL"/>, <see cref="DecryptedMessageActionReadMessages"/>, <see cref="DecryptedMessageActionDeleteMessages"/>, <see cref="DecryptedMessageActionScreenshotMessages"/>, <see cref="DecryptedMessageActionFlushHistory"/>, <see cref="DecryptedMessageActionResend"/>, <see cref="DecryptedMessageActionNotifyLayer"/>, <see cref="DecryptedMessageActionTyping"/>, <see cref="DecryptedMessageActionRequestKey"/>, <see cref="DecryptedMessageActionAcceptKey"/>, <see cref="DecryptedMessageActionAbortKey"/>, <see cref="DecryptedMessageActionCommitKey"/>, <see cref="DecryptedMessageActionNoop"/></para></summary>
|
||||
public abstract partial class DecryptedMessageAction : IObject { }
|
||||
|
||||
/// <summary>Indicates the location of a photo, will be deprecated soon <para>See <a href="https://corefork.telegram.org/type/FileLocation"/></para></summary>
|
||||
/// <summary>Indicates the location of a photo, will be deprecated soon <para>See <a href="https://corefork.telegram.org/type/FileLocation"/></para> <para>Derived classes: <see cref="FileLocationUnavailable"/>, <see cref="FileLocation"/></para></summary>
|
||||
public abstract partial class FileLocationBase : IObject
|
||||
{
|
||||
/// <summary>Volume ID</summary>
|
||||
public virtual long VolumeId => default;
|
||||
/// <summary>Local ID</summary>
|
||||
public virtual int LocalId => default;
|
||||
/// <summary>Secret</summary>
|
||||
public virtual long Secret => default;
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +59,7 @@ namespace TL
|
|||
{
|
||||
/// <summary>Random message ID, assigned by the author of message.<br/>Must be equal to the ID passed to sending method.</summary>
|
||||
public long random_id;
|
||||
/// <summary>Random bytes, removed in layer 17.</summary>
|
||||
public byte[] random_bytes;
|
||||
/// <summary>Message text</summary>
|
||||
public string message;
|
||||
|
|
@ -67,6 +72,7 @@ namespace TL
|
|||
public override string Message => message;
|
||||
/// <summary>Media content</summary>
|
||||
public override DecryptedMessageMedia Media => media;
|
||||
/// <summary>Random bytes, removed in layer 17.</summary>
|
||||
public override byte[] RandomBytes => random_bytes;
|
||||
}
|
||||
/// <summary>Contents of an encrypted service message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageService"/></para></summary>
|
||||
|
|
@ -75,12 +81,14 @@ namespace TL
|
|||
{
|
||||
/// <summary>Random message ID, assigned by the message author.<br/>Must be equal to the ID passed to the sending method.</summary>
|
||||
public long random_id;
|
||||
/// <summary>Random bytes, removed in Layer 17.</summary>
|
||||
public byte[] random_bytes;
|
||||
/// <summary>Action relevant to the service message</summary>
|
||||
public DecryptedMessageAction action;
|
||||
|
||||
/// <summary>Random message ID, assigned by the message author.<br/>Must be equal to the ID passed to the sending method.</summary>
|
||||
public override long RandomId => random_id;
|
||||
/// <summary>Random bytes, removed in Layer 17.</summary>
|
||||
public override byte[] RandomBytes => random_bytes;
|
||||
/// <summary>Action relevant to the service message</summary>
|
||||
public override DecryptedMessageAction Action => action;
|
||||
|
|
@ -167,6 +175,7 @@ namespace TL
|
|||
public int thumb_w;
|
||||
/// <summary>Thumbnail height</summary>
|
||||
public int thumb_h;
|
||||
/// <summary>File name, moved to <c>attributes</c> in Layer 45.</summary>
|
||||
public string file_name;
|
||||
/// <summary>File MIME-type</summary>
|
||||
public string mime_type;
|
||||
|
|
@ -498,25 +507,38 @@ namespace TL
|
|||
[TLDef(0x7C596B46)]
|
||||
public sealed partial class FileLocationUnavailable : FileLocationBase
|
||||
{
|
||||
/// <summary>Volume ID</summary>
|
||||
public long volume_id;
|
||||
/// <summary>Local ID</summary>
|
||||
public int local_id;
|
||||
/// <summary>Secret</summary>
|
||||
public long secret;
|
||||
|
||||
/// <summary>Volume ID</summary>
|
||||
public override long VolumeId => volume_id;
|
||||
/// <summary>Local ID</summary>
|
||||
public override int LocalId => local_id;
|
||||
/// <summary>Secret</summary>
|
||||
public override long Secret => secret;
|
||||
}
|
||||
/// <summary>File location. <para>See <a href="https://corefork.telegram.org/constructor/fileLocation"/></para></summary>
|
||||
[TLDef(0x53D69076)]
|
||||
public sealed partial class FileLocation : FileLocationBase
|
||||
{
|
||||
/// <summary>DC ID</summary>
|
||||
public int dc_id;
|
||||
/// <summary>Volume ID</summary>
|
||||
public long volume_id;
|
||||
/// <summary>Local ID</summary>
|
||||
public int local_id;
|
||||
/// <summary>Secret</summary>
|
||||
public long secret;
|
||||
|
||||
/// <summary>Volume ID</summary>
|
||||
public override long VolumeId => volume_id;
|
||||
/// <summary>Local ID</summary>
|
||||
public override int LocalId => local_id;
|
||||
/// <summary>Secret</summary>
|
||||
public override long Secret => secret;
|
||||
}
|
||||
}
|
||||
|
|
@ -775,7 +797,6 @@ namespace TL
|
|||
{
|
||||
/// <summary>Field <see cref="reply_to_random_id"/> has a value</summary>
|
||||
has_reply_to_random_id = 0x8,
|
||||
/// <summary>Whether this is a silent message (no notification triggered)</summary>
|
||||
silent = 0x20,
|
||||
/// <summary>Field <see cref="entities"/> has a value</summary>
|
||||
has_entities = 0x80,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace TL
|
|||
{
|
||||
public static partial class Layer
|
||||
{
|
||||
public const int Version = 206; // fetched 02/07/2025 12:40:29
|
||||
public const int Version = 216; // fetched 10/10/2025 20:01:17
|
||||
internal const int SecretChats = 144;
|
||||
internal const int MTProto2 = 73;
|
||||
internal const uint VectorCtor = 0x1CB5C415;
|
||||
|
|
@ -41,7 +41,9 @@ namespace TL
|
|||
[0x3BCBF734] = typeof(DhGenOk),
|
||||
[0x46DC1FB9] = typeof(DhGenRetry),
|
||||
[0xA69DAE02] = typeof(DhGenFail),
|
||||
[0x7ABE77EC] = typeof(Methods.Ping),
|
||||
[0xF660E1D4] = typeof(DestroyAuthKeyOk),
|
||||
[0x0A9F2259] = typeof(DestroyAuthKeyNone),
|
||||
[0xEA109B13] = typeof(DestroyAuthKeyFail),
|
||||
[0x62D6B459] = typeof(MsgsAck),
|
||||
[0xA7EFF811] = typeof(BadMsgNotification),
|
||||
[0xEDAB447B] = typeof(BadServerSalt),
|
||||
|
|
@ -66,6 +68,7 @@ namespace TL
|
|||
[0x37982646] = typeof(IpPortSecret),
|
||||
[0x4679B65F] = typeof(AccessPointRule),
|
||||
[0x5A592A6C] = typeof(Help_ConfigSimple),
|
||||
[0x7ABE77EC] = typeof(Methods.Ping),
|
||||
// from TL.SchemaExtensions:
|
||||
[0x3FEDD339] = typeof(True),
|
||||
[0xC4B9F9BB] = typeof(Error),
|
||||
|
|
@ -140,7 +143,7 @@ namespace TL
|
|||
[0xFE685355] = typeof(Channel),
|
||||
[0x17D493D5] = typeof(ChannelForbidden),
|
||||
[0x2633421B] = typeof(ChatFull),
|
||||
[0xE07429DE] = typeof(ChannelFull),
|
||||
[0xE4E0B29D] = typeof(ChannelFull),
|
||||
[0xC02D4007] = typeof(ChatParticipant),
|
||||
[0xE46BCEE4] = typeof(ChatParticipantCreator),
|
||||
[0xA0933F5B] = typeof(ChatParticipantAdmin),
|
||||
|
|
@ -197,7 +200,7 @@ namespace TL
|
|||
[0x502F92F7] = typeof(MessageActionInviteToGroupCall),
|
||||
[0x3C134D7B] = typeof(MessageActionSetMessagesTTL),
|
||||
[0xB3A07661] = typeof(MessageActionGroupCallScheduled),
|
||||
[0xAA786345] = typeof(MessageActionSetChatTheme),
|
||||
[0xB91BBD3A] = typeof(MessageActionSetChatTheme),
|
||||
[0xEBBCA3CB] = typeof(MessageActionChatJoinedByRequest),
|
||||
[0x47DD8079] = typeof(MessageActionWebViewDataSentMe),
|
||||
[0xB4C38CB5] = typeof(MessageActionWebViewDataSent),
|
||||
|
|
@ -215,8 +218,8 @@ namespace TL
|
|||
[0x41B3E202] = typeof(MessageActionPaymentRefunded),
|
||||
[0x45D5B021] = typeof(MessageActionGiftStars),
|
||||
[0xB00C47A2] = typeof(MessageActionPrizeStars),
|
||||
[0x4717E8A4] = typeof(MessageActionStarGift),
|
||||
[0x2E3AE60E] = typeof(MessageActionStarGiftUnique),
|
||||
[0xF24DE7FA] = typeof(MessageActionStarGift),
|
||||
[0x95728543] = typeof(MessageActionStarGiftUnique),
|
||||
[0xAC1F1FCD] = typeof(MessageActionPaidMessagesRefunded),
|
||||
[0x84B88578] = typeof(MessageActionPaidMessagesPrice),
|
||||
[0x2FFE2F7A] = typeof(MessageActionConferenceCall),
|
||||
|
|
@ -226,6 +229,7 @@ namespace TL
|
|||
[0x95DDCF69] = typeof(MessageActionSuggestedPostSuccess),
|
||||
[0x69F916F8] = typeof(MessageActionSuggestedPostRefund),
|
||||
[0xA8A3C699] = typeof(MessageActionGiftTon),
|
||||
[0x2C8F2A25] = typeof(MessageActionSuggestBirthday),
|
||||
[0xD58A08C6] = typeof(Dialog),
|
||||
[0x71BD134C] = typeof(DialogFolder),
|
||||
[0x2331B22D] = typeof(PhotoEmpty),
|
||||
|
|
@ -240,7 +244,7 @@ namespace TL
|
|||
[0xB2A2F663] = typeof(GeoPoint),
|
||||
[0x5E002502] = typeof(Auth_SentCode),
|
||||
[0x2390FE44] = typeof(Auth_SentCodeSuccess),
|
||||
[0xD7CEF980] = typeof(Auth_SentCodePaymentRequired),
|
||||
[0xE0955A3C] = typeof(Auth_SentCodePaymentRequired),
|
||||
[0x2EA2C0D4] = typeof(Auth_Authorization),
|
||||
[0x44747E9A] = typeof(Auth_AuthorizationSignUpRequired),
|
||||
[0xB434E2B8] = typeof(Auth_ExportedAuthorization),
|
||||
|
|
@ -254,7 +258,7 @@ namespace TL
|
|||
[0xF47741F7] = typeof(PeerSettings),
|
||||
[0xA437C3ED] = typeof(WallPaper),
|
||||
[0xE0804116] = typeof(WallPaperNoFile),
|
||||
[0x99E78045] = typeof(UserFull),
|
||||
[0xA02BC13E] = typeof(UserFull),
|
||||
[0x145ADE0B] = typeof(Contact),
|
||||
[0xC13E3C50] = typeof(ImportedContact),
|
||||
[0x16D9703B] = typeof(ContactStatus),
|
||||
|
|
@ -266,8 +270,8 @@ namespace TL
|
|||
[0x15BA6C40] = typeof(Messages_Dialogs),
|
||||
[0x71E094F3] = typeof(Messages_DialogsSlice),
|
||||
[0xF0E3E596] = typeof(Messages_DialogsNotModified),
|
||||
[0x8C718E87] = typeof(Messages_Messages),
|
||||
[0x3A54685E] = typeof(Messages_MessagesSlice),
|
||||
[0x1D73E7EA] = typeof(Messages_Messages),
|
||||
[0x5F206716] = typeof(Messages_MessagesSlice),
|
||||
[0xC776BA4E] = typeof(Messages_ChannelMessages),
|
||||
[0x74535F21] = typeof(Messages_MessagesNotModified),
|
||||
[0x64FF9FD5] = typeof(Messages_Chats),
|
||||
|
|
@ -294,7 +298,7 @@ namespace TL
|
|||
[0x1F2B0AFD] = typeof(UpdateNewMessage),
|
||||
[0x4E90BFD6] = typeof(UpdateMessageID),
|
||||
[0xA20DB0E5] = typeof(UpdateDeleteMessages),
|
||||
[0xC01E857F] = typeof(UpdateUserTyping),
|
||||
[0x2A17BF5C] = typeof(UpdateUserTyping),
|
||||
[0x83487AF0] = typeof(UpdateChatUserTyping),
|
||||
[0x07761198] = typeof(UpdateChatParticipants),
|
||||
[0xE5BDF8DE] = typeof(UpdateUserStatus),
|
||||
|
|
@ -311,7 +315,7 @@ namespace TL
|
|||
[0xEBE46819] = typeof(UpdateServiceNotification),
|
||||
[0xEE3B272A] = typeof(UpdatePrivacy),
|
||||
[0x05492A13] = typeof(UpdateUserPhone),
|
||||
[0x9C974FDF] = typeof(UpdateReadHistoryInbox),
|
||||
[0x9E84BC99] = typeof(UpdateReadHistoryInbox),
|
||||
[0x2F2F21BF] = typeof(UpdateReadHistoryOutbox),
|
||||
[0x7F891213] = typeof(UpdateWebPage),
|
||||
[0xF8227181] = typeof(UpdateReadMessagesContents),
|
||||
|
|
@ -398,8 +402,6 @@ namespace TL
|
|||
[0x6F7863F4] = typeof(UpdateRecentReactions),
|
||||
[0x86FCCF85] = typeof(UpdateMoveStickerSetToTop),
|
||||
[0xD5A41724] = typeof(UpdateMessageExtendedMedia),
|
||||
[0x192EFBE3] = typeof(UpdateChannelPinnedTopic),
|
||||
[0xFE198602] = typeof(UpdateChannelPinnedTopics),
|
||||
[0x20529438] = typeof(UpdateUser),
|
||||
[0xEC05B097] = typeof(UpdateAutoSaveSettings),
|
||||
[0x75B3B798] = typeof(UpdateStory),
|
||||
|
|
@ -436,6 +438,10 @@ namespace TL
|
|||
[0x77B0E372] = typeof(UpdateReadMonoForumInbox),
|
||||
[0xA4A79376] = typeof(UpdateReadMonoForumOutbox),
|
||||
[0x9F812B08] = typeof(UpdateMonoForumNoPaidException),
|
||||
[0x78C314E0] = typeof(UpdateGroupCallMessage),
|
||||
[0xC957A766] = typeof(UpdateGroupCallEncryptedMessage),
|
||||
[0x683B2C52] = typeof(UpdatePinnedForumTopic),
|
||||
[0xDEF143D0] = typeof(UpdatePinnedForumTopics),
|
||||
[0xA56C2A3E] = typeof(Updates_State),
|
||||
[0x5D75A138] = typeof(Updates_DifferenceEmpty),
|
||||
[0x00F49CA0] = typeof(Updates_Difference),
|
||||
|
|
@ -505,6 +511,7 @@ namespace TL
|
|||
[0xB05AC6B1] = typeof(SendMessageChooseStickerAction),
|
||||
[0x25972BCB] = typeof(SendMessageEmojiInteraction),
|
||||
[0xB665902E] = typeof(SendMessageEmojiInteractionSeen),
|
||||
[0x376D975C] = typeof(SendMessageTextDraftAction),
|
||||
[0xB3134D9D] = typeof(Contacts_Found),
|
||||
[0x0D09E07B] = typeof(InputPrivacyValueAllowContacts),
|
||||
[0x184B35CE] = typeof(InputPrivacyValueAllowAll),
|
||||
|
|
@ -1008,6 +1015,7 @@ namespace TL
|
|||
[0x2E94C3E7] = typeof(WebPageAttributeStory),
|
||||
[0x50CC03D3] = typeof(WebPageAttributeStickerSet),
|
||||
[0xCF6F6DB8] = typeof(WebPageAttributeUniqueStarGift),
|
||||
[0x31CAD303] = typeof(WebPageAttributeStarGiftCollection),
|
||||
[0x4899484E] = typeof(Messages_VotesList),
|
||||
[0xF568028A] = typeof(BankCardOpenUrl),
|
||||
[0x3E24E573] = typeof(Payments_BankCardData),
|
||||
|
|
@ -1039,7 +1047,7 @@ namespace TL
|
|||
[0x455B853D] = typeof(MessageViews),
|
||||
[0xB6C4F543] = typeof(Messages_MessageViews),
|
||||
[0xA6341782] = typeof(Messages_DiscussionMessage),
|
||||
[0xAFBC09DB] = typeof(MessageReplyHeader),
|
||||
[0x6917560B] = typeof(MessageReplyHeader),
|
||||
[0x0E5AF939] = typeof(MessageReplyStoryHeader),
|
||||
[0x83D60FC2] = typeof(MessageReplies),
|
||||
[0xE8FD8014] = typeof(PeerBlocked),
|
||||
|
|
@ -1078,6 +1086,10 @@ namespace TL
|
|||
[0xE3779861] = typeof(Account_ResetPasswordFailedWait),
|
||||
[0xE9EFFC7D] = typeof(Account_ResetPasswordRequestedWait),
|
||||
[0xE926D63E] = typeof(Account_ResetPasswordOk),
|
||||
[0xC3DFFC04] = typeof(ChatTheme),
|
||||
[0x3458F9C8] = typeof(ChatThemeUniqueGift),
|
||||
[0xE011E1C4] = null,//Account_ChatThemesNotModified
|
||||
[0xBE098173] = typeof(Account_ChatThemes),
|
||||
[0x7DBF8673] = typeof(SponsoredMessage),
|
||||
[0xFFDA656D] = typeof(Messages_SponsoredMessages),
|
||||
[0x1839490F] = null,//Messages_SponsoredMessagesEmpty
|
||||
|
|
@ -1128,7 +1140,10 @@ namespace TL
|
|||
[0x4A5F5BD9] = typeof(InputInvoiceStarGiftTransfer),
|
||||
[0xDABAB2EF] = typeof(InputInvoicePremiumGiftStars),
|
||||
[0xF4997E42] = typeof(InputInvoiceBusinessBotTransferStars),
|
||||
[0x63CBC38C] = typeof(InputInvoiceStarGiftResale),
|
||||
[0xC39F5324] = typeof(InputInvoiceStarGiftResale),
|
||||
[0x9A0B48B8] = typeof(InputInvoiceStarGiftPrepaidUpgrade),
|
||||
[0x3E77F614] = typeof(InputInvoicePremiumAuthCode),
|
||||
[0x0923D8D1] = typeof(InputInvoiceStarGiftDropOriginalDetails),
|
||||
[0xAED0CBD9] = typeof(Payments_ExportedInvoice),
|
||||
[0xCFB9D957] = typeof(Messages_TranscribedAudio),
|
||||
[0x5334759C] = typeof(Help_PremiumPromo),
|
||||
|
|
@ -1136,7 +1151,7 @@ namespace TL
|
|||
[0x616F7FE8] = typeof(InputStorePaymentGiftPremium),
|
||||
[0xFB790393] = typeof(InputStorePaymentPremiumGiftCode),
|
||||
[0x160544CA] = typeof(InputStorePaymentPremiumGiveaway),
|
||||
[0xDDDD0F56] = typeof(InputStorePaymentStarsTopup),
|
||||
[0xF9A2A6CB] = typeof(InputStorePaymentStarsTopup),
|
||||
[0x1D741EF7] = typeof(InputStorePaymentStarsGift),
|
||||
[0x751F08FA] = typeof(InputStorePaymentStarsGiveaway),
|
||||
[0x9BB2636D] = typeof(InputStorePaymentAuthCode),
|
||||
|
|
@ -1171,7 +1186,7 @@ namespace TL
|
|||
[0xFCFEB29C] = typeof(StickerKeyword),
|
||||
[0xB4073647] = typeof(Username),
|
||||
[0x023F109B] = typeof(ForumTopicDeleted),
|
||||
[0x71701DA9] = typeof(ForumTopic),
|
||||
[0xCDFF0ECA] = typeof(ForumTopic),
|
||||
[0x367617D3] = typeof(Messages_ForumTopics),
|
||||
[0x43B46B20] = typeof(DefaultHistoryTTL),
|
||||
[0x41BF109B] = typeof(ExportedContactToken),
|
||||
|
|
@ -1213,7 +1228,7 @@ namespace TL
|
|||
[0x8D595CD6] = typeof(StoryViews),
|
||||
[0x51E6EE4F] = typeof(StoryItemDeleted),
|
||||
[0xFFADC913] = typeof(StoryItemSkipped),
|
||||
[0x79B26A24] = typeof(StoryItem),
|
||||
[0xEDF164F1] = typeof(StoryItem),
|
||||
[0x1158FE3E] = typeof(Stories_AllStoriesNotModified),
|
||||
[0x6EFC5E81] = typeof(Stories_AllStories),
|
||||
[0x63C3DD0A] = typeof(Stories_Stories),
|
||||
|
|
@ -1222,7 +1237,7 @@ namespace TL
|
|||
[0xBD74CF49] = typeof(StoryViewPublicRepost),
|
||||
[0x59D78FC5] = typeof(Stories_StoryViewsList),
|
||||
[0xDE9EED1D] = typeof(Stories_StoryViews),
|
||||
[0xB07038B0] = typeof(InputReplyToMessage),
|
||||
[0x869FBE10] = typeof(InputReplyToMessage),
|
||||
[0x5881323A] = typeof(InputReplyToStory),
|
||||
[0x69D66C45] = typeof(InputReplyToMonoForum),
|
||||
[0x3FC9053B] = typeof(ExportedStoryLink),
|
||||
|
|
@ -1259,6 +1274,8 @@ namespace TL
|
|||
[0xEDF3ADD0] = typeof(PublicForwardStory),
|
||||
[0x93037E20] = typeof(Stats_PublicForwards),
|
||||
[0xB54B5ACF] = typeof(PeerColor),
|
||||
[0xB9C0639A] = typeof(PeerColorCollectible),
|
||||
[0xB8EA86A9] = typeof(InputPeerColorCollectible),
|
||||
[0x26219A58] = typeof(Help_PeerColorSet),
|
||||
[0x767D61EB] = typeof(Help_PeerColorProfileSet),
|
||||
[0xADEC6EBE] = typeof(Help_PeerColorOption),
|
||||
|
|
@ -1361,10 +1378,10 @@ namespace TL
|
|||
[0x4BA3A95A] = typeof(MessageReactor),
|
||||
[0x94CE852A] = typeof(StarsGiveawayOption),
|
||||
[0x54236209] = typeof(StarsGiveawayWinnersOption),
|
||||
[0xC62ACA28] = typeof(StarGift),
|
||||
[0x6411DB89] = typeof(StarGiftUnique),
|
||||
[0x80AC53C3] = typeof(StarGift),
|
||||
[0xB0BF741B] = typeof(StarGiftUnique),
|
||||
[0xA388A368] = null,//Payments_StarGiftsNotModified
|
||||
[0x901689EA] = typeof(Payments_StarGifts),
|
||||
[0x2ED82995] = typeof(Payments_StarGifts),
|
||||
[0x7903E3D9] = typeof(MessageReportOption),
|
||||
[0xF0E4E0B6] = typeof(ReportResultChooseOption),
|
||||
[0x6F09AC31] = typeof(ReportResultAddComment),
|
||||
|
|
@ -1386,12 +1403,12 @@ namespace TL
|
|||
[0x13ACFF19] = typeof(StarGiftAttributePattern),
|
||||
[0xD93D859C] = typeof(StarGiftAttributeBackdrop),
|
||||
[0xE0BFF26C] = typeof(StarGiftAttributeOriginalDetails),
|
||||
[0x167BD90B] = typeof(Payments_StarGiftUpgradePreview),
|
||||
[0x3DE1DFED] = typeof(Payments_StarGiftUpgradePreview),
|
||||
[0x62D706B8] = typeof(Users_Users),
|
||||
[0x315A4974] = typeof(Users_UsersSlice),
|
||||
[0xCAA2F60B] = typeof(Payments_UniqueStarGift),
|
||||
[0xB53E8B21] = typeof(Messages_WebPagePreview),
|
||||
[0xDFDA0499] = typeof(SavedStarGift),
|
||||
[0x416C56E8] = typeof(Payments_UniqueStarGift),
|
||||
[0x8C9A88AC] = typeof(Messages_WebPagePreview),
|
||||
[0x8983A452] = typeof(SavedStarGift),
|
||||
[0x95F389B1] = typeof(Payments_SavedStarGifts),
|
||||
[0x69279795] = typeof(InputSavedStarGiftUser),
|
||||
[0xF101AA7F] = typeof(InputSavedStarGiftChat),
|
||||
|
|
@ -1420,6 +1437,25 @@ namespace TL
|
|||
[0x49B92A26] = typeof(TodoList),
|
||||
[0x4CC120B7] = typeof(TodoCompletion),
|
||||
[0x0E8E37E5] = typeof(SuggestedPost),
|
||||
[0x1B0E4F07] = typeof(StarsRating),
|
||||
[0x9D6B13B0] = typeof(StarGiftCollection),
|
||||
[0xA0BA4F17] = null,//Payments_StarGiftCollectionsNotModified
|
||||
[0x8A2932F3] = typeof(Payments_StarGiftCollections),
|
||||
[0x9325705A] = typeof(StoryAlbum),
|
||||
[0x564EDAEB] = null,//Stories_AlbumsNotModified
|
||||
[0xC3987A3A] = typeof(Stories_Albums),
|
||||
[0x3E0B5B6A] = typeof(SearchPostsFlood),
|
||||
[0x512FE446] = typeof(Payments_UniqueStarGiftValueInfo),
|
||||
[0xE3878AA4] = typeof(Users_SavedMusicNotModified),
|
||||
[0x34A2F297] = typeof(Users_SavedMusic),
|
||||
[0x4FC81D6E] = null,//Account_SavedMusicIdsNotModified
|
||||
[0x998D6636] = typeof(Account_SavedMusicIds),
|
||||
[0x374FA7AD] = typeof(Payments_CheckCanSendGiftResultOk),
|
||||
[0xD5E58274] = typeof(Payments_CheckCanSendGiftResultFail),
|
||||
[0x83268483] = null,//InputChatThemeEmpty
|
||||
[0xC93DE95C] = typeof(InputChatTheme),
|
||||
[0x87E5DFE4] = typeof(InputChatThemeUniqueGift),
|
||||
[0x99EA331D] = typeof(StarGiftUpgradePrice),
|
||||
// from TL.Secret:
|
||||
[0x6ABD9782] = typeof(Layer143.DecryptedMessageMediaDocument),
|
||||
[0x020DF5D0] = typeof(Layer101.MessageEntityBlockquote),
|
||||
|
|
@ -1539,6 +1575,7 @@ namespace TL
|
|||
[typeof(ChatReactions)] = 0xEAFC32BC, //chatReactionsNone
|
||||
[typeof(Messages_Reactions)] = 0xB06FDBDF, //messages.reactionsNotModified
|
||||
// from TL.Secret:
|
||||
[typeof(Account_ChatThemes)] = 0xE011E1C4, //account.chatThemesNotModified
|
||||
[typeof(EmojiStatusBase)] = 0x2DE11AAE, //emojiStatusEmpty
|
||||
[typeof(EmojiList)] = 0x481EADFA, //emojiListNotModified
|
||||
[typeof(Messages_EmojiGroups)] = 0x6FB4AD87, //messages.emojiGroupsNotModified
|
||||
|
|
@ -1553,6 +1590,10 @@ namespace TL
|
|||
[typeof(PaidReactionPrivacy)] = 0x206AD49E, //paidReactionPrivacyDefault
|
||||
[typeof(RequirementToContact)] = 0x050A9839, //requirementToContactEmpty
|
||||
[typeof(Contacts_SponsoredPeers)] = 0xEA32B4B1, //contacts.sponsoredPeersEmpty
|
||||
[typeof(Payments_StarGiftCollections)] = 0xA0BA4F17, //payments.starGiftCollectionsNotModified
|
||||
[typeof(Stories_Albums)] = 0x564EDAEB, //stories.albumsNotModified
|
||||
[typeof(Account_SavedMusicIds)] = 0x4FC81D6E, //account.savedMusicIdsNotModified
|
||||
[typeof(InputChatThemeBase)] = 0x83268483, //inputChatThemeEmpty
|
||||
[typeof(DecryptedMessageMedia)] = 0x089F5C4A, //decryptedMessageMediaEmpty
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,6 @@ namespace TL
|
|||
{
|
||||
public abstract long ID { get; }
|
||||
protected internal abstract IPeerInfo UserOrChat(Dictionary<long, User> users, Dictionary<long, ChatBase> chats);
|
||||
public static implicit operator long(Peer peer) => peer.ID;
|
||||
}
|
||||
partial class PeerUser
|
||||
{
|
||||
|
|
@ -216,7 +215,7 @@ namespace TL
|
|||
/// <remarks>a <c>null</c> value means <a href="https://corefork.telegram.org/constructor/userStatusEmpty">userStatusEmpty</a> = last seen a long time ago, more than a month (or blocked/deleted users)</remarks>
|
||||
partial class UserStatus { internal abstract TimeSpan LastSeenAgo { get; } }
|
||||
partial class UserStatusOnline { internal override TimeSpan LastSeenAgo => TimeSpan.Zero; }
|
||||
partial class UserStatusOffline { internal override TimeSpan LastSeenAgo => DateTime.UtcNow - new DateTime((was_online + 62135596800L) * 10000000, DateTimeKind.Utc); }
|
||||
partial class UserStatusOffline { internal override TimeSpan LastSeenAgo => DateTime.UtcNow - was_online; }
|
||||
/// <remarks>covers anything between 1 second and 2-3 days</remarks>
|
||||
partial class UserStatusRecently { internal override TimeSpan LastSeenAgo => TimeSpan.FromDays(1); }
|
||||
/// <remarks>between 2-3 and seven days</remarks>
|
||||
|
|
@ -690,8 +689,8 @@ namespace TL
|
|||
{
|
||||
System.Text.Json.JsonValueKind.True or
|
||||
System.Text.Json.JsonValueKind.False => new JsonBool { value = elem.GetBoolean() },
|
||||
System.Text.Json.JsonValueKind.Object => new JsonObject { value = elem.EnumerateObject().Select(FromJsonProperty).ToArray() },
|
||||
System.Text.Json.JsonValueKind.Array => new JsonArray { value = elem.EnumerateArray().Select(FromJsonElement).ToArray() },
|
||||
System.Text.Json.JsonValueKind.Object => new JsonObject { value = [.. elem.EnumerateObject().Select(FromJsonProperty)] },
|
||||
System.Text.Json.JsonValueKind.Array => new JsonArray { value = [.. elem.EnumerateArray().Select(FromJsonElement)] },
|
||||
System.Text.Json.JsonValueKind.String => new JsonString { value = elem.GetString() },
|
||||
System.Text.Json.JsonValueKind.Number => new JsonNumber { value = elem.GetDouble() },
|
||||
_ => new JsonNull(),
|
||||
|
|
@ -710,7 +709,7 @@ namespace TL
|
|||
sb.Append(i == 0 ? "" : ",").Append(value[i]);
|
||||
return sb.Append(']').ToString();
|
||||
}
|
||||
public object[] ToNativeArray() => value.Select(v => v.ToNative()).ToArray();
|
||||
public object[] ToNativeArray() => [.. value.Select(v => v.ToNative())];
|
||||
public override object ToNative()
|
||||
{
|
||||
if (value.Length == 0) return Array.Empty<object>();
|
||||
|
|
|
|||
69
src/TL.cs
69
src/TL.cs
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
|
|
@ -16,7 +17,7 @@ namespace TL
|
|||
#else
|
||||
public interface IObject { }
|
||||
#endif
|
||||
public interface IMethod<ReturnType> : IObject { }
|
||||
public interface IMethod<out ReturnType> : IObject { }
|
||||
public interface IPeerResolver { IPeerInfo UserOrChat(Peer peer); }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
|
|
@ -48,6 +49,15 @@ namespace TL
|
|||
|
||||
public static class Serialization
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static byte[] ToBytes<T>(this T obj) where T : IObject
|
||||
{
|
||||
using var ms = new MemoryStream(384);
|
||||
using var writer = new BinaryWriter(ms);
|
||||
writer.WriteTLObject(obj);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public static void WriteTLObject<T>(this BinaryWriter writer, T obj) where T : IObject
|
||||
{
|
||||
if (obj == null) { writer.WriteTLNull(typeof(T)); return; }
|
||||
|
|
@ -105,6 +115,17 @@ namespace TL
|
|||
#endif
|
||||
}
|
||||
|
||||
public static IMethod<X> ReadTLMethod<X>(this BinaryReader reader)
|
||||
{
|
||||
uint ctorNb = reader.ReadUInt32();
|
||||
if (!Layer.Methods.TryGetValue(ctorNb, out var ctor))
|
||||
throw new WTelegram.WTException($"Cannot find method for ctor #{ctorNb:x}");
|
||||
var method = ctor?.Invoke(reader);
|
||||
if (method is IMethod<bool> && typeof(X) == typeof(object))
|
||||
method = new BoolMethod { query = method };
|
||||
return (IMethod<X>)method;
|
||||
}
|
||||
|
||||
internal static void WriteTLValue(this BinaryWriter writer, object value, Type valueType)
|
||||
{
|
||||
if (value == null)
|
||||
|
|
@ -197,10 +218,10 @@ namespace TL
|
|||
foreach (var msg in messages)
|
||||
{
|
||||
writer.Write(msg.msg_id);
|
||||
writer.Write(msg.seq_no);
|
||||
writer.Write(msg.seqno);
|
||||
var patchPos = writer.BaseStream.Position;
|
||||
writer.Write(0); // patched below
|
||||
if ((msg.seq_no & 1) != 0)
|
||||
if ((msg.seqno & 1) != 0)
|
||||
WTelegram.Helpers.Log(1, $" → {msg.body.GetType().Name.TrimEnd('_'),-38} #{(short)msg.msg_id.GetHashCode():X4}");
|
||||
else
|
||||
WTelegram.Helpers.Log(1, $" → {msg.body.GetType().Name.TrimEnd('_'),-38}");
|
||||
|
|
@ -222,6 +243,21 @@ namespace TL
|
|||
writer.WriteTLValue(array.GetValue(i), elementType);
|
||||
}
|
||||
|
||||
internal static void WriteTLRawVector(this BinaryWriter writer, Array array, int elementSize)
|
||||
{
|
||||
var startPos = writer.BaseStream.Position;
|
||||
int count = array.Length;
|
||||
var elementType = array.GetType().GetElementType();
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
{
|
||||
writer.BaseStream.Position = startPos + i * elementSize;
|
||||
writer.WriteTLValue(array.GetValue(i), elementType);
|
||||
}
|
||||
writer.BaseStream.Position = startPos;
|
||||
writer.Write(count);
|
||||
writer.BaseStream.Position = startPos + count * elementSize + 4;
|
||||
}
|
||||
|
||||
internal static List<T> ReadTLRawVector<T>(this BinaryReader reader, uint ctorNb)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
|
|
@ -289,13 +325,14 @@ namespace TL
|
|||
}
|
||||
|
||||
internal static void WriteTLStamp(this BinaryWriter writer, DateTime datetime)
|
||||
=> writer.Write(datetime == DateTime.MaxValue ? int.MaxValue : (int)(datetime.ToUniversalTime().Ticks / 10000000 - 62135596800L));
|
||||
=> writer.Write((int)Math.Min(Math.Max(datetime.ToUniversalTime().Ticks / 10000000 - 62135596800L, 0), int.MaxValue));
|
||||
|
||||
internal static DateTime ReadTLStamp(this BinaryReader reader)
|
||||
internal static DateTime ReadTLStamp(this BinaryReader reader) => reader.ReadInt32() switch
|
||||
{
|
||||
int unixstamp = reader.ReadInt32();
|
||||
return unixstamp == int.MaxValue ? DateTime.MaxValue : new((unixstamp + 62135596800L) * 10000000, DateTimeKind.Utc);
|
||||
}
|
||||
<= 0 => default,
|
||||
int.MaxValue => DateTime.MaxValue,
|
||||
int unixstamp => new((unixstamp + 62135596800L) * 10000000, DateTimeKind.Utc)
|
||||
};
|
||||
|
||||
internal static void WriteTLString(this BinaryWriter writer, string str)
|
||||
{
|
||||
|
|
@ -428,10 +465,10 @@ namespace TL
|
|||
}
|
||||
|
||||
[TLDef(0x5BB8E511)] //message#5bb8e511 msg_id:long seqno:int bytes:int body:Object = Message
|
||||
public sealed partial class _Message(long msgId, int seqNo, IObject obj) : IObject
|
||||
public sealed partial class _Message(long msgId, int seqno, IObject obj) : IObject
|
||||
{
|
||||
public long msg_id = msgId;
|
||||
public int seq_no = seqNo;
|
||||
public int seqno = seqno;
|
||||
public int bytes;
|
||||
public IObject body = obj;
|
||||
}
|
||||
|
|
@ -443,4 +480,16 @@ namespace TL
|
|||
|
||||
[TLDef(0x3072CFA1)] //gzip_packed#3072cfa1 packed_data:bytes = Object
|
||||
public sealed partial class GzipPacked : IObject { public byte[] packed_data; }
|
||||
|
||||
public sealed class Null<X> : IObject
|
||||
{
|
||||
public readonly static Null<X> Instance = new();
|
||||
public void WriteTL(BinaryWriter writer) => writer.WriteTLNull(typeof(X));
|
||||
}
|
||||
|
||||
public sealed class BoolMethod : IMethod<object>
|
||||
{
|
||||
public IObject query;
|
||||
public void WriteTL(BinaryWriter writer) => query.WriteTL(writer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -566,7 +566,7 @@ namespace WTelegram
|
|||
|
||||
/// <summary>Save the current state of the manager to JSON file</summary>
|
||||
/// <param name="statePath">File path to write</param>
|
||||
/// <remarks>Note: This does not save the the content of collected Users/Chats dictionaries</remarks>
|
||||
/// <remarks>Note: This does not save the content of collected Users/Chats dictionaries</remarks>
|
||||
public void SaveState(string statePath)
|
||||
=> System.IO.File.WriteAllText(statePath, System.Text.Json.JsonSerializer.Serialize(State, Helpers.JsonOptions));
|
||||
public static Dictionary<long, MBoxState> LoadState(string statePath) => !System.IO.File.Exists(statePath) ? null
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
<PackageId>WTelegramClient</PackageId>
|
||||
<Authors>Wizou</Authors>
|
||||
<VersionPrefix>0.0.0</VersionPrefix>
|
||||
<VersionSuffix>layer.206</VersionSuffix>
|
||||
<Description>Telegram Client API (MTProto) library written 100% in C# and .NET Standard | Latest API layer: 206
|
||||
<VersionSuffix>layer.216</VersionSuffix>
|
||||
<Description>Telegram Client API (MTProto) library written 100% in C# and .NET Standard | Latest API layer: 216
|
||||
|
||||
Release Notes:
|
||||
$(ReleaseNotes.Replace("|", "%0D%0A").Replace(" - ","%0D%0A- ").Replace(" ", "%0D%0A%0D%0A"))</Description>
|
||||
$(ReleaseNotes)</Description>
|
||||
<Copyright>Copyright © Olivier Marcoux 2021-2025</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://wiz0u.github.io/WTelegramClient</PackageProjectUrl>
|
||||
|
|
@ -27,7 +27,7 @@ $(ReleaseNotes.Replace("|", "%0D%0A").Replace(" - ","%0D%0A- ").Replace(" ", "%
|
|||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>Telegram;MTProto;Client;Api;UserBot</PackageTags>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageReleaseNotes>$(ReleaseNotes.Replace("|", "%0D%0A").Replace(" - ","%0D%0A- ").Replace(" ", "%0D%0A%0D%0A"))</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>$(ReleaseNotes)</PackageReleaseNotes>
|
||||
<NoWarn>NETSDK1138;CS0419;CS1573;CS1591</NoWarn>
|
||||
<DefineConstants>TRACE;OBFUSCATION;MTPG</DefineConstants>
|
||||
<!--<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>-->
|
||||
|
|
|
|||
Loading…
Reference in a new issue