@peopl-health/nexus 1.1.5 → 1.1.7
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.
- package/lib/adapters/BaileysProvider.js +1 -1
- package/lib/config/awsConfig.js +103 -0
- package/lib/config/llmConfig.js +4 -0
- package/lib/controllers/assistantController.js +9 -28
- package/lib/controllers/conversationController.js +13 -2
- package/lib/controllers/templateController.js +69 -34
- package/lib/controllers/templateFlowController.js +108 -0
- package/lib/controllers/uploadController.js +82 -0
- package/lib/core/NexusMessaging.js +15 -1
- package/lib/helpers/assistantHelper.js +292 -0
- package/lib/helpers/baileysHelper.js +149 -0
- package/lib/helpers/filesHelper.js +134 -0
- package/lib/helpers/llmsHelper.js +202 -0
- package/lib/helpers/mediaHelper.js +72 -0
- package/lib/helpers/mongoHelper.js +45 -0
- package/lib/helpers/qrHelper.js +22 -0
- package/lib/helpers/twilioHelper.js +138 -0
- package/lib/helpers/whatsappHelper.js +75 -0
- package/lib/models/templateModel.js +72 -0
- package/lib/routes/index.js +5 -3
- package/lib/services/assistantService.js +42 -46
- package/lib/templates/predefinedTemplates.js +81 -0
- package/lib/templates/templateStructure.js +204 -0
- package/lib/utils/errorHandler.js +8 -0
- package/package.json +5 -1
- package/lib/services/whatsappService.js +0 -23
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const { Historial_Clinico_ID } = require('../config/airtableConfig.js');
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
let llmProvider = null;
|
|
4
|
+
const configureLLMProvider = (provider) => {
|
|
5
|
+
llmProvider = provider;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
let assistantConfig = null;
|
|
9
|
+
let assistantRegistry = {};
|
|
4
10
|
|
|
5
11
|
const { Message, formatTimestamp } = require('../models/messageModel.js');
|
|
6
12
|
const { Thread } = require('../models/threadModel.js');
|
|
@@ -9,26 +15,28 @@ const { checkRunStatus, getCurRow } = require('../helpers/assistantHelper.js');
|
|
|
9
15
|
const { processMessage, getLastMessages } = require('../helpers/assistantHelper.js');
|
|
10
16
|
const { delay } = require('../helpers/whatsappHelper.js');
|
|
11
17
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const configureAssistants = (config) => {
|
|
19
|
+
if (!config) {
|
|
20
|
+
throw new Error('Assistant configuration is required');
|
|
21
|
+
}
|
|
22
|
+
assistantConfig = config;
|
|
23
|
+
};
|
|
17
24
|
|
|
25
|
+
const registerAssistant = (assistantId, AssistantClass) => {
|
|
26
|
+
assistantRegistry[assistantId] = AssistantClass;
|
|
27
|
+
};
|
|
18
28
|
|
|
19
29
|
const getAssistantById = (assistant_id, thread) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
case PATIENT_SCHEDULE_ASST:
|
|
28
|
-
return new PatientScheduleAssistant(thread);
|
|
29
|
-
default:
|
|
30
|
-
return new GeneralAssistant(thread);
|
|
30
|
+
if (!assistantConfig) {
|
|
31
|
+
throw new Error('Assistants not configured. Call configureAssistants() first.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const AssistantClass = assistantRegistry[assistant_id];
|
|
35
|
+
if (!AssistantClass) {
|
|
36
|
+
throw new Error(`Assistant '${assistant_id}' not found. Available assistants: ${Object.keys(assistantRegistry).join(', ')}`);
|
|
31
37
|
}
|
|
38
|
+
|
|
39
|
+
return new AssistantClass(thread);
|
|
32
40
|
};
|
|
33
41
|
|
|
34
42
|
|
|
@@ -50,7 +58,7 @@ const createAssistant = async (code, assistant_id, messages=[], prevThread=null)
|
|
|
50
58
|
|
|
51
59
|
// Add new messages to memory
|
|
52
60
|
for (const message of messages) {
|
|
53
|
-
await
|
|
61
|
+
await llmProvider.beta.threads.messages.create(
|
|
54
62
|
initialThread.id, { role: 'assistant', content: message }
|
|
55
63
|
);
|
|
56
64
|
}
|
|
@@ -73,7 +81,7 @@ const createAssistant = async (code, assistant_id, messages=[], prevThread=null)
|
|
|
73
81
|
|
|
74
82
|
// Delete previous thread
|
|
75
83
|
if (prevThread) {
|
|
76
|
-
await
|
|
84
|
+
await llmProvider.beta.threads.del(prevThread.thread_id);
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
return thread;
|
|
@@ -87,7 +95,7 @@ const addMsgAssistant = async (code, inMessages, reply = false) => {
|
|
|
87
95
|
|
|
88
96
|
for (const message of inMessages) {
|
|
89
97
|
console.log(message);
|
|
90
|
-
await
|
|
98
|
+
await llmProvider.beta.threads.messages.create(
|
|
91
99
|
thread.thread_id, { role: 'assistant', content: message }
|
|
92
100
|
);
|
|
93
101
|
}
|
|
@@ -95,7 +103,7 @@ const addMsgAssistant = async (code, inMessages, reply = false) => {
|
|
|
95
103
|
if (!reply) return null;
|
|
96
104
|
|
|
97
105
|
const assistant = getAssistantById(thread.assistant_id, thread);
|
|
98
|
-
const run = await
|
|
106
|
+
const run = await llmProvider.beta.threads.runs.create(
|
|
99
107
|
thread.thread_id,
|
|
100
108
|
{
|
|
101
109
|
assistant_id: thread.assistant_id
|
|
@@ -106,7 +114,7 @@ const addMsgAssistant = async (code, inMessages, reply = false) => {
|
|
|
106
114
|
await checkRunStatus(assistant, run.thread_id, run.id);
|
|
107
115
|
await Thread.updateOne({ code: thread.code, active: true }, { $set: { run_id: null } });
|
|
108
116
|
|
|
109
|
-
const messages = await
|
|
117
|
+
const messages = await llmProvider.beta.threads.messages.list(run.thread_id, { run_id: run.id });
|
|
110
118
|
const ans = messages.data[0].content[0].text.value;
|
|
111
119
|
console.log('THE ANS IS', ans);
|
|
112
120
|
|
|
@@ -124,7 +132,7 @@ const addInsAssistant = async (code, instruction) => {
|
|
|
124
132
|
if (thread === null) return null;
|
|
125
133
|
|
|
126
134
|
const assistant = getAssistantById(thread.assistant_id, thread);
|
|
127
|
-
const run = await
|
|
135
|
+
const run = await llmProvider.beta.threads.runs.create(
|
|
128
136
|
thread.thread_id, {
|
|
129
137
|
assistant_id: thread.assistant_id,
|
|
130
138
|
additional_instructions: instruction,
|
|
@@ -138,7 +146,7 @@ const addInsAssistant = async (code, instruction) => {
|
|
|
138
146
|
await checkRunStatus(assistant, run.thread_id, run.id);
|
|
139
147
|
await Thread.updateOne({ code: thread.code, active: true }, { $set: { run_id: null } });
|
|
140
148
|
|
|
141
|
-
const messages = await
|
|
149
|
+
const messages = await llmProvider.beta.threads.messages.list(run.thread_id, { run_id: run.id });
|
|
142
150
|
console.log(messages.data[0].content);
|
|
143
151
|
const ans = messages.data[0].content[0].text.value;
|
|
144
152
|
|
|
@@ -166,7 +174,7 @@ const getThread = async (code, message = null) => {
|
|
|
166
174
|
|
|
167
175
|
while (thread && thread.run_id) {
|
|
168
176
|
console.log(`Wait for ${thread.run_id} to be executed`);
|
|
169
|
-
const run = await
|
|
177
|
+
const run = await llmProvider.beta.threads.runs.retrieve(thread.thread_id, thread.run_id);
|
|
170
178
|
if (run.status === 'cancelled' || run.status === 'expired' || run.status === 'completed') {
|
|
171
179
|
await Thread.updateOne({ code: code }, { $set: { run_id: null } });
|
|
172
180
|
}
|
|
@@ -204,11 +212,11 @@ const replyAssistant = async function (code, message_ = null, thread_ = null, ru
|
|
|
204
212
|
return null;
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
let activeRuns = await
|
|
215
|
+
let activeRuns = await llmProvider.beta.threads.runs.list(thread.thread_id);
|
|
208
216
|
console.log('ACTIVE RUNS:', activeRuns.length);
|
|
209
217
|
while (activeRuns.length > 0) {
|
|
210
218
|
console.log(`ACTIVE RUNS ${thread.thread_id}`);
|
|
211
|
-
activeRuns = await
|
|
219
|
+
activeRuns = await llmProvider.beta.threads.runs.list(thread.thread_id);
|
|
212
220
|
await delay(5000);
|
|
213
221
|
}
|
|
214
222
|
|
|
@@ -223,21 +231,6 @@ const replyAssistant = async function (code, message_ = null, thread_ = null, ru
|
|
|
223
231
|
|
|
224
232
|
if (urls.length > 0) {
|
|
225
233
|
console.log('urls', urls);
|
|
226
|
-
/*for (const url of urls) {
|
|
227
|
-
console.log("url", url);
|
|
228
|
-
await addRecord(Monitoreo_ID, 'estudios', [{"fields": {"estudios": urls,
|
|
229
|
-
"combined_estudios": [ { "url": url} ], "patient_id": [thread.patient_id]}}]);
|
|
230
|
-
}
|
|
231
|
-
const { pdfBuffer, processedFiles } = await combineImagesToPDF(code);
|
|
232
|
-
console.log("AFTER COMBINED IN BUFFER", processedFiles);
|
|
233
|
-
const key = `${code}-${Date.now()}-combined.pdf`;
|
|
234
|
-
await AWS.uploadBufferToS3(pdfBuffer, bucketName, key, "application/pdf");
|
|
235
|
-
const url = await AWS.generatePresignedUrl(bucketName, key);
|
|
236
|
-
console.log("New record", {"estudios": urls, "combined_estudios":
|
|
237
|
-
[ { "url": url} ], "patient_id": [thread.patient_id]});
|
|
238
|
-
await addRecord(Monitoreo_ID, 'estudios', [{"fields": {"estudios": urls,
|
|
239
|
-
"combined_estudios": [ { "url": url} ], "patient_id": [thread.patient_id]}}]);
|
|
240
|
-
await cleanupFiles(processedFiles);*/
|
|
241
234
|
}
|
|
242
235
|
|
|
243
236
|
thread = await getThread(code);
|
|
@@ -245,7 +238,7 @@ const replyAssistant = async function (code, message_ = null, thread_ = null, ru
|
|
|
245
238
|
if (!patientMsg || !thread || thread?.stopped) return null;
|
|
246
239
|
|
|
247
240
|
const assistant = getAssistantById(thread.assistant_id, thread);
|
|
248
|
-
const run = await
|
|
241
|
+
const run = await llmProvider.beta.threads.runs.create(
|
|
249
242
|
thread.thread_id,
|
|
250
243
|
{
|
|
251
244
|
assistant_id: thread.assistant_id,
|
|
@@ -261,7 +254,7 @@ const replyAssistant = async function (code, message_ = null, thread_ = null, ru
|
|
|
261
254
|
console.log('RUN STATUS', runStatus);
|
|
262
255
|
await Thread.updateOne({ code: thread.code, active: true }, { $set: { run_id: null } });
|
|
263
256
|
|
|
264
|
-
const messages = await
|
|
257
|
+
const messages = await llmProvider.beta.threads.messages.list(run.thread_id, { run_id: run.id });
|
|
265
258
|
const reply = messages.data?.[0]?.content?.[0]?.text?.value || '';
|
|
266
259
|
console.log(reply);
|
|
267
260
|
|
|
@@ -292,5 +285,8 @@ module.exports = {
|
|
|
292
285
|
replyAssistant,
|
|
293
286
|
addMsgAssistant,
|
|
294
287
|
addInsAssistant,
|
|
295
|
-
switchAssistant
|
|
288
|
+
switchAssistant,
|
|
289
|
+
configureAssistants,
|
|
290
|
+
registerAssistant,
|
|
291
|
+
configureLLMProvider
|
|
296
292
|
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const { Template } = require('./templateStructure');
|
|
2
|
+
|
|
3
|
+
const predefinedTemplates = {
|
|
4
|
+
citaDrLimon: () => {
|
|
5
|
+
const template = new Template('cita_dr_limon', 'UTILITY', 'es');
|
|
6
|
+
|
|
7
|
+
const patientVars = [
|
|
8
|
+
{
|
|
9
|
+
name: 'Nombre del paciente',
|
|
10
|
+
description: 'Nombre completo del paciente',
|
|
11
|
+
example: 'Juan Pérez'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'Fecha de la cita',
|
|
15
|
+
description: 'Fecha en formato DD/MM',
|
|
16
|
+
example: '18/05'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'Hora de la cita',
|
|
20
|
+
description: 'Hora en formato HH:MM am/pm',
|
|
21
|
+
example: '9:30 am'
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
template.addBodyVariation(
|
|
26
|
+
'Hola buenos días {{1}}, \n\nSoy Briggite del equipo del Dr Limón \n\n📆 Tienes programado una cita con el dr Limón para mañana {{2}} a las {{3}}\n\n✅ Confirma tu asistencia por favor',
|
|
27
|
+
patientVars
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
template.addBodyVariation(
|
|
31
|
+
'Hola {{1}}, buenos días\n\nLe saluda Briggite del equipo médico del Dr Limón\n\n📆 Su cita con el Dr Limón está programada para mañana {{2}} a las {{3}}\n\n✅ Por favor confirme su asistencia',
|
|
32
|
+
patientVars
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
template.addBodyVariation(
|
|
36
|
+
'Buenos días {{1}}\n\nSoy Briggite, asistente del Dr Limón\n\n📆 Le recordamos su cita para mañana {{2}} a las {{3}} con el Dr Limón\n\n✅ Agradecemos su confirmación',
|
|
37
|
+
patientVars
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
template.setBody('Hola buenos días {{1}}, \n\nSoy Briggite del equipo del Dr Limón \n\n📆 Tienes programado una cita con el dr Limón para mañana {{2}} a las {{3}}\n\n✅ Confirma tu asistencia por favor',
|
|
41
|
+
patientVars);
|
|
42
|
+
|
|
43
|
+
return template;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
seguimientoDrLimon: () => {
|
|
47
|
+
const template = new Template('seguimiento_dr_limon', 'UTILITY', 'es');
|
|
48
|
+
|
|
49
|
+
const followUpVars = [
|
|
50
|
+
{
|
|
51
|
+
name: 'Nombre del paciente',
|
|
52
|
+
description: 'Nombre completo del paciente',
|
|
53
|
+
example: 'Carmen Rodríguez'
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
template.addBodyVariation(
|
|
58
|
+
'Hola {{1}} buenos días ¿cómo han amanecido? ¿cómo les fue con las recomendaciones?',
|
|
59
|
+
followUpVars
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
template.addBodyVariation(
|
|
63
|
+
'Buenos días {{1}}, ¿cómo se encuentra hoy? ¿ha podido seguir las recomendaciones médicas?',
|
|
64
|
+
followUpVars
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
template.addBodyVariation(
|
|
68
|
+
'Hola {{1}}, buenos días. Seguimiento médico: ¿cómo se ha sentido? ¿pudo implementar las recomendaciones indicadas?',
|
|
69
|
+
followUpVars
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
template.setBody('Hola {{1}} buenos días ¿cómo han amanecido? ¿cómo les fue con las recomendaciones?',
|
|
73
|
+
followUpVars);
|
|
74
|
+
|
|
75
|
+
return template;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
predefinedTemplates
|
|
81
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const { v4: uuidv4 } = require('uuid');
|
|
2
|
+
|
|
3
|
+
// Nexus provider will be injected
|
|
4
|
+
let nexusProvider = null;
|
|
5
|
+
|
|
6
|
+
// Configure Nexus provider
|
|
7
|
+
const configureNexusProvider = (provider) => {
|
|
8
|
+
nexusProvider = provider;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Check if provider supports templates
|
|
12
|
+
const checkTemplateSupport = () => {
|
|
13
|
+
if (!nexusProvider) {
|
|
14
|
+
throw new Error('Nexus provider not configured. Call configureNexusProvider() first.');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!nexusProvider.createTemplate || typeof nexusProvider.createTemplate !== 'function') {
|
|
18
|
+
throw new Error('Template operations are only supported with Twilio provider');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
class Template {
|
|
23
|
+
constructor(name, category = 'UTILITY', language = 'es') {
|
|
24
|
+
this.name = name;
|
|
25
|
+
this.category = category.toUpperCase();
|
|
26
|
+
this.language = language;
|
|
27
|
+
this.components = {
|
|
28
|
+
header: [],
|
|
29
|
+
body: [],
|
|
30
|
+
footer: [],
|
|
31
|
+
buttons: []
|
|
32
|
+
};
|
|
33
|
+
this.variables = [];
|
|
34
|
+
|
|
35
|
+
const timestamp = Date.now().toString();
|
|
36
|
+
const uniqueId = uuidv4().substring(0, 6);
|
|
37
|
+
this.friendlyName = `${name}_${timestamp}_${uniqueId}`;
|
|
38
|
+
this.templateName = `${name}_${Date.now().toString().substring(0, 10)}`;
|
|
39
|
+
this.approvalName = this.name || `template_${name}_${Math.floor(Math.random() * 1000)}`;
|
|
40
|
+
this.status = 'DRAFT';
|
|
41
|
+
|
|
42
|
+
this.variations = [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
addBodyVariation(text, variableDescriptions = []) {
|
|
46
|
+
this.variations.push({
|
|
47
|
+
text: text,
|
|
48
|
+
variables: variableDescriptions
|
|
49
|
+
});
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setBody(text, variableDescriptions = []) {
|
|
54
|
+
this.addBodyVariation(text, variableDescriptions);
|
|
55
|
+
|
|
56
|
+
const selectedVariation = this.variations.length > 1 ?
|
|
57
|
+
this.variations[Math.floor(Math.random() * this.variations.length)] :
|
|
58
|
+
{ text, variables: variableDescriptions };
|
|
59
|
+
|
|
60
|
+
const enhancedText = `${selectedVariation.text}`;
|
|
61
|
+
|
|
62
|
+
const processedVariables = selectedVariation.variables.map((desc, i) => {
|
|
63
|
+
if (typeof desc === 'string') {
|
|
64
|
+
return {
|
|
65
|
+
name: `var_${i + 1}`,
|
|
66
|
+
description: desc,
|
|
67
|
+
example: `Ejemplo ${i + 1}`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
name: desc.name || `var_${i + 1}`,
|
|
72
|
+
description: desc.description || `Variable ${i + 1}`,
|
|
73
|
+
example: desc.example || `Ejemplo ${i + 1}`
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
this.components.body = [{
|
|
78
|
+
type: 'BODY',
|
|
79
|
+
text: enhancedText,
|
|
80
|
+
example: {
|
|
81
|
+
body_text: processedVariables.map(v => v.example)
|
|
82
|
+
}
|
|
83
|
+
}];
|
|
84
|
+
|
|
85
|
+
this.variables = processedVariables;
|
|
86
|
+
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
addQuickReply(text) {
|
|
91
|
+
if (this.components.buttons.length >= 3) {
|
|
92
|
+
throw new Error('Maximum of 3 buttons allowed');
|
|
93
|
+
}
|
|
94
|
+
this.components.buttons.push({
|
|
95
|
+
type: 'QUICK_REPLY',
|
|
96
|
+
text: text
|
|
97
|
+
});
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
addCallToAction(text, url) {
|
|
102
|
+
if (this.components.buttons.length >= 3) {
|
|
103
|
+
throw new Error('Maximum of 3 buttons allowed');
|
|
104
|
+
}
|
|
105
|
+
this.components.buttons.push({
|
|
106
|
+
type: 'URL',
|
|
107
|
+
text: text,
|
|
108
|
+
url: url
|
|
109
|
+
});
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setFooter(text) {
|
|
114
|
+
if (text.length > 60) {
|
|
115
|
+
throw new Error('Footer text must be 60 characters or less');
|
|
116
|
+
}
|
|
117
|
+
this.components.footer = [{
|
|
118
|
+
type: 'FOOTER',
|
|
119
|
+
text: text
|
|
120
|
+
}];
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
generateTemplateName(baseName) {
|
|
125
|
+
const timestamp = new Date().toISOString().replace(/[^0-9]/g, '').slice(-8);
|
|
126
|
+
return `${baseName}_${timestamp}`.toUpperCase();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
validate() {
|
|
130
|
+
if (!this.components.body || !this.components.body[0]?.text) {
|
|
131
|
+
throw new Error('Template body is required');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const varMatches = this.components.body[0].text.match(/\{\{\d+\}\}/g) || [];
|
|
135
|
+
const varCount = new Set(varMatches.map(m => m.match(/\d+/)[0])).size;
|
|
136
|
+
|
|
137
|
+
if (varCount !== this.variables.length) {
|
|
138
|
+
throw new Error(`Mismatch between variable placeholders (${varCount}) and variable definitions (${this.variables.length})`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
toTwilioFormat() {
|
|
145
|
+
this.validate();
|
|
146
|
+
|
|
147
|
+
const variables = {};
|
|
148
|
+
this.variables.forEach((variable, i) => {
|
|
149
|
+
variables[`${i + 1}`] = variable.example;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const template = {
|
|
153
|
+
friendly_name: this.friendlyName,
|
|
154
|
+
language: this.language,
|
|
155
|
+
variables: variables,
|
|
156
|
+
types: {
|
|
157
|
+
'twilio/text': {
|
|
158
|
+
body: this.components.body[0].text
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
if (this.category) {
|
|
164
|
+
template.categories = [this.category];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.components.buttons && this.components.buttons.length > 0) {
|
|
168
|
+
const actions = this.components.buttons.map((button, index) => {
|
|
169
|
+
return {
|
|
170
|
+
title: button.text,
|
|
171
|
+
id: `button_${index + 1}`
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
template.types['twilio/quick-reply'] = {
|
|
176
|
+
body: this.components.body[0].text,
|
|
177
|
+
actions: actions
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (this.components.footer && this.components.footer.length > 0) {
|
|
182
|
+
const textTypes = ['twilio/text', 'twilio/quick-reply'];
|
|
183
|
+
textTypes.forEach(type => {
|
|
184
|
+
if (template.types[type]) {
|
|
185
|
+
template.types[type].footer = this.components.footer[0].text;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return template;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async save() {
|
|
194
|
+
checkTemplateSupport();
|
|
195
|
+
const twilioFormat = this.toTwilioFormat();
|
|
196
|
+
const createdTemplate = await nexusProvider.createTemplate(twilioFormat);
|
|
197
|
+
return createdTemplate;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
Template,
|
|
203
|
+
configureNexusProvider
|
|
204
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peopl-health/nexus",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"description": "Core messaging and assistant library for WhatsApp communication platforms",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -51,10 +51,13 @@
|
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"airtable": "^0.12.2",
|
|
54
|
+
"aws-sdk": "2.1674.0",
|
|
54
55
|
"axios": "^1.5.0",
|
|
55
56
|
"dotenv": "^16.4.7",
|
|
56
57
|
"moment-timezone": "^0.5.43",
|
|
57
58
|
"mongoose": "^7.5.0",
|
|
59
|
+
"multer": "1.4.5-lts.1",
|
|
60
|
+
"pdf-lib": "1.17.1",
|
|
58
61
|
"pino": "^8.15.0",
|
|
59
62
|
"pino-pretty": "^10.2.0",
|
|
60
63
|
"uuid": "^9.0.0"
|
|
@@ -69,6 +72,7 @@
|
|
|
69
72
|
"@types/node": "^20.5.0",
|
|
70
73
|
"eslint": "^8.47.0",
|
|
71
74
|
"jest": "^29.6.2",
|
|
75
|
+
"sharp": "0.32.6",
|
|
72
76
|
"typescript": "^5.1.6"
|
|
73
77
|
},
|
|
74
78
|
"engines": {
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
let sendMessage;
|
|
2
|
-
let sendScheduledMessage;
|
|
3
|
-
if (process.env.MESSAGING_PROVIDER === 'baileys') {
|
|
4
|
-
const baileyAdapter = require('../adapters/baileys');
|
|
5
|
-
console.log('Baileys adapter:', baileyAdapter);
|
|
6
|
-
console.log('Has sendMessage?', 'sendMessage' in baileyAdapter);
|
|
7
|
-
sendMessage = baileyAdapter.sendMessage;
|
|
8
|
-
sendScheduledMessage = baileyAdapter.sendScheduledMessage;
|
|
9
|
-
} else if (process.env.MESSAGING_PROVIDER === 'twilio') {
|
|
10
|
-
const { sendMessage: twilioSendMessage } = require('../messaging/messageService');
|
|
11
|
-
const { sendScheduledMessage: twilioSendScheduledMessage } = require('../messaging/scheduledMessageService');
|
|
12
|
-
const twilioAdapter = require('../adapters/twilio');
|
|
13
|
-
const twilioClient = twilioAdapter.twilioClient;
|
|
14
|
-
sendMessage = (messageData) => twilioSendMessage(twilioClient, messageData);
|
|
15
|
-
sendScheduledMessage = (messageData) => twilioSendScheduledMessage(twilioClient, messageData);
|
|
16
|
-
} else {
|
|
17
|
-
throw new Error('Unsupported MESSAGING_PROVIDER specified');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
module.exports = {
|
|
21
|
-
sendMessage,
|
|
22
|
-
sendScheduledMessage
|
|
23
|
-
};
|