@raevon/n8n-nodes-whatsapp 1.0.0 → 1.0.1
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.
|
@@ -66,6 +66,7 @@ let reconnectTimer = null;
|
|
|
66
66
|
let qrResolve = null;
|
|
67
67
|
let qrHttpServer = null;
|
|
68
68
|
let latestQr = null;
|
|
69
|
+
let generation = 0; // #10: Generation counter — prevents stale handlers on reconnect
|
|
69
70
|
function todayStartIso() {
|
|
70
71
|
return new Date().toISOString().slice(0, 10) + 'T00:00:00.000Z';
|
|
71
72
|
}
|
|
@@ -115,6 +116,7 @@ function stopQrServer() {
|
|
|
115
116
|
qrHttpServer = null;
|
|
116
117
|
}
|
|
117
118
|
}
|
|
119
|
+
// #7: Reconnect backoff with jitter — exponential 1s→60s, ±20% randomization
|
|
118
120
|
async function scheduleReconnect(cfg) {
|
|
119
121
|
if (reconnectTimer)
|
|
120
122
|
return;
|
|
@@ -129,6 +131,7 @@ async function scheduleReconnect(cfg) {
|
|
|
129
131
|
async function initSocket(cfg, authPath) {
|
|
130
132
|
if (socketInstance && socketStatus === 'connected')
|
|
131
133
|
return socketInstance;
|
|
134
|
+
const gen = ++generation; // #10: Snapshot generation for this connection attempt
|
|
132
135
|
const resolvedPath = authPath.startsWith('~')
|
|
133
136
|
? node_path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', authPath.slice(1))
|
|
134
137
|
: authPath;
|
|
@@ -139,11 +142,13 @@ async function initSocket(cfg, authPath) {
|
|
|
139
142
|
const { version } = await (0, baileys_1.fetchLatestBaileysVersion)();
|
|
140
143
|
const sock = (0, baileys_1.default)({
|
|
141
144
|
version,
|
|
145
|
+
// #12: Browser fingerprint — appears as Ubuntu Firefox, not a bot
|
|
142
146
|
browser: baileys_1.Browsers.ubuntu('n8n WhatsApp Node'),
|
|
143
147
|
auth: {
|
|
144
148
|
creds: state.creds,
|
|
145
149
|
keys: (0, baileys_1.makeCacheableSignalKeyStore)(state.keys, { level: 'silent' }),
|
|
146
150
|
},
|
|
151
|
+
// #11: Stealth flags — don't go online, don't sync history, no link previews
|
|
147
152
|
markOnlineOnConnect: false,
|
|
148
153
|
syncFullHistory: false,
|
|
149
154
|
shouldSyncHistoryMessage: () => false,
|
|
@@ -154,10 +159,14 @@ async function initSocket(cfg, authPath) {
|
|
|
154
159
|
socketStatus = 'connecting';
|
|
155
160
|
reconnectAttempts = 0;
|
|
156
161
|
sock.ev.on('creds.update', () => {
|
|
162
|
+
if (gen !== generation)
|
|
163
|
+
return; // #10: Ignore stale connection events
|
|
157
164
|
saveCreds().catch(() => { });
|
|
158
165
|
});
|
|
159
166
|
sock.ev.on('connection.update', (update) => {
|
|
160
167
|
var _a, _b;
|
|
168
|
+
if (gen !== generation)
|
|
169
|
+
return; // #10: Ignore stale connection events
|
|
161
170
|
const { connection, lastDisconnect, qr } = update;
|
|
162
171
|
if (qr) {
|
|
163
172
|
latestQr = qr;
|
|
@@ -180,6 +189,7 @@ async function initSocket(cfg, authPath) {
|
|
|
180
189
|
if (code === baileys_1.DisconnectReason.loggedOut) {
|
|
181
190
|
socketStatus = 'logged_out';
|
|
182
191
|
socketInstance = null;
|
|
192
|
+
++generation; // #10: Invalidate all handlers from this session
|
|
183
193
|
stopQrServer();
|
|
184
194
|
}
|
|
185
195
|
else {
|
|
@@ -188,6 +198,8 @@ async function initSocket(cfg, authPath) {
|
|
|
188
198
|
}
|
|
189
199
|
}
|
|
190
200
|
});
|
|
201
|
+
// #8: Fire-and-forget — no receipt tracking, no delivery acks, minimal protocol chatter
|
|
202
|
+
// (Baileys doesn't auto-track receipts unless you call readMessages/sendReceipt — we don't)
|
|
191
203
|
return sock;
|
|
192
204
|
}
|
|
193
205
|
// --- Public API ---
|
|
@@ -296,7 +308,7 @@ async function sendMessageWithAntiBan(to, content, cfg) {
|
|
|
296
308
|
}
|
|
297
309
|
}
|
|
298
310
|
const jid = normalizeRecipient(to);
|
|
299
|
-
// Recipient validation
|
|
311
|
+
// Recipient validation (#6)
|
|
300
312
|
if (cfg.checkRecipientExists && !jid.endsWith('@g.us')) {
|
|
301
313
|
const results = await sock.onWhatsApp(jid);
|
|
302
314
|
const result = results === null || results === void 0 ? void 0 : results[0];
|
|
@@ -317,12 +329,13 @@ async function sendMessageWithAntiBan(to, content, cfg) {
|
|
|
317
329
|
dailySendLimit: cfg.dailySendLimit,
|
|
318
330
|
checkRecipientExists: cfg.checkRecipientExists,
|
|
319
331
|
};
|
|
320
|
-
|
|
332
|
+
// #9: HTTP timeout race — if anti-ban delays push past 15s, return queued
|
|
333
|
+
const task = queue.add(async () => {
|
|
321
334
|
var _a;
|
|
322
335
|
const wait = nextSendAt - Date.now();
|
|
323
336
|
if (wait > 0)
|
|
324
337
|
await sleep(wait);
|
|
325
|
-
// Typing simulation
|
|
338
|
+
// Typing simulation (#4)
|
|
326
339
|
await simulateTyping(jid, content, antiBanCfg);
|
|
327
340
|
// Build message content
|
|
328
341
|
const msgContent = {};
|
|
@@ -345,9 +358,9 @@ async function sendMessageWithAntiBan(to, content, cfg) {
|
|
|
345
358
|
if (content.ptt !== undefined)
|
|
346
359
|
msgContent.ptt = content.ptt;
|
|
347
360
|
const response = await sock.sendMessage(jid, msgContent);
|
|
348
|
-
// Update anti-ban timing
|
|
361
|
+
// Update anti-ban timing (#2, #3)
|
|
349
362
|
nextSendAt = Date.now() + sendGapMs(antiBanCfg) + burstPauseMs(antiBanCfg);
|
|
350
|
-
// Update daily count
|
|
363
|
+
// Update daily count (#5)
|
|
351
364
|
if (cfg.dailySendLimit > 0)
|
|
352
365
|
sentTodayCount++;
|
|
353
366
|
if (!((_a = response === null || response === void 0 ? void 0 : response.key) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
@@ -359,7 +372,23 @@ async function sendMessageWithAntiBan(to, content, cfg) {
|
|
|
359
372
|
recipient: jid,
|
|
360
373
|
};
|
|
361
374
|
});
|
|
362
|
-
|
|
375
|
+
// #9: HTTP timeout race — hand off to background if anti-ban delay exceeds 15s
|
|
376
|
+
let timer;
|
|
377
|
+
const winner = await Promise.race([
|
|
378
|
+
task.then(result => ({ result }), error => ({ error })),
|
|
379
|
+
new Promise(resolve => { timer = setTimeout(() => resolve(null), 15000); }),
|
|
380
|
+
]);
|
|
381
|
+
clearTimeout(timer);
|
|
382
|
+
if (!winner) {
|
|
383
|
+
// Queued behind anti-ban delays — let it send in background
|
|
384
|
+
task.then(() => { }, () => { });
|
|
385
|
+
return { messageId: 'queued', status: 'queued', recipient: jid };
|
|
386
|
+
}
|
|
387
|
+
const w = winner;
|
|
388
|
+
if (w.error) {
|
|
389
|
+
throw w.error;
|
|
390
|
+
}
|
|
391
|
+
return w.result;
|
|
363
392
|
}
|
|
364
393
|
function getConnectionStatus() {
|
|
365
394
|
var _a;
|
package/package.json
CHANGED