@kichat/n8n-nodes-kirimchat 1.3.0 → 1.3.2

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,114 @@ function validateInteractiveMessage(interactive) {
41
41
  }
42
42
  return true;
43
43
  }
44
+ /**
45
+ * Count unique placeholder numbers in text (e.g., "{{1}} and {{2}} and {{1}}" = 2)
46
+ */
47
+ function countUniquePlaceholders(text) {
48
+ if (!text)
49
+ return 0;
50
+ const matches = text.match(/\{\{\d+\}\}/g);
51
+ if (!matches)
52
+ return 0;
53
+ const unique = new Set(matches.map((m) => m.replace(/[{}]/g, '')));
54
+ return unique.size;
55
+ }
56
+ /**
57
+ * Build WhatsApp template components with correct variable distribution
58
+ * across header, body, and button components
59
+ */
60
+ function buildTemplateComponentsFromStructure(variables, structure) {
61
+ var _a, _b;
62
+ const components = [];
63
+ let varIdx = 0;
64
+ // Header component
65
+ if (((_a = structure.headerType) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === 'TEXT' && structure.headerContent) {
66
+ const headerVarCount = countUniquePlaceholders(structure.headerContent);
67
+ if (headerVarCount > 0 && varIdx < variables.length) {
68
+ const headerParams = variables.slice(varIdx, varIdx + headerVarCount).map((v) => ({
69
+ type: 'text',
70
+ text: v,
71
+ }));
72
+ components.push({ type: 'header', parameters: headerParams });
73
+ varIdx += headerVarCount;
74
+ }
75
+ }
76
+ else if (['IMAGE', 'VIDEO', 'DOCUMENT'].includes(((_b = structure.headerType) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || '')) {
77
+ if (varIdx < variables.length) {
78
+ const mediaValue = variables[varIdx];
79
+ const mediaType = structure.headerType.toLowerCase();
80
+ const isUrl = mediaValue.startsWith('http');
81
+ components.push({
82
+ type: 'header',
83
+ parameters: [{
84
+ type: mediaType,
85
+ [mediaType]: isUrl ? { link: mediaValue } : { id: mediaValue },
86
+ }],
87
+ });
88
+ varIdx += 1;
89
+ }
90
+ }
91
+ // Body component
92
+ const bodyVarCount = countUniquePlaceholders(structure.content);
93
+ if (bodyVarCount > 0 && varIdx < variables.length) {
94
+ const bodyParams = variables.slice(varIdx, varIdx + bodyVarCount).map((v) => ({
95
+ type: 'text',
96
+ text: v,
97
+ }));
98
+ components.push({ type: 'body', parameters: bodyParams });
99
+ varIdx += bodyVarCount;
100
+ }
101
+ else if (!structure.content && variables.length > 0) {
102
+ // Fallback for old dropdown format (no structure info): all variables go to body
103
+ const bodyParams = variables.map((v) => ({ type: 'text', text: v }));
104
+ components.push({ type: 'body', parameters: bodyParams });
105
+ return components;
106
+ }
107
+ // Button components (URL buttons with dynamic variables)
108
+ if (structure.buttons && Array.isArray(structure.buttons)) {
109
+ structure.buttons.forEach((btn, index) => {
110
+ var _a;
111
+ if (btn.type === 'URL' && ((_a = btn.url) === null || _a === void 0 ? void 0 : _a.includes('{{1}}')) && varIdx < variables.length) {
112
+ components.push({
113
+ type: 'button',
114
+ sub_type: 'url',
115
+ index,
116
+ parameters: [{ type: 'text', text: variables[varIdx] }],
117
+ });
118
+ varIdx += 1;
119
+ }
120
+ });
121
+ }
122
+ return components;
123
+ }
124
+ const MAX_TEMPLATE_VARS = 20;
125
+ const templateVariableCountOptions = [
126
+ { name: 'No variables', value: 0 },
127
+ ...Array.from({ length: MAX_TEMPLATE_VARS }, (_, i) => ({
128
+ name: `${i + 1}`,
129
+ value: i + 1,
130
+ })),
131
+ ];
132
+ // Generate individual template variable fields with displayOptions
133
+ // Each field shows only when templateVariableCount >= its number
134
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
+ const templateVariableFields = Array.from({ length: MAX_TEMPLATE_VARS }, (_, idx) => {
136
+ const varNum = idx + 1;
137
+ return {
138
+ displayName: `Variable {{${varNum}}}`,
139
+ name: `templateVar${varNum}`,
140
+ type: 'string',
141
+ displayOptions: {
142
+ show: {
143
+ operation: ['sendMessage'],
144
+ messageType: ['template'],
145
+ templateVariableCount: Array.from({ length: MAX_TEMPLATE_VARS - idx }, (_, j) => varNum + j),
146
+ },
147
+ },
148
+ default: '',
149
+ description: `Value for template placeholder {{${varNum}}}`,
150
+ };
151
+ });
44
152
  class KirimChat {
45
153
  constructor() {
46
154
  this.description = {
@@ -99,6 +207,12 @@ class KirimChat {
99
207
  description: 'Send typing indicator to show you are typing',
100
208
  action: 'Send typing indicator',
101
209
  },
210
+ {
211
+ name: 'Get Message Status',
212
+ value: 'getMessageStatus',
213
+ description: 'Check the delivery status of a sent message (sent, delivered, read, failed)',
214
+ action: 'Get message status',
215
+ },
102
216
  ],
103
217
  default: 'sendMessage',
104
218
  },
@@ -483,19 +597,21 @@ class KirimChat {
483
597
  placeholder: 'en',
484
598
  },
485
599
  {
486
- displayName: 'Template Variables',
487
- name: 'templateVariables',
488
- type: 'string',
600
+ displayName: 'Number of Variables',
601
+ name: 'templateVariableCount',
602
+ type: 'options',
489
603
  displayOptions: {
490
604
  show: {
491
605
  operation: ['sendMessage'],
492
606
  messageType: ['template'],
493
607
  },
494
608
  },
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',
609
+ options: templateVariableCountOptions,
610
+ default: 0,
611
+ noDataExpression: true,
612
+ description: 'Match the variable count shown in template name (e.g., [12 var] = select 12)',
498
613
  },
614
+ ...templateVariableFields,
499
615
  {
500
616
  displayName: 'Advanced: Template Components (JSON)',
501
617
  name: 'templateComponents',
@@ -934,6 +1050,23 @@ class KirimChat {
934
1050
  placeholder: 'tpl_abc123',
935
1051
  },
936
1052
  // ============================================
1053
+ // GET MESSAGE STATUS FIELDS
1054
+ // ============================================
1055
+ {
1056
+ displayName: 'Message ID',
1057
+ name: 'statusMessageId',
1058
+ type: 'string',
1059
+ required: true,
1060
+ displayOptions: {
1061
+ show: {
1062
+ operation: ['getMessageStatus'],
1063
+ },
1064
+ },
1065
+ default: '',
1066
+ description: 'The message ID returned from Send Message operation',
1067
+ placeholder: 'cmlacq91h000pltieyh3w20rh',
1068
+ },
1069
+ // ============================================
937
1070
  // SEND TYPING FIELDS
938
1071
  // ============================================
939
1072
  {
@@ -1192,25 +1325,51 @@ class KirimChat {
1192
1325
  if (templates.length === 0) {
1193
1326
  return [{ name: '-- No approved templates --', value: '' }];
1194
1327
  }
1195
- // Count variables in content
1196
- const countVariables = (content) => {
1197
- const matches = content === null || content === void 0 ? void 0 : content.match(/\{\{\d+\}\}/g);
1198
- return matches ? matches.length : 0;
1328
+ // Count unique placeholder numbers in text
1329
+ const countUniqueVars = (text) => {
1330
+ if (!text)
1331
+ return 0;
1332
+ const matches = text.match(/\{\{\d+\}\}/g);
1333
+ if (!matches)
1334
+ return 0;
1335
+ const unique = new Set(matches.map((m) => m.replace(/[{}]/g, '')));
1336
+ return unique.size;
1337
+ };
1338
+ // Count variables across all template components
1339
+ const countAllVariables = (t) => {
1340
+ var _a, _b, _c;
1341
+ let count = 0;
1342
+ if (((_a = t.header_type) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === 'TEXT') {
1343
+ count += countUniqueVars(t.header_content);
1344
+ }
1345
+ else if (['IMAGE', 'VIDEO', 'DOCUMENT'].includes(((_b = t.header_type) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || '')) {
1346
+ count += 1;
1347
+ }
1348
+ count += countUniqueVars(t.content);
1349
+ if (t.buttons && Array.isArray(t.buttons)) {
1350
+ for (const btn of t.buttons) {
1351
+ if (btn.type === 'URL' && ((_c = btn.url) === null || _c === void 0 ? void 0 : _c.includes('{{1}}'))) {
1352
+ count += 1;
1353
+ }
1354
+ }
1355
+ }
1356
+ return count;
1199
1357
  };
1200
1358
  return templates.map((t) => {
1201
1359
  var _a;
1202
- const varCount = countVariables(t.content);
1360
+ const varCount = countAllVariables(t);
1203
1361
  const varInfo = varCount > 0 ? ` [${varCount} var]` : '';
1204
1362
  return {
1205
1363
  name: `${t.template_name} (${t.language})${varInfo}`,
1206
1364
  value: JSON.stringify({
1207
1365
  name: t.template_name,
1208
1366
  language: t.language,
1209
- has_variables: varCount > 0,
1210
- variable_count: varCount,
1211
- content: t.content,
1367
+ header_type: t.header_type || null,
1368
+ header_content: t.header_content || null,
1369
+ content: t.content || null,
1370
+ buttons: t.buttons || null,
1212
1371
  }),
1213
- description: ((_a = t.content) === null || _a === void 0 ? void 0 : _a.substring(0, 80)) || '',
1372
+ description: ((_a = t.content) === null || _a === void 0 ? void 0 : _a.substring(0, 100)) || '',
1214
1373
  };
1215
1374
  });
1216
1375
  }
@@ -1276,6 +1435,12 @@ class KirimChat {
1276
1435
  let templateName;
1277
1436
  let templateLanguage;
1278
1437
  let templateComponents = [];
1438
+ let templateStructure = {
1439
+ headerType: null,
1440
+ headerContent: null,
1441
+ content: null,
1442
+ buttons: null,
1443
+ };
1279
1444
  if (manualEntry) {
1280
1445
  // Manual entry mode - user types name and language
1281
1446
  templateName = this.getNodeParameter('templateName', i);
@@ -1305,30 +1470,28 @@ class KirimChat {
1305
1470
  const templateData = JSON.parse(templateSelectValue);
1306
1471
  templateName = templateData.name;
1307
1472
  templateLanguage = templateData.language;
1473
+ templateStructure = {
1474
+ headerType: templateData.header_type || null,
1475
+ headerContent: templateData.header_content || null,
1476
+ content: templateData.content || null,
1477
+ buttons: templateData.buttons || null,
1478
+ };
1308
1479
  }
1309
1480
  catch (error) {
1310
1481
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid template selection. Please re-select the template.', { itemIndex: i });
1311
1482
  }
1312
1483
  }
1313
- // Handle comma-separated variables
1314
- const templateVariablesStr = this.getNodeParameter('templateVariables', i, '');
1315
- const templateVariables = templateVariablesStr
1316
- ? templateVariablesStr.split(',').map((v) => v.trim()).filter((v) => v.length > 0)
1317
- : [];
1484
+ // Handle template variables from individual fields
1485
+ const templateVarCount = this.getNodeParameter('templateVariableCount', i, 0);
1486
+ const templateVariables = [];
1487
+ for (let v = 1; v <= templateVarCount; v++) {
1488
+ const val = this.getNodeParameter(`templateVar${v}`, i, '');
1489
+ templateVariables.push(val);
1490
+ }
1318
1491
  if (templateVariables.length > 0) {
1319
- // Build body component with parameters
1320
- const bodyParameters = templateVariables.map((v) => ({
1321
- type: 'text',
1322
- text: v,
1323
- }));
1324
- // If no components provided, create body component with variables
1492
+ // If no components provided, build from template structure
1325
1493
  if (!Array.isArray(templateComponents) || templateComponents.length === 0) {
1326
- templateComponents = [
1327
- {
1328
- type: 'body',
1329
- parameters: bodyParameters,
1330
- },
1331
- ];
1494
+ templateComponents = buildTemplateComponentsFromStructure(templateVariables, templateStructure);
1332
1495
  }
1333
1496
  }
1334
1497
  body.template = {
@@ -1653,6 +1816,17 @@ class KirimChat {
1653
1816
  json: true,
1654
1817
  });
1655
1818
  }
1819
+ else if (operation === 'getMessageStatus') {
1820
+ // ============================================
1821
+ // GET MESSAGE STATUS OPERATION
1822
+ // ============================================
1823
+ const statusMsgId = this.getNodeParameter('statusMessageId', i);
1824
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
1825
+ method: 'GET',
1826
+ url: `${baseUrl}/messages/${statusMsgId}/status`,
1827
+ json: true,
1828
+ });
1829
+ }
1656
1830
  else {
1657
1831
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not supported!`);
1658
1832
  }
@@ -0,0 +1 @@
1
+ {"root":["../credentials/kirimchatapi.credentials.ts","../nodes/kirimchat/kirimchat.node.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kichat/n8n-nodes-kirimchat",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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",