@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
- const result = await queue.add(async () => {
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
- return result;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raevon/n8n-nodes-whatsapp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "n8n community node for WhatsApp — send and receive messages with anti-ban protection via the Baileys library",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",