@tiledesk/tiledesk-server 2.2.13 → 2.2.17

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,50 @@
1
+ Trigger1:
2
+ * se agenti disponibili
3
+ * Azione: rispondi con messaggio “online”
4
+ * Azione: invia in chat messaggio “Assegnata a $REQUEST_FIRST_PARTECIPANT” [SCATTA SU: ONAFTERSERVE()]
5
+
6
+ * Trigger2:
7
+ * agenti non disponibili
8
+ * Azione: rispondi con messaggio “offline” (il messaggio offline comprende info sulla messa in coda e sul tempo medio di attesa) $AVG_WAIT_TIME
9
+ * richiesta served() => in unassigned (in coda)
10
+
11
+ * Trigger3:
12
+ * orari chiusi
13
+ * Azione: messaggio “orari chiusi”
14
+ * Il chatbot rimane (più probabile perchè il chatbot di ingresso già gestisce le faq)
15
+ * metti in coda con messaggio (“orari chiusi” oppure altro messaggio)
16
+
17
+ senza chatbot
18
+
19
+ ATTUALE: se online fai serve() con messaggio online
20
+ ATTUALE: se offline fai serve() con messaggio offline
21
+ ATTUALE: se orari chiusi fai serve() solo messaggio orari chiusi
22
+ AGGIUNGERE (con documentazione): se orari chiusi invita un chatbot (visto che non c’è, ma serve solo negli orari di chiusura) con messaggio orari chiusi
23
+
24
+
25
+
26
+ # 2.2.17 -> PROD
27
+ - removed default fallback limit on parse reply
28
+
29
+ # 2.2.16 -> PROD
30
+ - Email templates endpoint
31
+ - Created request.updated event for request event and deprecated request.update.comment
32
+ - Added Handlebars template processor for the message transformer module only if message.attributes.templateProcessor=true
33
+ - Email test send endpoint
34
+ - Bugfix widget label
35
+ - Added /intents alias for /faq endpoint
36
+ - The request_id field of the request model has now a unique index
37
+
38
+ # 2.2.15
39
+ - Added catch messageService.send for bot
40
+ - Added external searcher for bot( ex. Rasa proxy)
41
+ - Faq language fix taken from bot language for create single and import from csv
42
+ - Lower case reset password fix
43
+ - Added alias /bots for /faq_kb
44
+
45
+ # 2.2.14
46
+ - Fix Tiledesk Queue 1.1.11 with authEvent.queueEnabled = true
47
+
1
48
  # 2.2.13
2
49
  - Send message validation with empty text
3
50
 
package/README.md CHANGED
@@ -15,7 +15,7 @@ You can find more info here: https://developer.tiledesk.com
15
15
 
16
16
  # Prerequisites
17
17
 
18
- * [Nodejs](https://www.npmjs.com/) and npm installed
18
+ * [Nodejs](https://www.npmjs.com/) and npm installed. Suggested versions are NodeJS 12.20.2 and NPM 6.14.11
19
19
  * [MongoDb](https://www.mongodb.com) installed
20
20
 
21
21
  # Running Tiledesk Server
package/app.js CHANGED
@@ -102,6 +102,7 @@ var campaigns = require('./routes/campaigns');
102
102
  var logs = require('./routes/logs');
103
103
  var requestUtilRoot = require('./routes/requestUtilRoot');
104
104
  var urls = require('./routes/urls');
105
+ var email = require('./routes/email');
105
106
 
106
107
  var bootDataLoader = require('./services/bootDataLoader');
107
108
  var settingDataLoader = require('./services/settingDataLoader');
@@ -294,7 +295,6 @@ app.use('/users', [passport.authenticate(['basic', 'jwt'], { session: false }),
294
295
  app.use('/logs', logs);
295
296
  app.use('/requests_util', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], requestUtilRoot);
296
297
 
297
-
298
298
  // TODO security issues
299
299
  if (process.env.DISABLE_TRANSCRIPT_VIEW_PAGE ) {
300
300
  winston.info(" Transcript view page is disabled");
@@ -333,15 +333,16 @@ app.use('/:projectid/departments', department);
333
333
 
334
334
  channelManager.useUnderProjects(app);
335
335
 
336
-
336
+ //deprecated
337
337
  app.use('/:projectid/faq', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], faq);
338
+ app.use('/:projectid/intents', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], faq);
338
339
 
339
340
  //Deprecated??
340
341
  app.use('/:projectid/faqpub', faqpub);
341
342
 
342
343
  //deprecated
343
344
  app.use('/:projectid/faq_kb', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['bot','subscription'])], faq_kb);
344
- app.use('/:projectid/bot', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['bot','subscription'])], faq_kb);
345
+ app.use('/:projectid/bots', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['bot','subscription'])], faq_kb);
345
346
 
