@@ -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 ) ;
0 commit comments