@@ -40,6 +40,8 @@ export default class Instance{
4040 private qrCodeResolver ?: ( qrBase64 : string ) => void ;
4141 private qrCodePromise ?: Promise < string > ;
4242 private phoneNumber ?: string | undefined ;
43+ private reconnectAttempts = 0 ;
44+ private maxReconnectAttempts = 5 ;
4345
4446 getSock ( ) : ( WASocket | undefined ) {
4547 return this ?. sock ;
@@ -64,26 +66,40 @@ export default class Instance{
6466 const browser : WABrowserDescription = [ UserConfig . sessionClient , UserConfig . sessionName , release ( ) ] ;
6567 const agents = await genProxy ( UserConfig . proxyUrl ) ;
6668
67- this . sock = makeWASocket ( {
68- auth : state ,
69- version,
70- browser,
71- emitOwnEvents : true ,
72- generateHighQualityLinkPreview : true ,
73- syncFullHistory : true ,
74- msgRetryCounterCache : msgRetryCounterCache ,
75- userDevicesCache : userDevicesCache ,
76- enableAutoSessionRecreation : true ,
77- agent : agents . wsAgent ,
78- fetchAgent : agents . fetchAgent ,
79- retryRequestDelayMs : 3 * 1000 ,
80- maxMsgRetryCount : 1000 ,
81- logger : P ( { level : 'fatal' } ) ,
82- cachedGroupMetadata : async ( jid ) => groupCache . get ( jid ) ,
83- getMessage : async ( key ) => await this . getMessage ( key . id ! ) as proto . IMessage ,
84- qrTimeout : UserConfig . qrCodeTimeout * 1000
69+ this . qrCodePromise = new Promise ( ( resolve ) => {
70+ this . qrCodeResolver = resolve ;
8571 } ) ;
8672
73+ let sock : WASocket | undefined ;
74+ try {
75+ sock = makeWASocket ( {
76+ auth : state ,
77+ version,
78+ browser,
79+ emitOwnEvents : true ,
80+ generateHighQualityLinkPreview : true ,
81+ syncFullHistory : true ,
82+ msgRetryCounterCache : msgRetryCounterCache ,
83+ userDevicesCache : userDevicesCache ,
84+ enableAutoSessionRecreation : true ,
85+ agent : agents . wsAgent ,
86+ fetchAgent : agents . fetchAgent ,
87+ retryRequestDelayMs : 3 * 1000 ,
88+ maxMsgRetryCount : 1000 ,
89+ logger : P ( { level : 'fatal' } ) ,
90+ cachedGroupMetadata : async ( jid ) => groupCache . get ( jid ) ,
91+ getMessage : async ( key ) => await this . getMessage ( key . id ! ) as proto . IMessage ,
92+ qrTimeout : UserConfig . qrCodeTimeout * 1000
93+ } ) ;
94+ } catch ( err ) {
95+ console . error ( `[${ this . owner } /${ this . instanceName } ] Error creating socket` , err ) ;
96+ await this . reconnectWithBackoff ( ) ;
97+ throw err ;
98+ }
99+
100+ this . sock = sock ;
101+ this . attachSocketErrorHandlers ( ) ;
102+
87103 this . key = `${ this . owner } _${ this . instanceName } ` ;
88104
89105 this . instance = {
@@ -97,11 +113,6 @@ export default class Instance{
97113
98114 this . setStatus ( "OFFLINE" ) ;
99115
100- // Criar Promise para aguardar o QR code
101- this . qrCodePromise = new Promise ( ( resolve ) => {
102- this . qrCodeResolver = resolve ;
103- } ) ;
104-
105116 this . instanceEvents ( saveCreds ) ;
106117
107118 this . qrCodeCount = 0 ;
@@ -154,6 +165,78 @@ export default class Instance{
154165
155166 }
156167
168+ private attachSocketErrorHandlers ( ) {
169+ try {
170+ this . sock ?. ws ?. on ?.( 'error' , ( err : any ) => this . handleSocketError ( err ) ) ;
171+ this . sock ?. ws ?. on ?.( 'close' , ( ) => {
172+ if ( this . instance ?. connectionStatus === 'ONLINE' ) {
173+ console . warn ( `[${ this . owner } /${ this . instanceName } ] ws closed unexpectedly` ) ;
174+ this . handleSocketError ( new Error ( 'ws closed' ) ) ;
175+ }
176+ } ) ;
177+ } catch ( e ) {
178+ console . warn ( `[${ this . owner } /${ this . instanceName } ] Failed to register ws handlers` , e ) ;
179+ }
180+
181+ try {
182+ const anySock : any = this . sock ;
183+ anySock ?. options ?. agent ?. on ?.( 'error' , ( err : any ) => this . handleSocketError ( err ) ) ;
184+ anySock ?. options ?. fetchAgent ?. on ?.( 'error' , ( err : any ) => this . handleSocketError ( err ) ) ;
185+ } catch ( e ) {
186+ console . warn ( `[${ this . owner } /${ this . instanceName } ] Failed to register agents handlers` , e ) ;
187+ }
188+ }
189+
190+ private handleSocketError ( err : any ) {
191+ if ( ! err ) return ;
192+ const msg = String ( err ?. message || '' ) ;
193+ const code = err ?. code ;
194+ const isUndici = code === 'UND_ERR_SOCKET' || / t e r m i n a t e d / i. test ( msg ) || / o t h e r s i d e c l o s e d / i. test ( msg ) ;
195+ console . error ( `[${ this . owner } /${ this . instanceName } ] Socket/Fetch error captured` , { code, msg } ) ;
196+ if ( isUndici ) {
197+ this . reconnectWithBackoff ( ) ;
198+ }
199+ }
200+
201+ private async reconnectWithBackoff ( ) {
202+ if ( this . instance ?. connectionStatus === 'REMOVED' ) return ;
203+ if ( this . reconnectAttempts >= this . maxReconnectAttempts ) {
204+ console . error ( `[${ this . owner } /${ this . instanceName } ] Reconnection limit reached` ) ;
205+ return ;
206+ }
207+ const wait = Math . min ( 30000 , 1000 * 2 ** this . reconnectAttempts ) ;
208+ this . reconnectAttempts ++ ;
209+ console . log ( `[${ this . owner } /${ this . instanceName } ] Trying to reconnect in ${ wait } ms (attempt ${ this . reconnectAttempts } /${ this . maxReconnectAttempts } )` ) ;
210+ await delay ( wait ) ;
211+ try {
212+ await this . create ( { owner : this . owner , instanceName : this . instanceName , phoneNumber : this . phoneNumber } ) ;
213+ } catch ( e ) {
214+ console . error ( `[${ this . owner } /${ this . instanceName } ] Reconnection failed` , e ) ;
215+ }
216+ }
217+
218+ private registerGlobalHandlers ( ) {
219+ if ( ! ( global as any ) . __zap_global_error_wrapped ) {
220+ ( global as any ) . __zap_global_error_wrapped = true ;
221+
222+ process . on ( 'uncaughtException' , ( err ) => {
223+ if ( / t e r m i n a t e d / i. test ( String ( err ?. message ) ) ) {
224+ console . error ( 'UncaughtException (terminated) captured. Process preserved.' ) ;
225+ } else {
226+ console . error ( 'UncaughtException' , err ) ;
227+ }
228+ } ) ;
229+
230+ process . on ( 'unhandledRejection' , ( reason : any ) => {
231+ if ( / t e r m i n a t e d / i. test ( String ( reason ?. message ) ) ) {
232+ console . error ( 'UnhandledRejection (terminated) captured. Process preserved.' ) ;
233+ } else {
234+ console . error ( 'UnhandledRejection' , reason ) ;
235+ }
236+ } ) ;
237+ }
238+ }
239+
157240 async instanceEvents ( saveCreds : ( ) => Promise < void > ) {
158241
159242 this . sock . ev . on ( "creds.update" , saveCreds as ( data : BaileysEventMap [ "creds.update" ] ) => void ) ;
@@ -310,7 +393,8 @@ export default class Instance{
310393 } ) ;
311394 }
312395
313- await PrismaConnection . saveManyMessages ( `${ this . instance . owner } _${ this . instance . instanceName } ` , messages ) ;
396+ const sanitized = messages . map ( m => this . sanitizeWAMessage ( m ) ) ;
397+ await PrismaConnection . saveManyMessages ( `${ this . instance . owner } _${ this . instance . instanceName } ` , sanitized ) ;
314398 trySendWebhook ( "messages.set" , this . instance , rawMessages ) ;
315399 }
316400
@@ -382,7 +466,8 @@ export default class Instance{
382466 } ) ;
383467 }
384468
385- await PrismaConnection . saveManyMessages ( `${ this . instance . owner } _${ this . instance . instanceName } ` , messages . messages ) ;
469+ const sanitized = messages . messages . map ( m => this . sanitizeWAMessage ( m ) ) ;
470+ await PrismaConnection . saveManyMessages ( `${ this . instance . owner } _${ this . instance . instanceName } ` , sanitized ) ;
386471 await trySendWebhook ( "messages.upsert" , this . instance , rawMessages ) ;
387472
388473 } ) ;
@@ -555,6 +640,36 @@ export default class Instance{
555640
556641 }
557642
643+ private deepSanitize ( value : any ) : any {
644+ if ( value === null || value === undefined ) return value ;
645+ if ( typeof value === 'bigint' ) return Number ( value ) ;
646+ if ( typeof value === 'function' ) return undefined ;
647+ if ( value instanceof Uint8Array ) return Buffer . from ( value ) . toString ( 'base64' ) ;
648+ if ( Array . isArray ( value ) ) {
649+ return value . map ( v => this . deepSanitize ( v ) ) . filter ( v => v !== undefined ) ;
650+ }
651+ if ( typeof value === 'object' ) {
652+ if ( 'low' in value && 'high' in value &&
653+ typeof ( value as any ) . low === 'number' &&
654+ typeof ( value as any ) . high === 'number' ) {
655+ const low = ( value as any ) . low >>> 0 ;
656+ const high = ( value as any ) . high >>> 0 ;
657+ return high * 2 ** 32 + low ;
658+ }
659+ const out : any = { } ;
660+ for ( const [ k , v ] of Object . entries ( value ) ) {
661+ const sv = this . deepSanitize ( v ) ;
662+ if ( sv !== undefined ) out [ k ] = sv ;
663+ }
664+ return out ;
665+ }
666+ return value ;
667+ }
668+
669+ private sanitizeWAMessage ( msg : any ) : any {
670+ return this . deepSanitize ( msg ) ;
671+ }
672+
558673}
559674
560675
0 commit comments