@kichat/n8n-nodes-kirimchat 1.2.8 → 1.3.1

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.
@@ -41,6 +41,34 @@ function validateInteractiveMessage(interactive) {
41
41
  }
42
42
  return true;
43
43
  }
44
+ const MAX_TEMPLATE_VARS = 20;
45
+ const templateVariableCountOptions = [
46
+ { name: 'No variables', value: 0 },
47
+ ...Array.from({ length: MAX_TEMPLATE_VARS }, (_, i) => ({
48
+ name: `${i + 1}`,
49
+ value: i + 1,
50
+ })),
51
+ ];
52
+ // Generate individual template variable fields with displayOptions
53
+ // Each field shows only when templateVariableCount >= its number
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ const templateVariableFields = Array.from({ length: MAX_TEMPLATE_VARS }, (_, idx) => {
56
+ const varNum = idx + 1;
57
+ return {
58
+ displayName: `Variable {{${varNum}}}`,
59
+ name: `templateVar${varNum}`,
60
+ type: 'string',
61
+ displayOptions: {
62
+ show: {
63
+ operation: ['sendMessage'],
64
+ messageType: ['template'],
65
+ templateVariableCount: Array.from({ length: MAX_TEMPLATE_VARS - idx }, (_, j) => varNum + j),
66
+ },
67
+ },
68
+ default: '',
69
+ description: `Value for template placeholder {{${varNum}}}`,
70
+ };
71
+ });
44
72
  class KirimChat {
45
73
  constructor() {
46
74
  this.description = {
@@ -99,6 +127,12 @@ class KirimChat {
99
127
  description: 'Send typing indicator to show you are typing',
100
128
  action: 'Send typing indicator',
101
129
  },
130
+ {
131
+ name: 'Get Message Status',
132
+ value: 'getMessageStatus',
133
+ description: 'Check the delivery status of a sent message (sent, delivered, read, failed)',
134
+ action: 'Get message status',
135
+ },
102
136
  ],
103
137
  default: 'sendMessage',
104
138
  },
@@ -483,19 +517,21 @@ class KirimChat {
483
517
  placeholder: 'en',
484
518
  },
485
519
  {
486
- displayName: 'Template Variables',
487
- name: 'templateVariables',
488
- type: 'string',
520
+ displayName: 'Number of Variables',
521
+ name: 'templateVariableCount',
522
+ type: 'options',
489
523
  displayOptions: {
490
524
  show: {
491
525
  operation: ['sendMessage'],
492
526
  messageType: ['template'],
493
527
  },
494
528
  },
495
- default: '',
496
- description: 'Comma-separated values for template variables. Example: "John, ORD-123" replaces {{1}} with John and {{2}} with ORD-123. Leave empty if template has no variables.',
497
- placeholder: 'value1, value2, value3',
529
+ options: templateVariableCountOptions,
530
+ default: 0,
531
+ noDataExpression: true,
532
+ description: 'Match the variable count shown in template name (e.g., [12 var] = select 12)',
498
533
  },
534
+ ...templateVariableFields,
499
535
  {
500
536
  displayName: 'Advanced: Template Components (JSON)',
501
537
  name: 'templateComponents',
@@ -621,6 +657,65 @@ class KirimChat {
621
657
  placeholder: 'https://example.com',
622
658
  },
623
659
  // Reply buttons specific fields
660
+ {
661
+ displayName: 'Use Advanced Mode (JSON)',
662
+ name: 'replyButtonsAdvancedMode',
663
+ type: 'boolean',
664
+ displayOptions: {
665
+ show: {
666
+ operation: ['sendMessage'],
667
+ messageType: ['interactive'],
668
+ interactiveType: ['button'],
669
+ },
670
+ },
671
+ default: false,
672
+ description: 'Enable to enter buttons as JSON. Disable for simple form input.',
673
+ },
674
+ // Simple mode - form fields
675
+ {
676
+ displayName: 'Reply Buttons',
677
+ name: 'replyButtonItems',
678
+ type: 'fixedCollection',
679
+ typeOptions: {
680
+ multipleValues: true,
681
+ maxValue: 3,
682
+ },
683
+ displayOptions: {
684
+ show: {
685
+ operation: ['sendMessage'],
686
+ messageType: ['interactive'],
687
+ interactiveType: ['button'],
688
+ replyButtonsAdvancedMode: [false],
689
+ },
690
+ },
691
+ default: { buttons: [] },
692
+ description: 'Add up to 3 reply buttons',
693
+ options: [
694
+ {
695
+ name: 'buttons',
696
+ displayName: 'Buttons',
697
+ values: [
698
+ {
699
+ displayName: 'ID',
700
+ name: 'id',
701
+ type: 'string',
702
+ default: '',
703
+ description: 'Unique identifier for this button (max 256 chars)',
704
+ placeholder: 'btn-yes',
705
+ },
706
+ {
707
+ displayName: 'Title',
708
+ name: 'title',
709
+ type: 'string',
710
+ default: '',
711
+ description: 'Button text displayed to user (max 20 chars)',
712
+ placeholder: 'Yes',
713
+ },
714
+ ],
715
+ },
716
+ ],
717
+ },
718
+ // Advanced mode (JSON)
624
719
  {
625
720
  displayName: 'Buttons (JSON)',
626
721
  name: 'replyButtons',
@@ -631,6 +726,7 @@ class KirimChat {
631
726
  operation: ['sendMessage'],
632
727
  messageType: ['interactive'],
633
728
  interactiveType: ['button'],
729
+ replyButtonsAdvancedMode: [true],
634
730
  },
635
731
  },
636
732
  default: '[{"id": "btn1", "title": "Yes"}, {"id": "btn2", "title": "No"}]',
@@ -874,6 +970,23 @@ class KirimChat {
874
970
  placeholder: 'tpl_abc123',
875
971
  },
876
972
  // ============================================
973
+ // GET MESSAGE STATUS FIELDS
974
+ // ============================================
975
+ {
976
+ displayName: 'Message ID',
977
+ name: 'statusMessageId',
978
+ type: 'string',
979
+ required: true,
980
+ displayOptions: {
981
+ show: {
982
+ operation: ['getMessageStatus'],
983
+ },
984
+ },
985
+ default: '',
986
+ description: 'The message ID returned from Send Message operation',
987
+ placeholder: 'cmlacq91h000pltieyh3w20rh',
988
+ },
989
+ // ============================================
877
990
  // SEND TYPING FIELDS
878
991
  // ============================================
879
992
  {
@@ -1146,11 +1259,8 @@ class KirimChat {
1146
1259
  value: JSON.stringify({
1147
1260
  name: t.template_name,
1148
1261
  language: t.language,
1149
- has_variables: varCount > 0,
1150
- variable_count: varCount,
1151
- content: t.content,
1152
1262
  }),
1153
- description: ((_a = t.content) === null || _a === void 0 ? void 0 : _a.substring(0, 80)) || '',
1263
+ description: ((_a = t.content) === null || _a === void 0 ? void 0 : _a.substring(0, 100)) || '',
1154
1264
  };
1155
1265
  });
1156
1266
  }
