diff --git a/src/Client.cs b/src/Client.cs
index b39974f..9942a44 100644
--- a/src/Client.cs
+++ b/src/Client.cs
@@ -31,10 +31,15 @@ namespace WTelegram
public Config TLConfig { get; private set; }
/// Number of automatic reconnections on connection/reactor failure
public int MaxAutoReconnects { get; set; } = 5;
+ /// Number of seconds under which an error 420 FLOOD_WAIT_X will not be raised and your request will instead be auto-retried after the delay
+ public int FloodRetryThreshold { get; set; } = 60;
/// Is this Client instance the main or a secondary DC session
public bool IsMainDC => (_dcSession?.DataCenter?.id ?? 0) == _session.MainDC;
/// Is this Client currently disconnected?
public bool Disconnected => _tcpClient != null && !(_tcpClient.Client?.Connected ?? false);
+ /// Used to indicate progression of file download/upload
+ /// total size of file in bytes, or 0 if unknown
+ public delegate void ProgressCallback(long transmitted, long totalSize);
private readonly Func _config;
private readonly int _apiId;
@@ -421,8 +426,11 @@ namespace WTelegram
_bareRequest = 0;
}
// TODO: implement an Updates gaps handling system? https://core.telegram.org/api/updates
- var udpatesState = await this.Updates_GetState(); // this call reenables incoming Updates
- OnUpdate(udpatesState);
+ if (IsMainDC)
+ {
+ var udpatesState = await this.Updates_GetState(); // this call reenables incoming Updates
+ OnUpdate(udpatesState);
+ }
}
else
throw;
@@ -772,7 +780,7 @@ namespace WTelegram
else if (rpcError.error_code == 420 && ((number = rpcError.error_message.IndexOf("_WAIT_")) > 0))
{
number = int.Parse(rpcError.error_message[(number + 6)..]);
- if (number <= 60)
+ if (number <= FloodRetryThreshold)
{
await Task.Delay(number * 1000);
goto retry;
@@ -1019,23 +1027,25 @@ namespace WTelegram
return user;
}
-#region TL-Helpers
+ #region TL-Helpers
/// Helper function to upload a file to Telegram
/// Path to the file to upload
+ /// (optional) Callback for tracking the progression of the transfer
/// an or than can be used in various requests
- public Task UploadFileAsync(string pathname)
- => UploadFileAsync(File.OpenRead(pathname), Path.GetFileName(pathname));
+ public Task UploadFileAsync(string pathname, ProgressCallback progress = null)
+ => UploadFileAsync(File.OpenRead(pathname), Path.GetFileName(pathname), progress);
/// Helper function to upload a file to Telegram
/// Content of the file to upload
/// Name of the file
+ /// (optional) Callback for tracking the progression of the transfer
/// an or than can be used in various requests
- public async Task UploadFileAsync(Stream stream, string filename)
+ public async Task UploadFileAsync(Stream stream, string filename, ProgressCallback progress = null)
{
using var md5 = MD5.Create();
using (stream)
{
- long length = stream.Length;
+ long transmitted = 0, length = stream.Length;
var isBig = length >= 10 * 1024 * 1024;
int file_total_parts = (int)((length - 1) / FilePartSize) + 1;
long file_id = Helpers.RandomLong();
@@ -1047,11 +1057,11 @@ namespace WTelegram
var bytes = new byte[Math.Min(FilePartSize, bytesLeft)];
read = await FullReadAsync(stream, bytes, bytes.Length);
await _parallelTransfers.WaitAsync();
+ bytesLeft -= read;
var task = SavePart(file_part, bytes);
lock (tasks) tasks[file_part] = task;
if (!isBig)
md5.TransformBlock(bytes, 0, read, null, 0);
- bytesLeft -= read;
if (read < FilePartSize && bytesLeft != 0) throw new ApplicationException($"Failed to fully read stream ({read},{bytesLeft})");
async Task SavePart(int file_part, byte[] bytes)
@@ -1062,7 +1072,8 @@ namespace WTelegram
await this.Upload_SaveBigFilePart(file_id, file_part, file_total_parts, bytes);
else
await this.Upload_SaveFilePart(file_id, file_part, bytes);
- lock (tasks) tasks.Remove(file_part);
+ lock (tasks) { transmitted += bytes.Length; tasks.Remove(file_part); }
+ progress?.Invoke(transmitted, length);
}
catch (Exception)
{
@@ -1175,23 +1186,25 @@ namespace WTelegram
/// The photo to download
/// Stream to write the file content to. This method does not close/dispose the stream
/// A specific size/version of the photo, or to download the largest version of the photo
+ /// (optional) Callback for tracking the progression of the transfer
/// The file type of the photo
- public async Task DownloadFileAsync(Photo photo, Stream outputStream, PhotoSizeBase photoSize = null)
+ public async Task DownloadFileAsync(Photo photo, Stream outputStream, PhotoSizeBase photoSize = null, ProgressCallback progress = null)
{
photoSize ??= photo.LargestPhotoSize;
var fileLocation = photo.ToFileLocation(photoSize);
- return await DownloadFileAsync(fileLocation, outputStream, photo.dc_id, photoSize.FileSize);
+ return await DownloadFileAsync(fileLocation, outputStream, photo.dc_id, photoSize.FileSize, progress);
}
/// Download a document from Telegram into the outputStream
/// The document to download
/// Stream to write the file content to. This method does not close/dispose the stream
/// A specific size/version of the document thumbnail to download, or to download the document itself
+ /// (optional) Callback for tracking the progression of the transfer
/// MIME type of the document/thumbnail
- public async Task DownloadFileAsync(Document document, Stream outputStream, PhotoSizeBase thumbSize = null)
+ public async Task DownloadFileAsync(Document document, Stream outputStream, PhotoSizeBase thumbSize = null, ProgressCallback progress = null)
{
var fileLocation = document.ToFileLocation(thumbSize);
- var fileType = await DownloadFileAsync(fileLocation, outputStream, document.dc_id, thumbSize?.FileSize ?? document.size);
+ var fileType = await DownloadFileAsync(fileLocation, outputStream, document.dc_id, thumbSize?.FileSize ?? document.size, progress);
return thumbSize == null ? document.mime_type : "image/" + fileType;
}
@@ -1200,14 +1213,16 @@ namespace WTelegram
/// Stream to write file content to. This method does not close/dispose the stream
/// (optional) DC on which the file is stored
/// (optional) Expected file size
+ /// (optional) Callback for tracking the progression of the transfer
/// The file type
- public async Task DownloadFileAsync(InputFileLocationBase fileLocation, Stream outputStream, int fileDC = 0, int fileSize = 0)
+ public async Task DownloadFileAsync(InputFileLocationBase fileLocation, Stream outputStream, int fileDC = 0, int fileSize = 0, ProgressCallback progress = null)
{
Storage_FileType fileType = Storage_FileType.unknown;
var client = fileDC == 0 ? this : await GetClientForDC(fileDC, true);
using var writeSem = new SemaphoreSlim(1);
long streamStartPos = outputStream.Position;
int fileOffset = 0, maxOffsetSeen = 0;
+ long transmitted = 0;
var tasks = new Dictionary();
bool abort = false;
while (!abort)
@@ -1267,6 +1282,7 @@ namespace WTelegram
}
await outputStream.WriteAsync(fileData.bytes, 0, fileData.bytes.Length);
maxOffsetSeen = Math.Max(maxOffsetSeen, offset + fileData.bytes.Length);
+ transmitted += fileData.bytes.Length;
}
catch (Exception)
{
@@ -1276,6 +1292,7 @@ namespace WTelegram
finally
{
writeSem.Release();
+ progress?.Invoke(transmitted, fileSize);
}
}
lock (tasks) tasks.Remove(offset);