@raevon/n8n-nodes-whatsapp 1.0.13 → 2.0.0
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.
- package/dist/nodes/WhatsApp/WhatsAppApiHelper.d.ts +3 -52
- package/dist/nodes/WhatsApp/WhatsAppApiHelper.js +76 -468
- package/dist/nodes/WhatsApp/WhatsAppConnect.node.js +50 -20
- package/dist/nodes/WhatsApp/WhatsAppSend.node.js +30 -70
- package/dist/nodes/WhatsApp/WhatsAppServer.d.ts +1 -0
- package/dist/nodes/WhatsApp/WhatsAppServer.js +390 -0
- package/dist/nodes/WhatsApp/WhatsAppTrigger.node.js +27 -41
- package/package.json +1 -1
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.WhatsAppConnect = void 0;
|
|
4
7
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
8
|
const WhatsAppApiHelper_1 = require("./WhatsAppApiHelper");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
function expandHome(p) {
|
|
12
|
+
if (!p.startsWith('~'))
|
|
13
|
+
return p;
|
|
14
|
+
return node_path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', p.slice(1));
|
|
15
|
+
}
|
|
6
16
|
class WhatsAppConnect {
|
|
7
17
|
constructor() {
|
|
8
18
|
this.description = {
|
|
@@ -12,7 +22,7 @@ class WhatsAppConnect {
|
|
|
12
22
|
group: ['transform'],
|
|
13
23
|
version: 1,
|
|
14
24
|
subtitle: '={{$parameter["operation"]}}',
|
|
15
|
-
description: 'Connect to WhatsApp — scan QR
|
|
25
|
+
description: 'Connect to WhatsApp — starts background server, scan QR on first run',
|
|
16
26
|
defaults: { name: 'WhatsApp Connect' },
|
|
17
27
|
inputs: ['main'],
|
|
18
28
|
outputs: ['main'],
|
|
@@ -24,8 +34,9 @@ class WhatsAppConnect {
|
|
|
24
34
|
type: 'options',
|
|
25
35
|
noDataExpression: true,
|
|
26
36
|
options: [
|
|
27
|
-
{ name: 'Connect', value: 'connect', description: '
|
|
28
|
-
{ name: 'Get Status', value: 'status', description: 'Get
|
|
37
|
+
{ name: 'Connect', value: 'connect', description: 'Start WhatsApp server and connect', action: 'Connect to WhatsApp' },
|
|
38
|
+
{ name: 'Get Status', value: 'status', description: 'Get server and connection status', action: 'Get status' },
|
|
39
|
+
{ name: 'Sign Out', value: 'signOut', description: 'Stop server and delete session', action: 'Sign out' },
|
|
29
40
|
],
|
|
30
41
|
default: 'connect',
|
|
31
42
|
},
|
|
@@ -41,11 +52,11 @@ class WhatsAppConnect {
|
|
|
41
52
|
for (let i = 0; i < items.length; i++) {
|
|
42
53
|
try {
|
|
43
54
|
if (operation === 'connect') {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
// Start the background WhatsApp server
|
|
56
|
+
await (0, WhatsAppApiHelper_1.ensureServerRunning)(cfg);
|
|
57
|
+
// Connect to the server
|
|
58
|
+
const response = await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/connect`, { method: 'POST' });
|
|
59
|
+
const result = await response.json();
|
|
49
60
|
returnData.push({
|
|
50
61
|
json: {
|
|
51
62
|
...result,
|
|
@@ -55,25 +66,44 @@ class WhatsAppConnect {
|
|
|
55
66
|
});
|
|
56
67
|
}
|
|
57
68
|
else if (operation === 'status') {
|
|
58
|
-
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/status`);
|
|
71
|
+
const result = await response.json();
|
|
72
|
+
returnData.push({
|
|
73
|
+
json: { ...result, sessionPath: cfg.sessionPath },
|
|
74
|
+
pairedItem: { item: i },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
returnData.push({
|
|
79
|
+
json: { status: 'server_not_running', connected: false, sessionPath: cfg.sessionPath },
|
|
80
|
+
pairedItem: { item: i },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (operation === 'signOut') {
|
|
85
|
+
try {
|
|
86
|
+
await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/signout`, { method: 'POST' });
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
// Delete session files
|
|
90
|
+
const resolvedPath = expandHome(cfg.sessionPath);
|
|
91
|
+
if (node_fs_1.default.existsSync(resolvedPath)) {
|
|
92
|
+
const files = node_fs_1.default.readdirSync(resolvedPath);
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
node_fs_1.default.unlinkSync(node_path_1.default.join(resolvedPath, file));
|
|
95
|
+
}
|
|
96
|
+
node_fs_1.default.rmdirSync(resolvedPath);
|
|
97
|
+
}
|
|
59
98
|
returnData.push({
|
|
60
|
-
json: {
|
|
61
|
-
...status,
|
|
62
|
-
sessionPath: cfg.sessionPath,
|
|
63
|
-
},
|
|
99
|
+
json: { success: true, message: 'Signed out. Session deleted.', sessionPath: cfg.sessionPath },
|
|
64
100
|
pairedItem: { item: i },
|
|
65
101
|
});
|
|
66
102
|
}
|
|
67
103
|
}
|
|
68
104
|
catch (error) {
|
|
69
105
|
if (this.continueOnFail()) {
|
|
70
|
-
returnData.push({
|
|
71
|
-
json: {
|
|
72
|
-
error: error.message,
|
|
73
|
-
success: false,
|
|
74
|
-
},
|
|
75
|
-
pairedItem: { item: i },
|
|
76
|
-
});
|
|
106
|
+
returnData.push({ json: { error: error.message, success: false }, pairedItem: { item: i } });
|
|
77
107
|
continue;
|
|
78
108
|
}
|
|
79
109
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
@@ -12,7 +12,7 @@ class WhatsAppSend {
|
|
|
12
12
|
group: ['transform'],
|
|
13
13
|
version: 1,
|
|
14
14
|
subtitle: '={{$parameter["operation"]}}',
|
|
15
|
-
description: 'Send WhatsApp messages
|
|
15
|
+
description: 'Send WhatsApp messages via background server',
|
|
16
16
|
defaults: { name: 'WhatsApp Send' },
|
|
17
17
|
inputs: ['main'],
|
|
18
18
|
outputs: ['main'],
|
|
@@ -38,7 +38,7 @@ class WhatsAppSend {
|
|
|
38
38
|
type: 'string',
|
|
39
39
|
default: '',
|
|
40
40
|
required: true,
|
|
41
|
-
description: 'Phone number (e.g., +1234567890) or WhatsApp JID
|
|
41
|
+
description: 'Phone number (e.g., +1234567890) or WhatsApp JID',
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
displayName: 'Text',
|
|
@@ -64,7 +64,7 @@ class WhatsAppSend {
|
|
|
64
64
|
type: 'string',
|
|
65
65
|
default: '',
|
|
66
66
|
displayOptions: { show: { operation: ['sendImage', 'sendDocument'] } },
|
|
67
|
-
description: 'Optional caption
|
|
67
|
+
description: 'Optional caption',
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
displayName: 'File Name',
|
|
@@ -75,21 +75,13 @@ class WhatsAppSend {
|
|
|
75
75
|
displayOptions: { show: { operation: ['sendDocument'] } },
|
|
76
76
|
description: 'Name of the file being sent',
|
|
77
77
|
},
|
|
78
|
-
{
|
|
79
|
-
displayName: 'MIME Type',
|
|
80
|
-
name: 'mimeType',
|
|
81
|
-
type: 'string',
|
|
82
|
-
default: '',
|
|
83
|
-
displayOptions: { show: { operation: ['sendImage', 'sendDocument', 'sendAudio'] } },
|
|
84
|
-
description: 'MIME type (auto-detected if empty)',
|
|
85
|
-
},
|
|
86
78
|
{
|
|
87
79
|
displayName: 'Voice Note',
|
|
88
80
|
name: 'ptt',
|
|
89
81
|
type: 'boolean',
|
|
90
82
|
default: false,
|
|
91
83
|
displayOptions: { show: { operation: ['sendAudio'] } },
|
|
92
|
-
description: 'Send as voice note
|
|
84
|
+
description: 'Send as voice note',
|
|
93
85
|
},
|
|
94
86
|
{
|
|
95
87
|
displayName: 'Latitude',
|
|
@@ -109,22 +101,6 @@ class WhatsAppSend {
|
|
|
109
101
|
displayOptions: { show: { operation: ['sendLocation'] } },
|
|
110
102
|
description: 'Location longitude',
|
|
111
103
|
},
|
|
112
|
-
{
|
|
113
|
-
displayName: 'Location Name',
|
|
114
|
-
name: 'locationName',
|
|
115
|
-
type: 'string',
|
|
116
|
-
default: '',
|
|
117
|
-
displayOptions: { show: { operation: ['sendLocation'] } },
|
|
118
|
-
description: 'Optional name for the location',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
displayName: 'Location Address',
|
|
122
|
-
name: 'locationAddress',
|
|
123
|
-
type: 'string',
|
|
124
|
-
default: '',
|
|
125
|
-
displayOptions: { show: { operation: ['sendLocation'] } },
|
|
126
|
-
description: 'Optional address for the location',
|
|
127
|
-
},
|
|
128
104
|
],
|
|
129
105
|
};
|
|
130
106
|
}
|
|
@@ -133,85 +109,69 @@ class WhatsAppSend {
|
|
|
133
109
|
const returnData = [];
|
|
134
110
|
const credentials = await this.getCredentials('whatsappApi');
|
|
135
111
|
const cfg = await (0, WhatsAppApiHelper_1.getWhatsAppCredentials)(credentials);
|
|
136
|
-
|
|
112
|
+
// Ensure server is running
|
|
113
|
+
await (0, WhatsAppApiHelper_1.ensureServerRunning)(cfg);
|
|
114
|
+
const serverUrl = (0, WhatsAppApiHelper_1.getServerUrl)(cfg);
|
|
137
115
|
for (let i = 0; i < items.length; i++) {
|
|
138
116
|
try {
|
|
139
117
|
const operation = this.getNodeParameter('operation', i);
|
|
140
118
|
const recipient = this.getNodeParameter('recipient', i);
|
|
141
|
-
|
|
142
|
-
let content = {};
|
|
119
|
+
let body = { to: recipient };
|
|
143
120
|
switch (operation) {
|
|
144
121
|
case 'sendText':
|
|
145
|
-
|
|
122
|
+
body.text = this.getNodeParameter('text', i);
|
|
146
123
|
break;
|
|
147
124
|
case 'sendImage': {
|
|
148
125
|
const binaryProperty = this.getNodeParameter('binaryProperty', i);
|
|
149
126
|
const buffer = await this.helpers.getBinaryDataBuffer(i, binaryProperty);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
caption: this.getNodeParameter('caption', i) || undefined,
|
|
153
|
-
mimeType: this.getNodeParameter('mimeType', i) || undefined,
|
|
154
|
-
};
|
|
127
|
+
body.image = buffer.toString('base64');
|
|
128
|
+
body.caption = this.getNodeParameter('caption', i) || undefined;
|
|
155
129
|
break;
|
|
156
130
|
}
|
|
157
131
|
case 'sendDocument': {
|
|
158
132
|
const binaryProperty = this.getNodeParameter('binaryProperty', i);
|
|
159
133
|
const buffer = await this.helpers.getBinaryDataBuffer(i, binaryProperty);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
caption: this.getNodeParameter('caption', i) || undefined,
|
|
164
|
-
mimeType: this.getNodeParameter('mimeType', i) || undefined,
|
|
165
|
-
};
|
|
134
|
+
body.document = buffer.toString('base64');
|
|
135
|
+
body.fileName = this.getNodeParameter('fileName', i);
|
|
136
|
+
body.caption = this.getNodeParameter('caption', i) || undefined;
|
|
166
137
|
break;
|
|
167
138
|
}
|
|
168
139
|
case 'sendAudio': {
|
|
169
140
|
const binaryProperty = this.getNodeParameter('binaryProperty', i);
|
|
170
141
|
const buffer = await this.helpers.getBinaryDataBuffer(i, binaryProperty);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
ptt: this.getNodeParameter('ptt', i),
|
|
174
|
-
mimeType: this.getNodeParameter('mimeType', i) || undefined,
|
|
175
|
-
};
|
|
142
|
+
body.audio = buffer.toString('base64');
|
|
143
|
+
body.ptt = this.getNodeParameter('ptt', i);
|
|
176
144
|
break;
|
|
177
145
|
}
|
|
178
146
|
case 'sendLocation':
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
degreesLongitude: this.getNodeParameter('longitude', i),
|
|
183
|
-
name: this.getNodeParameter('locationName', i) || undefined,
|
|
184
|
-
address: this.getNodeParameter('locationAddress', i) || undefined,
|
|
185
|
-
},
|
|
147
|
+
body.location = {
|
|
148
|
+
degreesLatitude: this.getNodeParameter('latitude', i),
|
|
149
|
+
degreesLongitude: this.getNodeParameter('longitude', i),
|
|
186
150
|
};
|
|
187
151
|
break;
|
|
188
152
|
}
|
|
189
|
-
const
|
|
153
|
+
const response = await fetch(`${serverUrl}/send`, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
body: JSON.stringify(body),
|
|
157
|
+
});
|
|
158
|
+
const result = await response.json();
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
throw new Error(result.error || 'Send failed');
|
|
161
|
+
}
|
|
190
162
|
returnData.push({
|
|
191
|
-
json: {
|
|
192
|
-
...result,
|
|
193
|
-
operation,
|
|
194
|
-
success: true,
|
|
195
|
-
},
|
|
163
|
+
json: { ...result, operation, success: true },
|
|
196
164
|
pairedItem: { item: i },
|
|
197
165
|
});
|
|
198
166
|
}
|
|
199
167
|
catch (error) {
|
|
200
168
|
if (this.continueOnFail()) {
|
|
201
|
-
returnData.push({
|
|
202
|
-
json: {
|
|
203
|
-
error: error.message,
|
|
204
|
-
success: false,
|
|
205
|
-
},
|
|
206
|
-
pairedItem: { item: i },
|
|
207
|
-
});
|
|
169
|
+
returnData.push({ json: { error: error.message, success: false }, pairedItem: { item: i } });
|
|
208
170
|
continue;
|
|
209
171
|
}
|
|
210
172
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
211
173
|
}
|
|
212
174
|
}
|
|
213
|
-
// Disconnect after sending — prevents conflict with other n8n workers
|
|
214
|
-
await (0, WhatsAppApiHelper_1.disconnect)();
|
|
215
175
|
return [returnData];
|
|
216
176
|
}
|
|
217
177
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|