UNPKG

@tiledesk/tiledesk-server

Version:
709 lines (577 loc) 21 kB
var express = require('express'); var router = express.Router(); var Faq = require("../models/faq"); var Faq_kb = require("../models/faq_kb"); var multer = require('multer') const faqBotEvent = require('../event/faqBotEvent'); var winston = require('../config/winston'); const faqEvent = require('../event/faqBotEvent') var parsecsv = require("fast-csv"); const botEvent = require('../event/botEvent'); const uuidv4 = require('uuid/v4'); csv = require('csv-express'); csv.separator = ';'; const axios = require("axios").default; var configGlobal = require('../config/global'); const roleConstants = require('../models/roleConstants'); const roleChecker = require('../middleware/has-role'); const { ChatbotService } = require('../services/chatbotService'); const apiUrl = process.env.API_URL || configGlobal.apiUrl; let MAX_UPLOAD_FILE_SIZE = process.env.MAX_UPLOAD_FILE_SIZE; let uploadlimits = undefined; if (MAX_UPLOAD_FILE_SIZE) { uploadlimits = {fileSize: parseInt(MAX_UPLOAD_FILE_SIZE)} ; winston.debug("Max upload file size is : " + MAX_UPLOAD_FILE_SIZE); } else { winston.debug("Max upload file size is infinity"); } var upload = multer({limits: uploadlimits}); // POST CSV FILE UPLOAD FROM CLIENT router.post('/uploadcsv', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), upload.single('uploadFile'), function (req, res, next) { winston.debug(' -> -> REQ BODY ', req.body); winston.debug(' -> ID FAQ-KB ', req.body.id_faq_kb); winston.debug(' -> DELIMITER ', req.body.delimiter); winston.debug(' -> FILE ', req.file); var id_faq_kb = req.body.id_faq_kb; winston.debug('id_faq_kb: ' + id_faq_kb); var delimiter = req.body.delimiter || ";"; winston.debug('delimiter: ' + delimiter); var csv = req.file.buffer.toString('utf8'); winston.debug("--> csv: ", csv) // winston.debug(' -> CSV STRING ', csv); // res.json({ success: true, msg: 'Importing CSV...' }); // PARSE CSV Faq_kb.findById(id_faq_kb).exec(function (err, faq_kb) { if (err) { return res.status(500).send({ success: false, msg: 'Error getting object.' }); } if (!faq_kb) { return res.status(404).send({ success: false, msg: 'Object not found.' }); } winston.debug('faq_kb ', faq_kb.toJSON()); // getFaqKbKeyById(req.body.id_faq_kb, function (remote_faqkb_key) { parsecsv.parseString(csv, { headers: false, delimiter: delimiter }) .on("data", function (data) { winston.debug('PARSED CSV ', data); winston.debug('--> PARSED CSV ', data); var question = data[0] //var answer = data[1] var intent_id = data[2]; var intent_display_name = data[3]; var webhook_enabled = data[4]; var actions = [ { _tdActionType: "reply", _tdActionId: uuidv4(), text: data[1], attributes: { commands: [ { type: "wait", time: 500 }, { type: "message", message: { type: "text", text: data[1] } } ] } } ] var webhook_enabled_boolean = false; if (webhook_enabled) { webhook_enabled_boolean = (webhook_enabled == 'true'); } // var row = {question: element.question, answer: element.answer, // intent_id: element.intent_id, intent_display_name: element.intent_display_name, // webhook_enabled: element.webhook_enabled || false } var newFaq = new Faq({ id_faq_kb: id_faq_kb, question: question, //answer: answer, actions: actions, intent_id: intent_id, intent_display_name: intent_display_name, webhook_enabled: webhook_enabled_boolean, language: faq_kb.language, id_project: req.projectid, createdBy: req.user.id, updatedBy: req.user.id }); winston.debug("--> newFaq: ", JSON.stringify(newFaq, null, 2)); newFaq.save(function (err, savedFaq) { if (err) { winston.error('--- > ERROR uploadcsv', err) // return res.status(500).send({ success: false, msg: 'Error saving object.' }); // ADDED 24 APR } else { faqBotEvent.emit('faq.create', savedFaq); } }); }) .on("end", function () { winston.debug("PARSE DONE"); //faqBotEvent.emit('faq_train.create', id_faq_kb) res.json({ success: true, msg: 'CSV Parsed' }); }) .on("error", function (err) { winston.error("PARSE ERROR uploadcsv", err); res.json({ success: false, msg: 'Parsing error' }); }); }); }); router.post('/', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { winston.debug(req.body); Faq_kb.findById(req.body.id_faq_kb).exec(function (err, faq_kb) { if (err) { return res.status(500).send({ success: false, msg: 'Error getting object.' }); } if (!faq_kb) { return res.status(404).send({ success: false, msg: 'Object not found.' }); } winston.debug('faq_kb ', faq_kb.toJSON()); var newFaq = new Faq({ _id: req.body._id, id_faq_kb: req.body.id_faq_kb, question: req.body.question, answer: req.body.answer, reply: req.body.reply, form: req.body.form, enabled: true, id_project: req.projectid, topic: req.body.topic, language: faq_kb.language, webhook_enabled: req.body.webhook_enabled, intent_display_name: req.body.intent_display_name, createdBy: req.user.id, updatedBy: req.user.id }); if (req.body.enabled != undefined) { newFaq.enabled = req.body.enabled; } if (req.body.actions) { newFaq.actions = req.body.actions } if (req.body.attributes) { newFaq.attributes = req.body.attributes } if (req.body.intent_id) { newFaq.intent_id = req.body.intent_id; } newFaq.save(function (err, savedFaq) { if (err) { if (err.code == 11000) { return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' }); } else { winston.debug('--- > ERROR ', err) return res.status(500).send({ success: false, msg: 'Error saving object.' }); } } winston.debug('1. ID OF THE NEW FAQ CREATED ', savedFaq._id) winston.debug('1. QUESTION OF THE NEW FAQ CREATED ', savedFaq.question) winston.debug('1. ANSWER OF THE NEW FAQ CREATED ', savedFaq.answer) winston.debug('1. ID FAQKB GET IN THE OBJECT OF NEW FAQ CREATED ', savedFaq.id_faq_kb); faqBotEvent.emit('faq.create', savedFaq); //faqBotEvent.emit('faq_train.create', req.body.id_faq_kb) res.json(savedFaq); }); }); }); router.post('/ops_update', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), async (req, res) => { let id_faq_kb = req.body.id_faq_kb; let operations = req.body.operations; let chatbotService = new ChatbotService(); for (let op of operations) { let HTTPREQUEST; let id; // method post switch (op.type) { case 'post': HTTPREQUEST = { url: apiUrl + '/' + req.projectid + '/faq/', headers: { 'Content-Type': 'application/json', 'Authorization': req.headers.authorization }, json: op.intent, method: 'post' } winston.debug("operation HTTPREQUEST: ", HTTPREQUEST); myrequest( HTTPREQUEST, async (err, resbody) => { if (err) { winston.error("err performing operation: ", err); } else { winston.debug("\n\nresbody operation: ", resbody); chatbotService.setModified(id_faq_kb, true) } } ) break; // method put case 'put': id = op.intent._id; if (op.intent.intent_id) { id = "intentId" + op.intent.intent_id; } HTTPREQUEST = { url: apiUrl + '/' + req.projectid + '/faq/' + id, headers: { 'Content-Type': 'application/json', 'Authorization': req.headers.authorization }, json: op.intent, method: 'put' } winston.debug("operation HTTPREQUEST: ", HTTPREQUEST); myrequest( HTTPREQUEST, async (err, resbody) => { if (err) { winston.error("err performing operation: ", err); } else { winston.debug("\n\nresbody operation: ", resbody); chatbotService.setModified(id_faq_kb, true) } } ) break; // method patch case 'patch': HTTPREQUEST = { url: apiUrl + '/' + req.projectid + '/faq/' + op.intent._id + '/attributes', headers: { 'Content-Type': 'application/json', 'Authorization': req.headers.authorization }, json: op.intent.attributes, method: 'patch' } winston.debug("operation HTTPREQUEST: ", HTTPREQUEST); myrequest( HTTPREQUEST, async (err, resbody) => { if (err) { winston.error("err performing operation: ", err); } else { winston.debug("\n\nresbody operation: ", resbody); chatbotService.setModified(id_faq_kb, true) } } ) break; // method delete case 'delete': id = op.intent._id; if (op.intent.intent_id) { id = "intentId" + op.intent.intent_id; } HTTPREQUEST = { url: apiUrl + '/' + req.projectid + '/faq/' + id + '?id_faq_kb=' + id_faq_kb, headers: { 'Content-Type': 'application/json', 'Authorization': req.headers.authorization }, method: 'delete' } winston.debug("operation HTTPREQUEST: ", HTTPREQUEST); myrequest( HTTPREQUEST, async (err, resbody) => { if (err) { winston.error("err performing operation: ", err); } else { winston.debug("\n\nresbody operation: ", resbody); chatbotService.setModified(id_faq_kb, true) } } ) break; } } res.status(200).send({ success: true }); }) router.patch('/:faqid/attributes', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { let data = req.body; winston.debug("data: ", data); // aggiugnere controllo su intent_id qui Faq.findById(req.params.faqid, function (err, updatedFaq) { if (err) { winston.error('Find Faq by id ERROR: ', err); return res.status(500).send({ success: false, msg: 'Error updating object.' }); } if (!updatedFaq) { return res.status(404).send({ success: false, msg: 'Object not found.' }); } if (!updatedFaq.attributes) { winston.debug("empty attributes"); winston.debug("empty attributes"); updatedFaq.attributes = {}; } winston.debug("updatedFaq attributes", updatedFaq.attributes); Object.keys(data).forEach(function (key) { var val = data[key]; winston.debug("data attributes" + key + " " + val); updatedFaq.attributes[key] = val; }) winston.debug("updatedFaq: ", updatedFaq); winston.debug("updatedFaq attributes: ", updatedFaq.attributes); winston.debug("updatedBot attributes", updatedFaq.attributes) updatedFaq.markModified('attributes'); //cache invalidation updatedFaq.save(function (err, savedFaq) { if (err) { winston.error("saving faq attributes ERROR: ", err); return res.status(500).send({ success: false, msg: 'Error saving object.' }); } winston.debug("saved faq attributes", savedFaq.toObject()); winston.verbose("saved faq attributes", savedFaq.toObject()); faqBotEvent.emit('faq.update', savedFaq); res.json(savedFaq); }) }) }) router.put('/:faqid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { winston.debug('UPDATE FAQ ', req.body); let faqid = req.params.faqid; if (!req.body.id_faq_kb) { return res.status(422).send({ err: "Missing id_faq_kb in Request Body" }) } let id_faq_kb = req.body.id_faq_kb; var update = {}; if (req.body.intent != undefined) { update.intent = req.body.intent; } if (req.body.question != undefined) { update.question = req.body.question; } if (req.body.answer != undefined) { update.answer = req.body.answer; } if (req.body.topic != undefined) { update.topic = req.body.topic; } if (req.body.status != undefined) { update.status = req.body.status; } if (req.body.language != undefined) { update.language = req.body.language; } if (req.body.intent_display_name != undefined) { update.intent_display_name = req.body.intent_display_name; } if (req.body.webhook_enabled != undefined) { update.webhook_enabled = req.body.webhook_enabled; } if (req.body.enabled != undefined) { update.enabled = req.body.enabled; } if (req.body.reply != undefined) { update.reply = req.body.reply; } if (req.body.form != undefined) { update.form = req.body.form; } if (req.body.actions != undefined) { update.actions = req.body.actions; } if (req.body.attributes != undefined) { update.attributes = req.body.attributes; } if (req.body.agents_available != undefined) { update.agents_available = req.body.agents_available; } if (faqid.startsWith("intentId")) { let intent_id = faqid.substring(8); //Faq.findOneAndUpdate({ id_faq_kb: id_faq_kb, intent_id: intent_id }, update, { new: true, upsert: true }, (err, updatedFaq) => { Faq.findOneAndUpdate({ id_faq_kb: id_faq_kb, intent_id: intent_id }, update, { new: true }, (err, updatedFaq) => { if (err) { if (err.code == 11000) { return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' }); } else { return res.status(500).send({ success: false, msg: 'Error updating object.' }); } } faqBotEvent.emit('faq.update', updatedFaq); //faqBotEvent.emit('faq_train.update', updatedFaq.id_faq_kb); res.status(200).send(updatedFaq); }) } else { Faq.findByIdAndUpdate(req.params.faqid, update, { new: true, upsert: true }, function (err, updatedFaq) { if (err) { if (err.code == 11000) { return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' }); } else { return res.status(500).send({ success: false, msg: 'Error updating object.' }); } } faqBotEvent.emit('faq.update', updatedFaq); //faqBotEvent.emit('faq_train.update', updatedFaq.id_faq_kb); res.status(200).send(updatedFaq); // updateRemoteFaq(updatedFaq) }); } }); // DELETE REMOTE AND LOCAL FAQ router.delete('/:faqid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { winston.debug('DELETE FAQ - FAQ ID ', req.params.faqid); let faqid = req.params.faqid; let id_faq_kb; if (req.query && req.query.id_faq_kb) { id_faq_kb = req.query.id_faq_kb; } if (faqid.startsWith("intentId")) { let intent_id = faqid.substring(8); if (!id_faq_kb) { return res.status(500).send({ success: false, msg: "Unable to delete object. Query param 'id_faq_kb' is mandatory if you want to delete via intent_id" }) } Faq.findOneAndDelete({ intent_id: intent_id, id_faq_kb: id_faq_kb }, (err, faq) => { if (err) { return res.status(500).send({ success: false, msg: "Error deleting object." }); } if (!faq) { return res.status(404).send({ success: false, msg: "Error deleting object. The object does not exists." }) } winston.debug('Deleted FAQ ', faq); faqBotEvent.emit('faq.delete', faq); //faqBotEvent.emit('faq_train.delete', faq.id_faq_kb); res.status(200).send(faq); }) } else { Faq.findByIdAndRemove({ _id: req.params.faqid }, function (err, faq) { if (err) { return res.status(500).send({ success: false, msg: 'Error deleting object.' }); } winston.debug('Deleted FAQ ', faq); faqBotEvent.emit('faq.delete', faq); //faqBotEvent.emit('faq_train.delete', faq.id_faq_kb); res.status(200).send(faq); }); } }); // EXPORT FAQ TO CSV router.get('/csv', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { var query = {}; winston.debug('req.query', req.query); if (req.query.id_faq_kb) { query.id_faq_kb = req.query.id_faq_kb; } winston.debug('EXPORT FAQS TO CSV QUERY', query); Faq.find(query, 'question answer intent_id intent_display_name webhook_enabled -_id').lean().exec(function (err, faqs) { if (err) { winston.debug('EXPORT FAQS TO CSV ERR', err) return (err) }; var csv = []; faqs.forEach(function (element) { var row = { question: element.question, answer: element.answer, intent_id: element.intent_id, intent_display_name: element.intent_display_name, webhook_enabled: element.webhook_enabled || false } csv.push(row); }); winston.debug('EXPORT FAQ TO CSV FAQS', csv) res.csv(csv, true) // res.json(faq); }); }); router.get('/:faqid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) { winston.debug(req.body); Faq.findById(req.params.faqid, function (err, faq) { if (err) { return res.status(500).send({ success: false, msg: 'Error getting object.' }); } if (!faq) { return res.status(404).send({ success: false, msg: 'Object not found.' }); } res.json(faq); }); }); router.get('/', roleChecker.hasRoleOrTypes('agent', ['bot', 'subscription']), function (req, res, next) { var query = {}; winston.debug("GET ALL FAQ OF THE BOT ID (req.query): ", req.query); let restricted_mode; let project_user = req.projectuser; if (project_user && project_user.role === roleConstants.AGENT) { restricted_mode = true; } if (req.query.id_faq_kb) { query.id_faq_kb = req.query.id_faq_kb; } var limit = 3000; // Number of request per page if (req.query.limit) { limit = parseInt(req.query.limit); winston.debug('faq ROUTE - limit: ' + limit); } var page = 0; if (req.query.page) { page = req.query.page; } var skip = page * limit; winston.debug('faq ROUTE - SKIP PAGE ', skip); if (req.query.text) { winston.debug("GET FAQ req.projectid", req.projectid); // query.$text = req.query.text; query.$text = { "$search": req.query.text }; query.id_project = req.projectid } if (req.query.intent_display_name) { query.intent_display_name = req.query.intent_display_name } if (restricted_mode) { query.agents_available = { $in: [ null, true ] } } winston.debug("GET FAQ query", query); // query.$text = {"$search": "question"}; // TODO ORDER BY SCORE // return Faq.find(query, {score: { $meta: "textScore" } }) // .sort( { score: { $meta: "textScore" } } ) //https://docs.mongodb.com/manual/reference/operator/query/text/#sort-by-text-search-score // in testing... // return Faq.search('a closer', (err, result) => { // console.log("result: ", result); // }) return Faq.find(query) .skip(skip).limit(limit) .populate({ path: 'faq_kb' }) .lean() .exec(function (err, faqs) { winston.debug("GET FAQ ", faqs); if (err) { winston.debug('GET FAQ err ', err) return next(err) }; if (restricted_mode === true) { faqs = faqs.map(({ webhook_enabled, faq_kb, actions, attributes, createdBy, createdAt, updatedAt, __v, ...keepAttrs }) => keepAttrs) faqs = faqs.filter(f => (f.intent_display_name !== "start" && f.intent_display_name !== 'defaultFallback')); } res.status(200).send(faqs); }); }); async function myrequest(options, callback) { winston.debug("myrequest options: ", options) return await axios({ url: options.url, method: options.method, data: options.json, params: options.params, headers: options.headers }).then((res) => { if (res && res.status == 200 && res.data) { if (callback) { callback(null, res.data); } } else { if (callback) { callback(TiledeskClient.getErr({ message: "Response status not 200" }, options, res), null, null); } } }).catch((err) => { if (callback) { callback(err, null, null); } }) } module.exports = router;