@peopl-health/nexus 3.15.3 → 3.15.5
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.
|
@@ -8,11 +8,12 @@ const { getBug, VALID_SEVERITIES } = require('../models/bugModel');
|
|
|
8
8
|
|
|
9
9
|
const { addRecord, getRecordByFilter } = require('../services/airtableService');
|
|
10
10
|
|
|
11
|
-
async function logBugReportToAirtable(
|
|
11
|
+
async function logBugReportToAirtable(reportedBug) {
|
|
12
|
+
const { reporter, whatsapp_id, description, severity, messages, server, bugType, project } = reportedBug;
|
|
12
13
|
try {
|
|
13
14
|
let conversation = null;
|
|
14
|
-
if (
|
|
15
|
-
const msgs = await Message.find({ _id: { $in:
|
|
15
|
+
if (messages?.length) {
|
|
16
|
+
const msgs = await Message.find({ _id: { $in: messages } }).sort({ createdAt: 1 });
|
|
16
17
|
conversation = msgs.map(msg => {
|
|
17
18
|
const timestamp = new Date(msg.timestamp).toISOString().slice(0, 16).replace('T', ' ');
|
|
18
19
|
const role = msg.from_me ? 'Assistant' : 'Patient';
|
|
@@ -28,13 +29,15 @@ async function logBugReportToAirtable(reporter, whatsapp_id, description, severi
|
|
|
28
29
|
logger.warn('Could not find patient in estado_general:', { error: err.message });
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
await addRecord(Logging_ID, 'bug_reports', {
|
|
32
|
-
reporter, description, severity,
|
|
32
|
+
const bugRecord = await addRecord(Logging_ID, 'bug_reports', {
|
|
33
|
+
reporter, description, severity, bugType,
|
|
34
|
+
...(project && {...project}),
|
|
33
35
|
...(patientId && { patient_id: [patientId] }),
|
|
34
36
|
...(conversation && { conversation }),
|
|
35
37
|
server
|
|
36
38
|
});
|
|
37
39
|
logger.debug('Bug report logged to Airtable successfully');
|
|
40
|
+
reportedBug.recordId = bugRecord.id;
|
|
38
41
|
} catch (error) {
|
|
39
42
|
logger.error('Error logging bug report to Airtable:', { error: error.message });
|
|
40
43
|
}
|
|
@@ -42,7 +45,7 @@ async function logBugReportToAirtable(reporter, whatsapp_id, description, severi
|
|
|
42
45
|
|
|
43
46
|
const reportBugController = async (req, res) => {
|
|
44
47
|
try {
|
|
45
|
-
const { reporter, whatsapp_id, description, severity, messages } = req.body;
|
|
48
|
+
const { reporter, whatsapp_id, description, severity, messages, bugType, project } = req.body;
|
|
46
49
|
|
|
47
50
|
if (!reporter) return res.status(400).json({ success: false, error: 'Reporter username is required' });
|
|
48
51
|
if (!whatsapp_id) return res.status(400).json({ success: false, error: 'WhatsApp ID is required' });
|
|
@@ -52,12 +55,23 @@ const reportBugController = async (req, res) => {
|
|
|
52
55
|
|
|
53
56
|
const server = runtimeConfig.get('SERVICE_NAME');
|
|
54
57
|
|
|
55
|
-
const
|
|
56
|
-
|
|
58
|
+
const reportedBug = {
|
|
59
|
+
reporter, whatsapp_id, description, severity,
|
|
60
|
+
messages: messages || [],
|
|
61
|
+
server,
|
|
62
|
+
recordId: null,
|
|
63
|
+
bugType: bugType || 'other',
|
|
64
|
+
...(project && { project })
|
|
65
|
+
};
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
)
|
|
67
|
+
try {
|
|
68
|
+
await logBugReportToAirtable(reportedBug);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.error('Error logging bug report to Airtable:', { error: error.message });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const Bug = getBug();
|
|
74
|
+
const bug = await Bug.create(reportedBug);
|
|
61
75
|
|
|
62
76
|
res.status(201).json({ success: true, message: 'Bug report submitted successfully', bug });
|
|
63
77
|
} catch (error) {
|
|
@@ -22,6 +22,7 @@ const { ProcessingPipeline } = require('../core/ProcessingPipeline');
|
|
|
22
22
|
const { AssistantProcessor } = require('../core/AssistantProcessor');
|
|
23
23
|
|
|
24
24
|
const { createQueueAdapter } = require('../queue');
|
|
25
|
+
const { ScheduledMessageJob } = require('../jobs/ScheduledMessageJob');
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Core messaging orchestrator for providers, storage, and assistant processing.
|
|
@@ -77,6 +78,11 @@ class NexusMessaging {
|
|
|
77
78
|
sendMessage: this.sendMessage.bind(this),
|
|
78
79
|
storeRunMetrics,
|
|
79
80
|
});
|
|
81
|
+
|
|
82
|
+
this.scheduledMessageJob = new ScheduledMessageJob({
|
|
83
|
+
queueAdapter: this.queueAdapter,
|
|
84
|
+
sendMessage: this.sendMessage.bind(this)
|
|
85
|
+
});
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
async initialize(options = {}) {
|
|
@@ -257,10 +263,16 @@ class NexusMessaging {
|
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
async sendScheduledMessage(scheduledMessage) {
|
|
260
|
-
if (!
|
|
261
|
-
throw new Error('
|
|
266
|
+
if (!scheduledMessage?._id) {
|
|
267
|
+
throw new Error('sendScheduledMessage requires a persisted ScheduledMessage with _id');
|
|
262
268
|
}
|
|
263
|
-
|
|
269
|
+
if (!scheduledMessage?.sendTime) {
|
|
270
|
+
throw new Error('sendScheduledMessage requires sendTime');
|
|
271
|
+
}
|
|
272
|
+
return await this.scheduledMessageJob.schedule({
|
|
273
|
+
scheduledMessageId: scheduledMessage._id,
|
|
274
|
+
sendTime: scheduledMessage.sendTime
|
|
275
|
+
});
|
|
264
276
|
}
|
|
265
277
|
|
|
266
278
|
/*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { logger } = require('../utils/logger');
|
|
2
|
+
const { ScheduledMessage } = require('../models/agendaMessageModel');
|
|
2
3
|
const { BaseJob } = require('./BaseJob');
|
|
3
4
|
|
|
4
5
|
const QUEUE_NAME = 'scheduled-messages';
|
|
@@ -10,9 +11,15 @@ const DEFAULT_JOB_OPTIONS = {
|
|
|
10
11
|
removeOnFail: 50
|
|
11
12
|
};
|
|
12
13
|
|
|
14
|
+
const TERMINAL_STATUSES = ['sent', 'cancelled'];
|
|
15
|
+
|
|
13
16
|
class ScheduledMessageJob extends BaseJob {
|
|
14
|
-
constructor({ queueAdapter } = {}) {
|
|
17
|
+
constructor({ queueAdapter, sendMessage } = {}) {
|
|
15
18
|
super({ queueAdapter, queueName: QUEUE_NAME });
|
|
19
|
+
if (typeof sendMessage !== 'function') {
|
|
20
|
+
throw new Error('ScheduledMessageJob requires a sendMessage function');
|
|
21
|
+
}
|
|
22
|
+
this.sendMessage = sendMessage;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
async schedule({ scheduledMessageId, sendTime }) {
|
|
@@ -47,9 +54,66 @@ class ScheduledMessageJob extends BaseJob {
|
|
|
47
54
|
return `scheduled-msg-${scheduledMessageId}`;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
async _process(
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
async _process({ scheduledMessageId }) {
|
|
58
|
+
const msg = await ScheduledMessage.findById(scheduledMessageId);
|
|
59
|
+
|
|
60
|
+
if (!msg) {
|
|
61
|
+
logger.warn('[ScheduledMessageJob] Scheduled message not found', { scheduledMessageId });
|
|
62
|
+
return { success: false, reason: 'not_found' };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (TERMINAL_STATUSES.includes(msg.status)) {
|
|
66
|
+
logger.info('[ScheduledMessageJob] Already processed', { scheduledMessageId, status: msg.status });
|
|
67
|
+
return { success: false, reason: 'already_processed', status: msg.status };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const claim = await ScheduledMessage.updateOne(
|
|
71
|
+
{ _id: scheduledMessageId, status: { $nin: TERMINAL_STATUSES.concat(['sending']) } },
|
|
72
|
+
{ $set: { status: 'sending', sendingAt: new Date() } }
|
|
73
|
+
);
|
|
74
|
+
if (!claim.modifiedCount && !claim.nModified) {
|
|
75
|
+
logger.info('[ScheduledMessageJob] Send already claimed by another worker', { scheduledMessageId });
|
|
76
|
+
return { success: false, reason: 'already_claimed' };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const result = await this.sendMessage({
|
|
81
|
+
code: msg.code,
|
|
82
|
+
body: msg.message,
|
|
83
|
+
fileUrl: msg.fileUrl,
|
|
84
|
+
fileType: msg.fileType,
|
|
85
|
+
contentSid: msg.contentSid,
|
|
86
|
+
variables: msg.variables,
|
|
87
|
+
hidePreview: msg.hidePreview
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const messageId = result?.messageId || result?.sid || null;
|
|
91
|
+
|
|
92
|
+
await ScheduledMessage.updateOne(
|
|
93
|
+
{ _id: scheduledMessageId },
|
|
94
|
+
{ $set: { status: 'sent', sentAt: new Date(), wa_id: messageId } }
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
logger.info('[ScheduledMessageJob] Sent', { scheduledMessageId, messageId });
|
|
98
|
+
|
|
99
|
+
return { success: true, messageId };
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await ScheduledMessage.updateOne(
|
|
102
|
+
{ _id: scheduledMessageId },
|
|
103
|
+
{
|
|
104
|
+
$set: {
|
|
105
|
+
status: 'failed',
|
|
106
|
+
failedAt: new Date(),
|
|
107
|
+
errorCode: error?.code || null,
|
|
108
|
+
errorMessage: error?.message || null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
logger.error('[ScheduledMessageJob] Send failed', { scheduledMessageId, error: error.message });
|
|
114
|
+
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
53
117
|
}
|
|
54
118
|
}
|
|
55
119
|
|