@peopl-health/nexus 3.3.4 → 3.3.6
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.
|
@@ -3,7 +3,8 @@ const { Message } = require('../models/messageModel.js');
|
|
|
3
3
|
const { ScheduledMessage: DefaultScheduledMessage } = require('../models/agendaMessageModel.js');
|
|
4
4
|
const {
|
|
5
5
|
sendMessage: defaultSendMessage,
|
|
6
|
-
sendScheduledMessage: defaultSendScheduledMessage
|
|
6
|
+
sendScheduledMessage: defaultSendScheduledMessage,
|
|
7
|
+
getDefaultInstance
|
|
7
8
|
} = require('../core/NexusMessaging');
|
|
8
9
|
const { ensureWhatsAppFormat } = require('../helpers/twilioHelper');
|
|
9
10
|
const { getRecordByFilter: defaultGetRecordByFilter } = require('../services/airtableService');
|
|
@@ -31,6 +32,12 @@ const ensureDependency = (res, condition, errorMessage) => {
|
|
|
31
32
|
return false;
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
const ensureDefaultInstance = (res) => ensureDependency(
|
|
36
|
+
res,
|
|
37
|
+
Boolean(getDefaultInstance()),
|
|
38
|
+
'NexusMessaging default instance not initialized'
|
|
39
|
+
);
|
|
40
|
+
|
|
34
41
|
const persistScheduledMessage = async (Model, payload) => {
|
|
35
42
|
if (Model && typeof Model.create === 'function') {
|
|
36
43
|
return await Model.create(payload);
|
|
@@ -80,6 +87,8 @@ const sendMessageController = async (req, res) => {
|
|
|
80
87
|
const hasScheduler = typeof dependencies.sendScheduledMessage === 'function';
|
|
81
88
|
const hasDirectSend = typeof dependencies.sendMessage === 'function';
|
|
82
89
|
if (!ensureDependency(res, hasScheduler || hasDirectSend, 'No messaging provider configured. Ensure Nexus.initialize() completed before using the message controllers.')) return;
|
|
90
|
+
if ((dependencies.sendMessage === defaultSendMessage || dependencies.sendScheduledMessage === defaultSendScheduledMessage)
|
|
91
|
+
&& !ensureDefaultInstance(res)) return;
|
|
83
92
|
|
|
84
93
|
try {
|
|
85
94
|
const payload = {
|
|
@@ -136,6 +145,8 @@ const sendBulkMessageController = async (req, res) => {
|
|
|
136
145
|
const hasScheduler = typeof dependencies.sendScheduledMessage === 'function';
|
|
137
146
|
const hasDirectSend = typeof dependencies.sendMessage === 'function';
|
|
138
147
|
if (!ensureDependency(res, hasScheduler || hasDirectSend, 'No messaging provider configured. Ensure Nexus.initialize() completed before using the message controllers.')) return;
|
|
148
|
+
if ((dependencies.sendMessage === defaultSendMessage || dependencies.sendScheduledMessage === defaultSendScheduledMessage)
|
|
149
|
+
&& !ensureDefaultInstance(res)) return;
|
|
139
150
|
|
|
140
151
|
try {
|
|
141
152
|
let numSend = 0;
|
|
@@ -216,6 +227,8 @@ const sendBulkMessageAirtableController = async (req, res) => {
|
|
|
216
227
|
const hasScheduler = typeof dependencies.sendScheduledMessage === 'function';
|
|
217
228
|
const hasDirectSend = typeof dependencies.sendMessage === 'function';
|
|
218
229
|
if (!ensureDependency(res, hasScheduler || hasDirectSend, 'No messaging provider configured. Ensure Nexus.initialize() completed before using the message controllers.')) return;
|
|
230
|
+
if ((dependencies.sendMessage === defaultSendMessage || dependencies.sendScheduledMessage === defaultSendScheduledMessage)
|
|
231
|
+
&& !ensureDefaultInstance(res)) return;
|
|
219
232
|
|
|
220
233
|
const airtableFetcher = dependencies.getRecordByFilter;
|
|
221
234
|
if (!ensureDependency(res, typeof airtableFetcher === 'function', 'Airtable getRecordByFilter not configured. Call configureMessageController() to inject it.')) return;
|
|
@@ -45,6 +45,7 @@ class NexusMessaging {
|
|
|
45
45
|
this.processingLocks = new Map(); // Per-chat locks to prevent parallel processing
|
|
46
46
|
this.activeRequests = new Map(); // Track active AI requests per chat
|
|
47
47
|
this.abandonedRuns = new Set(); // Track runs that should be ignored
|
|
48
|
+
this.typingIntervals = new Map(); // Track active typing intervals per chat
|
|
48
49
|
this.batchingConfig = {
|
|
49
50
|
enabled: config.messageBatching?.enabled ?? true,
|
|
50
51
|
abortOnNewMessage: config.messageBatching?.abortOnNewMessage ?? true,
|
|
@@ -667,8 +668,6 @@ class NexusMessaging {
|
|
|
667
668
|
* Handle message with check-after strategy - process immediately, check for new messages after
|
|
668
669
|
*/
|
|
669
670
|
async _handleWithCheckAfter(chatId) {
|
|
670
|
-
const typingInterval = await this._startTypingRefresh(chatId);
|
|
671
|
-
|
|
672
671
|
if (this.processingLocks.has(chatId)) {
|
|
673
672
|
logger.info(`[CheckAfter] Already processing ${chatId}, new message will be included`);
|
|
674
673
|
|
|
@@ -680,6 +679,11 @@ class NexusMessaging {
|
|
|
680
679
|
if (this.batchingConfig.immediateRestart) {
|
|
681
680
|
this.processingLocks.delete(chatId);
|
|
682
681
|
this.activeRequests.delete(chatId);
|
|
682
|
+
const existingTypingInterval = this.typingIntervals.get(chatId);
|
|
683
|
+
if (existingTypingInterval) {
|
|
684
|
+
clearInterval(existingTypingInterval);
|
|
685
|
+
this.typingIntervals.delete(chatId);
|
|
686
|
+
}
|
|
683
687
|
|
|
684
688
|
logger.info(`[CheckAfter] Starting immediate reprocessing for ${chatId}`);
|
|
685
689
|
await this._processWithLock(chatId, null);
|
|
@@ -688,6 +692,7 @@ class NexusMessaging {
|
|
|
688
692
|
return;
|
|
689
693
|
}
|
|
690
694
|
|
|
695
|
+
const typingInterval = await this._startTypingRefresh(chatId);
|
|
691
696
|
await this._processWithLock(chatId, typingInterval);
|
|
692
697
|
}
|
|
693
698
|
|
|
@@ -697,13 +702,20 @@ class NexusMessaging {
|
|
|
697
702
|
async _processWithLock(chatId, existingTypingInterval = null) {
|
|
698
703
|
this.processingLocks.set(chatId, true);
|
|
699
704
|
let typingInterval = existingTypingInterval;
|
|
705
|
+
if (typingInterval) {
|
|
706
|
+
this.typingIntervals.set(chatId, typingInterval);
|
|
707
|
+
}
|
|
700
708
|
|
|
701
709
|
const runId = `run_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
702
710
|
this.activeRequests.set(chatId, runId);
|
|
703
711
|
|
|
704
712
|
try {
|
|
705
713
|
if (!typingInterval) {
|
|
714
|
+
// Keep typing intervals scoped to the active processing run to avoid leaks on restarts.
|
|
706
715
|
typingInterval = await this._startTypingRefresh(chatId);
|
|
716
|
+
if (typingInterval) {
|
|
717
|
+
this.typingIntervals.set(chatId, typingInterval);
|
|
718
|
+
}
|
|
707
719
|
}
|
|
708
720
|
|
|
709
721
|
const startTime = Date.now();
|
|
@@ -755,6 +767,7 @@ class NexusMessaging {
|
|
|
755
767
|
if (typingInterval) {
|
|
756
768
|
clearInterval(typingInterval);
|
|
757
769
|
typingInterval = null;
|
|
770
|
+
this.typingIntervals.delete(chatId);
|
|
758
771
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
759
772
|
}
|
|
760
773
|
|
|
@@ -775,7 +788,10 @@ class NexusMessaging {
|
|
|
775
788
|
} catch (error) {
|
|
776
789
|
logger.error('[CheckAfter] Error processing messages:', { chatId, error: error.message });
|
|
777
790
|
} finally {
|
|
778
|
-
if (typingInterval)
|
|
791
|
+
if (typingInterval) {
|
|
792
|
+
clearInterval(typingInterval);
|
|
793
|
+
}
|
|
794
|
+
this.typingIntervals.delete(chatId);
|
|
779
795
|
if (this.activeRequests.get(chatId) === runId) {
|
|
780
796
|
this.processingLocks.delete(chatId);
|
|
781
797
|
this.activeRequests.delete(chatId);
|
|
@@ -886,12 +902,19 @@ const setDefaultInstance = (instance) => {
|
|
|
886
902
|
|
|
887
903
|
const getDefaultInstance = () => defaultInstance;
|
|
888
904
|
|
|
905
|
+
const requireDefaultInstance = () => {
|
|
906
|
+
if (!defaultInstance) {
|
|
907
|
+
throw new Error('NexusMessaging default instance not initialized');
|
|
908
|
+
}
|
|
909
|
+
return defaultInstance;
|
|
910
|
+
};
|
|
911
|
+
|
|
889
912
|
const sendMessage = async (messageData) => {
|
|
890
|
-
return await
|
|
913
|
+
return await requireDefaultInstance().sendMessage(messageData);
|
|
891
914
|
};
|
|
892
915
|
|
|
893
916
|
const sendScheduledMessage = async (scheduledMessage) => {
|
|
894
|
-
return await
|
|
917
|
+
return await requireDefaultInstance().sendScheduledMessage(scheduledMessage);
|
|
895
918
|
};
|
|
896
919
|
|
|
897
920
|
module.exports = {
|