@peopl-health/nexus 1.0.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.
- package/CHANGELOG.md +55 -0
- package/LICENSE +21 -0
- package/MIGRATION_GUIDE.md +388 -0
- package/README.md +532 -0
- package/examples/.env.example +24 -0
- package/examples/assistants/BaseAssistant.js +242 -0
- package/examples/assistants/ExampleAssistant.js +111 -0
- package/examples/assistants/index.js +7 -0
- package/examples/basic-usage.js +109 -0
- package/examples/consumer-server.js +206 -0
- package/lib/adapters/BaileysProvider.js +180 -0
- package/lib/adapters/TwilioProvider.js +118 -0
- package/lib/adapters/index.js +7 -0
- package/lib/core/MessageProvider.js +71 -0
- package/lib/core/NexusMessaging.js +231 -0
- package/lib/core/index.js +7 -0
- package/lib/index.d.ts +276 -0
- package/lib/index.js +204 -0
- package/lib/models/index.js +9 -0
- package/lib/models/messageModel.js +91 -0
- package/lib/models/threadModel.js +20 -0
- package/lib/storage/MongoStorage.js +183 -0
- package/lib/storage/index.js +5 -0
- package/lib/utils/AssistantManager.js +218 -0
- package/lib/utils/DefaultLLMProvider.js +22 -0
- package/lib/utils/MessageParser.js +249 -0
- package/lib/utils/index.js +22 -0
- package/lib/utils/logger.js +22 -0
- package/lib/utils/mongoAuthConfig.js +139 -0
- package/lib/utils/twilioHelper.js +77 -0
- package/lib/utils/whatsappHelper.js +68 -0
- package/package.json +82 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const { MessageProvider } = require('../core/MessageProvider');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Baileys WhatsApp messaging provider
|
|
5
|
+
*/
|
|
6
|
+
class BaileysProvider extends MessageProvider {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.waSocket = null;
|
|
10
|
+
this.authState = config.authState || 'auth_info_baileys';
|
|
11
|
+
this.mongoUri = config.mongoUri;
|
|
12
|
+
this.userDbMongo = config.userDbMongo;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async initialize() {
|
|
16
|
+
try {
|
|
17
|
+
const { default: makeWASocket, useMultiFileAuthState } = require('baileys');
|
|
18
|
+
const { useMongoDBAuthState } = require('../utils/mongoAuthConfig');
|
|
19
|
+
const pino = require('pino');
|
|
20
|
+
|
|
21
|
+
const logger = pino({ level: 'warn' });
|
|
22
|
+
let state, saveCreds;
|
|
23
|
+
|
|
24
|
+
if (this.mongoUri && this.userDbMongo) {
|
|
25
|
+
({ state, saveCreds } = await useMongoDBAuthState(
|
|
26
|
+
this.mongoUri,
|
|
27
|
+
this.userDbMongo,
|
|
28
|
+
'baileys'
|
|
29
|
+
));
|
|
30
|
+
} else {
|
|
31
|
+
({ state, saveCreds } = await useMultiFileAuthState(this.authState));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.waSocket = makeWASocket({
|
|
35
|
+
auth: state,
|
|
36
|
+
keepAliveIntervalMs: 30000,
|
|
37
|
+
logger,
|
|
38
|
+
printQRInTerminal: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
this.waSocket.ev.on('connection.update', async (update) => {
|
|
42
|
+
await this.handleConnectionUpdate(update);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.waSocket.ev.on('creds.update', saveCreds);
|
|
46
|
+
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`Failed to initialize Baileys: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async handleConnectionUpdate(update) {
|
|
53
|
+
const { connection, lastDisconnect, qr } = update || {};
|
|
54
|
+
|
|
55
|
+
if (qr) {
|
|
56
|
+
// Emit QR code event for consumer to handle
|
|
57
|
+
this.emit?.('qr', qr);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (connection === 'close' && lastDisconnect) {
|
|
61
|
+
const { DisconnectReason } = require('baileys');
|
|
62
|
+
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
|
|
63
|
+
if (shouldReconnect) {
|
|
64
|
+
await this.initialize();
|
|
65
|
+
}
|
|
66
|
+
} else if (connection === 'open') {
|
|
67
|
+
this.isConnected = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
formatCode(code) {
|
|
72
|
+
return code.includes('@s.whatsapp.net') ? code : `${code}@s.whatsapp.net`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
formatMessage(message) {
|
|
76
|
+
return message.replace(/\\n/g, '\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async sendMessage(messageData) {
|
|
80
|
+
if (!this.isConnected || !this.waSocket) {
|
|
81
|
+
throw new Error('Baileys provider not connected');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { to, message, fileUrl, fileType, hidePreview = false } = messageData;
|
|
85
|
+
|
|
86
|
+
if (!to) {
|
|
87
|
+
throw new Error('Recipient is required');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!message && !fileUrl) {
|
|
91
|
+
throw new Error('Message or file URL is required');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const code = this.formatCode(to);
|
|
95
|
+
let sendOptions = {};
|
|
96
|
+
let formattedMessage = this.formatMessage(message || '');
|
|
97
|
+
|
|
98
|
+
// Handle mentions
|
|
99
|
+
const regex = /@\d+/g;
|
|
100
|
+
const matches = formattedMessage.match(regex);
|
|
101
|
+
if (matches) {
|
|
102
|
+
const processedNumbers = matches.map(match => {
|
|
103
|
+
return match.replace('@', '') + '@s.whatsapp.net';
|
|
104
|
+
});
|
|
105
|
+
sendOptions.mentions = processedNumbers;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Handle link preview
|
|
109
|
+
const keywords = ['meet.google.com', 'airtable', 'zoom'];
|
|
110
|
+
if (keywords.some(keyword => formattedMessage.includes(keyword)) || hidePreview) {
|
|
111
|
+
sendOptions.linkPreview = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Set message content based on type
|
|
115
|
+
if (!fileUrl || fileType === 'text') {
|
|
116
|
+
sendOptions.text = formattedMessage;
|
|
117
|
+
} else {
|
|
118
|
+
if (fileType === 'image') {
|
|
119
|
+
sendOptions.image = { url: fileUrl };
|
|
120
|
+
sendOptions.mimetype = 'image/png';
|
|
121
|
+
sendOptions.caption = formattedMessage;
|
|
122
|
+
} else if (fileType === 'document') {
|
|
123
|
+
sendOptions.document = { url: fileUrl };
|
|
124
|
+
sendOptions.mimetype = 'application/pdf';
|
|
125
|
+
sendOptions.caption = formattedMessage;
|
|
126
|
+
} else if (fileType === 'audio') {
|
|
127
|
+
sendOptions.audio = { url: fileUrl };
|
|
128
|
+
sendOptions.mimetype = 'audio/mpeg';
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(`Unsupported file type: ${fileType}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const result = await this.waSocket.sendMessage(code, sendOptions);
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
messageId: result?.key?.id,
|
|
139
|
+
provider: 'baileys',
|
|
140
|
+
result
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
throw new Error(`Baileys send failed: ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async sendScheduledMessage(scheduledMessage) {
|
|
148
|
+
const { sendTime, timeZone } = scheduledMessage;
|
|
149
|
+
const delay = this.calculateDelay(sendTime, timeZone);
|
|
150
|
+
|
|
151
|
+
setTimeout(async () => {
|
|
152
|
+
try {
|
|
153
|
+
await this.sendMessage(scheduledMessage);
|
|
154
|
+
console.log('Scheduled message sent successfully');
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(`Scheduled message failed: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
}, delay);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
calculateDelay(sendTime) {
|
|
162
|
+
const now = new Date();
|
|
163
|
+
const targetTime = new Date(sendTime);
|
|
164
|
+
return Math.max(0, targetTime.getTime() - now.getTime());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getConnectionStatus() {
|
|
168
|
+
return this.waSocket?.user ? true : false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async disconnect() {
|
|
172
|
+
if (this.waSocket) {
|
|
173
|
+
await this.waSocket.logout();
|
|
174
|
+
this.waSocket = null;
|
|
175
|
+
}
|
|
176
|
+
this.isConnected = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = { BaileysProvider };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const { MessageProvider } = require('../core/MessageProvider');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Twilio WhatsApp messaging provider
|
|
5
|
+
*/
|
|
6
|
+
class TwilioProvider extends MessageProvider {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.twilioClient = null;
|
|
10
|
+
this.accountSid = config.accountSid;
|
|
11
|
+
this.authToken = config.authToken;
|
|
12
|
+
this.whatsappNumber = config.whatsappNumber;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async initialize() {
|
|
16
|
+
if (!this.accountSid || !this.authToken) {
|
|
17
|
+
throw new Error('Twilio credentials (accountSid, authToken) are required');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const twilio = require('twilio');
|
|
22
|
+
this.twilioClient = twilio(this.accountSid, this.authToken);
|
|
23
|
+
this.isConnected = true;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new Error(`Failed to initialize Twilio: ${error.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ensureWhatsAppFormat(number) {
|
|
30
|
+
if (!number) return null;
|
|
31
|
+
if (number.startsWith('whatsapp:')) return number;
|
|
32
|
+
if (number.startsWith('+')) return `whatsapp:${number}`;
|
|
33
|
+
return `whatsapp:+${number}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async sendMessage(messageData) {
|
|
37
|
+
if (!this.isConnected || !this.twilioClient) {
|
|
38
|
+
throw new Error('Twilio provider not initialized');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { to, message, fileUrl, fileType, variables, contentSid } = messageData;
|
|
42
|
+
|
|
43
|
+
const formattedFrom = this.ensureWhatsAppFormat(this.whatsappNumber);
|
|
44
|
+
const formattedTo = this.ensureWhatsAppFormat(to);
|
|
45
|
+
|
|
46
|
+
if (!formattedFrom || !formattedTo) {
|
|
47
|
+
throw new Error('Invalid sender or recipient number');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const messageParams = {
|
|
51
|
+
from: formattedFrom,
|
|
52
|
+
to: formattedTo
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Handle template messages
|
|
56
|
+
if (contentSid) {
|
|
57
|
+
messageParams.contentSid = contentSid;
|
|
58
|
+
if (variables && Object.keys(variables).length > 0) {
|
|
59
|
+
const formattedVariables = {};
|
|
60
|
+
Object.keys(variables).forEach(key => {
|
|
61
|
+
const numericString = key.replace(/[^0-9]/g, '');
|
|
62
|
+
const numericKey = parseInt(numericString, 10).toString();
|
|
63
|
+
formattedVariables[numericKey] = variables[key];
|
|
64
|
+
});
|
|
65
|
+
messageParams.contentVariables = JSON.stringify(formattedVariables);
|
|
66
|
+
}
|
|
67
|
+
} else if (message) {
|
|
68
|
+
messageParams.body = message;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Handle media messages
|
|
72
|
+
if (fileUrl && fileType !== 'text') {
|
|
73
|
+
messageParams.mediaUrl = [fileUrl];
|
|
74
|
+
if (!messageParams.body || messageParams.body.trim() === '') {
|
|
75
|
+
delete messageParams.body;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Validate message has content
|
|
80
|
+
if (!messageParams.body && !messageParams.mediaUrl && !messageParams.contentSid) {
|
|
81
|
+
throw new Error('Message must have body, media URL, or content SID');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const result = await this.twilioClient.messages.create(messageParams);
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
messageId: result.sid,
|
|
89
|
+
provider: 'twilio',
|
|
90
|
+
result
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`Twilio send failed: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async sendScheduledMessage(scheduledMessage) {
|
|
98
|
+
const { sendTime, timeZone } = scheduledMessage;
|
|
99
|
+
const delay = this.calculateDelay(sendTime, timeZone);
|
|
100
|
+
|
|
101
|
+
setTimeout(async () => {
|
|
102
|
+
try {
|
|
103
|
+
await this.sendMessage(scheduledMessage);
|
|
104
|
+
console.log('Scheduled message sent successfully');
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(`Scheduled message failed: ${error.message}`);
|
|
107
|
+
}
|
|
108
|
+
}, delay);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
calculateDelay(sendTime) {
|
|
112
|
+
const now = new Date();
|
|
113
|
+
const targetTime = new Date(sendTime);
|
|
114
|
+
return Math.max(0, targetTime.getTime() - now.getTime());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = { TwilioProvider };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for messaging providers
|
|
3
|
+
*/
|
|
4
|
+
class MessageProvider {
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
this.isConnected = false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initialize the provider connection
|
|
12
|
+
* @returns {Promise<void>}
|
|
13
|
+
*/
|
|
14
|
+
async initialize() {
|
|
15
|
+
throw new Error('initialize() must be implemented by provider');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Connect the provider
|
|
20
|
+
* @returns {Promise<void>}
|
|
21
|
+
*/
|
|
22
|
+
async connect() {
|
|
23
|
+
throw new Error('connect() must be implemented by provider');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Send a message
|
|
28
|
+
* @param {Object} messageData - Message data
|
|
29
|
+
* @param {string} messageData.to - Recipient
|
|
30
|
+
* @param {string} messageData.message - Message text
|
|
31
|
+
* @param {string} messageData.fileUrl - Optional file URL
|
|
32
|
+
* @param {string} messageData.fileType - File type (text, image, document, audio)
|
|
33
|
+
* @param {Object} messageData.variables - Template variables
|
|
34
|
+
* @param {string} messageData.contentSid - Template content SID
|
|
35
|
+
* @returns {Promise<Object>} Message result
|
|
36
|
+
*/
|
|
37
|
+
async sendMessage(messageData) {
|
|
38
|
+
// eslint-disable-next-line no-unused-vars
|
|
39
|
+
messageData;
|
|
40
|
+
throw new Error('sendMessage() must be implemented by provider');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Send a scheduled message
|
|
45
|
+
* @param {Object} scheduledMessage - Scheduled message data
|
|
46
|
+
* @returns {Promise<void>}
|
|
47
|
+
*/
|
|
48
|
+
async sendScheduledMessage(scheduledMessage) {
|
|
49
|
+
// eslint-disable-next-line no-unused-vars
|
|
50
|
+
scheduledMessage;
|
|
51
|
+
throw new Error('sendScheduledMessage() must be implemented by provider');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if provider is connected
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
getConnectionStatus() {
|
|
59
|
+
return this.isConnected;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Disconnect the provider
|
|
64
|
+
* @returns {Promise<void>}
|
|
65
|
+
*/
|
|
66
|
+
async disconnect() {
|
|
67
|
+
throw new Error('disconnect() must be implemented by provider');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { MessageProvider };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const { TwilioProvider } = require('../adapters/TwilioProvider');
|
|
2
|
+
const { BaileysProvider } = require('../adapters/BaileysProvider');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Core messaging class that manages providers and message handling
|
|
6
|
+
*/
|
|
7
|
+
class NexusMessaging {
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.provider = null;
|
|
11
|
+
this.messageStorage = null;
|
|
12
|
+
this.handlers = {
|
|
13
|
+
onMessage: null,
|
|
14
|
+
onInteractive: null,
|
|
15
|
+
onMedia: null,
|
|
16
|
+
onCommand: null,
|
|
17
|
+
onKeyword: null,
|
|
18
|
+
onFlow: null
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Initialize messaging with specified provider
|
|
24
|
+
* @param {string} providerType - 'twilio' or 'baileys'
|
|
25
|
+
* @param {Object} providerConfig - Provider-specific configuration
|
|
26
|
+
*/
|
|
27
|
+
async initializeProvider(providerType, providerConfig) {
|
|
28
|
+
switch (providerType.toLowerCase()) {
|
|
29
|
+
case 'twilio':
|
|
30
|
+
this.provider = new TwilioProvider(providerConfig);
|
|
31
|
+
break;
|
|
32
|
+
case 'baileys':
|
|
33
|
+
this.provider = new BaileysProvider(providerConfig);
|
|
34
|
+
break;
|
|
35
|
+
default:
|
|
36
|
+
throw new Error(`Unsupported provider: ${providerType}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await this.provider.initialize();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Set message storage interface
|
|
44
|
+
* @param {Object} storage - Storage interface with save methods
|
|
45
|
+
*/
|
|
46
|
+
setMessageStorage(storage) {
|
|
47
|
+
this.messageStorage = storage;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Register event handlers for different message types
|
|
52
|
+
* @param {Object} handlers - Handler functions
|
|
53
|
+
*/
|
|
54
|
+
setHandlers(handlers) {
|
|
55
|
+
this.handlers = { ...this.handlers, ...handlers };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Register a single message handler
|
|
60
|
+
* @param {Function} handler - Message handler function
|
|
61
|
+
*/
|
|
62
|
+
onMessage(handler) {
|
|
63
|
+
this.handlers.onMessage = handler;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register an interactive message handler
|
|
68
|
+
* @param {Function} handler - Interactive handler function
|
|
69
|
+
*/
|
|
70
|
+
onInteractive(handler) {
|
|
71
|
+
this.handlers.onInteractive = handler;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Register a media handler
|
|
76
|
+
* @param {Function} handler - Media handler function
|
|
77
|
+
*/
|
|
78
|
+
onMedia(handler) {
|
|
79
|
+
this.handlers.onMedia = handler;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Register a command handler
|
|
84
|
+
* @param {Function} handler - Command handler function
|
|
85
|
+
*/
|
|
86
|
+
onCommand(handler) {
|
|
87
|
+
this.handlers.onCommand = handler;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Register a keyword handler
|
|
92
|
+
* @param {Function} handler - Keyword handler function
|
|
93
|
+
*/
|
|
94
|
+
onKeyword(handler) {
|
|
95
|
+
this.handlers.onKeyword = handler;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Register a flow handler
|
|
100
|
+
* @param {Function} handler - Flow handler function
|
|
101
|
+
*/
|
|
102
|
+
onFlow(handler) {
|
|
103
|
+
this.handlers.onFlow = handler;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Send a message through the active provider
|
|
108
|
+
* @param {Object} messageData - Message data
|
|
109
|
+
*/
|
|
110
|
+
async sendMessage(messageData) {
|
|
111
|
+
if (!this.provider) {
|
|
112
|
+
throw new Error('No provider initialized');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const result = await this.provider.sendMessage(messageData);
|
|
116
|
+
|
|
117
|
+
// Store message if storage is configured
|
|
118
|
+
if (this.messageStorage) {
|
|
119
|
+
await this.messageStorage.saveMessage({
|
|
120
|
+
...messageData,
|
|
121
|
+
messageId: result.messageId,
|
|
122
|
+
provider: result.provider,
|
|
123
|
+
timestamp: new Date(),
|
|
124
|
+
fromMe: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Send a scheduled message
|
|
133
|
+
* @param {Object} scheduledMessage - Scheduled message data
|
|
134
|
+
*/
|
|
135
|
+
async sendScheduledMessage(scheduledMessage) {
|
|
136
|
+
if (!this.provider) {
|
|
137
|
+
throw new Error('No provider initialized');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return await this.provider.sendScheduledMessage(scheduledMessage);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Process incoming message with configured handlers
|
|
145
|
+
* @param {Object} messageData - Incoming message data
|
|
146
|
+
*/
|
|
147
|
+
async processIncomingMessage(messageData) {
|
|
148
|
+
// Store incoming message
|
|
149
|
+
if (this.messageStorage) {
|
|
150
|
+
await this.messageStorage.saveMessage({
|
|
151
|
+
...messageData,
|
|
152
|
+
timestamp: new Date(),
|
|
153
|
+
fromMe: false
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Determine message type and call appropriate handler
|
|
158
|
+
if (messageData.interactive) {
|
|
159
|
+
return await this.handleInteractive(messageData);
|
|
160
|
+
} else if (messageData.media) {
|
|
161
|
+
return await this.handleMedia(messageData);
|
|
162
|
+
} else if (messageData.command) {
|
|
163
|
+
return await this.handleCommand(messageData);
|
|
164
|
+
} else if (messageData.keyword) {
|
|
165
|
+
return await this.handleKeyword(messageData);
|
|
166
|
+
} else if (messageData.flow) {
|
|
167
|
+
return await this.handleFlow(messageData);
|
|
168
|
+
} else {
|
|
169
|
+
return await this.handleMessage(messageData);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async handleMessage(messageData) {
|
|
174
|
+
if (this.handlers.onMessage) {
|
|
175
|
+
return await this.handlers.onMessage(messageData, this);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async handleInteractive(messageData) {
|
|
180
|
+
// Store interactive message
|
|
181
|
+
if (this.messageStorage) {
|
|
182
|
+
await this.messageStorage.saveInteractive(messageData);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (this.handlers.onInteractive) {
|
|
186
|
+
return await this.handlers.onInteractive(messageData, this);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async handleMedia(messageData) {
|
|
191
|
+
if (this.handlers.onMedia) {
|
|
192
|
+
return await this.handlers.onMedia(messageData, this);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async handleCommand(messageData) {
|
|
197
|
+
if (this.handlers.onCommand) {
|
|
198
|
+
return await this.handlers.onCommand(messageData, this);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async handleKeyword(messageData) {
|
|
203
|
+
if (this.handlers.onKeyword) {
|
|
204
|
+
return await this.handlers.onKeyword(messageData, this);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async handleFlow(messageData) {
|
|
209
|
+
if (this.handlers.onFlow) {
|
|
210
|
+
return await this.handlers.onFlow(messageData, this);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get provider connection status
|
|
216
|
+
*/
|
|
217
|
+
isConnected() {
|
|
218
|
+
return this.provider ? this.provider.getConnectionStatus() : false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Disconnect the provider
|
|
223
|
+
*/
|
|
224
|
+
async disconnect() {
|
|
225
|
+
if (this.provider) {
|
|
226
|
+
await this.provider.disconnect();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = { NexusMessaging };
|