@kichat/n8n-nodes-kirimchat 1.2.7 → 1.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.
|
@@ -621,6 +621,65 @@ class KirimChat {
|
|
|
621
621
|
placeholder: 'https://example.com',
|
|
622
622
|
},
|
|
623
623
|
// Reply buttons specific fields
|
|
624
|
+
{
|
|
625
|
+
displayName: 'Use Advanced Mode (JSON)',
|
|
626
|
+
name: 'replyButtonsAdvancedMode',
|
|
627
|
+
type: 'boolean',
|
|
628
|
+
displayOptions: {
|
|
629
|
+
show: {
|
|
630
|
+
operation: ['sendMessage'],
|
|
631
|
+
messageType: ['interactive'],
|
|
632
|
+
interactiveType: ['button'],
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
default: false,
|
|
636
|
+
description: 'Enable to enter buttons as JSON. Disable for simple form input.',
|
|
637
|
+
},
|
|
638
|
+
// Simple mode - form fields
|
|
639
|
+
{
|
|
640
|
+
displayName: 'Reply Buttons',
|
|
641
|
+
name: 'replyButtonItems',
|
|
642
|
+
type: 'fixedCollection',
|
|
643
|
+
typeOptions: {
|
|
644
|
+
multipleValues: true,
|
|
645
|
+
maxValue: 3,
|
|
646
|
+
},
|
|
647
|
+
displayOptions: {
|
|
648
|
+
show: {
|
|
649
|
+
operation: ['sendMessage'],
|
|
650
|
+
messageType: ['interactive'],
|
|
651
|
+
interactiveType: ['button'],
|
|
652
|
+
replyButtonsAdvancedMode: [false],
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
default: { buttons: [] },
|
|
656
|
+
description: 'Add up to 3 reply buttons',
|
|
657
|
+
options: [
|
|
658
|
+
{
|
|
659
|
+
name: 'buttons',
|
|
660
|
+
displayName: 'Buttons',
|
|
661
|
+
values: [
|
|
662
|
+
{
|
|
663
|
+
displayName: 'ID',
|
|
664
|
+
name: 'id',
|
|
665
|
+
type: 'string',
|
|
666
|
+
default: '',
|
|
667
|
+
description: 'Unique identifier for this button (max 256 chars)',
|
|
668
|
+
placeholder: 'btn-yes',
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
displayName: 'Title',
|
|
672
|
+
name: 'title',
|
|
673
|
+
type: 'string',
|
|
674
|
+
default: '',
|
|
675
|
+
description: 'Button text displayed to user (max 20 chars)',
|
|
676
|
+
placeholder: 'Yes',
|
|
677
|
+
},
|
|
678
|
+
],
|
|
679
|
+
},
|
|
680
|
+
],
|
|
681
|
+
},
|
|
682
|
+
// Advanced mode (JSON)
|
|
624
683
|
{
|
|
625
684
|
displayName: 'Buttons (JSON)',
|
|
626
685
|
name: 'replyButtons',
|
|
@@ -631,6 +690,7 @@ class KirimChat {
|
|
|
631
690
|
operation: ['sendMessage'],
|
|
632
691
|
messageType: ['interactive'],
|
|
633
692
|
interactiveType: ['button'],
|
|
693
|
+
replyButtonsAdvancedMode: [true],
|
|
634
694
|
},
|
|
635
695
|
},
|
|
636
696
|
default: '[{"id": "btn1", "title": "Yes"}, {"id": "btn2", "title": "No"}]',
|
|
@@ -1280,9 +1340,13 @@ class KirimChat {
|
|
|
1280
1340
|
else if (messageType === 'interactive') {
|
|
1281
1341
|
// WhatsApp interactive message
|
|
1282
1342
|
const interactiveType = this.getNodeParameter('interactiveType', i);
|
|
1283
|
-
const interactiveBody = this.getNodeParameter('interactiveBody', i);
|
|
1284
|
-
const interactiveHeader = this.getNodeParameter('interactiveHeader', i, '');
|
|
1285
|
-
const interactiveFooter = this.getNodeParameter('interactiveFooter', i, '');
|
|
1343
|
+
const interactiveBody = this.getNodeParameter('interactiveBody', i).trim();
|
|
1344
|
+
const interactiveHeader = this.getNodeParameter('interactiveHeader', i, '').trim();
|
|
1345
|
+
const interactiveFooter = this.getNodeParameter('interactiveFooter', i, '').trim();
|
|
1346
|
+
// Validate body text is not empty
|
|
1347
|
+
if (!interactiveBody) {
|
|
1348
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Body Text is required for interactive messages', { itemIndex: i });
|
|
1349
|
+
}
|
|
1286
1350
|
const interactive = {
|
|
1287
1351
|
type: interactiveType,
|
|
1288
1352
|
body: { text: interactiveBody },
|
|
@@ -1314,31 +1378,70 @@ class KirimChat {
|
|
|
1314
1378
|
};
|
|
1315
1379
|
}
|
|
1316
1380
|
else if (interactiveType === 'button') {
|
|
1317
|
-
const
|
|
1318
|
-
let
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1381
|
+
const advancedMode = this.getNodeParameter('replyButtonsAdvancedMode', i, false);
|
|
1382
|
+
let formattedButtons;
|
|
1383
|
+
if (advancedMode) {
|
|
1384
|
+
// Advanced mode: parse JSON
|
|
1385
|
+
const replyButtonsJson = this.getNodeParameter('replyButtons', i);
|
|
1386
|
+
let replyButtons;
|
|
1387
|
+
try {
|
|
1388
|
+
replyButtons = JSON.parse(replyButtonsJson);
|
|
1389
|
+
}
|
|
1390
|
+
catch (error) {
|
|
1391
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Reply Buttons: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
|
|
1392
|
+
}
|
|
1393
|
+
if (!Array.isArray(replyButtons) || replyButtons.length === 0 || replyButtons.length > 3) {
|
|
1394
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Reply Buttons must be a JSON array with 1-3 buttons. Example: [{"id": "btn1", "title": "Yes"}]', { itemIndex: i });
|
|
1395
|
+
}
|
|
1396
|
+
// Transform to WhatsApp format
|
|
1397
|
+
formattedButtons = replyButtons.map((btn) => ({
|
|
1398
|
+
type: 'reply',
|
|
1399
|
+
reply: {
|
|
1400
|
+
id: (btn.id || '').trim(),
|
|
1401
|
+
title: (btn.title || '').trim(),
|
|
1402
|
+
},
|
|
1403
|
+
}));
|
|
1324
1404
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1405
|
+
else {
|
|
1406
|
+
// Simple mode: build from form fields
|
|
1407
|
+
const buttonItemsData = this.getNodeParameter('replyButtonItems', i, { buttons: [] });
|
|
1408
|
+
if (!buttonItemsData.buttons || buttonItemsData.buttons.length === 0) {
|
|
1409
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one reply button is required', { itemIndex: i });
|
|
1410
|
+
}
|
|
1411
|
+
if (buttonItemsData.buttons.length > 3) {
|
|
1412
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Maximum 3 reply buttons allowed', { itemIndex: i });
|
|
1413
|
+
}
|
|
1414
|
+
// Track IDs to ensure uniqueness
|
|
1415
|
+
const usedIds = new Set();
|
|
1416
|
+
formattedButtons = buttonItemsData.buttons.map((btn, index) => {
|
|
1417
|
+
const id = (btn.id || '').trim();
|
|
1418
|
+
const title = (btn.title || '').trim();
|
|
1419
|
+
if (!id || !title) {
|
|
1420
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: ID and Title are required (cannot be empty)`, { itemIndex: i });
|
|
1421
|
+
}
|
|
1422
|
+
// Check for duplicate IDs
|
|
1423
|
+
if (usedIds.has(id)) {
|
|
1424
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: Duplicate ID "${id}". Each button must have a unique ID.`, { itemIndex: i });
|
|
1425
|
+
}
|
|
1426
|
+
usedIds.add(id);
|
|
1427
|
+
if (id.length > 256) {
|
|
1428
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: ID exceeds maximum length of 256 characters`, { itemIndex: i });
|
|
1429
|
+
}
|
|
1430
|
+
if (title.length > 20) {
|
|
1431
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: Title exceeds maximum length of 20 characters`, { itemIndex: i });
|
|
1432
|
+
}
|
|
1433
|
+
return {
|
|
1434
|
+
type: 'reply',
|
|
1435
|
+
reply: { id, title },
|
|
1436
|
+
};
|
|
1437
|
+
});
|
|
1327
1438
|
}
|
|
1328
|
-
// Transform to n8n format
|
|
1329
|
-
const formattedButtons = replyButtons.map((btn) => ({
|
|
1330
|
-
type: 'reply',
|
|
1331
|
-
reply: {
|
|
1332
|
-
id: btn.id || '',
|
|
1333
|
-
title: btn.title || '',
|
|
1334
|
-
},
|
|
1335
|
-
}));
|
|
1336
1439
|
interactive.action = {
|
|
1337
1440
|
buttons: formattedButtons,
|
|
1338
1441
|
};
|
|
1339
1442
|
}
|
|
1340
1443
|
else if (interactiveType === 'list') {
|
|
1341
|
-
const listButtonText = this.getNodeParameter('listButtonText', i);
|
|
1444
|
+
const listButtonText = this.getNodeParameter('listButtonText', i).trim();
|
|
1342
1445
|
const advancedMode = this.getNodeParameter('listAdvancedMode', i, false);
|
|
1343
1446
|
if (!listButtonText) {
|
|
1344
1447
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'List Button Text is required for list interactive messages', { itemIndex: i });
|
|
@@ -1371,31 +1474,41 @@ class KirimChat {
|
|
|
1371
1474
|
}
|
|
1372
1475
|
else {
|
|
1373
1476
|
// Simple mode: build from form fields
|
|
1374
|
-
const sectionTitle = this.getNodeParameter('listSectionTitle', i, '');
|
|
1477
|
+
const sectionTitle = this.getNodeParameter('listSectionTitle', i, '').trim();
|
|
1375
1478
|
const listItemsData = this.getNodeParameter('listItems', i, { items: [] });
|
|
1376
1479
|
if (!listItemsData.items || listItemsData.items.length === 0) {
|
|
1377
1480
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one list item is required', { itemIndex: i });
|
|
1378
1481
|
}
|
|
1482
|
+
// Track IDs to ensure uniqueness
|
|
1483
|
+
const usedIds = new Set();
|
|
1379
1484
|
// Validate and build rows
|
|
1380
1485
|
const rows = listItemsData.items.map((item, index) => {
|
|
1381
|
-
|
|
1382
|
-
|
|
1486
|
+
const id = (item.id || '').trim();
|
|
1487
|
+
const title = (item.title || '').trim();
|
|
1488
|
+
const description = (item.description || '').trim();
|
|
1489
|
+
if (!id || !title) {
|
|
1490
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `List item ${index + 1}: ID and Title are required (cannot be empty)`, { itemIndex: i });
|
|
1491
|
+
}
|
|
1492
|
+
// Check for duplicate IDs
|
|
1493
|
+
if (usedIds.has(id)) {
|
|
1494
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `List item ${index + 1}: Duplicate ID "${id}". Each item must have a unique ID.`, { itemIndex: i });
|
|
1383
1495
|
}
|
|
1384
|
-
|
|
1496
|
+
usedIds.add(id);
|
|
1497
|
+
if (id.length > 200) {
|
|
1385
1498
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `List item ${index + 1}: ID exceeds maximum length of 200 characters`, { itemIndex: i });
|
|
1386
1499
|
}
|
|
1387
|
-
if (
|
|
1500
|
+
if (title.length > 24) {
|
|
1388
1501
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `List item ${index + 1}: Title exceeds maximum length of 24 characters`, { itemIndex: i });
|
|
1389
1502
|
}
|
|
1390
|
-
if (
|
|
1503
|
+
if (description && description.length > 72) {
|
|
1391
1504
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `List item ${index + 1}: Description exceeds maximum length of 72 characters`, { itemIndex: i });
|
|
1392
1505
|
}
|
|
1393
1506
|
const row = {
|
|
1394
|
-
id
|
|
1395
|
-
title
|
|
1507
|
+
id,
|
|
1508
|
+
title,
|
|
1396
1509
|
};
|
|
1397
|
-
if (
|
|
1398
|
-
row.description =
|
|
1510
|
+
if (description) {
|
|
1511
|
+
row.description = description;
|
|
1399
1512
|
}
|
|
1400
1513
|
return row;
|
|
1401
1514
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kichat/n8n-nodes-kirimchat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "n8n community node for KirimChat - Send WhatsApp, Instagram & Messenger messages with interactive buttons, flexible customer lookup, and typing indicators",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|