@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,
|
|
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:
|
|
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
|
|
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
|
-
//
|
|
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
|
-
},
|
|
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
|
|
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
|
-
//
|
|
45
|
-
const
|
|
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
|
-
|
|
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