@tiledesk/tiledesk-server 2.3.53 → 2.3.54

Sign up to get free protection for your applications and to get access to all the features.
package/app.js CHANGED
@@ -172,6 +172,7 @@ var IPFilter = require('./middleware/ipFilter');
172
172
 
173
173
  // job_here
174
174
  var BanUserNotifier = require('./services/banUserNotifier');
175
+ const { ChatbotService } = require('./services/chatbotService');
175
176
  BanUserNotifier.listen();
176
177
 
177
178
  var modulesManager = undefined;
@@ -207,6 +208,8 @@ var app = express();
207
208
  app.set('views', path.join(__dirname, 'views'));
208
209
  app.set('view engine', 'jade');
209
210
 
211
+ app.set('chatbot_service', new ChatbotService())
212
+
210
213
 
211
214
  // TODO DELETE IT IN THE NEXT RELEASE
212
215
  if (process.env.ENABLE_ALTERNATIVE_CORS_MIDDLEWARE === "true") {
@@ -50,7 +50,7 @@ var DepartmentSchema = new Schema({
50
50
  tags: [TagSchema],
51
51
  status: {
52
52
  type: Number,
53
- default: 1,
53
+ default: 1, // 1: enabled; 0 hidden for widget; -1 hidden for the dashboard;
54
54
  index: true
55
55
  // required: true
56
56
  },
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.3.53",
4
+ "version": "2.3.54",
5
5
  "scripts": {
6
6
  "start": "node ./bin/www",
7
7
  "pretest": "mongodb-runner start",
@@ -42,7 +42,7 @@
42
42
  "@tiledesk/tiledesk-chatbot-util": "^0.8.33",
43
43
  "@tiledesk/tiledesk-json-rules-engine": "^4.0.3",
44
44
  "@tiledesk/tiledesk-rasa-connector": "^1.0.10",
45
- "@tiledesk/tiledesk-tybot-connector": "^0.1.22",
45
+ "@tiledesk/tiledesk-tybot-connector": "^0.1.27",
46
46
  "@tiledesk/tiledesk-dialogflow-connector": "^1.7.4",
47
47
  "app-root-path": "^3.0.0",
48
48
  "bcrypt-nodejs": "0.0.3",
@@ -18,9 +18,11 @@ var leadService = require('../../services/leadService');
18
18
  var LeadConstants = require('../../models/leadConstants');
19
19
  var operatingHoursService = require("../../services/operatingHoursService");
20
20
  var sendMessageUtil = require("../../utils/sendMessageUtil");
21
+ var sendEmailUtil = require("../../utils/sendEmailUtil");
21
22
  var cacheUtil = require("../../utils/cacheUtil");
22
23
  var cacheEnabler = require("../../services/cacheEnabler");
23
24
  var UIDGenerator = require("../../utils/UIDGenerator");
25
+ const RequestConstants = require('../../models/requestConstants');
24
26
 
25
27
 
26
28
  class RulesTrigger {
@@ -201,6 +203,85 @@ class RulesTrigger {
201
203
 
202
204
 
203
205
 
206
+ triggerEventEmitter.on('email.send', function(eventTrigger) {
207
+
208
+ try {
209
+
210
+ winston.debug('runAction eventTrigger.eventSuccess:', eventTrigger.eventSuccess);
211
+ var trigger = eventTrigger.trigger;
212
+ winston.debug('runAction trigger', trigger.toObject());
213
+
214
+
215
+ var action = eventTrigger.action;
216
+ winston.debug('runAction action', action.toObject());
217
+
218
+
219
+ var fullname = action.parameters.fullName || "BOT";
220
+ winston.debug('runAction action fullname: ' + fullname);
221
+
222
+ var subject = action.parameters.fullName || "New Email";
223
+ winston.debug('runAction action subject: ' + subject);
224
+
225
+ var sender = "system";
226
+
227
+ if (action.parameters.sender) {
228
+ sender = action.parameters.sender;
229
+ }
230
+ winston.debug('runAction action sender: ' + sender);
231
+
232
+ var text = action.parameters.text;
233
+ winston.debug('runAction action text: ' + text);
234
+
235
+ var attributes = {};
236
+
237
+ // var attributes = action.parameters.attributes;
238
+ // winston.debug('runAction action attributes: ' + attributes);
239
+
240
+
241
+ var recipient;
242
+ if (eventTrigger.eventKey=="request.create" || eventTrigger.eventKey=="request.participants.join") {
243
+ recipient = eventTrigger.event.request_id;
244
+ }
245
+ if (eventTrigger.eventKey=="message.create.from.requester" || eventTrigger.eventKey=="message.received") {
246
+ recipient = eventTrigger.event.recipient;
247
+ }
248
+ if (eventTrigger.eventKey=="event.emit") {
249
+ winston.debug('runAction action event.emit: ', eventTrigger.event.toObject());
250
+
251
+ //TODO funziona?
252
+ recipient = eventTrigger.event.project_user.id_user;
253
+ }
254
+
255
+ // console.log("eventTrigger.event", eventTrigger.event);
256
+
257
+ winston.debug('runAction action recipient: ' + recipient);
258
+
259
+ var id_project = eventTrigger.event.id_project;
260
+ winston.debug('runAction action id_project: ' + id_project);
261
+
262
+
263
+ var message = eventTrigger.event;
264
+ winston.debug('runAction action message: ', message);
265
+
266
+ if (eventTrigger.event.request && eventTrigger.event.request.lead && eventTrigger.event.request.lead.email) {
267
+ var to = eventTrigger.event.request.lead.email;
268
+ winston.debug('to ' + to);
269
+
270
+ // sendEmailDirect(to, text, project, request_id, subject, tokenQueryString, sourcePage) {
271
+ sendEmailUtil.sendEmailDirect(to, text, id_project, recipient, subject, message);
272
+ } else {
273
+ winston.info('email.send trigger. Lead email is undefined ');
274
+ }
275
+
276
+
277
+ } catch(e) {
278
+ winston.error("Error runAction", e);
279
+ }
280
+
281
+ });
282
+
283
+
284
+
204
285
 
205
286
 
206
287
  triggerEventEmitter.on('request.department.route', function(eventTrigger) {
@@ -683,6 +764,8 @@ class RulesTrigger {
683
764
  var departmentid = action.parameters.departmentid;
684
765
  winston.debug('runAction action departmentid: ' + departmentid);
685
766
 
767
+ var participants = action.parameters.participants;
768
+ winston.debug('runAction action participants: ' + participants);
686
769
  // var attributes = action.parameters.attributes;
687
770
  // winston.debug('runAction action attributes: ' + attributes);
688
771
 
@@ -768,10 +851,24 @@ class RulesTrigger {
768
851
  departmentid = eventAttributes.department;
769
852
  }
770
853
 
854
+
855
+
771
856
  if (eventAttributes.text) {
772
857
  text = eventAttributes.text;
773
858
  }
774
859
 
860
+ // console.log("eventAttributes.participants.length"+ eventAttributes.participants.length);
861
+ if (eventAttributes.participants && eventAttributes.participants.length>0) {
862
+ participants = eventAttributes.participants;
863
+ if (participants[0].indexOf("bot_")>-1) {
864
+ text = "\\start"; //if participants is passed than the bot reply to the first message "welcome" so I changed "welcome" with "\start"
865
+ }
866
+ // status = RequestConstants.ASSIGNED;
867
+ // console.log("eventAttributes.participants",eventAttributes.participants);
868
+ }
869
+
870
+ // console.log("text", text);
871
+
775
872
  if (eventAttributes.status) {
776
873
  status = eventAttributes.status;
777
874
  }
@@ -864,7 +961,7 @@ class RulesTrigger {
864
961
 
865
962
  var new_request = {
866
963
  request_id: request_id, project_user_id: project_user_id, lead_id: createdLead._id, id_project: id_project,
867
- first_text: text, departmentid: departmentid, sourcePage: sourcePage,
964
+ first_text: text, participants: participants, departmentid: departmentid, sourcePage: sourcePage,
868
965
  language: language, userAgent: userAgent, status: status, createdBy: id_user,
869
966
  attributes: attributes, subject: undefined, preflight: preflight, channel: undefined, location: undefined,
870
967
  lead: createdLead, requester: puser
@@ -175,7 +175,7 @@ router.get('/allstatus', [passport.authenticate(['basic', 'jwt'], { session: fal
175
175
 
176
176
  winston.debug("## GET ALL DEPTS req.project ", req.project)
177
177
 
178
- var query = { "id_project": req.projectid };
178
+ var query = { "id_project": req.projectid, status: { $gte: 0 } }; // nascondi quelli con status = hidden (-1) for dashboard
179
179
  //secondo me qui manca un parentesi tonda per gli or
180
180
  if (req.project && req.project.profile && (req.project.profile.type === 'free' && req.project.trialExpired === true) || (req.project.profile.type === 'payment' && req.project.isActiveSubscription === false)) {
181
181
 
package/routes/faq.js CHANGED
@@ -189,7 +189,7 @@ router.put('/:faqid', function (req, res) {
189
189
  update.enabled = req.body.enabled;
190
190
  }
191
191
  if (req.body.reply!=undefined) {
192
- update.reply = req.body.enabled;
192
+ update.reply = req.body.reply;
193
193
  }
194
194
  if (req.body.form!=undefined) {
195
195
  update.form = req.body.form;
package/routes/faq_kb.js CHANGED
@@ -11,6 +11,9 @@ var httpUtil = require("../utils/httpUtil");
11
11
  const { forEach } = require('lodash');
12
12
  var multer = require('multer')
13
13
  var upload = multer()
14
+ var configGlobal = require('../config/global');
15
+
16
+ var chatbot_templates_api_url = "https://chatbot-templates.herokuapp.com/chatbots/public/templates"
14
17
 
15
18
 
16
19
  router.post('/', function (req, res) {
@@ -339,55 +342,129 @@ router.get('/', function (req, res) {
339
342
 
340
343
  });
341
344
 
345
+ router.post('/fork/:id_faq_kb', async (req, res) => {
346
+
347
+ let id_faq_kb = req.params.id_faq_kb;
348
+ winston.info('id_faq_kb: ' + id_faq_kb);
349
+
350
+ const api_url = process.env.API_URL || configGlobal.apiUrl;
351
+ winston.info("fork --> base_url: " + api_url); // check if correct
352
+
353
+ let current_project_id = req.projectid;
354
+ winston.info("current project id: " + current_project_id);
355
+
356
+ let landing_project_id = req.query.projectid;
357
+ winston.info("landing project id " + landing_project_id)
358
+
359
+ let new_bot_name = req.query.name;
360
+ winston.info("new bot name: " + new_bot_name);
361
+
362
+ let public = req.query.public;
363
+ winston.info("public " + public);
364
+
365
+ let token = req.headers.authorization;
366
+
367
+ let cs = req.app.get('chatbot_service')
368
+
369
+ let chatbot = await cs.getBotById(id_faq_kb, public, api_url, chatbot_templates_api_url, token, current_project_id);
370
+ winston.debug("chatbot: ", chatbot)
371
+
372
+ if (!chatbot) {
373
+ return res.status(500).send({ success: false, message: "Unable to get chatbot" });
374
+ }
375
+
376
+ //chatbot.name = new_bot_name;
377
+
378
+ let savedChatbot = await cs.createBot(api_url, token, chatbot, landing_project_id);
379
+ winston.info("savedChatbot: ", savedChatbot)
380
+
381
+ if (!savedChatbot) {
382
+ return res.status(500).send({ success: false, message: "Unable to create new chatbot" });
383
+ }
384
+
385
+ let import_result = await cs.importFaqs(api_url, savedChatbot._id, token, chatbot, landing_project_id);
386
+ winston.info("imported: ", import_result);
387
+
388
+ if (import_result.success == "false") {
389
+ return res.status(500).send({ success: false, message: "Unable to import intents in the new chatbot" });
390
+ }
391
+
392
+ return res.status(200).send({ message: "Chatbot forked successfully", bot_id: savedChatbot._id });
393
+
394
+ })
395
+
396
+
342
397
  router.post('/importjson/:id_faq_kb', upload.single('uploadFile'), (req, res) => {
343
398
 
344
399
  let id_faq_kb = req.params.id_faq_kb;
345
400
  winston.debug('id_faq_kb: ', id_faq_kb);
346
401
 
347
- let json_string = req.file.buffer.toString('utf-8');
348
- winston.debug("json_string: ", json_string);
402
+ let json_string;
403
+ let json;
404
+ if (req.file) {
405
+ json_string = req.file.buffer.toString('utf-8');
406
+ json = JSON.parse(json_string);
407
+ } else {
408
+ json = req.body;
409
+ }
410
+
411
+ winston.info("json_string: " + json_string);
349
412
 
350
413
  if (req.query.intentsOnly == "true") {
351
414
 
352
415
  winston.info("intents only")
353
416
 
354
- const json = JSON.parse(json_string)
355
- console.log("\n intents --> \n")
356
- console.log(json)
357
-
358
417
  json.intents.forEach((intent) => {
359
418
 
360
- var new_faq = new Faq({
419
+ let new_faq = {
361
420
  id_faq_kb: id_faq_kb,
362
421
  id_project: req.projectid,
363
422
  createdBy: req.user.id,
423
+ intent_display_name: intent.intent_display_name,
364
424
  question: intent.question,
365
425
  answer: intent.answer,
366
426
  reply: intent.reply,
367
427
  form: intent.form,
368
428
  enabled: intent.enabled,
369
429
  webhook_enabled: intent.webhook_enabled,
370
- language: intent.language,
371
-
372
- })
430
+ language: intent.language
431
+ }
373
432
 
374
- new_faq.save((err, savedFaq) => {
375
- if (err) {
376
- winston.error("GET FAQ-KB ERROR", err);
377
- if (err.code == 11000) {
378
- return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' });
433
+ // overwrite duplicated intents
434
+ if (req.query.overwrite == "true") {
435
+ Faq.findOneAndUpdate({ id_faq_kb: id_faq_kb, intent_display_name: intent.intent_display_name }, new_faq, { new: true, upsert: true, rawResult: true }, (err, savingResult) => {
436
+ if (err) {
437
+ winston.error("findOneAndUpdate (upsert) FAQ ERROR ", err);
379
438
  } else {
380
- winston.debug('--- > ERROR ', err)
381
- return res.status(500).send({ success: false, msg: 'Error saving intent.' });
439
+ if (savingResult.lastErrorObject.updatedExisting == true) {
440
+ winston.info("updated existing intent")
441
+ faqBotEvent.emit('faq.update', savingResult.value);
442
+ } else {
443
+ winston.info("new intent crated")
444
+ faqBotEvent.emit('faq.create', savingResult.value);
445
+ }
382
446
  }
383
- }
384
- winston.debug("NEW FAQ CREATED WITH ID: ", savedFaq._id);
385
- faqBotEvent.emit('faq.create', savedFaq);
386
- })
447
+ })
448
+
449
+ // don't overwrite duplicated intents
450
+ } else {
451
+ Faq.create(new_faq, (err, savedFaq) => {
452
+ if (err) {
453
+ winston.debug("create new FAQ ERROR ", err);
454
+ if (err.code == 11000) {
455
+ winston.error("Duplicate intent_display_name.");
456
+ winston.info("Skip duplicated intent_display_name");
457
+ } else {
458
+ winston.info("new intent crated")
459
+ faqBotEvent.emit('faq.create', savedFaq);
460
+ }
461
+ }
462
+ })
463
+ }
387
464
 
388
465
  })
389
466
 
390
- return res.status(200).send({ success: true, msg: "Intents imported successfully"})
467
+ return res.status(200).send({ success: true, msg: "Intents imported successfully" })
391
468
 
392
469
  } else {
393
470
 
@@ -427,33 +504,54 @@ router.post('/importjson/:id_faq_kb', upload.single('uploadFile'), (req, res) =>
427
504
 
428
505
  json.intents.forEach((intent) => {
429
506
 
430
- var new_faq = new Faq({
507
+ let new_faq = {
431
508
  id_faq_kb: updatedFaq_kb._id,
432
509
  id_project: req.projectid,
433
510
  createdBy: req.user.id,
511
+ intent_display_name: intent.intent_display_name,
434
512
  question: intent.question,
435
513
  answer: intent.answer,
436
514
  reply: intent.reply,
437
515
  form: intent.form,
438
516
  enabled: intent.enabled,
439
517
  webhook_enabled: intent.webhook_enabled,
440
- language: intent.language,
441
-
442
- })
518
+ language: intent.language
519
+ }
443
520
 
444
- new_faq.save((err, savedFaq) => {
445
- if (err) {
446
- winston.error("GET FAQ-KB ERROR", err);
447
- if (err.code == 11000) {
448
- return res.status(409).send({ success: false, msg: 'Duplicate intent_display_name.' });
521
+ // overwrite duplicated intents
522
+ if (req.query.overwrite == "true") {
523
+ Faq.findOneAndUpdate({ id_faq_kb: id_faq_kb, intent_display_name: intent.intent_display_name }, new_faq, { new: true, upsert: true, rawResult: true }, (err, savingResult) => {
524
+ if (err) {
525
+ winston.error("findOneAndUpdate (upsert) FAQ ERROR ", err);
449
526
  } else {
450
- winston.debug('--- > ERROR ', err)
451
- return res.status(500).send({ success: false, msg: 'Error saving intent.' });
527
+
528
+ if (savingResult.lastErrorObject.updatedExisting == true) {
529
+ winston.info("updated existing intent")
530
+ faqBotEvent.emit('faq.update', savingResult.value);
531
+ } else {
532
+ winston.info("new intent crated")
533
+ faqBotEvent.emit('faq.create', savingResult.value);
534
+ }
535
+
452
536
  }
453
- }
454
- winston.debug("NEW FAQ CREATED WITH ID: ", savedFaq._id);
455
- faqBotEvent.emit('faq.create', savedFaq);
456
- })
537
+
538
+ })
539
+
540
+ // don't overwrite duplicated intents
541
+ } else {
542
+ Faq.create(new_faq, (err, savedFaq) => {
543
+ if (err) {
544
+ winston.debug("create new FAQ ERROR ", err);
545
+ if (err.code == 11000) {
546
+ winston.error("Duplicate intent_display_name.");
547
+ winston.info("Skip duplicated intent_display_name");
548
+ } else {
549
+ winston.info("new intent crated")
550
+ faqBotEvent.emit('faq.create', savedFaq);
551
+ }
552
+ }
553
+ })
554
+ }
457
555
 
458
556
  })
459
557
 
@@ -499,12 +597,16 @@ router.get('/exportjson/:id_faq_kb', (req, res) => {
499
597
  intents: intents
500
598
  }
501
599
  let intents_string = JSON.stringify(intents_obj);
502
- res.set({ "Content-Disposition": "attachment; filename=\"intents.txt\"" });
600
+ res.set({ "Content-Disposition": "attachment; filename=\"intents.json\"" });
503
601
  return res.send(intents_string);
504
602
 
505
603
  } else {
604
+
605
+ // if (req.query.file == "false") {
606
+ // return res.status(200).send(json);
607
+ // }
506
608
  let json_string = JSON.stringify(json);
507
- res.set({ "Content-Disposition": "attachment; filename=\"bot.txt\"" });
609
+ res.set({ "Content-Disposition": "attachment; filename=\"bot.json\"" });
508
610
  return res.send(json_string);
509
611
  }
510
612
 
package/routes/request.js CHANGED
@@ -67,7 +67,7 @@ async (req, res) => {
67
67
  let messageStatus = req.body.status || MessageConstants.CHAT_MESSAGE_STATUS.SENDING;
68
68
  winston.debug('messageStatus: ' + messageStatus);
69
69
 
70
- var request_id = req.params.request_id || 'support-group-' + req.projectid + "-" + UIDGenerator.generate();
70
+ var request_id = req.body.request_id || 'support-group-' + req.projectid + "-" + UIDGenerator.generate();
71
71
  winston.debug('request_id: ' + request_id);
72
72
 
73
73
  if (sender) {
@@ -0,0 +1,101 @@
1
+ const axios = require("axios").default;
2
+ const winston = require('../config/winston');
3
+ var Faq_kb = require("../models/faq_kb");
4
+
5
+ class ChatbotService {
6
+
7
+ constructor() {
8
+
9
+ }
10
+
11
+
12
+ async getBotById(id_faq_kb, published, api_url, chatbot_templates_api_url, token, project_id) {
13
+
14
+ winston.info("[CHATBOT SERVICE] getBotById");
15
+
16
+ // private bot
17
+ if (published == "false") {
18
+
19
+ return await axios({
20
+ url: api_url + "/" + project_id + "/faq_kb/exportjson/" + id_faq_kb,
21
+ headers: {
22
+ 'Content-Type': 'application/json',
23
+ 'Authorization': token
24
+ },
25
+ method: 'GET'
26
+ }).then((resbody) => {
27
+ winston.info("(CHATBOT SERVICE) forking private chatbot " + resbody.data.name)
28
+ let chatbot = resbody.data;
29
+ return chatbot;
30
+ }).catch((err) => {
31
+ winston.error('(CHATBOT SERVICE) FAQ_KB EXPORTJSON ERROR ' + err);
32
+ return err;
33
+ })
34
+
35
+ // public bot
36
+ } else {
37
+
38
+ return await axios({
39
+ url: chatbot_templates_api_url + "/" + id_faq_kb,
40
+ headers: {
41
+ 'Content-Type': 'application/json'
42
+ },
43
+ method: 'GET'
44
+ }).then((resbody) => {
45
+ winston.info("(CHATBOT SERVICE) forking public chatbot " + resbody.data);
46
+ let chatbot = resbody.data;
47
+ return chatbot
48
+ }).catch((err) => {
49
+ winston.error('(CHATBOT SERVICE) FAQ_KB CHATBOT TEMPLATES ERROR ' + err);
50
+ return err;
51
+ })
52
+ }
53
+
54
+ }
55
+
56
+ async createBot(api_url, token, chatbot, project_id) {
57
+
58
+ winston.info("[CHATBOT SERVICE] createBot");
59
+
60
+ return await axios({
61
+ url: api_url + '/' + project_id + '/faq_kb/',
62
+ headers: {
63
+ 'Content-Type': 'application/json',
64
+ 'Authorization': token
65
+ },
66
+ data: chatbot,
67
+ method: 'POST'
68
+ }).then((resbody) => {
69
+ winston.debug("(CHATBOT SERVICE) createBot resbody: ", resbody.data);
70
+ return resbody.data;
71
+ }).catch((err) => {
72
+ winston.error("(CHATBOT SERVICE) CREATE NEW CHATBOT ERROR " + err);
73
+ return err;
74
+ })
75
+
76
+ }
77
+
78
+ async importFaqs(api_url, id_faq_kb, token, chatbot, project_id) {
79
+
80
+ winston.info("[CHATBOT SERVICE] importFaqs");
81
+
82
+ return await axios({
83
+ url: api_url + '/' + project_id + '/faq_kb/importjson/' + id_faq_kb + "?intentsOnly=true",
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ 'Authorization': token
87
+ },
88
+ data: chatbot,
89
+ method: 'POST'
90
+ }).then((resbody) => {
91
+ winston.debug("(CHATBOT SERVICE) importFaqs resbody: ", resbody.data);
92
+ return resbody.data;
93
+ }).catch((err) => {
94
+ winston.error("(CHATBOT SERVICE) IMPORT FAQS ERROR " + err);
95
+ return err;
96
+ })
97
+ }
98
+
99
+ }
100
+
101
+ module.exports = { ChatbotService }