@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 replyButtonsJson = this.getNodeParameter('replyButtons', i);
1318
- let replyButtons;
1319
- try {
1320
- replyButtons = JSON.parse(replyButtonsJson);
1321
- }
1322
- catch (error) {
1323
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Reply Buttons: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
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
- if (!Array.isArray(replyButtons) || replyButtons.length === 0 || replyButtons.length > 3) {
1326
- 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 });
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
- if (!item.id || !item.title) {
1382
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `List item ${index + 1}: ID and Title are required`, { itemIndex: i });
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
- if (item.id.length > 200) {
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 (item.title.length > 24) {
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 (item.description && item.description.length > 72) {
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: item.id,
1395
- title: item.title,
1507
+ id,
1508
+ title,
1396
1509
  };
1397
- if (item.description) {
1398
- row.description = item.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.2.7",
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",