@tiledesk/tiledesk-server 2.28.2 → 3.0.0
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/CHANGELOG.md +20 -0
- package/app.js +6 -35
- package/config/native_mcp/native_mcp_servers.json +7 -0
- package/controllers/chatbot.controller.js +659 -0
- package/controllers/intents.controller.js +139 -0
- package/errorCodes.js +2 -1
- package/event/botEvent.js +43 -3
- package/middleware/errorHandler.js +41 -0
- package/models/chatbotTemplates.js +4 -0
- package/models/faq.js +10 -0
- package/models/faq_kb.js +49 -0
- package/package.json +2 -2
- package/pubmodules/aiGenerations/README.md +154 -0
- package/pubmodules/aiGenerations/index.js +7 -0
- package/pubmodules/aiGenerations/listeners.js +96 -0
- package/pubmodules/aiGenerations/models/faqKbRevision.js +51 -0
- package/pubmodules/aiGenerations/routes/aiGenerations.js +426 -0
- package/pubmodules/aiGenerations/services/agentWriter.js +286 -0
- package/pubmodules/aiGenerations/services/atomic.js +70 -0
- package/pubmodules/aiGenerations/services/references.js +78 -0
- package/pubmodules/aiGenerations/services/revisionService.js +169 -0
- package/pubmodules/aiGenerations/services/snapshot.js +109 -0
- package/pubmodules/aiGenerations/test/aiGenerationsRoute.js +503 -0
- package/pubmodules/pubModulesManager.js +17 -0
- package/routes/faq.js +10 -3
- package/routes/faq_kb.js +716 -411
- package/services/chatbotService.js +84 -5
- package/services/faqService.js +159 -45
- package/services/v3FlowService.js +416 -0
- package/template/chatbot/blank.js +21 -3
- package/template/chatbot/blank_subagent.js +139 -0
- package/template/chatbot/blank_voice.js +21 -4
- package/template/chatbot/blank_voice_twilio.js +21 -5
- package/template/chatbot/index.js +1 -0
- package/template/chatbot/subagent_notes.js +30 -0
- package/test/faqRoute.js +83 -0
- package/test/faqkbRoute.js +982 -523
- package/test/faqkbV3Route.js +436 -0
- package/test/fixtures/example-json-with-subagents.txt +1 -0
- package/test/nativemcp.test.js +6 -1
- package/test/v3FlowService.js +187 -0
- package/test/webhookRoute.js +2 -1
- package/utils/AppError.js +9 -0
- package/utils/asyncHandler.js +10 -0
package/routes/faq_kb.js
CHANGED
|
@@ -19,6 +19,11 @@ var roleChecker = require('../middleware/has-role');
|
|
|
19
19
|
const roleConstants = require('../models/roleConstants');
|
|
20
20
|
const errorCodes = require('../errorCodes');
|
|
21
21
|
const faq_kb = require('../models/faq_kb');
|
|
22
|
+
const chatbotController = require('../controllers/chatbot.controller');
|
|
23
|
+
const intentsController = require('../controllers/intents.controller');
|
|
24
|
+
const AppError = require('../utils/AppError');
|
|
25
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
26
|
+
const v3FlowService = require('../services/v3FlowService');
|
|
22
27
|
|
|
23
28
|
let chatbot_templates_api_url = process.env.CHATBOT_TEMPLATES_API_URL
|
|
24
29
|
|
|
@@ -261,57 +266,100 @@ router.put('/:faq_kbid/publish', roleChecker.hasRole('admin'), async (req, res)
|
|
|
261
266
|
}
|
|
262
267
|
|
|
263
268
|
const api_url = process.env.API_URL || configGlobal.apiUrl;
|
|
264
|
-
winston.debug("fork --> base_url: " + api_url);
|
|
269
|
+
winston.debug("fork --> base_url: " + api_url);
|
|
265
270
|
|
|
266
|
-
|
|
267
|
-
winston.debug("current project id: " + current_project_id);
|
|
268
|
-
|
|
269
|
-
let token = req.headers.authorization;
|
|
270
|
-
|
|
271
|
-
let cs = req.app.get('chatbot_service')
|
|
271
|
+
const cs = req.app.get('chatbot_service');
|
|
272
272
|
|
|
273
273
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
274
|
+
const published = await cs.publish({
|
|
275
|
+
id_faq_kb,
|
|
276
|
+
chatbot_id,
|
|
277
|
+
api_url,
|
|
278
|
+
token: req.headers.authorization,
|
|
279
|
+
project_id: req.projectid,
|
|
280
|
+
user_id: req.user.id,
|
|
281
|
+
release_note,
|
|
282
|
+
req
|
|
283
|
+
});
|
|
280
284
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
winston.
|
|
284
|
-
|
|
285
|
+
return res.status(200).send({ message: "Chatbot published successfully", bot_id: published.bot_id });
|
|
286
|
+
} catch (e) {
|
|
287
|
+
winston.error("Error Unable publish chatbot: ", e);
|
|
288
|
+
return res.status(500).send({ success: false, message: "Unable publish chatbot" });
|
|
289
|
+
}
|
|
290
|
+
});
|
|
285
291
|
|
|
286
|
-
|
|
287
|
-
let TILEBOT_ENDPOINT = process.env.API_ENDPOINT ? process.env.API_ENDPOINT + "/modules/tilebot/ext/" : ("http://localhost:" + port + "/modules/tilebot/ext/");
|
|
292
|
+
router.put('/:faq_kbid/publish/multi', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
|
|
288
293
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
294
|
+
const id_project = req.projectid;
|
|
295
|
+
const parent_id = req.params.faq_kbid;
|
|
296
|
+
const chatbots = req.body.chatbots;
|
|
297
|
+
const isValidChatbot = (chatbot) => chatbot && typeof chatbot === 'object' && !Array.isArray(chatbot) && typeof chatbot.id === 'string' && chatbot.id.trim();
|
|
293
298
|
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
if (!Array.isArray(chatbots) || chatbots.length === 0 || chatbots.some((chatbot) => !isValidChatbot(chatbot))) {
|
|
300
|
+
return res.status(400).send({ success: false, message: "chatbots must be a non-empty array of objects with an id" });
|
|
301
|
+
}
|
|
296
302
|
|
|
297
|
-
|
|
303
|
+
await chatbotController.getChatbot(parent_id, id_project);
|
|
304
|
+
await chatbotController.assertAreSubagents(
|
|
305
|
+
id_project,
|
|
306
|
+
parent_id,
|
|
307
|
+
chatbots.map((chatbot) => chatbot.id)
|
|
308
|
+
);
|
|
298
309
|
|
|
299
|
-
|
|
310
|
+
const api_url = process.env.API_URL || configGlobal.apiUrl;
|
|
311
|
+
const token = req.headers.authorization;
|
|
312
|
+
const user_id = req.user.id;
|
|
313
|
+
const release_note = req.body.release_note || "No comment";
|
|
314
|
+
const cs = req.app.get('chatbot_service');
|
|
315
|
+
const results = [];
|
|
316
|
+
|
|
317
|
+
for (const chatbot of chatbots) {
|
|
318
|
+
const id_faq_kb = chatbot.id;
|
|
319
|
+
try {
|
|
320
|
+
const published = await cs.publish({
|
|
321
|
+
id_faq_kb,
|
|
322
|
+
chatbot_id: id_faq_kb,
|
|
323
|
+
api_url,
|
|
324
|
+
token,
|
|
325
|
+
project_id: id_project,
|
|
326
|
+
user_id,
|
|
327
|
+
release_note: chatbot.release_note || release_note,
|
|
328
|
+
req
|
|
329
|
+
});
|
|
330
|
+
results.push({ id: id_faq_kb, success: true, bot_id: published.bot_id });
|
|
331
|
+
} catch (err) {
|
|
332
|
+
const error = err?.response?.data?.message || err?.response?.data?.error || err?.message || "Publish failed";
|
|
333
|
+
winston.error("Error publishing chatbot " + id_faq_kb + ": ", err);
|
|
334
|
+
results.push({ id: id_faq_kb, success: false, error });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
300
337
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
338
|
+
const failed = results.filter((result) => !result.success);
|
|
339
|
+
if (failed.length > 0) {
|
|
340
|
+
return res.status(500).send({
|
|
341
|
+
success: false,
|
|
342
|
+
message: "Some chatbots failed to publish",
|
|
343
|
+
results
|
|
306
344
|
});
|
|
307
|
-
|
|
308
|
-
return res.status(200).send({ message: "Chatbot published successfully", bot_id: forkedChatBotId });
|
|
309
|
-
|
|
310
|
-
} catch (e) {
|
|
311
|
-
winston.error("Error Unable publish chatbot: ", e);
|
|
312
|
-
return res.status(500).send({ success: false, message: "Unable publish chatbot" });
|
|
313
345
|
}
|
|
314
|
-
|
|
346
|
+
|
|
347
|
+
return res.status(200).send({
|
|
348
|
+
success: true,
|
|
349
|
+
message: "Chatbots published successfully",
|
|
350
|
+
results
|
|
351
|
+
});
|
|
352
|
+
}));
|
|
353
|
+
|
|
354
|
+
router.put('/:faq_kbid/restore/:publish_id', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
|
|
355
|
+
const chatbot = await chatbotController.restoreFromPublished(
|
|
356
|
+
req.params.faq_kbid,
|
|
357
|
+
req.params.publish_id,
|
|
358
|
+
req.projectid,
|
|
359
|
+
req.user.id
|
|
360
|
+
);
|
|
361
|
+
return res.status(200).send(chatbot);
|
|
362
|
+
}));
|
|
315
363
|
|
|
316
364
|
router.put('/:faq_kbid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) {
|
|
317
365
|
|
|
@@ -410,12 +458,81 @@ router.put('/:faq_kbid/language/:language', roleChecker.hasRoleOrTypes('admin',
|
|
|
410
458
|
|
|
411
459
|
})
|
|
412
460
|
|
|
461
|
+
/**
|
|
462
|
+
* V3 agents: the connectors list, read and written as a whole.
|
|
463
|
+
* `GET` answers the list (or null for an agent that has none).
|
|
464
|
+
* `PUT` replaces it: the body is the list (`{ version: 1, items: [...] }`), validated in shape; an
|
|
465
|
+
* `If-Match` header (or an `if_match` query parameter) carrying the `updatedAt` the client last saw turns a save on top of a newer
|
|
466
|
+
* list into a 409 that returns the current one, so two editors cannot overwrite each other blindly.
|
|
467
|
+
* The agent is marked as modified (it has to be published again) and the engine's cache is cleared.
|
|
468
|
+
* Only a V3 agent has a list: for any other agent the PUT is refused and nothing changes.
|
|
469
|
+
*/
|
|
470
|
+
router.get('/:faq_kbid/connectors', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), asyncHandler(async (req, res) => {
|
|
471
|
+
const faq_kb = await Faq_kb.findOne({ _id: req.params.faq_kbid, id_project: req.projectid }).select('+connectors').exec();
|
|
472
|
+
if (!faq_kb) {
|
|
473
|
+
return res.status(404).send({ success: false, msg: "Chatbot not found with id " + req.params.faq_kbid + " for project " + req.projectid });
|
|
474
|
+
}
|
|
475
|
+
return res.json(faq_kb.connectors === undefined ? null : faq_kb.connectors);
|
|
476
|
+
}));
|
|
477
|
+
|
|
478
|
+
router.put('/:faq_kbid/connectors', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), asyncHandler(async (req, res) => {
|
|
479
|
+
const chatbot_id = req.params.faq_kbid;
|
|
480
|
+
const id_project = req.projectid;
|
|
481
|
+
const collection = req.body;
|
|
482
|
+
|
|
483
|
+
const errors = v3FlowService.validateConnectors(collection);
|
|
484
|
+
if (errors.length > 0) {
|
|
485
|
+
return res.status(400).send({ success: false, msg: "Invalid connectors list", errors: errors });
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const faq_kb = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }).select('+connectors').exec();
|
|
489
|
+
if (!faq_kb) {
|
|
490
|
+
return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
|
|
491
|
+
}
|
|
492
|
+
if (!v3FlowService.isV3(faq_kb)) {
|
|
493
|
+
return res.status(400).send({ success: false, msg: "The connectors list belongs to V3 agents only (ds_version 'v3')" });
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// The `updatedAt` the client last saw: `If-Match` header, or `if_match` query parameter for the
|
|
497
|
+
// clients that cannot send a custom header through a CORS gateway.
|
|
498
|
+
const expected = req.get('If-Match') || req.query.if_match;
|
|
499
|
+
const current = faq_kb.connectors && faq_kb.connectors.updatedAt ? new Date(faq_kb.connectors.updatedAt) : null;
|
|
500
|
+
if (expected && current && new Date(expected).getTime() !== current.getTime()) {
|
|
501
|
+
winston.verbose("(faq_kb) connectors of " + chatbot_id + " changed since " + expected + ": conflict");
|
|
502
|
+
return res.status(409).send({ success: false, msg: "The connectors list changed since " + expected, connectors: faq_kb.connectors });
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
faq_kb.connectors = { version: collection.version, updatedAt: new Date(), items: collection.items };
|
|
506
|
+
faq_kb.modified = true;
|
|
507
|
+
faq_kb.markModified('connectors');
|
|
508
|
+
const saved = await faq_kb.save();
|
|
509
|
+
botEvent.emit('faqbot.update', saved);
|
|
510
|
+
winston.debug("(faq_kb) connectors of " + chatbot_id + " saved: " + saved.connectors.items.length + " records");
|
|
511
|
+
return res.json(saved.connectors);
|
|
512
|
+
}));
|
|
513
|
+
|
|
413
514
|
router.patch('/:faq_kbid/attributes', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { //TODO add cache_bot_here
|
|
414
515
|
var data = req.body;
|
|
415
516
|
|
|
416
517
|
const chatbot_id = req.params.faq_kbid;
|
|
417
518
|
const id_project = req.projectid;
|
|
418
519
|
|
|
520
|
+
// The version label and the connectors list are fields of the bot since 15/10/2026, not attributes.
|
|
521
|
+
// A client still sending them the old way (`dsVersion`, `connectors`) gets them promoted: written
|
|
522
|
+
// to the fields, removed from the attributes, exactly as the import does. The Design Studio V3
|
|
523
|
+
// sends them this way on purpose when it repairs an agent saved before that date.
|
|
524
|
+
let promoted = null;
|
|
525
|
+
if (v3FlowService.legacyAttributeKeys(data).length > 0) {
|
|
526
|
+
promoted = { attributes: data };
|
|
527
|
+
v3FlowService.promoteLegacyJson(promoted);
|
|
528
|
+
if (promoted.connectors !== undefined && promoted.connectors !== null) {
|
|
529
|
+
const errors = v3FlowService.validateConnectors(promoted.connectors);
|
|
530
|
+
if (errors.length > 0) {
|
|
531
|
+
return res.status(400).send({ success: false, msg: "Invalid connectors list", errors: errors });
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
419
536
|
// TODO use service method
|
|
420
537
|
Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }, function (err, updatedBot) {
|
|
421
538
|
if (err) {
|
|
@@ -440,6 +557,20 @@ router.patch('/:faq_kbid/attributes', roleChecker.hasRoleOrTypes('admin', ['bot'
|
|
|
440
557
|
updatedBot.attributes[key] = val;
|
|
441
558
|
});
|
|
442
559
|
|
|
560
|
+
if (promoted) {
|
|
561
|
+
if (promoted.ds_version && !updatedBot.ds_version) {
|
|
562
|
+
updatedBot.ds_version = promoted.ds_version;
|
|
563
|
+
}
|
|
564
|
+
if (promoted.connectors !== undefined && promoted.connectors !== null) {
|
|
565
|
+
updatedBot.connectors = { version: promoted.connectors.version, updatedAt: new Date(), items: promoted.connectors.items };
|
|
566
|
+
updatedBot.markModified('connectors');
|
|
567
|
+
updatedBot.modified = true;
|
|
568
|
+
}
|
|
569
|
+
delete updatedBot.attributes.dsVersion;
|
|
570
|
+
delete updatedBot.attributes.connectors;
|
|
571
|
+
winston.verbose("(faq_kb) attributes of " + chatbot_id + ": dsVersion/connectors promoted to the bot fields");
|
|
572
|
+
}
|
|
573
|
+
|
|
443
574
|
winston.debug("updatedBot attributes", updatedBot.attributes)
|
|
444
575
|
|
|
445
576
|
// https://stackoverflow.com/questions/24054552/mongoose-not-saving-nested-object
|
|
@@ -459,29 +590,69 @@ router.patch('/:faq_kbid/attributes', roleChecker.hasRoleOrTypes('admin', ['bot'
|
|
|
459
590
|
|
|
460
591
|
});
|
|
461
592
|
|
|
462
|
-
router.delete('/:faq_kbid', roleChecker.hasRole('admin'), function (req, res) {
|
|
593
|
+
router.delete('/:faq_kbid', roleChecker.hasRole('admin'), asyncHandler(async function (req, res) {
|
|
463
594
|
|
|
464
595
|
winston.debug(req.body);
|
|
465
596
|
|
|
466
597
|
const chatbot_id = req.params.faq_kbid;
|
|
467
598
|
const id_project = req.projectid;
|
|
468
599
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
600
|
+
let chatbot;
|
|
601
|
+
try {
|
|
602
|
+
chatbot = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project });
|
|
603
|
+
} catch (err) {
|
|
604
|
+
winston.error("Error getting chatbot: ", err);
|
|
605
|
+
return res.status(500).send({ success: false, msg: 'Error getting chatbot.' });
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (!chatbot) {
|
|
609
|
+
return res.status(404).send({
|
|
610
|
+
success: false,
|
|
611
|
+
msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (chatbot.subtype === 'subagent') {
|
|
616
|
+
let referencingFaq;
|
|
617
|
+
try {
|
|
618
|
+
referencingFaq = await chatbotController.findSubagentCaller(chatbot, id_project);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
winston.error("Error checking subagent references: ", err);
|
|
621
|
+
return res.status(500).send({ success: false, msg: 'Error checking subagent references.' });
|
|
472
622
|
}
|
|
473
623
|
|
|
474
|
-
if (
|
|
475
|
-
return res.status(
|
|
624
|
+
if (referencingFaq) {
|
|
625
|
+
return res.status(409).send({
|
|
626
|
+
success: false,
|
|
627
|
+
msg: "Cannot delete subagent because it is still referenced by another chatbot",
|
|
628
|
+
error_code: errorCodes.CHATBOT.ERRORS.SUBAGENT_IN_USE,
|
|
629
|
+
referenced_by: {
|
|
630
|
+
id_faq_kb: referencingFaq.id_faq_kb,
|
|
631
|
+
intent_display_name: referencingFaq.intent_display_name
|
|
632
|
+
}
|
|
633
|
+
});
|
|
476
634
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
let deletedFaqKb;
|
|
638
|
+
try {
|
|
639
|
+
deletedFaqKb = await Faq_kb.findOneAndDelete({ _id: chatbot_id, id_project: id_project });
|
|
640
|
+
} catch (err) {
|
|
641
|
+
winston.error("Error deleting chatbot: ", err);
|
|
642
|
+
return res.status(500).send({ success: false, msg: 'Error deleting object.' });
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (!deletedFaqKb) {
|
|
646
|
+
return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* WARNING: faq_kb is the operation result, not the faq_kb object. The event subscriber will not receive the object as expected.
|
|
651
|
+
*/
|
|
652
|
+
botEvent.emit('faqbot.delete', deletedFaqKb);
|
|
653
|
+
botEvent.emit('faqbot.deleted', { req, chatbot: deletedFaqKb });
|
|
654
|
+
return res.status(200).send({ success: true, message: "Chatbot with id " + req.params.faq_kbid + " deleted successfully" });
|
|
655
|
+
}));
|
|
485
656
|
|
|
486
657
|
router.get('/:faq_kbid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) {
|
|
487
658
|
|
|
@@ -490,7 +661,8 @@ router.get('/:faq_kbid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscripti
|
|
|
490
661
|
const chatbot_id = req.params.faq_kbid;
|
|
491
662
|
const id_project = req.projectid;
|
|
492
663
|
|
|
493
|
-
|
|
664
|
+
// The single GET is how the Design Studio opens an agent: a V3 agent needs its connectors list here.
|
|
665
|
+
Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }).select('+connectors').exec(function (err, faq_kb) { //TODO add cache_bot_here
|
|
494
666
|
if (err) {
|
|
495
667
|
return res.status(500).send({ success: false, msg: 'Error getting object.' });
|
|
496
668
|
}
|
|
@@ -549,6 +721,26 @@ router.get('/:faq_kbid/published', roleChecker.hasRoleOrTypes('admin', ['bot', '
|
|
|
549
721
|
|
|
550
722
|
})
|
|
551
723
|
|
|
724
|
+
router.get('/:faq_kbid/subagents', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), async function (req, res) {
|
|
725
|
+
|
|
726
|
+
const id_project = req.projectid;
|
|
727
|
+
const chatbot_id = req.params.faq_kbid;
|
|
728
|
+
|
|
729
|
+
let subagents;
|
|
730
|
+
try {
|
|
731
|
+
subagents = await Faq_kb.find({
|
|
732
|
+
id_project: id_project,
|
|
733
|
+
parent_id: chatbot_id,
|
|
734
|
+
trashed: { $in: [null, false] }
|
|
735
|
+
}).sort({ createdAt: -1 }).lean().exec();
|
|
736
|
+
return res.status(200).send(subagents);
|
|
737
|
+
} catch (err) {
|
|
738
|
+
winston.error("Error finding subagents: ", err);
|
|
739
|
+
return res.status(500).send({ success: false, error: "Error finding subagents from root " + chatbot_id });
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
})
|
|
743
|
+
|
|
552
744
|
router.get('/:faq_kbid/jwt', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) {
|
|
553
745
|
|
|
554
746
|
winston.debug(req.query);
|
|
@@ -613,7 +805,7 @@ router.get('/', roleChecker.hasRoleOrTypes('agent', ['bot', 'subscription']), fu
|
|
|
613
805
|
* if filter only for 'trashed = false',
|
|
614
806
|
* the bots created before the implementation of the 'trashed' property are not returned
|
|
615
807
|
*/
|
|
616
|
-
let query = { id_project: id_project, trashed: { $in: [null, false] } };
|
|
808
|
+
let query = { id_project: id_project, subtype: { $ne: "subagent" }, trashed: { $in: [null, false] } };
|
|
617
809
|
|
|
618
810
|
if (restricted_mode === true) {
|
|
619
811
|
query.agents_available = {
|
|
@@ -656,97 +848,151 @@ router.get('/', roleChecker.hasRoleOrTypes('agent', ['bot', 'subscription']), fu
|
|
|
656
848
|
})
|
|
657
849
|
});
|
|
658
850
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
851
|
+
// DEPRECATED: create shell via createBot, then import intents/subagents into it
|
|
852
|
+
// router.post('/fork/:id_faq_kb', roleChecker.hasRole('admin'), async (req, res) => {
|
|
853
|
+
//
|
|
854
|
+
// let id_faq_kb = req.params.id_faq_kb;
|
|
855
|
+
// winston.debug('id_faq_kb: ' + id_faq_kb);
|
|
856
|
+
//
|
|
857
|
+
// const api_url = process.env.API_URL || configGlobal.apiUrl;
|
|
858
|
+
// winston.debug("fork --> base_url: " + api_url); // check if correct
|
|
859
|
+
//
|
|
860
|
+
// let current_project_id = req.projectid;
|
|
861
|
+
// winston.debug("current project id: " + current_project_id);
|
|
862
|
+
//
|
|
863
|
+
// let landing_project_id = req.query.projectid;
|
|
864
|
+
// winston.debug("landing project id " + landing_project_id)
|
|
865
|
+
//
|
|
866
|
+
// let public = req.query.public;
|
|
867
|
+
// winston.debug("public " + public);
|
|
868
|
+
//
|
|
869
|
+
// let globals = req.query.globals;
|
|
870
|
+
// winston.debug("export globals " + globals);
|
|
871
|
+
//
|
|
872
|
+
// let token = req.headers.authorization;
|
|
873
|
+
//
|
|
874
|
+
// let cs = req.app.get('chatbot_service')
|
|
875
|
+
// let chatbot;
|
|
876
|
+
// try {
|
|
877
|
+
// chatbot = await cs.getBotById(id_faq_kb, public, api_url, chatbot_templates_api_url, token, current_project_id, globals);
|
|
878
|
+
// } catch (err) {
|
|
879
|
+
// return res.status(500).send({ success: false, error: "Unable to get chatbot to be forked"});
|
|
880
|
+
// }
|
|
881
|
+
//
|
|
882
|
+
// if (!chatbot) {
|
|
883
|
+
// return res.status(500).send({ success: false, message: "Unable to get chatbot to be forked" });
|
|
884
|
+
// }
|
|
885
|
+
//
|
|
886
|
+
// if (!globals) {
|
|
887
|
+
// if (chatbot.attributes) {
|
|
888
|
+
// delete chatbot.attributes.globals
|
|
889
|
+
// }
|
|
890
|
+
// }
|
|
891
|
+
//
|
|
892
|
+
// delete chatbot.modified;
|
|
893
|
+
//
|
|
894
|
+
// chatbot.template = "empty";
|
|
895
|
+
//
|
|
896
|
+
// let savedChatbot
|
|
897
|
+
// try {
|
|
898
|
+
// savedChatbot = await cs.createBot(api_url, token, chatbot, landing_project_id);
|
|
899
|
+
// winston.debug("savedChatbot: ", savedChatbot)
|
|
900
|
+
// } catch(err) {
|
|
901
|
+
// winston.error("Error creating new chatbot: ", err);
|
|
902
|
+
// return res.status(500).send({ success: false, message: "An error occurred during chatbot creation"});
|
|
903
|
+
// }
|
|
904
|
+
//
|
|
905
|
+
// if (!savedChatbot) {
|
|
906
|
+
// return res.status(500).send({ success: false, message: "Unable to create new chatbot" });
|
|
907
|
+
// }
|
|
908
|
+
//
|
|
909
|
+
// let import_result
|
|
910
|
+
// try {
|
|
911
|
+
// // Imports parent intents and linked subagents (with their intents) into the new parent.
|
|
912
|
+
// import_result = await cs.importFaqs(api_url, savedChatbot._id, token, chatbot, landing_project_id);
|
|
913
|
+
// winston.debug("imported: ", import_result);
|
|
914
|
+
// } catch(err) {
|
|
915
|
+
// winston.error("Error importing chatbot content on fork: ", err);
|
|
916
|
+
// return res.status(500).send({ success: false, message: "Error importing chatbot content (intents/subagents)"});
|
|
917
|
+
// }
|
|
918
|
+
//
|
|
919
|
+
// if (import_result?.success == false) {
|
|
920
|
+
// return res.status(500).send({ success: false, message: "Unable to import chatbot content in the new chatbot" });
|
|
921
|
+
// }
|
|
922
|
+
//
|
|
923
|
+
// return res.status(200).send({ message: "Chatbot forked successfully", bot_id: savedChatbot._id });
|
|
924
|
+
//
|
|
925
|
+
// })
|
|
926
|
+
|
|
927
|
+
// Export source chatbot, then import with create=true (parent + intents + linked subagents)
|
|
928
|
+
router.post('/fork/:id_faq_kb', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
|
|
929
|
+
|
|
930
|
+
const id_faq_kb = req.params.id_faq_kb;
|
|
664
931
|
const api_url = process.env.API_URL || configGlobal.apiUrl;
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
932
|
+
const current_project_id = req.projectid;
|
|
933
|
+
const landing_project_id = req.query.projectid;
|
|
934
|
+
const public = req.query.public;
|
|
935
|
+
const globals = req.query.globals;
|
|
936
|
+
const token = req.headers.authorization;
|
|
937
|
+
const user_id = req.user.id;
|
|
938
|
+
|
|
939
|
+
winston.debug('fork id_faq_kb: ' + id_faq_kb);
|
|
940
|
+
winston.debug("fork --> base_url: " + api_url);
|
|
668
941
|
winston.debug("current project id: " + current_project_id);
|
|
669
|
-
|
|
670
|
-
let landing_project_id = req.query.projectid;
|
|
671
|
-
winston.debug("landing project id " + landing_project_id)
|
|
672
|
-
|
|
673
|
-
let public = req.query.public;
|
|
942
|
+
winston.debug("landing project id " + landing_project_id);
|
|
674
943
|
winston.debug("public " + public);
|
|
675
|
-
|
|
676
|
-
let globals = req.query.globals;
|
|
677
944
|
winston.debug("export globals " + globals);
|
|
678
945
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
let cs = req.app.get('chatbot_service')
|
|
946
|
+
const cs = req.app.get('chatbot_service');
|
|
682
947
|
let chatbot;
|
|
683
948
|
try {
|
|
684
949
|
chatbot = await cs.getBotById(id_faq_kb, public, api_url, chatbot_templates_api_url, token, current_project_id, globals);
|
|
685
950
|
} catch (err) {
|
|
686
|
-
return res.status(500).send({ success: false, error: "Unable to get chatbot to be forked"});
|
|
951
|
+
return res.status(500).send({ success: false, error: "Unable to get chatbot to be forked" });
|
|
687
952
|
}
|
|
688
953
|
|
|
689
954
|
if (!chatbot) {
|
|
690
955
|
return res.status(500).send({ success: false, message: "Unable to get chatbot to be forked" });
|
|
691
956
|
}
|
|
692
957
|
|
|
693
|
-
if (!globals) {
|
|
694
|
-
|
|
695
|
-
delete chatbot.attributes.globals
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
delete chatbot.modified;
|
|
700
|
-
|
|
701
|
-
chatbot.template = "empty";
|
|
702
|
-
|
|
703
|
-
let savedChatbot
|
|
704
|
-
try {
|
|
705
|
-
savedChatbot = await cs.createBot(api_url, token, chatbot, landing_project_id);
|
|
706
|
-
winston.debug("savedChatbot: ", savedChatbot)
|
|
707
|
-
} catch(err) {
|
|
708
|
-
winston.error("Error creating new chatbot: ", err);
|
|
709
|
-
return res.status(500).send({ success: false, message: "An error occurred during chatbot creation"});
|
|
958
|
+
if (!globals && chatbot.attributes) {
|
|
959
|
+
delete chatbot.attributes.globals;
|
|
710
960
|
}
|
|
711
961
|
|
|
712
|
-
|
|
713
|
-
return res.status(500).send({ success: false, message: "Unable to create new chatbot" });
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
let import_result
|
|
717
|
-
try {
|
|
718
|
-
import_result = await cs.importFaqs(api_url, savedChatbot._id, token, chatbot, landing_project_id);
|
|
719
|
-
winston.debug("imported: ", import_result);
|
|
720
|
-
} catch(err) {
|
|
721
|
-
winston.error("Error importing intents on new chatbot: ", err);
|
|
722
|
-
return res.status(500).send({ success: false, message: "Error importing intents in the chatbot"});
|
|
723
|
-
}
|
|
962
|
+
delete chatbot.modified;
|
|
724
963
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
964
|
+
// Publish passes skipSubagents so actions keep the development subagent id.
|
|
965
|
+
// A plain fork still clones subagents into the copy.
|
|
966
|
+
const skipSubagents = req.query.skipSubagents === 'true';
|
|
967
|
+
|
|
968
|
+
const savedChatbot = await chatbotController.import(
|
|
969
|
+
chatbot,
|
|
970
|
+
landing_project_id,
|
|
971
|
+
null,
|
|
972
|
+
true,
|
|
973
|
+
false,
|
|
974
|
+
false,
|
|
975
|
+
user_id,
|
|
976
|
+
skipSubagents
|
|
977
|
+
);
|
|
728
978
|
|
|
729
979
|
if (req.query.for_publish !== 'true') {
|
|
730
980
|
botEvent.emit('faqbot.created', { req, chatbot: savedChatbot, id_project: landing_project_id });
|
|
731
981
|
}
|
|
732
982
|
|
|
733
983
|
return res.status(200).send({ message: "Chatbot forked successfully", bot_id: savedChatbot._id });
|
|
984
|
+
}))
|
|
734
985
|
|
|
735
|
-
|
|
986
|
+
router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.single('uploadFile'), asyncHandler(async (req, res) => {
|
|
736
987
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
winston.debug('import with option create: ' + req.query.create);
|
|
745
|
-
winston.debug('import with option replace: ' + req.query.replace);
|
|
746
|
-
winston.debug('import with option overwrite: ' + req.query.overwrite);
|
|
988
|
+
const chatbot_id = req.params.id_faq_kb;
|
|
989
|
+
const id_project = req.projectid;
|
|
990
|
+
const create = req.query.create === 'true';
|
|
991
|
+
const replace = req.query.replace === 'true';
|
|
992
|
+
const overwrite = req.query.overwrite === 'true';
|
|
993
|
+
const user_id = req.user.id;
|
|
747
994
|
|
|
748
|
-
let json_string;
|
|
749
|
-
let json;
|
|
995
|
+
let json, json_string;
|
|
750
996
|
if (req.file) {
|
|
751
997
|
json_string = req.file.buffer.toString('utf-8');
|
|
752
998
|
json = JSON.parse(json_string);
|
|
@@ -754,313 +1000,372 @@ router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.singl
|
|
|
754
1000
|
json = req.body;
|
|
755
1001
|
}
|
|
756
1002
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
// **** CREATE TRUE option ****
|
|
761
|
-
// ****************************
|
|
762
|
-
if (req.query.create === 'true') {
|
|
763
|
-
// if (json.subtype && (json.subtype === 'webhook' || json.subtype === 'copilot')) {
|
|
764
|
-
// json.template = 'empty';
|
|
765
|
-
// }
|
|
766
|
-
json.template = 'empty';
|
|
767
|
-
let savedChatbot = await faqService.create(req.projectid, req.user.id, json)
|
|
768
|
-
.catch((err) => {
|
|
769
|
-
winston.error("Error creating new chatbot")
|
|
770
|
-
return res.status(400).send({ succes: false, message: "Error creatings new chatbot", error: err })
|
|
771
|
-
})
|
|
772
|
-
|
|
773
|
-
// Edit attributes.rules
|
|
774
|
-
let attributes = json.attributes;
|
|
775
|
-
if (attributes &&
|
|
776
|
-
attributes.rules &&
|
|
777
|
-
attributes.rules.length > 0) {
|
|
778
|
-
|
|
779
|
-
await attributes.rules.forEach((rule) => {
|
|
780
|
-
if (rule.do &&
|
|
781
|
-
rule.do[0] &&
|
|
782
|
-
rule.do[0].message &&
|
|
783
|
-
rule.do[0].message.participants &&
|
|
784
|
-
rule.do[0].message.participants[0]) {
|
|
785
|
-
rule.do[0].message.participants[0] = "bot_" + savedChatbot._id
|
|
786
|
-
winston.debug("attributes rule new participant: ", rule.do[0].message.participants[0])
|
|
787
|
-
}
|
|
788
|
-
})
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
let chatbot_edited = { attributes: attributes };
|
|
792
|
-
|
|
793
|
-
let updatedChatbot = await Faq_kb.findByIdAndUpdate(savedChatbot._id, chatbot_edited, { new: true }).catch((err) => {
|
|
794
|
-
winston.error("Error updating chatbot attributes: ", err);
|
|
795
|
-
winston.debug("Skip Error updating chatbot attributes: ", err);
|
|
796
|
-
// return res.status(400).send({ success: false, message: "Error updating chatbot attributes", error: err })
|
|
797
|
-
})
|
|
798
|
-
|
|
799
|
-
botEvent.emit('faqbot.create', savedChatbot);
|
|
800
|
-
botEvent.emit('faqbot.created', { req, chatbot: savedChatbot });
|
|
801
|
-
|
|
802
|
-
if (json.intents) {
|
|
803
|
-
|
|
804
|
-
json.intents.forEach(async (intent) => {
|
|
805
|
-
|
|
806
|
-
let new_faq = {
|
|
807
|
-
id_faq_kb: savedChatbot._id,
|
|
808
|
-
id_project: req.projectid,
|
|
809
|
-
createdBy: req.user.id,
|
|
810
|
-
intent_display_name: intent.intent_display_name,
|
|
811
|
-
intent_id: intent.intent_id,
|
|
812
|
-
question: intent.question,
|
|
813
|
-
answer: intent.answer,
|
|
814
|
-
reply: intent.reply,
|
|
815
|
-
form: intent.form,
|
|
816
|
-
enabled: intent.enabled,
|
|
817
|
-
webhook_enabled: intent.webhook_enabled,
|
|
818
|
-
language: intent.language,
|
|
819
|
-
actions: intent.actions,
|
|
820
|
-
attributes: intent.attributes
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
let faq = await Faq.create(new_faq).catch((err) => {
|
|
824
|
-
if (err.code == 11000) { // should never happen
|
|
825
|
-
winston.error("Duplicate intent_display_name.");
|
|
826
|
-
winston.debug("Skip duplicated intent_display_name"); // Don't stop the flow
|
|
827
|
-
} else {
|
|
828
|
-
winston.error("Error saving new faq: ", err)
|
|
829
|
-
}
|
|
830
|
-
})
|
|
831
|
-
|
|
832
|
-
if (faq) {
|
|
833
|
-
winston.debug("new intent created")
|
|
834
|
-
faqBotEvent.emit('faq.create', faq);
|
|
835
|
-
}
|
|
836
|
-
})
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (updatedChatbot) {
|
|
840
|
-
return res.status(200).send(updatedChatbot)
|
|
841
|
-
} else {
|
|
842
|
-
return res.status(200).send(savedChatbot)
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
// *****************************
|
|
846
|
-
// **** CREATE FALSE option ****
|
|
847
|
-
// *****************************
|
|
848
|
-
else {
|
|
849
|
-
|
|
850
|
-
if (!chatbot_id) {
|
|
851
|
-
return res.status(400).send({ success: false, message: "With replace or overwrite option a id_faq_kb must be provided" })
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
let chatbot = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }).catch((err) => {
|
|
855
|
-
winston.error("Error finding chatbot with id " + chatbot_id);
|
|
856
|
-
return res.status(404).send({ success: false, message: "Error finding chatbot with id " + chatbot_id, error: err });
|
|
857
|
-
})
|
|
858
|
-
|
|
859
|
-
if (!chatbot) {
|
|
860
|
-
winston.error("Chatbot not found with id " + chatbot_id + " for project " + id_project);
|
|
861
|
-
return res.status(404).send({ success: false, message: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
if (json.webhook_enabled) {
|
|
865
|
-
chatbot.webhook_enabled = json.webhook_enabled;
|
|
866
|
-
}
|
|
867
|
-
if (json.webhook_url) {
|
|
868
|
-
chatbot.webhook_url = json.webhook_url;
|
|
869
|
-
}
|
|
870
|
-
if (json.language) {
|
|
871
|
-
chatbot.language = json.language;
|
|
872
|
-
}
|
|
873
|
-
if (json.name) {
|
|
874
|
-
chatbot.name = json.name;
|
|
875
|
-
}
|
|
876
|
-
if (json.description) {
|
|
877
|
-
chatbot.description = json.description;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (json.attributes) {
|
|
881
|
-
let attributes = json.attributes;
|
|
882
|
-
if (attributes.rules &&
|
|
883
|
-
attributes.rules.length > 0) {
|
|
884
|
-
await attributes.rules.forEach((rule) => {
|
|
885
|
-
if (rule.do &&
|
|
886
|
-
rule.do[0] &&
|
|
887
|
-
rule.do[0].message &&
|
|
888
|
-
rule.do[0].message.participants &&
|
|
889
|
-
rule.do[0].message.participants[0]) {
|
|
890
|
-
rule.do[0].message.participants[0] = "bot_" + chatbot._id
|
|
891
|
-
winston.debug("attributes rule new participant: " + rule.do[0].message.participants[0])
|
|
892
|
-
}
|
|
893
|
-
})
|
|
894
|
-
chatbot.attributes = json.attributes;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
let updatedChatbot = await Faq_kb.findByIdAndUpdate(chatbot._id, chatbot, { new: true }).catch((err) => {
|
|
899
|
-
winston.error("Error updating chatbot");
|
|
900
|
-
return res.status(400).send({ success: false, message: "Error updating chatbot", error: err })
|
|
901
|
-
})
|
|
902
|
-
|
|
903
|
-
botEvent.emit('faqbot.update', updatedChatbot);
|
|
904
|
-
|
|
905
|
-
// *****************************
|
|
906
|
-
// **** REPLACE TRUE option ****
|
|
907
|
-
// *****************************
|
|
908
|
-
if (req.query.replace === 'true') {
|
|
909
|
-
let result = await Faq.deleteMany({ id_faq_kb: chatbot._id }).catch((err) => {
|
|
910
|
-
winston.error("Unable to delete all existing faqs with id_faq_kb " + chatbot._id);
|
|
911
|
-
})
|
|
912
|
-
|
|
913
|
-
winston.verbose("All faq for chatbot " + chatbot._id + " deleted successfully")
|
|
914
|
-
winston.debug("DeleteMany faqs result ", result);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
if (json.intents) {
|
|
918
|
-
await json.intents.forEach(async (intent) => {
|
|
919
|
-
|
|
920
|
-
let new_faq = {
|
|
921
|
-
id_faq_kb: updatedChatbot._id,
|
|
922
|
-
id_project: req.projectid,
|
|
923
|
-
createdBy: req.user.id,
|
|
924
|
-
intent_display_name: intent.intent_display_name,
|
|
925
|
-
intent_id: intent.intent_id,
|
|
926
|
-
question: intent.question,
|
|
927
|
-
answer: intent.answer,
|
|
928
|
-
reply: intent.reply,
|
|
929
|
-
form: intent.form,
|
|
930
|
-
enabled: intent.enabled,
|
|
931
|
-
webhook_enabled: intent.webhook_enabled,
|
|
932
|
-
language: intent.language,
|
|
933
|
-
actions: intent.actions,
|
|
934
|
-
attributes: intent.attributes
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// *******************************
|
|
938
|
-
// **** OVERWRITE TRUE option ****
|
|
939
|
-
// *******************************
|
|
940
|
-
if (req.query.overwrite == "true") {
|
|
941
|
-
|
|
942
|
-
let savingResult = await Faq.findOneAndUpdate({ id_faq_kb: chatbot._id, intent_display_name: intent.intent_display_name }, new_faq, { new: true, upsert: true, rawResult: true }).catch((err) => {
|
|
943
|
-
winston.error("Unable to create faq: ", err);
|
|
944
|
-
})
|
|
945
|
-
|
|
946
|
-
if (savingResult) {
|
|
947
|
-
if (savingResult.lastErrorObject.updatedExisting === true) {
|
|
948
|
-
winston.verbose("updated existing intent")
|
|
949
|
-
faqBotEvent.emit('faq.update', savingResult.value);
|
|
950
|
-
} else {
|
|
951
|
-
winston.verbose("new intent created")
|
|
952
|
-
faqBotEvent.emit('faq.create', savingResult.value);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// ********************************
|
|
957
|
-
// **** OVERWRITE FALSE option ****
|
|
958
|
-
// ********************************
|
|
959
|
-
} else {
|
|
960
|
-
|
|
961
|
-
let faq = await Faq.create(new_faq).catch((err) => {
|
|
962
|
-
if (err.code == 11000) {
|
|
963
|
-
winston.error("Duplicate intent_display_name.");
|
|
964
|
-
winston.verbose("Skip duplicated intent_display_name");
|
|
965
|
-
} else {
|
|
966
|
-
winston.error("Error creating new intent: ", err);
|
|
967
|
-
}
|
|
968
|
-
})
|
|
969
|
-
|
|
970
|
-
if (faq) {
|
|
971
|
-
winston.debug("new intent created: ", faq)
|
|
972
|
-
faqBotEvent.emit('faq.create', faq);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
})
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
if (updatedChatbot) {
|
|
979
|
-
return res.send(updatedChatbot);
|
|
980
|
-
} else {
|
|
981
|
-
return res.send(chatbot);
|
|
982
|
-
}
|
|
1003
|
+
const chatbot = await chatbotController.import(json, id_project, chatbot_id, create, replace, overwrite, user_id);
|
|
1004
|
+
if (create) {
|
|
1005
|
+
botEvent.emit('faqbot.created', { req, chatbot });
|
|
983
1006
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1007
|
+
return res.status(200).send(chatbot);
|
|
1008
|
+
}))
|
|
1009
|
+
|
|
1010
|
+
// router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.single('uploadFile'), async (req, res) => {
|
|
1011
|
+
|
|
1012
|
+
// let chatbot_id = req.params.id_faq_kb;
|
|
1013
|
+
// let id_project = req.projectid;
|
|
1014
|
+
|
|
1015
|
+
// winston.debug('import on id_faq_kb: ' + chatbot_id);
|
|
1016
|
+
|
|
1017
|
+
// winston.debug('import with option create: ' + req.query.create);
|
|
1018
|
+
// winston.debug('import with option replace: ' + req.query.replace);
|
|
1019
|
+
// winston.debug('import with option overwrite: ' + req.query.overwrite);
|
|
1020
|
+
|
|
1021
|
+
// let json_string;
|
|
1022
|
+
// let json;
|
|
1023
|
+
// if (req.file) {
|
|
1024
|
+
// json_string = req.file.buffer.toString('utf-8');
|
|
1025
|
+
// json = JSON.parse(json_string);
|
|
1026
|
+
// } else {
|
|
1027
|
+
// json = req.body;
|
|
1028
|
+
// }
|
|
1029
|
+
|
|
1030
|
+
// winston.debug("json source " + json_string)
|
|
1031
|
+
|
|
1032
|
+
// // ****************************
|
|
1033
|
+
// // **** CREATE TRUE option ****
|
|
1034
|
+
// // ****************************
|
|
1035
|
+
// if (req.query.create === 'true') {
|
|
1036
|
+
// // if (json.subtype && (json.subtype === 'webhook' || json.subtype === 'copilot')) {
|
|
1037
|
+
// // json.template = 'empty';
|
|
1038
|
+
// // }
|
|
1039
|
+
// json.template = 'empty';
|
|
1040
|
+
// let savedChatbot = await faqService.create(req.projectid, req.user.id, json)
|
|
1041
|
+
// .catch((err) => {
|
|
1042
|
+
// winston.error("Error creating new chatbot")
|
|
1043
|
+
// return res.status(400).send({ succes: false, message: "Error creatings new chatbot", error: err })
|
|
1044
|
+
// })
|
|
1045
|
+
|
|
1046
|
+
// // Edit attributes.rules
|
|
1047
|
+
// let attributes = json.attributes;
|
|
1048
|
+
// if (attributes &&
|
|
1049
|
+
// attributes.rules &&
|
|
1050
|
+
// attributes.rules.length > 0) {
|
|
1051
|
+
|
|
1052
|
+
// await attributes.rules.forEach((rule) => {
|
|
1053
|
+
// if (rule.do &&
|
|
1054
|
+
// rule.do[0] &&
|
|
1055
|
+
// rule.do[0].message &&
|
|
1056
|
+
// rule.do[0].message.participants &&
|
|
1057
|
+
// rule.do[0].message.participants[0]) {
|
|
1058
|
+
// rule.do[0].message.participants[0] = "bot_" + savedChatbot._id
|
|
1059
|
+
// winston.debug("attributes rule new participant: ", rule.do[0].message.participants[0])
|
|
1060
|
+
// }
|
|
1061
|
+
// })
|
|
1062
|
+
// }
|
|
1063
|
+
|
|
1064
|
+
// let chatbot_edited = { attributes: attributes };
|
|
1065
|
+
|
|
1066
|
+
// let updatedChatbot = await Faq_kb.findByIdAndUpdate(savedChatbot._id, chatbot_edited, { new: true }).catch((err) => {
|
|
1067
|
+
// winston.error("Error updating chatbot attributes: ", err);
|
|
1068
|
+
// winston.debug("Skip Error updating chatbot attributes: ", err);
|
|
1069
|
+
// // return res.status(400).send({ success: false, message: "Error updating chatbot attributes", error: err })
|
|
1070
|
+
// })
|
|
1071
|
+
|
|
1072
|
+
// botEvent.emit('faqbot.create', savedChatbot);
|
|
1073
|
+
|
|
1074
|
+
// if (json.intents) {
|
|
1075
|
+
|
|
1076
|
+
// json.intents.forEach(async (intent) => {
|
|
1077
|
+
|
|
1078
|
+
// let new_faq = {
|
|
1079
|
+
// id_faq_kb: savedChatbot._id,
|
|
1080
|
+
// id_project: req.projectid,
|
|
1081
|
+
// createdBy: req.user.id,
|
|
1082
|
+
// intent_display_name: intent.intent_display_name,
|
|
1083
|
+
// intent_id: intent.intent_id,
|
|
1084
|
+
// question: intent.question,
|
|
1085
|
+
// answer: intent.answer,
|
|
1086
|
+
// reply: intent.reply,
|
|
1087
|
+
// form: intent.form,
|
|
1088
|
+
// enabled: intent.enabled,
|
|
1089
|
+
// webhook_enabled: intent.webhook_enabled,
|
|
1090
|
+
// language: intent.language,
|
|
1091
|
+
// actions: intent.actions,
|
|
1092
|
+
// attributes: intent.attributes
|
|
1093
|
+
// }
|
|
1094
|
+
|
|
1095
|
+
// let faq = await Faq.create(new_faq).catch((err) => {
|
|
1096
|
+
// if (err.code == 11000) { // should never happen
|
|
1097
|
+
// winston.error("Duplicate intent_display_name.");
|
|
1098
|
+
// winston.debug("Skip duplicated intent_display_name"); // Don't stop the flow
|
|
1099
|
+
// } else {
|
|
1100
|
+
// winston.error("Error saving new faq: ", err)
|
|
1101
|
+
// }
|
|
1102
|
+
// })
|
|
1103
|
+
|
|
1104
|
+
// if (faq) {
|
|
1105
|
+
// winston.debug("new intent created")
|
|
1106
|
+
// faqBotEvent.emit('faq.create', faq);
|
|
1107
|
+
// }
|
|
1108
|
+
// })
|
|
1109
|
+
// }
|
|
1110
|
+
|
|
1111
|
+
// if (updatedChatbot) {
|
|
1112
|
+
// return res.status(200).send(updatedChatbot)
|
|
1113
|
+
// } else {
|
|
1114
|
+
// return res.status(200).send(savedChatbot)
|
|
1115
|
+
// }
|
|
1116
|
+
// }
|
|
1117
|
+
// // *****************************
|
|
1118
|
+
// // **** CREATE FALSE option ****
|
|
1119
|
+
// // *****************************
|
|
1120
|
+
// else {
|
|
1121
|
+
|
|
1122
|
+
// if (!chatbot_id) {
|
|
1123
|
+
// return res.status(400).send({ success: false, message: "With replace or overwrite option a id_faq_kb must be provided" })
|
|
1124
|
+
// }
|
|
1125
|
+
|
|
1126
|
+
// let chatbot = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }).catch((err) => {
|
|
1127
|
+
// winston.error("Error finding chatbot with id " + chatbot_id);
|
|
1128
|
+
// return res.status(404).send({ success: false, message: "Error finding chatbot with id " + chatbot_id, error: err });
|
|
1129
|
+
// })
|
|
1130
|
+
|
|
1131
|
+
// if (!chatbot) {
|
|
1132
|
+
// winston.error("Chatbot not found with id " + chatbot_id + " for project " + id_project);
|
|
1133
|
+
// return res.status(404).send({ success: false, message: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
|
|
1134
|
+
// }
|
|
1135
|
+
|
|
1136
|
+
// if (json.webhook_enabled) {
|
|
1137
|
+
// chatbot.webhook_enabled = json.webhook_enabled;
|
|
1138
|
+
// }
|
|
1139
|
+
// if (json.webhook_url) {
|
|
1140
|
+
// chatbot.webhook_url = json.webhook_url;
|
|
1141
|
+
// }
|
|
1142
|
+
// if (json.language) {
|
|
1143
|
+
// chatbot.language = json.language;
|
|
1144
|
+
// }
|
|
1145
|
+
// if (json.name) {
|
|
1146
|
+
// chatbot.name = json.name;
|
|
1147
|
+
// }
|
|
1148
|
+
// if (json.description) {
|
|
1149
|
+
// chatbot.description = json.description;
|
|
1150
|
+
// }
|
|
1151
|
+
|
|
1152
|
+
// if (json.attributes) {
|
|
1153
|
+
// let attributes = json.attributes;
|
|
1154
|
+
// if (attributes.rules &&
|
|
1155
|
+
// attributes.rules.length > 0) {
|
|
1156
|
+
// await attributes.rules.forEach((rule) => {
|
|
1157
|
+
// if (rule.do &&
|
|
1158
|
+
// rule.do[0] &&
|
|
1159
|
+
// rule.do[0].message &&
|
|
1160
|
+
// rule.do[0].message.participants &&
|
|
1161
|
+
// rule.do[0].message.participants[0]) {
|
|
1162
|
+
// rule.do[0].message.participants[0] = "bot_" + chatbot._id
|
|
1163
|
+
// winston.debug("attributes rule new participant: " + rule.do[0].message.participants[0])
|
|
1164
|
+
// }
|
|
1165
|
+
// })
|
|
1166
|
+
// chatbot.attributes = json.attributes;
|
|
1167
|
+
// }
|
|
1168
|
+
// }
|
|
1169
|
+
|
|
1170
|
+
// let updatedChatbot = await Faq_kb.findByIdAndUpdate(chatbot._id, chatbot, { new: true }).catch((err) => {
|
|
1171
|
+
// winston.error("Error updating chatbot");
|
|
1172
|
+
// return res.status(400).send({ success: false, message: "Error updating chatbot", error: err })
|
|
1173
|
+
// })
|
|
1174
|
+
|
|
1175
|
+
// botEvent.emit('faqbot.update', updatedChatbot);
|
|
1176
|
+
|
|
1177
|
+
// // *****************************
|
|
1178
|
+
// // **** REPLACE TRUE option ****
|
|
1179
|
+
// // *****************************
|
|
1180
|
+
// if (req.query.replace === 'true') {
|
|
1181
|
+
// let result = await Faq.deleteMany({ id_faq_kb: chatbot._id }).catch((err) => {
|
|
1182
|
+
// winston.error("Unable to delete all existing faqs with id_faq_kb " + chatbot._id);
|
|
1183
|
+
// })
|
|
1184
|
+
|
|
1185
|
+
// winston.verbose("All faq for chatbot " + chatbot._id + " deleted successfully")
|
|
1186
|
+
// winston.debug("DeleteMany faqs result ", result);
|
|
1187
|
+
// }
|
|
1188
|
+
|
|
1189
|
+
// if (json.intents) {
|
|
1190
|
+
// await json.intents.forEach(async (intent) => {
|
|
1191
|
+
|
|
1192
|
+
// let new_faq = {
|
|
1193
|
+
// id_faq_kb: updatedChatbot._id,
|
|
1194
|
+
// id_project: req.projectid,
|
|
1195
|
+
// createdBy: req.user.id,
|
|
1196
|
+
// intent_display_name: intent.intent_display_name,
|
|
1197
|
+
// intent_id: intent.intent_id,
|
|
1198
|
+
// question: intent.question,
|
|
1199
|
+
// answer: intent.answer,
|
|
1200
|
+
// reply: intent.reply,
|
|
1201
|
+
// form: intent.form,
|
|
1202
|
+
// enabled: intent.enabled,
|
|
1203
|
+
// webhook_enabled: intent.webhook_enabled,
|
|
1204
|
+
// language: intent.language,
|
|
1205
|
+
// actions: intent.actions,
|
|
1206
|
+
// attributes: intent.attributes
|
|
1207
|
+
// }
|
|
1208
|
+
|
|
1209
|
+
// // *******************************
|
|
1210
|
+
// // **** OVERWRITE TRUE option ****
|
|
1211
|
+
// // *******************************
|
|
1212
|
+
// if (req.query.overwrite == "true") {
|
|
1213
|
+
|
|
1214
|
+
// let savingResult = await Faq.findOneAndUpdate({ id_faq_kb: chatbot._id, intent_display_name: intent.intent_display_name }, new_faq, { new: true, upsert: true, rawResult: true }).catch((err) => {
|
|
1215
|
+
// winston.error("Unable to create faq: ", err);
|
|
1216
|
+
// })
|
|
1217
|
+
|
|
1218
|
+
// if (savingResult) {
|
|
1219
|
+
// if (savingResult.lastErrorObject.updatedExisting === true) {
|
|
1220
|
+
// winston.verbose("updated existing intent")
|
|
1221
|
+
// faqBotEvent.emit('faq.update', savingResult.value);
|
|
1222
|
+
// } else {
|
|
1223
|
+
// winston.verbose("new intent created")
|
|
1224
|
+
// faqBotEvent.emit('faq.create', savingResult.value);
|
|
1225
|
+
// }
|
|
1226
|
+
// }
|
|
1227
|
+
|
|
1228
|
+
// // ********************************
|
|
1229
|
+
// // **** OVERWRITE FALSE option ****
|
|
1230
|
+
// // ********************************
|
|
1231
|
+
// } else {
|
|
1232
|
+
|
|
1233
|
+
// let faq = await Faq.create(new_faq).catch((err) => {
|
|
1234
|
+
// if (err.code == 11000) {
|
|
1235
|
+
// winston.error("Duplicate intent_display_name.");
|
|
1236
|
+
// winston.verbose("Skip duplicated intent_display_name");
|
|
1237
|
+
// } else {
|
|
1238
|
+
// winston.error("Error creating new intent: ", err);
|
|
1239
|
+
// }
|
|
1240
|
+
// })
|
|
1241
|
+
|
|
1242
|
+
// if (faq) {
|
|
1243
|
+
// winston.debug("new intent created: ", faq)
|
|
1244
|
+
// faqBotEvent.emit('faq.create', faq);
|
|
1245
|
+
// }
|
|
1246
|
+
// }
|
|
1247
|
+
// })
|
|
1248
|
+
// }
|
|
1249
|
+
|
|
1250
|
+
// if (updatedChatbot) {
|
|
1251
|
+
// return res.send(updatedChatbot);
|
|
1252
|
+
// } else {
|
|
1253
|
+
// return res.send(chatbot);
|
|
1254
|
+
// }
|
|
1255
|
+
// }
|
|
1256
|
+
// })
|
|
1257
|
+
|
|
1258
|
+
router.get('/exportjson/:id_faq_kb', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
|
|
989
1259
|
|
|
990
1260
|
const chatbot_id = req.params.id_faq_kb;
|
|
991
1261
|
const id_project = req.projectid;
|
|
1262
|
+
const exportGlobals = req.query.globals === 'true';
|
|
1263
|
+
const intentsOnly = req.query.intentsOnly === 'true';
|
|
992
1264
|
|
|
993
|
-
|
|
994
|
-
if (err) {
|
|
995
|
-
winston.error('GET FAQ-KB ERROR ', err)
|
|
996
|
-
return res.status(500).send({ success: false, msg: 'Error getting bot.' });
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
if (!faq_kb) {
|
|
1000
|
-
return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id });
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
const isPublic = faq_kb.public === true;
|
|
1004
|
-
const isOwner = faq_kb.id_project === id_project;
|
|
1265
|
+
const chatbot = await chatbotController.getChatbotById(chatbot_id);
|
|
1005
1266
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1267
|
+
if (!chatbotController.canAccess(chatbot, id_project)) {
|
|
1268
|
+
throw new AppError(403, `Unauthorized to access chatbot with id ${chatbot_id} for project ${id_project}`);
|
|
1269
|
+
}
|
|
1012
1270
|
|
|
1013
|
-
|
|
1271
|
+
let intents = await intentsController.getIntents(chatbot_id);
|
|
1014
1272
|
|
|
1015
|
-
|
|
1273
|
+
if (!exportGlobals) {
|
|
1274
|
+
winston.verbose("Delete globals from attributes")
|
|
1275
|
+
delete chatbot.attributes?.globals;
|
|
1276
|
+
}
|
|
1016
1277
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1278
|
+
let subagents = await chatbotController.findSubagents(chatbot_id, id_project);
|
|
1279
|
+
await Promise.all(subagents.map(async (subagent) => {
|
|
1280
|
+
subagent.intents = await intentsController.getIntents(subagent._id);
|
|
1281
|
+
}));
|
|
1019
1282
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1283
|
+
let exportData = chatbotController.export(chatbot, intents, intentsOnly, subagents);
|
|
1284
|
+
let filename = intentsOnly ? "intents.json" : "bot.json";
|
|
1285
|
+
res.set({ "Content-Disposition": `attachment; filename="${filename}"` });
|
|
1286
|
+
return res.send(exportData);
|
|
1026
1287
|
|
|
1027
|
-
|
|
1028
|
-
webhook_enabled: faq_kb.webhook_enabled,
|
|
1029
|
-
webhook_url: faq_kb.webhook_url,
|
|
1030
|
-
language: faq_kb.language,
|
|
1031
|
-
name: faq_kb.name,
|
|
1032
|
-
slug: faq_kb.slug,
|
|
1033
|
-
type: faq_kb.type,
|
|
1034
|
-
subtype: faq_kb.subtype,
|
|
1035
|
-
description: faq_kb.description,
|
|
1036
|
-
attributes: faq_kb.attributes,
|
|
1037
|
-
intents: intents
|
|
1038
|
-
}
|
|
1288
|
+
}))
|
|
1039
1289
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
intents: intents
|
|
1043
|
-
}
|
|
1044
|
-
let intents_string = JSON.stringify(intents_obj);
|
|
1045
|
-
res.set({ "Content-Disposition": "attachment; filename=\"intents.json\"" });
|
|
1046
|
-
return res.send(intents_string);
|
|
1290
|
+
// DEPRECATED
|
|
1291
|
+
// router.get('/exportjson/:id_faq_kb', roleChecker.hasRole('admin'), (req, res) => {
|
|
1047
1292
|
|
|
1048
|
-
|
|
1293
|
+
// winston.debug("exporting bot...")
|
|
1049
1294
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
// }
|
|
1053
|
-
let json_string = JSON.stringify(json);
|
|
1054
|
-
res.set({ "Content-Disposition": "attachment; filename=\"bot.json\"" });
|
|
1055
|
-
return res.send(json_string);
|
|
1056
|
-
}
|
|
1295
|
+
// const chatbot_id = req.params.id_faq_kb;
|
|
1296
|
+
// const id_project = req.projectid;
|
|
1057
1297
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1298
|
+
// Faq_kb.findOne({ _id: chatbot_id }, (err, faq_kb) => {
|
|
1299
|
+
// if (err) {
|
|
1300
|
+
// winston.error('GET FAQ-KB ERROR ', err)
|
|
1301
|
+
// return res.status(500).send({ success: false, msg: 'Error getting bot.' });
|
|
1302
|
+
// }
|
|
1303
|
+
|
|
1304
|
+
// if (!faq_kb) {
|
|
1305
|
+
// return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id });
|
|
1306
|
+
// }
|
|
1307
|
+
|
|
1308
|
+
// const isPublic = faq_kb.public === true;
|
|
1309
|
+
// const isOwner = faq_kb.id_project === id_project;
|
|
1310
|
+
|
|
1311
|
+
// if (!isPublic && !isOwner) {
|
|
1312
|
+
// return res.status(403).send({
|
|
1313
|
+
// success: false,
|
|
1314
|
+
// msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project
|
|
1315
|
+
// });
|
|
1316
|
+
// }
|
|
1317
|
+
|
|
1318
|
+
// winston.debug('FAQ-KB: ', faq_kb)
|
|
1319
|
+
|
|
1320
|
+
// faqService.getAll(chatbot_id).then((faqs) => {
|
|
1321
|
+
|
|
1322
|
+
// // delete from exclude map intent_id
|
|
1323
|
+
// const intents = faqs.map(({ _id, id_project, topic, status, chatbot_id, createdBy, createdAt, updatedAt, __v, ...keepAttrs }) => keepAttrs)
|
|
1324
|
+
|
|
1325
|
+
// if (!req.query.globals) {
|
|
1326
|
+
// winston.verbose("Delete globals from attributes!")
|
|
1327
|
+
// if (faq_kb.attributes) {
|
|
1328
|
+
// delete faq_kb.attributes.globals;
|
|
1329
|
+
// }
|
|
1330
|
+
// }
|
|
1331
|
+
|
|
1332
|
+
// let json = {
|
|
1333
|
+
// webhook_enabled: faq_kb.webhook_enabled,
|
|
1334
|
+
// webhook_url: faq_kb.webhook_url,
|
|
1335
|
+
// language: faq_kb.language,
|
|
1336
|
+
// name: faq_kb.name,
|
|
1337
|
+
// slug: faq_kb.slug,
|
|
1338
|
+
// type: faq_kb.type,
|
|
1339
|
+
// subtype: faq_kb.subtype,
|
|
1340
|
+
// description: faq_kb.description,
|
|
1341
|
+
// attributes: faq_kb.attributes,
|
|
1342
|
+
// intents: intents
|
|
1343
|
+
// }
|
|
1344
|
+
|
|
1345
|
+
// if (req.query.intentsOnly == 'true') {
|
|
1346
|
+
// let intents_obj = {
|
|
1347
|
+
// intents: intents
|
|
1348
|
+
// }
|
|
1349
|
+
// let intents_string = JSON.stringify(intents_obj);
|
|
1350
|
+
// res.set({ "Content-Disposition": "attachment; filename=\"intents.json\"" });
|
|
1351
|
+
// return res.send(intents_string);
|
|
1352
|
+
|
|
1353
|
+
// } else {
|
|
1354
|
+
|
|
1355
|
+
// // if (req.query.file == "false") {
|
|
1356
|
+
// // return res.status(200).send(json);
|
|
1357
|
+
// // }
|
|
1358
|
+
// let json_string = JSON.stringify(json);
|
|
1359
|
+
// res.set({ "Content-Disposition": "attachment; filename=\"bot.json\"" });
|
|
1360
|
+
// return res.send(json_string);
|
|
1361
|
+
// }
|
|
1362
|
+
|
|
1363
|
+
// }).catch((err) => {
|
|
1364
|
+
// winston.error('GET FAQ ERROR: ', err)
|
|
1365
|
+
// return res.status(500).send({ success: false, msg: 'Error getting faqs.' });
|
|
1366
|
+
// })
|
|
1367
|
+
// })
|
|
1368
|
+
// })
|
|
1064
1369
|
|
|
1065
1370
|
router.post('/:faq_kbid/training', roleChecker.hasRole('admin'), function (req, res) {
|
|
1066
1371
|
|