@peopl-health/nexus 1.7.7 → 1.7.9

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.
@@ -218,6 +218,11 @@ class TwilioProvider extends MessageProvider {
218
218
  // Convert Mongoose document to plain object if needed
219
219
  const payload = scheduledMessage.toObject ? scheduledMessage.toObject() : { ...scheduledMessage };
220
220
  delete payload.__nexusSend;
221
+
222
+ // Map message field to body for consistency (scheduled messages use 'message' field)
223
+ if (payload.message && !payload.body) {
224
+ payload.body = payload.message;
225
+ }
221
226
  console.log('[TwilioProvider] Timer fired', {
222
227
  code: payload.code,
223
228
  hasMessage: Boolean(payload.message || payload.body),
@@ -100,7 +100,7 @@ async function useMongoDBAuthState(uri, dbName, sessionId) {
100
100
  }
101
101
  };
102
102
 
103
- const creds = (await readData('creds')) || (0, initAuthCreds)();
103
+ const creds = (await readData('creds')) || await initAuthCreds();
104
104
 
105
105
  return {
106
106
  state: {
@@ -110,8 +110,8 @@ async function useMongoDBAuthState(uri, dbName, sessionId) {
110
110
  const data = {};
111
111
  await Promise.all(ids.map(async (id) => {
112
112
  let value = await readData(`${type}-${id}`);
113
- if (type === 'app-state-sync-key') {
114
- value = proto.Message.AppStateSyncKeyData.fromObject(data);
113
+ if (type === 'app-state-sync-key' && value) {
114
+ value = proto.Message.AppStateSyncKeyData.fromObject(value);
115
115
  }
116
116
  data[id] = value;
117
117
  }));
@@ -25,7 +25,7 @@ const addInsAssistantController = async (req, res) => {
25
25
 
26
26
  try {
27
27
  const ans = await addInsAssistant(code, instruction);
28
- if (ans) await sendMessage({code, message: ans, fileType: 'text'});
28
+ if (ans) await sendMessage({code, body: ans, fileType: 'text'});
29
29
  return res.status(200).send({ message: 'Add instruction to the assistant' });
30
30
  } catch (error) {
31
31
  console.log(error);
@@ -38,7 +38,7 @@ const addMsgAssistantController = async (req, res) => {
38
38
 
39
39
  try {
40
40
  const ans = await addMsgAssistant(code, messages, reply);
41
- if (ans) await sendMessage({code, message: ans, fileType: 'text'});
41
+ if (ans) await sendMessage({code, body: ans, fileType: 'text'});
42
42
  return res.status(200).send({ message: 'Add message to the assistant' });
43
43
  } catch (error) {
44
44
  console.log(error);
@@ -66,7 +66,7 @@ const createAssistantController = async (req, res) => {
66
66
  console.log('messages', messages);
67
67
  for (const message of messages) {
68
68
  console.log('message', message);
69
- await sendMessage({code, message, fileType: 'text'});
69
+ await sendMessage({code, body: message, fileType: 'text'});
70
70
  }
71
71
  }
72
72
  res.status(200).send({ message: 'Create the assistant' });
@@ -120,7 +120,10 @@ const sendMessageController = async (req, res) => {
120
120
  const scheduledRecord = await persistScheduledMessage(ScheduledMessageModel, payload);
121
121
  const result = hasScheduler
122
122
  ? await dependencies.sendScheduledMessage(scheduledRecord)
123
- : await dependencies.sendMessage(payload);
123
+ : await dependencies.sendMessage({
124
+ ...payload,
125
+ body: payload.message
126
+ });
124
127
 
125
128
  res.status(200).json({
126
129
  status: 200,
@@ -185,7 +188,10 @@ const sendBulkMessageController = async (req, res) => {
185
188
  const savedMessage = await persistScheduledMessage(ScheduledMessageModel, payload);
186
189
  const result = hasScheduler
187
190
  ? await dependencies.sendScheduledMessage(savedMessage)
188
- : await dependencies.sendMessage(payload);
191
+ : await dependencies.sendMessage({
192
+ ...payload,
193
+ body: payload.message
194
+ });
189
195
  return { result, scheduled: savedMessage };
190
196
  })
191
197
  );
@@ -289,7 +295,10 @@ const sendBulkMessageAirtableController = async (req, res) => {
289
295
  const savedMessage = await persistScheduledMessage(ScheduledMessageModel, payload);
290
296
  const result = hasScheduler
291
297
  ? await dependencies.sendScheduledMessage(savedMessage)
292
- : await dependencies.sendMessage(payload);
298
+ : await dependencies.sendMessage({
299
+ ...payload,
300
+ body: payload.message
301
+ });
293
302
  return { result, scheduled: savedMessage };
294
303
  })
295
304
  );
@@ -2,6 +2,7 @@ const { airtable, getBase } = require('../config/airtableConfig');
2
2
  const { replyAssistant } = require('../services/assistantService');
3
3
  const { createProvider } = require('../adapters/registry');
4
4
  const runtimeConfig = require('../config/runtimeConfig');
5
+ const { hasPreprocessingHandler, invokePreprocessingHandler } = require('../services/preprocessingHooks');
5
6
 
6
7
  const mongoose = require('mongoose');
7
8
  const OpenAI = require('openai');
@@ -324,7 +325,16 @@ class NexusMessaging {
324
325
  }
325
326
 
326
327
  const chatId = messageData.from || messageData.From;
327
-
328
+
329
+ if (chatId && hasPreprocessingHandler()) {
330
+ const stop = await invokePreprocessingHandler({
331
+ code: chatId,
332
+ context: 'incomingMessage',
333
+ message: messageData
334
+ });
335
+ if (stop) return;
336
+ }
337
+
328
338
  // Handle immediate response messages (interactive, flows, etc.)
329
339
  if (messageData.interactive) {
330
340
  return await this.handleInteractive(messageData);
@@ -624,11 +634,10 @@ class NexusMessaging {
624
634
 
625
635
  // Get assistant response
626
636
  const botResponse = await replyAssistant(chatId);
627
- if (botResponse && this.provider) {
628
- await this.provider.sendMessage({
629
- to: chatId,
630
- body: botResponse,
631
- type: 'text',
637
+ if (botResponse) {
638
+ await this.sendMessage({
639
+ code: chatId,
640
+ message: botResponse,
632
641
  processed: true
633
642
  });
634
643
  }
@@ -31,12 +31,18 @@ async function checkRunStatus(assistant, thread_id, run_id, retryCount = 0, maxR
31
31
  return true;
32
32
  } else if (run.status === 'requires_action') {
33
33
  console.log('requires_action');
34
- if (retryCount < maxRetries) await assistant.handleRequiresAction(run);
34
+ if (retryCount >= maxRetries) {
35
+ return false;
36
+ }
37
+ await assistant.handleRequiresAction(run);
35
38
  await new Promise(resolve => setTimeout(resolve, 5000));
36
- return checkRunStatus(assistant, thread_id, run_id, maxRetries, maxRetries);
39
+ return checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries);
37
40
  } else if (run.status !== 'completed') {
41
+ if (retryCount >= maxRetries) {
42
+ return false;
43
+ }
38
44
  await new Promise(resolve => setTimeout(resolve, 1000));
39
- return checkRunStatus(assistant, thread_id, run_id);
45
+ return checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries);
40
46
  } else {
41
47
  console.log('Run completed.');
42
48
  return true;
@@ -6,7 +6,12 @@ const { downloadMediaMessage } = require('baileys');
6
6
  async function processMessage(message, messageType) {
7
7
  try {
8
8
  const { content, reply } = extractMessageContent(message, messageType);
9
- if (content != null && content < 1) return;
9
+ if (typeof content === 'string' && content.trim().length === 0) {
10
+ return;
11
+ }
12
+ if (content == null) {
13
+ return;
14
+ }
10
15
 
11
16
  const values = getMessageValues(message, content, reply, false);
12
17
  await insertMessage(values);
package/lib/index.d.ts CHANGED
@@ -130,6 +130,13 @@ declare module '@peopl-health/nexus' {
130
130
  setup?: (this: BaseAssistant, context: { assistantId: string; thread?: any; options?: any }) => void;
131
131
  }
132
132
 
133
+ export interface IncomingPreprocessingPayload {
134
+ code: string;
135
+ context?: string;
136
+ message: MessageData;
137
+ [key: string]: any;
138
+ }
139
+
133
140
  export class BaseAssistant {
134
141
  constructor(options?: {
135
142
  assistantId?: string;
@@ -176,6 +183,13 @@ declare module '@peopl-health/nexus' {
176
183
  export function configureAssistantsLLM(client: any): void;
177
184
  export function overrideGetAssistantById(resolver: (assistantId: string, thread?: any) => any): void;
178
185
  export function configureAssistants(config: any): void;
186
+ export function setPreprocessingHandler(
187
+ handler: (payload: IncomingPreprocessingPayload) => any | Promise<any>
188
+ ): void;
189
+ export function hasPreprocessingHandler(): boolean;
190
+ export function invokePreprocessingHandler(
191
+ payload: IncomingPreprocessingPayload
192
+ ): Promise<boolean>;
179
193
 
180
194
  export class TwilioProvider extends MessageProvider {
181
195
  constructor(config: TwilioConfig);
package/lib/index.js CHANGED
@@ -17,6 +17,11 @@ const {
17
17
  const { TwilioProvider } = require('./adapters/TwilioProvider');
18
18
  const { BaileysProvider } = require('./adapters/BaileysProvider');
19
19
  const { BaseAssistant: CoreBaseAssistant } = require('./assistants/BaseAssistant');
20
+ const {
21
+ setPreprocessingHandler,
22
+ hasPreprocessingHandler,
23
+ invokePreprocessingHandler
24
+ } = require('./services/preprocessingHooks');
20
25
 
21
26
  /**
22
27
  * Main Nexus class that orchestrates all components
@@ -230,7 +235,7 @@ class Nexus {
230
235
  * Send a scheduled message
231
236
  * @param {Object} scheduledMessage - Scheduled message data
232
237
  * @param {string} scheduledMessage.code - Recipient phone number
233
- * @param {string} scheduledMessage.message - Message text
238
+ * @param {string} scheduledMessage.body - Message text
234
239
  * @param {Date|string} scheduledMessage.sendAt - When to send the message
235
240
  * @returns {Promise<Object>} Scheduled message result
236
241
  */
@@ -332,6 +337,9 @@ module.exports = {
332
337
  configureAssistantsLLM,
333
338
  overrideGetAssistantById,
334
339
  configureAssistants: setAssistantsConfig,
340
+ setPreprocessingHandler,
341
+ hasPreprocessingHandler,
342
+ invokePreprocessingHandler,
335
343
  routes,
336
344
  setupDefaultRoutes: routes.setupDefaultRoutes,
337
345
  createRouter: routes.createRouter,
@@ -339,7 +339,7 @@ const getThread = async (code, message = null) => {
339
339
 
340
340
  const getThreadInfo = async (code) => {
341
341
  try {
342
- let thread = await Thread.findOne({ code: code, active: true });
342
+ let thread = await Thread.findOne({ code: code });
343
343
  return thread;
344
344
  } catch (error) {
345
345
  console.log(error);
@@ -0,0 +1,28 @@
1
+ let preprocessingHandler = null;
2
+
3
+ const setPreprocessingHandler = (handler) => {
4
+ if (typeof handler === 'function') {
5
+ preprocessingHandler = handler;
6
+ } else {
7
+ preprocessingHandler = null;
8
+ }
9
+ };
10
+
11
+ const hasPreprocessingHandler = () => typeof preprocessingHandler === 'function';
12
+
13
+ const invokePreprocessingHandler = async (payload) => {
14
+ if (!hasPreprocessingHandler()) return false;
15
+ try {
16
+ const result = await preprocessingHandler(payload);
17
+ return Boolean(result);
18
+ } catch (error) {
19
+ console.warn('[PreprocessingHooks] Handler threw an error:', error?.message || error);
20
+ return false;
21
+ }
22
+ };
23
+
24
+ module.exports = {
25
+ setPreprocessingHandler,
26
+ hasPreprocessingHandler,
27
+ invokePreprocessingHandler
28
+ };
@@ -137,7 +137,7 @@ class MongoStorage {
137
137
  fileUrl: undefined,
138
138
  fileType: mediaPayload.mediaType || messageData.fileType,
139
139
  isMedia: true,
140
- message: messageData.body || rawMessage.Body || primary.caption || '',
140
+ body: messageData.body || rawMessage.Body || primary.caption || '',
141
141
  caption: primary.caption || messageData.caption
142
142
  };
143
143
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "1.7.7",
3
+ "version": "1.7.9",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -78,7 +78,7 @@
78
78
  "mongoose": "^7.5.0",
79
79
  "multer": "1.4.5-lts.1",
80
80
  "pdf-lib": "1.17.1",
81
- "pino": "^8.15.0",
81
+ "pino": "10.0.0",
82
82
  "pino-pretty": "^10.2.0",
83
83
  "uuid": "^9.0.0"
84
84
  },
@@ -90,11 +90,11 @@
90
90
  "typescript": "^5.1.6"
91
91
  },
92
92
  "peerDependencies": {
93
+ "@anthropic-ai/sdk": "^0.32.0",
93
94
  "baileys": "^6.4.0",
94
95
  "express": "4.21.2",
95
96
  "openai": "^4.0.0",
96
- "twilio": "5.6.0",
97
- "@anthropic-ai/sdk": "^0.32.0"
97
+ "twilio": "5.6.0"
98
98
  },
99
99
  "engines": {
100
100
  "node": ">=20.0.0"
@@ -102,4 +102,4 @@
102
102
  "publishConfig": {
103
103
  "access": "public"
104
104
  }
105
- }
105
+ }