@@ -1250,11 +1360,13 @@ class KirimChat {
1250
1360
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid template selection. Please re-select the template.', { itemIndex: i });
1251
1361
  }
1252
1362
  }
1253
- // Handle comma-separated variables
1254
- const templateVariablesStr = this.getNodeParameter('templateVariables', i, '');
1255
- const templateVariables = templateVariablesStr
1256
- ? templateVariablesStr.split(',').map((v) => v.trim()).filter((v) => v.length > 0)
1257
- : [];
1363
+ // Handle template variables from individual fields
1364
+ const templateVarCount = this.getNodeParameter('templateVariableCount', i, 0);
1365
+ const templateVariables = [];
1366
+ for (let v = 1; v <= templateVarCount; v++) {
1367
+ const val = this.getNodeParameter(`templateVar${v}`, i, '');
1368
+ templateVariables.push(val);
1369
+ }
1258
1370
  if (templateVariables.length > 0) {
1259
1371
  // Build body component with parameters
1260
1372
  const bodyParameters = templateVariables.map((v) => ({
@@ -1318,25 +1430,64 @@ class KirimChat {
1318
1430
  };
1319
1431
  }
1320
1432
  else if (interactiveType === 'button') {
1321
- const replyButtonsJson = this.getNodeParameter('replyButtons', i);
1322
- let replyButtons;
1323
- try {
1324
- replyButtons = JSON.parse(replyButtonsJson);
1325
- }
1326
- catch (error) {
1327
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Reply Buttons: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
1433
+ const advancedMode = this.getNodeParameter('replyButtonsAdvancedMode', i, false);
1434
+ let formattedButtons;
1435
+ if (advancedMode) {
1436
+ // Advanced mode: parse JSON
1437
+ const replyButtonsJson = this.getNodeParameter('replyButtons', i);
1438
+ let replyButtons;
1439
+ try {
1440
+ replyButtons = JSON.parse(replyButtonsJson);
1441
+ }
1442
+ catch (error) {
1443
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Reply Buttons: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
1444
+ }
1445
+ if (!Array.isArray(replyButtons) || replyButtons.length === 0 || replyButtons.length > 3) {
1446
+ 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 });
1447
+ }
1448
+ // Transform to WhatsApp format
1449
+ formattedButtons = replyButtons.map((btn) => ({
1450
+ type: 'reply',
1451
+ reply: {
1452
+ id: (btn.id || '').trim(),
1453
+ title: (btn.title || '').trim(),
1454
+ },
1455
+ }));
1328
1456
  }
1329
- if (!Array.isArray(replyButtons) || replyButtons.length === 0 || replyButtons.length > 3) {
1330
- 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 });
1457
+ else {
1458
+ // Simple mode: build from form fields
1459
+ const buttonItemsData = this.getNodeParameter('replyButtonItems', i, { buttons: [] });
1460
+ if (!buttonItemsData.buttons || buttonItemsData.buttons.length === 0) {
1461
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one reply button is required', { itemIndex: i });
1462
+ }
1463
+ if (buttonItemsData.buttons.length > 3) {
1464
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Maximum 3 reply buttons allowed', { itemIndex: i });
1465
+ }
1466
+ // Track IDs to ensure uniqueness
1467
+ const usedIds = new Set();
1468
+ formattedButtons = buttonItemsData.buttons.map((btn, index) => {
1469
+ const id = (btn.id || '').trim();
1470
+ const title = (btn.title || '').trim();
1471
+ if (!id || !title) {
1472
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: ID and Title are required (cannot be empty)`, { itemIndex: i });
1473
+ }
1474
+ // Check for duplicate IDs
1475
+ if (usedIds.has(id)) {
1476
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: Duplicate ID "${id}". Each button must have a unique ID.`, { itemIndex: i });
1477
+ }
1478
+ usedIds.add(id);
1479
+ if (id.length > 256) {
1480
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: ID exceeds maximum length of 256 characters`, { itemIndex: i });
1481
+ }
1482
+ if (title.length > 20) {
1483
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Button ${index + 1}: Title exceeds maximum length of 20 characters`, { itemIndex: i });
1484
+ }
1485
+ return {
1486
+ type: 'reply',
1487
+ reply: { id, title },
1488
+ };
1489
+ });
1331
1490
  }
1332
- // Transform to n8n format
1333
- const formattedButtons = replyButtons.map((btn) => ({
1334
- type: 'reply',
1335
- reply: {
1336
- id: btn.id || '',
1337
- title: btn.title || '',
1338
- },
1339
- }));
1340
1491
  interactive.action = {
1341
1492
  buttons: formattedButtons,
1342
1493
  };
@@ -1554,6 +1705,17 @@ class KirimChat {
1554
1705
  json: true,
1555
1706
  });
1556
1707
  }
1708
+ else if (operation === 'getMessageStatus') {
1709
+ // ============================================
1710
+ // GET MESSAGE STATUS OPERATION
1711
+ // ============================================
1712
+ const statusMsgId = this.getNodeParameter('statusMessageId', i);
1713
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
1714
+ method: 'GET',
1715
+ url: `${baseUrl}/messages/${statusMsgId}/status`,
1716
+ json: true,
1717
+ });
1718
+ }
1557
1719
  else {
1558
1720
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not supported!`);
1559
1721
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kichat/n8n-nodes-kirimchat",
3
- "version": "1.2.8",
3
+ "version": "1.3.1",
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",