@tiledesk/tiledesk-server 2.3.6 → 2.3.7-1.2

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.
Files changed (126) hide show
  1. package/.github/workflows/docker-community-worker-push-latest.yml +23 -0
  2. package/.github/workflows/docker-image-tag-worker-community-tag-push.yml +22 -0
  3. package/CHANGELOG.md +361 -3
  4. package/Dockerfile-jobs +31 -0
  5. package/app.js +62 -69
  6. package/channels/chat21/chat21Handler.js +37 -6
  7. package/channels/chat21/chat21WebHook.js +62 -34
  8. package/channels/chat21/package-lock.json +663 -706
  9. package/channels/chat21/package.json +2 -2
  10. package/config/labels/widget.json +337 -136
  11. package/deploy.sh +2 -0
  12. package/event/messageEvent.js +110 -9
  13. package/jobs.js +80 -0
  14. package/jobsManager.js +47 -0
  15. package/middleware/has-role.js +10 -3
  16. package/middleware/ipFilter.js +220 -0
  17. package/middleware/passport.js +8 -2
  18. package/models/department.js +1 -1
  19. package/models/faq.js +77 -25
  20. package/models/faq_kb.js +19 -0
  21. package/models/message.js +10 -8
  22. package/models/project.js +10 -0
  23. package/models/project_user.js +10 -0
  24. package/models/request.js +12 -1
  25. package/package.json +12 -11
  26. package/pubmodules/activities/activityArchiver.js +216 -90
  27. package/pubmodules/activities/routes/activity.js +1 -1
  28. package/pubmodules/apps/index.js +8 -0
  29. package/pubmodules/apps/listener.js +27 -0
  30. package/pubmodules/cache/index.js +2 -0
  31. package/pubmodules/cache/mongoose-cachegoose-fn.js +630 -0
  32. package/pubmodules/canned/cannedResponse.js +4 -0
  33. package/pubmodules/canned/cannedResponseRoute.js +10 -5
  34. package/pubmodules/dialogflow/index.js +10 -0
  35. package/pubmodules/dialogflow/listener.js +66 -0
  36. package/pubmodules/emailNotification/requestNotification.js +58 -28
  37. package/pubmodules/events/eventRoute.js +49 -24
  38. package/pubmodules/messageTransformer/messageHandlebarsTransformerInterceptor.js +6 -1
  39. package/pubmodules/messageTransformer/messageTransformerInterceptor.js +10 -4
  40. package/pubmodules/pubModulesManager.js +173 -7
  41. package/pubmodules/queue/index.js +4 -0
  42. package/pubmodules/queue/reconnect.js +331 -0
  43. package/pubmodules/queue/reconnectFanout.js +256 -0
  44. package/pubmodules/rasa/listener.js +5 -5
  45. package/pubmodules/routing-queue/index.js +3 -0
  46. package/pubmodules/routing-queue/listener.js +328 -0
  47. package/pubmodules/rules/conciergeBot.js +2 -2
  48. package/pubmodules/scheduler/tasks/closeAgentUnresponsiveRequestTask.js +6 -1
  49. package/pubmodules/scheduler/tasks/closeBotUnresponsiveRequestTask.js +7 -1
  50. package/pubmodules/tilebot/index.js +11 -0
  51. package/pubmodules/tilebot/listener.js +85 -0
  52. package/pubmodules/trigger/rulesTrigger.js +137 -14
  53. package/pubmodules/trigger/start.js +5 -1
  54. package/pubmodules/whatsapp/index.js +7 -0
  55. package/pubmodules/whatsapp/listener.js +32 -0
  56. package/routes/auth.js +7 -2
  57. package/routes/campaigns.js +3 -3
  58. package/routes/department.js +3 -2
  59. package/routes/email.js +32 -2
  60. package/routes/faq.js +37 -2
  61. package/routes/faq_kb.js +496 -133
  62. package/routes/faqpub.js +5 -0
  63. package/routes/lead.js +56 -0
  64. package/routes/message.js +196 -14
  65. package/routes/messagesRoot.js +39 -0
  66. package/routes/project.js +76 -4
  67. package/routes/project_user.js +11 -1
  68. package/routes/project_user_test.js +19 -0
  69. package/routes/request.js +134 -30
  70. package/routes/troubleshooting.js +12 -0
  71. package/routes/users-util.js +39 -0
  72. package/routes/users.js +1 -1
  73. package/routes/widget.js +64 -2
  74. package/services/BotSubscriptionNotifier.js +5 -0
  75. package/services/banUserNotifier.js +86 -0
  76. package/services/cacheEnabler.js +56 -0
  77. package/services/chatbotService.js +101 -0
  78. package/services/departmentService.js +25 -3
  79. package/services/emailService.js +170 -28
  80. package/services/faqBotHandler.js +2 -3
  81. package/services/faqService.js +28 -3
  82. package/services/geoService.js +36 -6
  83. package/services/labelService.js +1 -1
  84. package/services/leadService.js +3 -2
  85. package/services/messageService.js +4 -2
  86. package/services/modulesManager.js +23 -76
  87. package/services/operatingHoursService.js +9 -4
  88. package/services/requestService.js +75 -39
  89. package/services/subscriptionNotifier.js +9 -4
  90. package/services/trainingService.js +106 -0
  91. package/template/email/assignedEmailMessage.html +21 -11
  92. package/template/email/assignedRequest.html +21 -11
  93. package/template/email/beenInvitedExistingUser.html +16 -6
  94. package/template/email/beenInvitedNewUser.html +16 -6
  95. package/template/email/emailDirect.html +130 -0
  96. package/template/email/newMessage.html +18 -8
  97. package/template/email/newMessageFollower.html +22 -12
  98. package/template/email/passwordChanged.html +15 -5
  99. package/template/email/pooledEmailMessage.html +21 -11
  100. package/template/email/pooledRequest.html +20 -10
  101. package/template/email/resetPassword.html +15 -5
  102. package/template/email/sendTranscript.html +7 -4
  103. package/template/email/ticket.html +17 -7
  104. package/template/email/verify.html +15 -5
  105. package/test/cannedRoute.js +157 -0
  106. package/test/chatbot-mock.js +127 -0
  107. package/test/example-json-intents.txt +1 -0
  108. package/test/example-json.txt +1 -0
  109. package/test/example.json +1 -0
  110. package/test/faqRoute.js +353 -208
  111. package/test/faqkbRoute.js +669 -64
  112. package/test/imageRoute.js +1 -1
  113. package/test/messageRoute.js +387 -5
  114. package/test/requestRoute.js +6 -6
  115. package/test/requestService.js +55 -4
  116. package/test-int/cache-project.js +90 -0
  117. package/test-int/cache-project_user.js +88 -0
  118. package/utils/UIDGenerator.js +20 -0
  119. package/utils/cacheUtil.js +2 -2
  120. package/utils/orgUtil.js +3 -3
  121. package/utils/promiseUtil.js +31 -0
  122. package/utils/recipientEmailUtil.js +66 -0
  123. package/utils/sendEmailUtil.js +34 -0
  124. package/utils/sendMessageUtil.js +1 -1
  125. package/utils/stringUtil.js +12 -0
  126. package/websocket/webSocketServer.js +33 -10
