whatsapp-pi 1.0.44 → 1.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/services/session.manager.ts +3 -2
- package/src/services/whatsapp.service.ts +56 -36
- package/whatsapp-pi.ts +31 -31
package/package.json
CHANGED
|
@@ -136,10 +136,11 @@ export class SessionManager {
|
|
|
136
136
|
public async saveConfig() {
|
|
137
137
|
const tempPath = `${this.configPath}.${process.pid}.${Date.now()}.tmp`;
|
|
138
138
|
try {
|
|
139
|
+
this.hasAuthState = this.hasAuthState || await this.hasCredentialsFile();
|
|
139
140
|
const config = {
|
|
140
141
|
allowList: this.allowList,
|
|
141
|
-
blockList: this.blockList,
|
|
142
|
-
ignoredNumbers: this.ignoredNumbers,
|
|
142
|
+
blockList: this.blockList,
|
|
143
|
+
ignoredNumbers: this.ignoredNumbers,
|
|
143
144
|
status: this.status,
|
|
144
145
|
hasAuthState: this.hasAuthState,
|
|
145
146
|
openaiKey: this.openaiKey,
|
|
@@ -71,12 +71,16 @@ interface BoomLikeError {
|
|
|
71
71
|
message?: string;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
export class WhatsAppService {
|
|
75
|
-
private
|
|
76
|
-
private
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
private
|
|
74
|
+
export class WhatsAppService {
|
|
75
|
+
private static readonly INITIAL_RECONNECT_DELAY_MS = 5_000;
|
|
76
|
+
private static readonly MAX_RECONNECT_DELAY_MS = 120_000;
|
|
77
|
+
|
|
78
|
+
private socket?: WhatsAppSocketLike;
|
|
79
|
+
private sessionManager: SessionManager;
|
|
80
|
+
private messageSender: MessageSender;
|
|
81
|
+
private isReconnecting = false;
|
|
82
|
+
private reconnectAttempts = 0;
|
|
83
|
+
private verboseMode = false;
|
|
80
84
|
private onIncomingMessageRecorded?: (message: IncomingMessage) => void | Promise<void>;
|
|
81
85
|
private saveCreds?: () => Promise<void>;
|
|
82
86
|
private restoreBaileysConsoleFilter?: () => void;
|
|
@@ -164,12 +168,17 @@ export class WhatsAppService {
|
|
|
164
168
|
return '';
|
|
165
169
|
}
|
|
166
170
|
|
|
167
|
-
private clearReconnectTimeout() {
|
|
168
|
-
if (this.reconnectTimeout) {
|
|
169
|
-
clearTimeout(this.reconnectTimeout);
|
|
170
|
-
this.reconnectTimeout = undefined;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
171
|
+
private clearReconnectTimeout() {
|
|
172
|
+
if (this.reconnectTimeout) {
|
|
173
|
+
clearTimeout(this.reconnectTimeout);
|
|
174
|
+
this.reconnectTimeout = undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private getReconnectDelayMs(): number {
|
|
179
|
+
const delay = WhatsAppService.INITIAL_RECONNECT_DELAY_MS * (2 ** Math.max(0, this.reconnectAttempts - 1));
|
|
180
|
+
return Math.min(delay, WhatsAppService.MAX_RECONNECT_DELAY_MS);
|
|
181
|
+
}
|
|
173
182
|
|
|
174
183
|
private cleanupSocket() {
|
|
175
184
|
this.clearReconnectTimeout();
|
|
@@ -303,10 +312,11 @@ export class WhatsAppService {
|
|
|
303
312
|
console.log('WhatsApp connection successfully opened');
|
|
304
313
|
}
|
|
305
314
|
|
|
306
|
-
this.isReconnecting = false;
|
|
307
|
-
this.
|
|
308
|
-
|
|
309
|
-
await this.
|
|
315
|
+
this.isReconnecting = false;
|
|
316
|
+
this.reconnectAttempts = 0;
|
|
317
|
+
this.clearReconnectTimeout();
|
|
318
|
+
await this.saveCreds?.();
|
|
319
|
+
await this.sessionManager.markAuthStateAvailable();
|
|
310
320
|
this.sessionManager.setStatus('connected');
|
|
311
321
|
this.onStatusUpdate?.('| WhatsApp: Connected');
|
|
312
322
|
}
|
|
@@ -354,6 +364,7 @@ export class WhatsAppService {
|
|
|
354
364
|
}
|
|
355
365
|
this.cleanupSocket();
|
|
356
366
|
this.isReconnecting = false;
|
|
367
|
+
this.reconnectAttempts = 0;
|
|
357
368
|
await this.sessionManager.setStatus('disconnected');
|
|
358
369
|
if (!isBadMac) {
|
|
359
370
|
this.onStatusUpdate?.('| WhatsApp: Disconnected');
|
|
@@ -361,26 +372,35 @@ export class WhatsAppService {
|
|
|
361
372
|
return;
|
|
362
373
|
}
|
|
363
374
|
|
|
364
|
-
if (statusCode === DisconnectReason.connectionReplaced) {
|
|
365
|
-
if (this.verboseMode) {
|
|
366
|
-
console.error('Connection replaced - another instance connected');
|
|
367
|
-
}
|
|
368
|
-
this.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
this.
|
|
382
|
-
this.
|
|
383
|
-
|
|
375
|
+
if (statusCode === DisconnectReason.connectionReplaced) {
|
|
376
|
+
if (this.verboseMode) {
|
|
377
|
+
console.error('Connection replaced - another instance connected');
|
|
378
|
+
}
|
|
379
|
+
this.cleanupSocket();
|
|
380
|
+
this.isReconnecting = false;
|
|
381
|
+
this.reconnectAttempts = 0;
|
|
382
|
+
await this.sessionManager.setStatus('disconnected');
|
|
383
|
+
this.onStatusUpdate?.('| WhatsApp: Conflict (Another Instance)');
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (shouldReconnect && !this.isReconnecting) {
|
|
388
|
+
this.isReconnecting = true;
|
|
389
|
+
this.reconnectAttempts++;
|
|
390
|
+
const reconnectDelayMs = this.getReconnectDelayMs();
|
|
391
|
+
this.onStatusUpdate?.('| WhatsApp: Reconnecting...');
|
|
392
|
+
this.clearReconnectTimeout();
|
|
393
|
+
await this.saveCreds?.();
|
|
394
|
+
this.cleanupSocket();
|
|
395
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
396
|
+
this.isReconnecting = false;
|
|
397
|
+
void this.start(options);
|
|
398
|
+
}, reconnectDelayMs);
|
|
399
|
+
} else if (!shouldReconnect) {
|
|
400
|
+
this.reconnectAttempts = 0;
|
|
401
|
+
this.sessionManager.setStatus('logged-out');
|
|
402
|
+
this.onStatusUpdate?.('| WhatsApp: Disconnected');
|
|
403
|
+
}
|
|
384
404
|
}
|
|
385
405
|
|
|
386
406
|
private extractText(message: IncomingMessageContent | undefined): string {
|
package/whatsapp-pi.ts
CHANGED
|
@@ -60,7 +60,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
// Initial status setup
|
|
63
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
63
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
64
64
|
_ctx = ctx;
|
|
65
65
|
// Check verbose mode
|
|
66
66
|
const isVerboseFlagSet = process.argv.includes("--verbose");
|
|
@@ -86,10 +86,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
86
86
|
await whatsappService.stop();
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
|
-
whatsappService.setIncomingMessageRecorder(async (message) => {
|
|
90
|
-
await recentsService.recordMessage({
|
|
91
|
-
messageId: message.id,
|
|
92
|
-
senderNumber: `+${message.remoteJid.split('@')[0]}`,
|
|
89
|
+
whatsappService.setIncomingMessageRecorder(async (message) => {
|
|
90
|
+
await recentsService.recordMessage({
|
|
91
|
+
messageId: message.id,
|
|
92
|
+
senderNumber: `+${message.remoteJid.split('@')[0]}`,
|
|
93
93
|
senderName: message.pushName,
|
|
94
94
|
text: message.text || '',
|
|
95
95
|
direction: 'incoming',
|
|
@@ -97,20 +97,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
const savedStateEntry = [...ctx.sessionManager.getEntries()]
|
|
101
|
-
.reverse()
|
|
102
|
-
.find(entry => entry.type === "custom" && entry.customType === "whatsapp-state");
|
|
103
|
-
const isWhatsappPiOn = pi.getFlag("whatsapp-pi-online") === true;
|
|
104
|
-
const registered = await sessionManager.isRegistered();
|
|
105
|
-
|
|
106
|
-
if (savedStateEntry) {
|
|
107
|
-
const data = (savedStateEntry as { data?: any }).data;
|
|
108
|
-
if (data.status) {
|
|
109
|
-
const restoredStatus = data.status === 'connected' && !(isWhatsappPiOn && registered)
|
|
110
|
-
? 'disconnected'
|
|
111
|
-
: data.status;
|
|
112
|
-
await sessionManager.setStatus(restoredStatus);
|
|
113
|
-
}
|
|
100
|
+
const savedStateEntry = [...ctx.sessionManager.getEntries()]
|
|
101
|
+
.reverse()
|
|
102
|
+
.find(entry => entry.type === "custom" && entry.customType === "whatsapp-state");
|
|
103
|
+
const isWhatsappPiOn = pi.getFlag("whatsapp-pi-online") === true;
|
|
104
|
+
const registered = await sessionManager.isRegistered();
|
|
105
|
+
|
|
106
|
+
if (savedStateEntry) {
|
|
107
|
+
const data = (savedStateEntry as { data?: any }).data;
|
|
108
|
+
if (data.status) {
|
|
109
|
+
const restoredStatus = data.status === 'connected' && !(isWhatsappPiOn && registered)
|
|
110
|
+
? 'disconnected'
|
|
111
|
+
: data.status;
|
|
112
|
+
await sessionManager.setStatus(restoredStatus);
|
|
113
|
+
}
|
|
114
114
|
if (Array.isArray(data.allowList)) {
|
|
115
115
|
for (const n of data.allowList) {
|
|
116
116
|
const num = typeof n === "string" ? n : n.number;
|
|
@@ -118,10 +118,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
118
118
|
await sessionManager.addNumber(num, name);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (isWhatsappPiOn && registered) {
|
|
124
|
-
ctx.ui.setStatus('whatsapp', '| WhatsApp: Auto-connecting...');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (isWhatsappPiOn && registered) {
|
|
124
|
+
ctx.ui.setStatus('whatsapp', '| WhatsApp: Auto-connecting...');
|
|
125
125
|
|
|
126
126
|
// Retry logic (max 3 attempts, 3s delay)
|
|
127
127
|
let attempts = 0;
|
|
@@ -131,7 +131,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
131
131
|
attempts++;
|
|
132
132
|
try {
|
|
133
133
|
await whatsappService.start({ allowPairingOnAuthFailure: false });
|
|
134
|
-
} catch {
|
|
134
|
+
} catch {
|
|
135
135
|
if (attempts < maxAttempts) {
|
|
136
136
|
ctx.ui.notify(`WhatsApp: Connection attempt ${attempts} failed. Retrying...`, 'warning');
|
|
137
137
|
setTimeout(tryConnect, 3000);
|
|
@@ -143,11 +143,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
await tryConnect();
|
|
146
|
-
} else if (isWhatsappPiOn) {
|
|
147
|
-
ctx.ui.notify('WhatsApp: Auto-connect requested, but no saved WhatsApp credentials were found. Use Connect WhatsApp once to scan the QR code.', 'warning');
|
|
148
|
-
} else {
|
|
149
|
-
ctx.ui.notify('WhatsApp: Use Connect / Reconnect WhatsApp. QR code will appear only if pairing is needed.', 'info');
|
|
150
|
-
}
|
|
146
|
+
} else if (isWhatsappPiOn) {
|
|
147
|
+
ctx.ui.notify('WhatsApp: Auto-connect requested, but no saved WhatsApp credentials were found. Use Connect WhatsApp once to scan the QR code.', 'warning');
|
|
148
|
+
} else {
|
|
149
|
+
ctx.ui.notify('WhatsApp: Use Connect / Reconnect WhatsApp. QR code will appear only if pairing is needed.', 'info');
|
|
150
|
+
}
|
|
151
151
|
|
|
152
152
|
ctx.ui.notify('WhatsApp: Session reset via /new is now fully supported.', 'info');
|
|
153
153
|
|
|
@@ -157,7 +157,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
157
157
|
if (code !== 0 && code !== 99) { // 99 is a common exit code for -v in some versions
|
|
158
158
|
throw new Error(`pdftotext returned code ${code}`);
|
|
159
159
|
}
|
|
160
|
-
} catch {
|
|
160
|
+
} catch {
|
|
161
161
|
ctx.ui.notify('WhatsApp: pdftotext not found. PDF document support will be limited to storage only.', 'warning');
|
|
162
162
|
logger.warn('[WhatsApp-Pi] Warning: pdftotext not found in system PATH.');
|
|
163
163
|
}
|
|
@@ -333,7 +333,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
333
333
|
} else {
|
|
334
334
|
ctx.ui.notify(`Failed to send WhatsApp reply`, 'error');
|
|
335
335
|
}
|
|
336
|
-
} catch {
|
|
336
|
+
} catch {
|
|
337
337
|
ctx.ui.notify(`Failed to send WhatsApp reply`, 'error');
|
|
338
338
|
}
|
|
339
339
|
}
|