@peopl-health/nexus 3.4.1 → 3.5.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.
|
@@ -14,8 +14,12 @@ class AssistantProcessor {
|
|
|
14
14
|
|
|
15
15
|
async process({ code, body = null, runOptions = {} }) {
|
|
16
16
|
if (!code) throw new Error('code is required for assistant processing');
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
const result = (this.mode === 'queue')
|
|
19
|
+
? await this._processViaQueue({ code, body, runOptions })
|
|
20
|
+
: await this._processLocal({ code, body, runOptions });
|
|
21
|
+
|
|
22
|
+
return result;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
async _processLocal({ code, body = null, runOptions = {} }) {
|
|
@@ -5,10 +5,12 @@ const llmConfigModule = require('../config/llmConfig');
|
|
|
5
5
|
const { connect } = require('../config/mongoConfig');
|
|
6
6
|
|
|
7
7
|
const { logger } = require('../utils/logger');
|
|
8
|
+
const { createEvent, safeEmit } = require('../utils/eventUtils');
|
|
8
9
|
|
|
9
10
|
const { Message, getMessageValues, insertMessage } = require('../models/messageModel');
|
|
10
11
|
const { Thread } = require('../models/threadModel');
|
|
11
12
|
|
|
13
|
+
const { setEventBus: setStatusEventBus } = require('../helpers/messageStatusHelper');
|
|
12
14
|
const { ensureThreadExists } = require('../helpers/threadHelper');
|
|
13
15
|
const { enrichMessageWithTwilioMedia } = require('../helpers/twilioMediaHelper');
|
|
14
16
|
const { convertTwilioToInternalFormat } = require('../helpers/twilioHelper');
|
|
@@ -43,6 +45,7 @@ class NexusMessaging {
|
|
|
43
45
|
onFlow: null
|
|
44
46
|
};
|
|
45
47
|
this.events = new EventEmitter();
|
|
48
|
+
setStatusEventBus(this.events);
|
|
46
49
|
this.middleware = {
|
|
47
50
|
any: [],
|
|
48
51
|
message: [],
|
|
@@ -192,7 +195,6 @@ class NexusMessaging {
|
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
async _handleWithPipeline(type, handlerKey, messageData, fallback) {
|
|
195
|
-
this.events.emit(`${type}:received`, messageData);
|
|
196
198
|
const result = await this._runPipeline(type, messageData, async (ctx) => {
|
|
197
199
|
const handler = this.handlers[handlerKey];
|
|
198
200
|
if (handler) {
|
|
@@ -202,7 +204,6 @@ class NexusMessaging {
|
|
|
202
204
|
return await fallback(ctx);
|
|
203
205
|
}
|
|
204
206
|
});
|
|
205
|
-
this.events.emit(`${type}:handled`, messageData);
|
|
206
207
|
return result;
|
|
207
208
|
}
|
|
208
209
|
|
|
@@ -274,6 +275,19 @@ class NexusMessaging {
|
|
|
274
275
|
}
|
|
275
276
|
|
|
276
277
|
const chatId = messageData.from || messageData.From;
|
|
278
|
+
const messageId = messageData.id || messageData.message_id;
|
|
279
|
+
|
|
280
|
+
if (chatId && messageId) {
|
|
281
|
+
safeEmit(this.events, 'message:new', createEvent('message:new', {
|
|
282
|
+
messageId,
|
|
283
|
+
from: chatId,
|
|
284
|
+
body: messageData.body || messageData.message || '',
|
|
285
|
+
media: messageData.media || null,
|
|
286
|
+
type: messageData.interactive ? 'interactive' : messageData.media ? 'media' : 'message'
|
|
287
|
+
}));
|
|
288
|
+
} else {
|
|
289
|
+
logger.warn('[processIncomingMessage] Skipping event emission: missing chatId or messageId', { chatId, messageId });
|
|
290
|
+
}
|
|
277
291
|
|
|
278
292
|
if (chatId) {
|
|
279
293
|
ensureThreadExists(chatId);
|
|
@@ -423,6 +437,8 @@ class NexusMessaging {
|
|
|
423
437
|
async disconnect() {
|
|
424
438
|
if (this.provider) await this.provider.disconnect();
|
|
425
439
|
if (this.queueAdapter) await this.queueAdapter.shutdown();
|
|
440
|
+
this.events.removeAllListeners();
|
|
441
|
+
setStatusEventBus(null);
|
|
426
442
|
}
|
|
427
443
|
}
|
|
428
444
|
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
const { logger } = require('../utils/logger');
|
|
2
|
+
const { createEvent, safeEmit } = require('../utils/eventUtils');
|
|
2
3
|
|
|
3
4
|
const { Message } = require('../models/messageModel');
|
|
4
5
|
|
|
5
6
|
const { handle24HourWindowError } = require('../helpers/templateRecoveryHelper');
|
|
6
7
|
|
|
8
|
+
let _eventBus = null;
|
|
9
|
+
|
|
10
|
+
function setEventBus(eventBus) {
|
|
11
|
+
_eventBus = eventBus;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
async function updateMessageStatus(messageSid, status, errorCode = null, errorMessage = null) {
|
|
8
15
|
try {
|
|
9
16
|
const updateData = {
|
|
@@ -40,6 +47,16 @@ async function handleStatusCallback(twilioStatusData) {
|
|
|
40
47
|
|
|
41
48
|
const updated = await updateMessageStatus(MessageSid, MessageStatus.toLowerCase(), ErrorCode, ErrorMessage);
|
|
42
49
|
|
|
50
|
+
if (updated && _eventBus) {
|
|
51
|
+
safeEmit(_eventBus, 'message:status', createEvent('message:status', {
|
|
52
|
+
messageId: MessageSid,
|
|
53
|
+
to: updated.numero,
|
|
54
|
+
status: MessageStatus.toLowerCase(),
|
|
55
|
+
errorCode: ErrorCode || null,
|
|
56
|
+
errorMessage: ErrorMessage || null
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
if ((ErrorCode === 63016 || ErrorCode === '63016') && updated) {
|
|
44
61
|
handle24HourWindowError(updated, MessageSid).catch(err =>
|
|
45
62
|
logger.error('[MessageStatus] Recovery error', { messageSid: MessageSid, error: err.message })
|
|
@@ -63,5 +80,6 @@ async function getMessageStatus(messageSid) {
|
|
|
63
80
|
module.exports = {
|
|
64
81
|
updateMessageStatus,
|
|
65
82
|
handleStatusCallback,
|
|
66
|
-
getMessageStatus
|
|
83
|
+
getMessageStatus,
|
|
84
|
+
setEventBus
|
|
67
85
|
};
|
|
@@ -66,6 +66,12 @@ async function handle24HourWindowError(message, messageSid) {
|
|
|
66
66
|
try {
|
|
67
67
|
await sendMessage({ code: message.numero, contentSid: twilioContent.sid, variables: {} });
|
|
68
68
|
logger.info('[TemplateRecovery] Template sent', { messageSid, templateSid: twilioContent.sid });
|
|
69
|
+
try {
|
|
70
|
+
await provider.deleteTemplate(twilioContent.sid);
|
|
71
|
+
logger.info('[TemplateRecovery] Template deleted', { messageSid, templateSid: twilioContent.sid });
|
|
72
|
+
} catch (deleteErr) {
|
|
73
|
+
logger.warn('[TemplateRecovery] Failed to delete template after send', { messageSid, templateSid: twilioContent.sid, error: deleteErr.message });
|
|
74
|
+
}
|
|
69
75
|
} catch (sendErr) {
|
|
70
76
|
await Message.updateOne(
|
|
71
77
|
{ message_id: messageSid },
|
|
@@ -75,6 +81,12 @@ async function handle24HourWindowError(message, messageSid) {
|
|
|
75
81
|
}
|
|
76
82
|
} else if (approvalStatus === 'REJECTED') {
|
|
77
83
|
logger.warn('[TemplateRecovery] Template rejected', { messageSid, templateSid: twilioContent.sid });
|
|
84
|
+
try {
|
|
85
|
+
await provider.deleteTemplate(twilioContent.sid);
|
|
86
|
+
logger.info('[TemplateRecovery] Rejected template deleted', { messageSid, templateSid: twilioContent.sid });
|
|
87
|
+
} catch (deleteErr) {
|
|
88
|
+
logger.warn('[TemplateRecovery] Failed to delete rejected template', { messageSid, templateSid: twilioContent.sid, error: deleteErr.message });
|
|
89
|
+
}
|
|
78
90
|
} else {
|
|
79
91
|
checkApproval(attempt + 1);
|
|
80
92
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -161,6 +161,36 @@ declare module '@peopl-health/nexus' {
|
|
|
161
161
|
setReplies(replies: any): void;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// Event Types
|
|
165
|
+
export interface NexusEventEnvelope<T = any> {
|
|
166
|
+
event: string;
|
|
167
|
+
timestamp: number;
|
|
168
|
+
data: T;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface MessageNewEventData {
|
|
172
|
+
messageId: string;
|
|
173
|
+
from: string;
|
|
174
|
+
body: string;
|
|
175
|
+
media: any | null;
|
|
176
|
+
type: 'message' | 'interactive' | 'media';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface MessageStatusEventData {
|
|
180
|
+
messageId: string;
|
|
181
|
+
to: string;
|
|
182
|
+
status: 'queued' | 'sending' | 'sent' | 'delivered' | 'undelivered' | 'failed' | 'read';
|
|
183
|
+
errorCode: string | null;
|
|
184
|
+
errorMessage: string | null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface NexusEventMap {
|
|
188
|
+
'message:new': NexusEventEnvelope<MessageNewEventData>;
|
|
189
|
+
'message:status': NexusEventEnvelope<MessageStatusEventData>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export type NexusEventName = keyof NexusEventMap;
|
|
193
|
+
|
|
164
194
|
// Core Classes
|
|
165
195
|
export abstract class MessageProvider {
|
|
166
196
|
constructor(config: any);
|
|
@@ -212,6 +242,9 @@ declare module '@peopl-health/nexus' {
|
|
|
212
242
|
sendMessage(messageData: MessageData): Promise<any>;
|
|
213
243
|
sendScheduledMessage(scheduledMessage: ScheduledMessage): Promise<void>;
|
|
214
244
|
processIncomingMessage(messageData: MessageData): Promise<any>;
|
|
245
|
+
getEventBus(): import('events').EventEmitter;
|
|
246
|
+
getBatchingManager(): BatchingManager;
|
|
247
|
+
getAssistantProcessor(): AssistantProcessor;
|
|
215
248
|
isConnected(): boolean;
|
|
216
249
|
disconnect(): Promise<void>;
|
|
217
250
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { logger } = require('./logger');
|
|
2
|
+
|
|
3
|
+
function createEvent(event, data) {
|
|
4
|
+
return { event, timestamp: Date.now(), data };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function safeEmit(emitter, event, payload) {
|
|
8
|
+
try {
|
|
9
|
+
emitter.emit(event, payload);
|
|
10
|
+
} catch (err) {
|
|
11
|
+
logger.error('[EventBus] Listener error', { event, error: err.message });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { createEvent, safeEmit };
|