@@ -0,0 +1,256 @@
1
+ var amqp = require('amqplib/callback_api');
2
+ var winston = require('../../config/winston');
3
+ const requestEvent = require('../../event/requestEvent');
4
+ const messageEvent = require('../../event/messageEvent');
5
+ const authEvent = require('../../event/authEvent');
6
+ // https://elements.heroku.com/addons/cloudamqp
7
+ // https://gist.github.com/carlhoerberg/006b01ac17a0a94859ba#file-reconnect-js
8
+ // http://www.rabbitmq.com/tutorials/tutorial-one-javascript.html
9
+
10
+ // if the connection is closed or fails to be established at all, we will reconnect
11
+ var amqpConn = null;
12
+ var url = process.env.CLOUDAMQP_URL + "?heartbeat=60" || "amqp://localhost";
13
+ // attento devi aggiornare configMap di PRE E PROD
14
+ // var url = process.env.AMQP_URL + "?heartbeat=60" || "amqp://localhost";
15
+
16
+ // MOD0
17
+ var exchange = 'ws';
18
+
19
+ function start() {
20
+ amqp.connect(url, function(err, conn) {
21
+ if (err) {
22
+ winston.error("[AMQP Fanout]", err);
23
+ return setTimeout(start, 1000);
24
+ }
25
+ conn.on("error", function(err) {
26
+ if (err.message !== "Connection closing") {
27
+ winston.error("[AMQP Fanout] conn error", err);
28
+ }
29
+ });
30
+ conn.on("close", function() {
31
+ winston.error("[AMQP Fanout] reconnecting");
32
+ return setTimeout(start, 1000);
33
+ });
34
+
35
+ winston.info("[AMQP Fanout] connected");
36
+ amqpConn = conn;
37
+
38
+ whenConnected();
39
+ });
40
+ }
41
+
42
+ function whenConnected() {
43
+ startPublisher();
44
+ startWorker();
45
+ }
46
+
47
+ var pubChannel = null;
48
+ var offlinePubQueue = [];
49
+ function startPublisher() {
50
+ amqpConn.createConfirmChannel(function(err, ch) {
51
+ if (closeOnErr(err)) return;
52
+ ch.on("error", function(err) {
53
+ winston.error("[AMQP Fanout] channel error", err);
54
+ });
55
+ ch.on("close", function() {
56
+ winston.info("[AMQP Fanout] channel closed");
57
+ });
58
+
59
+ pubChannel = ch;
60
+ while (true) {
61
+ var m = offlinePubQueue.shift();
62
+ if (!m) break;
63
+ publish(m[0], m[1], m[2]);
64
+ }
65
+ });
66
+ }
67
+
68
+ // method to publish a message, will queue messages internally if the connection is down and resend later
69
+ function publish(exchange, routingKey, content) {
70
+ try {
71
+
72
+ // MOD2
73
+ // pubChannel.publish('logs', '', Buffer.from('Hello World!'));
74
+ pubChannel.publish(exchange, routingKey, content, { },
75
+ // pubChannel.publish(exchange, routingKey, content, { persistent: true },
76
+ function(err, ok) {
77
+ if (err) {
78
+ winston.error("[AMQP Fanout] publish", err);
79
+ offlinePubQueue.push([exchange, routingKey, content]);
80
+ pubChannel.connection.close();
81
+ }
82
+ });
83
+ } catch (e) {
84
+ winston.error("[AMQP Fanout] publish", e);
85
+ offlinePubQueue.push([exchange, routingKey, content]);
86
+ }
87
+ }
88
+
89
+ // A worker that acks messages only if processed succesfully
90
+ // var channel;
91
+ function startWorker() {
92
+ amqpConn.createChannel(function(err, ch) {
93
+ if (closeOnErr(err)) return;
94
+ ch.on("error", function(err) {
95
+ winston.error("[AMQP Fanout] channel error", err);
96
+ });
97
+ ch.on("close", function() {
98
+ winston.info("[AMQP Fanout] channel closed");
99
+ });
100
+ ch.prefetch(10);//leggila da env
101
+
102
+ // ch.assertExchange(exchange, 'topic', {
103
+ // durable: true
104
+ // });
105
+
106
+ // MOD1
107
+ ch.assertExchange(exchange, 'fanout', {durable: false});
108
+
109
+ //MOD3
110
+ ch.assertQueue('', {exclusive: true}, function(err, _ok) {
111
+ // ch.assertQueue("jobs", { durable: true }, function(err, _ok) {
112
+
113
+ if (closeOnErr(err)) return;
114
+
115
+ //MOD4
116
+ ch.bindQueue(_ok.queue, exchange, '', {}, function(err3, oka) {
117
+ winston.info("Queue Fanout bind: "+_ok.queue+ " err: "+err3);
118
+ winston.info("Data Queue Fanout", oka)
119
+ });
120
+
121
+ // ch.bindQueue(_ok.queue, exchange, "request_create", {}, function(err3, oka) {
122
+ // console.log("queue bind: "+_ok.queue+ " err: "+err3+ " key: request_create");
123
+ // console.log("data queue", oka)
124
+ // });
125
+ // ch.bindQueue(_ok.queue, exchange, "request_update", {}, function(err3, oka) {
126
+ // console.log("queue bind: "+_ok.queue+ " err: "+err3+ " key: request_update");
127
+ // console.log("data queue", oka)
128
+ // });
129
+ // ch.bindQueue(_ok.queue, exchange, "message_create", {}, function(err3, oka) {
130
+ // console.log("queue bind: "+_ok.queue+ " err: "+err3+ " key: message_create");
131
+ // console.log("data queue", oka)
132
+ // });
133
+ // ch.bindQueue(_ok.queue, exchange, "project_user_update", {}, function(err3, oka) {
134
+ // console.log("queue bind: "+_ok.queue+ " err: "+err3+ " key: project_user_update");
135
+ // console.log("data queue", oka)
136
+ // });
137
+ ch.consume(_ok.queue, processMsg, { noAck: false });
138
+ winston.info("Worker Fanout is started");
139
+ });
140
+
141
+
142
+ function processMsg(msg) {
143
+ work(msg, function(ok) {
144
+ try {
145
+ if (ok)
146
+ ch.ack(msg);
147
+ else
148
+ ch.reject(msg, true);
149
+ } catch (e) {
150
+ closeOnErr(e);
151
+ }
152
+ });
153
+ }
154
+ });
155
+ }
156
+
157
+ function work(msg, cb) {
158
+ const message_string = msg.content.toString();
159
+ const topic = msg.fields.routingKey //.replace(/[.]/g, '/');
160
+
161
+ winston.debug("Got Fanout msg topic:" + topic);
162
+
163
+ winston.debug("Got Fanout msg:"+ message_string + " topic:" + topic);
164
+
165
+ if (topic === 'request_create') {
166
+ winston.debug("reconnectfanout here topic:" + topic);
167
+ winston.debug("reconnect request.update")
168
+ requestEvent.emit('request.create.queue.pubsub', JSON.parse(message_string));
169
+ }
170
+ if (topic === 'request_update') {
171
+ winston.debug("reconnectfanout here topic:" + topic);
172
+ requestEvent.emit('request.update.queue.pubsub', JSON.parse(message_string));
173
+ }
174
+ if (topic === 'message_create') {
175
+ winston.debug("reconnectfanout here topic:" + topic);
176
+ messageEvent.emit('message.create.queue.pubsub', JSON.parse(message_string));
177
+ }
178
+ if (topic === 'project_user_update') {
179
+ winston.debug("reconnectfanout here topic:" + topic);
180
+ authEvent.emit('project_user.update.queue.pubsub', JSON.parse(message_string));
181
+ }
182
+ cb(true);
183
+ // WebSocket.cb(true);
184
+ // requestEvent.on(msg.KEYYYYYYY+'.ws', msg.content);
185
+ }
186
+
187
+
188
+ function closeOnErr(err) {
189
+ if (!err) return false;
190
+ winston.error("[AMQP Fanout] error", err);
191
+ amqpConn.close();
192
+ return true;
193
+ }
194
+
195
+ // setInterval(function() {
196
+ // var d = new Date();
197
+ // publish(exchange, "request_create", Buffer.from("work work work: "+d));
198
+ // }, 10000);
199
+
200
+
201
+
202
+ // http://www.squaremobius.net/amqp.node/channel_api.html
203
+ // https://docs.parseplatform.org/parse-server/guide/#scalability
204
+
205
+
206
+
207
+ function listen() {
208
+
209
+ requestEvent.on('request.create', function(request) {
210
+ setImmediate(() => {
211
+ publish(exchange, "request_create", Buffer.from(JSON.stringify(request)));
212
+ });
213
+ });
214
+
215
+ requestEvent.on('request.update', function(request) {
216
+ setImmediate(() => {
217
+ publish(exchange, "request_update", Buffer.from(JSON.stringify(request)));
218
+ });
219
+ });
220
+
221
+
222
+ messageEvent.on('message.create', function(message) {
223
+ setImmediate(() => {
224
+ publish(exchange, "message_create", Buffer.from(JSON.stringify(message)));
225
+ });
226
+ });
227
+
228
+ authEvent.on('project_user.update',function(data) {
229
+ setImmediate(() => {
230
+ let user = undefined;
231
+ let body = undefined;
232
+ if (data.req ) {
233
+ if (data.req.user) { //i think is null from chat21webhook
234
+ user = data.req.user;
235
+ }
236
+ if (data.req.body) {
237
+ body = data.req.body;
238
+ }
239
+ }
240
+ var dat = {updatedProject_userPopulated: data.updatedProject_userPopulated, req: {user: user, body: body}}; //remove request
241
+ winston.debug("dat",dat);
242
+
243
+ publish(exchange, "project_user_update", Buffer.from(JSON.stringify(dat)));
244
+ });
245
+ });
246
+ }
247
+
248
+ if (process.env.QUEUE_ENABLED === "true") {
249
+ requestEvent.queueEnabled = true;
250
+ messageEvent.queueEnabled = true;
251
+ authEvent.queueEnabled = true;
252
+ listen();
253
+ start();
254
+ winston.info("Queue Fanout enabled. endpint: " + url );
255
+ }
256
+
@@ -6,7 +6,7 @@ var configGlobal = require('../../config/global');
6
6
 
