n8n-nodes-whaapy 0.2.3 → 0.3.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.
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
2
|
export declare class Whaapy implements INodeType {
|
|
3
3
|
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
4
5
|
}
|
|
@@ -1,6 +1,82 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Whaapy = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
// Helper function to convert string to slug (for auto-generating IDs)
|
|
6
|
+
function slugify(text) {
|
|
7
|
+
return text
|
|
8
|
+
.toString()
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.trim()
|
|
11
|
+
.replace(/\s+/g, '_')
|
|
12
|
+
.replace(/[^\w\-]+/g, '')
|
|
13
|
+
.replace(/\-\-+/g, '_')
|
|
14
|
+
.replace(/^-+/, '')
|
|
15
|
+
.replace(/-+$/, '')
|
|
16
|
+
.substring(0, 256);
|
|
17
|
+
}
|
|
18
|
+
// Build interactive message payload from structured fields
|
|
19
|
+
function buildInteractivePayload(params) {
|
|
20
|
+
// If using raw JSON, return it directly
|
|
21
|
+
if (params.useRawJson && params.rawJson) {
|
|
22
|
+
return params.rawJson;
|
|
23
|
+
}
|
|
24
|
+
const interactive = {
|
|
25
|
+
type: params.interactiveType,
|
|
26
|
+
body: {
|
|
27
|
+
text: params.bodyText,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
// Add header if specified
|
|
31
|
+
if (params.headerType && params.headerType !== 'none') {
|
|
32
|
+
if (params.headerType === 'text' && params.headerText) {
|
|
33
|
+
interactive.header = {
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: params.headerText,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
else if (['image', 'video', 'document'].includes(params.headerType) && params.headerMediaUrl) {
|
|
39
|
+
interactive.header = {
|
|
40
|
+
type: params.headerType,
|
|
41
|
+
[params.headerType]: {
|
|
42
|
+
link: params.headerMediaUrl,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Add footer if specified
|
|
48
|
+
if (params.footerText) {
|
|
49
|
+
interactive.footer = {
|
|
50
|
+
text: params.footerText,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Build action based on type
|
|
54
|
+
if (params.interactiveType === 'button' && params.buttons && params.buttons.length > 0) {
|
|
55
|
+
interactive.action = {
|
|
56
|
+
buttons: params.buttons.map((btn) => ({
|
|
57
|
+
type: 'reply',
|
|
58
|
+
reply: {
|
|
59
|
+
id: btn.id || slugify(btn.title),
|
|
60
|
+
title: btn.title.substring(0, 20),
|
|
61
|
+
},
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
else if (params.interactiveType === 'list' && params.sections && params.sections.length > 0) {
|
|
66
|
+
interactive.action = {
|
|
67
|
+
button: params.listButtonText || 'Ver Opciones',
|
|
68
|
+
sections: params.sections.map((section) => ({
|
|
69
|
+
title: section.title || undefined,
|
|
70
|
+
rows: section.rows.map((row) => ({
|
|
71
|
+
id: row.id || slugify(row.title),
|
|
72
|
+
title: row.title.substring(0, 24),
|
|
73
|
+
description: row.description ? row.description.substring(0, 72) : undefined,
|
|
74
|
+
})),
|
|
75
|
+
})),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return interactive;
|
|
79
|
+
}
|
|
4
80
|
class Whaapy {
|
|
5
81
|
constructor() {
|
|
6
82
|
this.description = {
|
|
@@ -279,19 +355,270 @@ class Whaapy {
|
|
|
279
355
|
},
|
|
280
356
|
],
|
|
281
357
|
},
|
|
282
|
-
//
|
|
358
|
+
// ===========================================
|
|
359
|
+
// INTERACTIVE MESSAGE FIELDS (Structured)
|
|
360
|
+
// ===========================================
|
|
361
|
+
// Interactive: Type selector (button or list)
|
|
283
362
|
{
|
|
284
|
-
displayName: 'Interactive
|
|
285
|
-
name: '
|
|
286
|
-
type: '
|
|
363
|
+
displayName: 'Interactive Type',
|
|
364
|
+
name: 'interactiveType',
|
|
365
|
+
type: 'options',
|
|
287
366
|
required: true,
|
|
288
|
-
|
|
289
|
-
|
|
367
|
+
options: [
|
|
368
|
+
{ name: 'Buttons (Reply Buttons)', value: 'button' },
|
|
369
|
+
{ name: 'List (Menu)', value: 'list' },
|
|
370
|
+
],
|
|
371
|
+
default: 'button',
|
|
372
|
+
description: 'Type of interactive message. Buttons show up to 3 options, Lists show a menu with sections.',
|
|
290
373
|
displayOptions: {
|
|
291
374
|
show: { resource: ['message'], operation: ['send'], messageType: ['interactive'] },
|
|
292
375
|
},
|
|
293
|
-
|
|
294
|
-
|
|
376
|
+
},
|
|
377
|
+
// Interactive: Body text (required)
|
|
378
|
+
{
|
|
379
|
+
displayName: 'Body Text',
|
|
380
|
+
name: 'interactiveBodyText',
|
|
381
|
+
type: 'string',
|
|
382
|
+
typeOptions: { rows: 3 },
|
|
383
|
+
required: true,
|
|
384
|
+
default: '',
|
|
385
|
+
placeholder: '¿Cómo podemos ayudarte hoy?',
|
|
386
|
+
description: 'Main text of the message. Max 1024 characters.',
|
|
387
|
+
displayOptions: {
|
|
388
|
+
show: { resource: ['message'], operation: ['send'], messageType: ['interactive'] },
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
// Interactive: Header type (optional)
|
|
392
|
+
{
|
|
393
|
+
displayName: 'Header Type',
|
|
394
|
+
name: 'interactiveHeaderType',
|
|
395
|
+
type: 'options',
|
|
396
|
+
default: 'none',
|
|
397
|
+
options: [
|
|
398
|
+
{ name: 'None', value: 'none' },
|
|
399
|
+
{ name: 'Text', value: 'text' },
|
|
400
|
+
{ name: 'Image', value: 'image' },
|
|
401
|
+
{ name: 'Video', value: 'video' },
|
|
402
|
+
{ name: 'Document', value: 'document' },
|
|
403
|
+
],
|
|
404
|
+
description: 'Optional header for the message',
|
|
405
|
+
displayOptions: {
|
|
406
|
+
show: { resource: ['message'], operation: ['send'], messageType: ['interactive'] },
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
// Interactive: Header text (if type=text)
|
|
410
|
+
{
|
|
411
|
+
displayName: 'Header Text',
|
|
412
|
+
name: 'interactiveHeaderText',
|
|
413
|
+
type: 'string',
|
|
414
|
+
default: '',
|
|
415
|
+
placeholder: '🍕 Pizzería Whaapy',
|
|
416
|
+
description: 'Header text. Max 60 characters.',
|
|
417
|
+
displayOptions: {
|
|
418
|
+
show: {
|
|
419
|
+
resource: ['message'],
|
|
420
|
+
operation: ['send'],
|
|
421
|
+
messageType: ['interactive'],
|
|
422
|
+
interactiveHeaderType: ['text'],
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
// Interactive: Header media URL (if type=image|video|document)
|
|
427
|
+
{
|
|
428
|
+
displayName: 'Header Media URL',
|
|
429
|
+
name: 'interactiveHeaderMediaUrl',
|
|
430
|
+
type: 'string',
|
|
431
|
+
default: '',
|
|
432
|
+
placeholder: 'https://example.com/image.jpg',
|
|
433
|
+
description: 'Public URL of the media file for header',
|
|
434
|
+
displayOptions: {
|
|
435
|
+
show: {
|
|
436
|
+
resource: ['message'],
|
|
437
|
+
operation: ['send'],
|
|
438
|
+
messageType: ['interactive'],
|
|
439
|
+
interactiveHeaderType: ['image', 'video', 'document'],
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
// Interactive: Footer text (optional)
|
|
444
|
+
{
|
|
445
|
+
displayName: 'Footer Text',
|
|
446
|
+
name: 'interactiveFooterText',
|
|
447
|
+
type: 'string',
|
|
448
|
+
default: '',
|
|
449
|
+
placeholder: 'Responde con una opción',
|
|
450
|
+
description: 'Optional footer text in gray. Max 60 characters.',
|
|
451
|
+
displayOptions: {
|
|
452
|
+
show: { resource: ['message'], operation: ['send'], messageType: ['interactive'] },
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
// Interactive: Buttons (if type=button)
|
|
456
|
+
{
|
|
457
|
+
displayName: 'Buttons',
|
|
458
|
+
name: 'interactiveButtons',
|
|
459
|
+
type: 'fixedCollection',
|
|
460
|
+
typeOptions: {
|
|
461
|
+
multipleValues: true,
|
|
462
|
+
maxValue: 3,
|
|
463
|
+
},
|
|
464
|
+
default: { buttonValues: [] },
|
|
465
|
+
description: 'Reply buttons (1-3). Users tap to respond.',
|
|
466
|
+
displayOptions: {
|
|
467
|
+
show: {
|
|
468
|
+
resource: ['message'],
|
|
469
|
+
operation: ['send'],
|
|
470
|
+
messageType: ['interactive'],
|
|
471
|
+
interactiveType: ['button'],
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
options: [
|
|
475
|
+
{
|
|
476
|
+
displayName: 'Button',
|
|
477
|
+
name: 'buttonValues',
|
|
478
|
+
values: [
|
|
479
|
+
{
|
|
480
|
+
displayName: 'Title',
|
|
481
|
+
name: 'title',
|
|
482
|
+
type: 'string',
|
|
483
|
+
required: true,
|
|
484
|
+
default: '',
|
|
485
|
+
placeholder: 'Ver Menú',
|
|
486
|
+
description: 'Button text visible to user. Max 20 characters.',
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
displayName: 'ID',
|
|
490
|
+
name: 'id',
|
|
491
|
+
type: 'string',
|
|
492
|
+
default: '',
|
|
493
|
+
placeholder: 'ver_menu (optional, auto-generated if empty)',
|
|
494
|
+
description: 'Unique ID returned in webhook when user clicks. If empty, generated from title.',
|
|
495
|
+
},
|
|
496
|
+
],
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
},
|
|
500
|
+
// Interactive: List button text (if type=list)
|
|
501
|
+
{
|
|
502
|
+
displayName: 'List Button Text',
|
|
503
|
+
name: 'interactiveListButtonText',
|
|
504
|
+
type: 'string',
|
|
505
|
+
required: true,
|
|
506
|
+
default: 'Ver Opciones',
|
|
507
|
+
placeholder: 'Ver Menú',
|
|
508
|
+
description: 'Text for the button that opens the list menu. Max 20 characters.',
|
|
509
|
+
displayOptions: {
|
|
510
|
+
show: {
|
|
511
|
+
resource: ['message'],
|
|
512
|
+
operation: ['send'],
|
|
513
|
+
messageType: ['interactive'],
|
|
514
|
+
interactiveType: ['list'],
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
// Interactive: Sections (if type=list)
|
|
519
|
+
{
|
|
520
|
+
displayName: 'Sections',
|
|
521
|
+
name: 'interactiveSections',
|
|
522
|
+
type: 'fixedCollection',
|
|
523
|
+
typeOptions: {
|
|
524
|
+
multipleValues: true,
|
|
525
|
+
maxValue: 10,
|
|
526
|
+
},
|
|
527
|
+
default: { sectionValues: [] },
|
|
528
|
+
description: 'Menu sections. Each section has a title and rows (options).',
|
|
529
|
+
displayOptions: {
|
|
530
|
+
show: {
|
|
531
|
+
resource: ['message'],
|
|
532
|
+
operation: ['send'],
|
|
533
|
+
messageType: ['interactive'],
|
|
534
|
+
interactiveType: ['list'],
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
options: [
|
|
538
|
+
{
|
|
539
|
+
displayName: 'Section',
|
|
540
|
+
name: 'sectionValues',
|
|
541
|
+
values: [
|
|
542
|
+
{
|
|
543
|
+
displayName: 'Section Title',
|
|
544
|
+
name: 'title',
|
|
545
|
+
type: 'string',
|
|
546
|
+
default: '',
|
|
547
|
+
placeholder: 'Pizzas',
|
|
548
|
+
description: 'Section title. Required if more than 1 section. Max 24 characters.',
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
displayName: 'Rows',
|
|
552
|
+
name: 'rows',
|
|
553
|
+
type: 'fixedCollection',
|
|
554
|
+
typeOptions: {
|
|
555
|
+
multipleValues: true,
|
|
556
|
+
maxValue: 10,
|
|
557
|
+
},
|
|
558
|
+
default: { rowValues: [] },
|
|
559
|
+
description: 'Options in this section',
|
|
560
|
+
options: [
|
|
561
|
+
{
|
|
562
|
+
displayName: 'Row',
|
|
563
|
+
name: 'rowValues',
|
|
564
|
+
values: [
|
|
565
|
+
{
|
|
566
|
+
displayName: 'Title',
|
|
567
|
+
name: 'title',
|
|
568
|
+
type: 'string',
|
|
569
|
+
required: true,
|
|
570
|
+
default: '',
|
|
571
|
+
placeholder: 'Margarita',
|
|
572
|
+
description: 'Row title. Max 24 characters.',
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
displayName: 'Description',
|
|
576
|
+
name: 'description',
|
|
577
|
+
type: 'string',
|
|
578
|
+
default: '',
|
|
579
|
+
placeholder: 'Tomate, mozzarella - $150',
|
|
580
|
+
description: 'Row description. Optional. Max 72 characters.',
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
displayName: 'ID',
|
|
584
|
+
name: 'id',
|
|
585
|
+
type: 'string',
|
|
586
|
+
default: '',
|
|
587
|
+
placeholder: 'pizza_margarita (optional)',
|
|
588
|
+
description: 'Unique ID returned in webhook. If empty, generated from title.',
|
|
589
|
+
},
|
|
590
|
+
],
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
},
|
|
596
|
+
],
|
|
597
|
+
},
|
|
598
|
+
// Interactive: Advanced JSON (fallback for complex cases)
|
|
599
|
+
{
|
|
600
|
+
displayName: 'Use Raw JSON',
|
|
601
|
+
name: 'interactiveUseRawJson',
|
|
602
|
+
type: 'boolean',
|
|
603
|
+
default: false,
|
|
604
|
+
description: 'Use raw JSON instead of structured fields (for advanced use cases)',
|
|
605
|
+
displayOptions: {
|
|
606
|
+
show: { resource: ['message'], operation: ['send'], messageType: ['interactive'] },
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
displayName: 'Interactive JSON',
|
|
611
|
+
name: 'interactiveRawJson',
|
|
612
|
+
type: 'json',
|
|
613
|
+
default: '{}',
|
|
614
|
+
description: 'Raw interactive content JSON. See docs.whaapy.com for structure.',
|
|
615
|
+
displayOptions: {
|
|
616
|
+
show: {
|
|
617
|
+
resource: ['message'],
|
|
618
|
+
operation: ['send'],
|
|
619
|
+
messageType: ['interactive'],
|
|
620
|
+
interactiveUseRawJson: [true],
|
|
621
|
+
},
|
|
295
622
|
},
|
|
296
623
|
},
|
|
297
624
|
// Message: Send - Location
|
|
@@ -1785,5 +2112,624 @@ class Whaapy {
|
|
|
1785
2112
|
],
|
|
1786
2113
|
};
|
|
1787
2114
|
}
|
|
2115
|
+
async execute() {
|
|
2116
|
+
const items = this.getInputData();
|
|
2117
|
+
const returnData = [];
|
|
2118
|
+
for (let i = 0; i < items.length; i++) {
|
|
2119
|
+
try {
|
|
2120
|
+
const resource = this.getNodeParameter('resource', i);
|
|
2121
|
+
const operation = this.getNodeParameter('operation', i);
|
|
2122
|
+
const credentials = await this.getCredentials('whaapyApi');
|
|
2123
|
+
const baseUrl = credentials.baseUrl;
|
|
2124
|
+
const apiKey = credentials.apiKey;
|
|
2125
|
+
let response;
|
|
2126
|
+
// ===========================================
|
|
2127
|
+
// MESSAGE RESOURCE
|
|
2128
|
+
// ===========================================
|
|
2129
|
+
if (resource === 'message') {
|
|
2130
|
+
if (operation === 'send') {
|
|
2131
|
+
const to = this.getNodeParameter('to', i);
|
|
2132
|
+
const messageType = this.getNodeParameter('messageType', i);
|
|
2133
|
+
const body = { to, type: messageType };
|
|
2134
|
+
// Handle different message types
|
|
2135
|
+
if (messageType === 'text') {
|
|
2136
|
+
body.content = this.getNodeParameter('textContent', i);
|
|
2137
|
+
}
|
|
2138
|
+
else if (['image', 'video', 'audio', 'document', 'sticker'].includes(messageType)) {
|
|
2139
|
+
const mediaUrl = this.getNodeParameter('mediaUrl', i);
|
|
2140
|
+
body[messageType] = { link: mediaUrl };
|
|
2141
|
+
if (['image', 'video', 'document'].includes(messageType)) {
|
|
2142
|
+
const caption = this.getNodeParameter('caption', i, '');
|
|
2143
|
+
if (caption)
|
|
2144
|
+
body[messageType].caption = caption;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
else if (messageType === 'template') {
|
|
2148
|
+
body.templateName = this.getNodeParameter('templateName', i);
|
|
2149
|
+
body.language = this.getNodeParameter('templateLanguage', i);
|
|
2150
|
+
const templateOptions = this.getNodeParameter('templateOptions', i, {});
|
|
2151
|
+
if (templateOptions.parameters) {
|
|
2152
|
+
body.template_parameters = templateOptions.parameters.split(',').map((v) => v.trim());
|
|
2153
|
+
}
|
|
2154
|
+
if (templateOptions.headerMediaType && templateOptions.headerMediaUrl) {
|
|
2155
|
+
body.header_media = {
|
|
2156
|
+
type: templateOptions.headerMediaType,
|
|
2157
|
+
url: templateOptions.headerMediaUrl,
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
else if (messageType === 'interactive') {
|
|
2162
|
+
// Build interactive message from structured fields
|
|
2163
|
+
const useRawJson = this.getNodeParameter('interactiveUseRawJson', i, false);
|
|
2164
|
+
if (useRawJson) {
|
|
2165
|
+
const rawJson = this.getNodeParameter('interactiveRawJson', i, '{}');
|
|
2166
|
+
body.interactive = typeof rawJson === 'string' ? JSON.parse(rawJson) : rawJson;
|
|
2167
|
+
}
|
|
2168
|
+
else {
|
|
2169
|
+
const interactiveType = this.getNodeParameter('interactiveType', i);
|
|
2170
|
+
const bodyText = this.getNodeParameter('interactiveBodyText', i);
|
|
2171
|
+
const headerType = this.getNodeParameter('interactiveHeaderType', i, 'none');
|
|
2172
|
+
const headerText = this.getNodeParameter('interactiveHeaderText', i, '');
|
|
2173
|
+
const headerMediaUrl = this.getNodeParameter('interactiveHeaderMediaUrl', i, '');
|
|
2174
|
+
const footerText = this.getNodeParameter('interactiveFooterText', i, '');
|
|
2175
|
+
// Build buttons array
|
|
2176
|
+
let buttons = [];
|
|
2177
|
+
if (interactiveType === 'button') {
|
|
2178
|
+
const buttonsData = this.getNodeParameter('interactiveButtons', i, { buttonValues: [] });
|
|
2179
|
+
buttons = buttonsData.buttonValues || [];
|
|
2180
|
+
}
|
|
2181
|
+
// Build sections array
|
|
2182
|
+
let sections = [];
|
|
2183
|
+
let listButtonText = '';
|
|
2184
|
+
if (interactiveType === 'list') {
|
|
2185
|
+
listButtonText = this.getNodeParameter('interactiveListButtonText', i, 'Ver Opciones');
|
|
2186
|
+
const sectionsData = this.getNodeParameter('interactiveSections', i, { sectionValues: [] });
|
|
2187
|
+
if (sectionsData.sectionValues) {
|
|
2188
|
+
sections = sectionsData.sectionValues.map((section) => {
|
|
2189
|
+
var _a;
|
|
2190
|
+
return ({
|
|
2191
|
+
title: section.title,
|
|
2192
|
+
rows: ((_a = section.rows) === null || _a === void 0 ? void 0 : _a.rowValues) || [],
|
|
2193
|
+
});
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
body.interactive = buildInteractivePayload({
|
|
2198
|
+
interactiveType,
|
|
2199
|
+
bodyText,
|
|
2200
|
+
headerType,
|
|
2201
|
+
headerText,
|
|
2202
|
+
headerMediaUrl,
|
|
2203
|
+
footerText,
|
|
2204
|
+
buttons,
|
|
2205
|
+
listButtonText,
|
|
2206
|
+
sections,
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
else if (messageType === 'location') {
|
|
2211
|
+
body.location = {
|
|
2212
|
+
latitude: this.getNodeParameter('latitude', i),
|
|
2213
|
+
longitude: this.getNodeParameter('longitude', i),
|
|
2214
|
+
name: this.getNodeParameter('locationName', i, '') || undefined,
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
else if (messageType === 'contacts') {
|
|
2218
|
+
const contactsData = this.getNodeParameter('contactsData', i);
|
|
2219
|
+
body.contacts = typeof contactsData === 'string' ? JSON.parse(contactsData) : contactsData;
|
|
2220
|
+
}
|
|
2221
|
+
else if (messageType === 'reaction') {
|
|
2222
|
+
body.reaction = {
|
|
2223
|
+
message_id: this.getNodeParameter('reactionMessageId', i),
|
|
2224
|
+
emoji: this.getNodeParameter('reactionEmoji', i),
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
// Add additional fields
|
|
2228
|
+
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
2229
|
+
if (additionalFields.pauseAi) {
|
|
2230
|
+
body.ai = body.ai || {};
|
|
2231
|
+
body.ai.pause = additionalFields.pauseAi;
|
|
2232
|
+
}
|
|
2233
|
+
if (additionalFields.pauseDuration) {
|
|
2234
|
+
body.ai = body.ai || {};
|
|
2235
|
+
body.ai.pauseDuration = additionalFields.pauseDuration;
|
|
2236
|
+
}
|
|
2237
|
+
if (additionalFields.disableAi) {
|
|
2238
|
+
body.ai = body.ai || {};
|
|
2239
|
+
body.ai.disable = additionalFields.disableAi;
|
|
2240
|
+
}
|
|
2241
|
+
if (additionalFields.replyTo) {
|
|
2242
|
+
body.context = { message_id: additionalFields.replyTo };
|
|
2243
|
+
}
|
|
2244
|
+
if (additionalFields.createConversation !== undefined) {
|
|
2245
|
+
body.createConversation = additionalFields.createConversation;
|
|
2246
|
+
}
|
|
2247
|
+
if (additionalFields.metadata) {
|
|
2248
|
+
body.metadata = typeof additionalFields.metadata === 'string'
|
|
2249
|
+
? JSON.parse(additionalFields.metadata)
|
|
2250
|
+
: additionalFields.metadata;
|
|
2251
|
+
}
|
|
2252
|
+
response = await this.helpers.request({
|
|
2253
|
+
method: 'POST',
|
|
2254
|
+
url: `${baseUrl}/messages/v1`,
|
|
2255
|
+
headers: {
|
|
2256
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
2257
|
+
'Content-Type': 'application/json',
|
|
2258
|
+
},
|
|
2259
|
+
body,
|
|
2260
|
+
json: true,
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
else if (operation === 'retry') {
|
|
2264
|
+
const messageId = this.getNodeParameter('messageId', i);
|
|
2265
|
+
response = await this.helpers.request({
|
|
2266
|
+
method: 'POST',
|
|
2267
|
+
url: `${baseUrl}/messages/v1/${messageId}/retry`,
|
|
2268
|
+
headers: {
|
|
2269
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
2270
|
+
'Content-Type': 'application/json',
|
|
2271
|
+
},
|
|
2272
|
+
json: true,
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
// ===========================================
|
|
2277
|
+
// CONVERSATION RESOURCE
|
|
2278
|
+
// ===========================================
|
|
2279
|
+
else if (resource === 'conversation') {
|
|
2280
|
+
if (operation === 'list') {
|
|
2281
|
+
const filters = this.getNodeParameter('conversationFilters', i, {});
|
|
2282
|
+
const qs = {};
|
|
2283
|
+
if (filters.search)
|
|
2284
|
+
qs.search = filters.search;
|
|
2285
|
+
if (filters.status && filters.status !== 'all')
|
|
2286
|
+
qs.status = filters.status;
|
|
2287
|
+
if (filters.limit)
|
|
2288
|
+
qs.limit = filters.limit;
|
|
2289
|
+
if (filters.offset)
|
|
2290
|
+
qs.offset = filters.offset;
|
|
2291
|
+
response = await this.helpers.request({
|
|
2292
|
+
method: 'GET',
|
|
2293
|
+
url: `${baseUrl}/conversations/v1`,
|
|
2294
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2295
|
+
qs,
|
|
2296
|
+
json: true,
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
else if (operation === 'get') {
|
|
2300
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2301
|
+
response = await this.helpers.request({
|
|
2302
|
+
method: 'GET',
|
|
2303
|
+
url: `${baseUrl}/conversations/v1/${conversationId}`,
|
|
2304
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2305
|
+
json: true,
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
else if (operation === 'getByPhone') {
|
|
2309
|
+
const phoneNumber = this.getNodeParameter('phoneNumber', i);
|
|
2310
|
+
response = await this.helpers.request({
|
|
2311
|
+
method: 'GET',
|
|
2312
|
+
url: `${baseUrl}/conversations/v1/by-phone/${encodeURIComponent(phoneNumber)}`,
|
|
2313
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2314
|
+
json: true,
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
else if (operation === 'getMessages') {
|
|
2318
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2319
|
+
const options = this.getNodeParameter('messagesOptions', i, {});
|
|
2320
|
+
const qs = {};
|
|
2321
|
+
if (options.limit)
|
|
2322
|
+
qs.limit = options.limit;
|
|
2323
|
+
if (options.cursor)
|
|
2324
|
+
qs.cursor = options.cursor;
|
|
2325
|
+
response = await this.helpers.request({
|
|
2326
|
+
method: 'GET',
|
|
2327
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/messages`,
|
|
2328
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2329
|
+
qs,
|
|
2330
|
+
json: true,
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
else if (operation === 'close') {
|
|
2334
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2335
|
+
response = await this.helpers.request({
|
|
2336
|
+
method: 'POST',
|
|
2337
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/close`,
|
|
2338
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2339
|
+
json: true,
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
else if (operation === 'archive') {
|
|
2343
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2344
|
+
response = await this.helpers.request({
|
|
2345
|
+
method: 'POST',
|
|
2346
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/archive`,
|
|
2347
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2348
|
+
json: true,
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
else if (operation === 'markRead') {
|
|
2352
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2353
|
+
response = await this.helpers.request({
|
|
2354
|
+
method: 'PATCH',
|
|
2355
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/mark-read`,
|
|
2356
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2357
|
+
json: true,
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
else if (operation === 'setAi') {
|
|
2361
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2362
|
+
const aiEnabled = this.getNodeParameter('aiEnabled', i);
|
|
2363
|
+
response = await this.helpers.request({
|
|
2364
|
+
method: 'PATCH',
|
|
2365
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/ai`,
|
|
2366
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2367
|
+
body: { aiEnabled },
|
|
2368
|
+
json: true,
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
else if (operation === 'pauseAi') {
|
|
2372
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2373
|
+
const duration = this.getNodeParameter('pauseDurationConv', i);
|
|
2374
|
+
response = await this.helpers.request({
|
|
2375
|
+
method: 'POST',
|
|
2376
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/ai/pause`,
|
|
2377
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2378
|
+
body: { duration },
|
|
2379
|
+
json: true,
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
else if (operation === 'aiSuggest') {
|
|
2383
|
+
const conversationId = this.getNodeParameter('conversationId', i);
|
|
2384
|
+
response = await this.helpers.request({
|
|
2385
|
+
method: 'POST',
|
|
2386
|
+
url: `${baseUrl}/conversations/v1/${conversationId}/ai-suggest`,
|
|
2387
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2388
|
+
json: true,
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
// ===========================================
|
|
2393
|
+
// AGENT RESOURCE
|
|
2394
|
+
// ===========================================
|
|
2395
|
+
else if (resource === 'agent') {
|
|
2396
|
+
if (operation === 'toggle') {
|
|
2397
|
+
const enabled = this.getNodeParameter('agentEnabled', i);
|
|
2398
|
+
response = await this.helpers.request({
|
|
2399
|
+
method: 'POST',
|
|
2400
|
+
url: `${baseUrl}/agent/v1/toggle`,
|
|
2401
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2402
|
+
body: { enabled },
|
|
2403
|
+
json: true,
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
else if (operation === 'pause') {
|
|
2407
|
+
const duration = this.getNodeParameter('agentPauseDuration', i);
|
|
2408
|
+
response = await this.helpers.request({
|
|
2409
|
+
method: 'POST',
|
|
2410
|
+
url: `${baseUrl}/agent/v1/pause`,
|
|
2411
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2412
|
+
body: { duration },
|
|
2413
|
+
json: true,
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
// ===========================================
|
|
2418
|
+
// TEMPLATE RESOURCE
|
|
2419
|
+
// ===========================================
|
|
2420
|
+
else if (resource === 'template') {
|
|
2421
|
+
if (operation === 'list') {
|
|
2422
|
+
const filters = this.getNodeParameter('templateFilters', i, {});
|
|
2423
|
+
const qs = {};
|
|
2424
|
+
if (filters.status)
|
|
2425
|
+
qs.status = filters.status;
|
|
2426
|
+
if (filters.limit)
|
|
2427
|
+
qs.limit = filters.limit;
|
|
2428
|
+
if (filters.offset)
|
|
2429
|
+
qs.offset = filters.offset;
|
|
2430
|
+
response = await this.helpers.request({
|
|
2431
|
+
method: 'GET',
|
|
2432
|
+
url: `${baseUrl}/templates/v1`,
|
|
2433
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2434
|
+
qs,
|
|
2435
|
+
json: true,
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
else if (operation === 'get') {
|
|
2439
|
+
const templateId = this.getNodeParameter('templateId', i);
|
|
2440
|
+
response = await this.helpers.request({
|
|
2441
|
+
method: 'GET',
|
|
2442
|
+
url: `${baseUrl}/templates/v1/${templateId}`,
|
|
2443
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2444
|
+
json: true,
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
else if (operation === 'getVariables') {
|
|
2448
|
+
response = await this.helpers.request({
|
|
2449
|
+
method: 'GET',
|
|
2450
|
+
url: `${baseUrl}/templates/v1/variables`,
|
|
2451
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2452
|
+
json: true,
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
else if (operation === 'sync') {
|
|
2456
|
+
response = await this.helpers.request({
|
|
2457
|
+
method: 'POST',
|
|
2458
|
+
url: `${baseUrl}/templates/v1/sync`,
|
|
2459
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2460
|
+
json: true,
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
// ===========================================
|
|
2465
|
+
// MEDIA RESOURCE
|
|
2466
|
+
// ===========================================
|
|
2467
|
+
else if (resource === 'media') {
|
|
2468
|
+
if (operation === 'upload') {
|
|
2469
|
+
// Media upload requires special handling with binary data
|
|
2470
|
+
const mediaType = this.getNodeParameter('mediaType', i);
|
|
2471
|
+
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
|
|
2472
|
+
// For now, just return an error - binary upload needs special handling
|
|
2473
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Media upload from n8n requires binary data handling. Use the HTTP Request node with binary data or upload via URL.', { itemIndex: i });
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
// ===========================================
|
|
2477
|
+
// CONTACT RESOURCE
|
|
2478
|
+
// ===========================================
|
|
2479
|
+
else if (resource === 'contact') {
|
|
2480
|
+
if (operation === 'list') {
|
|
2481
|
+
const filters = this.getNodeParameter('contactFilters', i, {});
|
|
2482
|
+
const qs = {};
|
|
2483
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
2484
|
+
if (value !== undefined && value !== '') {
|
|
2485
|
+
qs[key === 'sortBy' ? 'sort_by' : key === 'sortOrder' ? 'sort_order' : key === 'funnelStageId' ? 'funnel_stage_id' : key] = value;
|
|
2486
|
+
}
|
|
2487
|
+
});
|
|
2488
|
+
response = await this.helpers.request({
|
|
2489
|
+
method: 'GET',
|
|
2490
|
+
url: `${baseUrl}/contacts/v1`,
|
|
2491
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2492
|
+
qs,
|
|
2493
|
+
json: true,
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
else if (operation === 'get') {
|
|
2497
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
2498
|
+
response = await this.helpers.request({
|
|
2499
|
+
method: 'GET',
|
|
2500
|
+
url: `${baseUrl}/contacts/v1/${contactId}`,
|
|
2501
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2502
|
+
json: true,
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
else if (operation === 'create') {
|
|
2506
|
+
const name = this.getNodeParameter('contactName', i);
|
|
2507
|
+
const phoneNumber = this.getNodeParameter('contactPhone', i);
|
|
2508
|
+
const additional = this.getNodeParameter('contactAdditional', i, {});
|
|
2509
|
+
const body = { name, phoneNumber };
|
|
2510
|
+
if (additional.email)
|
|
2511
|
+
body.email = additional.email;
|
|
2512
|
+
if (additional.tags)
|
|
2513
|
+
body.tags = additional.tags;
|
|
2514
|
+
if (additional.customFields) {
|
|
2515
|
+
body.customFields = typeof additional.customFields === 'string'
|
|
2516
|
+
? JSON.parse(additional.customFields)
|
|
2517
|
+
: additional.customFields;
|
|
2518
|
+
}
|
|
2519
|
+
if (additional.metadata) {
|
|
2520
|
+
body.metadata = typeof additional.metadata === 'string'
|
|
2521
|
+
? JSON.parse(additional.metadata)
|
|
2522
|
+
: additional.metadata;
|
|
2523
|
+
}
|
|
2524
|
+
response = await this.helpers.request({
|
|
2525
|
+
method: 'POST',
|
|
2526
|
+
url: `${baseUrl}/contacts/v1`,
|
|
2527
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2528
|
+
body,
|
|
2529
|
+
json: true,
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
else if (operation === 'update') {
|
|
2533
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
2534
|
+
const updateFields = this.getNodeParameter('contactUpdateFields', i, {});
|
|
2535
|
+
const body = {};
|
|
2536
|
+
Object.entries(updateFields).forEach(([key, value]) => {
|
|
2537
|
+
if (value !== undefined && value !== '') {
|
|
2538
|
+
if (key === 'customFields') {
|
|
2539
|
+
body[key] = typeof value === 'string' ? JSON.parse(value) : value;
|
|
2540
|
+
}
|
|
2541
|
+
else {
|
|
2542
|
+
body[key] = value;
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
});
|
|
2546
|
+
response = await this.helpers.request({
|
|
2547
|
+
method: 'PATCH',
|
|
2548
|
+
url: `${baseUrl}/contacts/v1/${contactId}`,
|
|
2549
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2550
|
+
body,
|
|
2551
|
+
json: true,
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
else if (operation === 'delete') {
|
|
2555
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
2556
|
+
response = await this.helpers.request({
|
|
2557
|
+
method: 'DELETE',
|
|
2558
|
+
url: `${baseUrl}/contacts/v1/${contactId}`,
|
|
2559
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2560
|
+
json: true,
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
else if (operation === 'search') {
|
|
2564
|
+
const query = this.getNodeParameter('searchQuery', i);
|
|
2565
|
+
const options = this.getNodeParameter('searchOptions', i, {});
|
|
2566
|
+
const body = { query };
|
|
2567
|
+
if (options.filters) {
|
|
2568
|
+
body.filters = typeof options.filters === 'string' ? JSON.parse(options.filters) : options.filters;
|
|
2569
|
+
}
|
|
2570
|
+
if (options.limit)
|
|
2571
|
+
body.limit = options.limit;
|
|
2572
|
+
if (options.cursor)
|
|
2573
|
+
body.cursor = options.cursor;
|
|
2574
|
+
response = await this.helpers.request({
|
|
2575
|
+
method: 'POST',
|
|
2576
|
+
url: `${baseUrl}/contacts/v1/search`,
|
|
2577
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2578
|
+
body,
|
|
2579
|
+
json: true,
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
else if (operation === 'bulk') {
|
|
2583
|
+
const bulkOperation = this.getNodeParameter('bulkOperation', i);
|
|
2584
|
+
const contacts = this.getNodeParameter('bulkContacts', i);
|
|
2585
|
+
const data = this.getNodeParameter('bulkData', i, '{}');
|
|
2586
|
+
response = await this.helpers.request({
|
|
2587
|
+
method: 'POST',
|
|
2588
|
+
url: `${baseUrl}/contacts/v1/bulk`,
|
|
2589
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2590
|
+
body: {
|
|
2591
|
+
operation: bulkOperation,
|
|
2592
|
+
contacts: typeof contacts === 'string' ? JSON.parse(contacts) : contacts,
|
|
2593
|
+
data: typeof data === 'string' ? JSON.parse(data) : data,
|
|
2594
|
+
},
|
|
2595
|
+
json: true,
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
else if (operation === 'merge') {
|
|
2599
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
2600
|
+
const mergeWith = this.getNodeParameter('mergeWithId', i);
|
|
2601
|
+
response = await this.helpers.request({
|
|
2602
|
+
method: 'POST',
|
|
2603
|
+
url: `${baseUrl}/contacts/v1/${contactId}/merge`,
|
|
2604
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2605
|
+
body: { mergeWith },
|
|
2606
|
+
json: true,
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
else if (operation === 'getTags') {
|
|
2610
|
+
response = await this.helpers.request({
|
|
2611
|
+
method: 'GET',
|
|
2612
|
+
url: `${baseUrl}/contacts/v1/tags`,
|
|
2613
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2614
|
+
json: true,
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
else if (operation === 'getFields') {
|
|
2618
|
+
response = await this.helpers.request({
|
|
2619
|
+
method: 'GET',
|
|
2620
|
+
url: `${baseUrl}/contacts/v1/fields`,
|
|
2621
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2622
|
+
json: true,
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
// ===========================================
|
|
2627
|
+
// FUNNEL RESOURCE
|
|
2628
|
+
// ===========================================
|
|
2629
|
+
else if (resource === 'funnel') {
|
|
2630
|
+
if (operation === 'listStages') {
|
|
2631
|
+
const options = this.getNodeParameter('stageListOptions', i, {});
|
|
2632
|
+
const qs = {};
|
|
2633
|
+
if (options.limit)
|
|
2634
|
+
qs.limit = options.limit;
|
|
2635
|
+
if (options.offset)
|
|
2636
|
+
qs.offset = options.offset;
|
|
2637
|
+
response = await this.helpers.request({
|
|
2638
|
+
method: 'GET',
|
|
2639
|
+
url: `${baseUrl}/funnel/v1/stages`,
|
|
2640
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2641
|
+
qs,
|
|
2642
|
+
json: true,
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
else if (operation === 'getStage') {
|
|
2646
|
+
const stageId = this.getNodeParameter('stageId', i);
|
|
2647
|
+
response = await this.helpers.request({
|
|
2648
|
+
method: 'GET',
|
|
2649
|
+
url: `${baseUrl}/funnel/v1/stages/${stageId}`,
|
|
2650
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2651
|
+
json: true,
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
else if (operation === 'createStage') {
|
|
2655
|
+
const name = this.getNodeParameter('stageName', i);
|
|
2656
|
+
const options = this.getNodeParameter('stageOptions', i, {});
|
|
2657
|
+
const body = { name };
|
|
2658
|
+
if (options.position !== undefined)
|
|
2659
|
+
body.position = options.position;
|
|
2660
|
+
if (options.color)
|
|
2661
|
+
body.color = options.color;
|
|
2662
|
+
if (options.description)
|
|
2663
|
+
body.description = options.description;
|
|
2664
|
+
response = await this.helpers.request({
|
|
2665
|
+
method: 'POST',
|
|
2666
|
+
url: `${baseUrl}/funnel/v1/stages`,
|
|
2667
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2668
|
+
body,
|
|
2669
|
+
json: true,
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
else if (operation === 'updateStage') {
|
|
2673
|
+
const stageId = this.getNodeParameter('stageId', i);
|
|
2674
|
+
const updateFields = this.getNodeParameter('stageUpdateFields', i, {});
|
|
2675
|
+
const body = {};
|
|
2676
|
+
Object.entries(updateFields).forEach(([key, value]) => {
|
|
2677
|
+
if (value !== undefined && value !== '') {
|
|
2678
|
+
body[key] = value;
|
|
2679
|
+
}
|
|
2680
|
+
});
|
|
2681
|
+
response = await this.helpers.request({
|
|
2682
|
+
method: 'PATCH',
|
|
2683
|
+
url: `${baseUrl}/funnel/v1/stages/${stageId}`,
|
|
2684
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2685
|
+
body,
|
|
2686
|
+
json: true,
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
else if (operation === 'deleteStage') {
|
|
2690
|
+
const stageId = this.getNodeParameter('stageId', i);
|
|
2691
|
+
response = await this.helpers.request({
|
|
2692
|
+
method: 'DELETE',
|
|
2693
|
+
url: `${baseUrl}/funnel/v1/stages/${stageId}`,
|
|
2694
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
2695
|
+
json: true,
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
else if (operation === 'reorderStages') {
|
|
2699
|
+
const stages = this.getNodeParameter('stagesOrder', i);
|
|
2700
|
+
response = await this.helpers.request({
|
|
2701
|
+
method: 'PATCH',
|
|
2702
|
+
url: `${baseUrl}/funnel/v1/stages/reorder`,
|
|
2703
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2704
|
+
body: { stages: typeof stages === 'string' ? JSON.parse(stages) : stages },
|
|
2705
|
+
json: true,
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
else if (operation === 'moveContact') {
|
|
2709
|
+
const contactId = this.getNodeParameter('contactIdFunnel', i);
|
|
2710
|
+
const stageId = this.getNodeParameter('targetStageId', i);
|
|
2711
|
+
response = await this.helpers.request({
|
|
2712
|
+
method: 'POST',
|
|
2713
|
+
url: `${baseUrl}/funnel/v1/contacts/${contactId}/move`,
|
|
2714
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
2715
|
+
body: { stageId },
|
|
2716
|
+
json: true,
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
if (response) {
|
|
2721
|
+
returnData.push({ json: response });
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
catch (error) {
|
|
2725
|
+
if (this.continueOnFail()) {
|
|
2726
|
+
returnData.push({ json: { error: error.message } });
|
|
2727
|
+
continue;
|
|
2728
|
+
}
|
|
2729
|
+
throw error;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
return [returnData];
|
|
2733
|
+
}
|
|
1788
2734
|
}
|
|
1789
2735
|
exports.Whaapy = Whaapy;
|
|
@@ -91,16 +91,8 @@ class WhaapyTrigger {
|
|
|
91
91
|
type: 'collection',
|
|
92
92
|
placeholder: 'Add Option',
|
|
93
93
|
default: {},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
displayName: 'Webhook Secret',
|
|
97
|
-
name: 'webhookSecret',
|
|
98
|
-
type: 'string',
|
|
99
|
-
typeOptions: { password: true },
|
|
100
|
-
default: '',
|
|
101
|
-
description: 'Secret to verify webhook signatures (optional)',
|
|
102
|
-
},
|
|
103
|
-
],
|
|
94
|
+
description: 'The webhook secret is automatically generated by Whaapy and returned in the X-Webhook-Signature header',
|
|
95
|
+
options: [],
|
|
104
96
|
},
|
|
105
97
|
],
|
|
106
98
|
};
|
|
@@ -113,13 +105,13 @@ class WhaapyTrigger {
|
|
|
113
105
|
try {
|
|
114
106
|
const response = await this.helpers.request({
|
|
115
107
|
method: 'GET',
|
|
116
|
-
url: `${credentials.baseUrl}/webhooks
|
|
108
|
+
url: `${credentials.baseUrl}/user-webhooks`,
|
|
117
109
|
headers: {
|
|
118
110
|
Authorization: `Bearer ${credentials.apiKey}`,
|
|
119
111
|
},
|
|
120
112
|
json: true,
|
|
121
113
|
});
|
|
122
|
-
const webhooks = response.
|
|
114
|
+
const webhooks = response.data || response.webhooks || [];
|
|
123
115
|
return webhooks.some((webhook) => {
|
|
124
116
|
var _a;
|
|
125
117
|
return webhook.url === webhookUrl &&
|
|
@@ -131,22 +123,21 @@ class WhaapyTrigger {
|
|
|
131
123
|
}
|
|
132
124
|
},
|
|
133
125
|
async create() {
|
|
134
|
-
var _a;
|
|
126
|
+
var _a, _b;
|
|
135
127
|
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
136
128
|
const event = this.getNodeParameter('event');
|
|
137
|
-
const options = this.getNodeParameter('options');
|
|
138
129
|
const credentials = await this.getCredentials('whaapyApi');
|
|
130
|
+
// Generate a unique name for the webhook
|
|
131
|
+
const eventName = event === '*' ? 'all-events' : event.replace('.', '-');
|
|
139
132
|
const body = {
|
|
133
|
+
name: `n8n-${eventName}-${Date.now()}`,
|
|
140
134
|
url: webhookUrl,
|
|
141
135
|
events: event === '*' ? ['*'] : [event],
|
|
142
136
|
};
|
|
143
|
-
if (options.webhookSecret) {
|
|
144
|
-
body.secret = options.webhookSecret;
|
|
145
|
-
}
|
|
146
137
|
try {
|
|
147
138
|
const response = await this.helpers.request({
|
|
148
139
|
method: 'POST',
|
|
149
|
-
url: `${credentials.baseUrl}/webhooks
|
|
140
|
+
url: `${credentials.baseUrl}/user-webhooks`,
|
|
150
141
|
headers: {
|
|
151
142
|
Authorization: `Bearer ${credentials.apiKey}`,
|
|
152
143
|
'Content-Type': 'application/json',
|
|
@@ -155,7 +146,7 @@ class WhaapyTrigger {
|
|
|
155
146
|
json: true,
|
|
156
147
|
});
|
|
157
148
|
const webhookData = this.getWorkflowStaticData('node');
|
|
158
|
-
webhookData.webhookId = response.id || ((
|
|
149
|
+
webhookData.webhookId = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.id) || response.id || ((_b = response.webhook) === null || _b === void 0 ? void 0 : _b.id);
|
|
159
150
|
return true;
|
|
160
151
|
}
|
|
161
152
|
catch (error) {
|
|
@@ -171,7 +162,7 @@ class WhaapyTrigger {
|
|
|
171
162
|
try {
|
|
172
163
|
await this.helpers.request({
|
|
173
164
|
method: 'DELETE',
|
|
174
|
-
url: `${credentials.baseUrl}/webhooks
|
|
165
|
+
url: `${credentials.baseUrl}/user-webhooks/${webhookData.webhookId}`,
|
|
175
166
|
headers: {
|
|
176
167
|
Authorization: `Bearer ${credentials.apiKey}`,
|
|
177
168
|
},
|