346
347
 
347
348
 
@@ -374,6 +375,8 @@ app.use('/:projectid/labels', [fetchLabels],labels);
374
375
 
375
376
  app.use('/:projectid/campaigns',[passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], campaigns);
376
377
 
378
+ app.use('/:projectid/emails',[passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('owner')], email);
379
+
377
380
 
378
381
 
379
382
  if (pubModulesManager) {
@@ -5,8 +5,8 @@
5
5
  "LABEL_PLACEHOLDER": "type your message..",
6
6
  "LABEL_START_NW_CONV": "New conversation",
7
7
  "LABEL_FIRST_MSG": "Describe shortly your problem, you will be contacted by an agent.",
8
- "LABEL_FIRST_MSG_NO_AGENTS": "🤔 All operators are offline at the moment. You can anyway describe your problem. It will be assigned to the support team who will answer you as soon as possible.",
9
- "LABEL_FIRST_MSG_OPERATING_HOURS_CLOSED": "🤔 Our offices are closed. You can anyway describe your problem. It will be assigned to the support team who will answer you as soon as possible.",
8
+ "LABEL_FIRST_MSG_NO_AGENTS": "🤔 All operators are offline at the moment. You can anyway describe your problem. It will be assigned to the support team who will get back to you as soon as possible.",
9
+ "LABEL_FIRST_MSG_OPERATING_HOURS_CLOSED": "🤔 Our offices are closed. You can anyway describe your problem. It will be assigned to the support team who will get back to you as soon as possible.",
10
10
  "LABEL_SELECT_TOPIC": "Select a topic",
11
11
  "LABEL_COMPLETE_FORM": "Complete the form to start a conversation with the next available agent.",
12
12
  "LABEL_FIELD_NAME": "Name",
package/models/request.js CHANGED
@@ -43,7 +43,7 @@ var RequestSchema = new Schema({
43
43
  type: String,
44
44
  required: true,
45
45
  index: true,
46
- // unique: true
46
+ unique: true
47
47
 
48
48
  },
49
49
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-server",
3
3
  "description": "The Tiledesk server module",
4
- "version": "2.2.13",
4
+ "version": "2.2.17",
5
5
  "scripts": {
6
6
  "start": "node ./bin/www",
7
7
  "pretest": "mongodb-runner start",
@@ -39,12 +39,12 @@
39
39
  "@tiledesk-ent/tiledesk-server-jwthistory": "^1.1.9",
40
40
  "@tiledesk-ent/tiledesk-server-mt": "^1.1.7",
41
41
  "@tiledesk-ent/tiledesk-server-payments": "^1.1.5",
42
- "@tiledesk-ent/tiledesk-server-queue": "^1.1.10",
42
+ "@tiledesk-ent/tiledesk-server-queue": "^1.1.11",
43
43
  "@tiledesk-ent/tiledesk-server-request-history": "^1.1.5",
44
- "@tiledesk-ent/tiledesk-server-resthook": "^1.1.51",
44
+ "@tiledesk-ent/tiledesk-server-resthook": "^1.1.53",
45
45
  "@tiledesk-ent/tiledesk-server-routing-queue": "^1.1.11",
46
46
  "@tiledesk-ent/tiledesk-server-tags": "^1.1.1",
47
- "@tiledesk-ent/tiledesk-server-triggers": "^1.1.79",
47
+ "@tiledesk-ent/tiledesk-server-triggers": "^1.1.82",
48
48
  "@tiledesk-ent/tiledesk-server-visitorcounter": "^1.1.1"
49
49
  },
50
50
  "dependencies": {
@@ -0,0 +1,10 @@
1
+ {
2
+ "text": "question",
3
+ "intent": { "name": "brutteparole", "confidence": 0.9257488250732422 },
4
+ "entities": [],
5
+ "text_tokens": [ [ 0, 8 ] ],
6
+ "intent_ranking": [
7
+ { "name": "brutteparole", "confidence": 0.9257488250732422 },
8
+ { "name": "eta", "confidence": 0.0742512047290802 }
9
+ ]
10
+ }
@@ -1,6 +1,9 @@
1
1
  const messageTransformerInterceptor = require("./messageTransformerInterceptor");
2
2
  const microLanguageTransformerInterceptor = require("./microLanguageAttributesTransformerInterceptor");
3
+ const messageHandlebarsTransformerInterceptor = require("./messageHandlebarsTransformerInterceptor");
3
4
 
4
5
 
5
6
 
6
- module.exports = {messageTransformerInterceptor:messageTransformerInterceptor, microLanguageTransformerInterceptor:microLanguageTransformerInterceptor};
7
+ module.exports = {messageTransformerInterceptor:messageTransformerInterceptor,
8
+ microLanguageTransformerInterceptor:microLanguageTransformerInterceptor,
9
+ messageHandlebarsTransformerInterceptor: messageHandlebarsTransformerInterceptor};
@@ -0,0 +1,83 @@
1
+
2
+ const messagePromiseEvent = require('../../event/messagePromiseEvent');
3
+ const Request = require('../../models/request');
4
+ var winston = require('../../config/winston');
5
+ var cacheUtil = require('../../utils/cacheUtil');
6
+ var handlebars = require('handlebars');
7
+
8
+ class MessageHandlebarsTransformerInterceptor {
9
+
10
+
11
+
12
+
13
+ listen() {
14
+
15
+ var that = this;
16
+ winston.info("MessageHandlebarsTransformerInterceptor listener start ");
17
+
18
+
19
+ messagePromiseEvent.on('message.create.simple.before', async (data) => {
20
+ winston.debug('MessageHandlebarsTransformerInterceptor message.create.simple.before', data);
21
+
22
+ var message = data.beforeMessage;
23
+
24
+ if (!message.text) { //for image i think
25
+ return data;
26
+ }
27
+ if (message.attributes && message.attributes.templateProcessor == true) {
28
+
29
+ // TODO if variables are presents
30
+
31
+ var q1 = Request.findOne({request_id: message.recipient, id_project: message.id_project});
32
+
33
+ // if (message.attributes && message.attributes.populateTemplate == true) {
34
+ q1.populate('lead').
35
+ populate('department').
36
+ populate('participatingBots').
37
+ populate('participatingAgents').
38
+ populate({path:'requester',populate:{path:'id_user'}});
39
+ // }
40
+
41
+ var request = await q1.
42
+ cache(cacheUtil.defaultTTL, message.id_project+":requests:request_id:"+message.recipient).
43
+ exec();
44
+
45
+
46
+
47
+ var requestJSON = request.toJSON();
48
+
49
+ winston.debug('request mti: ', requestJSON);
50
+
51
+
52
+ var template = handlebars.compile(message.text);
53
+ winston.debug('template: '+ template);
54
+
55
+ // var templateSpec = handlebars.precompile(message.text);
56
+ // winston.info('templateSpec: ', templateSpec);
57
+
58
+
59
+ var replacements = {
60
+ request: requestJSON,
61
+ };
62
+
63
+ // {{request.first_text}}
64
+ // {{request.participatingAgents.0.firstname}}
65
+
66
+
67
+
68
+
69
+ var text = template(replacements);
70
+ winston.debug('text: '+ text);
71
+ message.text=text;
72
+
73
+ }
74
+ winston.debug('data: ' + JSON.stringify(data) );
75
+ return data;
76
+ });
77
+
78
+ }
79
+
80
+ }
81
+
82
+ var messageHandlebarsTransformerInterceptor = new MessageHandlebarsTransformerInterceptor();
83
+ module.exports = messageHandlebarsTransformerInterceptor;
@@ -168,6 +168,7 @@ class PubModulesManager {
168
168
  try {
169
169
  this.messageTransformer.messageTransformerInterceptor.listen();
170
170
  this.messageTransformer.microLanguageTransformerInterceptor.listen();
171
+ this.messageTransformer.messageHandlebarsTransformerInterceptor.listen();
171
172
  winston.info("PubModulesManager messageTransformer started.");
172
173
  } catch(err) {
173
174
  winston.info("PubModulesManager error starting messageTransformer module", err);
package/routes/auth.js CHANGED
@@ -476,8 +476,12 @@ router.get('/pendinginvitationsnoauth/:pendinginvitationid', function (req, res)
476
476
  router.put('/requestresetpsw', function (req, res) {
477
477
 
478
478
  winston.debug('REQUEST RESET PSW - EMAIL REQ BODY ', req.body);
479
+
480
+ var email = req.body.email.toLowerCase();
481
+ winston.debug("email", email);
482
+
479
483
  // auttype
480
- User.findOne({ email: req.body.email, status: 100
484
+ User.findOne({ email: email, status: 100
481
485
  // , authType: 'email_password'
482
486
  }, function (err, user) {
483
487
  if (err) {
@@ -0,0 +1,32 @@
1
+ var express = require('express');
2
+
3
+ var router = express.Router();
4
+
5
+ var emailService = require("../services/emailService");
6
+ var winston = require('../config/winston');
7
+
8
+ router.get('/templates/:templateid',
9
+ async (req, res) => {
10
+ let templateid = req.params.templateid+".html";
11
+ winston.debug("templateid",templateid);
12
+
13
+ var html = await emailService.readTemplateFile(templateid);
14
+ res.json({template:html});
15
+ });
16
+
17
+ router.post('/test/send',
18
+ async (req, res) => {
19
+ let to = req.body.to;
20
+ winston.info("to",to);
21
+
22
+ let configEmail = req.body.config;
23
+ winston.info("configEmail", configEmail);
24
+
25
+ emailService.sendTest(to, configEmail, function(err,obj) {
26
+ // winston.info("sendTest rest", err, obj);
27
+ res.json({error: err, response:obj});
28
+ });
29
+
30
+ });
31
+
32
+ module.exports = router;
package/routes/faq.js CHANGED
@@ -1,6 +1,7 @@
1
1
  var express = require('express');
2
2
  var router = express.Router();
3
3
  var Faq = require("../models/faq");
4
+ var Faq_kb = require("../models/faq_kb");
4
5
  var multer = require('multer')
5
6
  var upload = multer()
6
7
  const faqBotEvent = require('../event/faqBotEvent');
@@ -31,6 +32,16 @@ router.post('/uploadcsv', upload.single('uploadFile'), function (req, res, next)
31
32
  // PARSE CSV
32
33
 
33
34
 
35
+
36
+ Faq_kb.findById(id_faq_kb).exec(function(err, faq_kb) {
37
+ if (err) {
38
+ return res.status(500).send({ success: false, msg: 'Error getting object.' });
39
+ }
40
+ if (!faq_kb) {
41
+ return res.status(404).send({ success: false, msg: 'Object not found.' });
42
+ }
43
+ winston.debug('faq_kb ', faq_kb.toJSON());
44
+
34
45
  // getFaqKbKeyById(req.body.id_faq_kb, function (remote_faqkb_key) {
35
46
 
36
47
  parsecsv.parseString(csv, { headers: false, delimiter: delimiter })
@@ -57,6 +68,7 @@ router.post('/uploadcsv', upload.single('uploadFile'), function (req, res, next)
57
68
  intent_id:intent_id,
58
69
  intent_display_name: intent_display_name,
59
70
  webhook_enabled: webhook_enabled_boolean,
71
+ language: faq_kb.language,
60
72
  id_project: req.projectid,
61
73
  createdBy: req.user.id,
62
74
  updatedBy: req.user.id
@@ -81,45 +93,56 @@ router.post('/uploadcsv', upload.single('uploadFile'), function (req, res, next)
81
93
  winston.error("PARSE ERROR uploadcsv", err);
82
94
  res.json({ success: false, msg: 'Parsing error' });
83
95
  });
84
- // });
96
+ });
85
97
  });
86
98
 
87
99
 
88
100
  router.post('/', function (req, res) {
89
101
 
90
102
  winston.debug(req.body);
91
- var newFaq = new Faq({
92
- id_faq_kb: req.body.id_faq_kb,
93
- question: req.body.question,
94
- answer: req.body.answer,
95
- id_project: req.projectid,
96
- topic: req.body.topic,
97
- webhook_enabled: req.body.webhook_enabled,
98
- intent_display_name: req.body.intent_display_name,
99
- createdBy: req.user.id,
100
- updatedBy: req.user.id
101
- });
102
103
 
103
- newFaq.save(function (err, savedFaq) {
104
+ Faq_kb.findById(req.body.id_faq_kb).exec(function(err, faq_kb) {
104
105
  if (err) {
105
- if (err.code == 11000) {
106
- return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' });
107
- } else {
108
- winston.debug('--- > ERROR ', err)
109
- return res.status(500).send({ success: false, msg: 'Error saving object.' });
110
- }
106
+ return res.status(500).send({ success: false, msg: 'Error getting object.' });
111
107
  }
112
- winston.debug('1. ID OF THE NEW FAQ CREATED ', savedFaq._id)
113
- winston.debug('1. QUESTION OF THE NEW FAQ CREATED ', savedFaq.question)
114
- winston.debug('1. ANSWER OF THE NEW FAQ CREATED ', savedFaq.answer)
115
- winston.debug('1. ID FAQKB GET IN THE OBJECT OF NEW FAQ CREATED ', savedFaq.id_faq_kb);
108
+ if (!faq_kb) {
109
+ return res.status(404).send({ success: false, msg: 'Object not found.' });
110
+ }
111
+ winston.debug('faq_kb ', faq_kb.toJSON());
112
+
113
+ var newFaq = new Faq({
114
+ id_faq_kb: req.body.id_faq_kb,
115
+ question: req.body.question,
116
+ answer: req.body.answer,
117
+ id_project: req.projectid,
118
+ topic: req.body.topic,
119
+ language: faq_kb.language,
120
+ webhook_enabled: req.body.webhook_enabled,
121
+ intent_display_name: req.body.intent_display_name,
122
+ createdBy: req.user.id,
123
+ updatedBy: req.user.id
124
+ });
116
125
 
117
- faqBotEvent.emit('faq.create', savedFaq);
126
+ newFaq.save(function (err, savedFaq) {
127
+ if (err) {
128
+ if (err.code == 11000) {
129
+ return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' });
130
+ } else {
131
+ winston.debug('--- > ERROR ', err)
132
+ return res.status(500).send({ success: false, msg: 'Error saving object.' });
133
+ }
134
+ }
135
+ winston.debug('1. ID OF THE NEW FAQ CREATED ', savedFaq._id)
136
+ winston.debug('1. QUESTION OF THE NEW FAQ CREATED ', savedFaq.question)
137
+ winston.debug('1. ANSWER OF THE NEW FAQ CREATED ', savedFaq.answer)
138
+ winston.debug('1. ID FAQKB GET IN THE OBJECT OF NEW FAQ CREATED ', savedFaq.id_faq_kb);
118
139
 
119
- res.json(savedFaq);
140
+ faqBotEvent.emit('faq.create', savedFaq);
120
141
 
121
-
142
+ res.json(savedFaq);
122
143
 
144
+
145
+ });
123
146
  });
124
147
  });
125
148
 
package/routes/faq_kb.js CHANGED
@@ -6,8 +6,10 @@ var Department = require("../models/department");
6
6
  var faqService = require("../services/faqService");
7
7
  const botEvent = require('../event/botEvent');
8
8
  var winston = require('../config/winston');
9
+ var httpUtil = require("../utils/httpUtil");
9
10
 
10
11
  router.post('/', function (req, res) {
12
+ winston.info('create BOT ', req.body);
11
13
  // create(name, url, projectid, user_id, type, description) {
12
14
  faqService.create(req.body.name, req.body.url, req.projectid, req.user.id, req.body.type, req.body.description, undefined, undefined,
13
15
  req.body.language).then(function(savedFaq_kb) {
@@ -18,6 +20,106 @@ router.post('/', function (req, res) {
18
20
  });
19
21
 
20
22
 
23
+ router.post('/train', function (req, res) {
24
+
25
+ winston.info('train BOT ', req.body);
26
+
27
+ Faq_kb.findById(req.body.id_faq_kb).exec(function(err, faq_kb) {
28
+ if (err) {
29
+ return res.status(500).send({ success: false, msg: 'Error getting object.' });
30
+ }
31
+ if (!faq_kb) {
32
+ return res.status(404).send({ success: false, msg: 'Object not found.' });
33
+ }
34
+ winston.debug('faq_kb ', faq_kb.toJSON());
35
+
36
+ winston.debug('faq_kb.type :'+ faq_kb.type);
37
+ if (faq_kb.type =="internal" && faq_kb.url) {
38
+
39
+
40
+
41
+ var train = {
42
+ language:faq_kb.language,
43
+ nlu:[]
44
+ };
45
+ winston.info("train", train);
46
+
47
+
48
+ var query = { "id_project": req.projectid, "id_faq_kb": req.body.id_faq_kb};
49
+
50
+ Faq.find(query)
51
+ .limit(10000)
52
+ .lean().
53
+ exec(async (err, faqs) => {
54
+ if (err) {
55
+ return res.status(500).send({ success: false, msg: 'Error getting object.' });
56
+ }
57
+ if (faqs && faqs.length>0) {
58
+ winston.info("faqs exact", faqs);
59
+
60
+ faqs.forEach(function(f) {
61
+ var intent = {
62
+ intent:f.intent_display_name,
63
+ examples:[]
64
+ }
65
+ var questions = f.question.split("\n");
66
+ winston.info("questions", questions);
67
+
68
+ questions.forEach(function(q) {
69
+ winston.info("q", q);
70
+ intent.examples.push(q);
71
+ });
72
+ winston.info("intent", intent);
73
+ train.nlu.push(intent);
74
+ });
75
+ winston.info("train", train);
76
+
77
+ try {
78
+ var trainHttp = await httpUtil.call(faq_kb.url+"/trainandload", undefined, train, "POST");
79
+ }catch(e) {
80
+ winston.error("error training", e);
81
+ }
82
+
83
+
84
+ return res.json({train:train, httpResponse:trainHttp});
85
+ // return res.json(trainHttp);
86
+ }else {
87
+ return res.status(400).send({ success: false, msg: 'no faq to train on external bot.' });
88
+ }
89
+ });
90
+ } else {
91
+ winston.debug('external query: ');
92
+ return res.status(400).send({ success: false, msg: 'you can train a standard internal bot or an external bot.' });
93
+ }
94
+
95
+ });
96
+
97
+ /*
98
+ {
99
+ "language":"it",
100
+ "nlu":[
101
+ {
102
+ "intent":"eta",
103
+ "examples":[
104
+ "quanti anni hai",
105
+ "dimmi la tua età",
106
+ "quanto sei grande",
107
+ "parlami della tua età"
108
+ ]
109
+ },
110
+ {
111
+ "intent":"brutteparole",
112
+ "examples":[
113
+ "non dire parolacce",
114
+ "le brutte parole non dovrebbero dirsi"
115
+ ]
116
+ }
117
+ ]
118
+ }
119
+ */
120
+
121
+ });
122
+
21
123
 
22
124
  router.post('/askbot', function (req, res) {
23
125