@raevon/n8n-nodes-whatsapp 1.0.3 → 1.0.4
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',
|
|
@@ -47,8 +47,6 @@ 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;
|
|
54
52
|
// I8: Extracted ~ path expansion to avoid duplication
|
|
@@ -74,8 +72,6 @@ let sentTodayDate = '';
|
|
|
74
72
|
let reconnectAttempts = 0;
|
|
75
73
|
let reconnectTimer = null;
|
|
76
74
|
let qrResolve = null;
|
|
77
|
-
let qrHttpServer = null;
|
|
78
|
-
let qrTimeout = null; // C3: Store timeout ref for cleanup
|
|
79
75
|
let latestQr = null;
|
|
80
76
|
let generation = 0; // #10: Generation counter — prevents stale handlers on reconnect
|
|
81
77
|
function todayStartIso() {
|
|
@@ -100,39 +96,6 @@ function normalizeRecipient(to) {
|
|
|
100
96
|
}
|
|
101
97
|
return `${digits}@s.whatsapp.net`;
|
|
102
98
|
}
|
|
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
99
|
// #7: Reconnect backoff with jitter — exponential 1s→60s, ±20% randomization
|
|
137
100
|
async function scheduleReconnect(cfg) {
|
|
138
101
|
if (reconnectTimer)
|
|
@@ -188,6 +151,7 @@ async function initSocket(cfg, authPath) {
|
|
|
188
151
|
if (qr) {
|
|
189
152
|
latestQr = qr;
|
|
190
153
|
socketStatus = 'qr_ready';
|
|
154
|
+
// Output quickchart URL — works from anywhere, no localhost needed
|
|
191
155
|
if (qrResolve) {
|
|
192
156
|
const qrUrl = `https://quickchart.io/qr?text=${encodeURIComponent(qr)}&size=300`;
|
|
193
157
|
qrResolve({ qr, qrUrl });
|
|
@@ -198,8 +162,6 @@ async function initSocket(cfg, authPath) {
|
|
|
198
162
|
socketStatus = 'connected';
|
|
199
163
|
reconnectAttempts = 0;
|
|
200
164
|
latestQr = null;
|
|
201
|
-
clearQrTimeout(); // C3: Clear QR timeout on successful connection
|
|
202
|
-
stopQrServer();
|
|
203
165
|
return;
|
|
204
166
|
}
|
|
205
167
|
if (connection === 'close') {
|
|
@@ -208,7 +170,6 @@ async function initSocket(cfg, authPath) {
|
|
|
208
170
|
socketStatus = 'logged_out';
|
|
209
171
|
socketInstance = null;
|
|
210
172
|
++generation; // #10: Invalidate all handlers from this session
|
|
211
|
-
stopQrServer();
|
|
212
173
|
}
|
|
213
174
|
else {
|
|
214
175
|
socketInstance = null;
|
|
@@ -217,7 +178,6 @@ async function initSocket(cfg, authPath) {
|
|
|
217
178
|
}
|
|
218
179
|
});
|
|
219
180
|
// #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
181
|
return sock;
|
|
222
182
|
}
|
|
223
183
|
// --- Public API ---
|
|
@@ -225,7 +185,6 @@ async function getWhatsAppCredentials(credentials) {
|
|
|
225
185
|
var _a, _b;
|
|
226
186
|
return {
|
|
227
187
|
sessionPath: credentials.sessionPath || '~/.n8n/whatsapp-auth',
|
|
228
|
-
qrPort: credentials.qrPort || 3456,
|
|
229
188
|
messageDelayMinMs: credentials.messageDelayMinMs || 5000,
|
|
230
189
|
messageDelayMaxMs: credentials.messageDelayMaxMs || 9000,
|
|
231
190
|
burstSize: (_a = credentials.burstSize) !== null && _a !== void 0 ? _a : 20,
|
|
@@ -255,29 +214,25 @@ async function ensureConnected(cfg) {
|
|
|
255
214
|
if (!queue) {
|
|
256
215
|
queue = new p_queue_1.default({ concurrency: 1 });
|
|
257
216
|
}
|
|
258
|
-
//
|
|
217
|
+
// Check if session already exists
|
|
259
218
|
const resolvedPath = expandHome(cfg.sessionPath);
|
|
260
219
|
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
220
|
const sock = await initSocket(antiBanCfg, cfg.sessionPath);
|
|
265
221
|
// If no session, wait for QR scan
|
|
266
222
|
if (!hasSession) {
|
|
267
223
|
const qrData = await new Promise((resolve) => {
|
|
268
224
|
qrResolve = resolve;
|
|
269
|
-
//
|
|
270
|
-
|
|
225
|
+
// Timeout after 2 minutes — QR codes expire quickly
|
|
226
|
+
setTimeout(() => {
|
|
271
227
|
if (qrResolve) {
|
|
272
228
|
qrResolve(null);
|
|
273
229
|
qrResolve = null;
|
|
274
230
|
}
|
|
275
|
-
|
|
276
|
-
}, 300000);
|
|
231
|
+
}, 120000);
|
|
277
232
|
});
|
|
278
233
|
if (!qrData) {
|
|
279
234
|
throw new n8n_workflow_1.NodeApiError({}, {
|
|
280
|
-
message: 'QR code expired.
|
|
235
|
+
message: 'QR code expired. Run the Connect node again to get a new QR code.',
|
|
281
236
|
});
|
|
282
237
|
}
|
|
283
238
|
// 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
|
-
:
|
|
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