@peopl-health/nexus 3.15.4 → 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.
|
@@ -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
|
|