@app-connect/core 1.5.8 → 1.6.4
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/.env.test +5 -5
- package/README.md +434 -434
- package/adapter/mock.js +76 -76
- package/adapter/registry.js +247 -247
- package/handlers/admin.js +62 -60
- package/handlers/auth.js +205 -156
- package/handlers/contact.js +274 -274
- package/handlers/disposition.js +193 -193
- package/handlers/log.js +612 -587
- package/handlers/user.js +101 -101
- package/index.js +1302 -1204
- package/jest.config.js +56 -56
- package/lib/analytics.js +52 -52
- package/lib/callLogComposer.js +550 -485
- package/lib/constants.js +8 -8
- package/lib/encode.js +30 -30
- package/lib/generalErrorMessage.js +41 -41
- package/lib/jwt.js +16 -16
- package/lib/oauth.js +34 -21
- package/lib/util.js +43 -40
- package/models/adminConfigModel.js +17 -17
- package/models/cacheModel.js +23 -23
- package/models/callLogModel.js +27 -27
- package/models/dynamo/lockSchema.js +24 -24
- package/models/dynamo/noteCacheSchema.js +30 -0
- package/models/messageLogModel.js +25 -25
- package/models/sequelize.js +16 -16
- package/models/userModel.js +38 -38
- package/package.json +67 -64
- package/releaseNotes.json +701 -605
- package/test/adapter/registry.test.js +270 -270
- package/test/handlers/auth.test.js +230 -230
- package/test/lib/jwt.test.js +161 -161
- package/test/setup.js +176 -176
package/handlers/log.js
CHANGED
|
@@ -1,587 +1,612 @@
|
|
|
1
|
-
const Op = require('sequelize').Op;
|
|
2
|
-
const { CallLogModel } = require('../models/callLogModel');
|
|
3
|
-
const { MessageLogModel } = require('../models/messageLogModel');
|
|
4
|
-
const { UserModel } = require('../models/userModel');
|
|
5
|
-
const oauth = require('../lib/oauth');
|
|
6
|
-
const errorMessage = require('../lib/generalErrorMessage');
|
|
7
|
-
const { composeCallLog, getLogFormatType } = require('../lib/callLogComposer');
|
|
8
|
-
const adapterRegistry = require('../adapter/registry');
|
|
9
|
-
const { LOG_DETAILS_FORMAT_TYPE } = require('../lib/constants');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
let
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return {
|
|
248
|
-
successful: false,
|
|
249
|
-
returnMessage: errorMessage.
|
|
250
|
-
extraDataTracking: {
|
|
251
|
-
statusCode: e.response?.status,
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
let
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
1
|
+
const Op = require('sequelize').Op;
|
|
2
|
+
const { CallLogModel } = require('../models/callLogModel');
|
|
3
|
+
const { MessageLogModel } = require('../models/messageLogModel');
|
|
4
|
+
const { UserModel } = require('../models/userModel');
|
|
5
|
+
const oauth = require('../lib/oauth');
|
|
6
|
+
const errorMessage = require('../lib/generalErrorMessage');
|
|
7
|
+
const { composeCallLog, getLogFormatType } = require('../lib/callLogComposer');
|
|
8
|
+
const adapterRegistry = require('../adapter/registry');
|
|
9
|
+
const { LOG_DETAILS_FORMAT_TYPE } = require('../lib/constants');
|
|
10
|
+
const { NoteCache } = require('../models/dynamo/noteCacheSchema');
|
|
11
|
+
const moment = require('moment');
|
|
12
|
+
|
|
13
|
+
async function createCallLog({ platform, userId, incomingData, isFromSSCL }) {
|
|
14
|
+
try {
|
|
15
|
+
const existingCallLog = await CallLogModel.findOne({
|
|
16
|
+
where: {
|
|
17
|
+
sessionId: incomingData.logInfo.sessionId
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (existingCallLog) {
|
|
21
|
+
return {
|
|
22
|
+
successful: false,
|
|
23
|
+
returnMessage: {
|
|
24
|
+
message: `Existing log for session ${incomingData.logInfo.sessionId}`,
|
|
25
|
+
messageType: 'warning',
|
|
26
|
+
ttl: 3000
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
let user = await UserModel.findByPk(userId);
|
|
31
|
+
if (!user || !user.accessToken) {
|
|
32
|
+
return {
|
|
33
|
+
successful: false,
|
|
34
|
+
returnMessage: {
|
|
35
|
+
message: `User not found`,
|
|
36
|
+
messageType: 'warning',
|
|
37
|
+
ttl: 5000
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
42
|
+
const callLog = incomingData.logInfo;
|
|
43
|
+
const additionalSubmission = incomingData.additionalSubmission;
|
|
44
|
+
let note = incomingData.note;
|
|
45
|
+
if (isFromSSCL) {
|
|
46
|
+
const noteCache = await NoteCache.get({ sessionId: incomingData.logInfo.sessionId });
|
|
47
|
+
if (noteCache) {
|
|
48
|
+
note = noteCache.note;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const aiNote = incomingData.aiNote;
|
|
52
|
+
const transcript = incomingData.transcript;
|
|
53
|
+
const authType = platformModule.getAuthType();
|
|
54
|
+
let authHeader = '';
|
|
55
|
+
switch (authType) {
|
|
56
|
+
case 'oauth':
|
|
57
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname })));
|
|
58
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
59
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
60
|
+
break;
|
|
61
|
+
case 'apiKey':
|
|
62
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
63
|
+
authHeader = `Basic ${basicAuth}`;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
const contactNumber = callLog.direction === 'Inbound' ? callLog.from.phoneNumber : callLog.to.phoneNumber;
|
|
67
|
+
const contactId = incomingData.contactId;
|
|
68
|
+
if (!contactId || contactId === 0) {
|
|
69
|
+
return {
|
|
70
|
+
successful: false,
|
|
71
|
+
returnMessage: {
|
|
72
|
+
message: `Contact not found for number ${contactNumber}`,
|
|
73
|
+
messageType: 'warning',
|
|
74
|
+
ttl: 5000
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const contactInfo = {
|
|
79
|
+
id: contactId,
|
|
80
|
+
phoneNumber: contactNumber,
|
|
81
|
+
type: incomingData.contactType ?? "",
|
|
82
|
+
name: incomingData.contactName ?? ""
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Compose call log details centrally
|
|
86
|
+
const logFormat = getLogFormatType(platform);
|
|
87
|
+
let composedLogDetails = '';
|
|
88
|
+
if (logFormat === LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT || logFormat === LOG_DETAILS_FORMAT_TYPE.HTML || logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
|
|
89
|
+
composedLogDetails = await composeCallLog({
|
|
90
|
+
logFormat,
|
|
91
|
+
callLog,
|
|
92
|
+
contactInfo,
|
|
93
|
+
user,
|
|
94
|
+
note,
|
|
95
|
+
aiNote,
|
|
96
|
+
transcript,
|
|
97
|
+
recordingLink: callLog.recording?.link,
|
|
98
|
+
subject: callLog.customSubject,
|
|
99
|
+
startTime: callLog.startTime,
|
|
100
|
+
duration: callLog.duration,
|
|
101
|
+
result: callLog.result,
|
|
102
|
+
platform
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { logId, returnMessage, extraDataTracking } = await platformModule.createCallLog({
|
|
107
|
+
user,
|
|
108
|
+
contactInfo,
|
|
109
|
+
authHeader,
|
|
110
|
+
callLog,
|
|
111
|
+
note,
|
|
112
|
+
additionalSubmission,
|
|
113
|
+
aiNote,
|
|
114
|
+
transcript,
|
|
115
|
+
composedLogDetails,
|
|
116
|
+
isFromSSCL
|
|
117
|
+
});
|
|
118
|
+
if (logId) {
|
|
119
|
+
await CallLogModel.create({
|
|
120
|
+
id: incomingData.logInfo.telephonySessionId || incomingData.logInfo.id,
|
|
121
|
+
sessionId: incomingData.logInfo.sessionId,
|
|
122
|
+
platform,
|
|
123
|
+
thirdPartyLogId: logId,
|
|
124
|
+
userId,
|
|
125
|
+
contactId
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return { successful: !!logId, logId, returnMessage, extraDataTracking };
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.error(`platform: ${platform} \n${e.stack} \n${JSON.stringify(e.response?.data)}`);
|
|
131
|
+
if (e.response?.status === 429) {
|
|
132
|
+
return {
|
|
133
|
+
successful: false,
|
|
134
|
+
returnMessage: errorMessage.rateLimitErrorMessage({ platform })
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
else if (e.response?.status >= 400 && e.response?.status < 410) {
|
|
138
|
+
return {
|
|
139
|
+
successful: false,
|
|
140
|
+
returnMessage: errorMessage.authorizationErrorMessage({ platform }),
|
|
141
|
+
extraDataTracking: {
|
|
142
|
+
statusCode: e.response?.status,
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
successful: false,
|
|
148
|
+
returnMessage:
|
|
149
|
+
{
|
|
150
|
+
message: `Error creating call log`,
|
|
151
|
+
messageType: 'warning',
|
|
152
|
+
details: [
|
|
153
|
+
{
|
|
154
|
+
title: 'Details',
|
|
155
|
+
items: [
|
|
156
|
+
{
|
|
157
|
+
id: '1',
|
|
158
|
+
type: 'text',
|
|
159
|
+
text: `Please check if your account has permission to CREATE logs.`
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
ttl: 5000
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function getCallLog({ userId, sessionIds, platform, requireDetails }) {
|
|
171
|
+
try {
|
|
172
|
+
let user = await UserModel.findByPk(userId);
|
|
173
|
+
if (!user || !user.accessToken) {
|
|
174
|
+
return { successful: false, message: `Contact not found` };
|
|
175
|
+
}
|
|
176
|
+
let logs = [];
|
|
177
|
+
let returnMessage = null;
|
|
178
|
+
let extraDataTracking = {};;
|
|
179
|
+
|
|
180
|
+
// Handle undefined or null sessionIds
|
|
181
|
+
if (!sessionIds) {
|
|
182
|
+
return { successful: false, message: `No session IDs provided` };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let sessionIdsArray = sessionIds.split(',');
|
|
186
|
+
if (sessionIdsArray.length > 5) {
|
|
187
|
+
sessionIdsArray = sessionIdsArray.slice(0, 5);
|
|
188
|
+
}
|
|
189
|
+
if (requireDetails) {
|
|
190
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
191
|
+
const authType = platformModule.getAuthType();
|
|
192
|
+
let authHeader = '';
|
|
193
|
+
switch (authType) {
|
|
194
|
+
case 'oauth':
|
|
195
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname })));
|
|
196
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
197
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
198
|
+
break;
|
|
199
|
+
case 'apiKey':
|
|
200
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
201
|
+
authHeader = `Basic ${basicAuth}`;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
const callLogs = await CallLogModel.findAll({
|
|
205
|
+
where: {
|
|
206
|
+
sessionId: {
|
|
207
|
+
[Op.in]: sessionIdsArray
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
for (const sId of sessionIdsArray) {
|
|
212
|
+
const callLog = callLogs.find(c => c.sessionId === sId);
|
|
213
|
+
if (!callLog) {
|
|
214
|
+
logs.push({ sessionId: sId, matched: false });
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const getCallLogResult = await platformModule.getCallLog({ user, callLogId: callLog.thirdPartyLogId, contactId: callLog.contactId, authHeader });
|
|
218
|
+
returnMessage = getCallLogResult.returnMessage;
|
|
219
|
+
extraDataTracking = getCallLogResult.extraDataTracking;
|
|
220
|
+
logs.push({ sessionId: callLog.sessionId, matched: true, logId: callLog.thirdPartyLogId, logData: getCallLogResult.callLogInfo });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const callLogs = await CallLogModel.findAll({
|
|
226
|
+
where: {
|
|
227
|
+
sessionId: {
|
|
228
|
+
[Op.in]: sessionIdsArray
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
for (const sId of sessionIdsArray) {
|
|
233
|
+
const callLog = callLogs.find(c => c.sessionId === sId);
|
|
234
|
+
if (!callLog) {
|
|
235
|
+
logs.push({ sessionId: sId, matched: false });
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
logs.push({ sessionId: callLog.sessionId, matched: true, logId: callLog.thirdPartyLogId });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return { successful: true, logs, returnMessage, extraDataTracking };
|
|
243
|
+
}
|
|
244
|
+
catch (e) {
|
|
245
|
+
console.error(`platform: ${platform} \n${e.stack} \n${JSON.stringify(e.response?.data)}`);
|
|
246
|
+
if (e.response?.status === 429) {
|
|
247
|
+
return {
|
|
248
|
+
successful: false,
|
|
249
|
+
returnMessage: errorMessage.rateLimitErrorMessage({ platform }),
|
|
250
|
+
extraDataTracking: {
|
|
251
|
+
statusCode: e.response?.status,
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
else if (e.response?.status >= 400 && e.response?.status < 410) {
|
|
256
|
+
return {
|
|
257
|
+
successful: false,
|
|
258
|
+
returnMessage: errorMessage.authorizationErrorMessage({ platform }),
|
|
259
|
+
extraDataTracking: {
|
|
260
|
+
statusCode: e.response?.status,
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
successful: false,
|
|
266
|
+
returnMessage:
|
|
267
|
+
{
|
|
268
|
+
message: `Error getting call log`,
|
|
269
|
+
messageType: 'warning',
|
|
270
|
+
details: [
|
|
271
|
+
{
|
|
272
|
+
title: 'Details',
|
|
273
|
+
items: [
|
|
274
|
+
{
|
|
275
|
+
id: '1',
|
|
276
|
+
type: 'text',
|
|
277
|
+
text: `Please check if your account has permission to CREATE logs.`
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
],
|
|
282
|
+
ttl: 5000
|
|
283
|
+
},
|
|
284
|
+
extraDataTracking: {
|
|
285
|
+
statusCode: e.response?.status,
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function updateCallLog({ platform, userId, incomingData, isFromSSCL }) {
|
|
292
|
+
try {
|
|
293
|
+
const existingCallLog = await CallLogModel.findOne({
|
|
294
|
+
where: {
|
|
295
|
+
sessionId: incomingData.sessionId
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
if (existingCallLog) {
|
|
299
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
300
|
+
let user = await UserModel.findByPk(userId);
|
|
301
|
+
if (!user || !user.accessToken) {
|
|
302
|
+
return { successful: false, message: `Contact not found` };
|
|
303
|
+
}
|
|
304
|
+
const authType = platformModule.getAuthType();
|
|
305
|
+
let authHeader = '';
|
|
306
|
+
switch (authType) {
|
|
307
|
+
case 'oauth':
|
|
308
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname })));
|
|
309
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
310
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
311
|
+
break;
|
|
312
|
+
case 'apiKey':
|
|
313
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
314
|
+
authHeader = `Basic ${basicAuth}`;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Fetch existing call log details once to avoid duplicate API calls
|
|
319
|
+
let existingCallLogDetails = null; // Compose updated call log details centrally
|
|
320
|
+
const logFormat = getLogFormatType(platform);
|
|
321
|
+
let composedLogDetails = '';
|
|
322
|
+
if (logFormat === LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT || logFormat === LOG_DETAILS_FORMAT_TYPE.HTML || logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
|
|
323
|
+
let existingBody = '';
|
|
324
|
+
try {
|
|
325
|
+
const getLogResult = await platformModule.getCallLog({
|
|
326
|
+
user,
|
|
327
|
+
callLogId: existingCallLog.thirdPartyLogId,
|
|
328
|
+
authHeader
|
|
329
|
+
});
|
|
330
|
+
existingCallLogDetails = getLogResult?.callLogInfo?.fullLogResponse;
|
|
331
|
+
// Extract existing body from the platform-specific response
|
|
332
|
+
if (getLogResult.callLogInfo?.fullBody) {
|
|
333
|
+
existingBody = getLogResult.callLogInfo.fullBody;
|
|
334
|
+
} else if (getLogResult.callLogInfo?.note) {
|
|
335
|
+
existingBody = getLogResult.callLogInfo.note;
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.log('Error getting existing log details, proceeding with empty body', error);
|
|
339
|
+
}
|
|
340
|
+
composedLogDetails = await composeCallLog({
|
|
341
|
+
logFormat,
|
|
342
|
+
existingBody,
|
|
343
|
+
callLog: {
|
|
344
|
+
sessionId: existingCallLog.sessionId,
|
|
345
|
+
startTime: incomingData.startTime,
|
|
346
|
+
duration: incomingData.duration,
|
|
347
|
+
result: incomingData.result,
|
|
348
|
+
direction: incomingData.direction,
|
|
349
|
+
from: incomingData.from,
|
|
350
|
+
to: incomingData.to
|
|
351
|
+
},
|
|
352
|
+
contactInfo: null, // Not needed for updates
|
|
353
|
+
user,
|
|
354
|
+
note: incomingData.note,
|
|
355
|
+
aiNote: incomingData.aiNote,
|
|
356
|
+
transcript: incomingData.transcript,
|
|
357
|
+
recordingLink: incomingData.recordingLink,
|
|
358
|
+
subject: incomingData.subject,
|
|
359
|
+
startTime: incomingData.startTime,
|
|
360
|
+
duration: incomingData.duration,
|
|
361
|
+
result: incomingData.result,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const { updatedNote, returnMessage, extraDataTracking } = await platformModule.updateCallLog({
|
|
366
|
+
user,
|
|
367
|
+
existingCallLog,
|
|
368
|
+
authHeader,
|
|
369
|
+
recordingLink: incomingData.recordingLink,
|
|
370
|
+
recordingDownloadLink: incomingData.recordingDownloadLink,
|
|
371
|
+
subject: incomingData.subject,
|
|
372
|
+
note: incomingData.note,
|
|
373
|
+
startTime: incomingData.startTime,
|
|
374
|
+
duration: incomingData.duration,
|
|
375
|
+
result: incomingData.result,
|
|
376
|
+
aiNote: incomingData.aiNote,
|
|
377
|
+
transcript: incomingData.transcript,
|
|
378
|
+
additionalSubmission: incomingData.additionalSubmission,
|
|
379
|
+
composedLogDetails,
|
|
380
|
+
existingCallLogDetails, // Pass the fetched details to avoid duplicate API calls
|
|
381
|
+
isFromSSCL
|
|
382
|
+
});
|
|
383
|
+
return { successful: true, logId: existingCallLog.thirdPartyLogId, updatedNote, returnMessage, extraDataTracking };
|
|
384
|
+
}
|
|
385
|
+
return { successful: false };
|
|
386
|
+
} catch (e) {
|
|
387
|
+
console.error(`platform: ${platform} \n${e.stack} \n${JSON.stringify(e.response?.data)}`);
|
|
388
|
+
if (e.response?.status === 429) {
|
|
389
|
+
return {
|
|
390
|
+
successful: false,
|
|
391
|
+
returnMessage: errorMessage.rateLimitErrorMessage({ platform }),
|
|
392
|
+
extraDataTracking: {
|
|
393
|
+
statusCode: e.response?.status,
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
else if (e.response?.status >= 400 && e.response?.status < 410) {
|
|
398
|
+
return {
|
|
399
|
+
successful: false,
|
|
400
|
+
returnMessage: errorMessage.authorizationErrorMessage({ platform }),
|
|
401
|
+
extraDataTracking: {
|
|
402
|
+
statusCode: e.response?.status,
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
successful: false,
|
|
408
|
+
returnMessage:
|
|
409
|
+
{
|
|
410
|
+
message: `Error updating call log`,
|
|
411
|
+
messageType: 'warning',
|
|
412
|
+
details: [
|
|
413
|
+
{
|
|
414
|
+
title: 'Details',
|
|
415
|
+
items: [
|
|
416
|
+
{
|
|
417
|
+
id: '1',
|
|
418
|
+
type: 'text',
|
|
419
|
+
text: `Please check if the log entity still exist on ${platform} and your account has permission to EDIT logs.`
|
|
420
|
+
}
|
|
421
|
+
]
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
ttl: 5000
|
|
425
|
+
},
|
|
426
|
+
extraDataTracking: {
|
|
427
|
+
statusCode: e.response?.status,
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async function createMessageLog({ platform, userId, incomingData }) {
|
|
434
|
+
try {
|
|
435
|
+
let returnMessage = null;
|
|
436
|
+
let extraDataTracking = {};;
|
|
437
|
+
if (incomingData.logInfo.messages.length === 0) {
|
|
438
|
+
return {
|
|
439
|
+
successful: false,
|
|
440
|
+
returnMessage:
|
|
441
|
+
{
|
|
442
|
+
message: 'No message to log.',
|
|
443
|
+
messageType: 'warning',
|
|
444
|
+
ttl: 3000
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
449
|
+
const contactNumber = incomingData.logInfo.correspondents[0].phoneNumber;
|
|
450
|
+
const additionalSubmission = incomingData.additionalSubmission;
|
|
451
|
+
let user = await UserModel.findByPk(userId);
|
|
452
|
+
if (!user || !user.accessToken) {
|
|
453
|
+
return {
|
|
454
|
+
successful: false,
|
|
455
|
+
returnMessage:
|
|
456
|
+
{
|
|
457
|
+
message: `Contact not found`,
|
|
458
|
+
messageType: 'warning',
|
|
459
|
+
ttl: 5000
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
const authType = platformModule.getAuthType();
|
|
464
|
+
let authHeader = '';
|
|
465
|
+
switch (authType) {
|
|
466
|
+
case 'oauth':
|
|
467
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname })));
|
|
468
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
469
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
470
|
+
break;
|
|
471
|
+
case 'apiKey':
|
|
472
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
473
|
+
authHeader = `Basic ${basicAuth}`;
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
const contactId = incomingData.contactId;
|
|
477
|
+
if (!contactId) {
|
|
478
|
+
return {
|
|
479
|
+
successful: false,
|
|
480
|
+
returnMessage:
|
|
481
|
+
{
|
|
482
|
+
message: `Contact not found for number ${contactNumber}`,
|
|
483
|
+
messageType: 'warning',
|
|
484
|
+
ttl: 5000
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
const contactInfo = {
|
|
489
|
+
id: contactId,
|
|
490
|
+
phoneNumber: contactNumber,
|
|
491
|
+
type: incomingData.contactType ?? "",
|
|
492
|
+
name: incomingData.contactName ?? ""
|
|
493
|
+
};
|
|
494
|
+
const messageIds = incomingData.logInfo.messages.map(m => { return { id: m.id.toString() }; });
|
|
495
|
+
const existingMessages = await MessageLogModel.findAll({
|
|
496
|
+
where: {
|
|
497
|
+
[Op.or]: messageIds
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
const existingIds = existingMessages.map(m => m.id);
|
|
501
|
+
const logIds = [];
|
|
502
|
+
// reverse the order of messages to log the oldest message first
|
|
503
|
+
const reversedMessages = incomingData.logInfo.messages.reverse();
|
|
504
|
+
for (const message of reversedMessages) {
|
|
505
|
+
if (existingIds.includes(message.id.toString())) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
let recordingLink = null;
|
|
509
|
+
if (message.attachments && message.attachments.some(a => a.type === 'AudioRecording')) {
|
|
510
|
+
recordingLink = message.attachments.find(a => a.type === 'AudioRecording').link;
|
|
511
|
+
}
|
|
512
|
+
let faxDocLink = null;
|
|
513
|
+
let faxDownloadLink = null;
|
|
514
|
+
if (message.attachments && message.attachments.some(a => a.type === 'RenderedDocument')) {
|
|
515
|
+
faxDocLink = message.attachments.find(a => a.type === 'RenderedDocument').link;
|
|
516
|
+
faxDownloadLink = message.attachments.find(a => a.type === 'RenderedDocument').uri + `?access_token=${incomingData.logInfo.rcAccessToken}`
|
|
517
|
+
}
|
|
518
|
+
const existingSameDateMessageLog = await MessageLogModel.findOne({
|
|
519
|
+
where: {
|
|
520
|
+
conversationLogId: incomingData.logInfo.conversationLogId
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
let crmLogId = ''
|
|
524
|
+
if (existingSameDateMessageLog) {
|
|
525
|
+
const updateMessageResult = await platformModule.updateMessageLog({ user, contactInfo, existingMessageLog: existingSameDateMessageLog, message, authHeader, additionalSubmission });
|
|
526
|
+
crmLogId = existingSameDateMessageLog.thirdPartyLogId;
|
|
527
|
+
returnMessage = updateMessageResult?.returnMessage;
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
const createMessageLogResult = await platformModule.createMessageLog({ user, contactInfo, authHeader, message, additionalSubmission, recordingLink, faxDocLink, faxDownloadLink });
|
|
531
|
+
crmLogId = createMessageLogResult.logId;
|
|
532
|
+
returnMessage = createMessageLogResult?.returnMessage;
|
|
533
|
+
extraDataTracking = createMessageLogResult.extraDataTracking;
|
|
534
|
+
}
|
|
535
|
+
if (crmLogId) {
|
|
536
|
+
const createdMessageLog =
|
|
537
|
+
await MessageLogModel.create({
|
|
538
|
+
id: message.id.toString(),
|
|
539
|
+
platform,
|
|
540
|
+
conversationId: incomingData.logInfo.conversationId,
|
|
541
|
+
thirdPartyLogId: crmLogId,
|
|
542
|
+
userId,
|
|
543
|
+
conversationLogId: incomingData.logInfo.conversationLogId
|
|
544
|
+
});
|
|
545
|
+
logIds.push(createdMessageLog.id);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return { successful: true, logIds, returnMessage, extraDataTracking };
|
|
549
|
+
}
|
|
550
|
+
catch (e) {
|
|
551
|
+
console.log(`platform: ${platform} \n${e.stack}`);
|
|
552
|
+
if (e.response?.status === 429) {
|
|
553
|
+
return {
|
|
554
|
+
successful: false,
|
|
555
|
+
returnMessage: errorMessage.rateLimitErrorMessage({ platform }),
|
|
556
|
+
extraDataTracking: {
|
|
557
|
+
statusCode: e.response?.status,
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
else if (e.response?.status >= 400 && e.response?.status < 410) {
|
|
562
|
+
return {
|
|
563
|
+
successful: false,
|
|
564
|
+
returnMessage: errorMessage.authorizationErrorMessage({ platform }),
|
|
565
|
+
extraDataTracking: {
|
|
566
|
+
statusCode: e.response?.status,
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
successful: false,
|
|
572
|
+
returnMessage:
|
|
573
|
+
{
|
|
574
|
+
message: `Error creating message log`,
|
|
575
|
+
messageType: 'warning',
|
|
576
|
+
details: [
|
|
577
|
+
{
|
|
578
|
+
title: 'Details',
|
|
579
|
+
items: [
|
|
580
|
+
{
|
|
581
|
+
id: '1',
|
|
582
|
+
type: 'text',
|
|
583
|
+
text: `Please check if your account has permission to CREATE logs.`
|
|
584
|
+
}
|
|
585
|
+
]
|
|
586
|
+
}
|
|
587
|
+
],
|
|
588
|
+
ttl: 5000
|
|
589
|
+
},
|
|
590
|
+
extraDataTracking: {
|
|
591
|
+
statusCode: e.response?.status,
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function saveNoteCache({ sessionId, note }) {
|
|
598
|
+
try {
|
|
599
|
+
const now = moment();
|
|
600
|
+
const noteCache = await NoteCache.create({ sessionId, note, ttl: now.unix() + 3600 });
|
|
601
|
+
return { successful: true, returnMessage: 'Note cache saved' };
|
|
602
|
+
} catch (e) {
|
|
603
|
+
console.error(`Error saving note cache: ${e.stack}`);
|
|
604
|
+
return { successful: false, returnMessage: 'Error saving note cache' };
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
exports.createCallLog = createCallLog;
|
|
609
|
+
exports.updateCallLog = updateCallLog;
|
|
610
|
+
exports.createMessageLog = createMessageLog;
|
|
611
|
+
exports.getCallLog = getCallLog;
|
|
612
|
+
exports.saveNoteCache = saveNoteCache;
|