@peopl-health/nexus 3.3.9 → 3.3.10

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.
Files changed (74) hide show
  1. package/examples/basic-usage.js +0 -1
  2. package/lib/adapters/BaileysProvider.js +1 -1
  3. package/lib/adapters/TwilioProvider.js +111 -250
  4. package/lib/adapters/index.js +6 -1
  5. package/lib/assistants/BaseAssistant.js +15 -146
  6. package/lib/config/awsConfig.js +5 -3
  7. package/lib/config/llmConfig.js +24 -55
  8. package/lib/controllers/assistantController.js +49 -54
  9. package/lib/controllers/bugReportController.js +18 -22
  10. package/lib/controllers/caseDocumentationController.js +12 -19
  11. package/lib/controllers/conversationController.js +50 -225
  12. package/lib/controllers/interactionController.js +2 -2
  13. package/lib/controllers/mediaController.js +25 -66
  14. package/lib/controllers/messageController.js +114 -232
  15. package/lib/controllers/messageStatusController.js +32 -62
  16. package/lib/controllers/patientController.js +1 -1
  17. package/lib/controllers/qualityMessageController.js +21 -15
  18. package/lib/controllers/templateController.js +76 -247
  19. package/lib/controllers/templateFlowController.js +36 -90
  20. package/lib/controllers/threadController.js +30 -85
  21. package/lib/controllers/uploadController.js +31 -54
  22. package/lib/{utils/messageParser.js → core/MessageParser.js} +16 -35
  23. package/lib/core/NexusMessaging.js +266 -452
  24. package/lib/helpers/assistantHelper.js +12 -41
  25. package/lib/helpers/baileysHelper.js +39 -94
  26. package/lib/helpers/filesHelper.js +36 -103
  27. package/lib/helpers/llmsHelper.js +48 -135
  28. package/lib/helpers/mediaHelper.js +10 -21
  29. package/lib/helpers/messageHelper.js +52 -88
  30. package/lib/helpers/messageStatusHelper.js +11 -42
  31. package/lib/helpers/metaFlowHelper.js +3 -1
  32. package/lib/helpers/mongoHelper.js +8 -21
  33. package/lib/helpers/processHelper.js +65 -189
  34. package/lib/helpers/qrHelper.js +1 -1
  35. package/lib/helpers/templateFlowControllerHelper.js +15 -44
  36. package/lib/helpers/templateRecoveryHelper.js +16 -28
  37. package/lib/helpers/threadHelper.js +20 -45
  38. package/lib/helpers/threadRecoveryHelper.js +11 -20
  39. package/lib/helpers/twilioHelper.js +23 -92
  40. package/lib/helpers/twilioMediaHelper.js +125 -0
  41. package/lib/helpers/whatsappHelper.js +8 -22
  42. package/lib/index.js +2 -23
  43. package/lib/memory/DefaultMemoryManager.js +47 -83
  44. package/lib/models/templateModel.js +9 -25
  45. package/lib/observability/telemetry.js +40 -72
  46. package/lib/providers/OpenAIAssistantsProvider.js +62 -169
  47. package/lib/providers/OpenAIResponsesProvider.js +90 -186
  48. package/lib/providers/OpenAIResponsesProviderTools.js +21 -25
  49. package/lib/providers/createLLMProvider.js +4 -7
  50. package/lib/services/airtableService.js +42 -61
  51. package/lib/services/assistantService.js +330 -49
  52. package/lib/services/conversationService.js +91 -63
  53. package/lib/services/metaFlowService.js +18 -47
  54. package/lib/services/metaService.js +50 -82
  55. package/lib/services/{preprocessingHooks.js → preprocessingService.js} +1 -1
  56. package/lib/services/twilioService.js +8 -51
  57. package/lib/storage/MongoStorage.js +24 -50
  58. package/lib/templates/predefinedTemplates.js +15 -73
  59. package/lib/templates/templateStructure.js +32 -99
  60. package/lib/utils/dateUtils.js +11 -28
  61. package/lib/utils/{outputSanitizer.js → formatUtils.js} +2 -4
  62. package/lib/utils/{retryHelper.js → retryUtils.js} +2 -8
  63. package/lib/utils/sanitizerUtils.js +62 -0
  64. package/lib/utils/scheduleUtils.js +8 -18
  65. package/lib/utils/tracingDecorator.js +6 -21
  66. package/package.json +1 -17
  67. package/lib/config/configLoader.js +0 -38
  68. package/lib/core/index.js +0 -7
  69. package/lib/helpers/twilioMediaProcessor.js +0 -167
  70. package/lib/services/assistantServiceCore.js +0 -327
  71. package/lib/utils/index.js +0 -11
  72. package/lib/utils/inputSanitizer.js +0 -56
  73. /package/lib/{core → adapters}/MessageProvider.js +0 -0
  74. /package/lib/utils/{errorHandler.js → errorUtils.js} +0 -0
