@app-connect/core 1.7.8 → 1.7.11

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 (69) hide show
  1. package/connector/developerPortal.js +43 -0
  2. package/connector/proxy/index.js +10 -3
  3. package/connector/registry.js +8 -6
  4. package/handlers/admin.js +44 -21
  5. package/handlers/auth.js +97 -69
  6. package/handlers/calldown.js +10 -4
  7. package/handlers/contact.js +45 -112
  8. package/handlers/disposition.js +4 -142
  9. package/handlers/log.js +174 -259
  10. package/handlers/user.js +19 -6
  11. package/index.js +310 -122
  12. package/lib/analytics.js +3 -1
  13. package/lib/authSession.js +68 -0
  14. package/lib/callLogComposer.js +498 -420
  15. package/lib/errorHandler.js +206 -0
  16. package/lib/jwt.js +2 -0
  17. package/lib/logger.js +190 -0
  18. package/lib/oauth.js +21 -12
  19. package/lib/ringcentral.js +2 -10
  20. package/lib/sharedSMSComposer.js +471 -0
  21. package/mcp/SupportedPlatforms.md +12 -0
  22. package/mcp/lib/validator.js +91 -0
  23. package/mcp/mcpHandler.js +166 -0
  24. package/mcp/tools/checkAuthStatus.js +90 -0
  25. package/mcp/tools/collectAuthInfo.js +86 -0
  26. package/mcp/tools/createCallLog.js +299 -0
  27. package/mcp/tools/createMessageLog.js +283 -0
  28. package/mcp/tools/doAuth.js +185 -0
  29. package/mcp/tools/findContactByName.js +87 -0
  30. package/mcp/tools/findContactByPhone.js +96 -0
  31. package/mcp/tools/getCallLog.js +98 -0
  32. package/mcp/tools/getHelp.js +39 -0
  33. package/mcp/tools/getPublicConnectors.js +46 -0
  34. package/mcp/tools/index.js +58 -0
  35. package/mcp/tools/logout.js +63 -0
  36. package/mcp/tools/rcGetCallLogs.js +73 -0
  37. package/mcp/tools/setConnector.js +64 -0
  38. package/mcp/tools/updateCallLog.js +122 -0
  39. package/models/accountDataModel.js +34 -0
  40. package/models/cacheModel.js +3 -0
  41. package/package.json +6 -4
  42. package/releaseNotes.json +36 -0
  43. package/test/connector/registry.test.js +145 -0
  44. package/test/handlers/admin.test.js +583 -0
  45. package/test/handlers/auth.test.js +355 -0
  46. package/test/handlers/contact.test.js +852 -0
  47. package/test/handlers/log.test.js +872 -0
  48. package/test/lib/callLogComposer.test.js +1231 -0
  49. package/test/lib/debugTracer.test.js +328 -0
  50. package/test/lib/logger.test.js +206 -0
  51. package/test/lib/oauth.test.js +359 -0
  52. package/test/lib/ringcentral.test.js +473 -0
  53. package/test/lib/sharedSMSComposer.test.js +1084 -0
  54. package/test/lib/util.test.js +282 -0
  55. package/test/mcp/tools/collectAuthInfo.test.js +192 -0
  56. package/test/mcp/tools/createCallLog.test.js +412 -0
  57. package/test/mcp/tools/createMessageLog.test.js +580 -0
  58. package/test/mcp/tools/doAuth.test.js +363 -0
  59. package/test/mcp/tools/findContactByName.test.js +263 -0
  60. package/test/mcp/tools/findContactByPhone.test.js +284 -0
  61. package/test/mcp/tools/getCallLog.test.js +286 -0
  62. package/test/mcp/tools/getPublicConnectors.test.js +128 -0
  63. package/test/mcp/tools/logout.test.js +169 -0
  64. package/test/mcp/tools/setConnector.test.js +177 -0
  65. package/test/mcp/tools/updateCallLog.test.js +346 -0
  66. package/test/models/accountDataModel.test.js +98 -0
  67. package/test/models/dynamo/connectorSchema.test.js +189 -0
  68. package/test/models/models.test.js +539 -0
  69. package/test/setup.js +176 -176
@@ -26,7 +26,7 @@ const { LOG_DETAILS_FORMAT_TYPE } = require('./constants');
26
26
  * @param {string} params.result - Call result
27
27
  * @returns {Promise<string>} Composed log body
28
28
  */
