@app-connect/core 1.7.24 → 1.7.26
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 +441 -441
- package/connector/developerPortal.js +31 -42
- package/connector/mock.js +84 -77
- package/connector/proxy/engine.js +164 -163
- package/connector/proxy/index.js +500 -500
- package/connector/registry.js +252 -252
- package/docs/README.md +50 -50
- package/docs/architecture.md +93 -93
- package/docs/connectors.md +116 -117
- package/docs/handlers.md +125 -125
- package/docs/libraries.md +101 -101
- package/docs/models.md +144 -144
- package/docs/routes.md +115 -115
- package/docs/tests.md +73 -73
- package/handlers/admin.js +523 -523
- package/handlers/appointment.js +193 -0
- package/handlers/auth.js +296 -296
- package/handlers/calldown.js +99 -99
- package/handlers/contact.js +280 -280
- package/handlers/disposition.js +82 -80
- package/handlers/log.js +984 -973
- package/handlers/managedAuth.js +446 -446
- package/handlers/plugin.js +208 -208
- package/handlers/user.js +142 -142
- package/index.js +3140 -2652
- package/jest.config.js +56 -56
- package/lib/analytics.js +54 -54
- package/lib/authSession.js +109 -109
- package/lib/cacheCleanup.js +21 -0
- package/lib/callLogComposer.js +898 -898
- package/lib/callLogLookup.js +34 -0
- package/lib/constants.js +8 -8
- package/lib/debugTracer.js +177 -177
- package/lib/encode.js +30 -30
- package/lib/errorHandler.js +218 -206
- package/lib/generalErrorMessage.js +41 -41
- package/lib/jwt.js +18 -18
- package/lib/logger.js +190 -190
- package/lib/migrateCallLogsSchema.js +116 -0
- package/lib/ringcentral.js +266 -266
- package/lib/s3ErrorLogReport.js +65 -65
- package/lib/sharedSMSComposer.js +471 -471
- package/lib/util.js +67 -67
- package/mcp/README.md +412 -395
- package/mcp/lib/validator.js +91 -91
- package/mcp/mcpHandler.js +425 -425
- package/mcp/tools/cancelAppointment.js +101 -0
- package/mcp/tools/checkAuthStatus.js +105 -105
- package/mcp/tools/confirmAppointment.js +101 -0
- package/mcp/tools/createAppointment.js +157 -0
- package/mcp/tools/createCallLog.js +327 -316
- package/mcp/tools/createContact.js +117 -117
- package/mcp/tools/createMessageLog.js +287 -287
- package/mcp/tools/doAuth.js +60 -60
- package/mcp/tools/findContactByName.js +93 -93
- package/mcp/tools/findContactByPhone.js +101 -101
- package/mcp/tools/getCallLog.js +111 -102
- package/mcp/tools/getGoogleFilePicker.js +99 -99
- package/mcp/tools/getHelp.js +43 -43
- package/mcp/tools/getPublicConnectors.js +94 -94
- package/mcp/tools/getSessionInfo.js +90 -90
- package/mcp/tools/index.js +51 -41
- package/mcp/tools/listAppointments.js +163 -0
- package/mcp/tools/logout.js +96 -96
- package/mcp/tools/rcGetCallLogs.js +65 -65
- package/mcp/tools/updateAppointment.js +154 -0
- package/mcp/tools/updateCallLog.js +130 -126
- package/mcp/ui/App/App.tsx +358 -358
- package/mcp/ui/App/components/AuthInfoForm.tsx +113 -113
- package/mcp/ui/App/components/AuthSuccess.tsx +22 -22
- package/mcp/ui/App/components/ConnectorList.tsx +82 -82
- package/mcp/ui/App/components/DebugPanel.tsx +43 -43
- package/mcp/ui/App/components/OAuthConnect.tsx +270 -270
- package/mcp/ui/App/lib/callTool.ts +130 -130
- package/mcp/ui/App/lib/debugLog.ts +41 -41
- package/mcp/ui/App/lib/developerPortal.ts +111 -111
- package/mcp/ui/App/main.css +5 -5
- package/mcp/ui/App/root.tsx +13 -13
- package/mcp/ui/index.html +13 -13
- package/mcp/ui/package-lock.json +6356 -6356
- package/mcp/ui/package.json +25 -25
- package/mcp/ui/tsconfig.json +26 -26
- package/mcp/ui/vite.config.ts +16 -16
- package/models/accountDataModel.js +33 -33
- package/models/adminConfigModel.js +35 -35
- package/models/cacheModel.js +30 -26
- package/models/callDownListModel.js +34 -34
- package/models/callLogModel.js +33 -27
- package/models/dynamo/connectorSchema.js +146 -146
- package/models/dynamo/lockSchema.js +24 -24
- package/models/dynamo/noteCacheSchema.js +29 -29
- package/models/llmSessionModel.js +17 -17
- package/models/messageLogModel.js +25 -25
- package/models/sequelize.js +16 -16
- package/models/userModel.js +45 -45
- package/package.json +72 -72
- package/releaseNotes.json +1093 -1073
- package/test/connector/proxy/engine.test.js +126 -93
- package/test/connector/proxy/index.test.js +279 -279
- package/test/connector/proxy/sample.json +161 -161
- package/test/connector/registry.test.js +415 -415
- package/test/handlers/admin.test.js +616 -616
- package/test/handlers/auth.test.js +1018 -1015
- package/test/handlers/contact.test.js +1014 -1014
- package/test/handlers/log.test.js +1298 -1160
- package/test/handlers/managedAuth.test.js +458 -458
- package/test/handlers/plugin.test.js +380 -380
- package/test/index.test.js +105 -105
- package/test/lib/cacheCleanup.test.js +42 -0
- package/test/lib/callLogComposer.test.js +1231 -1231
- package/test/lib/debugTracer.test.js +328 -328
- package/test/lib/jwt.test.js +176 -176
- package/test/lib/logger.test.js +206 -206
- package/test/lib/oauth.test.js +359 -359
- package/test/lib/ringcentral.test.js +467 -467
- package/test/lib/sharedSMSComposer.test.js +1084 -1084
- package/test/lib/util.test.js +329 -329
- package/test/mcp/tools/checkAuthStatus.test.js +83 -82
- package/test/mcp/tools/createCallLog.test.js +436 -436
- package/test/mcp/tools/createContact.test.js +58 -58
- package/test/mcp/tools/createMessageLog.test.js +595 -595
- package/test/mcp/tools/doAuth.test.js +113 -113
- package/test/mcp/tools/findContactByName.test.js +275 -275
- package/test/mcp/tools/findContactByPhone.test.js +296 -296
- package/test/mcp/tools/getCallLog.test.js +298 -298
- package/test/mcp/tools/getGoogleFilePicker.test.js +281 -281
- package/test/mcp/tools/getPublicConnectors.test.js +107 -107
- package/test/mcp/tools/getSessionInfo.test.js +127 -127
- package/test/mcp/tools/logout.test.js +233 -233
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -56
- package/test/mcp/tools/updateCallLog.test.js +360 -360
- package/test/models/accountDataModel.test.js +98 -98
- package/test/models/dynamo/connectorSchema.test.js +189 -189
- package/test/models/models.test.js +568 -539
- package/test/routes/managedAuthRoutes.test.js +104 -129
- package/test/setup.js +178 -178
package/handlers/log.js
CHANGED
|
@@ -1,973 +1,984 @@
|
|
|
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 { CacheModel } = require('../models/cacheModel');
|
|
6
|
-
const oauth = require('../lib/oauth');
|
|
7
|
-
const { composeCallLog } = require('../lib/callLogComposer');
|
|
8
|
-
const { composeSharedSMSLog } = require('../lib/sharedSMSComposer');
|
|
9
|
-
const connectorRegistry = require('../connector/registry');
|
|
10
|
-
const { LOG_DETAILS_FORMAT_TYPE } = require('../lib/constants');
|
|
11
|
-
const { NoteCache } = require('../models/dynamo/noteCacheSchema');
|
|
12
|
-
const { Connector } = require('../models/dynamo/connectorSchema');
|
|
13
|
-
const moment = require('moment');
|
|
14
|
-
const { getMediaReaderLinkByPlatformMediaLink } = require('../lib/util');
|
|
15
|
-
const axios = require('axios');
|
|
16
|
-
const { getPluginsFromUserSettings } = require('../lib/util');
|
|
17
|
-
const logger = require('../lib/logger');
|
|
18
|
-
const { handleApiError, handleDatabaseError } = require('../lib/errorHandler');
|
|
19
|
-
const { v4: uuidv4 } = require('uuid');
|
|
20
|
-
const { AccountDataModel } = require('../models/accountDataModel');
|
|
21
|
-
const pluginCore = require('./plugin');
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
const
|
|
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
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
if (!
|
|
316
|
-
return { successful: false, message: `
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
let
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (
|
|
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
|
-
let
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
let
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
let
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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 { CacheModel } = require('../models/cacheModel');
|
|
6
|
+
const oauth = require('../lib/oauth');
|
|
7
|
+
const { composeCallLog } = require('../lib/callLogComposer');
|
|
8
|
+
const { composeSharedSMSLog } = require('../lib/sharedSMSComposer');
|
|
9
|
+
const connectorRegistry = require('../connector/registry');
|
|
10
|
+
const { LOG_DETAILS_FORMAT_TYPE } = require('../lib/constants');
|
|
11
|
+
const { NoteCache } = require('../models/dynamo/noteCacheSchema');
|
|
12
|
+
const { Connector } = require('../models/dynamo/connectorSchema');
|
|
13
|
+
const moment = require('moment');
|
|
14
|
+
const { getMediaReaderLinkByPlatformMediaLink } = require('../lib/util');
|
|
15
|
+
const axios = require('axios');
|
|
16
|
+
const { getPluginsFromUserSettings } = require('../lib/util');
|
|
17
|
+
const logger = require('../lib/logger');
|
|
18
|
+
const { handleApiError, handleDatabaseError } = require('../lib/errorHandler');
|
|
19
|
+
const { v4: uuidv4 } = require('uuid');
|
|
20
|
+
const { AccountDataModel } = require('../models/accountDataModel');
|
|
21
|
+
const pluginCore = require('./plugin');
|
|
22
|
+
const {
|
|
23
|
+
getCallLogExtensionNumber,
|
|
24
|
+
buildCallLogSessionWhere,
|
|
25
|
+
findMatchingCallLog,
|
|
26
|
+
} = require('../lib/callLogLookup');
|
|
27
|
+
|
|
28
|
+
function mergePluginWarnings({ returnMessage, warningMessages }) {
|
|
29
|
+
if (!warningMessages.length) {
|
|
30
|
+
return returnMessage;
|
|
31
|
+
}
|
|
32
|
+
const warningMessage = warningMessages.join(' ');
|
|
33
|
+
if (!returnMessage) {
|
|
34
|
+
return {
|
|
35
|
+
message: warningMessage,
|
|
36
|
+
messageType: 'warning',
|
|
37
|
+
ttl: 5000,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...returnMessage,
|
|
42
|
+
message: `${returnMessage.message || ''} ${warningMessage}`.trim(),
|
|
43
|
+
messageType: returnMessage.messageType === 'error' ? 'error' : 'warning',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getPluginWarningMessage({ pluginId }) {
|
|
48
|
+
return `Plugin ${pluginId} skipped: missing account-level plugin jwtToken. Reinstall or re-register plugin.`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function createCallLog({ platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
|
|
52
|
+
try {
|
|
53
|
+
const extensionNumber = getCallLogExtensionNumber(incomingData);
|
|
54
|
+
let existingCallLog = null;
|
|
55
|
+
try {
|
|
56
|
+
existingCallLog = await CallLogModel.findOne({
|
|
57
|
+
where: buildCallLogSessionWhere({
|
|
58
|
+
sessionId: incomingData.logInfo.sessionId,
|
|
59
|
+
extensionNumber,
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return handleDatabaseError(error, 'Error finding existing call log');
|
|
65
|
+
}
|
|
66
|
+
if (existingCallLog) {
|
|
67
|
+
return {
|
|
68
|
+
successful: false,
|
|
69
|
+
returnMessage: {
|
|
70
|
+
message: `Existing log for session ${incomingData.logInfo.sessionId}`,
|
|
71
|
+
messageType: 'warning',
|
|
72
|
+
ttl: 3000
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
let user = null;
|
|
77
|
+
try {
|
|
78
|
+
user = await UserModel.findByPk(userId);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return handleDatabaseError(error, 'Error finding user');
|
|
82
|
+
}
|
|
83
|
+
if (!user || !user.accessToken) {
|
|
84
|
+
return {
|
|
85
|
+
successful: false,
|
|
86
|
+
returnMessage: {
|
|
87
|
+
message: `User not found`,
|
|
88
|
+
messageType: 'warning',
|
|
89
|
+
ttl: 5000
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
95
|
+
const callLog = incomingData.logInfo;
|
|
96
|
+
const additionalSubmission = incomingData.additionalSubmission;
|
|
97
|
+
let note = incomingData.note;
|
|
98
|
+
if (process.env.USE_CACHE && isFromSSCL) {
|
|
99
|
+
const noteCache = await NoteCache.get({ sessionId: incomingData.logInfo.sessionId });
|
|
100
|
+
if (noteCache) {
|
|
101
|
+
note = noteCache.note;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const aiNote = incomingData.aiNote;
|
|
105
|
+
const transcript = incomingData.transcript;
|
|
106
|
+
let proxyConfig;
|
|
107
|
+
const proxyId = user.platformAdditionalInfo?.proxyId;
|
|
108
|
+
if (proxyId) {
|
|
109
|
+
proxyConfig = await Connector.getProxyConfig(proxyId);
|
|
110
|
+
}
|
|
111
|
+
const authType = await platformModule.getAuthType({ proxyId, proxyConfig });
|
|
112
|
+
let authHeader = '';
|
|
113
|
+
switch (authType) {
|
|
114
|
+
case 'oauth':
|
|
115
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname, proxyId, proxyConfig })));
|
|
116
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
117
|
+
if (!user) {
|
|
118
|
+
return {
|
|
119
|
+
successful: false,
|
|
120
|
+
returnMessage: {
|
|
121
|
+
message: `User session expired. Please connect again.`,
|
|
122
|
+
messageType: 'warning',
|
|
123
|
+
ttl: 5000
|
|
124
|
+
},
|
|
125
|
+
isRevokeUserSession: true
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
129
|
+
break;
|
|
130
|
+
case 'apiKey':
|
|
131
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
132
|
+
authHeader = `Basic ${basicAuth}`;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
const contactNumber = callLog.direction === 'Inbound' ? callLog.from.phoneNumber : callLog.to.phoneNumber;
|
|
136
|
+
const contactId = incomingData.contactId;
|
|
137
|
+
if (!contactId || contactId === 0) {
|
|
138
|
+
return {
|
|
139
|
+
successful: false,
|
|
140
|
+
returnMessage: {
|
|
141
|
+
message: `Contact not found for number ${contactNumber}`,
|
|
142
|
+
messageType: 'warning',
|
|
143
|
+
ttl: 5000
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const contactInfo = {
|
|
148
|
+
id: contactId,
|
|
149
|
+
phoneNumber: contactNumber,
|
|
150
|
+
type: incomingData.contactType ?? "",
|
|
151
|
+
name: incomingData.contactName ?? ""
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
const pluginAsyncTaskIds = [];
|
|
156
|
+
const pluginWarnings = [];
|
|
157
|
+
// Plugins
|
|
158
|
+
const accountPlugins = await pluginCore.getPluginsFromRcAccountId({ rcAccountId: user.rcAccountId });
|
|
159
|
+
const callPlugins = accountPlugins.filter(plugin => plugin.data.supportedLogTypes.includes('call'));
|
|
160
|
+
for (const plugin of callPlugins) {
|
|
161
|
+
const pluginId = plugin.id;
|
|
162
|
+
const pluginJwtToken = plugin.data.jwtToken;
|
|
163
|
+
const pluginManifest = plugin.data;
|
|
164
|
+
const pluginEndpointUrl = pluginManifest.endpointUrl;
|
|
165
|
+
if (!pluginEndpointUrl) {
|
|
166
|
+
throw new Error('Plugin URL is not set');
|
|
167
|
+
}
|
|
168
|
+
const userConfig = pluginCore.getPluginConfigFromUserSettings({ userSettings: user.userSettings, pluginId });
|
|
169
|
+
if (plugin.data.isAsync) {
|
|
170
|
+
const asyncTaskId = `${userId}-${uuidv4()}`;
|
|
171
|
+
pluginAsyncTaskIds.push(asyncTaskId);
|
|
172
|
+
await CacheModel.create({
|
|
173
|
+
id: asyncTaskId,
|
|
174
|
+
status: 'initialized',
|
|
175
|
+
userId,
|
|
176
|
+
cacheKey: `pluginTask-${plugin.data.name}`,
|
|
177
|
+
expiry: moment().add(1, 'hour').toDate()
|
|
178
|
+
});
|
|
179
|
+
try {
|
|
180
|
+
const syncPluginTokenResponse = await axios.post(plugin.data.tokenSyncUrl, {},
|
|
181
|
+
{
|
|
182
|
+
headers: {
|
|
183
|
+
Authorization: `Bearer ${pluginJwtToken}`,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
const syncedPluginJwtToken = syncPluginTokenResponse?.data?.jwtToken ?? pluginJwtToken;
|
|
188
|
+
axios.post(pluginEndpointUrl, {
|
|
189
|
+
data: incomingData,
|
|
190
|
+
config: userConfig,
|
|
191
|
+
asyncTaskId
|
|
192
|
+
}, {
|
|
193
|
+
headers: {
|
|
194
|
+
Authorization: `Bearer ${syncedPluginJwtToken ?? pluginJwtToken}`,
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
if (syncedPluginJwtToken) {
|
|
198
|
+
pluginCore.persistPluginData({
|
|
199
|
+
rcAccountId: user.rcAccountId,
|
|
200
|
+
platformName: platform,
|
|
201
|
+
pluginId,
|
|
202
|
+
jwtToken: syncedPluginJwtToken,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
logger.error('Error syncing plugin JWT token', { stack: error.stack });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
const processedResultResponse = await axios.post(pluginEndpointUrl, {
|
|
212
|
+
data: incomingData,
|
|
213
|
+
config: userConfig,
|
|
214
|
+
}, {
|
|
215
|
+
headers: {
|
|
216
|
+
Authorization: `Bearer ${pluginJwtToken}`,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
const refreshedPluginJwtToken = pluginCore.getRefreshedJwtTokenFromHeaders({ headers: processedResultResponse.headers });
|
|
220
|
+
if (refreshedPluginJwtToken) {
|
|
221
|
+
pluginCore.persistPluginData({
|
|
222
|
+
rcAccountId: user.rcAccountId,
|
|
223
|
+
platformName: platform,
|
|
224
|
+
pluginId,
|
|
225
|
+
jwtToken: refreshedPluginJwtToken,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
// eslint-disable-next-line no-param-reassign
|
|
229
|
+
incomingData = processedResultResponse.data;
|
|
230
|
+
note = incomingData.note;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Compose call log details centrally
|
|
235
|
+
const logFormat = platformModule.getLogFormatType ? platformModule.getLogFormatType(platform, proxyConfig) : LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT;
|
|
236
|
+
let composedLogDetails = '';
|
|
237
|
+
if (logFormat === LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT || logFormat === LOG_DETAILS_FORMAT_TYPE.HTML || logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
|
|
238
|
+
composedLogDetails = composeCallLog({
|
|
239
|
+
logFormat,
|
|
240
|
+
callLog,
|
|
241
|
+
contactInfo,
|
|
242
|
+
user,
|
|
243
|
+
note,
|
|
244
|
+
aiNote,
|
|
245
|
+
transcript,
|
|
246
|
+
recordingLink: callLog.recording?.link,
|
|
247
|
+
subject: callLog.customSubject,
|
|
248
|
+
startTime: callLog.startTime,
|
|
249
|
+
duration: callLog.duration,
|
|
250
|
+
result: callLog.result,
|
|
251
|
+
platform,
|
|
252
|
+
ringSenseTranscript: incomingData.ringSenseTranscript,
|
|
253
|
+
ringSenseSummary: incomingData.ringSenseSummary,
|
|
254
|
+
ringSenseAIScore: incomingData.ringSenseAIScore,
|
|
255
|
+
ringSenseBulletedSummary: incomingData.ringSenseBulletedSummary,
|
|
256
|
+
ringSenseLink: incomingData.ringSenseLink,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let { logId, returnMessage, extraDataTracking } = await platformModule.createCallLog({
|
|
261
|
+
user,
|
|
262
|
+
contactInfo,
|
|
263
|
+
authHeader,
|
|
264
|
+
callLog,
|
|
265
|
+
note,
|
|
266
|
+
additionalSubmission,
|
|
267
|
+
aiNote,
|
|
268
|
+
transcript,
|
|
269
|
+
ringSenseTranscript: incomingData.ringSenseTranscript,
|
|
270
|
+
ringSenseSummary: incomingData.ringSenseSummary,
|
|
271
|
+
ringSenseAIScore: incomingData.ringSenseAIScore,
|
|
272
|
+
ringSenseBulletedSummary: incomingData.ringSenseBulletedSummary,
|
|
273
|
+
ringSenseLink: incomingData.ringSenseLink,
|
|
274
|
+
composedLogDetails,
|
|
275
|
+
hashedAccountId,
|
|
276
|
+
isFromSSCL,
|
|
277
|
+
proxyConfig,
|
|
278
|
+
});
|
|
279
|
+
if (!extraDataTracking) {
|
|
280
|
+
extraDataTracking = {};
|
|
281
|
+
}
|
|
282
|
+
extraDataTracking.withSmartNoteLog = !!aiNote;
|
|
283
|
+
extraDataTracking.withTranscript = !!transcript;
|
|
284
|
+
if (logId) {
|
|
285
|
+
try {
|
|
286
|
+
await CallLogModel.create({
|
|
287
|
+
id: incomingData.logInfo.telephonySessionId || incomingData.logInfo.id,
|
|
288
|
+
sessionId: incomingData.logInfo.sessionId,
|
|
289
|
+
extensionNumber,
|
|
290
|
+
platform,
|
|
291
|
+
thirdPartyLogId: logId,
|
|
292
|
+
userId,
|
|
293
|
+
contactId
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
return handleDatabaseError(error, 'Error creating call log');
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
successful: !!logId,
|
|
301
|
+
logId,
|
|
302
|
+
returnMessage: mergePluginWarnings({ returnMessage, warningMessages: pluginWarnings }),
|
|
303
|
+
extraDataTracking,
|
|
304
|
+
pluginAsyncTaskIds
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
} catch (e) {
|
|
308
|
+
return handleApiError(e, platform, 'createCallLog', { userId });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function getCallLog({ userId, sessionIds, extensionNumber, platform, requireDetails }) {
|
|
313
|
+
try {
|
|
314
|
+
let user = await UserModel.findByPk(userId);
|
|
315
|
+
if (!user || !user.accessToken) {
|
|
316
|
+
return { successful: false, message: `Contact not found` };
|
|
317
|
+
}
|
|
318
|
+
let logs = [];
|
|
319
|
+
let returnMessage = null;
|
|
320
|
+
let extraDataTracking = {};
|
|
321
|
+
|
|
322
|
+
// Handle undefined or null sessionIds
|
|
323
|
+
if (!sessionIds) {
|
|
324
|
+
return { successful: false, message: `No session IDs provided` };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
let sessionIdsArray = sessionIds.split(',');
|
|
328
|
+
if (sessionIdsArray.length > 5) {
|
|
329
|
+
sessionIdsArray = sessionIdsArray.slice(0, 5);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (requireDetails) {
|
|
333
|
+
const proxyId = user.platformAdditionalInfo?.proxyId;
|
|
334
|
+
let proxyConfig = null;
|
|
335
|
+
if (proxyId) {
|
|
336
|
+
proxyConfig = await Connector.getProxyConfig(proxyId);
|
|
337
|
+
}
|
|
338
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
339
|
+
const authType = await platformModule.getAuthType({ proxyId, proxyConfig });
|
|
340
|
+
let authHeader = '';
|
|
341
|
+
switch (authType) {
|
|
342
|
+
case 'oauth':
|
|
343
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname, proxyId, proxyConfig })));
|
|
344
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
345
|
+
if (!user) {
|
|
346
|
+
return {
|
|
347
|
+
successful: false,
|
|
348
|
+
returnMessage: {
|
|
349
|
+
message: `User session expired. Please connect again.`,
|
|
350
|
+
messageType: 'warning',
|
|
351
|
+
ttl: 5000
|
|
352
|
+
},
|
|
353
|
+
isRevokeUserSession: true
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
357
|
+
break;
|
|
358
|
+
case 'apiKey':
|
|
359
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
360
|
+
authHeader = `Basic ${basicAuth}`;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
const callLogs = await CallLogModel.findAll({
|
|
364
|
+
where: buildCallLogSessionWhere({
|
|
365
|
+
sessionIds: sessionIdsArray,
|
|
366
|
+
extensionNumber,
|
|
367
|
+
}),
|
|
368
|
+
order: [['extensionNumber', 'ASC']]
|
|
369
|
+
});
|
|
370
|
+
for (const sId of sessionIdsArray) {
|
|
371
|
+
if (sId == 0) {
|
|
372
|
+
logs.push({ sessionId: sId, matched: false });
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const callLog = findMatchingCallLog(callLogs, sId, extensionNumber);
|
|
376
|
+
if (!callLog) {
|
|
377
|
+
logs.push({ sessionId: sId, matched: false });
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
const getCallLogResult = await platformModule.getCallLog({ user, telephonySessionId: callLog.id, callLogId: callLog.thirdPartyLogId, contactId: callLog.contactId, authHeader, proxyConfig });
|
|
381
|
+
returnMessage = getCallLogResult.returnMessage;
|
|
382
|
+
extraDataTracking = getCallLogResult.extraDataTracking;
|
|
383
|
+
logs.push({ sessionId: callLog.sessionId, matched: true, logId: callLog.thirdPartyLogId, logData: getCallLogResult.callLogInfo });
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
const callLogs = await CallLogModel.findAll({
|
|
389
|
+
where: buildCallLogSessionWhere({
|
|
390
|
+
sessionIds: sessionIdsArray,
|
|
391
|
+
extensionNumber,
|
|
392
|
+
}),
|
|
393
|
+
order: [['extensionNumber', 'ASC']]
|
|
394
|
+
});
|
|
395
|
+
for (const sId of sessionIdsArray) {
|
|
396
|
+
const callLog = findMatchingCallLog(callLogs, sId, extensionNumber);
|
|
397
|
+
if (!callLog) {
|
|
398
|
+
logs.push({ sessionId: sId, matched: false });
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
logs.push({ sessionId: callLog.sessionId, matched: true, logId: callLog.thirdPartyLogId });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return { successful: true, logs, returnMessage, extraDataTracking };
|
|
406
|
+
}
|
|
407
|
+
catch (e) {
|
|
408
|
+
return handleApiError(e, platform, 'getCallLog', { userId, sessionIds, requireDetails, extensionNumber });
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async function updateCallLog({ platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
|
|
413
|
+
try {
|
|
414
|
+
const extensionNumber = getCallLogExtensionNumber(incomingData);
|
|
415
|
+
let existingCallLog = null;
|
|
416
|
+
try {
|
|
417
|
+
existingCallLog = await CallLogModel.findOne({
|
|
418
|
+
where: buildCallLogSessionWhere({
|
|
419
|
+
sessionId: incomingData.sessionId,
|
|
420
|
+
extensionNumber,
|
|
421
|
+
})
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
return handleDatabaseError(error, 'Error finding existing call log');
|
|
426
|
+
}
|
|
427
|
+
if (existingCallLog) {
|
|
428
|
+
let user = await UserModel.findByPk(userId);
|
|
429
|
+
if (!user || !user.accessToken) {
|
|
430
|
+
return { successful: false, message: `Contact not found` };
|
|
431
|
+
}
|
|
432
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
433
|
+
const proxyId = user.platformAdditionalInfo?.proxyId;
|
|
434
|
+
let proxyConfig = null;
|
|
435
|
+
if (proxyId) {
|
|
436
|
+
proxyConfig = await Connector.getProxyConfig(proxyId);
|
|
437
|
+
}
|
|
438
|
+
const authType = await platformModule.getAuthType({ proxyId, proxyConfig });
|
|
439
|
+
let authHeader = '';
|
|
440
|
+
switch (authType) {
|
|
441
|
+
case 'oauth':
|
|
442
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname, proxyId, proxyConfig })));
|
|
443
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
444
|
+
if (!user) {
|
|
445
|
+
return {
|
|
446
|
+
successful: false,
|
|
447
|
+
returnMessage: {
|
|
448
|
+
message: `User session expired. Please connect again.`,
|
|
449
|
+
messageType: 'warning',
|
|
450
|
+
ttl: 5000
|
|
451
|
+
},
|
|
452
|
+
isRevokeUserSession: true
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
456
|
+
break;
|
|
457
|
+
case 'apiKey':
|
|
458
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
459
|
+
authHeader = `Basic ${basicAuth}`;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const pluginAsyncTaskIds = [];
|
|
464
|
+
const pluginWarnings = [];
|
|
465
|
+
// Plugins
|
|
466
|
+
const accountPlugins = await pluginCore.getPluginsFromRcAccountId({ rcAccountId: user.rcAccountId });
|
|
467
|
+
const callPlugins = accountPlugins.filter(plugin => plugin.data.supportedLogTypes.includes('call'));
|
|
468
|
+
for (const plugin of callPlugins) {
|
|
469
|
+
const pluginId = plugin.id;
|
|
470
|
+
const pluginJwtToken = plugin.data.jwtToken;
|
|
471
|
+
const pluginManifest = plugin.data;
|
|
472
|
+
const pluginEndpointUrl = pluginManifest.endpointUrl;
|
|
473
|
+
if (!pluginEndpointUrl) {
|
|
474
|
+
throw new Error('Plugin URL is not set');
|
|
475
|
+
}
|
|
476
|
+
const userConfig = pluginCore.getPluginConfigFromUserSettings({ userSettings: user.userSettings, pluginId });
|
|
477
|
+
if (plugin.data.isAsync) {
|
|
478
|
+
const asyncTaskId = `${userId}-${uuidv4()}`;
|
|
479
|
+
pluginAsyncTaskIds.push(asyncTaskId);
|
|
480
|
+
await CacheModel.create({
|
|
481
|
+
id: asyncTaskId,
|
|
482
|
+
status: 'initialized',
|
|
483
|
+
userId,
|
|
484
|
+
cacheKey: `pluginTask-${plugin.data.name}`,
|
|
485
|
+
expiry: moment().add(1, 'hour').toDate()
|
|
486
|
+
});
|
|
487
|
+
try {
|
|
488
|
+
const syncPluginTokenResponse = await axios.post(plugin.data.tokenSyncUrl, {},
|
|
489
|
+
{
|
|
490
|
+
headers: {
|
|
491
|
+
Authorization: `Bearer ${pluginJwtToken}`,
|
|
492
|
+
},
|
|
493
|
+
}
|
|
494
|
+
);
|
|
495
|
+
const syncedPluginJwtToken = pluginCore.getRefreshedJwtTokenFromHeaders({ headers: syncPluginTokenResponse.headers });
|
|
496
|
+
axios.post(pluginEndpointUrl, {
|
|
497
|
+
data: { logInfo: incomingData },
|
|
498
|
+
config: userConfig,
|
|
499
|
+
asyncTaskId
|
|
500
|
+
}, {
|
|
501
|
+
headers: {
|
|
502
|
+
Authorization: `Bearer ${syncedPluginJwtToken ?? pluginJwtToken}`,
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
if (syncedPluginJwtToken) {
|
|
506
|
+
pluginCore.persistPluginData({
|
|
507
|
+
rcAccountId: user.rcAccountId,
|
|
508
|
+
platformName: platform,
|
|
509
|
+
pluginId,
|
|
510
|
+
jwtToken: syncedPluginJwtToken,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
logger.error('Error syncing plugin JWT token', { stack: error.stack });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
const processedResultResponse = await axios.post(pluginEndpointUrl, {
|
|
520
|
+
data: incomingData,
|
|
521
|
+
config: userConfig
|
|
522
|
+
}, {
|
|
523
|
+
headers: {
|
|
524
|
+
Authorization: `Bearer ${pluginJwtToken}`,
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
const refreshedPluginJwtToken = pluginCore.getRefreshedJwtTokenFromHeaders({ headers: processedResultResponse.headers });
|
|
528
|
+
if (refreshedPluginJwtToken) {
|
|
529
|
+
pluginCore.persistPluginData({
|
|
530
|
+
rcAccountId: user.rcAccountId,
|
|
531
|
+
platformName: platform,
|
|
532
|
+
pluginId,
|
|
533
|
+
jwtToken: refreshedPluginJwtToken,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
// eslint-disable-next-line no-param-reassign
|
|
537
|
+
incomingData = processedResultResponse.data;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Fetch existing call log details once to avoid duplicate API calls
|
|
542
|
+
let existingCallLogDetails = null; // Compose updated call log details centrally
|
|
543
|
+
const logFormat = platformModule.getLogFormatType ? platformModule.getLogFormatType(platform, proxyConfig) : LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT;
|
|
544
|
+
let composedLogDetails = '';
|
|
545
|
+
if (logFormat === LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT || logFormat === LOG_DETAILS_FORMAT_TYPE.HTML || logFormat === LOG_DETAILS_FORMAT_TYPE.MARKDOWN) {
|
|
546
|
+
let existingBody = '';
|
|
547
|
+
try {
|
|
548
|
+
const getLogResult = await platformModule.getCallLog({
|
|
549
|
+
user,
|
|
550
|
+
telephonySessionId: existingCallLog.id,
|
|
551
|
+
callLogId: existingCallLog.thirdPartyLogId,
|
|
552
|
+
contactId: existingCallLog.contactId,
|
|
553
|
+
authHeader,
|
|
554
|
+
proxyConfig,
|
|
555
|
+
});
|
|
556
|
+
existingCallLogDetails = getLogResult?.callLogInfo?.fullLogResponse;
|
|
557
|
+
// Extract existing body from the platform-specific response
|
|
558
|
+
if (getLogResult.callLogInfo?.fullBody) {
|
|
559
|
+
existingBody = getLogResult.callLogInfo.fullBody;
|
|
560
|
+
} else if (getLogResult.callLogInfo?.note) {
|
|
561
|
+
existingBody = getLogResult.callLogInfo.note;
|
|
562
|
+
}
|
|
563
|
+
} catch (error) {
|
|
564
|
+
logger.error('Error getting existing log details, proceeding with empty body', { stack: error.stack });
|
|
565
|
+
}
|
|
566
|
+
composedLogDetails = composeCallLog({
|
|
567
|
+
logFormat,
|
|
568
|
+
existingBody,
|
|
569
|
+
callLog: {
|
|
570
|
+
sessionId: existingCallLog.sessionId,
|
|
571
|
+
startTime: incomingData.startTime,
|
|
572
|
+
duration: incomingData.duration,
|
|
573
|
+
result: incomingData.result,
|
|
574
|
+
direction: incomingData.direction,
|
|
575
|
+
from: incomingData.from,
|
|
576
|
+
to: incomingData.to,
|
|
577
|
+
legs: incomingData.legs || [],
|
|
578
|
+
},
|
|
579
|
+
contactInfo: null, // Not needed for updates
|
|
580
|
+
user,
|
|
581
|
+
note: incomingData.note,
|
|
582
|
+
aiNote: incomingData.aiNote,
|
|
583
|
+
transcript: incomingData.transcript,
|
|
584
|
+
recordingLink: incomingData.recordingLink,
|
|
585
|
+
subject: incomingData.subject,
|
|
586
|
+
startTime: incomingData.startTime,
|
|
587
|
+
duration: incomingData.duration,
|
|
588
|
+
result: incomingData.result,
|
|
589
|
+
ringSenseTranscript: incomingData.ringSenseTranscript,
|
|
590
|
+
ringSenseSummary: incomingData.ringSenseSummary,
|
|
591
|
+
ringSenseAIScore: incomingData.ringSenseAIScore,
|
|
592
|
+
ringSenseBulletedSummary: incomingData.ringSenseBulletedSummary,
|
|
593
|
+
ringSenseLink: incomingData.ringSenseLink,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
let { updatedNote, returnMessage, extraDataTracking } = await platformModule.updateCallLog({
|
|
598
|
+
user,
|
|
599
|
+
existingCallLog,
|
|
600
|
+
authHeader,
|
|
601
|
+
recordingLink: incomingData.recordingLink,
|
|
602
|
+
recordingDownloadLink: incomingData.recordingDownloadLink,
|
|
603
|
+
subject: incomingData.subject,
|
|
604
|
+
note: incomingData.note,
|
|
605
|
+
startTime: incomingData.startTime,
|
|
606
|
+
duration: incomingData.duration,
|
|
607
|
+
result: incomingData.result,
|
|
608
|
+
aiNote: incomingData.aiNote,
|
|
609
|
+
transcript: incomingData.transcript,
|
|
610
|
+
legs: incomingData.legs || [],
|
|
611
|
+
ringSenseTranscript: incomingData.ringSenseTranscript,
|
|
612
|
+
ringSenseSummary: incomingData.ringSenseSummary,
|
|
613
|
+
ringSenseAIScore: incomingData.ringSenseAIScore,
|
|
614
|
+
ringSenseBulletedSummary: incomingData.ringSenseBulletedSummary,
|
|
615
|
+
ringSenseLink: incomingData.ringSenseLink,
|
|
616
|
+
additionalSubmission: incomingData.additionalSubmission,
|
|
617
|
+
composedLogDetails,
|
|
618
|
+
existingCallLogDetails, // Pass the fetched details to avoid duplicate API calls
|
|
619
|
+
hashedAccountId,
|
|
620
|
+
isFromSSCL,
|
|
621
|
+
proxyConfig,
|
|
622
|
+
});
|
|
623
|
+
return {
|
|
624
|
+
successful: true,
|
|
625
|
+
logId: existingCallLog.thirdPartyLogId,
|
|
626
|
+
updatedNote,
|
|
627
|
+
returnMessage: mergePluginWarnings({ returnMessage, warningMessages: pluginWarnings }),
|
|
628
|
+
extraDataTracking,
|
|
629
|
+
pluginAsyncTaskIds
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return { successful: false };
|
|
633
|
+
} catch (e) {
|
|
634
|
+
return handleApiError(e, platform, 'updateCallLog', { userId });
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
async function createMessageLog({ platform, userId, incomingData }) {
|
|
639
|
+
try {
|
|
640
|
+
let returnMessage = null;
|
|
641
|
+
let extraDataTracking = {};
|
|
642
|
+
if (incomingData.logInfo.messages.length === 0) {
|
|
643
|
+
return {
|
|
644
|
+
successful: false,
|
|
645
|
+
returnMessage:
|
|
646
|
+
{
|
|
647
|
+
message: 'No message to log.',
|
|
648
|
+
messageType: 'warning',
|
|
649
|
+
ttl: 3000
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
654
|
+
const contactNumber = incomingData.logInfo.correspondents[0].phoneNumber;
|
|
655
|
+
const additionalSubmission = incomingData.additionalSubmission;
|
|
656
|
+
let user = null;
|
|
657
|
+
try {
|
|
658
|
+
user = await UserModel.findByPk(userId);
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
return handleDatabaseError(error, 'Error finding user');
|
|
662
|
+
}
|
|
663
|
+
if (!user || !user.accessToken) {
|
|
664
|
+
return {
|
|
665
|
+
successful: false,
|
|
666
|
+
returnMessage:
|
|
667
|
+
{
|
|
668
|
+
message: `Contact not found`,
|
|
669
|
+
messageType: 'warning',
|
|
670
|
+
ttl: 5000
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
const proxyId = user.platformAdditionalInfo?.proxyId;
|
|
675
|
+
let proxyConfig = null;
|
|
676
|
+
if (proxyId) {
|
|
677
|
+
proxyConfig = await Connector.getProxyConfig(proxyId);
|
|
678
|
+
}
|
|
679
|
+
const authType = await platformModule.getAuthType({ proxyId, proxyConfig });
|
|
680
|
+
let authHeader = '';
|
|
681
|
+
switch (authType) {
|
|
682
|
+
case 'oauth':
|
|
683
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname, proxyId, proxyConfig })));
|
|
684
|
+
user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
|
|
685
|
+
if (!user) {
|
|
686
|
+
return {
|
|
687
|
+
successful: false,
|
|
688
|
+
returnMessage: {
|
|
689
|
+
message: `User session expired. Please connect again.`,
|
|
690
|
+
messageType: 'warning',
|
|
691
|
+
ttl: 5000
|
|
692
|
+
},
|
|
693
|
+
isRevokeUserSession: true
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
authHeader = `Bearer ${user.accessToken}`;
|
|
697
|
+
break;
|
|
698
|
+
case 'apiKey':
|
|
699
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
|
|
700
|
+
authHeader = `Basic ${basicAuth}`;
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
const contactId = incomingData.contactId;
|
|
704
|
+
if (!contactId) {
|
|
705
|
+
return {
|
|
706
|
+
successful: false,
|
|
707
|
+
returnMessage:
|
|
708
|
+
{
|
|
709
|
+
message: `Contact not found for number ${contactNumber}`,
|
|
710
|
+
messageType: 'warning',
|
|
711
|
+
ttl: 5000
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
const contactInfo = {
|
|
716
|
+
id: contactId,
|
|
717
|
+
phoneNumber: contactNumber,
|
|
718
|
+
type: incomingData.contactType ?? "",
|
|
719
|
+
name: incomingData.contactName ?? ""
|
|
720
|
+
};
|
|
721
|
+
const isGroupSMS = incomingData.logInfo.correspondents.length > 1;
|
|
722
|
+
// For shared SMS
|
|
723
|
+
const assigneeName = incomingData.logInfo.assignee?.name;
|
|
724
|
+
const ownerName = incomingData.logInfo.owner?.name;
|
|
725
|
+
const isSharedSMS = !!ownerName;
|
|
726
|
+
|
|
727
|
+
const pluginAsyncTaskIds = [];
|
|
728
|
+
const pluginWarnings = [];
|
|
729
|
+
// Plugins
|
|
730
|
+
const isSMS = incomingData.logInfo.messages.some(m => m.type === 'SMS');
|
|
731
|
+
const isFax = incomingData.logInfo.messages.some(m => m.type === 'Fax');
|
|
732
|
+
const accountPlugins = await pluginCore.getPluginsFromRcAccountId({ rcAccountId: user.rcAccountId });
|
|
733
|
+
const smsPlugins = isSMS ? accountPlugins.filter(plugin => plugin.data.supportedLogTypes.includes('sms')) : [];
|
|
734
|
+
const faxPlugins = isFax ? accountPlugins.filter(plugin => plugin.data.supportedLogTypes.includes('fax')) : [];
|
|
735
|
+
const plugins = [...smsPlugins, ...faxPlugins];
|
|
736
|
+
for (const plugin of plugins) {
|
|
737
|
+
const pluginId = plugin.id;
|
|
738
|
+
const pluginJwtToken = plugin.data.jwtToken;
|
|
739
|
+
const pluginManifest = plugin.data;
|
|
740
|
+
const pluginEndpointUrl = pluginManifest.endpointUrl;
|
|
741
|
+
if (!pluginEndpointUrl) {
|
|
742
|
+
throw new Error('Plugin URL is not set');
|
|
743
|
+
}
|
|
744
|
+
const userConfig = pluginCore.getPluginConfigFromUserSettings({ userSettings: user.userSettings, pluginId });
|
|
745
|
+
if (plugin.data.isAsync) {
|
|
746
|
+
const asyncTaskId = `${userId}-${uuidv4()}`;
|
|
747
|
+
pluginAsyncTaskIds.push(asyncTaskId);
|
|
748
|
+
await CacheModel.create({
|
|
749
|
+
id: asyncTaskId,
|
|
750
|
+
status: 'initialized',
|
|
751
|
+
userId,
|
|
752
|
+
cacheKey: `pluginTask-${plugin.data.name}`,
|
|
753
|
+
expiry: moment().add(1, 'hour').toDate()
|
|
754
|
+
});
|
|
755
|
+
try {
|
|
756
|
+
const syncPluginTokenResponse = await axios.post(plugin.data.tokenSyncUrl, {},
|
|
757
|
+
{
|
|
758
|
+
headers: {
|
|
759
|
+
Authorization: `Bearer ${pluginJwtToken}`,
|
|
760
|
+
},
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
const syncedPluginJwtToken = pluginCore.getRefreshedJwtTokenFromHeaders({ headers: syncPluginTokenResponse.headers });
|
|
764
|
+
axios.post(pluginEndpointUrl, {
|
|
765
|
+
data: { logInfo: incomingData },
|
|
766
|
+
config: userConfig,
|
|
767
|
+
asyncTaskId
|
|
768
|
+
}, {
|
|
769
|
+
headers: {
|
|
770
|
+
Authorization: `Bearer ${syncedPluginJwtToken ?? pluginJwtToken}`,
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
if (syncedPluginJwtToken) {
|
|
774
|
+
pluginCore.persistPluginData({
|
|
775
|
+
rcAccountId: user.rcAccountId,
|
|
776
|
+
platformName: platform,
|
|
777
|
+
pluginId,
|
|
778
|
+
jwtToken: syncedPluginJwtToken,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
logger.error('Error syncing plugin JWT token', { stack: error.stack });
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
const processedResultResponse = await axios.post(pluginEndpointUrl, {
|
|
788
|
+
data: incomingData,
|
|
789
|
+
config: userConfig,
|
|
790
|
+
}, {
|
|
791
|
+
headers: {
|
|
792
|
+
Authorization: `Bearer ${pluginJwtToken}`,
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
const refreshedPluginJwtToken = pluginCore.getRefreshedJwtTokenFromHeaders({ headers: processedResultResponse.headers });
|
|
796
|
+
if (refreshedPluginJwtToken) {
|
|
797
|
+
pluginCore.persistPluginData({
|
|
798
|
+
rcAccountId: user.rcAccountId,
|
|
799
|
+
platformName: platform,
|
|
800
|
+
pluginId,
|
|
801
|
+
jwtToken: refreshedPluginJwtToken,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
// eslint-disable-next-line no-param-reassign
|
|
805
|
+
incomingData = processedResultResponse.data;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
let messageIds = [];
|
|
810
|
+
const correspondents = [];
|
|
811
|
+
if (isGroupSMS) {
|
|
812
|
+
messageIds = incomingData.logInfo.messages.map(m => { return { id: m.id.toString() + `-${incomingData.contactId}` }; });
|
|
813
|
+
for (var i = 0; i < incomingData.logInfo.correspondents.length; i++) {
|
|
814
|
+
// find cached contact by composite key; findByPk expects raw PK values, so use where clause
|
|
815
|
+
const correspondentContactInfo = await AccountDataModel.findOne({
|
|
816
|
+
where: {
|
|
817
|
+
rcAccountId: user.rcAccountId,
|
|
818
|
+
platformName: platform,
|
|
819
|
+
dataKey: `contact-${incomingData.logInfo.correspondents[i].phoneNumber}`
|
|
820
|
+
}
|
|
821
|
+
})
|
|
822
|
+
if (correspondentContactInfo && correspondentContactInfo.data[0]?.name != incomingData.contactName) {
|
|
823
|
+
correspondents.push(correspondentContactInfo.data);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
messageIds = incomingData.logInfo.messages.map(m => { return { id: m.id.toString() }; });
|
|
829
|
+
}
|
|
830
|
+
let existingMessages = null;
|
|
831
|
+
try {
|
|
832
|
+
existingMessages = await MessageLogModel.findAll({
|
|
833
|
+
where: {
|
|
834
|
+
[Op.or]: messageIds
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
catch (error) {
|
|
839
|
+
return handleDatabaseError(error, 'Error finding existing messages');
|
|
840
|
+
}
|
|
841
|
+
const existingIds = existingMessages.map(m => m.id);
|
|
842
|
+
const logIds = [];
|
|
843
|
+
// Case: Shared SMS
|
|
844
|
+
if (isSharedSMS) {
|
|
845
|
+
const existingMessageLog = await MessageLogModel.findOne({
|
|
846
|
+
where: {
|
|
847
|
+
conversationLogId: incomingData.logInfo.conversationLogId
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
const sharedSMSLogContent = composeSharedSMSLog({ logFormat: platformModule.getLogFormatType(platform, proxyConfig), conversation: incomingData.logInfo, contactName: contactInfo.name, timezoneOffset: user.timezoneOffset });
|
|
851
|
+
if (existingMessageLog) {
|
|
852
|
+
const updateMessageResult = await platformModule.updateMessageLog({ user, contactInfo, sharedSMSLogContent, existingMessageLog: existingMessageLog, authHeader, additionalSubmission, proxyConfig });
|
|
853
|
+
returnMessage = updateMessageResult?.returnMessage;
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
const createMessageLogResult = await platformModule.createMessageLog({ user, contactInfo, sharedSMSLogContent, authHeader, additionalSubmission, proxyConfig });
|
|
857
|
+
returnMessage = createMessageLogResult?.returnMessage;
|
|
858
|
+
extraDataTracking = createMessageLogResult.extraDataTracking;
|
|
859
|
+
if (createMessageLogResult.logId) {
|
|
860
|
+
const createdMessageLog =
|
|
861
|
+
await MessageLogModel.create({
|
|
862
|
+
id: incomingData.logInfo.conversationLogId,
|
|
863
|
+
platform,
|
|
864
|
+
conversationId: incomingData.logInfo.conversationId,
|
|
865
|
+
thirdPartyLogId: createMessageLogResult.logId,
|
|
866
|
+
userId,
|
|
867
|
+
conversationLogId: incomingData.logInfo.conversationLogId
|
|
868
|
+
});
|
|
869
|
+
logIds.push(createdMessageLog.id);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
// Case: normal SMS
|
|
874
|
+
else {
|
|
875
|
+
if (isGroupSMS) {
|
|
876
|
+
// eslint-disable-next-line no-param-reassign
|
|
877
|
+
incomingData.logInfo.conversationLogId = incomingData.logInfo.conversationLogId + `-${incomingData.contactId}`;
|
|
878
|
+
// eslint-disable-next-line no-param-reassign
|
|
879
|
+
incomingData.logInfo.conversationId = incomingData.logInfo.conversationId + `-${incomingData.contactId}`;
|
|
880
|
+
}
|
|
881
|
+
// reverse the order of messages to log the oldest message first
|
|
882
|
+
const reversedMessages = incomingData.logInfo.messages.reverse();
|
|
883
|
+
for (const message of reversedMessages) {
|
|
884
|
+
if (isGroupSMS) {
|
|
885
|
+
message.id = message.id.toString() + `-${incomingData.contactId}`;
|
|
886
|
+
}
|
|
887
|
+
if (existingIds.includes(message.id.toString())) {
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
let recordingLink = null;
|
|
891
|
+
if (message.attachments && message.attachments.some(a => a.type === 'AudioRecording')) {
|
|
892
|
+
recordingLink = message.attachments.find(a => a.type === 'AudioRecording').link;
|
|
893
|
+
}
|
|
894
|
+
let faxDocLink = null;
|
|
895
|
+
let faxDownloadLink = null;
|
|
896
|
+
if (message.attachments && message.attachments.some(a => a.type === 'RenderedDocument') && incomingData.logInfo.rcAccessToken) {
|
|
897
|
+
faxDocLink = message.attachments.find(a => a.type === 'RenderedDocument').link;
|
|
898
|
+
faxDownloadLink = message.attachments.find(a => a.type === 'RenderedDocument').uri + `?access_token=${incomingData.logInfo.rcAccessToken}`
|
|
899
|
+
}
|
|
900
|
+
let imageLink = null;
|
|
901
|
+
let imageDownloadLink = null;
|
|
902
|
+
let imageContentType = null;
|
|
903
|
+
if (message.attachments && message.attachments.some(a => a.type === 'MmsAttachment' && a.contentType.startsWith('image/')) && incomingData.logInfo.rcAccessToken) {
|
|
904
|
+
const imageAttachment = message.attachments.find(a => a.type === 'MmsAttachment' && a.contentType.startsWith('image/'));
|
|
905
|
+
if (imageAttachment) {
|
|
906
|
+
imageLink = getMediaReaderLinkByPlatformMediaLink(imageAttachment?.uri);
|
|
907
|
+
imageDownloadLink = imageAttachment?.uri + `?access_token=${incomingData.logInfo.rcAccessToken}`;
|
|
908
|
+
imageContentType = imageAttachment?.contentType;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
let videoLink = null;
|
|
912
|
+
if (message.attachments && message.attachments.some(a => a.type === 'MmsAttachment')) {
|
|
913
|
+
const imageAttachment = message.attachments.find(a => a.type === 'MmsAttachment' && a.contentType.startsWith('image/'));
|
|
914
|
+
if (imageAttachment) {
|
|
915
|
+
imageLink = getMediaReaderLinkByPlatformMediaLink(imageAttachment?.uri);
|
|
916
|
+
}
|
|
917
|
+
const videoAttachment = message.attachments.find(a => a.type === 'MmsAttachment' && a.contentType.startsWith('video/'));
|
|
918
|
+
if (videoAttachment) {
|
|
919
|
+
videoLink = getMediaReaderLinkByPlatformMediaLink(videoAttachment?.uri);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const existingSameDateMessageLog = await MessageLogModel.findOne({
|
|
923
|
+
where: {
|
|
924
|
+
conversationLogId: incomingData.logInfo.conversationLogId
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
let crmLogId = ''
|
|
928
|
+
if (existingSameDateMessageLog) {
|
|
929
|
+
const updateMessageResult = await platformModule.updateMessageLog({ user, contactInfo, assigneeName, ownerName, existingMessageLog: existingSameDateMessageLog, message, authHeader, additionalSubmission, imageLink, imageDownloadLink, imageContentType, videoLink, proxyConfig });
|
|
930
|
+
crmLogId = existingSameDateMessageLog.thirdPartyLogId;
|
|
931
|
+
returnMessage = updateMessageResult?.returnMessage;
|
|
932
|
+
extraDataTracking = updateMessageResult.extraDataTracking;
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
const createMessageLogResult = await platformModule.createMessageLog({ user, contactInfo, correspondents, assigneeName, ownerName, authHeader, message, additionalSubmission, recordingLink, faxDocLink, faxDownloadLink, imageLink, imageDownloadLink, imageContentType, videoLink, proxyConfig });
|
|
936
|
+
crmLogId = createMessageLogResult.logId;
|
|
937
|
+
returnMessage = createMessageLogResult?.returnMessage;
|
|
938
|
+
extraDataTracking = createMessageLogResult.extraDataTracking;
|
|
939
|
+
}
|
|
940
|
+
if (crmLogId) {
|
|
941
|
+
try {
|
|
942
|
+
const createdMessageLog =
|
|
943
|
+
await MessageLogModel.create({
|
|
944
|
+
id: message.id.toString(),
|
|
945
|
+
platform,
|
|
946
|
+
conversationId: incomingData.logInfo.conversationId,
|
|
947
|
+
thirdPartyLogId: crmLogId,
|
|
948
|
+
userId,
|
|
949
|
+
conversationLogId: incomingData.logInfo.conversationLogId
|
|
950
|
+
});
|
|
951
|
+
logIds.push(createdMessageLog.id);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
return handleDatabaseError(error, 'Error creating message log');
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return {
|
|
959
|
+
successful: true,
|
|
960
|
+
logIds,
|
|
961
|
+
returnMessage: mergePluginWarnings({ returnMessage, warningMessages: pluginWarnings }),
|
|
962
|
+
extraDataTracking
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
catch (e) {
|
|
966
|
+
return handleApiError(e, platform, 'createMessageLog', { userId });
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
async function saveNoteCache({ platform, userId, sessionId, note }) {
|
|
971
|
+
try {
|
|
972
|
+
const now = moment();
|
|
973
|
+
await NoteCache.create({ sessionId, note, ttl: now.unix() + 3600 });
|
|
974
|
+
return { successful: true, returnMessage: 'Note cache saved' };
|
|
975
|
+
} catch (e) {
|
|
976
|
+
return handleApiError(e, platform, 'saveNoteCache', { userId, sessionId, note });
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
exports.createCallLog = createCallLog;
|
|
981
|
+
exports.updateCallLog = updateCallLog;
|
|
982
|
+
exports.createMessageLog = createMessageLog;
|
|
983
|
+
exports.getCallLog = getCallLog;
|
|
984
|
+
exports.saveNoteCache = saveNoteCache;
|