@app-connect/core 1.6.4-beta.0 → 1.7.0-beta.1

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.
package/handlers/admin.js CHANGED
@@ -2,6 +2,7 @@ const axios = require('axios');
2
2
  const { AdminConfigModel } = require('../models/adminConfigModel');
3
3
  const adapterRegistry = require('../adapter/registry');
4
4
  const oauth = require('../lib/oauth');
5
+ const { RingCentral } = require('../lib/ringcentral');
5
6
 
6
7
  async function validateAdminRole({ rcAccessToken }) {
7
8
  const rcExtensionResponse = await axios.get(
@@ -36,6 +37,21 @@ async function getAdminSettings({ hashedRcAccountId }) {
36
37
  return existingAdminConfig;
37
38
  }
38
39
 
40
+ async function updateAdminRcTokens({ hashedRcAccountId, adminAccessToken, adminRefreshToken, adminTokenExpiry }) {
41
+ const existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId);
42
+ if (existingAdminConfig) {
43
+ await existingAdminConfig.update({ adminAccessToken, adminRefreshToken, adminTokenExpiry });
44
+ }
45
+ else {
46
+ await AdminConfigModel.create({
47
+ id: hashedRcAccountId,
48
+ adminAccessToken,
49
+ adminRefreshToken,
50
+ adminTokenExpiry
51
+ });
52
+ }
53
+ }
54
+
39
55
  async function getServerLoggingSettings({ user }) {
40
56
  const platformModule = adapterRegistry.getAdapter(user.platform);
41
57
  if (platformModule.getServerLoggingSettings) {
@@ -55,8 +71,259 @@ async function updateServerLoggingSettings({ user, additionalFieldValues }) {
55
71
  return {};
56
72
  }
57
73
 
74
+ async function getAdminReport({ rcAccountId, timezone, timeFrom, timeTo }) {
75
+ try {
76
+ const rcSDK = new RingCentral({
77
+ server: process.env.RINGCENTRAL_SERVER,
78
+ clientId: process.env.RINGCENTRAL_CLIENT_ID,
79
+ clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
80
+ redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback`
81
+ });
82
+ let adminConfig = await AdminConfigModel.findByPk(rcAccountId);
83
+ const isTokenExpired = adminConfig.adminTokenExpiry < new Date();
84
+ if (isTokenExpired) {
85
+ const { access_token, refresh_token, expire_time } = await rcSDK.refreshToken({
86
+ refresh_token: adminConfig.adminRefreshToken,
87
+ expires_in: adminConfig.adminTokenExpiry,
88
+ refresh_token_expires_in: adminConfig.adminTokenExpiry
89
+ });
90
+ adminConfig = await AdminConfigModel.update({ adminAccessToken: access_token, adminRefreshToken: refresh_token, adminTokenExpiry: expire_time }, { where: { id: rcAccountId } });
91
+ }
92
+ const callsAggregationData = await rcSDK.getCallsAggregationData({
93
+ token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' },
94
+ timezone,
95
+ timeFrom,
96
+ timeTo
97
+ });
98
+ var dataCounter = callsAggregationData.data.records[0].counters;
99
+ var inboundCallCount = dataCounter.callsByDirection.values.inbound;
100
+ var outboundCallCount = dataCounter.callsByDirection.values.outbound;
101
+ var answeredCallCount = dataCounter.callsByResponse.values.answered;
102
+ // keep 2 decimal places
103
+ var answeredCallPercentage = inboundCallCount === 0 ? '0%' : `${((answeredCallCount / inboundCallCount) * 100).toFixed(2)}%`;
104
+
105
+ var dataTimer = callsAggregationData.data.records[0].timers;
106
+ // keep 2 decimal places
107
+ var totalTalkTime = (dataTimer.allCalls.values / 60).toFixed(2);
108
+ // keep 2 decimal places
109
+ var averageTalkTime = (totalTalkTime / (inboundCallCount + outboundCallCount)).toFixed(2);
110
+ return {
111
+ callLogStats: {
112
+ inboundCallCount,
113
+ outboundCallCount,
114
+ answeredCallCount,
115
+ answeredCallPercentage,
116
+ totalTalkTime,
117
+ averageTalkTime
118
+ }
119
+ };
120
+ } catch (error) {
121
+ console.error(error);
122
+ return {
123
+ callLogStats: {}
124
+ };
125
+ }
126
+ }
127
+
128
+ async function getUserReport({ rcAccountId, rcExtensionId, timezone, timeFrom, timeTo }) {
129
+ try {
130
+ const rcSDK = new RingCentral({
131
+ server: process.env.RINGCENTRAL_SERVER,
132
+ clientId: process.env.RINGCENTRAL_CLIENT_ID,
133
+ clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
134
+ redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback`
135
+ });
136
+ let adminConfig = await AdminConfigModel.findByPk(rcAccountId);
137
+ const isTokenExpired = adminConfig.adminTokenExpiry < new Date();
138
+ if (isTokenExpired) {
139
+ const { access_token, refresh_token, expire_time } = await rcSDK.refreshToken({
140
+ refresh_token: adminConfig.adminRefreshToken,
141
+ expires_in: adminConfig.adminTokenExpiry,
142
+ refresh_token_expires_in: adminConfig.adminTokenExpiry
143
+ });
144
+ adminConfig = await AdminConfigModel.update({ adminAccessToken: access_token, adminRefreshToken: refresh_token, adminTokenExpiry: expire_time }, { where: { id: rcAccountId } });
145
+ }
146
+ const callLogData = await rcSDK.getCallLogData({
147
+ extensionId: rcExtensionId,
148
+ token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' },
149
+ timezone,
150
+ timeFrom,
151
+ timeTo
152
+ });
153
+ // phone activity
154
+ const inboundCallCount = callLogData.records.filter(call => call.direction === 'Inbound').length;
155
+ const outboundCallCount = callLogData.records.filter(call => call.direction === 'Outbound').length;
156
+ const answeredCallCount = callLogData.records.filter(call => call.direction === 'Inbound' && (call.result === 'Call connected' || call.result === 'Accepted' || call.result === 'Answered Not Accepted')).length;
157
+ const answeredCallPercentage = answeredCallCount === 0 ? '0%' : `${((answeredCallCount / (inboundCallCount || 1)) * 100).toFixed(2)}%`;
158
+ // phone engagement
159
+ const totalTalkTime = Math.round(callLogData.records.reduce((acc, call) => acc + (call.duration || 0), 0) / 60) || 0;
160
+ const averageTalkTime = Math.round(totalTalkTime / (inboundCallCount + outboundCallCount)) || 0;
161
+ const smsLogData = await rcSDK.getSMSData({
162
+ extensionId: rcExtensionId,
163
+ token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' },
164
+ timezone,
165
+ timeFrom,
166
+ timeTo
167
+ });
168
+ const smsSentCount = smsLogData.records.filter(sms => sms.direction === 'Outbound').length;
169
+ const smsReceivedCount = smsLogData.records.filter(sms => sms.direction === 'Inbound').length;
170
+ const reportStats = {
171
+ callLogStats: {
172
+ inboundCallCount,
173
+ outboundCallCount,
174
+ answeredCallCount,
175
+ answeredCallPercentage,
176
+ totalTalkTime,
177
+ averageTalkTime
178
+ },
179
+ smsLogStats: {
180
+ smsSentCount,
181
+ smsReceivedCount
182
+ }
183
+ };
184
+ return reportStats;
185
+ } catch (error) {
186
+ console.error(error);
187
+ return null;
188
+ }
189
+ }
190
+
191
+ async function getUserMapping({ user, hashedRcAccountId, rcExtensionList }) {
192
+ const adminConfig = await getAdminSettings({ hashedRcAccountId });
193
+ const platformModule = adapterRegistry.getAdapter(user.platform);
194
+ if (platformModule.getUserList) {
195
+ const authType = platformModule.getAuthType();
196
+ let authHeader = '';
197
+ switch (authType) {
198
+ case 'oauth':
199
+ const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname })));
200
+ // eslint-disable-next-line no-param-reassign
201
+ user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
202
+ authHeader = `Bearer ${user.accessToken}`;
203
+ break;
204
+ case 'apiKey':
205
+ const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
206
+ authHeader = `Basic ${basicAuth}`;
207
+ break;
208
+ }
209
+ const crmUserList = await platformModule.getUserList({ user, authHeader });
210
+ const userMappingResult = [];
211
+ const newUserMappings = [];
212
+ for (const crmUser of crmUserList) {
213
+ const existingMapping = adminConfig?.userMappings?.find(u => u.crmUserId == crmUser.id);
214
+ let existingMappingRcExtensionIds = [];
215
+ // TEMP: backward compatibility for string value
216
+ if (existingMapping?.rcExtensionId) {
217
+ if (typeof (existingMapping.rcExtensionId) === 'string') {
218
+ existingMappingRcExtensionIds = [existingMapping.rcExtensionId];
219
+ }
220
+ else {
221
+ existingMappingRcExtensionIds = existingMapping.rcExtensionId;
222
+ }
223
+ }
224
+ const rcExtension = rcExtensionList.filter(e => existingMappingRcExtensionIds.includes(e.id));
225
+ // Case: existing mapping
226
+ if (existingMapping) {
227
+ userMappingResult.push({
228
+ crmUser: {
229
+ id: crmUser.id,
230
+ name: crmUser.name ?? '',
231
+ email: crmUser.email ?? '',
232
+ },
233
+ rcUser: rcExtension.map(e => ({
234
+ extensionId: e.id,
235
+ name: e?.name || `${e.firstName} ${e.lastName}`,
236
+ extensionNumber: e?.extensionNumber ?? '',
237
+ email: e?.email ?? ''
238
+ }))
239
+ });
240
+ }
241
+ // Case: new mapping
242
+ else {
243
+ const rcExtensionForNewMapping = rcExtensionList.find(u =>
244
+ u.email === crmUser.email ||
245
+ u.name === crmUser.name ||
246
+ (`${u.firstName} ${u.lastName}` === crmUser.name)
247
+ );
248
+ if (rcExtensionForNewMapping) {
249
+ userMappingResult.push({
250
+ crmUser: {
251
+ id: crmUser.id,
252
+ name: crmUser.name ?? '',
253
+ email: crmUser.email ?? '',
254
+ },
255
+ rcUser: [{
256
+ extensionId: rcExtensionForNewMapping.id,
257
+ name: rcExtensionForNewMapping.name || `${rcExtensionForNewMapping.firstName} ${rcExtensionForNewMapping.lastName}`,
258
+ extensionNumber: rcExtensionForNewMapping?.extensionNumber ?? '',
259
+ email: rcExtensionForNewMapping?.email ?? ''
260
+ }]
261
+ });
262
+ newUserMappings.push({
263
+ crmUserId: crmUser.id.toString(),
264
+ rcExtensionId: [rcExtensionForNewMapping.id.toString()]
265
+ });
266
+ }
267
+ else {
268
+ userMappingResult.push({
269
+ crmUser: {
270
+ id: crmUser.id,
271
+ name: crmUser.name ?? '',
272
+ email: crmUser.email ?? '',
273
+ },
274
+ rcUser: []
275
+ });
276
+ }
277
+ }
278
+ }
279
+ // One-time init
280
+ if (!adminConfig?.userMappings) {
281
+ const initialUserMappings = [];
282
+ for (const userMapping of userMappingResult) {
283
+ if (userMapping.rcUser?.extensionId) {
284
+ initialUserMappings.push({
285
+ crmUserId: userMapping.crmUser.id.toString(),
286
+ rcExtensionId: [userMapping.rcUser.extensionId.toString()]
287
+ });
288
+ }
289
+ }
290
+ await upsertAdminSettings({
291
+ hashedRcAccountId,
292
+ adminSettings: {
293
+ userMappings: initialUserMappings
294
+ }
295
+ });
296
+ }
297
+ // Incremental update
298
+ if (newUserMappings.length > 0) {
299
+ // TEMP: convert string to array
300
+ if (adminConfig?.userMappings) {
301
+ adminConfig.userMappings = adminConfig.userMappings.map(u => ({
302
+ ...u,
303
+ rcExtensionId: [u.rcExtensionId]
304
+ }));
305
+ }
306
+ else {
307
+ adminConfig.userMappings = [];
308
+ }
309
+ await upsertAdminSettings({
310
+ hashedRcAccountId,
311
+ adminSettings: {
312
+ userMappings: [...adminConfig.userMappings, ...newUserMappings]
313
+ }
314
+ });
315
+ }
316
+ return userMappingResult;
317
+ }
318
+ return [];
319
+ }
320
+
58
321
  exports.validateAdminRole = validateAdminRole;
