@peopl-health/nexus 1.2.0 → 1.3.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
@@ -1,611 +1,132 @@
1
- # nexus
1
+ # @peopl-health/nexus
2
2
 
3
- Core messaging and assistant library for WhatsApp communication platforms.
3
+ A concise, configurable messaging and assistant toolkit for WhatsApp. It supports Twilio (production‑ready) and Baileys (limited), optional Mongo storage, OpenAI assistants, templates/flows via Twilio Content API, and an event/middleware model so apps can customize behavior.
4
4
 
5
- ## Features
6
-
7
- - **Multi-Provider Support**: Seamlessly switch between Twilio and Baileys
8
- - **Configurable Architecture**: Adapter pattern for messaging providers
9
- - **AI Assistant Integration**: Built-in support for OpenAI and custom LLM providers
10
- - **MongoDB Storage**: Persistent message and thread storage
11
- - **Template Messaging**: Support for WhatsApp Business templates
12
- - **Scheduled Messages**: Queue and send messages at specific times
13
- - **Flow Management**: Create conversational flows and approval processes
14
-
15
- ## Installation
5
+ ## Install
16
6
 
17
7
  ```bash
18
8
  npm install @peopl-health/nexus
9
+ # Providers / AI
10
+ npm install twilio baileys openai
19
11
  ```
20
12
 
21
- ### Peer Dependencies
22
-
23
- Install the providers you need:
24
-
25
- ```bash
26
- # For Twilio WhatsApp
27
- npm install twilio
28
-
29
- # For Baileys (direct WhatsApp connection)
30
- npm install baileys
13
+ ## Quick Start (Twilio)
31
14
 
32
- # For AI features
33
- npm install openai
34
- ```
35
-
36
- ## Quick Start
37
-
38
- ### Basic Messaging (Twilio)
39
-
40
- ```javascript
41
- const { Nexus } = require('nexus-messaging');
15
+ ```js
16
+ const express = require('express');
17
+ const { Nexus, setupDefaultRoutes } = require('@peopl-health/nexus');
42
18
 
43
19
  const nexus = new Nexus();
44
-
45
20
  await nexus.initialize({
46
21
  provider: 'twilio',
47
22
  providerConfig: {
48
- accountSid: process.env.TWILIO_SID,
49
- authToken: process.env.TWILIO_TOKEN,
50
- phoneNumber: 'whatsapp:+1234567890'
51
- }
52
- });
53
-
54
- // Send a message
55
- await nexus.sendMessage({
56
- to: '+1234567890',
57
- message: 'Hello from Nexus!'
58
- });
59
- ```
60
-
61
- ### With AI Assistant
62
-
63
- ```javascript
64
- const { Nexus, BaseAssistant } = require('@peopl/nexus');
65
-
66
- const nexus = new Nexus();
67
-
68
- await nexus.initialize({
69
- provider: 'twilio',
70
- providerConfig: { /* twilio config */ },
71
- llm: 'openai',
72
- llmConfig: {
73
- apiKey: process.env.OPENAI_API_KEY
74
- }
23
+ accountSid: process.env.TWILIO_ACCOUNT_SID,
24
+ authToken: process.env.TWILIO_AUTH_TOKEN,
25
+ phoneNumber: process.env.TWILIO_PHONE_NUMBER
26
+ },
27
+ storage: 'mongo', // or 'noop', or your custom adapter/instance
28
+ storageConfig: { mongoUri: process.env.MONGODB_URI }
75
29
  });
76
30
 
77
- // Set up message handler with AI
78
- nexus.setHandlers({
79
- message: async (messageData) => {
80
- const assistant = new BaseAssistant();
81
- return await assistant.processMessage(messageData);
82
- }
83
- });
31
+ // Built‑in routes (assistant, conversation, media, message, template)
32
+ const app = express();
33
+ app.use(express.json());
34
+ setupDefaultRoutes(app);
84
35
 
