@peopl-health/nexus 1.6.6 → 1.7.1

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +0 -0
  2. package/LICENSE +0 -0
  3. package/MIGRATION_GUIDE.md +0 -0
  4. package/README.md +0 -0
  5. package/examples/.env.example +0 -0
  6. package/examples/assistants/BaseAssistant.js +0 -0
  7. package/examples/assistants/DoctorScheduleAssistant.js +0 -0
  8. package/examples/assistants/ExampleAssistant.js +0 -0
  9. package/examples/assistants/index.js +0 -0
  10. package/examples/basic-usage.js +0 -0
  11. package/examples/consumer-server.js +0 -0
  12. package/lib/adapters/BaileysProvider.js +0 -0
  13. package/lib/adapters/TwilioProvider.js +0 -0
  14. package/lib/adapters/index.js +0 -0
  15. package/lib/adapters/registry.js +0 -0
  16. package/lib/assistants/BaseAssistant.js +0 -0
  17. package/lib/assistants/index.js +0 -0
  18. package/lib/config/airtableConfig.js +0 -0
  19. package/lib/config/awsConfig.js +0 -0
  20. package/lib/config/configLoader.js +0 -0
  21. package/lib/config/llmConfig.js +0 -0
  22. package/lib/config/mongoAuthConfig.js +0 -0
  23. package/lib/config/runtimeConfig.js +0 -0
  24. package/lib/controllers/assistantController.js +0 -0
  25. package/lib/controllers/conversationController.js +0 -0
  26. package/lib/controllers/mediaController.js +0 -0
  27. package/lib/controllers/messageController.js +0 -0
  28. package/lib/controllers/templateController.js +0 -0
  29. package/lib/controllers/templateFlowController.js +0 -0
  30. package/lib/controllers/uploadController.js +0 -0
  31. package/lib/core/MessageProvider.js +0 -0
  32. package/lib/core/NexusMessaging.js +89 -4
  33. package/lib/core/index.js +0 -0
  34. package/lib/helpers/assistantHelper.js +2 -2
  35. package/lib/helpers/baileysHelper.js +0 -0
  36. package/lib/helpers/filesHelper.js +0 -0
  37. package/lib/helpers/llmsHelper.js +0 -0
  38. package/lib/helpers/mediaHelper.js +0 -0
  39. package/lib/helpers/mongoHelper.js +0 -0
  40. package/lib/helpers/qrHelper.js +0 -0
  41. package/lib/helpers/twilioHelper.js +0 -0
  42. package/lib/helpers/twilioMediaProcessor.js +0 -0
  43. package/lib/helpers/whatsappHelper.js +0 -0
  44. package/lib/index.d.ts +0 -0
  45. package/lib/index.js +0 -0
  46. package/lib/interactive/index.js +0 -0
  47. package/lib/interactive/registry.js +0 -0
  48. package/lib/interactive/twilioMapper.js +0 -0
  49. package/lib/models/agendaMessageModel.js +0 -0
  50. package/lib/models/index.js +0 -0
  51. package/lib/models/messageModel.js +0 -0
  52. package/lib/models/templateModel.js +0 -0
  53. package/lib/models/threadModel.js +0 -0
  54. package/lib/providers/OpenAIProvider.js +0 -0
  55. package/lib/routes/index.js +0 -0
  56. package/lib/services/airtableService.js +0 -0
  57. package/lib/services/assistantService.js +2 -2
  58. package/lib/services/conversationService.js +0 -0
  59. package/lib/services/twilioService.js +0 -0
  60. package/lib/storage/MongoStorage.js +0 -0
  61. package/lib/storage/NoopStorage.js +0 -0
  62. package/lib/storage/index.js +0 -0
  63. package/lib/storage/registry.js +0 -0
  64. package/lib/templates/predefinedTemplates.js +0 -0
  65. package/lib/templates/templateStructure.js +0 -0
  66. package/lib/utils/dateUtils.js +0 -0
  67. package/lib/utils/defaultLLMProvider.js +0 -0
  68. package/lib/utils/errorHandler.js +0 -0
  69. package/lib/utils/index.js +0 -0
  70. package/lib/utils/logger.js +0 -0
  71. package/lib/utils/mediaValidator.js +0 -0
  72. package/lib/utils/messageParser.js +0 -0
  73. package/package.json +1 -1
package/CHANGELOG.md CHANGED
File without changes
package/LICENSE CHANGED
File without changes
File without changes
package/README.md CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -36,6 +36,13 @@ class NexusMessaging {
36
36
  keyword: [],
37
37
  flow: []
38
38
  };
39
+ // Message batching
40
+ this.pendingResponses = new Map();
41
+ this.batchingConfig = {
42
+ enabled: config.messageBatching?.enabled ?? true,
43
+ baseWaitTime: config.messageBatching?.baseWaitTime ?? 15000,
44
+ randomVariation: config.messageBatching?.randomVariation ?? 5000
45
+ };
39
46
  }
40
47
 