59
322
  exports.upsertAdminSettings = upsertAdminSettings;
60
323
  exports.getAdminSettings = getAdminSettings;
324
+ exports.updateAdminRcTokens = updateAdminRcTokens;
61
325
  exports.getServerLoggingSettings = getServerLoggingSettings;
62
326
  exports.updateServerLoggingSettings = updateServerLoggingSettings;
327
+ exports.getAdminReport = getAdminReport;
328
+ exports.getUserReport = getUserReport;
329
+ exports.getUserMapping = getUserMapping;
package/handlers/auth.js CHANGED
@@ -2,6 +2,8 @@ const oauth = require('../lib/oauth');
2
2
  const { UserModel } = require('../models/userModel');
3
3
  const adapterRegistry = require('../adapter/registry');
4
4
  const Op = require('sequelize').Op;
5
+ const { RingCentral } = require('../lib/ringcentral');
6
+ const adminCore = require('./admin');
5
7
 
6
8
  async function onOAuthCallback({ platform, hostname, tokenUrl, callbackUri, apiUrl, username, query }) {
7
9
  const platformModule = adapterRegistry.getAdapter(platform);
@@ -92,6 +94,7 @@ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken,
92
94
  if (existingUser) {
93
95
  await existingUser.update(
94
96
  {
97
+ platform,
95
98
  hostname,
96
99
  timezoneName,
97
100
  timezoneOffset,
@@ -204,7 +207,25 @@ async function authValidation({ platform, userId }) {
204
207
  }
205
208
  }
206
209
 
210
+ // Ringcentral
211
+ async function onRingcentralOAuthCallback({ code, rcAccountId }) {
212
+ const rcSDK = new RingCentral({
213
+ server: process.env.RINGCENTRAL_SERVER,
214
+ clientId: process.env.RINGCENTRAL_CLIENT_ID,
215
+ clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
216
+ redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback`
217
+ });
218
+ const { access_token, refresh_token, expire_time } = await rcSDK.generateToken({ code });
219
+ await adminCore.updateAdminRcTokens({
220
+ hashedRcAccountId: rcAccountId,
221
+ adminAccessToken: access_token,
222
+ adminRefreshToken: refresh_token,
223
+ adminTokenExpiry: expire_time
224
+ });
225
+ }
226
+
207
227
  exports.onOAuthCallback = onOAuthCallback;
208
228
  exports.onApiKeyLogin = onApiKeyLogin;
209
229
  exports.authValidation = authValidation;
210
- exports.getLicenseStatus = getLicenseStatus;
230
+ exports.getLicenseStatus = getLicenseStatus;
231
+ exports.onRingcentralOAuthCallback = onRingcentralOAuthCallback;
@@ -0,0 +1,60 @@
1
+ const { UserModel } = require('../models/userModel');
2
+ const { CallDownListModel } = require('../models/callDownListModel');
3
+ const { Op } = require('sequelize');
4
+ const jwt = require('../lib/jwt');
5
+
6
+ async function schedule({ jwtToken, rcAccessToken, body }) {
7
+ const unAuthData = jwt.decodeJwt(jwtToken);
8
+ if (!unAuthData?.id) throw new Error('Unauthorized');
9
+ const user = await UserModel.findByPk(unAuthData.id);
10
+ if (!user) throw new Error('User not found');
11
+ const crypto = require('crypto');
12
+ const recordId = crypto.randomBytes(16).toString('hex');
13
+ const payload = {
14
+ id: recordId,
15
+ userId: user.id,
16
+ contactId: body.contactId?.toString?.() ?? body.contactId,
17
+ contactType: body.contactType ?? 'contact',
18
+ contactName: body.contactName ?? '',
19
+ phoneNumber: body.phoneNumber ?? '',
20
+ status: 'scheduled',
21
+ scheduledAt: body.scheduledAt ? new Date(body.scheduledAt) : null,
22
+ lastCallAt: null
23
+ };
24
+ await CallDownListModel.create(payload);
25
+ return { id: recordId };
26
+ }
27
+
28
+ async function list({ jwtToken, status }) {
29
+ const unAuthData = jwt.decodeJwt(jwtToken);
30
+ if (!unAuthData?.id) throw new Error('Unauthorized');
31
+ const statusParam = (status || 'All').toString().toLowerCase();
32
+ const whereClause = { userId: unAuthData.id };
33
+ if (statusParam === 'called') whereClause.status = 'called';
34
+ else if (['not called', 'not_called', 'notcalled'].includes(statusParam)) whereClause.status = { [Op.ne]: 'called' };
35
+ const items = await CallDownListModel.findAll({ where: whereClause, order: [["scheduledAt", "ASC"]] });
36
+ return { items };
37
+ }
38
+
39
+ async function remove({ jwtToken, id }) {
40
+ const unAuthData = jwt.decodeJwt(jwtToken);
41
+ if (!unAuthData?.id) throw new Error('Unauthorized');
42
+ const deleted = await CallDownListModel.destroy({ where: { id, userId: unAuthData.id } });
43
+ if (!deleted) throw new Error('Not found');
44
+ return { successful: true };
45
+ }
46
+
47
+ async function markCalled({ jwtToken, id, lastCallAt }) {
48
+ const unAuthData = jwt.decodeJwt(jwtToken);
49
+ if (!unAuthData?.id) throw new Error('Unauthorized');
50
+ const when = lastCallAt ? new Date(lastCallAt) : new Date();
51
+ const [affected] = await CallDownListModel.update({ status: 'called', lastCallAt: when }, { where: { id, userId: unAuthData.id } });
52
+ if (!affected) throw new Error('Not found');
53
+ return { successful: true };
54
+ }
55
+
56
+ exports.schedule = schedule;
57
+ exports.list = list;
58
+ exports.remove = remove;
59
+ exports.markCalled = markCalled;
60
+
package/handlers/log.js CHANGED
@@ -9,8 +9,9 @@ const adapterRegistry = require('../adapter/registry');
9
9
  const { LOG_DETAILS_FORMAT_TYPE } = require('../lib/constants');
10
10
  const { NoteCache } = require('../models/dynamo/noteCacheSchema');
11
11
  const moment = require('moment');
12
+ const { getMediaReaderLinkByPlatformMediaLink } = require('../lib/util');
12
13
 
13
- async function createCallLog({ platform, userId, incomingData, isFromSSCL }) {
14
+ async function createCallLog({ platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
14
15
  try {
15
16
  const existingCallLog = await CallLogModel.findOne({
16
17
  where: {
@@ -42,7 +43,7 @@ async function createCallLog({ platform, userId, incomingData, isFromSSCL }) {
42
43
  const callLog = incomingData.logInfo;
43
44
  const additionalSubmission = incomingData.additionalSubmission;
44
45
  let note = incomingData.note;
45
- if (isFromSSCL) {
46
+ if (process.env.USE_CACHE && isFromSSCL) {
46
47
  const noteCache = await NoteCache.get({ sessionId: incomingData.logInfo.sessionId });
47
48
  if (noteCache) {
48
49
  note = noteCache.note;
@@ -113,6 +114,7 @@ async function createCallLog({ platform, userId, incomingData, isFromSSCL }) {
113
114
  aiNote,
114
115
  transcript,
115
116
  composedLogDetails,
117
+ hashedAccountId,
116
118
  isFromSSCL
117
119
  });
118
120
  if (logId) {
@@ -209,6 +211,11 @@ async function getCallLog({ userId, sessionIds, platform, requireDetails }) {
209
211
  }
210
212
  });
211
213
  for (const sId of sessionIdsArray) {
214
+ if(sId == 0)
215
+ {
216
+ logs.push({ sessionId: sId, matched: false });
217
+ continue;
218
+ }
212
219
  const callLog = callLogs.find(c => c.sessionId === sId);
213
220
  if (!callLog) {
214
221
  logs.push({ sessionId: sId, matched: false });
@@ -288,7 +295,7 @@ async function getCallLog({ userId, sessionIds, platform, requireDetails }) {
288
295
  }
289
296
  }
290
297
 
291
- async function updateCallLog({ platform, userId, incomingData, isFromSSCL }) {
298
+ async function updateCallLog({ platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
292
299
  try {
293
300
  const existingCallLog = await CallLogModel.findOne({
294
301
  where: {
@@ -347,7 +354,8 @@ async function updateCallLog({ platform, userId, incomingData, isFromSSCL }) {
347
354
  result: incomingData.result,
348
355
  direction: incomingData.direction,
349
356
  from: incomingData.from,
350
- to: incomingData.to
357
+ to: incomingData.to,
358
+ legs: incomingData.legs || [],
351
359
  },
352
360
  contactInfo: null, // Not needed for updates
353
361
  user,
@@ -375,9 +383,11 @@ async function updateCallLog({ platform, userId, incomingData, isFromSSCL }) {
375
383
  result: incomingData.result,
376
384
  aiNote: incomingData.aiNote,
377
385
  transcript: incomingData.transcript,
386
+ legs: incomingData.legs || [],
378
387
  additionalSubmission: incomingData.additionalSubmission,
379
388
  composedLogDetails,
380
389
  existingCallLogDetails, // Pass the fetched details to avoid duplicate API calls
390
+ hashedAccountId,
381
391
  isFromSSCL
382
392
  });
383
393
  return { successful: true, logId: existingCallLog.thirdPartyLogId, updatedNote, returnMessage, extraDataTracking };
@@ -515,6 +525,18 @@ async function createMessageLog({ platform, userId, incomingData }) {
515
525
  faxDocLink = message.attachments.find(a => a.type === 'RenderedDocument').link;
516
526
  faxDownloadLink = message.attachments.find(a => a.type === 'RenderedDocument').uri + `?access_token=${incomingData.logInfo.rcAccessToken}`
517
527
  }
528
+ let imageLink = null;
529
+ let videoLink = null;
530
+ if (message.attachments && message.attachments.some(a => a.type === 'MmsAttachment')) {
531
+ const imageAttachment = message.attachments.find(a => a.type === 'MmsAttachment' && a.contentType.startsWith('image/'));
532
+ if (imageAttachment) {
533
+ imageLink = getMediaReaderLinkByPlatformMediaLink(imageAttachment?.uri);
534
+ }
535
+ const videoAttachment = message.attachments.find(a => a.type === 'MmsAttachment' && a.contentType.startsWith('video/'));
536
+ if (videoAttachment) {
537
+ videoLink = getMediaReaderLinkByPlatformMediaLink(videoAttachment?.uri);
538
+ }
539
+ }
518
540
  const existingSameDateMessageLog = await MessageLogModel.findOne({
519
541
  where: {
520
542
  conversationLogId: incomingData.logInfo.conversationLogId
@@ -522,12 +544,12 @@ async function createMessageLog({ platform, userId, incomingData }) {
522
544
  });
523
545
  let crmLogId = ''
524
546
  if (existingSameDateMessageLog) {
525
- const updateMessageResult = await platformModule.updateMessageLog({ user, contactInfo, existingMessageLog: existingSameDateMessageLog, message, authHeader, additionalSubmission });
547
+ const updateMessageResult = await platformModule.updateMessageLog({ user, contactInfo, existingMessageLog: existingSameDateMessageLog, message, authHeader, additionalSubmission, imageLink, videoLink });
526
548
  crmLogId = existingSameDateMessageLog.thirdPartyLogId;
527
549
  returnMessage = updateMessageResult?.returnMessage;
528
550
  }
529
551
  else {
530
- const createMessageLogResult = await platformModule.createMessageLog({ user, contactInfo, authHeader, message, additionalSubmission, recordingLink, faxDocLink, faxDownloadLink });
552
+ const createMessageLogResult = await platformModule.createMessageLog({ user, contactInfo, authHeader, message, additionalSubmission, recordingLink, faxDocLink, faxDownloadLink, imageLink, videoLink });
531
553
  crmLogId = createMessageLogResult.logId;
532
554
  returnMessage = createMessageLogResult?.returnMessage;
533
555
  extraDataTracking = createMessageLogResult.extraDataTracking;