@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: '
|
|
487
|
-
name: '
|
|
488
|
-
type: '
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
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 =
|
|
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
|
-
|
|
1210
|
-
|
|
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,
|
|
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
|
|
1314
|
-
const
|
|
1315
|
-
const templateVariables =
|
|
1316
|
-
|
|
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
|
-
//
|
|
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.
|
|
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",
|