@peopl-health/nexus 1.0.3 → 1.1.0

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 CHANGED
@@ -219,6 +219,129 @@ await nexus.sendScheduledMessage({
219
219
  });
220
220
  ```
221
221
 
222
+ ## Routes for Consumer Servers
223
+
224
+ The library exports pre-built route definitions that consumer servers can use with minimal code:
225
+
226
+ ### Option 1: Setup All Default Routes (Minimal Code)
227
+
228
+ ```javascript
229
+ const express = require('express');
230
+ const { setupDefaultRoutes } = require('@peopl-health/nexus');
231
+
232
+ const app = express();
233
+
234
+ // Define your controllers
235
+ const myControllers = {
236
+ // Assistant controllers
237
+ activeAssistantController: (req, res) => { /* your logic */ },
238
+ createAssistantController: (req, res) => { /* your logic */ },
239
+ listAssistantController: (req, res) => { /* your logic */ },
240
+ addInsAssistantController: (req, res) => { /* your logic */ },
241
+ addMsgAssistantController: (req, res) => { /* your logic */ },
242
+ getInfoAssistantController: (req, res) => { /* your logic */ },
243
+ switchAssistantController: (req, res) => { /* your logic */ },
244
+ stopAssistantController: (req, res) => { /* your logic */ },
245
+
246
+ // Conversation controllers
247
+ getConversationController: (req, res) => { /* your logic */ },
248
+ searchConversationsController: (req, res) => { /* your logic */ },
249
+ getConversationsByNameController: (req, res) => { /* your logic */ },
250
+ getConversationMessagesController: (req, res) => { /* your logic */ },
251
+ getNewMessagesController: (req, res) => { /* your logic */ },
252
+ getConversationReplyController: (req, res) => { /* your logic */ },
253
+ sendTemplateToNewNumberController: (req, res) => { /* your logic */ },
254
+ markMessagesAsReadController: (req, res) => { /* your logic */ },
255
+
256
+ // Media controllers
257
+ getMediaController: (req, res) => { /* your logic */ },
258
+ handleFileUpload: (req, res) => { /* your logic */ },
259
+
260
+ // Message controllers
261
+ sendMessageController: (req, res) => { /* your logic */ },
262
+ sendBulkMessageController: (req, res) => { /* your logic */ },
263
+ sendBulkMessageAirtableController: (req, res) => { /* your logic */ },
264
+
265
+ // Template controllers
266
+ createTemplate: (req, res) => { /* your logic */ },
267
+ listTemplates: (req, res) => { /* your logic */ },
268
+ getPredefinedTemplates: (req, res) => { /* your logic */ },
269
+ getTemplate: (req, res) => { /* your logic */ },
270
+ getCompleteTemplate: (req, res) => { /* your logic */ },
271
+ createFlow: (req, res) => { /* your logic */ },
272
+ deleteFlow: (req, res) => { /* your logic */ },
273
+ submitForApproval: (req, res) => { /* your logic */ },
274
+ checkApprovalStatus: (req, res) => { /* your logic */ },
275
+ deleteTemplate: (req, res) => { /* your logic */ }
276
+ };
277
+
278
+ // Setup all default routes with one line
279
+ setupDefaultRoutes(app, myControllers);
280
+
281
+ // Add your custom routes
282
+ app.get('/api/custom', myCustomController);
283
+ app.post('/api/special', mySpecialController);
284
+ ```
285
+
286
+ ### Option 2: Individual Route Control
287
+
288
+ ```javascript
289
+ const express = require('express');
290
+ const { routes, createRouter } = require('@peopl-health/nexus');
291
+
292
+ const app = express();
293
+
294
+ // Setup only specific route groups
295
+ app.use('/api/assistant', createRouter(routes.assistantRoutes, myControllers));
296
+ app.use('/api/message', createRouter(routes.messageRoutes, myControllers));
297
+ app.use('/api/template', createRouter(routes.templateRoutes, myControllers));
298
+ ```
299
+
300
+ ### Available Default Route Groups
301
+
302
+ The `setupDefaultRoutes()` function sets up these 5 route groups:
303
+
304
+ #### **Assistant Routes** (`/api/assistant`)
305
+ - `POST /active` - Set active assistant
306
+ - `POST /add-instruction` - Add instruction to assistant
307
+ - `POST /add-msg` - Add message to assistant
308
+ - `POST /create` - Create new assistant
309
+ - `POST /get-info` - Get assistant information
310
+ - `GET /list` - List all assistants
311
+ - `POST /switch` - Switch between assistants
312
+ - `POST /stop` - Stop assistant
313
+
314
+ #### **Conversation Routes** (`/api/conversation`)
315
+ - `GET /` - Get conversations
316
+ - `GET /search` - Search conversations
317
+ - `GET /by-name` - Get conversations by name
318
+ - `GET /:phoneNumber` - Get conversation messages
319
+ - `GET /:phoneNumber/new` - Get new messages
320
+ - `POST /reply` - Send conversation reply
321
+ - `POST /send-template` - Send template to new number
322
+ - `POST /:phoneNumber/read` - Mark messages as read
323
+
324
+ #### **Media Routes** (`/api/media`)
325
+ - `GET /:key(*)` - Get media file
326
+ - `POST /upload` - Upload media file
327
+
328
+ #### **Message Routes** (`/api/message`)
329
+ - `POST /send` - Send single message
330
+ - `POST /send-bulk` - Send bulk messages
331
+ - `POST /send-bulk-airtable` - Send bulk messages from Airtable
332
+
333
+ #### **Template Routes** (`/api/template`)
334
+ - `POST /text` - Create text template
335
+ - `GET /` - List templates
336
+ - `GET /predefined` - Get predefined templates
337
+ - `GET /:id` - Get specific template
338
+ - `GET /complete/:sid` - Get complete template
339
+ - `POST /flow` - Create flow template
340
+ - `DELETE /flow/:sid` - Delete flow template
341
+ - `POST /approval` - Submit template for approval
342
+ - `GET /status/:sid` - Check approval status
343
+ - `DELETE /:id` - Delete template
344
+
222
345
  ## API Reference
223
346
 
224
347
  ### Nexus Class
@@ -5,97 +5,42 @@ async function main() {
5
5
  const nexus = new Nexus();
6
6
 
7
7
  try {
8
- // Initialize with defaults (twilio provider, mongo storage, openai llm)
8
+ // Initialize with minimal configuration
9
9
  await nexus.initialize({
10
10
  providerConfig: {
11
11
  accountSid: process.env.TWILIO_ACCOUNT_SID,
12
12
  authToken: process.env.TWILIO_AUTH_TOKEN,
13
13
  phoneNumber: process.env.TWILIO_PHONE_NUMBER
14
- },
15
- // storage: 'mongo' is default
16
- storageConfig: {
17
- uri: process.env.MONGODB_URI,
18
- dbName: 'nexus_basic'
19
- },
20
- // parser: 'MessageParser' is default
21
- parserConfig: {
22
- commandPrefixes: ['/', '!'],
23
- keywords: ['help', 'support', 'info'],
24
- flowTriggers: ['start survey', 'begin onboarding']
25
- },
26
- // llm: 'openai' is default (optional for basic usage)
27
- llmConfig: {
28
- apiKey: process.env.OPENAI_API_KEY
29
14
  }
30
15
  });
31
16
 
32
- // Set up message handlers
33
- nexus.onMessage(async (messageData) => {
34
- console.log(`Received message from ${messageData.from}: ${messageData.message}`);
35
-
36
- // Simple echo response
37
- await nexus.sendMessage({
38
- to: messageData.from,
39
- message: `You said: ${messageData.message}`
40
- });
41
- });
42
-
43
- nexus.onCommand(async (messageData) => {
44
- const { command, args } = messageData.command;
45
-
46
- switch (command) {
47
- case 'help':
48
- await nexus.sendMessage({
49
- to: messageData.from,
50
- message: 'Available commands:\n/help - Show this help\n/status - Check system status\n/echo [text] - Echo your text'
51
- });
52
- break;
53
- case 'status':
54
- await nexus.sendMessage({
55
- to: messageData.from,
56
- message: `System Status: ${nexus.isConnected() ? 'Connected' : 'Disconnected'}`
57
- });
58
- break;
59
- case 'echo':
60
- const text = args.join(' ') || 'Nothing to echo!';
61
- await nexus.sendMessage({
62
- to: messageData.from,
63
- message: `Echo: ${text}`
64
- });
65
- break;
66
- default:
67
- await nexus.sendMessage({
68
- to: messageData.from,
69
- message: `Unknown command: ${command}. Type /help for available commands.`
70
- });
71
- }
72
- });
17
+ // Set up handlers with minimal code
18
+ nexus.setHandlers({
19
+ onMessage: async (messageData) => {
20
+ await nexus.sendMessage({
21
+ to: messageData.from,
22
+ message: `Echo: ${messageData.message}`
23
+ });
24
+ },
73
25
 
74
- nexus.onKeyword(async (messageData) => {
75
- const keyword = messageData.keyword;
76
-
77
- switch (keyword) {
78
- case 'help':
79
- case 'support':
80
- await nexus.sendMessage({
81
- to: messageData.from,
82
- message: 'Hello! I can help you with:\n- Basic messaging\n- Commands (try /help)\n- Keyword responses\n\nWhat would you like to know?'
83
- });
84
- break;
85
- case 'info':
86
- await nexus.sendMessage({
87
- to: messageData.from,
88
- message: 'This is a basic Nexus messaging bot example. It demonstrates simple message handling without AI assistants.'
89
- });
90
- break;
26
+ onCommand: async (messageData) => {
27
+ const { command } = messageData.command;
28
+ const responses = {
29
+ help: 'Commands: /help, /status',
30
+ status: `Status: ${nexus.isConnected() ? 'Online' : 'Offline'}`
31
+ };
32
+
33
+ await nexus.sendMessage({
34
+ to: messageData.from,
35
+ message: responses[command] || `Unknown command: ${command}`
36
+ });
91
37
  }
92
38
  });
93
39
 
94
- console.log('Nexus basic example started successfully');
95
- console.log('Send messages to your configured phone number to test');
40
+ console.log('Nexus started - send messages to test');
96
41
 
97
42
  } catch (error) {
98
- console.error('Error starting Nexus:', error);
43
+ console.error('Error:', error);
99
44
  process.exit(1);
100
45
  }
101
46
  }
@@ -0,0 +1,168 @@
1
+ // Optional config - will be undefined if not available
2
+ let Config_ID;
3
+ try {
4
+ Config_ID = require('../config/airtableConfig')?.Config_ID;
5
+ } catch (e) {
6
+ Config_ID = null;
7
+ }
8
+
9
+ // Optional model imports
10
+ let updateThreadActive, updateThreadStop, Thread;
11
+ try {
12
+ const threadModel = require('../models/threadModel');
13
+ updateThreadActive = threadModel.updateThreadActive;
14
+ updateThreadStop = threadModel.updateThreadStop;
15
+ Thread = threadModel.Thread;
16
+ } catch (e) {
17
+ // Models not available
18
+ }
19
+
20
+ // Optional service imports
21
+ let getRecordByFilter, createAssistant, addMsgAssistant, addInsAssistant, getThreadInfo, switchAssistant, sendMessage;
22
+ try {
23
+ getRecordByFilter = require('../services/airtableService')?.getRecordByFilter;
24
+ } catch (e) {
25
+ // Service not available
26
+ }
27
+ try {
28
+ const assistantService = require('../services/assistantService');
29
+ createAssistant = assistantService.createAssistant;
30
+ addMsgAssistant = assistantService.addMsgAssistant;
31
+ addInsAssistant = assistantService.addInsAssistant;
32
+ getThreadInfo = assistantService.getThreadInfo;
33
+ switchAssistant = assistantService.switchAssistant;
34
+ } catch (e) {
35
+ // Service not available
36
+ }
37
+ try {
38
+ sendMessage = require('../services/whatsappService')?.sendMessage;
39
+ } catch (e) {
40
+ // Service not available
41
+ }
42
+
43
+
44
+ const activeAssistantController = async (req, res) => {
45
+ const { code, active } = req.body;
46
+
47
+ try {
48
+ await updateThreadActive(code, active);
49
+ return res.status(200).send({ message: 'Active assistant' });
50
+ } catch (error) {
51
+ console.log(error);
52
+ return res.status(500).send({ message: 'Failed to active assistant', error });
53
+ }
54
+ };
55
+
56
+ const addInsAssistantController = async (req, res) => {
57
+ const { code, instruction } = req.body;
58
+
59
+ try {
60
+ const ans = await addInsAssistant(code, instruction);
61
+ if (ans) await sendMessage({code, message: ans, fileType: 'text'});
62
+ return res.status(200).send({ message: 'Add instruction to the assistant' });
63
+ } catch (error) {
64
+ console.log(error);
65
+ res.status(500).send({ message: 'Failed to add instruction to assistant', error });
66
+ }
67
+ };
68
+
69
+ const addMsgAssistantController = async (req, res) => {
70
+ const { code, messages, reply } = req.body;
71
+
72
+ try {
73
+ const ans = await addMsgAssistant(code, messages, reply);
74
+ if (ans) await sendMessage({code, message: ans, fileType: 'text'});
75
+ return res.status(200).send({ message: 'Add message to the assistant' });
76
+ } catch (error) {
77
+ console.log(error);
78
+ res.status(500).send({ message: 'Failed to add message assistant', error });
79
+ }
80
+ };
81
+
82
+ const createAssistantController = async (req, res) => {
83
+ const { assistant_id, codes, messages=[], force=false } = req.body;
84
+ if (!Array.isArray(codes) || codes.length === 0) {
85
+ return res.status(400).send({ error: 'codes must be a non-empty array' });
86
+ }
87
+
88
+ try {
89
+ console.log('codes', codes);
90
+ for (const code of codes) {
91
+ const thread = await Thread.findOne({ code: code });
92
+ if (thread !== null) {
93
+ await switchAssistant(code, assistant_id);
94
+ console.log('FORCE', force);
95
+ if (!force) continue;
96
+ }
97
+
98
+ await createAssistant(code, assistant_id, messages, thread);
99
+ console.log('messages', messages);
100
+ for (const message of messages) {
101
+ console.log('message', message);
102
+ await sendMessage({code, message, fileType: 'text'});
103
+ }
104
+ }
105
+ res.status(200).send({ message: 'Create the assistant' });
106
+ } catch (error) {
107
+ console.log(error);
108
+ res.status(500).send({ message: 'Failed to create assistant', error });
109
+ }
110
+ };
111
+
112
+ const getInfoAssistantController = async (req, res) => {
113
+ const { code } = req.body;
114
+
115
+ try {
116
+ let threadInfo = await getThreadInfo(code);
117
+ return res.status(200).send({ message: 'Send assistant info' , threadInfo});
118
+ } catch (error) {
119
+ console.log(error);
120
+ res.status(500).send({ message: 'Failed to receive assistant info', error });
121
+ }
122
+ };
123
+
124
+ const listAssistantController = async (req, res) => {
125
+ try {
126
+ const assistants = await getRecordByFilter(Config_ID, 'assistants', `status="${process.env.NODE_ENV}"`);
127
+ return res.status(200).send({ message: 'List assistants' , assistants});
128
+ } catch (error) {
129
+ console.log(error);
130
+ res.status(500).send({ message: 'Failed to list assistants', error });
131
+ }
132
+ };
133
+
134
+ const switchAssistantController = async (req, res) => {
135
+ const { code, assistant_id } = req.body;
136
+
137
+ try {
138
+ await switchAssistant(code, assistant_id);
139
+ return res.status(200).send({ message: 'Switch assistant' });
140
+ } catch (error) {
141
+ console.log(error);
142
+ res.status(500).send({ message: 'Failed to switch assistant', error });
143
+ }
144
+ };
145
+
146
+ const stopAssistantController = async (req, res) => {
147
+ const { code, stop } = req.body;
148
+
149
+ try {
150
+ await updateThreadStop(code, stop);
151
+ return res.status(200).send({ message: 'Stop assistant' });
152
+ } catch (error) {
153
+ console.log(error);
154
+ return res.status(500).send({ message: 'Failed to stop assistant', error });
155
+ }
156
+ };
157
+
158
+
159
+ module.exports = {
160
+ activeAssistantController,
161
+ addInsAssistantController,
162
+ addMsgAssistantController,
163
+ createAssistantController,
164
+ getInfoAssistantController,
165
+ listAssistantController,
166
+ switchAssistantController,
167
+ stopAssistantController
168
+ };