Skip to content

Commit 5432ab1

Browse files
authored
Optimization of connection persistence and message satinization.
Optimization of connection persistence and message satinization.
2 parents a4553a4 + c24812b commit 5432ab1

File tree

1 file changed

+140
-25
lines changed

1 file changed

+140
-25
lines changed

src/infra/baileys/services.ts

Lines changed: 140 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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' || /terminated/i.test(msg) || /other side closed/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(/terminated/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(/terminated/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

Comments
 (0)