@raevon/n8n-nodes-whatsapp 1.0.3 → 1.0.5

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.
@@ -19,13 +19,6 @@ class WhatsAppApi {
19
19
  required: true,
20
20
  description: 'Directory path where WhatsApp session files are stored. After first QR scan, session persists here. Use the "WhatsApp Connect" node to scan QR on first setup.',
21
21
  },
22
- {
23
- displayName: 'QR Server Port',
24
- name: 'qrPort',
25
- type: 'number',
26
- default: 3456,
27
- description: 'Local HTTP server port for serving QR code during first-time setup. Only used when no session exists.',
28
- },
29
22
  {
30
23
  displayName: 'Message Delay Min (ms)',
31
24
  name: 'messageDelayMinMs',
@@ -1,7 +1,6 @@
1
1
  import { type WASocket } from '@whiskeysockets/baileys';
2
2
  export interface WhatsAppCredentials {
3
3
  sessionPath: string;
4
- qrPort: number;
5
4
  messageDelayMinMs: number;
6
5
  messageDelayMaxMs: number;
7
6
  burstSize: number;
@@ -47,10 +47,20 @@ const baileys_1 = __importStar(require("@whiskeysockets/baileys"));
47
47
  const p_queue_1 = __importDefault(require("p-queue"));
48
48
  const node_path_1 = __importDefault(require("node:path"));
49
49
  const node_fs_1 = __importDefault(require("node:fs"));
50
- const node_http_1 = __importDefault(require("node:http"));
51
- const node_url_1 = require("node:url");
52
50
  const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
53
51
  const randBetween = (min, max) => max > min ? min + Math.floor(Math.random() * (max - min + 1)) : min;
52
+ // Silent logger — Baileys expects pino-compatible logger with these methods
53
+ const noop = () => { };
54
+ const silentLogger = {
55
+ level: 'silent',
56
+ info: noop,
57
+ warn: noop,
58
+ error: noop,
59
+ debug: noop,
60
+ fatal: noop,
61
+ trace: noop,
62
+ child: () => silentLogger,
63
+ };
54
64
  // I8: Extracted ~ path expansion to avoid duplication
55
65
  function expandHome(p) {
56
66
  if (!p.startsWith('~'))
@@ -74,8 +84,6 @@ let sentTodayDate = '';
74
84
  let reconnectAttempts = 0;
75
85
  let reconnectTimer = null;
76
86
  let qrResolve = null;
77
- let qrHttpServer = null;
78
- let qrTimeout = null; // C3: Store timeout ref for cleanup
79
87
  let latestQr = null;
80
88
  let generation = 0; // #10: Generation counter — prevents stale handlers on reconnect
81
89
  function todayStartIso() {
@@ -100,39 +108,6 @@ function normalizeRecipient(to) {
100
108
  }
101
109
  return `${digits}@s.whatsapp.net`;
102
110
  }
103
- function startQrServer(port) {
104
- return new Promise((resolve) => {
105
- if (qrHttpServer) {
106
- resolve();
107
- return;
108
- }
109
- qrHttpServer = node_http_1.default.createServer((req, res) => {
110
- const url = new node_url_1.URL(req.url || '/', `http://localhost:${port}`);
111
- if (url.pathname === '/qr' && latestQr) {
112
- const qrUrl = `https://quickchart.io/qr?text=${encodeURIComponent(latestQr)}&size=300`;
113
- res.writeHead(200, { 'Content-Type': 'text/html' });
114
- res.end(`<!DOCTYPE html><html><head><title>WhatsApp QR</title><style>body{display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#111;color:white;font-family:sans-serif}img{border-radius:12px}p{margin-top:20px;text-align:center}</style></head><body><div><img src="${qrUrl}" width="300"/><p>Scan with WhatsApp → Settings → Linked Devices → Link a Device</p><p style="color:#888;font-size:12px">This page auto-refreshes. Session saves after scan.</p></div></body></html>`);
115
- }
116
- else {
117
- res.writeHead(404);
118
- res.end('Not found');
119
- }
120
- });
121
- qrHttpServer.listen(port, () => resolve());
122
- });
123
- }
124
- function stopQrServer() {
125
- if (qrHttpServer) {
126
- qrHttpServer.close();
127
- qrHttpServer = null;
128
- }
129
- }
130
- function clearQrTimeout() {
131
- if (qrTimeout) {
132
- clearTimeout(qrTimeout);
133
- qrTimeout = null;
134
- }
135
- }
136
111
  // #7: Reconnect backoff with jitter — exponential 1s→60s, ±20% randomization
137
112
  async function scheduleReconnect(cfg) {
138
113
  if (reconnectTimer)
@@ -161,14 +136,14 @@ async function initSocket(cfg, authPath) {
161
136
  browser: baileys_1.Browsers.ubuntu('n8n WhatsApp Node'),
162
137
  auth: {
163
138
  creds: state.creds,
164
- keys: (0, baileys_1.makeCacheableSignalKeyStore)(state.keys, { level: 'silent' }),
139
+ keys: (0, baileys_1.makeCacheableSignalKeyStore)(state.keys, silentLogger),
165
140
  },
166
141
  // #11: Stealth flags — don't go online, don't sync history, no link previews
167
142
  markOnlineOnConnect: false,
168
143
  syncFullHistory: false,
169
144
  shouldSyncHistoryMessage: () => false,
170
145
  generateHighQualityLinkPreview: false,
171
- logger: { level: 'silent' },
146
+ logger: silentLogger,
172
147
  });
173
148
  socketInstance = sock;
174
149
  socketStatus = 'connecting';
@@ -188,6 +163,7 @@ async function initSocket(cfg, authPath) {
188
163
  if (qr) {
189
164
  latestQr = qr;
190
165
  socketStatus = 'qr_ready';
166
+ // Output quickchart URL — works from anywhere, no localhost needed
191
167
  if (qrResolve) {
192
168
  const qrUrl = `https://quickchart.io/qr?text=${encodeURIComponent(qr)}&size=300`;
193
169
  qrResolve({ qr, qrUrl });
@@ -198,8 +174,6 @@ async function initSocket(cfg, authPath) {
198
174
  socketStatus = 'connected';
199
175
  reconnectAttempts = 0;
200
176
  latestQr = null;
201
- clearQrTimeout(); // C3: Clear QR timeout on successful connection
202
- stopQrServer();
203
177
  return;
204
178
  }
205
179
  if (connection === 'close') {
@@ -208,7 +182,6 @@ async function initSocket(cfg, authPath) {
208
182
  socketStatus = 'logged_out';
209
183
  socketInstance = null;
210
184
  ++generation; // #10: Invalidate all handlers from this session
211
- stopQrServer();
212
185
  }
213
186
  else {
214
187
  socketInstance = null;
@@ -217,7 +190,6 @@ async function initSocket(cfg, authPath) {
217
190
  }
218
191
  });
219
192
  // #8: Fire-and-forget — no receipt tracking, no delivery acks, minimal protocol chatter
220
- // (Baileys doesn't auto-track receipts unless you call readMessages/sendReceipt — we don't)
221
193
  return sock;
222
194
  }
223
195
  // --- Public API ---
@@ -225,7 +197,6 @@ async function getWhatsAppCredentials(credentials) {
225
197
  var _a, _b;
226
198
  return {
227
199
  sessionPath: credentials.sessionPath || '~/.n8n/whatsapp-auth',
228
- qrPort: credentials.qrPort || 3456,
229
200
  messageDelayMinMs: credentials.messageDelayMinMs || 5000,
230
201
  messageDelayMaxMs: credentials.messageDelayMaxMs || 9000,
231
202
  burstSize: (_a = credentials.burstSize) !== null && _a !== void 0 ? _a : 20,
@@ -255,29 +226,25 @@ async function ensureConnected(cfg) {
255
226
  if (!queue) {
256
227
  queue = new p_queue_1.default({ concurrency: 1 });
257
228
  }
258
- // Start QR server for first-time setup
229
+ // Check if session already exists
259
230
  const resolvedPath = expandHome(cfg.sessionPath);
260
231
  const hasSession = node_fs_1.default.existsSync(resolvedPath) && node_fs_1.default.readdirSync(resolvedPath).length > 0;
261
- if (!hasSession) {
262
- await startQrServer(cfg.qrPort);
263
- }
264
232
  const sock = await initSocket(antiBanCfg, cfg.sessionPath);
265
233
  // If no session, wait for QR scan
266
234
  if (!hasSession) {
267
235
  const qrData = await new Promise((resolve) => {
268
236
  qrResolve = resolve;
269
- // C3: Store timeout ref so it can be cleared on successful scan
270
- qrTimeout = setTimeout(() => {
237
+ // Timeout after 2 minutes QR codes expire quickly
238
+ setTimeout(() => {
271
239
  if (qrResolve) {
272
240
  qrResolve(null);
273
241
  qrResolve = null;
274
242
  }
275
- qrTimeout = null;
276
- }, 300000);
243
+ }, 120000);
277
244
  });
278
245
  if (!qrData) {
279
246
  throw new n8n_workflow_1.NodeApiError({}, {
280
- message: 'QR code expired. Please try again.',
247
+ message: 'QR code expired. Run the Connect node again to get a new QR code.',
281
248
  });
282
249
  }
283
250
  // Wait for connection to open after scan
@@ -50,9 +50,8 @@ class WhatsAppConnect {
50
50
  status: status.status,
51
51
  connected: status.connected,
52
52
  message: status.connected
53
- ? 'WhatsApp connected successfully'
54
- : `QR code ready — open http://localhost:${cfg.qrPort}/qr in your browser to scan`,
55
- qrUrl: status.qrAvailable ? `http://localhost:${cfg.qrPort}/qr` : null,
53
+ ? 'WhatsApp connected successfully. You can now use Send and Trigger nodes.'
54
+ : 'Waiting for QR scan...',
56
55
  sessionPath: cfg.sessionPath,
57
56
  },
58
57
  pairedItem: { item: i },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raevon/n8n-nodes-whatsapp",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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",