29
- async function composeCallLog(params) {
29
+ function composeCallLog(params) {
30
30
  const {
31
31
  logFormat = LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT,
32
32
  existingBody = '',
@@ -45,8 +45,7 @@ async function composeCallLog(params) {
45
45
  ringSenseSummary,
46
46
  ringSenseAIScore,
47
47
  ringSenseBulletedSummary,
48
- ringSenseLink,
49
- platform
48
+ ringSenseLink
50
49
  } = params;
51
50
 
52
51
  let body = existingBody;
@@ -158,150 +157,161 @@ async function composeCallLog(params) {
158
157
  function upsertCallAgentNote({ body, note, logFormat }) {
159
158
  if (!note) return body;
160
159
 
161
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
162
- // HTML logFormat with proper Agent notes section handling
163
- const noteRegex = RegExp('<b>Agent notes</b>([\\s\\S]+?)Call details</b>');
164
- if (noteRegex.test(body)) {
165
- return body.replace(noteRegex, `<b>Agent notes</b><br>${note}<br><br><b>Call details</b>`);
166
- }
167
- return `<b>Agent notes</b><br>${note}<br><br><b>Call details</b><br>` + body;
168
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
169
- // Markdown logFormat with proper Agent notes section handling
170
- const noteRegex = /## Agent notes\n([\s\S]*?)\n## Call details/;
171
- if (noteRegex.test(body)) {
172
- return body.replace(noteRegex, `## Agent notes\n${note}\n\n## Call details`);
173
- }
174
- if (body.startsWith('## Call details')) {
175
- return `## Agent notes\n${note}\n\n` + body;
176
- }
177
- return `## Agent notes\n${note}\n\n## Call details\n` + body;
178
- } else {
179
- // Plain text logFormat - FIXED REGEX for multi-line notes with blank lines
180
- const noteRegex = /- (?:Note|Agent notes): ([\s\S]*?)(?=\n- [A-Z][a-zA-Z\s/]*:|\n$|$)/;
181
- if (noteRegex.test(body)) {
182
- return body.replace(noteRegex, `- Note: ${note}`);
183
- }
184
- return `- Note: ${note}\n` + body;
160
+ let noteRegex = null;
161
+ switch (logFormat) {
162
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
163
+ // HTML logFormat with proper Agent notes section handling
164
+ noteRegex = RegExp('<b>Agent notes</b>([\\s\\S]+?)Call details</b>');
165
+ if (noteRegex.test(body)) {
166
+ return body.replace(noteRegex, `<b>Agent notes</b><br>${note}<br><br><b>Call details</b>`);
167
+ }
168
+ return `<b>Agent notes</b><br>${note}<br><br><b>Call details</b><br>` + body;
169
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
170
+ // Markdown logFormat with proper Agent notes section handling
171
+ noteRegex = /## Agent notes\n([\s\S]*?)\n## Call details/;
172
+ if (noteRegex.test(body)) {
173
+ return body.replace(noteRegex, `## Agent notes\n${note}\n\n## Call details`);
174
+ }
175
+ if (body.startsWith('## Call details')) {
176
+ return `## Agent notes\n${note}\n\n` + body;
177
+ }
178
+ return `## Agent notes\n${note}\n\n## Call details\n` + body;
179
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
180
+ // Plain text logFormat - FIXED REGEX for multi-line notes with blank lines
181
+ noteRegex = /- (?:Note|Agent notes): ([\s\S]*?)(?=\n- [A-Z][a-zA-Z\s/]*:|\n$|$)/;
182
+ if (noteRegex.test(body)) {
183
+ return body.replace(noteRegex, `- Note: ${note}`);
184
+ }
185
+ return `- Note: ${note}\n` + body;
185
186
  }
186
187
  }
187
188
 
188
189
  function upsertCallSessionId({ body, id, logFormat }) {
189
190
  if (!id) return body;
190
191
 
191
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
192
- // More flexible regex that handles both <li> wrapped and unwrapped content
193
- const idRegex = /(?:<li>)?<b>Session Id<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
194
- if (idRegex.test(body)) {
195
- return body.replace(idRegex, `<li><b>Session Id</b>: ${id}</li>`);
196
- }
197
- return body + `<li><b>Session Id</b>: ${id}</li>`;
198
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
199
- // Markdown format: **Session Id**: value
200
- const sessionIdRegex = /\*\*Session Id\*\*: [^\n]*\n*/;
201
- if (sessionIdRegex.test(body)) {
202
- return body.replace(sessionIdRegex, `**Session Id**: ${id}\n`);
203
- }
204
- return body + `**Session Id**: ${id}\n`;
205
- } else {
206
- // Match Session Id field and any trailing newlines, replace with single newline
207
- const sessionIdRegex = /- Session Id: [^\n]*\n*/;
208
- if (sessionIdRegex.test(body)) {
209
- return body.replace(sessionIdRegex, `- Session Id: ${id}\n`);
210
- }
211
- return body + `- Session Id: ${id}\n`;
192
+ let idRegex = null;
193
+ switch (logFormat) {
194
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
195
+ // More flexible regex that handles both <li> wrapped and unwrapped content
196
+ idRegex = /(?:<li>)?<b>Session Id<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
197
+ if (idRegex.test(body)) {
198
+ return body.replace(idRegex, `<li><b>Session Id</b>: ${id}</li>`);
199
+ }
200
+ return body + `<li><b>Session Id</b>: ${id}</li>`;
201
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
202
+ // Markdown format: **Session Id**: value
203
+ idRegex = /\*\*Session Id\*\*: [^\n]*\n*/;
204
+ if (idRegex.test(body)) {
205
+ return body.replace(idRegex, `**Session Id**: ${id}\n`);
206
+ }
207
+ return body + `**Session Id**: ${id}\n`;
208
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
209
+ // Match Session Id field and any trailing newlines, replace with single newline
210
+ idRegex = /- Session Id: [^\n]*\n*/;
211
+ if (idRegex.test(body)) {
212
+ return body.replace(idRegex, `- Session Id: ${id}\n`);
213
+ }
214
+ return body + `- Session Id: ${id}\n`;
212
215
  }
213
216
  }
214
217
 
215
218
  function upsertRingCentralUserName({ body, userName, logFormat }) {
216
219
  if (!userName) return body;
217
220
 
218
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
219
- const userNameRegex = /(?:<li>)?<b>RingCentral user name<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
220
- const match = body.match(userNameRegex);
221
- if (match) {
222
- // Only replace if existing value is (pending...)
223
- if (match[1].trim() === '(pending...)') {
224
- return body.replace(userNameRegex, `<li><b>RingCentral user name</b>: ${userName}</li>`);
221
+ let userNameRegex = null;
222
+ let match = null;
223
+ switch (logFormat) {
224
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
225
+ userNameRegex = /(?:<li>)?<b>RingCentral user name<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
226
+ match = body.match(userNameRegex);
227
+ if (match) {
228
+ // Only replace if existing value is (pending...)
229
+ if (match[1].trim() === '(pending...)') {
230
+ return body.replace(userNameRegex, `<li><b>RingCentral user name</b>: ${userName}</li>`);
231
+ }
232
+ return body;
233
+ } else {
234
+ return body + `<li><b>RingCentral user name</b>: ${userName}</li>`;
235
+ }
236
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
237
+ userNameRegex = /\*\*RingCentral user name\*\*: ([^\n]*)\n*/i;
238
+ match = body.match(userNameRegex);
239
+ if (match) {
240
+ // Only replace if existing value is (pending...)
241
+ if (match[1].trim() === '(pending...)') {
242
+ return body.replace(userNameRegex, `**RingCentral user name**: ${userName}\n`);
243
+ }
244
+ return body;
245
+ } else {
246
+ return body + `**RingCentral user name**: ${userName}\n`;
247
+ }
248
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
249
+ userNameRegex = /- RingCentral user name: ([^\n]*)\n*/;
250
+ match = body.match(userNameRegex);
251
+ if (match) {
252
+ // Only replace if existing value is (pending...)
253
+ if (match[1].trim() === '(pending...)') {
254
+ return body.replace(userNameRegex, `- RingCentral user name: ${userName}\n`);
255
+ }
256
+ return body;
257
+ } else {
258
+ return body + `- RingCentral user name: ${userName}\n`;
225
259
  }
226
- return body;
227
- } else {
228
- return body + `<li><b>RingCentral user name</b>: ${userName}</li>`;
229
- }
230
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
231
- const userNameRegex = /\*\*RingCentral user name\*\*: ([^\n]*)\n*/i;
232
- const match = body.match(userNameRegex);
233
- if (match) {
234
- // Only replace if existing value is (pending...)
235
- if (match[1].trim() === '(pending...)') {
236
- return body.replace(userNameRegex, `**RingCentral user name**: ${userName}\n`);
237
- }
238
- return body;
239
- } else {
240
- return body + `**RingCentral user name**: ${userName}\n`;
241
- }
242
- } else {
243
- const userNameRegex = /- RingCentral user name: ([^\n]*)\n*/;
244
- const match = body.match(userNameRegex);
245
- if (match) {
246
- // Only replace if existing value is (pending...)
247
- if (match[1].trim() === '(pending...)') {
248
- return body.replace(userNameRegex, `- RingCentral user name: ${userName}\n`);
249
- }
250
- return body;
251
- } else {
252
- return body + `- RingCentral user name: ${userName}\n`;
253
- }
254
260
  }
255
261
  }
256
262
 
257
263
  function upsertRingCentralNumberAndExtension({ body, number, extension, logFormat }) {
258
264
  if (!number && !extension) return body;
259
265
 
260
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
261
- const numberAndExtensionRegex = /(?:<li>)?<b>RingCentral number and extension<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
262
- if (numberAndExtensionRegex.test(body)) {
263
- return body.replace(numberAndExtensionRegex, `<li><b>RingCentral number and extension</b>: ${number} ${extension}</li>`);
264
- }
265
- return body + `<li><b>RingCentral number and extension</b>: ${number} ${extension}</li>`;
266
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
267
- const numberAndExtensionRegex = /\*\*RingCentral number and extension\*\*: [^\n]*\n*/i;
268
- if (numberAndExtensionRegex.test(body)) {
269
- return body.replace(numberAndExtensionRegex, `**RingCentral number and extension**: ${number} ${extension}\n`);
270
- }
271
- return body + `**RingCentral number and extension**: ${number} ${extension}\n`;
272
- } else {
273
- const numberAndExtensionRegex = /- RingCentral number and extension: [^\n]*\n*/;
274
- if (numberAndExtensionRegex.test(body)) {
275
- return body.replace(numberAndExtensionRegex, `- RingCentral number and extension: ${number} ${extension}\n`);
276
- }
277
- return body + `- RingCentral number and extension: ${number} ${extension}\n`;
266
+ let numberAndExtensionRegex = null;
267
+ switch (logFormat) {
268
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
269
+ numberAndExtensionRegex = /(?:<li>)?<b>RingCentral number and extension<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
270
+ if (numberAndExtensionRegex.test(body)) {
271
+ return body.replace(numberAndExtensionRegex, `<li><b>RingCentral number and extension</b>: ${number} ${extension}</li>`);
272
+ }
273
+ return body + `<li><b>RingCentral number and extension</b>: ${number} ${extension}</li>`;
274
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
275
+ numberAndExtensionRegex = /\*\*RingCentral number and extension\*\*: [^\n]*\n*/i;
276
+ if (numberAndExtensionRegex.test(body)) {
277
+ return body.replace(numberAndExtensionRegex, `**RingCentral number and extension**: ${number} ${extension}\n`);
278
+ }
279
+ return body + `**RingCentral number and extension**: ${number} ${extension}\n`;
280
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
281
+ numberAndExtensionRegex = /- RingCentral number and extension: [^\n]*\n*/;
282
+ if (numberAndExtensionRegex.test(body)) {
283
+ return body.replace(numberAndExtensionRegex, `- RingCentral number and extension: ${number} ${extension}\n`);
284
+ }
285
+ return body + `- RingCentral number and extension: ${number} ${extension}\n`;
278
286
  }
279
287
  }
280
288
 
281
289
  function upsertCallSubject({ body, subject, logFormat }) {
282
290
  if (!subject) return body;
283
291
 
284
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
285
- // More flexible regex that handles both <li> wrapped and unwrapped content
286
- const subjectRegex = /(?:<li>)?<b>Summary<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
287
- if (subjectRegex.test(body)) {
288
- return body.replace(subjectRegex, `<li><b>Summary</b>: ${subject}</li>`);
289
- }
290
- return body + `<li><b>Summary</b>: ${subject}</li>`;
291
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
292
- // Markdown format: **Summary**: value
293
- const subjectRegex = /\*\*Summary\*\*: [^\n]*\n*/;
294
- if (subjectRegex.test(body)) {
295
- return body.replace(subjectRegex, `**Summary**: ${subject}\n`);
296
- }
297
- return body + `**Summary**: ${subject}\n`;
298
- } else {
299
- // Match Summary field and any trailing newlines, replace with single newline
300
- const subjectRegex = /- Summary: [^\n]*\n*/;
301
- if (subjectRegex.test(body)) {
302
- return body.replace(subjectRegex, `- Summary: ${subject}\n`);
303
- }
304
- return body + `- Summary: ${subject}\n`;
292
+ let subjectRegex = null;
293
+ switch (logFormat) {
294
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
295
+ // More flexible regex that handles both <li> wrapped and unwrapped content
296
+ subjectRegex = /(?:<li>)?<b>Summary<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
297
+ if (subjectRegex.test(body)) {
298
+ return body.replace(subjectRegex, `<li><b>Summary</b>: ${subject}</li>`);
299
+ }
300
+ return body + `<li><b>Summary</b>: ${subject}</li>`;
301
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
302
+ // Markdown format: **Summary**: value
303
+ subjectRegex = /\*\*Summary\*\*: [^\n]*\n*/;
304
+ if (subjectRegex.test(body)) {
305
+ return body.replace(subjectRegex, `**Summary**: ${subject}\n`);
306
+ }
307
+ return body + `**Summary**: ${subject}\n`;
308
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
309
+ // Match Summary field and any trailing newlines, replace with single newline
310
+ subjectRegex = /- Summary: [^\n]*\n*/;
311
+ if (subjectRegex.test(body)) {
312
+ return body.replace(subjectRegex, `- Summary: ${subject}\n`);
313
+ }
314
+ return body + `- Summary: ${subject}\n`;
305
315
  }
306
316
  }
307
317
 
@@ -311,30 +321,35 @@ function upsertContactPhoneNumber({ body, phoneNumber, direction, logFormat }) {
311
321
  const label = direction === 'Outbound' ? 'Recipient' : 'Caller';
312
322
  let result = body;
313
323
 
314
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
315
- // More flexible regex that handles both <li> wrapped and unwrapped content
316
- const phoneNumberRegex = new RegExp(`(?:<li>)?<b>${label} phone number</b>:\\s*([^<\\n]+)(?:</li>|(?=<|$))`, 'i');
317
- if (phoneNumberRegex.test(result)) {
318
- result = result.replace(phoneNumberRegex, `<li><b>${label} phone number</b>: ${phoneNumber}</li>`);
319
- } else {
320
- result += `<li><b>${label} phone number</b>: ${phoneNumber}</li>`;
321
- }
322
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
323
- // Markdown format: **Contact Number**: value
324
- const phoneNumberRegex = /\*\*Contact Number\*\*: [^\n]*\n*/;
325
- if (phoneNumberRegex.test(result)) {
326
- result = result.replace(phoneNumberRegex, `**Contact Number**: ${phoneNumber}\n`);
327
- } else {
328
- result += `**Contact Number**: ${phoneNumber}\n`;
329
- }
330
- } else {
331
- // More flexible regex that handles both with and without newlines
332
- const phoneNumberRegex = /- Contact Number: ([^\n-]+)(?=\n-|\n|$)/;
333
- if (phoneNumberRegex.test(result)) {
334
- result = result.replace(phoneNumberRegex, `- Contact Number: ${phoneNumber}\n`);
335
- } else {
336
- result += `- Contact Number: ${phoneNumber}\n`;
337
- }
324
+ let phoneNumberRegex = null;
325
+ switch (logFormat) {
326
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
327
+ // More flexible regex that handles both <li> wrapped and unwrapped content
328
+ phoneNumberRegex = new RegExp(`(?:<li>)?<b>${label} phone number</b>:\\s*([^<\\n]+)(?:</li>|(?=<|$))`, 'i');
329
+ if (phoneNumberRegex.test(result)) {
330
+ result = result.replace(phoneNumberRegex, `<li><b>${label} phone number</b>: ${phoneNumber}</li>`);
331
+ } else {
332
+ result += `<li><b>${label} phone number</b>: ${phoneNumber}</li>`;
333
+ }
334
+ break;
335
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
336
+ // Markdown format: **Contact Number**: value
337
+ phoneNumberRegex = /\*\*Contact Number\*\*: [^\n]*\n*/;
338
+ if (phoneNumberRegex.test(result)) {
339
+ result = result.replace(phoneNumberRegex, `**Contact Number**: ${phoneNumber}\n`);
340
+ } else {
341
+ result += `**Contact Number**: ${phoneNumber}\n`;
342
+ }
343
+ break;
344
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
345
+ // More flexible regex that handles both with and without newlines
346
+ phoneNumberRegex = /- Contact Number: ([^\n-]+)(?=\n-|\n|$)/;
347
+ if (phoneNumberRegex.test(result)) {
348
+ result = result.replace(phoneNumberRegex, `- Contact Number: ${phoneNumber}\n`);
349
+ } else {
350
+ result += `- Contact Number: ${phoneNumber}\n`;
351
+ }
352
+ break;
338
353
  }
339
354
  return result;
340
355
  }
@@ -357,30 +372,35 @@ function upsertCallDateTime({ body, startTime, timezoneOffset, logFormat, logDat
357
372
  const formattedDateTime = momentTime.format(logDateFormat || 'YYYY-MM-DD hh:mm:ss A');
358
373
  let result = body;
359
374
 
360
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
361
- // More flexible regex that handles both <li> wrapped and unwrapped content
362
- const dateTimeRegex = /(?:<li>)?<b>Date\/time<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
363
- if (dateTimeRegex.test(result)) {
364
- result = result.replace(dateTimeRegex, `<li><b>Date/time</b>: ${formattedDateTime}</li>`);
365
- } else {
366
- result += `<li><b>Date/time</b>: ${formattedDateTime}</li>`;
367
- }
368
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
369
- // Markdown format: **Date/Time**: value
370
- const dateTimeRegex = /\*\*Date\/Time\*\*: [^\n]*\n*/;
371
- if (dateTimeRegex.test(result)) {
372
- result = result.replace(dateTimeRegex, `**Date/Time**: ${formattedDateTime}\n`);
373
- } else {
374
- result += `**Date/Time**: ${formattedDateTime}\n`;
375
- }
376
- } else {
377
- // Handle duplicated Date/Time entries and match complete date/time values
378
- const dateTimeRegex = /^(- Date\/Time:).*$/m;
379
- if (dateTimeRegex.test(result)) {
380
- result = result.replace(dateTimeRegex, `- Date/Time: ${formattedDateTime}`);
381
- } else {
382
- result += `- Date/Time: ${formattedDateTime}\n`;
383
- }
375
+ let dateTimeRegex = null;
376
+ switch (logFormat) {
377
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
378
+ // More flexible regex that handles both <li> wrapped and unwrapped content
379
+ dateTimeRegex = /(?:<li>)?<b>Date\/time<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
380
+ if (dateTimeRegex.test(result)) {
381
+ result = result.replace(dateTimeRegex, `<li><b>Date/time</b>: ${formattedDateTime}</li>`);
382
+ } else {
383
+ result += `<li><b>Date/time</b>: ${formattedDateTime}</li>`;
384
+ }
385
+ break;
386
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
387
+ // Markdown format: **Date/Time**: value
388
+ dateTimeRegex = /\*\*Date\/Time\*\*: [^\n]*\n*/;
389
+ if (dateTimeRegex.test(result)) {
390
+ result = result.replace(dateTimeRegex, `**Date/Time**: ${formattedDateTime}\n`);
391
+ } else {
392
+ result += `**Date/Time**: ${formattedDateTime}\n`;
393
+ }
394
+ break;
395
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
396
+ // Handle duplicated Date/Time entries and match complete date/time values
397
+ dateTimeRegex = /^(- Date\/Time:).*$/m;
398
+ if (dateTimeRegex.test(result)) {
399
+ result = result.replace(dateTimeRegex, `- Date/Time: ${formattedDateTime}`);
400
+ } else {
401
+ result += `- Date/Time: ${formattedDateTime}\n`;
402
+ }
403
+ break;
384
404
  }
385
405
  return result;
386
406
  }
@@ -390,31 +410,35 @@ function upsertCallDuration({ body, duration, logFormat }) {
390
410
 
391
411
  const formattedDuration = secondsToHoursMinutesSeconds(duration);
392
412
  let result = body;
393
-
394
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
395
- // More flexible regex that handles both <li> wrapped and unwrapped content
396
- const durationRegex = /(?:<li>)?<b>Duration<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
397
- if (durationRegex.test(result)) {
398
- result = result.replace(durationRegex, `<li><b>Duration</b>: ${formattedDuration}</li>`);
399
- } else {
400
- result += `<li><b>Duration</b>: ${formattedDuration}</li>`;
401
- }
402
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
403
- // Markdown format: **Duration**: value
404
- const durationRegex = /\*\*Duration\*\*: [^\n]*\n*/;
405
- if (durationRegex.test(result)) {
406
- result = result.replace(durationRegex, `**Duration**: ${formattedDuration}\n`);
407
- } else {
408
- result += `**Duration**: ${formattedDuration}\n`;
409
- }
410
- } else {
411
- // More flexible regex that handles both with and without newlines
412
- const durationRegex = /- Duration: ([^\n-]+)(?=\n-|\n|$)/;
413
- if (durationRegex.test(result)) {
414
- result = result.replace(durationRegex, `- Duration: ${formattedDuration}`);
415
- } else {
416
- result += `- Duration: ${formattedDuration}\n`;
417
- }
413
+ let durationRegex = null;
414
+ switch (logFormat) {
415
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
416
+ // More flexible regex that handles both <li> wrapped and unwrapped content
417
+ durationRegex = /(?:<li>)?<b>Duration<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
418
+ if (durationRegex.test(result)) {
419
+ result = result.replace(durationRegex, `<li><b>Duration</b>: ${formattedDuration}</li>`);
420
+ } else {
421
+ result += `<li><b>Duration</b>: ${formattedDuration}</li>`;
422
+ }
423
+ break;
424
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
425
+ // Markdown format: **Duration**: value
426
+ durationRegex = /\*\*Duration\*\*: [^\n]*\n*/;
427
+ if (durationRegex.test(result)) {
428
+ result = result.replace(durationRegex, `**Duration**: ${formattedDuration}\n`);
429
+ } else {
430
+ result += `**Duration**: ${formattedDuration}\n`;
431
+ }
432
+ break;
433
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
434
+ // More flexible regex that handles both with and without newlines
435
+ durationRegex = /- Duration: ([^\n-]+)(?=\n-|\n|$)/;
436
+ if (durationRegex.test(result)) {
437
+ result = result.replace(durationRegex, `- Duration: ${formattedDuration}`);
438
+ } else {
439
+ result += `- Duration: ${formattedDuration}\n`;
440
+ }
441
+ break;
418
442
  }
419
443
  return result;
420
444
  }
@@ -424,30 +448,35 @@ function upsertCallResult({ body, result, logFormat }) {
424
448
 
425
449
  let bodyResult = body;
426
450
 
427
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
428
- // More flexible regex that handles both <li> wrapped and unwrapped content
429
- const resultRegex = /(?:<li>)?<b>Result<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
430
- if (resultRegex.test(bodyResult)) {
431
- bodyResult = bodyResult.replace(resultRegex, `<li><b>Result</b>: ${result}</li>`);
432
- } else {
433
- bodyResult += `<li><b>Result</b>: ${result}</li>`;
434
- }
435
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
436
- // Markdown format: **Result**: value
437
- const resultRegex = /\*\*Result\*\*: [^\n]*\n*/;
438
- if (resultRegex.test(bodyResult)) {
439
- bodyResult = bodyResult.replace(resultRegex, `**Result**: ${result}\n`);
440
- } else {
441
- bodyResult += `**Result**: ${result}\n`;
442
- }
443
- } else {
444
- // More flexible regex that handles both with and without newlines
445
- const resultRegex = /- Result: ([^\n-]+)(?=\n-|\n|$)/;
446
- if (resultRegex.test(bodyResult)) {
447
- bodyResult = bodyResult.replace(resultRegex, `- Result: ${result}`);
448
- } else {
449
- bodyResult += `- Result: ${result}\n`;
450
- }
451
+ let resultRegex = null;
452
+ switch (logFormat) {
453
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
454
+ // More flexible regex that handles both <li> wrapped and unwrapped content
455
+ resultRegex = /(?:<li>)?<b>Result<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
456
+ if (resultRegex.test(bodyResult)) {
457
+ bodyResult = bodyResult.replace(resultRegex, `<li><b>Result</b>: ${result}</li>`);
458
+ } else {
459
+ bodyResult += `<li><b>Result</b>: ${result}</li>`;
460
+ }
461
+ break;
462
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
463
+ // Markdown format: **Result**: value
464
+ resultRegex = /\*\*Result\*\*: [^\n]*\n*/;
465
+ if (resultRegex.test(bodyResult)) {
466
+ bodyResult = bodyResult.replace(resultRegex, `**Result**: ${result}\n`);
467
+ } else {
468
+ bodyResult += `**Result**: ${result}\n`;
469
+ }
470
+ break;
471
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
472
+ // More flexible regex that handles both with and without newlines
473
+ resultRegex = /- Result: ([^\n-]+)(?=\n-|\n|$)/;
474
+ if (resultRegex.test(bodyResult)) {
475
+ bodyResult = bodyResult.replace(resultRegex, `- Result: ${result}`);
476
+ } else {
477
+ bodyResult += `- Result: ${result}\n`;
478
+ }
479
+ break;
451
480
  }
452
481
  return bodyResult;
453
482
  }
@@ -456,11 +485,12 @@ function upsertCallRecording({ body, recordingLink, logFormat }) {
456
485
  if (!recordingLink) return body;
457
486
 
458
487
  let result = body;
488
+ let recordingLinkRegex = null;
459
489
 
460
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
461
- // More flexible regex that handles both <li> wrapped and unwrapped content, and existing <a> anchors
462
- const recordingLinkRegex = /(?:<li>)?<b>Call recording link<\/b>:\s*(?:<a[^>]*>[^<]*<\/a>|[^<]+)(?:<\/li>|(?=<|$))/i;
463
- if (recordingLink) {
490
+ switch (logFormat) {
491
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
492
+ // More flexible regex that handles both <li> wrapped and unwrapped content, and existing <a> anchors
493
+ recordingLinkRegex = /(?:<li>)?<b>Call recording link<\/b>:\s*(?:<a[^>]*>[^<]*<\/a>|[^<]+)(?:<\/li>|(?=<|$))/i;
464
494
  if (recordingLinkRegex.test(result)) {
465
495
  if (recordingLink.startsWith('http')) {
466
496
  result = result.replace(recordingLinkRegex, `<li><b>Call recording link</b>: <a target="_blank" href="${recordingLink}">open</a></li>`);
@@ -480,26 +510,28 @@ function upsertCallRecording({ body, recordingLink, logFormat }) {
480
510
  result = result.replace('</ul>', `${text}</ul>`);
481
511
  }
482
512
  }
483
- }
484
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
485
- // Markdown format: **Call recording link**: value
486
- const recordingLinkRegex = /\*\*Call recording link\*\*: [^\n]*\n*/;
487
- if (recordingLinkRegex.test(result)) {
488
- result = result.replace(recordingLinkRegex, `**Call recording link**: ${recordingLink}\n`);
489
- } else {
490
- result += `**Call recording link**: ${recordingLink}\n`;
491
- }
492
- } else {
493
- // Match recording link field and any trailing content, replace with single newline
494
- const recordingLinkRegex = /- Call recording link: [^\n]*\n*/;
495
- if (recordingLinkRegex.test(result)) {
496
- result = result.replace(recordingLinkRegex, `- Call recording link: ${recordingLink}\n`);
497
- } else {
498
- if (result && !result.endsWith('\n')) {
499
- result += '\n';
513
+ break;
514
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
515
+ // Markdown format: **Call recording link**: value
516
+ recordingLinkRegex = /\*\*Call recording link\*\*: [^\n]*\n*/;
517
+ if (recordingLinkRegex.test(result)) {
518
+ result = result.replace(recordingLinkRegex, `**Call recording link**: ${recordingLink}\n`);
519
+ } else {
520
+ result += `**Call recording link**: ${recordingLink}\n`;
500
521
  }
501
- result += `- Call recording link: ${recordingLink}\n`;
502
- }
522
+ break;
523
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
524
+ // Match recording link field and any trailing content, replace with single newline
525
+ recordingLinkRegex = /- Call recording link: [^\n]*\n*/;
526
+ if (recordingLinkRegex.test(result)) {
527
+ result = result.replace(recordingLinkRegex, `- Call recording link: ${recordingLink}\n`);
528
+ } else {
529
+ if (result && !result.endsWith('\n')) {
530
+ result += '\n';
531
+ }
532
+ result += `- Call recording link: ${recordingLink}\n`;
533
+ }
534
+ break;
503
535
  }
504
536
  return result;
505
537
  }
@@ -509,30 +541,35 @@ function upsertAiNote({ body, aiNote, logFormat }) {
509
541
 
510
542
  const clearedAiNote = aiNote.replace(/\n+$/, '');
511
543
  let result = body;
512
-
513
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
514
- const formattedAiNote = clearedAiNote.replace(/(?:\r\n|\r|\n)/g, '<br>');
515
- const aiNoteRegex = /<div><b>AI Note<\/b><br>(.+?)<\/div>/;
516
- if (aiNoteRegex.test(result)) {
517
- result = result.replace(aiNoteRegex, `<div><b>AI Note</b><br>${formattedAiNote}</div>`);
518
- } else {
519
- result += `<div><b>AI Note</b><br>${formattedAiNote}</div><br>`;
520
- }
521
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
522
- // Markdown format: ### AI Note
523
- const aiNoteRegex = /### AI Note\n([\s\S]*?)(?=\n### |\n$|$)/;
524
- if (aiNoteRegex.test(result)) {
525
- result = result.replace(aiNoteRegex, `### AI Note\n${clearedAiNote}\n`);
526
- } else {
527
- result += `### AI Note\n${clearedAiNote}\n`;
528
- }
529
- } else {
530
- const aiNoteRegex = /- AI Note:([\s\S]*?)--- END/;
531
- if (aiNoteRegex.test(result)) {
532
- result = result.replace(aiNoteRegex, `- AI Note:\n${clearedAiNote}\n--- END`);
533
- } else {
534
- result += `\n- AI Note:\n${clearedAiNote}\n--- END\n`;
535
- }
544
+ let aiNoteRegex = null;
545
+
546
+ switch (logFormat) {
547
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
548
+ const formattedAiNote = clearedAiNote.replace(/(?:\r\n|\r|\n)/g, '<br>');
549
+ aiNoteRegex = /<div><b>AI Note<\/b><br>(.+?)<\/div>/;
550
+ if (aiNoteRegex.test(result)) {
551
+ result = result.replace(aiNoteRegex, `<div><b>AI Note</b><br>${formattedAiNote}</div>`);
552
+ } else {
553
+ result += `<div><b>AI Note</b><br>${formattedAiNote}</div><br>`;
554
+ }
555
+ break;
556
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
557
+ // Markdown format: ### AI Note
558
+ aiNoteRegex = /### AI Note\n([\s\S]*?)(?=\n### |\n$|$)/;
559
+ if (aiNoteRegex.test(result)) {
560
+ result = result.replace(aiNoteRegex, `### AI Note\n${clearedAiNote}\n`);
561
+ } else {
562
+ result += `### AI Note\n${clearedAiNote}\n`;
563
+ }
564
+ break;
565
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
566
+ aiNoteRegex = /- AI Note:([\s\S]*?)--- END/;
567
+ if (aiNoteRegex.test(result)) {
568
+ result = result.replace(aiNoteRegex, `- AI Note:\n${clearedAiNote}\n--- END`);
569
+ } else {
570
+ result += `\n- AI Note:\n${clearedAiNote}\n--- END\n`;
571
+ }
572
+ break;
536
573
  }
537
574
  return result;
538
575
  }
@@ -541,30 +578,35 @@ function upsertTranscript({ body, transcript, logFormat }) {
541
578
  if (!transcript) return body;
542
579
 
543
580
  let result = body;
544
-
545
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
546
- const formattedTranscript = transcript.replace(/(?:\r\n|\r|\n)/g, '<br>');
547
- const transcriptRegex = /<div><b>Transcript<\/b><br>(.+?)<\/div>/;
548
- if (transcriptRegex.test(result)) {
549
- result = result.replace(transcriptRegex, `<div><b>Transcript</b><br>${formattedTranscript}</div>`);
550
- } else {
551
- result += `<div><b>Transcript</b><br>${formattedTranscript}</div><br>`;
552
- }
553
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
554
- // Markdown format: ### Transcript
555
- const transcriptRegex = /### Transcript\n([\s\S]*?)(?=\n### |\n$|$)/;
556
- if (transcriptRegex.test(result)) {
557
- result = result.replace(transcriptRegex, `### Transcript\n${transcript}\n`);
558
- } else {
559
- result += `### Transcript\n${transcript}\n`;
560
- }
561
- } else {
562
- const transcriptRegex = /- Transcript:([\s\S]*?)--- END/;
563
- if (transcriptRegex.test(result)) {
564
- result = result.replace(transcriptRegex, `- Transcript:\n${transcript}\n--- END`);
565
- } else {
566
- result += `\n- Transcript:\n${transcript}\n--- END\n`;
567
- }
581
+ let transcriptRegex = null;
582
+
583
+ switch (logFormat) {
584
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
585
+ const formattedTranscript = transcript.replace(/(?:\r\n|\r|\n)/g, '<br>');
586
+ transcriptRegex = /<div><b>Transcript<\/b><br>(.+?)<\/div>/;
587
+ if (transcriptRegex.test(result)) {
588
+ result = result.replace(transcriptRegex, `<div><b>Transcript</b><br>${formattedTranscript}</div>`);
589
+ } else {
590
+ result += `<div><b>Transcript</b><br>${formattedTranscript}</div><br>`;
591
+ }
592
+ break;
593
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
594
+ // Markdown format: ### Transcript
595
+ transcriptRegex = /### Transcript\n([\s\S]*?)(?=\n### |\n$|$)/;
596
+ if (transcriptRegex.test(result)) {
597
+ result = result.replace(transcriptRegex, `### Transcript\n${transcript}\n`);
598
+ } else {
599
+ result += `### Transcript\n${transcript}\n`;
600
+ }
601
+ break;
602
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
603
+ transcriptRegex = /- Transcript:([\s\S]*?)--- END/;
604
+ if (transcriptRegex.test(result)) {
605
+ result = result.replace(transcriptRegex, `- Transcript:\n${transcript}\n--- END`);
606
+ } else {
607
+ result += `\n- Transcript:\n${transcript}\n--- END\n`;
608
+ }
609
+ break;
568
610
  }
569
611
  return result;
570
612
  }
@@ -617,28 +659,34 @@ function upsertLegs({ body, legs, logFormat }) {
617
659
 
618
660
  let result = body;
619
661
  let legsJourney = getLegsJourney(legs);
620
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
621
- legsJourney = legsJourney.replace(/(?:\r\n|\r|\n)/g, '<br>');
622
- const legsRegex = /<div><b>Call journey<\/b><br>(.+?)<\/div>/;
623
- if (legsRegex.test(result)) {
624
- result = result.replace(legsRegex, `<div><b>Call journey</b><br>${legsJourney}</div>`);
625
- } else {
626
- result += `<div><b>Call journey</b><br>${legsJourney}</div>`;
627
- }
628
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
629
- const legsRegex = /### Call journey\n([\s\S]*?)(?=\n### |\n$|$)/;
630
- if (legsRegex.test(result)) {
631
- result = result.replace(legsRegex, `### Call journey\n${legsJourney}\n`);
632
- } else {
633
- result += `### Call journey\n${legsJourney}\n`;
634
- }
635
- } else {
636
- const legsRegex = /- Call journey:([\s\S]*?)--- JOURNEY END/;
637
- if (legsRegex.test(result)) {
638
- result = result.replace(legsRegex, `- Call journey:\n${legsJourney}\n--- JOURNEY END`);
639
- } else {
640
- result += `- Call journey:\n${legsJourney}\n--- JOURNEY END\n`;
641
- }
662
+ let legsRegex = null;
663
+
664
+ switch (logFormat) {
665
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
666
+ legsJourney = legsJourney.replace(/(?:\r\n|\r|\n)/g, '<br>');
667
+ legsRegex = /<div><b>Call journey<\/b><br>(.+?)<\/div>/;
668
+ if (legsRegex.test(result)) {
669
+ result = result.replace(legsRegex, `<div><b>Call journey</b><br>${legsJourney}</div>`);
670
+ } else {
671
+ result += `<div><b>Call journey</b><br>${legsJourney}</div>`;
672
+ }
673
+ break;
674
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
675
+ legsRegex = /### Call journey\n([\s\S]*?)(?=\n### |\n$|$)/;
676
+ if (legsRegex.test(result)) {
677
+ result = result.replace(legsRegex, `### Call journey\n${legsJourney}\n`);
678
+ } else {
679
+ result += `### Call journey\n${legsJourney}\n`;
680
+ }
681
+ break;
682
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
683
+ legsRegex = /- Call journey:([\s\S]*?)--- JOURNEY END/;
684
+ if (legsRegex.test(result)) {
685
+ result = result.replace(legsRegex, `- Call journey:\n${legsJourney}\n--- JOURNEY END`);
686
+ } else {
687
+ result += `- Call journey:\n${legsJourney}\n--- JOURNEY END\n`;
688
+ }
689
+ break;
642
690
  }
643
691
 
644
692
  return result;
@@ -649,28 +697,34 @@ function upsertRingSenseTranscript({ body, transcript, logFormat }) {
649
697
 
650
698
  let result = body;
651
699
  const clearedTranscript = transcript.replace(/\n+$/, '');
652
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
653
- const formattedTranscript = clearedTranscript.replace(/(?:\r\n|\r|\n)/g, '<br>');
654
- const transcriptRegex = /<div><b>RingSense transcript<\/b><br>(.+?)<\/div>/;
655
- if (transcriptRegex.test(result)) {
656
- result = result.replace(transcriptRegex, `<div><b>RingSense transcript</b><br>${formattedTranscript}</div>`);
657
- } else {
658
- result += `<div><b>RingSense transcript</b><br>${formattedTranscript}</div>`;
659
- }
660
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
661
- const transcriptRegex = /### RingSense transcript\n([\s\S]*?)(?=\n### |\n$|$)/;
662
- if (transcriptRegex.test(result)) {
663
- result = result.replace(transcriptRegex, `### RingSense transcript\n${clearedTranscript}\n`);
664
- } else {
665
- result += `### RingSense transcript\n${clearedTranscript}\n`;
666
- }
667
- } else {
668
- const transcriptRegex = /- RingSense transcript:([\s\S]*?)--- END/;
669
- if (transcriptRegex.test(result)) {
670
- result = result.replace(transcriptRegex, `- RingSense transcript:\n${clearedTranscript}\n--- END`);
671
- } else {
672
- result += `\n- RingSense transcript:\n${clearedTranscript}\n--- END\n`;
673
- }
700
+ let transcriptRegex = null;
701
+
702
+ switch (logFormat) {
703
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
704
+ const formattedTranscript = clearedTranscript.replace(/(?:\r\n|\r|\n)/g, '<br>');
705
+ transcriptRegex = /<div><b>RingSense transcript<\/b><br>(.+?)<\/div>/;
706
+ if (transcriptRegex.test(result)) {
707
+ result = result.replace(transcriptRegex, `<div><b>RingSense transcript</b><br>${formattedTranscript}</div>`);
708
+ } else {
709
+ result += `<div><b>RingSense transcript</b><br>${formattedTranscript}</div>`;
710
+ }
711
+ break;
712
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
713
+ transcriptRegex = /### RingSense transcript\n([\s\S]*?)(?=\n### |\n$|$)/;
714
+ if (transcriptRegex.test(result)) {
715
+ result = result.replace(transcriptRegex, `### RingSense transcript\n${clearedTranscript}\n`);
716
+ } else {
717
+ result += `### RingSense transcript\n${clearedTranscript}\n`;
718
+ }
719
+ break;
720
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
721
+ transcriptRegex = /- RingSense transcript:([\s\S]*?)--- END/;
722
+ if (transcriptRegex.test(result)) {
723
+ result = result.replace(transcriptRegex, `- RingSense transcript:\n${clearedTranscript}\n--- END`);
724
+ } else {
725
+ result += `\n- RingSense transcript:\n${clearedTranscript}\n--- END\n`;
726
+ }
727
+ break;
674
728
  }
675
729
  return result;
676
730
  }
@@ -681,28 +735,34 @@ function upsertRingSenseSummary({ body, summary, logFormat }) {
681
735
  let result = body;
682
736
  // remove new line in last line of summary
683
737
  const clearedSummary = summary.replace(/\n+$/, '');
684
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
685
- const summaryRegex = /<div><b>RingSense summary<\/b><br>(.+?)<\/div>/;
686
- const formattedSummary = clearedSummary.replace(/(?:\r\n|\r|\n)/g, '<br>');
687
- if (summaryRegex.test(result)) {
688
- result = result.replace(summaryRegex, `<div><b>RingSense summary</b><br>${formattedSummary}</div>`);
689
- } else {
690
- result += `<div><b>RingSense summary</b><br>${formattedSummary}</div>`;
691
- }
692
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
693
- const summaryRegex = /### RingSense summary\n([\s\S]*?)(?=\n### |\n$|$)/;
694
- if (summaryRegex.test(result)) {
695
- result = result.replace(summaryRegex, `### RingSense summary\n${summary}\n`);
696
- } else {
697
- result += `### RingSense summary\n${summary}\n`;
698
- }
699
- } else {
700
- const summaryRegex = /- RingSense summary:([\s\S]*?)--- END/;
701
- if (summaryRegex.test(result)) {
702
- result = result.replace(summaryRegex, `- RingSense summary:\n${summary}\n--- END`);
703
- } else {
704
- result += `\n- RingSense summary:\n${summary}\n--- END\n`;
705
- }
738
+ let summaryRegex = null;
739
+
740
+ switch (logFormat) {
741
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
742
+ summaryRegex = /<div><b>RingSense summary<\/b><br>(.+?)<\/div>/;
743
+ const formattedSummary = clearedSummary.replace(/(?:\r\n|\r|\n)/g, '<br>');
744
+ if (summaryRegex.test(result)) {
745
+ result = result.replace(summaryRegex, `<div><b>RingSense summary</b><br>${formattedSummary}</div>`);
746
+ } else {
747
+ result += `<div><b>RingSense summary</b><br>${formattedSummary}</div>`;
748
+ }
749
+ break;
750
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
751
+ summaryRegex = /### RingSense summary\n([\s\S]*?)(?=\n### |\n$|$)/;
752
+ if (summaryRegex.test(result)) {
753
+ result = result.replace(summaryRegex, `### RingSense summary\n${summary}\n`);
754
+ } else {
755
+ result += `### RingSense summary\n${summary}\n`;
756
+ }
757
+ break;
758
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
759
+ summaryRegex = /- RingSense summary:([\s\S]*?)--- END/;
760
+ if (summaryRegex.test(result)) {
761
+ result = result.replace(summaryRegex, `- RingSense summary:\n${summary}\n--- END`);
762
+ } else {
763
+ result += `\n- RingSense summary:\n${summary}\n--- END\n`;
764
+ }
765
+ break;
706
766
  }
707
767
  return result;
708
768
  }
@@ -711,27 +771,33 @@ function upsertRingSenseAIScore({ body, score, logFormat }) {
711
771
  if (!score) return body;
712
772
 
713
773
  let result = body;
714
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
715
- const scoreRegex = /(?:<li>)?<b>Call score<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
716
- if (scoreRegex.test(result)) {
717
- result = result.replace(scoreRegex, `<li><b>Call score</b>: ${score}</li>`);
718
- } else {
719
- result += `<li><b>Call score</b>: ${score}</li>`;
720
- }
721
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
722
- const scoreRegex = /\*\*Call score\*\*: [^\n]*\n*/;
723
- if (scoreRegex.test(result)) {
724
- result = result.replace(scoreRegex, `**Call score**: ${score}\n`);
725
- } else {
726
- result += `**Call score**: ${score}\n`;
727
- }
728
- } else {
729
- const scoreRegex = /- Call score:\s*([^<\n]+)(?=\n|$)/i;
730
- if (scoreRegex.test(result)) {
731
- result = result.replace(scoreRegex, `- Call score: ${score}`);
732
- } else {
733
- result += `- Call score: ${score}\n`;
734
- }
774
+ let scoreRegex = null;
775
+
776
+ switch (logFormat) {
777
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
778
+ scoreRegex = /(?:<li>)?<b>Call score<\/b>:\s*([^<\n]+)(?:<\/li>|(?=<|$))/i;
779
+ if (scoreRegex.test(result)) {
780
+ result = result.replace(scoreRegex, `<li><b>Call score</b>: ${score}</li>`);
781
+ } else {
782
+ result += `<li><b>Call score</b>: ${score}</li>`;
783
+ }
784
+ break;
785
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
786
+ scoreRegex = /\*\*Call score\*\*: [^\n]*\n*/;
787
+ if (scoreRegex.test(result)) {
788
+ result = result.replace(scoreRegex, `**Call score**: ${score}\n`);
789
+ } else {
790
+ result += `**Call score**: ${score}\n`;
791
+ }
792
+ break;
793
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
794
+ scoreRegex = /- Call score:\s*([^<\n]+)(?=\n|$)/i;
795
+ if (scoreRegex.test(result)) {
796
+ result = result.replace(scoreRegex, `- Call score: ${score}`);
797
+ } else {
798
+ result += `- Call score: ${score}\n`;
799
+ }
800
+ break;
735
801
  }
736
802
  return result;
737
803
  }
@@ -741,28 +807,34 @@ function upsertRingSenseBulletedSummary({ body, summary, logFormat }) {
741
807
 
742
808
  let result = body;
743
809
  const clearedSummary = summary.replace(/\n+$/, '');
744
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
745
- const summaryRegex = /<div><b>RingSense bulleted summary<\/b><br>(.+?)<\/div>/;
746
- const formattedSummary = clearedSummary.replace(/(?:\r\n|\r|\n)/g, '<br>');
747
- if (summaryRegex.test(result)) {
748
- result = result.replace(summaryRegex, `<div><b>RingSense bulleted summary</b><br>${formattedSummary}</div>`);
749
- } else {
750
- result += `<div><b>RingSense bulleted summary</b><br>${formattedSummary}</div>`;
751
- }
752
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
753
- const summaryRegex = /### RingSense bulleted summary\n([\s\S]*?)(?=\n### |\n$|$)/;
754
- if (summaryRegex.test(result)) {
755
- result = result.replace(summaryRegex, `### RingSense bulleted summary\n${summary}\n`);
756
- } else {
757
- result += `### RingSense bulleted summary\n${summary}\n`;
758
- }
759
- } else {
760
- const summaryRegex = /- RingSense bulleted summary:\s*([^<\n]+)(?=\n|$)/i;
761
- if (summaryRegex.test(result)) {
762
- result = result.replace(summaryRegex, `- RingSense bulleted summary:\n${summary}\n--- END`);
763
- } else {
764
- result += `\n- RingSense bulleted summary:\n${summary}\n--- END\n`;
765
- }
810
+ let summaryRegex = null;
811
+
812
+ switch (logFormat) {
813
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
814
+ summaryRegex = /<div><b>RingSense bulleted summary<\/b><br>(.+?)<\/div>/;
815
+ const formattedSummary = clearedSummary.replace(/(?:\r\n|\r|\n)/g, '<br>');
816
+ if (summaryRegex.test(result)) {
817
+ result = result.replace(summaryRegex, `<div><b>RingSense bulleted summary</b><br>${formattedSummary}</div>`);
818
+ } else {
819
+ result += `<div><b>RingSense bulleted summary</b><br>${formattedSummary}</div>`;
820
+ }
821
+ break;
822
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
823
+ summaryRegex = /### RingSense bulleted summary\n([\s\S]*?)(?=\n### |\n$|$)/;
824
+ if (summaryRegex.test(result)) {
825
+ result = result.replace(summaryRegex, `### RingSense bulleted summary\n${summary}\n`);
826
+ } else {
827
+ result += `### RingSense bulleted summary\n${summary}\n`;
828
+ }
829
+ break;
830
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
831
+ summaryRegex = /- RingSense bulleted summary:\s*([^<\n]+)(?=\n|$)/i;
832
+ if (summaryRegex.test(result)) {
833
+ result = result.replace(summaryRegex, `- RingSense bulleted summary:\n${summary}\n--- END`);
834
+ } else {
835
+ result += `\n- RingSense bulleted summary:\n${summary}\n--- END\n`;
836
+ }
837
+ break;
766
838
  }
767
839
  return result;
768
840
  }
@@ -771,27 +843,33 @@ function upsertRingSenseLink({ body, link, logFormat }) {
771
843
  if (!link) return body;
772
844
 
773
845
  let result = body;
774
- if (logFormat === LOG_DETAILS_FORMAT_TYPE.HTML) {
775
- const linkRegex = /(?:<li>)?<b>RingSense recording link<\/b>:\s*(?:<a[^>]*>[^<]*<\/a>|[^<]+)(?:<\/li>|(?=<|$))/i;
776
- if (linkRegex.test(result)) {
777
- result = result.replace(linkRegex, `<li><b>RingSense recording link</b>: <a target="_blank" href="${link}">open</a></li>`);
778
- } else {
779
- result += `<li><b>RingSense recording link</b>: <a target="_blank" href="${link}">open</a></li>`;
780
- }
781
- } else if (logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
782
- const linkRegex = /\*\*RingSense recording link\*\*:\s*([^<\n]+)(?=\n|$)/i;
783
- if (linkRegex.test(result)) {
784
- result = result.replace(linkRegex, `**RingSense recording link**: ${link}\n`);
785
- } else {
786
- result += `**RingSense recording link**: ${link}\n`;
787
- }
788
- } else {
789
- const linkRegex = /- RingSense recording link:\s*([^<\n]+)(?=\n|$)/i;
790
- if (linkRegex.test(result)) {
791
- result = result.replace(linkRegex, `- RingSense recording link: ${link}`);
792
- } else {
793
- result += `- RingSense recording link: ${link}\n`;
794
- }
846
+ let linkRegex = null;
847
+
848
+ switch (logFormat) {
849
+ case LOG_DETAILS_FORMAT_TYPE.HTML:
850
+ linkRegex = /(?:<li>)?<b>RingSense recording link<\/b>:\s*(?:<a[^>]*>[^<]*<\/a>|[^<]+)(?:<\/li>|(?=<|$))/i;
851
+ if (linkRegex.test(result)) {
852
+ result = result.replace(linkRegex, `<li><b>RingSense recording link</b>: <a target="_blank" href="${link}">open</a></li>`);
853
+ } else {
854
+ result += `<li><b>RingSense recording link</b>: <a target="_blank" href="${link}">open</a></li>`;
855
+ }
856
+ break;
857
+ case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
858
+ linkRegex = /\*\*RingSense recording link\*\*:\s*([^<\n]+)(?=\n|$)/i;
859
+ if (linkRegex.test(result)) {
860
+ result = result.replace(linkRegex, `**RingSense recording link**: ${link}\n`);
861
+ } else {
862
+ result += `**RingSense recording link**: ${link}\n`;
863
+ }
864
+ break;
865
+ case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
866
+ linkRegex = /- RingSense recording link:\s*([^<\n]+)(?=\n|$)/i;
867
+ if (linkRegex.test(result)) {
868
+ result = result.replace(linkRegex, `- RingSense recording link: ${link}`);
869
+ } else {
870
+ result += `- RingSense recording link: ${link}\n`;
871
+ }
872
+ break;
795
873
  }
796
874
  return result;
797
875
  }