@peopl-health/nexus 1.1.1 → 1.1.4
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/README.md +2 -46
- package/examples/basic-usage.js +65 -49
- package/examples/consumer-server.js +10 -9
- package/lib/config/airtableConfig.js +45 -0
- package/lib/{utils → config}/mongoAuthConfig.js +3 -13
- package/lib/controllers/assistantController.js +8 -22
- package/lib/controllers/conversationController.js +1 -1
- package/lib/controllers/messageController.js +24 -12
- package/lib/controllers/templateController.js +28 -8
- package/lib/core/NexusMessaging.js +97 -0
- package/lib/routes/index.js +58 -7
- package/lib/services/airtableService.js +82 -0
- package/lib/services/appointmentService.js +55 -0
- package/lib/services/assistantService.js +296 -0
- package/lib/services/conversationService.js +274 -0
- package/lib/services/whatsappService.js +23 -0
- package/lib/utils/index.js +1 -25
- package/package.json +4 -2
- package/lib/utils/twilioHelper.js +0 -75
- package/lib/utils/whatsappHelper.js +0 -60
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
};
|
package/lib/utils/index.js
CHANGED
|
@@ -2,34 +2,10 @@ const AssistantManager = require('./AssistantManager');
|
|
|
2
2
|
const DefaultLLMProvider = require('./DefaultLLMProvider');
|
|
3
3
|
const MessageParser = require('./MessageParser');
|
|
4
4
|
const logger = require('./logger');
|
|
5
|
-
const { useMongoDBAuthState } = require('./mongoAuthConfig');
|
|
6
|
-
const {
|
|
7
|
-
convertTwilioToInternalFormat,
|
|
8
|
-
downloadMediaFromTwilio,
|
|
9
|
-
getMediaTypeFromContentType,
|
|
10
|
-
extractTitle,
|
|
11
|
-
ensureWhatsAppFormat
|
|
12
|
-
} = require('./twilioHelper');
|
|
13
|
-
const {
|
|
14
|
-
delay,
|
|
15
|
-
formatCode,
|
|
16
|
-
calculateDelay
|
|
17
|
-
} = require('./whatsappHelper');
|
|
18
5
|
|
|
19
6
|
module.exports = {
|
|
20
7
|
AssistantManager,
|
|
21
8
|
DefaultLLMProvider,
|
|
22
9
|
MessageParser,
|
|
23
|
-
logger
|
|
24
|
-
useMongoDBAuthState,
|
|
25
|
-
// Twilio utilities
|
|
26
|
-
convertTwilioToInternalFormat,
|
|
27
|
-
downloadMediaFromTwilio,
|
|
28
|
-
getMediaTypeFromContentType,
|
|
29
|
-
extractTitle,
|
|
30
|
-
ensureWhatsAppFormat,
|
|
31
|
-
// WhatsApp utilities
|
|
32
|
-
delay,
|
|
33
|
-
formatCode,
|
|
34
|
-
calculateDelay
|
|
10
|
+
logger
|
|
35
11
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peopl-health/nexus",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Core messaging and assistant library for WhatsApp communication platforms",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dev": "tsc --watch",
|
|
36
36
|
"test": "jest",
|
|
37
37
|
"lint": "eslint lib/**/*.js",
|
|
38
|
-
"prepublishOnly": "npm run lint",
|
|
38
|
+
"prepublishOnly": "npm test && npm run lint",
|
|
39
39
|
"version": "npm run prepublishOnly && git add -A lib",
|
|
40
40
|
"postversion": "git push && git push --tags"
|
|
41
41
|
},
|
|
@@ -50,7 +50,9 @@
|
|
|
50
50
|
"author": "PEOPL Health Tech",
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"airtable": "^0.12.2",
|
|
53
54
|
"axios": "^1.5.0",
|
|
55
|
+
"dotenv": "^16.4.7",
|
|
54
56
|
"moment-timezone": "^0.5.43",
|
|
55
57
|
"mongoose": "^7.5.0",
|
|
56
58
|
"pino": "^8.15.0",
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const { v4: uuidv4 } = require('uuid');
|
|
3
|
-
|
|
4
|
-
function convertTwilioToInternalFormat(twilioMessage) {
|
|
5
|
-
const from = twilioMessage.From || '';
|
|
6
|
-
const to = twilioMessage.To || '';
|
|
7
|
-
const fromMe = to === from;
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
key: {
|
|
11
|
-
id: twilioMessage.MessageSid || uuidv4(),
|
|
12
|
-
fromMe: fromMe,
|
|
13
|
-
remoteJid: fromMe ? to : from
|
|
14
|
-
},
|
|
15
|
-
pushName: twilioMessage.ProfileName || '',
|
|
16
|
-
message: {
|
|
17
|
-
conversation: twilioMessage.Body || ''
|
|
18
|
-
},
|
|
19
|
-
messageTimestamp: Math.floor(Date.now() / 1000)
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function downloadMediaFromTwilio(mediaUrl, credentials) {
|
|
24
|
-
try {
|
|
25
|
-
const response = await axios({
|
|
26
|
-
method: 'GET',
|
|
27
|
-
url: mediaUrl,
|
|
28
|
-
responseType: 'arraybuffer',
|
|
29
|
-
headers: {
|
|
30
|
-
'Authorization': `Basic ${Buffer.from(
|
|
31
|
-
`${credentials.accountSid}:${credentials.authToken}`
|
|
32
|
-
).toString('base64')}`
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
return Buffer.from(response.data);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.error(`Failed to download media: ${error.message}`);
|
|
39
|
-
throw error;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getMediaTypeFromContentType(contentType) {
|
|
44
|
-
if (contentType.startsWith('image/')) return 'imageMessage';
|
|
45
|
-
if (contentType.startsWith('audio/')) return 'audioMessage';
|
|
46
|
-
if (contentType.startsWith('video/')) return 'videoMessage';
|
|
47
|
-
if (contentType.startsWith('application/')) return 'documentMessage';
|
|
48
|
-
return 'unknownMessage';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function extractTitle(message, mediaType) {
|
|
52
|
-
if (mediaType === 'documentMessage' && message.MediaUrl0) {
|
|
53
|
-
const urlParts = message.MediaUrl0.split('/');
|
|
54
|
-
return urlParts[urlParts.length - 1] || null;
|
|
55
|
-
}
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const ensureWhatsAppFormat = (phoneNumber) => {
|
|
60
|
-
if (!phoneNumber) return null;
|
|
61
|
-
|
|
62
|
-
if (phoneNumber.startsWith('whatsapp:')) {
|
|
63
|
-
return phoneNumber;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return `whatsapp:${phoneNumber}`;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
module.exports = {
|
|
70
|
-
convertTwilioToInternalFormat,
|
|
71
|
-
downloadMediaFromTwilio,
|
|
72
|
-
getMediaTypeFromContentType,
|
|
73
|
-
extractTitle,
|
|
74
|
-
ensureWhatsAppFormat
|
|
75
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
const moment = require('moment-timezone');
|
|
2
|
-
|
|
3
|
-
function delay(ms) {
|
|
4
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function formatCode(codeBase) {
|
|
8
|
-
const [number, domain] = codeBase.split('@');
|
|
9
|
-
|
|
10
|
-
if (!number || !domain) {
|
|
11
|
-
throw new Error('Invalid code format: missing number or domain');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (domain !== 's.whatsapp.net') {
|
|
15
|
-
throw new Error('Invalid domain: must be s.whatsapp.net');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let formattedNumber = number;
|
|
19
|
-
|
|
20
|
-
if (formattedNumber.endsWith('-2')) {
|
|
21
|
-
formattedNumber = formattedNumber.slice(0, -2);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (formattedNumber.length === 10) {
|
|
25
|
-
formattedNumber = '52' + formattedNumber;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (formattedNumber.startsWith('52') && formattedNumber.length === 12) {
|
|
29
|
-
formattedNumber = formattedNumber.substring(0, 2) + '1' + formattedNumber.substring(2);
|
|
30
|
-
}
|
|
31
|
-
return `${formattedNumber}@s.whatsapp.net`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function calculateDelay(sendTime, timeZone) {
|
|
35
|
-
if (sendTime !== undefined && timeZone !== undefined) {
|
|
36
|
-
const sendMoment = moment.tz(sendTime, timeZone);
|
|
37
|
-
|
|
38
|
-
if (!sendMoment.isValid()) {
|
|
39
|
-
return { error: 'Invalid time format' };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const now = moment().tz(timeZone);
|
|
43
|
-
const randomDelay = Math.floor(Math.random() * 15001) + 15000;
|
|
44
|
-
const delay = sendMoment.diff(now) + randomDelay;
|
|
45
|
-
|
|
46
|
-
if (delay <= 0) {
|
|
47
|
-
return 2500;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return delay;
|
|
51
|
-
} else {
|
|
52
|
-
return 2500;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
module.exports = {
|
|
57
|
-
delay,
|
|
58
|
-
formatCode,
|
|
59
|
-
calculateDelay
|
|
60
|
-
};
|