@raevon/n8n-nodes-whatsapp 2.0.6 → 2.0.8
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.
|
@@ -11,5 +11,6 @@ export interface WhatsAppCredentials {
|
|
|
11
11
|
}
|
|
12
12
|
export declare function getServerUrl(cfg: WhatsAppCredentials): string;
|
|
13
13
|
export declare function ensureServerRunning(cfg: WhatsAppCredentials): Promise<void>;
|
|
14
|
+
export declare function stopServer(cfg: WhatsAppCredentials): Promise<void>;
|
|
14
15
|
export declare function disconnect(): Promise<void>;
|
|
15
16
|
export declare function getWhatsAppCredentials(credentials: Record<string, any>): Promise<WhatsAppCredentials>;
|
|
@@ -38,8 +38,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.getServerUrl = getServerUrl;
|
|
40
40
|
exports.ensureServerRunning = ensureServerRunning;
|
|
41
|
+
exports.stopServer = stopServer;
|
|
41
42
|
exports.disconnect = disconnect;
|
|
42
43
|
exports.getWhatsAppCredentials = getWhatsAppCredentials;
|
|
44
|
+
const node_child_process_1 = require("node:child_process");
|
|
43
45
|
const node_path_1 = __importDefault(require("node:path"));
|
|
44
46
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
45
47
|
const node_net_1 = __importDefault(require("node:net"));
|
|
@@ -356,9 +358,54 @@ async function ensureServerRunning(cfg) {
|
|
|
356
358
|
return; // Server alive, use it
|
|
357
359
|
}
|
|
358
360
|
catch { }
|
|
359
|
-
// Port in use but not responding —
|
|
360
|
-
|
|
361
|
-
|
|
361
|
+
// Port in use but not responding — try to kill via /proc
|
|
362
|
+
try {
|
|
363
|
+
const out = (0, node_child_process_1.execSync)(`cat /proc/net/tcp | awk '{if(NR>1){split($2,a,\":\");if(strtonum("0x"a[2])==${port})print $10}}'`, { encoding: 'utf-8' }).trim();
|
|
364
|
+
if (out) {
|
|
365
|
+
// Find PID from inode
|
|
366
|
+
const inode = out;
|
|
367
|
+
const pidOut = (0, node_child_process_1.execSync)(`for p in /proc/[0-9]*/fd/*; do if readlink "$p" 2>/dev/null | grep -q "socket:\\[${inode}]"; then echo "$p" | cut -d/ -f3; break; fi; done`, { encoding: 'utf-8' }).trim();
|
|
368
|
+
if (pidOut)
|
|
369
|
+
(0, node_child_process_1.execSync)(`kill -9 ${pidOut}`);
|
|
370
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch { }
|
|
374
|
+
// Try again
|
|
375
|
+
if (await isPortOpen(port)) {
|
|
376
|
+
await startServer(cfg);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
throw new Error(`Port ${port} occupied by unknown process. Restart n8n container to clear.`);
|
|
380
|
+
}
|
|
381
|
+
async function stopServer(cfg) {
|
|
382
|
+
const port = getPort(cfg);
|
|
383
|
+
// First try to shut down gracefully via the server's own /signout endpoint
|
|
384
|
+
try {
|
|
385
|
+
const res = await fetch(`http://127.0.0.1:${port}/signout`, { method: 'POST', signal: AbortSignal.timeout(3000) });
|
|
386
|
+
if (res.ok) {
|
|
387
|
+
servers.delete(port);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
catch { }
|
|
392
|
+
// If that fails, clean up in-memory state
|
|
393
|
+
const state = servers.get(port);
|
|
394
|
+
if (state) {
|
|
395
|
+
if (state.socket) {
|
|
396
|
+
try {
|
|
397
|
+
state.socket.end(new Error('Server stop'));
|
|
398
|
+
}
|
|
399
|
+
catch { }
|
|
400
|
+
}
|
|
401
|
+
if (state.server) {
|
|
402
|
+
try {
|
|
403
|
+
state.server.close();
|
|
404
|
+
}
|
|
405
|
+
catch { }
|
|
406
|
+
}
|
|
407
|
+
servers.delete(port);
|
|
408
|
+
}
|
|
362
409
|
}
|
|
363
410
|
async function disconnect() { }
|
|
364
411
|
async function getWhatsAppCredentials(credentials) {
|
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.WhatsAppConnect = void 0;
|
|
7
7
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
8
|
const WhatsAppApiHelper_1 = require("./WhatsAppApiHelper");
|
|
9
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
10
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
11
|
function expandHome(p) {
|
|
12
12
|
if (!p.startsWith('~'))
|
|
13
13
|
return p;
|
|
@@ -22,7 +22,7 @@ class WhatsAppConnect {
|
|
|
22
22
|
group: ['transform'],
|
|
23
23
|
version: 1,
|
|
24
24
|
subtitle: '={{$parameter["operation"]}}',
|
|
25
|
-
description: 'Connect to WhatsApp —
|
|
25
|
+
description: 'Connect to WhatsApp — manage server, scan QR, sign out',
|
|
26
26
|
defaults: { name: 'WhatsApp Connect' },
|
|
27
27
|
inputs: ['main'],
|
|
28
28
|
outputs: ['main'],
|
|
@@ -34,8 +34,10 @@ class WhatsAppConnect {
|
|
|
34
34
|
type: 'options',
|
|
35
35
|
noDataExpression: true,
|
|
36
36
|
options: [
|
|
37
|
-
{ name: 'Connect', value: 'connect', description: 'Start
|
|
37
|
+
{ name: 'Connect', value: 'connect', description: 'Start server and connect to WhatsApp', action: 'Connect to WhatsApp' },
|
|
38
38
|
{ name: 'Get Status', value: 'status', description: 'Get server and connection status', action: 'Get status' },
|
|
39
|
+
{ name: 'Start Server', value: 'startServer', description: 'Start the WhatsApp background server', action: 'Start server' },
|
|
40
|
+
{ name: 'Stop Server', value: 'stopServer', description: 'Stop the WhatsApp background server', action: 'Stop server' },
|
|
39
41
|
{ name: 'Sign Out', value: 'signOut', description: 'Stop server and delete session', action: 'Sign out' },
|
|
40
42
|
],
|
|
41
43
|
default: 'connect',
|
|
@@ -51,42 +53,49 @@ class WhatsAppConnect {
|
|
|
51
53
|
const operation = this.getNodeParameter('operation', 0);
|
|
52
54
|
for (let i = 0; i < items.length; i++) {
|
|
53
55
|
try {
|
|
54
|
-
if (operation === '
|
|
55
|
-
|
|
56
|
+
if (operation === 'startServer') {
|
|
57
|
+
await (0, WhatsAppApiHelper_1.ensureServerRunning)(cfg);
|
|
58
|
+
returnData.push({
|
|
59
|
+
json: { success: true, message: 'Server started', port: (0, WhatsAppApiHelper_1.getServerUrl)(cfg), sessionPath: cfg.sessionPath },
|
|
60
|
+
pairedItem: { item: i },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (operation === 'stopServer') {
|
|
64
|
+
await (0, WhatsAppApiHelper_1.stopServer)(cfg);
|
|
65
|
+
returnData.push({
|
|
66
|
+
json: { success: true, message: 'Server stopped', sessionPath: cfg.sessionPath },
|
|
67
|
+
pairedItem: { item: i },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else if (operation === 'connect') {
|
|
56
71
|
try {
|
|
57
72
|
await (0, WhatsAppApiHelper_1.ensureServerRunning)(cfg);
|
|
58
73
|
}
|
|
59
|
-
catch (
|
|
74
|
+
catch (err) {
|
|
60
75
|
returnData.push({
|
|
61
|
-
json: { connected: false, error:
|
|
76
|
+
json: { connected: false, error: err.message, sessionPath: cfg.sessionPath },
|
|
62
77
|
pairedItem: { item: i },
|
|
63
78
|
});
|
|
64
79
|
continue;
|
|
65
80
|
}
|
|
66
|
-
// Connect to the server
|
|
67
81
|
try {
|
|
68
82
|
const response = await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/connect`, { method: 'POST' });
|
|
69
83
|
const result = await response.json();
|
|
70
|
-
returnData.push({
|
|
71
|
-
json: { ...result, sessionPath: cfg.sessionPath },
|
|
72
|
-
pairedItem: { item: i },
|
|
73
|
-
});
|
|
84
|
+
returnData.push({ json: { ...result, sessionPath: cfg.sessionPath }, pairedItem: { item: i } });
|
|
74
85
|
}
|
|
75
|
-
catch (
|
|
86
|
+
catch (err) {
|
|
76
87
|
returnData.push({
|
|
77
|
-
json: { connected: false, error: `
|
|
88
|
+
json: { connected: false, error: `Server unreachable: ${err.message}`, sessionPath: cfg.sessionPath },
|
|
78
89
|
pairedItem: { item: i },
|
|
79
90
|
});
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
else if (operation === 'status') {
|
|
83
94
|
try {
|
|
95
|
+
await (0, WhatsAppApiHelper_1.ensureServerRunning)(cfg);
|
|
84
96
|
const response = await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/status`);
|
|
85
97
|
const result = await response.json();
|
|
86
|
-
returnData.push({
|
|
87
|
-
json: { ...result, sessionPath: cfg.sessionPath },
|
|
88
|
-
pairedItem: { item: i },
|
|
89
|
-
});
|
|
98
|
+
returnData.push({ json: { ...result, sessionPath: cfg.sessionPath }, pairedItem: { item: i } });
|
|
90
99
|
}
|
|
91
100
|
catch {
|
|
92
101
|
returnData.push({
|
|
@@ -96,17 +105,10 @@ class WhatsAppConnect {
|
|
|
96
105
|
}
|
|
97
106
|
}
|
|
98
107
|
else if (operation === 'signOut') {
|
|
99
|
-
|
|
100
|
-
await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/signout`, { method: 'POST' });
|
|
101
|
-
}
|
|
102
|
-
catch { }
|
|
103
|
-
// Delete session files
|
|
108
|
+
await (0, WhatsAppApiHelper_1.stopServer)(cfg);
|
|
104
109
|
const resolvedPath = expandHome(cfg.sessionPath);
|
|
105
110
|
if (node_fs_1.default.existsSync(resolvedPath)) {
|
|
106
|
-
|
|
107
|
-
for (const file of files) {
|
|
108
|
-
node_fs_1.default.unlinkSync(node_path_1.default.join(resolvedPath, file));
|
|
109
|
-
}
|
|
111
|
+
node_fs_1.default.readdirSync(resolvedPath).forEach(f => node_fs_1.default.unlinkSync(node_path_1.default.join(resolvedPath, f)));
|
|
110
112
|
node_fs_1.default.rmdirSync(resolvedPath);
|
|
111
113
|
}
|
|
112
114
|
returnData.push({
|
package/package.json
CHANGED