41
48
  /**
@@ -316,7 +323,9 @@ class NexusMessaging {
316
323
  });
317
324
  }
318
325
 
319
- // Determine message type and call appropriate handler
326
+ const chatId = messageData.from || messageData.From;
327
+
328
+ // Handle immediate response messages (interactive, flows, etc.)
320
329
  if (messageData.interactive) {
321
330
  return await this.handleInteractive(messageData);
322
331
  } else if (messageData.media) {
@@ -328,7 +337,12 @@ class NexusMessaging {
328
337
  } else if (messageData.flow) {
329
338
  return await this.handleFlow(messageData);
330
339
  } else {
331
- return await this.handleMessage(messageData);
340
+ // For regular messages, use batching if enabled
341
+ if (this.batchingConfig.enabled && chatId) {
342
+ return await this._handleWithBatching(messageData, chatId);
343
+ } else {
344
+ return await this.handleMessage(messageData);
345
+ }
332
346
  }
333
347
  }
334
348
 
@@ -548,15 +562,86 @@ class NexusMessaging {
548
562
  await this.provider.disconnect();
549
563
  }
550
564
  }
565
+
566
+ /**
567
+ * Handle message with batching - waits for additional messages before processing
568
+ */
569
+ async _handleWithBatching(messageData, chatId) {
570
+ // Clear existing timeout if there is one
571
+ if (this.pendingResponses.has(chatId)) {
572
+ clearTimeout(this.pendingResponses.get(chatId));
573
+ console.log(`Received additional message from ${chatId}, resetting wait timer`);
574
+ }
575
+
576
+ // Calculate wait time with random variation
577
+ const waitTime = this.batchingConfig.baseWaitTime +
578
+ Math.floor(Math.random() * this.batchingConfig.randomVariation);
579
+
580
+ // Set new timeout
581
+ const timeoutId = setTimeout(async () => {
582
+ try {
583
+ this.pendingResponses.delete(chatId);
584
+ await this._handleBatchedMessages(chatId);
585
+ } catch (error) {
586
+ console.error(`Error handling batched messages for ${chatId}:`, error);
587
+ }
588
+ }, waitTime);
589
+
590
+ this.pendingResponses.set(chatId, timeoutId);
591
+ console.log(`Waiting ${Math.round(waitTime/1000)} seconds for more messages from ${chatId}`);
592
+ }
593
+
594
+ /**
595
+ * Process all batched messages for a chat
596
+ */
597
+ async _handleBatchedMessages(chatId) {
598
+ try {
599
+ console.log(`Processing batched messages from ${chatId}`);
600
+
601
+ const botResponse = await replyAssistant(chatId);
602
+ if (botResponse && this.provider) {
603
+ await this.sendMessage({
604
+ code: chatId,
605
+ message: botResponse
606
+ });
607
+ }
608
+
609
+ this.events.emit('messages:batched', { chatId, response: botResponse });
610
+
611
+ } catch (error) {
612
+ console.error('Error in batched message handling:', error);
613
+ }
614
+ }
615
+
616
+ /**
617
+ * Clear pending response for a chat (useful for cleanup)
618
+ */
619
+ clearPendingResponse(chatId) {
620
+ if (this.pendingResponses.has(chatId)) {
621
+ clearTimeout(this.pendingResponses.get(chatId));
622
+ this.pendingResponses.delete(chatId);
623
+ }
624
+ }
625
+
626
+ /**
627
+ * Get batching status for a chat
628
+ */
629
+ hasPendingResponse(chatId) {
630
+ return this.pendingResponses.has(chatId);
631
+ }
551
632
  }
552
633
 
553
- let defaultInstance = new NexusMessaging();
634
+ // Global instance management
635
+ let defaultInstance = null;
554
636
 
555
637
  const setDefaultInstance = (instance) => {
556
638
  if (!instance) {
557
639
  throw new Error('setDefaultInstance requires a NexusMessaging instance');
558
640
  }
559
-
641
+ if (defaultInstance) {
642
+ throw new Error('setDefaultInstance called multiple times');
643
+ }
644
+
560
645
  const isCompatible = typeof instance.sendMessage === 'function' &&
561
646
  typeof instance.sendScheduledMessage === 'function' &&
562
647
  typeof instance.processIncomingMessage === 'function';
package/lib/core/index.js CHANGED
File without changes
@@ -185,7 +185,7 @@ async function downloadMediaAndCreateFile(code, reply) {
185
185
  return fileNames;
186
186
  }
187
187
 
188
- async function processMessage(code, reply, thread) {
188
+ async function processIndividualMessage(code, reply, thread) {
189
189
  try {
190
190
  const provider = llmConfig.requireOpenAIProvider();
191
191
  const formattedMessage = formatMessage(reply);
@@ -299,5 +299,5 @@ module.exports = {
299
299
  getCurRow,
300
300
  formatMessage,
301
301
  downloadMediaAndCreateFile,
302
- processMessage
302
+ processIndividualMessage
303
303
  };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/lib/index.d.ts CHANGED
File without changes
package/lib/index.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -30,7 +30,7 @@ const { Message, formatTimestamp } = require('../models/messageModel.js');
30
30
  const { Thread } = require('../models/threadModel.js');
31
31
 
32
32
  const { checkRunStatus, getCurRow } = require('../helpers/assistantHelper.js');
33
- const { processMessage, getLastMessages } = require('../helpers/assistantHelper.js');
33
+ const { processIndividualMessage, getLastMessages } = require('../helpers/assistantHelper.js');
34
34
  const { delay } = require('../helpers/whatsappHelper.js');
35
35
 
36
36
  const configureAssistants = (config) => {
@@ -375,7 +375,7 @@ const replyAssistant = async function (code, message_ = null, thread_ = null, ru
375
375
  let patientMsg = false;
376
376
  let urls = [];
377
377
  for (const reply of patientReply) {
378
- const { isNotAssistant, url } = await processMessage(code, reply, thread);
378
+ const { isNotAssistant, url } = await processIndividualMessage(code, reply, thread);
379
379
  console.log(`isNotAssistant ${isNotAssistant} ${url}`);
380
380
  patientMsg = patientMsg || isNotAssistant;
381
381
  if (url) urls.push({ 'url': url });
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "1.6.6",
3
+ "version": "1.7.1",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",