Skip to content

Commit bc56e0a

Browse files
committed
feat: copy session to multi client
1 parent 382ef95 commit bc56e0a

2 files changed

Lines changed: 131 additions & 23 deletions

File tree

TelegramDownloader/Data/TelegramService.cs

Lines changed: 128 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,49 +91,142 @@ private void newClient()
9191
if (downloadClient != null)
9292
return downloadClient;
9393

94-
var mainSessionPath = UserService.USERDATAFOLDER + "/WTelegram.session";
9594
var downloadSessionPath = UserService.USERDATAFOLDER + "/WTelegram_download.session";
9695

97-
// Copy the main session file if download session doesn't exist
98-
// Use FileShare.ReadWrite to allow reading while the main client has it open
99-
if (!File.Exists(downloadSessionPath) && File.Exists(mainSessionPath))
96+
// Create a new client with its own session file
97+
// It will authenticate using the same credentials as the main client
98+
downloadClient = new WTelegram.Client(
99+
Convert.ToInt32(GeneralConfigStatic.tlconfig?.api_id ?? Environment.GetEnvironmentVariable("api_id")),
100+
GeneralConfigStatic.tlconfig?.hash_id ?? Environment.GetEnvironmentVariable("hash_id"),
101+
downloadSessionPath);
102+
103+
// If download session already exists, try to use it
104+
// Otherwise, it will need to authenticate (which might require user interaction on first use)
105+
if (File.Exists(downloadSessionPath))
100106
{
101107
try
102108
{
103-
// Read with FileShare.ReadWrite to avoid locking conflicts
104-
using (var sourceStream = new FileStream(mainSessionPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
105-
using (var destStream = new FileStream(downloadSessionPath, FileMode.Create, FileAccess.Write, FileShare.None))
106-
{
107-
await sourceStream.CopyToAsync(destStream);
108-
}
109-
_logger.LogInformation("Created download client session from main session");
109+
await downloadClient.LoginUserIfNeeded();
110+
_logger.LogInformation("Download client initialized from existing session");
111+
return downloadClient;
110112
}
111-
catch (Exception copyEx)
113+
catch (Exception loginEx)
112114
{
113-
_logger.LogWarning(copyEx, "Could not copy session file, download client will create new session");
115+
_logger.LogWarning(loginEx, "Could not login download client from existing session, deleting invalid session and using main client");
116+
downloadClient?.Dispose();
117+
downloadClient = null;
118+
// Delete the invalid session file so it won't be tried again
119+
DeleteDownloadSession();
120+
return client;
114121
}
115122
}
123+
else
124+
{
125+
// No existing download session - use main client to avoid auth prompts
126+
_logger.LogInformation("No download session exists, using main client for downloads");
127+
downloadClient?.Dispose();
128+
downloadClient = null;
129+
return client;
130+
}
131+
}
132+
catch (Exception ex)
133+
{
134+
_logger.LogWarning(ex, "Failed to create dedicated download client, falling back to main client");
135+
downloadClient?.Dispose();
136+
downloadClient = null;
137+
return client;
138+
}
139+
finally
140+
{
141+
downloadClientMut.ReleaseMutex();
142+
}
143+
}
116144

117-
downloadClient = new WTelegram.Client(
118-
Convert.ToInt32(GeneralConfigStatic.tlconfig?.api_id ?? Environment.GetEnvironmentVariable("api_id")),
119-
GeneralConfigStatic.tlconfig?.hash_id ?? Environment.GetEnvironmentVariable("hash_id"),
120-
downloadSessionPath);
145+
/// <summary>
146+
/// Copies the main session file to the download session at startup, before Telegram client locks the file.
147+
/// This should be called in Program.cs before the TelegramService is created.
148+
/// </summary>
149+
public static void CopySessionForDownloadClient()
150+
{
151+
var mainSessionPath = UserService.USERDATAFOLDER + "/WTelegram.session";
152+
var downloadSessionPath = UserService.USERDATAFOLDER + "/WTelegram_download.session";
121153

122-
// Login the download client (should auto-login from session)
123-
await downloadClient.LoginUserIfNeeded();
124-
_logger.LogInformation("Download client initialized successfully - separate connection for downloads");
154+
try
155+
{
156+
if (File.Exists(mainSessionPath))
157+
{
158+
// Copy the main session to download session
159+
File.Copy(mainSessionPath, downloadSessionPath, overwrite: true);
160+
Console.WriteLine($"[TelegramService] Copied main session to download session");
161+
}
162+
else
163+
{
164+
Console.WriteLine($"[TelegramService] Main session does not exist, skipping download session copy");
165+
}
166+
}
167+
catch (Exception ex)
168+
{
169+
Console.WriteLine($"[TelegramService] Failed to copy session for download client: {ex.Message}");
170+
// If copy fails, delete the download session to ensure we use main client
171+
DeleteDownloadSession();
172+
}
173+
}
125174