7
7
  var port = process.env.PORT || '3000';
8
8
 
9
- const BOT_RASA_ENDPOINT = "http://localhost:" + port+ "/modules/rasa/rasabot" || process.env.BOT_RASA_ENDPOINT;
9
+ const BOT_RASA_ENDPOINT = process.env.BOT_RASA_ENDPOINT || "http://localhost:" + port+ "/modules/rasa/rasabot";
10
10
  winston.debug("BOT_RASA_ENDPOINT: " + BOT_RASA_ENDPOINT);
11
11
 
12
12
  // if (BOT_RASA_ENDPOINT) {
@@ -23,7 +23,7 @@ class Listener {
23
23
  listen(config) {
24
24
 
25
25
  winston.info('Rasa Listener listen');
26
- winston.debug("config databaseUri: " + config.databaseUri);
26
+ winston.debug("rasa config databaseUri: " + config.databaseUri);
27
27
 
28
28
 
29
29
  var that = this;
@@ -34,7 +34,7 @@ class Listener {
34
34
  KVBASE_COLLECTION : process.env.KVBASE_COLLECTION,
35
35
  MONGODB_URI: config.databaseUri,
36
36
  API_ENDPOINT: apiUrl,
37
- log: true
37
+ log: process.env.RASABOT_LOG
38
38
  }, () => {
39
39
  winston.info("RASA proxy server successfully started.");
40
40
  });
@@ -44,10 +44,10 @@ class Listener {
44
44
  botEvent.on('faqbot.create', function(bot) {
45
45
  if (BOT_RASA_ENDPOINT) {
46
46
 
47
- winston.info('bot.type:'+bot.type);
47
+ winston.debug('bot.type:'+bot.type);
48
48
  if (bot.type==="rasa") {
49
49
 
50
- winston.info('qui.type:'+bot.type);
50
+ winston.debug('qui.type:'+bot.type);
51
51
 
52
52
 
53
53
  Faq_kb.findByIdAndUpdate(bot.id, {"url":BOT_RASA_ENDPOINT}, { new: true, upsert: true }, function (err, savedFaq_kb) {
@@ -0,0 +1,3 @@
1
+ const listener = require("./listener");
2
+
3
+ module.exports = {listener:listener };
@@ -0,0 +1,328 @@
1
+ const projectEvent = require('../../event/projectEvent');
2
+ const departmentEvent = require('../../event/departmentEvent');
3
+ const authEvent = require('../../event/authEvent');
4
+ const requestEvent = require('../../event/requestEvent');
5
+ var Request = require('../../models/request');
6
+ var Project = require('../../models/project');
7
+ var Project_user = require('../../models/project_user');
8
+ var winston = require('../../config/winston');
9
+
10
+ var ProjectUserUtil = require("../../utils/project_userUtil");
11
+
12
+ // var request = require('retry-request', {
13
+ // request: require('request')
14
+ // });
15
+
16
+ // TODO riabilitare questo
17
+
18
+ // const ROUTE_QUEUE_ENDPOINT = process.env.ROUTE_QUEUE_ENDPOINT;
19
+ // winston.debug("ROUTE_QUEUE_ENDPOINT: " + ROUTE_QUEUE_ENDPOINT);
20
+
21
+ // if (ROUTE_QUEUE_ENDPOINT) {
22
+ // winston.info("Route queue endpoint: " + ROUTE_QUEUE_ENDPOINT);
23
+ // } else {
24
+ // winston.info("Route queue endpoint not configured");
25
+ // }
26
+
27
+
28
+ class Listener {
29
+
30
+
31
+ constructor() {
32
+ this.enabled = true;
33
+ if (process.env.ROUTE_QUEUE_ENABLED=="false" || process.env.ROUTE_QUEUE_ENABLED==false) {
34
+ this.enabled = false;
35
+ }
36
+ winston.debug("Listener this.enabled: "+ this.enabled);
37
+ }
38
+
39
+ nextOperator(array, index) {
40
+ // console.log('array: ', array);
41
+ // console.log('index: ' + index);
42
+
43
+ index = index || 0;
44
+
45
+ if (array === undefined || array === null)
46
+ array = [];
47
+ else if (!Array.isArray(array))
48
+ throw new Error('Expecting argument to RoundRound to be an Array');
49
+
50
+ // return function () {
51
+ index++;
52
+ if (index >= array.length) index = 0;
53
+ // console.log('index: ' + index);
54
+ return array[index];
55
+ // };
56
+ }
57
+
58
+ // db.getCollection('project_users').find({"number_assigned_requests" : {"$lt":0}}).count()
59
+
60
+
61
+ updateProjectUser(id_user, id_project, operation) {
62
+ winston.debug("updateProjectUser start");
63
+ return Project_user
64
+ .findOneAndUpdate({id_user: id_user, id_project: id_project}, {$inc : {'number_assigned_requests' : operation}}, {new: true, upsert:false}, function(err, updatedPU) {
65
+ if (err) {
66
+ return winston.error(err);
67
+ }
68
+ winston.debug("number_assigned_requests +1 :" + updatedPU.id);
69
+
70
+ updatedPU.populate({path:'id_user', select:{'firstname':1, 'lastname':1}},function (err, updatedProject_userPopulated){
71
+
72
+ var pu = updatedProject_userPopulated.toJSON();
73
+
74
+ return Project.findById(id_project).exec(function(err, project) {
75
+ pu.isBusy = ProjectUserUtil.isBusy(updatedProject_userPopulated, project.settings && project.settings.max_agent_assigned_chat);
76
+ winston.debug("pu.isBusy: "+ pu.isBusy);
77
+ authEvent.emit('project_user.update', {updatedProject_userPopulated:pu, req: undefined, skipArchive: true});
78
+ })
79
+
80
+ });
81
+
82
+ });
83
+ }
84
+
85
+ updateParticipatingProjectUsers(request, operation) {
86
+ winston.debug("request.participatingAgents", request.participatingAgents);
87
+ if (request.participatingAgents.length>0) {
88
+ request.participatingAgents.forEach(user => {
89
+ winston.debug("request.participatingAgents user",user); //it is a user and not a project_user
90
+ this.updateProjectUser(user.id, request.id_project, operation);
91
+ });
92
+ }
93
+ }
94
+
95
+ listen() {
96
+
97
+ if (this.enabled==true) {
98
+ winston.info("Route queue Listener listen");
99
+ } else {
100
+ return winston.info("Route queue Listener disabled");
101
+ }
102
+
103
+ var that = this;
104
+
105
+ // TODO fai versione che passa anche project
106
+ requestEvent.on('request.create', async (request) => {
107
+ setImmediate(() => {
108
+ this.updateParticipatingProjectUsers(request, +1);
109
+ });
110
+ });
111
+
112
+ // TODO usa versione complete con project per evitare query??
113
+ requestEvent.on('request.close', async (request) => {
114
+ setImmediate(() => {
115
+ this.updateParticipatingProjectUsers(request, -1);
116
+ });
117
+ });
118
+
119
+
120
+ requestEvent.on('request.participants.join', async (data) => {
121
+ var request = data.request;
122
+ var member = data.member;
123
+ setImmediate(() => {
124
+ this.updateProjectUser(member, request.id_project, 1);
125
+ });
126
+ });
127
+
128
+ requestEvent.on('request.participants.leave', async (data) => {
129
+ var request = data.request;
130
+ var member = data.member;
131
+ setImmediate(() => {
132
+ this.updateProjectUser(member, request.id_project, -1);
133
+ });
134
+ });
135
+
136
+ requestEvent.on('request.participants.update', async (data) => {
137
+ var request = data.request;
138
+ var removedParticipants = data.removedParticipants;
139
+ var addedParticipants = data.addedParticipants;
140
+
141
+ setImmediate(() => {
142
+
143
+ addedParticipants.forEach(participant => {
144
+ winston.debug('addedParticipants participant', participant);
145
+ this.updateProjectUser(participant, request.id_project, 1);
146
+ });
147
+
148
+ removedParticipants.forEach(participant => {
149
+ winston.debug('removedParticipants participant', participant);
150
+ this.updateProjectUser(participant, request.id_project, -1);
151
+ });
152
+
153
+ });
154
+ });
155
+
156
+ departmentEvent.on('operator.select.base2', async (res) => {
157
+ // departmentEvent.prependListener('operator.select', async (data) => {
158
+
159
+ var operatorsResult = res.result;
160
+ winston.debug('operator.select.base2 res', res);
161
+
162
+
163
+ var disableWebHookCall = res.disableWebHookCall;
164
+ winston.debug("operator.select.base2 disableWebHookCall: "+ disableWebHookCall);
165
+
166
+ if (disableWebHookCall===true) {
167
+ winston.debug("operator.select.base2 disableWebHookCall enabled: "+ disableWebHookCall);
168
+ // return callNextEvent('operator.select', res);
169
+ return res.resolve(operatorsResult);
170
+
171
+ }
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+ var project = operatorsResult.project;
181
+ var max_agent_assigned_chat = undefined;
182
+
183
+
184
+ // console.log("project: ", project);
185
+
186
+ if (project && project.settings && project.settings.chat_limit_on && project.settings.max_agent_assigned_chat) {
187
+ max_agent_assigned_chat = project.settings.max_agent_assigned_chat;
188
+ winston.debug('operator.select max_agent_assigned_chat: '+max_agent_assigned_chat);
189
+
190
+ } else {
191
+ winston.debug("chat_limit_on not defined calling next ");
192
+ return departmentEvent.callNextEvent('operator.select', res);
193
+ }
194
+
195
+
196
+
197
+
198
+ // winston.info('qui', operatorsResult.available_agents.length);
199
+ operatorsResult.available_agents_request = [];
200
+
201
+ if (operatorsResult && operatorsResult.available_agents && operatorsResult.available_agents.length > 0) {
202
+
203
+ // winston.info('qui1');
204
+ // qui1000
205
+ var query = {id_project: operatorsResult.id_project, status: {$lt:1000}};
206
+ // asyncForEach(operatorsResult.available_agents, async (aa) => {
207
+ for (const aa of operatorsResult.available_agents) {
208
+ query.participants = aa.id_user._id.toString();// attento qui
209
+ winston.debug("department operators query:" , query);
210
+
211
+
212
+ // questa cosa nn va bene. nn puoi usare ? number_assigned_requests??
213
+ var count = await Request.countDocuments(query);
214
+ winston.debug("department operators count: "+ count);
215
+ operatorsResult.available_agents_request.push({project_user: aa, openRequetsCount : count});
216
+
217
+ }
218
+
219
+ }
220
+
221
+
222
+
223
+
224
+
225
+
226
+ var available_agents_request = operatorsResult.available_agents_request;
227
+ var available_agents_not_busy = [];
228
+ if (available_agents_request && available_agents_request.length>0) {
229
+ for (const aa of available_agents_request) {
230
+ // console.log("aa.openRequetsCount ", aa.openRequetsCount, " for:", aa.project_user.id_user);
231
+
232
+ var maxAssignedchatForUser = max_agent_assigned_chat;
233
+
234
+ var max_agent_assigned_chat_specific_user = aa.project_user.max_assigned_chat;
235
+ // console.log("max_agent_assigned_chat_specific_user: ", max_agent_assigned_chat_specific_user);
236
+
237
+ if (max_agent_assigned_chat_specific_user && max_agent_assigned_chat_specific_user!=-1 ) {
238
+ maxAssignedchatForUser = max_agent_assigned_chat_specific_user;
239
+ }
240
+
241
+ winston.debug("maxAssignedchatForUser: "+ maxAssignedchatForUser);
242
+
243
+ if (aa.openRequetsCount < maxAssignedchatForUser) {
244
+ winston.debug("adding "+ aa.project_user.id_user+ " with openRequetsCount: "+ aa.openRequetsCount +" and with maxAssignedchatForUser " + maxAssignedchatForUser +" to available_agents_not_busy" );
245
+ available_agents_not_busy.push(aa.project_user);
246
+ }
247
+ }
248
+ } else {
249
+ winston.debug("available_agents_request not defined");
250
+ }
251
+
252
+ winston.debug("available_agents_not_busy", available_agents_not_busy);
253
+
254
+
255
+
256
+ // TODO non riassegnare allo stesso utente usa history oppure lastabandonedby in attributes
257
+ // in project_user sengni il numero di abandoni se uguale a psettng lo metto offline
258
+ winston.debug("res.context",res.context);
259
+
260
+ winston.debug("res.context.request.attributes",res.context.request.attributes);
261
+
262
+ if (res.context && res.context.request && res.context.request &&
263
+ res.context.request.attributes && res.context.request.attributes.abandoned_by_project_users ) { //&& res.context.request.attributes.queue_important==false (vip sla continuo reassign)
264
+
265
+ // var abandoned_by_project_users= {"5ecd44cfa3f5670034109b44":true,"5ecd56a10e7d2d00343203cc":true}
266
+
267
+ winston.debug("res.context.request.attributes.abandoned_by_project_users: ", res.context.request.attributes.abandoned_by_project_users );
268
+
269
+ var abandoned_by_project_usersAsArray = Object.keys(res.context.request.attributes.abandoned_by_project_users);
270
+
271
+ if (abandoned_by_project_usersAsArray.length>0 ) {
272
+ winston.debug("abandoned_by_project_usersAsArray", abandoned_by_project_usersAsArray);
273
+
274
+ var available_agents_not_busy = available_agents_not_busy.filter(projectUser=> !abandoned_by_project_usersAsArray.includes(projectUser._id.toString()))
275
+
276
+ winston.debug("available_agents_not_busy after: ", available_agents_not_busy );
277
+ }
278
+ }
279
+
280
+ // if (res.context && res.context.request && res.context.request &&
281
+ // res.context.request.attributes && res.context.request.attributes.last_abandoned_by ) {
282
+ // winston.debug("res.context.request.attributes.last_abandoned_by: "+res.context.request.attributes.last_abandoned_by );
283
+ // let last_abandoned_by_index = available_agents_not_busy.findIndex(projectUser => projectUser._id.toString() === res.context.request.attributes.last_abandoned_by);
284
+ // winston.debug("last_abandoned_by_index: "+last_abandoned_by_index );
285
+ // if (last_abandoned_by_index>-1) {
286
+ // available_agents_not_busy.splice(last_abandoned_by_index, 1);
287
+ // winston.debug("available_agents_not_busy after", available_agents_not_busy );
288
+ // }
289
+
290
+ // }
291
+
292
+
293
+ var lastOperatorId = operatorsResult.lastOperatorId;
294
+
295
+ let lastOperatorIndex = available_agents_not_busy.findIndex(projectUser => projectUser.id_user.toString() === lastOperatorId);
296
+ winston.debug("lastOperatorIndex: "+ lastOperatorIndex);
297
+ var nextOper = that.nextOperator(available_agents_not_busy, lastOperatorIndex);
298
+ // console.log("nextOper: ", nextOper);
299
+ var nextOperatorId = undefined;
300
+ if (nextOper && nextOper.id_user) {
301
+ nextOperatorId = nextOper.id_user;
302
+ winston.verbose("nextOperatorId: "+ nextOperatorId);
303
+
304
+ operatorsResult.operators = [{id_user: nextOperatorId}];
305
+
306
+ // return resolve(result);
307
+ } else {
308
+ winston.debug("nextOper is not defined");
309
+ operatorsResult.operators = [];
310
+ // return resolve(result);
311
+ }
312
+
313
+
314
+ return departmentEvent.callNextEvent('operator.select', res);
315
+
316
+
317
+
318
+ });
319
+
320
+
321
+ }
322
+
323
+ }
324
+
325
+ var listener = new Listener();
326
+
327
+
328
+ module.exports = listener;
@@ -198,7 +198,7 @@ devi mandare un messaggio welcome tu altrimenti il bot inserito successivamente
198
198
  "Chat closed",
199
199
  request.id_project,
200
200
  'system',
201
- {subtype:"info/support", "updateconversation" : false, messagelabel: {key: "CHAT_CLOSED"}},
201
+ {subtype:"info/support", "updateconversation" : false, messagelabel: {key: "CHAT_CLOSED"}}, // TODO send request.closed_by <- c'è il campo l'ho verificato
202
202
  undefined,
203
203
  request.language
204
204
  );
@@ -228,7 +228,7 @@ devi mandare un messaggio welcome tu altrimenti il bot inserito successivamente
228
228
  request.request_id,
229
229
  "Chat reopened",
230
230
  request.id_project,
231
- 'system',
231
+ 'system', //changed from false to true 6Aug22 because reopen from dashboard history doesn't update convs in ionic
232
232
  {subtype:"info/support", "updateconversation" : true, messagelabel: {key: "CHAT_REOPENED"}},
233
233
  undefined,
234
234
  request.language
@@ -90,7 +90,12 @@ findUnresponsiveRequests() {
90
90
  winston.verbose("CloseAgentUnresponsiveRequestTask: Request closed with request_id: " + request.request_id);
91
91
  // winston.info("Request closed",updatedStatusRequest);
92
92
  }).catch(function(err) {
93
- winston.error("CloseAgentUnresponsiveRequestTask: Error closing the request with request_id: " + request.request_id, err);
93
+ if (process.env.HIDE_CLOSE_REQUEST_ERRORS == true || process.env.HIDE_CLOSE_REQUEST_ERRORS == "true" ) {
94
+
95
+ } else {
96
+ winston.error("CloseAgentUnresponsiveRequestTask: Error closing the request with request_id: " + request.request_id, err);
97
+ }
98
+
94
99
  })
95
100
 
96
101
  });
@@ -65,6 +65,7 @@ findUnresponsiveRequests() {
65
65
 
66
66
  // db.getCollection('requests').find({"hasBot":true, "status": { "$lt": 1000 }, "createdAt": { "$lte" :new ISODate("2020-11-28T20:15:31Z")} }).count()
67
67
 
68
+
68
69
  var query = {hasBot:true, status: { $lt: 1000 }, createdAt: { $lte :new Date(Date.now() - this.queryAfterTimeout ).toISOString()} };
69
70
 
70
71
  if (this.queryProject) {
@@ -101,7 +102,12 @@ findUnresponsiveRequests() {
101
102
  winston.info("CloseBotUnresponsiveRequestTask: Request closed with request_id: " + request.request_id);
102
103
  // winston.info("Request closed",updatedStatusRequest);
103
104
  }).catch(function(err) {
104
- winston.error("CloseBotUnresponsiveRequestTask: Error closing the request with request_id: " + request.request_id, err);
105
+ if (process.env.HIDE_CLOSE_REQUEST_ERRORS == true || process.env.HIDE_CLOSE_REQUEST_ERRORS == "true" ) {
106
+
107
+ } else {
108
+ winston.error("CloseBotUnresponsiveRequestTask: Error closing the request with request_id: " + request.request_id, err);
109
+ }
110
+
105
111
  })
106
112
 
107
113
  });