@@ -172,7 +172,6 @@ async function startServer() {
172
172
  // Example using Airtable
173
173
  app.get('/airtable-test', async (req, res) => {
174
174
  try {
175
- const calendarBase = nexus.getAirtableBase('calendar');
176
175
  // Use calendarBase to interact with Airtable
177
176
  res.json({ success: true, message: 'Airtable connection ready' });
178
177
  } catch (error) {
@@ -1,7 +1,7 @@
1
1
  const { logger } = require('../utils/logger');
2
2
  const { calculateDelay } = require('../utils/scheduleUtils');
3
3
 
4
- const { MessageProvider } = require('../core/MessageProvider');
4
+ const { MessageProvider } = require('../adapters/MessageProvider');
5
5
 
6
6
  class BaileysProvider extends MessageProvider {
7
7
  constructor(config) {
@@ -1,18 +1,21 @@
1
1
  const axios = require('axios');
2
2
  const { v4: uuidv4 } = require('uuid');
3
+ const twilio = require('twilio');
3
4
 
4
5
  const runtimeConfig = require('../config/runtimeConfig');
5
6
  const { generatePresignedUrl } = require('../config/awsConfig');
6
7
 
7
- const { sanitizeMediaFilename } = require('../utils/inputSanitizer');
8
+ const { sanitizeMediaFilename } = require('../utils/sanitizerUtils');
8
9
  const { validateMedia, getMediaType } = require('../utils/mediaValidator');
9
10
  const { logger } = require('../utils/logger');
10
11
  const { calculateDelay } = require('../utils/scheduleUtils');
11
12
 
13
+ const { ScheduledMessage } = require('../models/agendaMessageModel');
14
+
12
15
  const { ensureWhatsAppFormat } = require('../helpers/twilioHelper');
13
16
  const { uploadMediaToS3, getFileExtension } = require('../helpers/mediaHelper');
14
17
 
15
- const { MessageProvider } = require('../core/MessageProvider');
18
+ const { MessageProvider } = require('../adapters/MessageProvider');
16
19
 
17
20
  class TwilioProvider extends MessageProvider {
18
21
  constructor(config) {
@@ -30,7 +33,6 @@ class TwilioProvider extends MessageProvider {
30
33
  }
31
34
 
32
35
  try {
33
- const twilio = require('twilio');
34
36
  this.twilioClient = twilio(this.accountSid, this.authToken);
35
37
  this.isConnected = true;
36
38
  } catch (error) {
@@ -89,7 +91,7 @@ class TwilioProvider extends MessageProvider {
89
91
  }
90
92
 
91
93
  if (fileUrl && fileType !== 'text') {
92
- const mediaPrep = await this.prepareOutboundMedia(messageData, formattedCode);
94
+ const mediaPrep = await this._prepareOutboundMedia(messageData, formattedCode);
93
95
  const outboundMediaUrl = mediaPrep.mediaUrl || fileUrl;
94
96
  messageParams.mediaUrl = [outboundMediaUrl];
95
97
  if (!messageParams.body || messageParams.body.trim() === '') {
@@ -108,69 +110,42 @@ class TwilioProvider extends MessageProvider {
108
110
  throw new Error('Message must have body, media URL, or content SID');
109
111
  }
110
112
 
113
+ const saveMessage = async (body, result) => {
114
+ if (!this.messageStorage?.saveMessage) return;
115
+ try {
116
+ await this.messageStorage.saveMessage({
117
+ ...messageData,
118
+ body,
119
+ code: formattedCode,
120
+ from: formattedFrom,
121
+ messageId: result.sid,
122
+ provider: 'twilio',
123
+ timestamp: new Date(),
124
+ fromMe: true,
125
+ processed: messageData.processed ?? false,
126
+ statusInfo: {
127
+ status: result.status?.toLowerCase() || null,
128
+ updatedAt: result.dateCreated || new Date()
129
+ }
130
+ });
131
+ } catch (err) {
132
+ logger.error('[TwilioProvider] Storage failed:', err);
133
+ }
134
+ };
135
+
111
136
  let result;
112
- const chunks = messageParams.body && messageParams.body.length > 1600 && !messageParams.mediaUrl && !messageParams.contentSid
113
- ? this.splitMessageAtWordBoundaries(messageParams.body)
114
- : null;
137
+ const chunks = messageParams.body?.length > 1600 && !messageParams.mediaUrl && !messageParams.contentSid
138
+ ? this._splitMessageAtWordBoundaries(messageParams.body) : null;
115
139
 
116
140
  if (chunks) {
117
141
  for (let i = 0; i < chunks.length; i++) {
118
- const chunkParams = { ...messageParams, body: chunks[i] };
119
- result = await this.twilioClient.messages.create(chunkParams);
120
- if (this.messageStorage && typeof this.messageStorage.saveMessage === 'function') {
121
- try {
122
- await this.messageStorage.saveMessage({
123
- ...messageData,
124
- body: chunks[i],
125
- code: formattedCode,
126
- from: formattedFrom,
127
- messageId: result.sid,
128
- provider: 'twilio',
129
- timestamp: new Date(),
130
- fromMe: true,
131
- processed: messageData.processed !== undefined ? messageData.processed : false,
132
- statusInfo: {
133
- status: result.status ? result.status.toLowerCase() : null,
134
- updatedAt: result.dateCreated || new Date()
135
- }
136
- });
137
- logger.info('[TwilioProvider] Message chunk persisted', { messageId: result.sid, chunk: i + 1, total: chunks.length });
138
- } catch (storageError) {
139
- logger.error('TwilioProvider storage failed:', storageError);
140
- }
141
- }
142
- if (i < chunks.length - 1) {
143
- await new Promise(resolve => setTimeout(resolve, 100));
144
- }
142
+ result = await this.twilioClient.messages.create({ ...messageParams, body: chunks[i] });
143
+ await saveMessage(chunks[i], result);
144
+ if (i < chunks.length - 1) await new Promise(r => setTimeout(r, 100));
145
145
  }
146
146
  } else {
147
- logger.info('[TwilioProvider] Sending message', messageParams);
148
- try {
149
- result = await this.twilioClient.messages.create(messageParams);
150
- } catch (error) {
151
- throw new Error(`Twilio send failed: ${error.message}`);
152
- }
153
- if (this.messageStorage && typeof this.messageStorage.saveMessage === 'function') {
154
- try {
155
- await this.messageStorage.saveMessage({
156
- ...messageData,
157
- code: formattedCode,
158
- from: formattedFrom,
159
- messageId: result.sid,
160
- provider: 'twilio',
161
- timestamp: new Date(),
162
- fromMe: true,
163
- processed: messageData.processed !== undefined ? messageData.processed : false,
164
- statusInfo: {
165
- status: result.status ? result.status.toLowerCase() : null,
166
- updatedAt: result.dateCreated || new Date()
167
- }
168
- });
169
- logger.info('[TwilioProvider] Message persisted successfully', { messageId: result.sid });
170
- } catch (storageError) {
171
- logger.error('TwilioProvider storage failed:', storageError);
172
- }
173
- }
147
+ result = await this.twilioClient.messages.create(messageParams);
148
+ await saveMessage(messageParams.body, result);
174
149
  }
175
150
 
176
151
  return {
@@ -232,61 +207,31 @@ class TwilioProvider extends MessageProvider {
232
207
  });
233
208
 
234
209
  const updateStatus = async (status, messageId = null, error = null) => {
235
- if (!scheduledMessage) return;
236
-
237
210
  const now = new Date();
238
- const errorCode = error?.code || error?.status || error?.errorCode || null;
239
- const errorMessage = error?.message || error?.statusMessage || null;
240
- const statusEntry = { status, at: now, errorCode, errorMessage };
241
- const baseUpdate = {
242
- status,
243
- lastStatus: status,
244
- lastStatusAt: now,
245
- errorCode,
246
- errorMessage
247
- };
248
-
249
- if (messageId) baseUpdate.wa_id = messageId;
250
-
251
- const ScheduledMessageModel = (() => {
252
- if (scheduledMessage.constructor && typeof scheduledMessage.constructor.updateOne === 'function') {
253
- return scheduledMessage.constructor;
254
- }
255
- try {
256
- return require('../models/agendaMessageModel').ScheduledMessage;
257
- } catch (e) {
258
- return null;
259
- }
260
- })();
261
-
262
- if (!ScheduledMessageModel) {
263
- logger.warn('[TwilioProvider] Scheduled message model unavailable for status update');
264
- return;
265
- }
266
-
267
- const query = (() => {
268
- if (scheduledMessage._id) return { _id: scheduledMessage._id };
269
- if (messageId) return { wa_id: messageId };
270
- if (scheduledMessage.wa_id) return { wa_id: scheduledMessage.wa_id };
271
- return null;
272
- })();
273
-
274
- if (!query) {
275
- logger.warn('[TwilioProvider] Scheduled message status update skipped: no identifier', {
276
- hasId: Boolean(scheduledMessage._id),
277
- messageId,
278
- existingWaId: scheduledMessage.wa_id
279
- });
280
- return;
281
- }
282
-
283
211
  try {
284
- await ScheduledMessageModel.updateOne(query, {
285
- $set: baseUpdate,
286
- $push: { statusHistory: statusEntry }
287
- });
288
- } catch (statusErr) {
289
- logger.warn('[TwilioProvider] Failed to update scheduled message status', statusErr?.message || statusErr);
212
+ await ScheduledMessage.updateOne(
213
+ { _id: scheduledMessage._id },
214
+ {
215
+ $set: {
216
+ status,
217
+ lastStatus: status,
218
+ lastStatusAt: now,
219
+ ...(messageId && { wa_id: messageId }),
220
+ errorCode: error?.code || null,
221
+ errorMessage: error?.message || null
222
+ },
223
+ $push: {
224
+ statusHistory: {
225
+ status,
226
+ at: now,
227
+ errorCode: error?.code || null,
228
+ errorMessage: error?.message || null
229
+ }
230
+ }
231
+ }
232
+ );
233
+ } catch (err) {
234
+ logger.warn('[TwilioProvider] Failed to update scheduled message status', err?.message);
290
235
  }
291
236
  };
292
237
 
@@ -319,8 +264,8 @@ class TwilioProvider extends MessageProvider {
319
264
  return true;
320
265
  }
321
266
 
322
- async prepareOutboundMedia(messageData, formattedCode) {
323
- const bucketName = runtimeConfig.get('AWS_S3_BUCKET_NAME') || process.env.AWS_S3_BUCKET_NAME;
267
+ async _prepareOutboundMedia(messageData, formattedCode) {
268
+ const bucketName = runtimeConfig.get('AWS_S3_BUCKET_NAME');
324
269
  const fileUrl = messageData.fileUrl;
325
270
 
326
271
  if (!fileUrl) {
@@ -346,7 +291,7 @@ class TwilioProvider extends MessageProvider {
346
291
  const response = await axios.get(fileUrl, { responseType: 'arraybuffer' });
347
292
  const buffer = Buffer.from(response.data);
348
293
  let contentType = messageData.contentType || response.headers['content-type'];
349
- const declaredType = typeof messageData.fileType === 'string' ? messageData.fileType : null;
294
+ const declaredType = messageData.fileType || null;
350
295
 
351
296
  if (!contentType) {
352
297
  const fallbackType = declaredType ? {
@@ -368,17 +313,15 @@ class TwilioProvider extends MessageProvider {
368
313
  });
369
314
  }
370
315
 
371
- const existingFileName = messageData.fileName || (() => {
372
- try {
373
- const parsed = new URL(fileUrl);
374
- const segments = parsed.pathname.split('/').filter(Boolean);
375
- return segments.length ? segments[segments.length - 1] : null;
376
- } catch (err) {
377
- return null;
316
+ let existingFileName = messageData.fileName;
317
+ if (!existingFileName) {
318
+ try {
319
+ existingFileName = new URL(fileUrl).pathname.split('/').pop() || null;
320
+ } catch {
321
+ existingFileName = null;
378
322
  }
379
- })();
380
-
381
- const baseName = existingFileName ? existingFileName.split('.').slice(0, -1).join('.') : `${mediaType}_${Date.now()}`;
323
+ }
324
+ const baseName = existingFileName?.split('.').slice(0, -1).join('.') || `${mediaType}_${Date.now()}`;
382
325
  const sanitizedBase = sanitizeMediaFilename(baseName) || `${mediaType}_${Date.now()}`;
383
326
  const extension = getFileExtension(contentType) || 'bin';
384
327
  const uploadId = uuidv4();
@@ -420,7 +363,7 @@ class TwilioProvider extends MessageProvider {
420
363
  }
421
364
  }
422
365
 
423
- splitMessageAtWordBoundaries(text, maxLength = 1600) {
366
+ _splitMessageAtWordBoundaries(text, maxLength = 1600) {
424
367
  if (!text || text.length <= maxLength) return [text];
425
368
  const chunks = [];
426
369
  let remaining = text;
@@ -492,15 +435,15 @@ class TwilioProvider extends MessageProvider {
492
435
  return null;
493
436
  }
494
437
 
495
- let textContent = this.extractTextFromTemplate(template);
438
+ let textContent = this._extractTextFromTemplate(template);
496
439
 
497
440
  if (!textContent) {
498
441
  logger.warn('[TwilioProvider] No text content found in template:', contentSid);
499
442
  return null;
500
443
  }
501
444
 
502
- if (variables && typeof variables === 'object' && Object.keys(variables).length > 0) {
503
- return this.renderTemplateWithVariables(textContent, variables);
445
+ if (variables && Object.keys(variables).length > 0) {
446
+ return this._renderTemplateWithVariables(textContent, variables);
504
447
  }
505
448
 
506
449
  return textContent.trim();
@@ -510,90 +453,42 @@ class TwilioProvider extends MessageProvider {
510
453
  }
511
454
  }
512
455
 
513
- extractTextFromTemplate(template) {
456
+ _extractTextFromTemplate(template) {
514
457
  const types = template.types || {};
515
458
 
516
- if (types['twilio/text']) {
517
- return types['twilio/text'].body || '';
518
- }
459
+ const appendItems = (text, items, formatter) => {
460
+ const formatted = (items || []).filter(i => i.title).map(formatter).join('\n');
461
+ return formatted ? text + (text ? '\n\n' : '') + formatted : text;
462
+ };
463
+
464
+ if (types['twilio/text']) return types['twilio/text'].body || '';
519
465
 
520
466
  if (types['twilio/quick-reply']) {
521
- const quickReply = types['twilio/quick-reply'];
522
- let text = quickReply.body || '';
523
-
524
- if (quickReply.actions && Array.isArray(quickReply.actions)) {
525
- const options = quickReply.actions
526
- .filter(action => action.title)
527
- .map(action => `• ${action.title}`)
528
- .join('\n');
529
- if (options) {
530
- text += (text ? '\n\n' : '') + options;
531
- }
532
- }
533
-
534
- return text;
467
+ const qr = types['twilio/quick-reply'];
468
+ return appendItems(qr.body || '', qr.actions, a => `• ${a.title}`);
535
469
  }
536
470
 
537
471
  if (types['twilio/list']) {
538
472
  const list = types['twilio/list'];
539
- let text = list.body || '';
540
-
541
- if (list.items && Array.isArray(list.items)) {
542
- const items = list.items
543
- .filter(item => item.title)
544
- .map((item, index) => `${index + 1}. ${item.title}`)
545
- .join('\n');
546
- if (items) {
547
- text += (text ? '\n\n' : '') + items;
548
- }
549
- }
550
-
551
- return text;
473
+ return appendItems(list.body || '', list.items, (item, i) => `${i + 1}. ${item.title}`);
552
474
  }
553
475
 
554
476
  if (types['twilio/button']) {
555
- const button = types['twilio/button'];
556
- let text = button.body || '';
557
-
558
- if (button.actions && Array.isArray(button.actions)) {
559
- const buttons = button.actions
560
- .filter(action => action.title)
561
- .map(action => `[${action.title}]`)
562
- .join(' ');
563
- if (buttons) {
564
- text += (text ? '\n\n' : '') + buttons;
565
- }
566
- }
567
-
568
- return text;
569
- }
570
-
571
- if (types['twilio/flows']) {
572
- return types['twilio/flows'].body || '';
573
- }
574
-
575
- if (types['twilio/media']) {
576
- return types['twilio/media'].caption || '';
477
+ const btn = types['twilio/button'];
478
+ const buttons = (btn.actions || []).filter(a => a.title).map(a => `[${a.title}]`).join(' ');
479
+ return buttons ? (btn.body || '') + ((btn.body && buttons) ? '\n\n' : '') + buttons : (btn.body || '');
577
480
  }
578
481
 
579
- for (const typeKey of Object.keys(types)) {
580
- const type = types[typeKey];
581
- if (type && typeof type === 'object' && type.body) {
582
- return type.body;
583
- }
584
- }
482
+ if (types['twilio/flows']) return types['twilio/flows'].body || '';
483
+ if (types['twilio/media']) return types['twilio/media'].caption || '';
585
484
 
586
- return '';
485
+ const fallback = Object.values(types).find(t => t?.body);
486
+ return fallback?.body || '';
587
487
  }
588
488
 
589
- renderTemplateWithVariables(templateBody, variables) {
590
- if (!templateBody || typeof templateBody !== 'string') {
591
- return '';
592
- }
593
-
594
- if (!variables || typeof variables !== 'object') {
595
- return templateBody;
596
- }
489
+ _renderTemplateWithVariables(templateBody, variables) {
490
+ if (!templateBody) return '';
491
+ if (!variables) return templateBody;
597
492
 
598
493
  try {
599
494
  let rendered = templateBody;
@@ -620,60 +515,26 @@ class TwilioProvider extends MessageProvider {
620
515
  }
621
516
  if (!sid) throw new Error('Content SID is required');
622
517
 
623
- try {
624
- const content = await this.twilioClient.content.v1.contents(sid).fetch();
625
- let processedApprovalRequest = null;
626
-
627
- try {
628
- const approvalRequest = await this.twilioClient.content.v1
629
- .contents(sid)
630
- .approvalFetch()
631
- .fetch();
632
-
633
- if (approvalRequest) {
634
- processedApprovalRequest = {
635
- sid: approvalRequest.sid,
636
- status: 'UNKNOWN'
637
- };
638
-
639
- if (approvalRequest.whatsapp) {
640
- processedApprovalRequest.status = approvalRequest.whatsapp.status?.toUpperCase() || 'UNKNOWN';
641
- processedApprovalRequest.category = approvalRequest.whatsapp.category;
642
- processedApprovalRequest.name = approvalRequest.whatsapp.name;
643
- processedApprovalRequest.rejectionReason = approvalRequest.whatsapp.rejection_reason;
644
- processedApprovalRequest.contentType = approvalRequest.whatsapp.content_type;
645
- } else {
646
- processedApprovalRequest.status = approvalRequest.status || 'PENDING';
647
- }
648
-
649
- if (processedApprovalRequest.status === 'approved') {
650
- processedApprovalRequest.status = 'APPROVED';
651
- } else if (processedApprovalRequest.status === 'rejected') {
652
- processedApprovalRequest.status = 'REJECTED';
653
- } else if (processedApprovalRequest.status === 'pending') {
654
- processedApprovalRequest.status = 'PENDING';
655
- }
656
-
657
- if (approvalRequest.dateCreated || approvalRequest.date_created) {
658
- processedApprovalRequest.dateCreated = approvalRequest.dateCreated || approvalRequest.date_created;
659
- }
518
+ const content = await this.twilioClient.content.v1.contents(sid).fetch();
519
+ let approvalRequest = null;
660
520
 
661
- if (approvalRequest.dateUpdated || approvalRequest.date_updated) {
662
- processedApprovalRequest.dateUpdated = approvalRequest.dateUpdated || approvalRequest.date_updated;
663
- }
664
- }
665
- } catch (approvalError) {
666
- logger.warn('Approval request fetch failed:', approvalError?.message || approvalError);
521
+ try {
522
+ const req = await this.twilioClient.content.v1.contents(sid).approvalFetch().fetch();
523
+ if (req) {
524
+ const wa = req.whatsapp;
525
+ approvalRequest = {
526
+ sid: req.sid,
527
+ status: (wa?.status || req.status || 'UNKNOWN').toUpperCase(),
528
+ ...(wa && { category: wa.category, name: wa.name, rejectionReason: wa.rejection_reason, contentType: wa.content_type }),
529
+ dateCreated: req.dateCreated || req.date_created,
530
+ dateUpdated: req.dateUpdated || req.date_updated
531
+ };
667
532
  }
668
-
669
- return {
670
- content,
671
- approvalRequest: processedApprovalRequest
672
- };
673
- } catch (error) {
674
- logger.error('Error checking approval status:', error);
675
- throw error;
533
+ } catch (err) {
534
+ logger.warn('Approval request fetch failed:', err?.message);
676
535
  }
536
+
537
+ return { content, approvalRequest };
677
538
  }
678
539
 
679
540
  async submitForApproval(contentSid, name, category) {
@@ -1,7 +1,12 @@
1
1
  const { TwilioProvider } = require('./TwilioProvider');
2
2
  const { BaileysProvider } = require('./BaileysProvider');
3
+ const { MessageProvider } = require('./MessageProvider');
4
+ const { createProvider, registerProvider } = require('./registry');
3
5
 
4
6
  module.exports = {
5
7
  TwilioProvider,
6
- BaileysProvider
8
+ BaileysProvider,
9
+ MessageProvider,
10
+ createProvider,
11
+ registerProvider
7
12
  };