shuttlepro-shared 1.4.2 → 1.4.3
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/common/repositories/call.repository.js +144 -95
- package/common/repositories/index.js +2 -0
- package/common/repositories/interactive.repository.js +124 -0
- package/models/Automation.js +10 -0
- package/models/Call.js +9 -1
- package/models/Card.js +5 -0
- package/models/Chatbot.js +1 -0
- package/models/Interactive.js +70 -0
- package/models.js +2 -0
- package/package.json +1 -1
|
@@ -8,10 +8,17 @@ class CallRepository {
|
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
// Create a new call
|
|
11
|
+
// Create a new call
|
|
12
12
|
async createCall(callData) {
|
|
13
13
|
try {
|
|
14
|
-
const
|
|
14
|
+
const historyEntry = {
|
|
15
|
+
timestamp: new Date(),
|
|
16
|
+
action: "created",
|
|
17
|
+
agentId: null,
|
|
18
|
+
details: "Call created in system",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const call = await Call.create({
|
|
15
22
|
callId: callData.callId,
|
|
16
23
|
callerName: callData.callerName,
|
|
17
24
|
callerNumber: callData.callerNumber,
|
|
@@ -24,16 +31,16 @@ class CallRepository {
|
|
|
24
31
|
platformId: callData.platformId || null,
|
|
25
32
|
workspaceId: callData.workspaceId || null,
|
|
26
33
|
profileId: callData.profileId || null,
|
|
34
|
+
platformTimestamp: callData.platformTimestamp || "",
|
|
35
|
+
receiver: callData.receiver || "",
|
|
36
|
+
callHistory: [historyEntry],
|
|
27
37
|
});
|
|
28
38
|
|
|
29
|
-
call.addToHistory("created", null, "Call created in system");
|
|
30
|
-
await call.save();
|
|
31
|
-
|
|
32
39
|
await this._updateCallMetrics("created");
|
|
33
40
|
return call;
|
|
34
41
|
} catch (error) {
|
|
35
42
|
console.error(`Failed to create call ${callData.callId}:`, error);
|
|
36
|
-
|
|
43
|
+
return null;
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -59,7 +66,7 @@ class CallRepository {
|
|
|
59
66
|
return await query.exec();
|
|
60
67
|
} catch (error) {
|
|
61
68
|
console.error(`Failed to get call ${callId}:`, error);
|
|
62
|
-
|
|
69
|
+
return null;
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
72
|
|
|
@@ -130,20 +137,16 @@ class CallRepository {
|
|
|
130
137
|
};
|
|
131
138
|
} catch (error) {
|
|
132
139
|
console.error("Failed to get calls:", error);
|
|
133
|
-
|
|
140
|
+
return {
|
|
141
|
+
calls: [],
|
|
142
|
+
pagination: { page: 1, limit: 50, total: 0, pages: 0 },
|
|
143
|
+
};
|
|
134
144
|
}
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
// Update call
|
|
138
148
|
async updateCall(callId, updateData) {
|
|
139
149
|
try {
|
|
140
|
-
const call = await Call.findOne({ callId });
|
|
141
|
-
if (!call) throw new Error(`Call ${callId} not found`);
|
|
142
|
-
|
|
143
|
-
if (updateData.__v !== undefined && call.__v !== updateData.__v) {
|
|
144
|
-
throw new Error(`Concurrent update detected for call ${callId}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
150
|
const allowedFields = [
|
|
148
151
|
"status",
|
|
149
152
|
"agentId",
|
|
@@ -158,54 +161,73 @@ class CallRepository {
|
|
|
158
161
|
"profileId",
|
|
159
162
|
];
|
|
160
163
|
|
|
164
|
+
const filteredUpdate = {};
|
|
161
165
|
Object.keys(updateData).forEach((key) => {
|
|
162
166
|
if (allowedFields.includes(key)) {
|
|
163
|
-
|
|
167
|
+
filteredUpdate[key] = updateData[key];
|
|
164
168
|
}
|
|
165
169
|
});
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const updateOps = {
|
|
172
|
+
$set: {
|
|
173
|
+
...filteredUpdate,
|
|
174
|
+
lastHeartbeat: new Date(),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if (updateData.status) {
|
|
179
|
+
updateOps.$push = {
|
|
180
|
+
callHistory: {
|
|
181
|
+
timestamp: new Date(),
|
|
182
|
+
action: updateData.status,
|
|
183
|
+
agentId: updateData.agentId || null,
|
|
184
|
+
details:
|
|
185
|
+
updateData.reason || `Status changed to ${updateData.status}`,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
173
188
|
}
|
|
174
189
|
|
|
175
|
-
call.
|
|
176
|
-
|
|
177
|
-
|
|
190
|
+
const call = await Call.findOneAndUpdate({ callId }, updateOps, {
|
|
191
|
+
new: true,
|
|
192
|
+
});
|
|
193
|
+
return call || null;
|
|
178
194
|
} catch (error) {
|
|
179
195
|
console.error(`Failed to update call ${callId}:`, error);
|
|
180
|
-
|
|
196
|
+
return null;
|
|
181
197
|
}
|
|
182
198
|
}
|
|
183
199
|
|
|
184
200
|
// Assign call to agent
|
|
185
201
|
async assignCallToAgent(callId, agentId) {
|
|
186
202
|
try {
|
|
187
|
-
const call = await Call.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
const call = await Call.findOneAndUpdate(
|
|
204
|
+
{ callId, status: { $in: ["pending", "hold"] } },
|
|
205
|
+
{
|
|
206
|
+
$set: {
|
|
207
|
+
agentId,
|
|
208
|
+
status: "active",
|
|
209
|
+
startTime: new Date(),
|
|
210
|
+
isOnHold: false,
|
|
211
|
+
lastHeartbeat: new Date(),
|
|
212
|
+
},
|
|
213
|
+
$push: {
|
|
214
|
+
callHistory: {
|
|
215
|
+
timestamp: new Date(),
|
|
216
|
+
action: "answered",
|
|
217
|
+
agentId,
|
|
218
|
+
details: "Call answered by agent",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
{ new: true }
|
|
223
|
+
);
|
|
224
|
+
return call || null;
|
|
203
225
|
} catch (error) {
|
|
204
226
|
console.error(
|
|
205
227
|
`Failed to assign call ${callId} to agent ${agentId}:`,
|
|
206
228
|
error
|
|
207
229
|
);
|
|
208
|
-
|
|
230
|
+
return null;
|
|
209
231
|
}
|
|
210
232
|
}
|
|
211
233
|
|
|
@@ -235,7 +257,7 @@ class CallRepository {
|
|
|
235
257
|
return await Call.bulkWrite(bulkOps);
|
|
236
258
|
} catch (error) {
|
|
237
259
|
console.error("Failed to perform bulk update:", error);
|
|
238
|
-
|
|
260
|
+
return [];
|
|
239
261
|
}
|
|
240
262
|
}
|
|
241
263
|
|
|
@@ -262,7 +284,7 @@ class CallRepository {
|
|
|
262
284
|
return await Call.aggregate(pipeline);
|
|
263
285
|
} catch (error) {
|
|
264
286
|
console.error("Failed to get hold queue:", error);
|
|
265
|
-
|
|
287
|
+
return [];
|
|
266
288
|
}
|
|
267
289
|
}
|
|
268
290
|
|
|
@@ -278,7 +300,7 @@ class CallRepository {
|
|
|
278
300
|
.lean(options.lean !== false);
|
|
279
301
|
} catch (error) {
|
|
280
302
|
console.error("Failed to get available calls:", error);
|
|
281
|
-
|
|
303
|
+
return [];
|
|
282
304
|
}
|
|
283
305
|
}
|
|
284
306
|
|
|
@@ -338,7 +360,7 @@ class CallRepository {
|
|
|
338
360
|
return result[0] || this._getEmptyStats();
|
|
339
361
|
} catch (error) {
|
|
340
362
|
console.error("Failed to get advanced statistics:", error);
|
|
341
|
-
|
|
363
|
+
return this._getEmptyStats();
|
|
342
364
|
}
|
|
343
365
|
}
|
|
344
366
|
|
|
@@ -356,11 +378,15 @@ class CallRepository {
|
|
|
356
378
|
|
|
357
379
|
for (const call of staleCalls) {
|
|
358
380
|
if (call.agentId) {
|
|
359
|
-
await this.moveCallToHoldQueue(
|
|
360
|
-
|
|
381
|
+
const moved = await this.moveCallToHoldQueue(
|
|
382
|
+
call.callId,
|
|
383
|
+
"Connection timeout"
|
|
384
|
+
);
|
|
385
|
+
if (moved)
|
|
386
|
+
results.push({ callId: call.callId, action: "moved_to_hold" });
|
|
361
387
|
} else {
|
|
362
|
-
await this.endCall(call.callId);
|
|
363
|
-
results.push({ callId: call.callId, action: "ended" });
|
|
388
|
+
const ended = await this.endCall(call.callId);
|
|
389
|
+
if (ended) results.push({ callId: call.callId, action: "ended" });
|
|
364
390
|
}
|
|
365
391
|
}
|
|
366
392
|
|
|
@@ -368,7 +394,73 @@ class CallRepository {
|
|
|
368
394
|
return results;
|
|
369
395
|
} catch (error) {
|
|
370
396
|
console.error("Failed to cleanup stale connections:", error);
|
|
371
|
-
|
|
397
|
+
return [];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async moveCallToHoldQueue(callId, reason = "Agent disconnected") {
|
|
402
|
+
try {
|
|
403
|
+
const call = await Call.findOneAndUpdate(
|
|
404
|
+
{ callId },
|
|
405
|
+
{
|
|
406
|
+
$set: {
|
|
407
|
+
agentId: null,
|
|
408
|
+
status: "hold",
|
|
409
|
+
isOnHold: true,
|
|
410
|
+
autoAccepted: true,
|
|
411
|
+
isHoldConnection: true,
|
|
412
|
+
lastHeartbeat: new Date(),
|
|
413
|
+
},
|
|
414
|
+
$inc: { priority: 1 },
|
|
415
|
+
$push: {
|
|
416
|
+
callHistory: {
|
|
417
|
+
timestamp: new Date(),
|
|
418
|
+
action: "disconnected",
|
|
419
|
+
agentId: null,
|
|
420
|
+
details: reason,
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
{ new: true }
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
if (call) {
|
|
428
|
+
console.log(
|
|
429
|
+
`Call ${callId} moved back to hold queue due to: ${reason}`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return call || null;
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.error(`Failed to move call ${callId} to hold queue:`, error);
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async endCall(callId, agentId = null) {
|
|
440
|
+
try {
|
|
441
|
+
const call = await Call.findOneAndUpdate(
|
|
442
|
+
{ callId },
|
|
443
|
+
{
|
|
444
|
+
$set: {
|
|
445
|
+
status: "ended",
|
|
446
|
+
endTime: new Date(),
|
|
447
|
+
lastHeartbeat: new Date(),
|
|
448
|
+
},
|
|
449
|
+
$push: {
|
|
450
|
+
callHistory: {
|
|
451
|
+
timestamp: new Date(),
|
|
452
|
+
action: "ended",
|
|
453
|
+
agentId,
|
|
454
|
+
details: "Call ended",
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
{ new: true }
|
|
459
|
+
);
|
|
460
|
+
return call || null;
|
|
461
|
+
} catch (error) {
|
|
462
|
+
console.error(`Failed to end call ${callId}:`, error);
|
|
463
|
+
return null;
|
|
372
464
|
}
|
|
373
465
|
}
|
|
374
466
|
|
|
@@ -432,49 +524,6 @@ class CallRepository {
|
|
|
432
524
|
console.error("Failed to update metrics:", error);
|
|
433
525
|
}
|
|
434
526
|
}
|
|
435
|
-
|
|
436
|
-
async moveCallToHoldQueue(callId, reason = "Agent disconnected") {
|
|
437
|
-
try {
|
|
438
|
-
const call = await Call.findOne({ callId });
|
|
439
|
-
if (!call) throw new Error(`Call ${callId} not found`);
|
|
440
|
-
|
|
441
|
-
const previousAgentId = call.agentId;
|
|
442
|
-
|
|
443
|
-
call.agentId = null;
|
|
444
|
-
call.status = "hold";
|
|
445
|
-
call.isOnHold = true;
|
|
446
|
-
call.autoAccepted = true;
|
|
447
|
-
call.isHoldConnection = true;
|
|
448
|
-
call.priority = Math.min(call.priority + 1, 10);
|
|
449
|
-
call.addToHistory("disconnected", previousAgentId, reason);
|
|
450
|
-
call.updateHeartbeat();
|
|
451
|
-
|
|
452
|
-
await call.save();
|
|
453
|
-
console.log(`Call ${callId} moved back to hold queue due to: ${reason}`);
|
|
454
|
-
return call;
|
|
455
|
-
} catch (error) {
|
|
456
|
-
console.error(`Failed to move call ${callId} to hold queue:`, error);
|
|
457
|
-
throw error;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
async endCall(callId, agentId = null) {
|
|
462
|
-
try {
|
|
463
|
-
const call = await Call.findOne({ callId });
|
|
464
|
-
if (!call) throw new Error(`Call ${callId} not found`);
|
|
465
|
-
|
|
466
|
-
call.status = "ended";
|
|
467
|
-
call.endTime = new Date();
|
|
468
|
-
call.duration = call.currentDuration;
|
|
469
|
-
call.addToHistory("ended", agentId, "Call ended");
|
|
470
|
-
|
|
471
|
-
await call.save();
|
|
472
|
-
return call;
|
|
473
|
-
} catch (error) {
|
|
474
|
-
console.error(`Failed to end call ${callId}:`, error);
|
|
475
|
-
throw error;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
527
|
}
|
|
479
528
|
|
|
480
529
|
module.exports = new CallRepository();
|
|
@@ -16,6 +16,7 @@ const customerProfileRepository = require("./customerProfile.repository");
|
|
|
16
16
|
const customerTimelineRepository = require("./customerTimeline.repository");
|
|
17
17
|
const shopRepository = require("./shop.repository");
|
|
18
18
|
const callRepository = require("./call.repository");
|
|
19
|
+
const interactiveRepository = require("./interactive.repository");
|
|
19
20
|
|
|
20
21
|
exports.module = {
|
|
21
22
|
workspaceRepository,
|
|
@@ -36,4 +37,5 @@ exports.module = {
|
|
|
36
37
|
customerTimelineRepository,
|
|
37
38
|
shopRepository,
|
|
38
39
|
callRepository,
|
|
40
|
+
interactiveRepository,
|
|
39
41
|
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// repositories/interactive.repository.js
|
|
2
|
+
const Interactive = require("../../models/Interactive");
|
|
3
|
+
const { getRedisData, setRedisData } = require("../../config/redis");
|
|
4
|
+
|
|
5
|
+
const CACHE_KEY_ALL = "interactive_all";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get cached interactive for all workspaces.
|
|
9
|
+
*/
|
|
10
|
+
const getCachedAllInteractive = async () => {
|
|
11
|
+
let interactive = await getRedisData(CACHE_KEY_ALL);
|
|
12
|
+
if (!interactive) {
|
|
13
|
+
interactive = await Interactive.find({}).lean().exec();
|
|
14
|
+
await setRedisData(CACHE_KEY_ALL, interactive);
|
|
15
|
+
}
|
|
16
|
+
return interactive;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Update cached interactive for all workspaces.
|
|
21
|
+
*/
|
|
22
|
+
const updateCachedAllInteractive = async () => {
|
|
23
|
+
const interactive = await Interactive.find({}).lean().exec();
|
|
24
|
+
await setRedisData(CACHE_KEY_ALL, interactive);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get interactive for a specific workspace from cache.
|
|
29
|
+
*/
|
|
30
|
+
const getWorkspaceInteractive = async (workspaceId) => {
|
|
31
|
+
if (!workspaceId) return [];
|
|
32
|
+
const allInteractive = await getCachedAllInteractive();
|
|
33
|
+
return allInteractive.filter(
|
|
34
|
+
(item) => item.workspaceId?.toString() === workspaceId.toString()
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Get interactive by ID (cache first).
|
|
39
|
+
*/
|
|
40
|
+
const getInteractiveById = async (id) => {
|
|
41
|
+
if (!id) return null;
|
|
42
|
+
|
|
43
|
+
const allInteractive = await getCachedAllInteractive();
|
|
44
|
+
let interactive = allInteractive.find(
|
|
45
|
+
(item) => item._id?.toString() === id.toString()
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// If not found in cache, fetch from DB and update cache
|
|
49
|
+
if (!interactive) {
|
|
50
|
+
interactive = await Interactive.findById(id).lean().exec();
|
|
51
|
+
if (interactive) {
|
|
52
|
+
await updateCachedAllInteractive();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return interactive || null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create new interactive and refresh cache.
|
|
61
|
+
*/
|
|
62
|
+
const createInteractive = async (data) => {
|
|
63
|
+
const newInteractive = await Interactive.create(data);
|
|
64
|
+
if (newInteractive) {
|
|
65
|
+
await updateCachedAllInteractive();
|
|
66
|
+
}
|
|
67
|
+
return newInteractive;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Update interactive by ID and refresh cache.
|
|
72
|
+
*/
|
|
73
|
+
const updateInteractiveById = async (id, data) => {
|
|
74
|
+
const updated = await Interactive.findByIdAndUpdate(id, data, {
|
|
75
|
+
new: true,
|
|
76
|
+
}).exec();
|
|
77
|
+
if (updated) {
|
|
78
|
+
await updateCachedAllInteractive();
|
|
79
|
+
}
|
|
80
|
+
return updated;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Update interactive by filter and refresh cache.
|
|
85
|
+
*/
|
|
86
|
+
const updateSingleInteractiveByFilter = async (filter, data) => {
|
|
87
|
+
const updated = await Interactive.findOneAndUpdate(filter, data, {
|
|
88
|
+
new: true,
|
|
89
|
+
}).exec();
|
|
90
|
+
if (updated) {
|
|
91
|
+
await updateCachedAllInteractive();
|
|
92
|
+
}
|
|
93
|
+
return updated;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Delete interactive by ID and refresh cache.
|
|
98
|
+
*/
|
|
99
|
+
const deleteInteractiveById = async (id) => {
|
|
100
|
+
const deleted = await Interactive.findByIdAndDelete(id).exec();
|
|
101
|
+
if (deleted) {
|
|
102
|
+
await updateCachedAllInteractive();
|
|
103
|
+
}
|
|
104
|
+
return deleted;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Delete interactive by filter and refresh cache.
|
|
109
|
+
*/
|
|
110
|
+
const deleteInteractiveByFilter = async (filter) => {
|
|
111
|
+
await Interactive.deleteMany(filter).exec();
|
|
112
|
+
await updateCachedAllInteractive();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
updateCachedAllInteractive,
|
|
117
|
+
getWorkspaceInteractive,
|
|
118
|
+
createInteractive,
|
|
119
|
+
updateInteractiveById,
|
|
120
|
+
updateSingleInteractiveByFilter,
|
|
121
|
+
deleteInteractiveById,
|
|
122
|
+
deleteInteractiveByFilter,
|
|
123
|
+
getInteractiveById,
|
|
124
|
+
};
|
package/models/Automation.js
CHANGED
|
@@ -61,6 +61,9 @@ const AutomationAction = new Schema({
|
|
|
61
61
|
deleteComment: {
|
|
62
62
|
enabled: { type: Boolean, default: false },
|
|
63
63
|
},
|
|
64
|
+
hideComment: {
|
|
65
|
+
enabled: { type: Boolean, default: false },
|
|
66
|
+
},
|
|
64
67
|
closeChat: {
|
|
65
68
|
enabled: { type: Boolean, default: false },
|
|
66
69
|
},
|
|
@@ -96,6 +99,11 @@ const AutomationAction = new Schema({
|
|
|
96
99
|
default: null,
|
|
97
100
|
},
|
|
98
101
|
members: [userJoin],
|
|
102
|
+
templateFormId: {
|
|
103
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
104
|
+
ref: "FormTemplate",
|
|
105
|
+
default: null,
|
|
106
|
+
},
|
|
99
107
|
},
|
|
100
108
|
escalation: {
|
|
101
109
|
enabled: { type: Boolean, default: false },
|
|
@@ -160,6 +168,7 @@ const AutomationCondition = new Schema({
|
|
|
160
168
|
"post",
|
|
161
169
|
"shift",
|
|
162
170
|
"newCommentPost",
|
|
171
|
+
"profile",
|
|
163
172
|
],
|
|
164
173
|
},
|
|
165
174
|
keyValue: {
|
|
@@ -183,6 +192,7 @@ const AutomationCondition = new Schema({
|
|
|
183
192
|
"orderConfirmation",
|
|
184
193
|
"orderPublish",
|
|
185
194
|
"newCommentPost",
|
|
195
|
+
"profile",
|
|
186
196
|
],
|
|
187
197
|
},
|
|
188
198
|
subKeyValue: {
|
package/models/Call.js
CHANGED
|
@@ -55,7 +55,7 @@ const callSchema = new mongoose.Schema(
|
|
|
55
55
|
default: null,
|
|
56
56
|
},
|
|
57
57
|
duration: {
|
|
58
|
-
type: Number,
|
|
58
|
+
type: Number,
|
|
59
59
|
default: 0,
|
|
60
60
|
},
|
|
61
61
|
whatsappSdp: {
|
|
@@ -104,6 +104,14 @@ const callSchema = new mongoose.Schema(
|
|
|
104
104
|
details: String,
|
|
105
105
|
},
|
|
106
106
|
],
|
|
107
|
+
platformTimestamp: {
|
|
108
|
+
type: String,
|
|
109
|
+
default: "",
|
|
110
|
+
},
|
|
111
|
+
receiver: {
|
|
112
|
+
type: String,
|
|
113
|
+
default: "",
|
|
114
|
+
},
|
|
107
115
|
platformId: {
|
|
108
116
|
type: Schema.Types.ObjectId,
|
|
109
117
|
ref: "Integration",
|
package/models/Card.js
CHANGED
|
@@ -156,6 +156,11 @@ const cardSchema = new mongoose.Schema(
|
|
|
156
156
|
type: mongoose.Schema.Types.Mixed,
|
|
157
157
|
default: {},
|
|
158
158
|
},
|
|
159
|
+
templateFormId: {
|
|
160
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
161
|
+
ref: "FormTemplate",
|
|
162
|
+
default: null,
|
|
163
|
+
},
|
|
159
164
|
},
|
|
160
165
|
{ timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
|
|
161
166
|
);
|
package/models/Chatbot.js
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { Schema, Types, model } = require("mongoose");
|
|
2
|
+
|
|
3
|
+
const ActionSchema = new Schema({
|
|
4
|
+
type: {
|
|
5
|
+
type: String,
|
|
6
|
+
default: "",
|
|
7
|
+
},
|
|
8
|
+
interactiveId: { type: Types.ObjectId, ref: "Interactive", default: null },
|
|
9
|
+
value: { type: Schema.Types.Mixed, default: "" },
|
|
10
|
+
});
|
|
11
|
+
const InteractiveSchema = new Schema({
|
|
12
|
+
name: { type: String, default: "" },
|
|
13
|
+
workspace: { type: Types.ObjectId, ref: "Workspace", default: null },
|
|
14
|
+
type: {
|
|
15
|
+
type: String,
|
|
16
|
+
enum: ["button", "list"],
|
|
17
|
+
default: "list",
|
|
18
|
+
},
|
|
19
|
+
header: {
|
|
20
|
+
type: {
|
|
21
|
+
type: String,
|
|
22
|
+
enum: ["text", "image", "video"],
|
|
23
|
+
default: "text",
|
|
24
|
+
},
|
|
25
|
+
value: { type: String, default: "" },
|
|
26
|
+
},
|
|
27
|
+
body: {
|
|
28
|
+
type: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: "text",
|
|
31
|
+
},
|
|
32
|
+
value: { type: String, default: "" },
|
|
33
|
+
},
|
|
34
|
+
footer: {
|
|
35
|
+
type: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: "text",
|
|
38
|
+
},
|
|
39
|
+
value: { type: String, default: "" },
|
|
40
|
+
},
|
|
41
|
+
buttonTitle: {
|
|
42
|
+
type: String,
|
|
43
|
+
default: "Menu",
|
|
44
|
+
},
|
|
45
|
+
sections: [
|
|
46
|
+
{
|
|
47
|
+
title: { type: String, default: "" },
|
|
48
|
+
rows: [
|
|
49
|
+
{
|
|
50
|
+
id: { type: String, default: "" },
|
|
51
|
+
title: { type: String, default: "" },
|
|
52
|
+
description: { type: String, default: "" },
|
|
53
|
+
action: ActionSchema,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
buttons: [
|
|
59
|
+
{
|
|
60
|
+
type: { type: String, default: "reply" },
|
|
61
|
+
reply: {
|
|
62
|
+
id: { type: String, default: "" },
|
|
63
|
+
title: { type: String, default: "" },
|
|
64
|
+
action: ActionSchema,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
module.exports = model("Interactive", InteractiveSchema);
|
package/models.js
CHANGED
|
@@ -43,6 +43,7 @@ const FormTemplate = require("./models/FormTemplate");
|
|
|
43
43
|
const GoogleClientInfo = require("./models/GoogleClientInfo");
|
|
44
44
|
const GoogleCredentials = require("./models/GoogleCredentials");
|
|
45
45
|
const Integration = require("./models/Integration");
|
|
46
|
+
const Interactive = require("./models/Interactive");
|
|
46
47
|
const InternalComments = require("./models/InternalComments");
|
|
47
48
|
const InternalThreads = require("./models/InternalThreads");
|
|
48
49
|
const JobDesign = require("./models/JobDesign");
|
|
@@ -152,6 +153,7 @@ module.exports = {
|
|
|
152
153
|
GoogleClientInfo,
|
|
153
154
|
GoogleCredentials,
|
|
154
155
|
Integration,
|
|
156
|
+
Interactive,
|
|
155
157
|
InternalComments,
|
|
156
158
|
InternalThreads,
|
|
157
159
|
JobDesign,
|