n8n-nodes-wappflow 0.1.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/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # n8n-nodes-wappflow
2
+
3
+ This is an n8n community node for the **Wappflow API**. It allows you to automate WhatsApp messaging, group management, and instance control directly within [n8n](https://n8n.io).
4
+
5
+ [Wappflow](https://wappflow.com) is a powerful WhatsApp API provider.
6
+
7
+ ## Operations
8
+
9
+ ### Message
10
+ - **Send Text**: Send a text message to a phone number.
11
+ - **Send Media**: Send images, videos, audio/voice notes, or documents.
12
+
13
+ ### Instance
14
+ - **Create**: Create a new WhatsApp instance.
15
+ - **List**: List all instances associated with your account.
16
+ - **Logout**: Logout a specific instance.
17
+ - **Delete**: Delete an instance.
18
+
19
+ ### Group
20
+ - **Create**: Create a new group with participants.
21
+ - **List**: List all groups for an instance.
22
+
23
+ ### Chat
24
+ - **Sync Contacts**: Sync/Refresh contacts.
25
+ - **Check Number**: Verify if a phone number is registered on WhatsApp.
26
+
27
+ ## Credentials
28
+
29
+ You will need a **Wappflow API Key**.
30
+ 1. Go to your Wappflow Dashboard.
31
+ 2. Create/Copy your API Key.
32
+ 3. In n8n, add a generic **Wappflow API** credential.
33
+ 4. Enter your **API Key** and **Base URL** (e.g., `https://api.wappflow.com`).
34
+
35
+ ## Installation
36
+
37
+ Follow the [n8n community node installation guide](https://docs.n8n.io/integrations/community-nodes/installation/).
38
+
39
+ Local installation:
40
+ ```bash
41
+ npm install n8n-nodes-wappflow
42
+ ```
43
+
44
+ ## License
45
+
46
+ MIT
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WappflowApi = void 0;
4
+ class WappflowApi {
5
+ constructor() {
6
+ this.name = 'wappflowApi';
7
+ this.displayName = 'Wappflow API';
8
+ // documentationUrl = 'https://wappflow.com/docs';
9
+ this.properties = [
10
+ {
11
+ displayName: 'API Key',
12
+ name: 'apiKey',
13
+ type: 'string',
14
+ default: '',
15
+ typeOptions: {
16
+ password: true,
17
+ },
18
+ },
19
+ {
20
+ displayName: 'Base URL',
21
+ name: 'baseUrl',
22
+ type: 'string',
23
+ default: 'https://api.wappflow.com',
24
+ description: 'The base URL of the Wappflow API (e.g. your self-hosted instance).',
25
+ },
26
+ ];
27
+ }
28
+ }
29
+ exports.WappflowApi = WappflowApi;
@@ -0,0 +1,534 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WappflowApi = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ class WappflowApi {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'Wappflow API',
9
+ name: 'wappflowApi',
10
+ icon: 'file:wappflow.png', // We need to add an icon later
11
+ group: ['transform'],
12
+ version: 1,
13
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
14
+ description: 'Interact with Wappflow API',
15
+ defaults: {
16
+ name: 'Wappflow API',
17
+ },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'wappflowApi',
23
+ required: true,
24
+ },
25
+ ],
26
+ properties: [
27
+ {
28
+ displayName: 'Resource',
29
+ name: 'resource',
30
+ type: 'options',
31
+ noDataExpression: true,
32
+ options: [
33
+ {
34
+ name: 'Instance',
35
+ value: 'instance',
36
+ },
37
+ {
38
+ name: 'Message',
39
+ value: 'message',
40
+ },
41
+ {
42
+ name: 'Group',
43
+ value: 'group',
44
+ },
45
+ {
46
+ name: 'Chat',
47
+ value: 'chat',
48
+ },
49
+ ],
50
+ default: 'message',
51
+ },
52
+ // --- Message Operations ---
53
+ {
54
+ displayName: 'Operation',
55
+ name: 'operation',
56
+ type: 'options',
57
+ noDataExpression: true,
58
+ displayOptions: {
59
+ show: {
60
+ resource: [
61
+ 'message',
62
+ ],
63
+ },
64
+ },
65
+ options: [
66
+ {
67
+ name: 'Send Text',
68
+ value: 'sendText',
69
+ action: 'Send a text message',
70
+ },
71
+ {
72
+ name: 'Send Media',
73
+ value: 'sendMedia',
74
+ action: 'Send a media message',
75
+ },
76
+ ],
77
+ default: 'sendText',
78
+ },
79
+ // --- Instance Operations ---
80
+ {
81
+ displayName: 'Operation',
82
+ name: 'operation',
83
+ type: 'options',
84
+ noDataExpression: true,
85
+ displayOptions: {
86
+ show: {
87
+ resource: [
88
+ 'instance',
89
+ ],
90
+ },
91
+ },
92
+ options: [
93
+ {
94
+ name: 'Create',
95
+ value: 'create',
96
+ action: 'Create a new instance',
97
+ },
98
+ {
99
+ name: 'Logout',
100
+ value: 'logout',
101
+ action: 'Logout an instance',
102
+ },
103
+ {
104
+ name: 'Delete',
105
+ value: 'delete',
106
+ action: 'Delete an instance',
107
+ },
108
+ {
109
+ name: 'List',
110
+ value: 'list',
111
+ action: 'List all instances',
112
+ },
113
+ ],
114
+ default: 'list',
115
+ },
116
+ // --- Group Operations ---
117
+ {
118
+ displayName: 'Operation',
119
+ name: 'operation',
120
+ type: 'options',
121
+ noDataExpression: true,
122
+ displayOptions: {
123
+ show: {
124
+ resource: [
125
+ 'group',
126
+ ],
127
+ },
128
+ },
129
+ options: [
130
+ {
131
+ name: 'Create',
132
+ value: 'create',
133
+ action: 'Create a new group',
134
+ },
135
+ {
136
+ name: 'List',
137
+ value: 'list',
138
+ action: 'List groups',
139
+ },
140
+ ],
141
+ default: 'list',
142
+ },
143
+ // --- Chat Operations ---
144
+ {
145
+ displayName: 'Operation',
146
+ name: 'operation',
147
+ type: 'options',
148
+ noDataExpression: true,
149
+ displayOptions: {
150
+ show: {
151
+ resource: [
152
+ 'chat',
153
+ ],
154
+ },
155
+ },
156
+ options: [
157
+ {
158
+ name: 'List',
159
+ value: 'list',
160
+ action: 'List chats',
161
+ },
162
+ ],
163
+ default: 'list',
164
+ },
165
+ // --- Parameters for Message: Send Text ---
166
+ {
167
+ displayName: 'Instance Name',
168
+ name: 'instanceName',
169
+ type: 'string',
170
+ default: '',
171
+ required: true,
172
+ displayOptions: {
173
+ show: {
174
+ resource: [
175
+ 'message',
176
+ ],
177
+ },
178
+ },
179
+ description: 'The name of the WhatsApp instance',
180
+ },
181
+ {
182
+ displayName: 'Phone Number',
183
+ name: 'number',
184
+ type: 'string',
185
+ default: '',
186
+ required: true,
187
+ displayOptions: {
188
+ show: {
189
+ resource: [
190
+ 'message',
191
+ ],
192
+ },
193
+ },
194
+ description: 'The phone number to send the message to (with country code)',
195
+ },
196
+ {
197
+ displayName: 'Message',
198
+ name: 'text',
199
+ type: 'string',
200
+ default: '',
201
+ required: true,
202
+ displayOptions: {
203
+ show: {
204
+ resource: [
205
+ 'message',
206
+ ],
207
+ operation: [
208
+ 'sendText'
209
+ ]
210
+ },
211
+ },
212
+ description: 'The text message to send',
213
+ },
214
+ // --- Parameters for Message: Send Media ---
215
+ {
216
+ displayName: 'Media Type',
217
+ name: 'mediaType',
218
+ type: 'options',
219
+ options: [
220
+ { name: 'Image', value: 'image' },
221
+ { name: 'Video', value: 'video' },
222
+ { name: 'Document', value: 'document' },
223
+ { name: 'Audio', value: 'audio' },
224
+ ],
225
+ default: 'image',
226
+ required: true,
227
+ displayOptions: {
228
+ show: {
229
+ resource: [
230
+ 'message',
231
+ ],
232
+ operation: [
233
+ 'sendMedia'
234
+ ]
235
+ },
236
+ },
237
+ },
238
+ {
239
+ displayName: 'Media URL',
240
+ name: 'mediaUrl',
241
+ type: 'string',
242
+ default: '',
243
+ required: true,
244
+ displayOptions: {
245
+ show: {
246
+ resource: [
247
+ 'message',
248
+ ],
249
+ operation: [
250
+ 'sendMedia'
251
+ ]
252
+ },
253
+ },
254
+ description: 'Direct URL to the media file',
255
+ },
256
+ {
257
+ displayName: 'Caption',
258
+ name: 'caption',
259
+ type: 'string',
260
+ default: '',
261
+ displayOptions: {
262
+ show: {
263
+ resource: [
264
+ 'message',
265
+ ],
266
+ operation: [
267
+ 'sendMedia'
268
+ ]
269
+ },
270
+ },
271
+ description: 'Caption for the media',
272
+ },
273
+ {
274
+ displayName: 'Filename',
275
+ name: 'fileName',
276
+ type: 'string',
277
+ default: '',
278
+ displayOptions: {
279
+ show: {
280
+ resource: [
281
+ 'message',
282
+ ],
283
+ operation: [
284
+ 'sendMedia'
285
+ ]
286
+ },
287
+ hide: {
288
+ mediaType: ['image', 'video', 'audio']
289
+ }
290
+ },
291
+ description: 'Filename for the document',
292
+ },
293
+ // --- Parameters for Group ---
294
+ {
295
+ displayName: 'Subject',
296
+ name: 'groupSubject',
297
+ type: 'string',
298
+ default: '',
299
+ required: true,
300
+ displayOptions: {
301
+ show: {
302
+ resource: [
303
+ 'group',
304
+ ],
305
+ operation: [
306
+ 'create'
307
+ ]
308
+ },
309
+ },
310
+ description: 'Name of the group',
311
+ },
312
+ {
313
+ displayName: 'Participants',
314
+ name: 'participants',
315
+ type: 'string',
316
+ default: '',
317
+ required: true,
318
+ displayOptions: {
319
+ show: {
320
+ resource: [
321
+ 'group',
322
+ ],
323
+ operation: [
324
+ 'create'
325
+ ]
326
+ },
327
+ },
328
+ description: 'Comma separated phone numbers of participants',
329
+ },
330
+ // --- Parameters for Chat ---
331
+ {
332
+ displayName: 'Phone Number',
333
+ name: 'number',
334
+ type: 'string',
335
+ default: '',
336
+ required: true,
337
+ displayOptions: {
338
+ show: {
339
+ resource: [
340
+ 'chat',
341
+ ],
342
+ operation: [
343
+ 'checkNumber'
344
+ ]
345
+ },
346
+ },
347
+ description: 'Phone number to check',
348
+ },
349
+ // --- Parameters for Instance ---
350
+ {
351
+ displayName: 'Instance Name',
352
+ name: 'instanceName',
353
+ type: 'string',
354
+ default: '',
355
+ required: true,
356
+ displayOptions: {
357
+ show: {
358
+ resource: [
359
+ 'instance',
360
+ 'group',
361
+ 'chat'
362
+ ],
363
+ operation: [
364
+ 'create',
365
+ 'logout',
366
+ 'delete',
367
+ 'list',
368
+ 'syncContacts',
369
+ 'checkNumber'
370
+ ]
371
+ },
372
+ hide: {
373
+ resource: ['instance'],
374
+ operation: ['list', 'create']
375
+ }
376
+ },
377
+ description: 'The name of the instance',
378
+ },
379
+ {
380
+ displayName: 'New Instance Name',
381
+ name: 'newInstanceName',
382
+ type: 'string',
383
+ default: '',
384
+ required: true,
385
+ displayOptions: {
386
+ show: {
387
+ resource: [
388
+ 'instance',
389
+ ],
390
+ operation: [
391
+ 'create'
392
+ ]
393
+ },
394
+ },
395
+ description: 'Name for the new instance',
396
+ },
397
+ ],
398
+ };
399
+ }
400
+ async execute() {
401
+ const items = this.getInputData();
402
+ const returnData = [];
403
+ // Get Credentials
404
+ const credentials = await this.getCredentials('wappflowApi');
405
+ const baseUrl = credentials.baseUrl.replace(/\/$/, '');
406
+ const apiKey = credentials.apiKey;
407
+ const resource = this.getNodeParameter('resource', 0);
408
+ const operation = this.getNodeParameter('operation', 0);
409
+ for (let i = 0; i < items.length; i++) {
410
+ let responseData;
411
+ const options = {
412
+ headers: {
413
+ 'Accept': 'application/json',
414
+ 'apikey': apiKey,
415
+ },
416
+ method: 'GET',
417
+ uri: '',
418
+ json: true,
419
+ };
420
+ try {
421
+ if (resource === 'message') {
422
+ const instanceName = this.getNodeParameter('instanceName', i);
423
+ const number = this.getNodeParameter('number', i);
424
+ if (operation === 'sendText') {
425
+ const text = this.getNodeParameter('text', i);
426
+ options.method = 'POST';
427
+ options.uri = `${baseUrl}/v1/message/text?key=${instanceName}`;
428
+ options.body = {
429
+ id: number,
430
+ message: text,
431
+ };
432
+ }
433
+ else if (operation === 'sendMedia') {
434
+ const mediaType = this.getNodeParameter('mediaType', i);
435
+ const mediaUrl = this.getNodeParameter('mediaUrl', i);
436
+ const caption = this.getNodeParameter('caption', i);
437
+ const fileName = this.getNodeParameter('fileName', i);
438
+ options.method = 'POST';
439
+ options.uri = `${baseUrl}/v1/message/${mediaType}?key=${instanceName}`;
440
+ const body = {
441
+ id: number,
442
+ url: mediaUrl,
443
+ };
444
+ if (caption)
445
+ body.caption = caption;
446
+ if (fileName && mediaType === 'document')
447
+ body.filename = fileName;
448
+ options.body = body;
449
+ }
450
+ }
451
+ else if (resource === 'instance') {
452
+ if (operation === 'create') {
453
+ const newInstanceName = this.getNodeParameter('newInstanceName', i);
454
+ options.method = 'POST';
455
+ options.uri = `${baseUrl}/v1/instance/create`;
456
+ options.body = {
457
+ instanceName: newInstanceName
458
+ };
459
+ }
460
+ else if (operation === 'list') {
461
+ options.method = 'GET';
462
+ options.uri = `${baseUrl}/v1/instance/fetchInstances`;
463
+ }
464
+ else if (operation === 'logout') {
465
+ const instanceName = this.getNodeParameter('instanceName', i);
466
+ options.method = 'DELETE';
467
+ options.uri = `${baseUrl}/v1/instance/logout/${instanceName}`;
468
+ }
469
+ else if (operation === 'delete') {
470
+ const instanceName = this.getNodeParameter('instanceName', i);
471
+ options.method = 'DELETE';
472
+ options.uri = `${baseUrl}/v1/instance/delete/${instanceName}`;
473
+ }
474
+ }
475
+ else if (resource === 'group') {
476
+ const instanceName = this.getNodeParameter('instanceName', i);
477
+ if (operation === 'create') {
478
+ const subject = this.getNodeParameter('groupSubject', i);
479
+ const participants = this.getNodeParameter('participants', i);
480
+ const participantsList = participants.split(',').map(p => p.trim());
481
+ options.method = 'POST';
482
+ options.uri = `${baseUrl}/v1/group/${instanceName}/create`;
483
+ options.body = {
484
+ subject: subject,
485
+ participants: participantsList
486
+ };
487
+ }
488
+ else if (operation === 'list') {
489
+ options.method = 'GET';
490
+ options.uri = `${baseUrl}/v1/group/${instanceName}/all`;
491
+ }
492
+ }
493
+ else if (resource === 'chat') {
494
+ const instanceName = this.getNodeParameter('instanceName', i);
495
+ if (operation === 'syncContacts') {
496
+ options.method = 'GET';
497
+ options.uri = `${baseUrl}/v1/chat/${instanceName}/sync-contacts`;
498
+ }
499
+ else if (operation === 'checkNumber') {
500
+ const number = this.getNodeParameter('number', i);
501
+ options.method = 'POST';
502
+ options.uri = `${baseUrl}/v1/chat/${instanceName}/check-number`;
503
+ options.body = {
504
+ id: number
505
+ };
506
+ }
507
+ }
508
+ // Execute Request
509
+ if (options.uri) {
510
+ responseData = await this.helpers.request(options);
511
+ }
512
+ else {
513
+ responseData = { message: 'Operation not implemented yet' };
514
+ }
515
+ }
516
+ catch (error) {
517
+ // Return actual error
518
+ if (this.continueOnFail()) {
519
+ returnData.push({ error: error.message });
520
+ continue;
521
+ }
522
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
523
+ }
524
+ if (Array.isArray(responseData)) {
525
+ returnData.push.apply(returnData, responseData);
526
+ }
527
+ else if (responseData !== undefined) {
528
+ returnData.push(responseData);
529
+ }
530
+ }
531
+ return [this.helpers.returnJsonArray(returnData)];
532
+ }
533
+ }
534
+ exports.WappflowApi = WappflowApi;
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "n8n-nodes-wappflow",
3
+ "version": "0.1.0",
4
+ "description": "n8n node for Wappflow API",
5
+ "keywords": [
6
+ "n8n-community-node-package"
7
+ ],
8
+ "license": "MIT",
9
+ "author": {
10
+ "name": "Wappflow",
11
+ "email": "support@wappflow.com"
12
+ },
13
+ "main": "index.js",
14
+ "scripts": {
15
+ "build": "tsc && gulp build:icons",
16
+ "dev": "tsc --watch",
17
+ "format": "prettier --write '**/*.{ts,js,json}'",
18
+ "lint": "eslint \"nodes/**/*.{ts,js}\" \"credentials/**/*.{ts,js}\"",
19
+ "lintfix": "eslint \"nodes/**/*.{ts,js}\" \"credentials/**/*.{ts,js}\" --fix",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "n8n": {
26
+ "nodes": [
27
+ "dist/nodes/WappflowApi/WappflowApi.node.js"
28
+ ],
29
+ "credentials": [
30
+ "dist/nodes/WappflowApi/WappflowApi.credentials.js"
31
+ ]
32
+ },
33
+ "devDependencies": {
34
+ "@types/express": "^4.17.6",
35
+ "@types/request-promise-native": "~1.0.15",
36
+ "@typescript-eslint/parser": "~5.45",
37
+ "eslint-plugin-n8n-nodes-base": "^1.11.0",
38
+ "gulp": "^4.0.2",
39
+ "n8n-core": "*",
40
+ "n8n-workflow": "*",
41
+ "prettier": "^2.7.1",
42
+ "typescript": "^5.0.0"
43
+ }
44
+ }