85
- // Process incoming webhook
36
+ // Incoming webhooks
86
37
  app.post('/webhook', async (req, res) => {
87
38
  await nexus.processMessage(req.body);
88
39
  res.sendStatus(200);
89
40
  });
90
41
  ```
91
42
 
92
- ## Integration Patterns
93
-
94
- ### Option 1: NPM Package (Recommended)
95
-
96
- ```bash
97
- npm install nexus-messaging twilio openai
98
- ```
99
-
100
- ### Option 2: Git Dependency
101
-
102
- ```json
103
- {
104
- "dependencies": {
105
- "@peopl/nexus": "git+https://github.com/peopl-health/nexus.git"
106
- }
107
- }
108
- ```
109
-
110
- ### Option 3: Local Development
111
-
112
- ```bash
113
- # Clone and link locally
114
- git clone https://github.com/peopl-health/nexus.git
115
- cd nexus
116
- npm link
117
-
118
- # In your project
119
- npm link @peopl/nexus
120
- ```
121
-
122
- ## Configuration Options
43
+ ## Templates & Approvals (Twilio)
123
44
 
124
- ### Providers
45
+ Nexus auto‑injects the active Twilio provider into the template controllers during `initialize`. Default routes under `/api/template` work immediately:
46
+ - `GET /api/template` list templates
47
+ - `GET /api/template/:id` get one
48
+ - `POST /api/template/text` create text template
49
+ - `POST /api/template/approval` submit for approval
50
+ - `GET /api/template/status/:sid` check status
51
+ - `DELETE /api/template/:id` delete
125
52
 
126
- #### Twilio (Production Ready)
127
- ```javascript
128
- {
129
- provider: 'twilio',
130
- providerConfig: {
131
- accountSid: 'your-twilio-sid',
132
- authToken: 'your-twilio-token',
133
- phoneNumber: 'whatsapp:+1234567890'
134
- }
135
- }
136
- ```
137
-
138
- #### Baileys (Direct WhatsApp)
139
- ```javascript
140
- {
141
- provider: 'baileys',
142
- providerConfig: {
143
- printQRInTerminal: true,
144
- // Additional Baileys options
145
- }
146
- }
147
- ```
148
-
149
- ### Storage Options
150
-
151
- ```javascript
152
- {
153
- storage: 'mongo', // Default
154
- storageConfig: {
155
- uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/nexus'
156
- }
157
- }
158
-
159
- // Or disable storage
160
- { storage: false }
161
- ```
162
-
163
- ### LLM Integration
164
-
165
- ```javascript
166
- {
167
- llm: 'openai',
168
- llmConfig: {
169
- apiKey: process.env.OPENAI_API_KEY,
170
- model: 'gpt-4',
171
- temperature: 0.7
172
- }
173
- }
174
- ```
175
-
176
- ## Advanced Usage
177
-
178
- ### Custom Assistant
179
-
180
- ```javascript
181
- const { BaseAssistant } = require('@peopl/nexus/utils');
182
-
183
- class CustomerServiceAssistant extends BaseAssistant {
184
- async processMessage(messageData) {
185
- // Custom business logic
186
- if (messageData.message.includes('order')) {
187
- return await this.handleOrderInquiry(messageData);
188
- }
189
-
190
- return await super.processMessage(messageData);
191
- }
192
-
193
- async handleOrderInquiry(messageData) {
194
- // Custom order handling logic
195
- }
196
- }
197
- ```
198
-
199
- ### Template Messages
200
-
201
- ```javascript
202
- await nexus.sendMessage({
203
- to: '+1234567890',
204
- contentSid: 'HX1234567890abcdef', // Twilio template SID
205
- variables: {
206
- '1': 'John',
207
- '2': 'Tomorrow at 3 PM'
208
- }
53
+ Programmatic use:
54
+ ```js
55
+ const provider = nexus.getMessaging().getProvider();
56
+ const content = await provider.createTemplate({
57
+ friendly_name: 'hello_' + Date.now(),
58
+ language: 'es',
59
+ variables: { '1': 'Nombre' },
60
+ types: { 'twilio/text': { body: 'Hola {{1}}' } }
209
61
  });
62
+ await provider.submitForApproval(content.sid, 'hello', 'UTILITY');
63
+ const status = await provider.checkApprovalStatus(content.sid);
210
64
  ```
211
65
 
212
- ### Scheduled Messages
213
-
214
- ```javascript
215
- await nexus.sendScheduledMessage({
216
- to: '+1234567890',
217
- message: 'Reminder: Your appointment is tomorrow',
218
- sendAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours from now
219
- });
220
- ```
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)
66
+ ## Interactive & Flows
227
67
 
228
- ```javascript
229
- const express = require('express');
230
- const { setupDefaultRoutes } = require('@peopl-health/nexus');
231
-
232
- const app = express();
233
-
234
- // Setup all default routes with built-in controllers (one line!)
235
- setupDefaultRoutes(app);
236
-
237
- // Add your custom routes
238
- app.get('/api/custom', myCustomController);
239
- app.post('/api/special', mySpecialController);
240
- ```
241
-
242
- ### Option 2: Individual Route Control
243
-
244
- ```javascript
245
- const express = require('express');
246
- const { routes, createRouter } = require('@peopl-health/nexus');
68
+ Provider‑agnostic APIs with Twilio mapping (Baileys: not supported):
69
+ ```js
70
+ const { registerFlow, sendInteractive, registerInteractiveHandler, attachInteractiveRouter } = require('@peopl-health/nexus');
247
71
 
248
- const app = express();
249
-
250
- // Setup only specific route groups
251
- app.use('/api/assistant', createRouter(routes.assistantRoutes, myControllers));
252
- app.use('/api/message', createRouter(routes.messageRoutes, myControllers));
253
- app.use('/api/template', createRouter(routes.templateRoutes, myControllers));
254
- ```
255
-
256
- ### Available Default Route Groups
257
-
258
- The `setupDefaultRoutes()` function sets up these 5 route groups:
259
-
260
- #### **Assistant Routes** (`/api/assistant`)
261
- - `POST /active` - Set active assistant
262
- - `POST /add-instruction` - Add instruction to assistant
263
- - `POST /add-msg` - Add message to assistant
264
- - `POST /create` - Create new assistant
265
- - `POST /get-info` - Get assistant information
266
- - `GET /list` - List all assistants
267
- - `POST /switch` - Switch between assistants
268
- - `POST /stop` - Stop assistant
269
-
270
- #### **Conversation Routes** (`/api/conversation`)
271
- - `GET /` - Get conversations
272
- - `GET /search` - Search conversations
273
- - `GET /by-name` - Get conversations by name
274
- - `GET /:phoneNumber` - Get conversation messages
275
- - `GET /:phoneNumber/new` - Get new messages
276
- - `POST /reply` - Send conversation reply
277
- - `POST /send-template` - Send template to new number
278
- - `POST /:phoneNumber/read` - Mark messages as read
279
-
280
- #### **Media Routes** (`/api/media`)
281
- - `GET /:key(*)` - Get media file
282
- - `POST /upload` - Upload media file
283
-
284
- #### **Message Routes** (`/api/message`)
285
- - `POST /send` - Send single message
286
- - `POST /send-bulk` - Send bulk messages
287
- - `POST /send-bulk-airtable` - Send bulk messages from Airtable
288
-
289
- #### **Template Routes** (`/api/template`)
290
- - `POST /text` - Create text template
291
- - `GET /` - List templates
292
- - `GET /predefined` - Get predefined templates
293
- - `GET /:id` - Get specific template
294
- - `GET /complete/:sid` - Get complete template
295
- - `POST /flow` - Create flow template
296
- - `DELETE /flow/:sid` - Delete flow template
297
- - `POST /approval` - Submit template for approval
298
- - `GET /status/:sid` - Check approval status
299
- - `DELETE /:id` - Delete template
300
-
301
- ## API Reference
302
-
303
- ### Nexus Class
304
-
305
- #### `initialize(options)` → `Promise<void>`
306
- Initialize with providers and configuration.
307
-
308
- **Parameters:**
309
- - `provider` (string): 'twilio' or 'baileys'
310
- - `providerConfig` (object): Provider-specific configuration
311
- - `storage` (string|false): 'mongo' or false to disable
312
- - `storageConfig` (object): Storage configuration
313
- - `llm` (string): 'openai' for AI features
314
- - `llmConfig` (object): LLM configuration
315
-
316
- #### `sendMessage(messageData)` → `Promise<Object>`
317
- Send a message through the configured provider.
318
-
319
- **Parameters:**
320
- - `to` (string): Recipient phone number
321
- - `message` (string): Message text
322
- - `fileUrl` (string, optional): File URL for media
323
- - `contentSid` (string, optional): Template content SID
324
- - `variables` (object, optional): Template variables
325
-
326
- #### `processMessage(rawMessage)` → `Promise<void>`
327
- Process incoming webhook/event message.
328
-
329
- #### `setHandlers(handlers)` → `void`
330
- Set message and status handlers.
331
-
332
- #### `isConnected()` → `boolean`
333
- Check provider connection status.
334
-
335
- #### `disconnect()` → `Promise<void>`
336
- Clean shutdown of all services.
337
-
338
- ## Examples
339
-
340
- Complete examples in `/examples`:
341
-
342
- - **`basic-usage.js`** - Pure messaging without AI
343
- - **`consumer-server.js`** - Full Express server with AI assistant
344
- - **`assistants/ExampleAssistant.js`** - Custom assistant implementation
345
-
346
- ## Publishing to NPM
347
-
348
- ```bash
349
- # Login to npm
350
- npm login
351
-
352
- # Publish (first time)
353
- npm publish
354
-
355
- # Update version and publish
356
- npm version patch # or minor/major
357
- npm publish
358
- ```
359
-
360
- ## Environment Variables
361
-
362
- ```bash
363
- # Twilio
364
- TWILIO_SID=your_account_sid
365
- TWILIO_TOKEN=your_auth_token
366
- TWILIO_PHONE=whatsapp:+1234567890
367
-
368
- # MongoDB
369
- MONGODB_URI=mongodb://localhost:27017/nexus
370
-
371
- # OpenAI
372
- OPENAI_API_KEY=your_openai_key
373
- ```
374
-
375
- ## Customization
376
-
377
- Define custom handlers for different message types:
378
-
379
- ```javascript
380
- nexus.setHandlers({
381
- onMessage: async (messageData, nexus) => {
382
- // Handle regular text messages
383
- },
384
-
385
- onInteractive: async (messageData, nexus) => {
386
- // Handle button clicks, list selections, flow responses
387
- const { type, payload } = messageData.interactive;
388
-
389
- if (type === 'button') {
390
- console.log('Button clicked:', payload);
391
- }
392
- },
393
-
394
- onMedia: async (messageData, nexus) => {
395
- // Handle image, document, audio messages
396
- const { url, contentType } = messageData.media;
397
- },
398
-
399
- onCommand: async (messageData, nexus) => {
400
- // Handle commands like /help, /start
401
- const { command, args } = messageData.command;
402
- },
403
-
404
- onKeyword: async (messageData, nexus) => {
405
- // Handle keyword triggers
406
- console.log('Keyword matched:', messageData.keyword);
407
- },
408
-
409
- onFlow: async (messageData, nexus) => {
410
- // Handle flow triggers
411
- console.log('Flow triggered:', messageData.flow);
412
- }
72
+ registerFlow('greeting_qr', {
73
+ type: 'quick-reply', language: 'es', body: 'Hola {{1}}', variables: { '1': 'Nombre' },
74
+ buttons: [{ text: 'Sí' }, { text: 'No' }]
413
75
  });
414
- ```
76
+ await sendInteractive(nexus, { to: '+521555...', id: 'greeting_qr' });
415
77
 
416
- ### Message Parser Configuration
417
-
418
- Customize how messages are parsed:
419
-
420
- ```javascript
421
- await nexus.initialize({
422
- // ... other config
423
- parserConfig: {
424
- commandPrefixes: ['/', '!', '#'],
425
- keywords: [
426
- 'help',
427
- 'support',
428
- { pattern: 'urgent.*issue', flags: 'i' }
429
- ],
430
- flowTriggers: [
431
- 'start onboarding',
432
- 'begin survey'
433
- ]
434
- }
78
+ registerInteractiveHandler({ type: 'button', id: /sí|si/i }, async (msg, messaging) => {
79
+ await messaging.sendMessage({ to: msg.from, message: '¡Confirmado!' });
435
80
  });
81
+ attachInteractiveRouter(nexus);
436
82
  ```
437
83
 
438
- ### AI Assistant Integration
439
-
440
- ```javascript
441
- const { OpenAI } = require('openai');
442
-
443
- const openai = new OpenAI({
444
- apiKey: process.env.OPENAI_API_KEY
445
- });
446
-
447
- await nexus.initialize({
448
- // ... other config
449
- assistant: {
450
- llmClient: openai,
451
- assistants: {
452
- 'SUPPORT_ASSISTANT': 'asst_abc123',
453
- 'SALES_ASSISTANT': 'asst_def456'
454
- },
455
- handlers: {
456
- onRequiresAction: async (result, threadData) => {
457
- // Handle function calls
458
- const toolCalls = result.run.required_action.submit_tool_outputs.tool_calls;
459
- const toolOutputs = [];
460
-
461
- for (const toolCall of toolCalls) {
462
- if (toolCall.function.name === 'get_weather') {
463
- const output = await getWeather(JSON.parse(toolCall.function.arguments));
464
- toolOutputs.push({
465
- tool_call_id: toolCall.id,
466
- output: JSON.stringify(output)
467
- });
468
- }
469
- }
470
-
471
- return await nexus.getAssistantManager().submitToolOutputs(
472
- threadData.threadId,
473
- result.run.id,
474
- toolOutputs
475
- );
476
- }
477
- }
478
- }
479
- });
84
+ ## Storage (Adapters)
480
85
 
481
- // Create assistant thread
482
- await nexus.createAssistantThread('user123', 'SUPPORT_ASSISTANT');
483
-
484
- // Send message to assistant
485
- const response = await nexus.sendToAssistant('user123', 'I need help with my account');
86
+ - Built‑in: `mongo` (default), `noop`.
87
+ - Register your adapter or pass an instance directly.
88
+ ```js
89
+ const { registerStorage } = require('@peopl-health/nexus/lib/storage/registry');
90
+ registerStorage('src', MyStorageClass);
91
+ await nexus.initialize({ storage: 'src', storageConfig: { /* ... */ } });
92
+ // OR
93
+ await nexus.initialize({ storage: new MyStorageClass(/* ... */) });
486
94
  ```
487
95
 
488
- ## API Reference
489
-
490
- ### Nexus Class
491
-
492
- #### `initialize(options)`
493
- Initialize Nexus with providers and configuration.
96
+ ## Middleware & Events
494
97
 
495
- #### `sendMessage(messageData)`
496
- Send a message through the active provider.
98
+ Add middleware per type or global; subscribe to events.
99
+ ```js
100
+ const bus = nexus.getMessaging().getEventBus();
101
+ bus.on('message:received', (m) => console.log('rx', m.id));
497
102
 
498
- ```javascript
499
- await nexus.sendMessage({
500
- to: '+1234567890',
501
- message: 'Hello World!',
502
- fileUrl: 'https://example.com/image.jpg', // optional
503
- fileType: 'image', // optional
504
- contentSid: 'template_sid', // optional for templates
505
- variables: { '1': 'John', '2': 'Doe' } // optional for templates
103
+ nexus.getMessaging().use('message', async (msg, nx, next) => {
104
+ // sanitize/annotate
105
+ return next();
506
106
  });
507
107
  ```
508
108
 
509
- #### `sendScheduledMessage(scheduledMessage)`
510
- Schedule a message for future delivery.
511
-
512
- ```javascript
513
- await nexus.sendScheduledMessage({
514
- to: '+1234567890',
515
- message: 'Reminder: Your appointment is tomorrow',
516
- sendTime: '2024-01-15T10:00:00Z',
517
- timeZone: 'America/Mexico_City'
518
- });
519
- ```
520
-
521
- #### `processMessage(rawMessage)`
522
- Process incoming message from webhook.
523
-
524
- ### Individual Components
525
-
526
- You can also use individual components for more control:
527
-
528
- ```javascript
529
- const { TwilioProvider, MongoStorage, MessageParser } = require('@peopl/nexus');
530
-
531
- // Use components individually
532
- const provider = new TwilioProvider(config);
533
- await provider.initialize();
534
-
535
- const storage = new MongoStorage(config);
536
- await storage.connect();
537
-
538
- const parser = new MessageParser(config);
539
- const parsedMessage = parser.parseMessage(rawMessage);
540
- ```
541
-
542
- ## Examples
543
-
544
- ### Basic Echo Bot
545
- ```javascript
546
- const { Nexus } = require('nexus-messaging');
547
-
548
- const nexus = new Nexus();
109
+ ## Assistants (Optional)
549
110
 
111
+ Register assistant classes and (optionally) a custom resolver. OpenAI is supported via `llm: 'openai'`.
112
+ ```js
550
113
  await nexus.initialize({
551
114
  provider: 'twilio',
552
- providerConfig: { /* twilio config */ }
553
- });
554
-
555
- nexus.setHandlers({
556
- onMessage: async (messageData, nexus) => {
557
- await nexus.sendMessage({
558
- to: messageData.from,
559
- message: `Echo: ${messageData.message}`
560
- });
115
+ llm: 'openai', llmConfig: { apiKey: process.env.OPENAI_API_KEY },
116
+ assistants: {
117
+ registry: { SUPPORT: SupportAssistantClass, SALES: SalesAssistantClass },
118
+ getAssistantById: (id, thread) => null // optional override
561
119
  }
562
120
  });
563
121
  ```
564
122
 
565
- ### Command Handler
566
- ```javascript
567
- nexus.setHandlers({
568
- onCommand: async (messageData, nexus) => {
569
- const { command, args } = messageData.command;
570
-
571
- switch (command) {
572
- case 'help':
573
- await nexus.sendMessage({
574
- to: messageData.from,
575
- message: 'Available commands: /help, /status, /info'
576
- });
577
- break;
578
-
579
- case 'status':
580
- await nexus.sendMessage({
581
- to: messageData.from,
582
- message: `System status: ${nexus.isConnected() ? 'Online' : 'Offline'}`
583
- });
584
- break;
585
- }
586
- }
587
- });
588
- ```
123
+ ## Routes (Importable)
589
124
 
590
- ### Interactive Messages
591
- ```javascript
592
- nexus.setHandlers({
593
- onInteractive: async (messageData, nexus) => {
594
- const { type, payload } = messageData.interactive;
595
-
596
- if (type === 'button' && payload === 'get_support') {
597
- await nexus.sendMessage({
598
- to: messageData.from,
599
- message: 'Connecting you to support...'
600
- });
601
-
602
- // Create assistant thread for support
603
- await nexus.createAssistantThread(messageData.from, 'SUPPORT_ASSISTANT');
604
- }
605
- }
606
- });
607
- ```
125
+ Use `setupDefaultRoutes(app)` to mount everything, or pick from `routes` + `createRouter()` to mount subsets.
126
+
127
+ ## Examples
608
128
 
609
- ## License
129
+ See:
130
+ - examples/basic-usage.js
131
+ - examples/assistants/
610
132
 
611
- MIT