@@ -10,8 +10,6 @@ import { StringSession } from 'telegram/sessions'
10
10
import type { Dialog } from 'telegram/tl/custom/dialog'
11
11
import config from './config'
12
12
import { ApiError , Code } from './error'
13
- // Using built-in types instead of node:timers for better compatibility
14
- type Timeout = ReturnType < typeof setTimeout >
15
13
16
14
Logger . setLevel ( 'none' )
17
15
@@ -109,16 +107,8 @@ class TelegramConnection {
109
107
private readonly listeners = new Map < number , Listener > ( )
110
108
private subID = 0
111
109
private handlerAdded = false
112
- private readinessInterval : Timeout | undefined
110
+ private readinessInterval : NodeJS . Timeout | undefined
113
111
private connecting = false
114
- private reconnectAttempts = 0
115
- private static readonly MAX_RECONNECT_ATTEMPTS = 3
116
- private static readonly RECONNECT_TIMEOUT = 5000 // 5 seconds timeout for connection attempts
117
- private static readonly RECONNECT_BACKOFF = 1000 // Add 1 second delay between retries
118
-
119
- private delay ( ms : number ) : Promise < void > {
120
- return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) )
121
- }
122
112
123
113
constructor (
124
114
readonly client : TelegramClient ,
@@ -138,79 +128,21 @@ class TelegramConnection {
138
128
}
139
129
}
140
130
141
- private async connectWithTimeout ( ) : Promise < void > {
142
- const timeoutId = setTimeout ( ( ) => {
143
- // Force disconnect if we timeout
144
- void this . client . disconnect ( )
145
- throw new Error ( 'Connection attempt timed out' )
146
- } , TelegramConnection . RECONNECT_TIMEOUT )
147
-
148
- try {
149
- await this . client . connect ( )
150
- } finally {
151
- clearTimeout ( timeoutId )
152
- }
153
- }
154
-
155
131
async tryReconnect ( ) : Promise < void > {
156
132
if ( this . connecting ) {
157
133
return
158
134
}
159
135
160
136
if ( this . client . connected === true ) {
161
137
console . log ( 'Already connected' )
162
- this . reconnectAttempts = 0 // Reset counter on successful connection
163
- return
164
- }
165
-
166
- if ( this . reconnectAttempts >= TelegramConnection . MAX_RECONNECT_ATTEMPTS ) {
167
- console . error ( `Max reconnection attempts (${ TelegramConnection . MAX_RECONNECT_ATTEMPTS } ) reached` )
168
- // Reset counter but wait for next interval
169
- this . reconnectAttempts = 0
170
138
return
171
139
}
172
140
173
141
try {
174
142
this . connecting = true
175
- await this . connectWithTimeout ( )
176
- this . reconnectAttempts = 0 // Reset on successful connection
143
+ await this . client . connect ( )
177
144
} catch ( e : unknown ) {
178
- this . reconnectAttempts ++
179
-
180
- // Handle specific error types
181
- if ( e instanceof RPCError ) {
182
- // Handle Telegram RPC-specific errors
183
- const rpcError = e
184
- console . error ( `Telegram RPC error during connection attempt ${ this . reconnectAttempts } : ${ rpcError . message } ` )
185
- if ( rpcError . message . includes ( 'AUTH_KEY_UNREGISTERED' ) || rpcError . message . includes ( 'SESSION_REVOKED' ) ) {
186
- // Authentication errors - need to re-authenticate
187
- this . _signInFlow = undefined // Force re-authentication
188
- throw new ApiError ( Code . PhoneCodeInvalid , 'Re-authentication required' ) // Use existing auth error code
189
- }
190
- } else if ( e instanceof Error ) {
191
- const error = e
192
- if ( error . message . includes ( 'Connection attempt timed out' ) ) {
193
- console . error ( `Connection timeout on attempt ${ this . reconnectAttempts } ` )
194
- } else if ( error . message . includes ( 'ECONNREFUSED' ) || error . message . includes ( 'ENETUNREACH' ) ) {
195
- console . error ( `Network error on attempt ${ this . reconnectAttempts } : ${ error . message } ` )
196
- } else {
197
- console . error ( `Connection attempt ${ this . reconnectAttempts } failed: ${ error . message } ` )
198
- }
199
- } else {
200
- console . error ( `Connection attempt ${ this . reconnectAttempts } failed with unknown error` )
201
- }
202
-
203
- // Add exponential backoff delay using callback
204
- const backoffDelay = TelegramConnection . RECONNECT_BACKOFF * Math . pow ( 2 , this . reconnectAttempts - 1 )
205
- await this . delay ( backoffDelay )
206
-
207
- // If we've hit max attempts, emit a more specific error
208
- if ( this . reconnectAttempts >= TelegramConnection . MAX_RECONNECT_ATTEMPTS ) {
209
- throw new ApiError (
210
- Code . PhoneCodeInvalid ,
211
- `Failed to connect after ${ TelegramConnection . MAX_RECONNECT_ATTEMPTS } attempts`
212
- ) // Use existing error code
213
- }
145
+ console . error ( e )
214
146
} finally {
215
147
this . connecting = false
216
148
}
@@ -224,57 +156,25 @@ class TelegramConnection {
224
156
await this . tryReconnect ( )
225
157
}
226
158
} catch ( err ) {
227
- if ( err instanceof Error ) {
228
- console . error ( `Reconnection error: ${ err . message } ` )
229
- } else {
230
- console . error ( 'Unknown reconnection error occurred' )
231
- }
159
+ console . log ( err )
232
160
}
233
161
}
234
162
235
- private clearHandlers ( ) : void {
236
- // Clear all event handlers to prevent memory leaks
237
- this . client . listEventHandlers ( ) . forEach ( ( [ builder , callback ] ) => {
238
- this . client . removeEventHandler ( callback , builder )
239
- } )
240
- this . handlerAdded = false
241
- this . listeners . clear ( )
242
- this . subID = 0
243
- }
244
-
245
- private clearIntervals ( ) : void {
163
+ async close ( ) : Promise < void > {
246
164
if ( this . readinessInterval !== undefined ) {
247
165
clearInterval ( this . readinessInterval )
248
- this . readinessInterval = undefined
249
- }
250
- }
251
-
252
- async close ( ) : Promise < void > {
253
- this . clearIntervals ( )
254
- this . clearHandlers ( )
255
-
256
- try {
257
- await this . client . disconnect ( )
258
- } catch ( e ) {
259
- // Log but don't throw as we're cleaning up
260
- console . error ( 'Error during disconnect:' , e instanceof Error ? e . message : 'Unknown error' )
261
166
}
167
+ this . client . listEventHandlers ( ) . forEach ( ( [ builder , callback ] ) => {
168
+ this . client . removeEventHandler ( callback , builder )
169
+ } )
170
+ await this . client . disconnect ( )
262
171
}
263
172
264
173
async signOut ( ) : Promise < void > {
265
- try {
266
- await this . client . invoke ( new Api . auth . LogOut ( ) )
267
- } finally {
268
- // Always clean up resources even if logout fails
269
- await this . close ( )
270
- this . _signInFlow = undefined
271
- }
174
+ await this . client . invoke ( new Api . auth . LogOut ( ) )
272
175
}
273
176
274
177
async signIn ( ) : Promise < void > {
275
- // Clear any existing handlers before starting new sign in
276
- this . clearHandlers ( )
277
-
278
178
this . _signInFlow = new SignInFlow ( this . client , this . phone , ( ) => {
279
179
this . _signInFlow = undefined
280
180
this . client . session . save ( )
@@ -428,22 +328,17 @@ export const telegram = new (class TelegramHelper {
428
328
readonly ttls = new Map < string , NodeJS . Timeout > ( )
429
329
430
330
async auth ( phone : string ) : Promise < SignInState > {
431
- // Clear any existing TTL timeout for this phone
432
- this . clearTTL ( phone )
433
-
434
331
const conn = await this . getOrCreate ( phone )
435
332
436
- // Set new TTL timeout
437
- this . ttls . set (
438
- phone ,
439
- setTimeout ( ( ) => {
440
- console . log ( `TTL expired for connection ${ phone } , cleaning up...` )
441
- this . forgetConnection ( phone )
442
- void conn . close ( ) . catch ( ( e ) => {
443
- console . error ( `Error during TTL cleanup for ${ phone } :` , e instanceof Error ? e . message : 'Unknown error' )
444
- } )
445
- } , config . TelegramAuthTTL )
446
- )
333
+ if ( ! this . ttls . has ( phone ) ) {
334
+ this . ttls . set (
335
+ phone ,
336
+ setTimeout ( ( ) => {
337
+ this . conns . delete ( phone )
338
+ void conn . close ( )
339
+ } , config . TelegramAuthTTL )
340
+ )
341
+ }
447
342
448
343
if ( conn . signInFlow !== undefined ) {
449
344
return conn . signInFlow . state
@@ -452,50 +347,23 @@ export const telegram = new (class TelegramHelper {
452
347
try {
453
348
await conn . signIn ( )
454
349
} catch ( err ) {
455
- // On error, clean up everything
456
350
this . forgetConnection ( phone )
457
- await conn . close ( ) . catch ( ( e ) => {
458
- console . error ( `Error during error cleanup for ${ phone } :` , e instanceof Error ? e . message : 'Unknown error' )
459
- } )
351
+ await conn . close ( )
460
352
461
353
throw err
462
354
}
463
355
464
356
return 'code'
465
357
}
466
358
467
- clearTTL ( phone : string ) : void {
468
- const existingTTL = this . ttls . get ( phone )
469
- if ( existingTTL !== undefined ) {
470
- clearTimeout ( existingTTL )
471
- this . ttls . delete ( phone )
472
- }
473
- }
474
-
475
359
async authCode ( phone : string , code : string ) : Promise < boolean > {
476
360
const conn = this . conns . get ( phone )
477
361
478
362
if ( conn ?. signInFlow === undefined ) {
479
363
throw Error ( 'Sign in is not initialized' )
480
364
}
481
365
482
- try {
483
- const needsPassword = await conn . signInFlow . code ( code )
484
-
485
- if ( ! needsPassword ) {
486
- // Authentication completed successfully, reset TTL
487
- this . clearTTL ( phone )
488
- }
489
-
490
- return needsPassword
491
- } catch ( err ) {
492
- // On authentication error, clean up
493
- this . forgetConnection ( phone )
494
- await conn . close ( ) . catch ( ( e ) => {
495
- console . error ( `Error during auth code cleanup for ${ phone } :` , e instanceof Error ? e . message : 'Unknown error' )
496
- } )
497
- throw err
498
- }
366
+ return await conn . signInFlow . code ( code )
499
367
}
500
368
501
369
async authPass ( phone : string , pass : string ) : Promise < void > {
@@ -505,18 +373,7 @@ export const telegram = new (class TelegramHelper {
505
373
throw Error ( 'Sign in is not initialized' )
506
374
}
507
375
508
- try {
509
- await conn . signInFlow . pass ( pass )
510
- // Authentication completed successfully, reset TTL
511
- this . clearTTL ( phone )
512
- } catch ( err ) {
513
- // On authentication error, clean up
514
- this . forgetConnection ( phone )
515
- await conn . close ( ) . catch ( ( e ) => {
516
- console . error ( `Error during auth pass cleanup for ${ phone } :` , e instanceof Error ? e . message : 'Unknown error' )
517
- } )
518
- throw err
519
- }
376
+ await conn . signInFlow . pass ( pass )
520
377
}
521
378
522
379
async getOrCreate ( phone : string ) : Promise < TelegramConnection > {
@@ -538,7 +395,12 @@ export const telegram = new (class TelegramHelper {
538
395
539
396
forgetConnection ( phone : string ) : void {
540
397
this . conns . delete ( phone )
541
- this . clearTTL ( phone )
398
+ const timeout = this . ttls . get ( phone )
399
+
400
+ if ( timeout !== undefined ) {
401
+ this . ttls . delete ( phone )
402
+ clearTimeout ( timeout )
403
+ }
542
404
}
543
405
544
406
async create ( phone : string , token ?: string ) : Promise < TelegramConnection > {
0 commit comments