@raevon/n8n-nodes-whatsapp 1.0.4 → 1.0.6

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.
@@ -22,6 +22,11 @@ interface AntiBanConfig {
22
22
  }
23
23
  export declare function getWhatsAppCredentials(credentials: Record<string, any>): Promise<WhatsAppCredentials>;
24
24
  export declare function ensureConnected(cfg: WhatsAppCredentials): Promise<WASocket>;
25
+ export declare function connectOrGetQr(cfg: WhatsAppCredentials): Promise<{
26
+ connected: boolean;
27
+ qrUrl?: string;
28
+ message: string;
29
+ }>;
25
30
  export declare function simulateTyping(jid: string, content: {
26
31
  text?: string;
27
32
  caption?: string;
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.getWhatsAppCredentials = getWhatsAppCredentials;
40
40
  exports.ensureConnected = ensureConnected;
41
+ exports.connectOrGetQr = connectOrGetQr;
41
42
  exports.simulateTyping = simulateTyping;
42
43
  exports.sendMessageWithAntiBan = sendMessageWithAntiBan;
43
44
  exports.getConnectionStatus = getConnectionStatus;
@@ -49,6 +50,18 @@ const node_path_1 = __importDefault(require("node:path"));
49
50
  const node_fs_1 = __importDefault(require("node:fs"));
50
51
  const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
51
52
  const randBetween = (min, max) => max > min ? min + Math.floor(Math.random() * (max - min + 1)) : min;
53
+ // Silent logger — Baileys expects pino-compatible logger with these methods
54
+ const noop = () => { };
55
+ const silentLogger = {
56
+ level: 'silent',
57
+ info: noop,
58
+ warn: noop,
59
+ error: noop,
60
+ debug: noop,
61
+ fatal: noop,
62
+ trace: noop,
63
+ child: () => silentLogger,
64
+ };
52
65
  // I8: Extracted ~ path expansion to avoid duplication
53
66
  function expandHome(p) {
54
67
  if (!p.startsWith('~'))
@@ -124,14 +137,14 @@ async function initSocket(cfg, authPath) {
124
137
  browser: baileys_1.Browsers.ubuntu('n8n WhatsApp Node'),
125
138
  auth: {
126
139
  creds: state.creds,
127
- keys: (0, baileys_1.makeCacheableSignalKeyStore)(state.keys, { level: 'silent' }),
140
+ keys: (0, baileys_1.makeCacheableSignalKeyStore)(state.keys, silentLogger),
128
141
  },
129
142
  // #11: Stealth flags — don't go online, don't sync history, no link previews
130
143
  markOnlineOnConnect: false,
131
144
  syncFullHistory: false,
132
145
  shouldSyncHistoryMessage: () => false,
133
146
  generateHighQualityLinkPreview: false,
134
- logger: { level: 'silent' },
147
+ logger: silentLogger,
135
148
  });
136
149
  socketInstance = sock;
137
150
  socketStatus = 'connecting';
@@ -218,26 +231,26 @@ async function ensureConnected(cfg) {
218
231
  const resolvedPath = expandHome(cfg.sessionPath);
219
232
  const hasSession = node_fs_1.default.existsSync(resolvedPath) && node_fs_1.default.readdirSync(resolvedPath).length > 0;
220
233
  const sock = await initSocket(antiBanCfg, cfg.sessionPath);
221
- // If no session, wait for QR scan
234
+ // If no session, wait for QR with a reasonable timeout
222
235
  if (!hasSession) {
223
236
  const qrData = await new Promise((resolve) => {
224
237
  qrResolve = resolve;
225
- // Timeout after 2 minutes — QR codes expire quickly
238
+ // 90 second timeout — QR codes expire quickly, user needs to act fast
226
239
  setTimeout(() => {
227
240
  if (qrResolve) {
228
241
  qrResolve(null);
229
242
  qrResolve = null;
230
243
  }
231
- }, 120000);
244
+ }, 90000);
232
245
  });
233
246
  if (!qrData) {
234
247
  throw new n8n_workflow_1.NodeApiError({}, {
235
248
  message: 'QR code expired. Run the Connect node again to get a new QR code.',
236
249
  });
237
250
  }
238
- // Wait for connection to open after scan
251
+ // Wait for connection to open after scan (30s timeout)
239
252
  await new Promise((resolve, reject) => {
240
- const timeout = setTimeout(() => reject(new Error('Connection timeout after QR scan')), 60000);
253
+ const timeout = setTimeout(() => reject(new Error('Connection timeout QR was scanned but connection did not open in time')), 30000);
241
254
  const checkInterval = setInterval(() => {
242
255
  if (socketStatus === 'connected') {
243
256
  clearTimeout(timeout);
@@ -254,6 +267,67 @@ async function ensureConnected(cfg) {
254
267
  }
255
268
  return sock;
256
269
  }
270
+ // Non-blocking version for Connect node — starts socket, returns QR URL if needed, doesn't wait
271
+ async function connectOrGetQr(cfg) {
272
+ if (socketStatus === 'connected' && socketInstance) {
273
+ return { connected: true, message: 'Already connected' };
274
+ }
275
+ const antiBanCfg = {
276
+ messageDelayMinMs: cfg.messageDelayMinMs,
277
+ messageDelayMaxMs: cfg.messageDelayMaxMs,
278
+ burstSize: cfg.burstSize,
279
+ burstPauseMinMs: cfg.burstPauseMinMs,
280
+ burstPauseMaxMs: cfg.burstPauseMaxMs,
281
+ typingSimulation: cfg.typingSimulation,
282
+ dailySendLimit: cfg.dailySendLimit,
283
+ checkRecipientExists: cfg.checkRecipientExists,
284
+ };
285
+ socketConfig = antiBanCfg;
286
+ sessionPath = cfg.sessionPath;
287
+ if (!queue) {
288
+ queue = new p_queue_1.default({ concurrency: 1 });
289
+ }
290
+ const resolvedPath = expandHome(cfg.sessionPath);
291
+ const hasSession = node_fs_1.default.existsSync(resolvedPath) && node_fs_1.default.readdirSync(resolvedPath).length > 0;
292
+ // If session exists, just connect (no QR needed)
293
+ if (hasSession) {
294
+ await initSocket(antiBanCfg, cfg.sessionPath);
295
+ // Wait up to 15s for connection
296
+ await new Promise((resolve) => {
297
+ const check = setInterval(() => {
298
+ if (socketStatus === 'connected' || socketStatus === 'error' || socketStatus === 'logged_out') {
299
+ clearInterval(check);
300
+ resolve();
301
+ }
302
+ }, 500);
303
+ setTimeout(() => { clearInterval(check); resolve(); }, 15000);
304
+ });
305
+ if (socketStatus === 'connected') {
306
+ return { connected: true, message: 'Connected successfully' };
307
+ }
308
+ return { connected: false, message: `Connection status: ${socketStatus}. Check n8n logs for details.` };
309
+ }
310
+ // No session — start socket and wait for QR (up to 30s)
311
+ const sock = await initSocket(antiBanCfg, cfg.sessionPath);
312
+ const qrData = await new Promise((resolve) => {
313
+ qrResolve = resolve;
314
+ setTimeout(() => {
315
+ if (qrResolve) {
316
+ qrResolve(null);
317
+ qrResolve = null;
318
+ }
319
+ }, 30000);
320
+ });
321
+ if (!qrData) {
322
+ return { connected: false, message: 'QR code not generated yet. Try again in a moment.' };
323
+ }
324
+ // Return QR URL immediately — user opens it in browser, scans, then runs Connect again
325
+ return {
326
+ connected: false,
327
+ qrUrl: qrData.qrUrl,
328
+ message: `Open this URL in your browser to scan QR:\n${qrData.qrUrl}\n\nAfter scanning, run this node again to complete connection.`,
329
+ };
330
+ }
257
331
  async function simulateTyping(jid, content, cfg) {
258
332
  if (!cfg.typingSimulation || !socketInstance)
259
333
  return;
@@ -41,17 +41,11 @@ class WhatsAppConnect {
41
41
  for (let i = 0; i < items.length; i++) {
42
42
  try {
43
43
  if (operation === 'connect') {
44
- // Connect to WhatsApp this triggers QR flow on first run
45
- const sock = await (0, WhatsAppApiHelper_1.ensureConnected)(cfg);
46
- const status = (0, WhatsAppApiHelper_1.getConnectionStatus)();
44
+ // Non-blocking: returns QR URL immediately if needed, doesn't wait for scan
45
+ const result = await (0, WhatsAppApiHelper_1.connectOrGetQr)(cfg);
47
46
  returnData.push({
48
47
  json: {
49
- success: true,
50
- status: status.status,
51
- connected: status.connected,
52
- message: status.connected
53
- ? 'WhatsApp connected successfully. You can now use Send and Trigger nodes.'
54
- : 'Waiting for QR scan...',
48
+ ...result,
55
49
  sessionPath: cfg.sessionPath,
56
50
  },
57
51
  pairedItem: { item: i },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raevon/n8n-nodes-whatsapp",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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",