126-
return downloadClient;
175+
/// <summary>
176+
/// Deletes the download session file (used when session becomes invalid)
177+
/// </summary>
178+
public static void DeleteDownloadSession()
179+
{
180+
try
181+
{
182+
var downloadSessionPath = UserService.USERDATAFOLDER + "/WTelegram_download.session";
183+
if (File.Exists(downloadSessionPath))
184+
{
185+
File.Delete(downloadSessionPath);
186+
Console.WriteLine($"[TelegramService] Deleted download session file");
187+
}
127188
}
128189
catch (Exception ex)
129190
{
130-
_logger.LogWarning(ex, "Failed to create dedicated download client, falling back to main client");
131-
return client;
191+
Console.WriteLine($"[TelegramService] Failed to delete download session file: {ex.Message}");
192+
}
193+
}
194+
195+
/// <summary>
196+
/// Checks if an exception indicates an invalid/expired session
197+
/// </summary>
198+
private static bool IsSessionInvalidException(Exception ex)
199+
{
200+
if (ex is RpcException rpcEx)
201+
{
202+
// Common auth errors that indicate session is invalid
203+
return rpcEx.Message.Contains("AUTH_KEY_UNREGISTERED") ||
204+
rpcEx.Message.Contains("AUTH_KEY_INVALID") ||
205+
rpcEx.Message.Contains("AUTH_KEY_DUPLICATED") ||
206+
rpcEx.Message.Contains("SESSION_EXPIRED") ||
207+
rpcEx.Message.Contains("USER_DEACTIVATED");
208+
}
209+
return false;
210+
}
211+
212+
/// <summary>
213+
/// Handles invalid session during download - resets download client and returns main client
214+
/// </summary>
215+
private WTelegram.Client HandleInvalidDownloadSession(Exception ex)
216+
{
217+
_logger.LogWarning(ex, "Download client session is invalid, resetting and using main client");
218+
downloadClientMut.WaitOne();
219+
try
220+
{
221+
downloadClient?.Dispose();
222+
downloadClient = null;
223+
DeleteDownloadSession();
132224
}
133225
finally
134226
{
135227
downloadClientMut.ReleaseMutex();
136228
}
229+
return client;
137230
}
138231

139232
/// <summary>
@@ -854,6 +947,12 @@ public async Task<Byte[]> DownloadFileStream(Message message, long offset, int l
854947
_logger.LogError(ex, "OFFSET_INVALID - CurrentOffset: {Offset}, TotalLimit: {Limit}", currentOffset, totalLimit);
855948
throw;
856949
}
950+
catch (Exception ex) when (IsSessionInvalidException(ex))
951+
{
952+
_logger.LogWarning(ex, "Download client session invalid, switching to main client");
953+
dlClient = HandleInvalidDownloadSession(ex);
954+
file = await dlClient.Upload_GetFile(location, currentOffset, limit: totalDownloadBytes);
955+
}
857956
catch (Exception ex)
858957
{
859958
_logger.LogError(ex, "Download Error - CurrentOffset: {Offset}, TotalLimit: {Limit}", currentOffset, totalLimit);
@@ -1017,6 +1116,12 @@ public async Task<Stream> DownloadFileAndReturnWithOffset(ChatMessages message,
10171116
_logger.LogError(ex, "OFFSET_INVALID at offset {Offset} - file may have changed", currentOffset);
10181117
throw;
10191118
}
1119+
catch (Exception ex) when (IsSessionInvalidException(ex))
1120+
{
1121+
_logger.LogWarning(ex, "Download client session invalid, switching to main client");
1122+
dlClient = HandleInvalidDownloadSession(ex);
1123+
file = await dlClient.Upload_GetFile(location, currentOffset, limit: chunkSize);
1124+
}
10201125
catch (Exception ex)
10211126
{
10221127
_logger.LogError(ex, "Download error at offset {Offset}", currentOffset);

TelegramDownloader/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
Directory.CreateDirectory(UserService.USERDATAFOLDER);
5454
}
5555

56+
// Copy main session to download session at startup (before Telegram client locks the file)
57+
TelegramService.CopySessionForDownloadClient();
58+
5659
if (!Directory.Exists(FileService.LOCALDIR))
5760
{
5861
Directory.CreateDirectory(FileService.LOCALDIR);

0 commit comments

Comments
 (0)