@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.
- package/.github/workflows/docker-community-worker-push-latest.yml +23 -0
- package/.github/workflows/docker-image-tag-worker-community-tag-push.yml +22 -0
- package/CHANGELOG.md +361 -3
- package/Dockerfile-jobs +31 -0
- package/app.js +62 -69
- package/channels/chat21/chat21Handler.js +37 -6
- package/channels/chat21/chat21WebHook.js +62 -34
- package/channels/chat21/package-lock.json +663 -706
- package/channels/chat21/package.json +2 -2
- package/config/labels/widget.json +337 -136
- package/deploy.sh +2 -0
- package/event/messageEvent.js +110 -9
- package/jobs.js +80 -0
- package/jobsManager.js +47 -0
- package/middleware/has-role.js +10 -3
- package/middleware/ipFilter.js +220 -0
- package/middleware/passport.js +8 -2
- package/models/department.js +1 -1
- package/models/faq.js +77 -25
- package/models/faq_kb.js +19 -0
- package/models/message.js +10 -8
- package/models/project.js +10 -0
- package/models/project_user.js +10 -0
- package/models/request.js +12 -1
- package/package.json +12 -11
- package/pubmodules/activities/activityArchiver.js +216 -90
- package/pubmodules/activities/routes/activity.js +1 -1
- package/pubmodules/apps/index.js +8 -0
- package/pubmodules/apps/listener.js +27 -0
- package/pubmodules/cache/index.js +2 -0
- package/pubmodules/cache/mongoose-cachegoose-fn.js +630 -0
- package/pubmodules/canned/cannedResponse.js +4 -0
- package/pubmodules/canned/cannedResponseRoute.js +10 -5
- package/pubmodules/dialogflow/index.js +10 -0
- package/pubmodules/dialogflow/listener.js +66 -0
- package/pubmodules/emailNotification/requestNotification.js +58 -28
- package/pubmodules/events/eventRoute.js +49 -24
- package/pubmodules/messageTransformer/messageHandlebarsTransformerInterceptor.js +6 -1
- package/pubmodules/messageTransformer/messageTransformerInterceptor.js +10 -4
- package/pubmodules/pubModulesManager.js +173 -7
- package/pubmodules/queue/index.js +4 -0
- package/pubmodules/queue/reconnect.js +331 -0
- package/pubmodules/queue/reconnectFanout.js +256 -0
- package/pubmodules/rasa/listener.js +5 -5
- package/pubmodules/routing-queue/index.js +3 -0
- package/pubmodules/routing-queue/listener.js +328 -0
- package/pubmodules/rules/conciergeBot.js +2 -2
- package/pubmodules/scheduler/tasks/closeAgentUnresponsiveRequestTask.js +6 -1
- package/pubmodules/scheduler/tasks/closeBotUnresponsiveRequestTask.js +7 -1
- package/pubmodules/tilebot/index.js +11 -0
- package/pubmodules/tilebot/listener.js +85 -0
- package/pubmodules/trigger/rulesTrigger.js +137 -14
- package/pubmodules/trigger/start.js +5 -1
- package/pubmodules/whatsapp/index.js +7 -0
- package/pubmodules/whatsapp/listener.js +32 -0
- package/routes/auth.js +7 -2
- package/routes/campaigns.js +3 -3
- package/routes/department.js +3 -2
- package/routes/email.js +32 -2
- package/routes/faq.js +37 -2
- package/routes/faq_kb.js +496 -133
- package/routes/faqpub.js +5 -0
- package/routes/lead.js +56 -0
- package/routes/message.js +196 -14
- package/routes/messagesRoot.js +39 -0
- package/routes/project.js +76 -4
- package/routes/project_user.js +11 -1
- package/routes/project_user_test.js +19 -0
- package/routes/request.js +134 -30
- package/routes/troubleshooting.js +12 -0
- package/routes/users-util.js +39 -0
- package/routes/users.js +1 -1
- package/routes/widget.js +64 -2
- package/services/BotSubscriptionNotifier.js +5 -0
- package/services/banUserNotifier.js +86 -0
- package/services/cacheEnabler.js +56 -0
- package/services/chatbotService.js +101 -0
- package/services/departmentService.js +25 -3
- package/services/emailService.js +170 -28
- package/services/faqBotHandler.js +2 -3
- package/services/faqService.js +28 -3
- package/services/geoService.js +36 -6
- package/services/labelService.js +1 -1
- package/services/leadService.js +3 -2
- package/services/messageService.js +4 -2
- package/services/modulesManager.js +23 -76
- package/services/operatingHoursService.js +9 -4
- package/services/requestService.js +75 -39
- package/services/subscriptionNotifier.js +9 -4
- package/services/trainingService.js +106 -0
- package/template/email/assignedEmailMessage.html +21 -11
- package/template/email/assignedRequest.html +21 -11
- package/template/email/beenInvitedExistingUser.html +16 -6
- package/template/email/beenInvitedNewUser.html +16 -6
- package/template/email/emailDirect.html +130 -0
- package/template/email/newMessage.html +18 -8
- package/template/email/newMessageFollower.html +22 -12
- package/template/email/passwordChanged.html +15 -5
- package/template/email/pooledEmailMessage.html +21 -11
- package/template/email/pooledRequest.html +20 -10
- package/template/email/resetPassword.html +15 -5
- package/template/email/sendTranscript.html +7 -4
- package/template/email/ticket.html +17 -7
- package/template/email/verify.html +15 -5
- package/test/cannedRoute.js +157 -0
- package/test/chatbot-mock.js +127 -0
- package/test/example-json-intents.txt +1 -0
- package/test/example-json.txt +1 -0
- package/test/example.json +1 -0
- package/test/faqRoute.js +353 -208
- package/test/faqkbRoute.js +669 -64
- package/test/imageRoute.js +1 -1
- package/test/messageRoute.js +387 -5
- package/test/requestRoute.js +6 -6
- package/test/requestService.js +55 -4
- package/test-int/cache-project.js +90 -0
- package/test-int/cache-project_user.js +88 -0
- package/utils/UIDGenerator.js +20 -0
- package/utils/cacheUtil.js +2 -2
- package/utils/orgUtil.js +3 -3
- package/utils/promiseUtil.js +31 -0
- package/utils/recipientEmailUtil.js +66 -0
- package/utils/sendEmailUtil.js +34 -0
- package/utils/sendMessageUtil.js +1 -1
- package/utils/stringUtil.js +12 -0
- 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"
|
|
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:
|
|
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.
|
|
47
|
+
winston.debug('bot.type:'+bot.type);
|
|
48
48
|
if (bot.type==="rasa") {
|
|
49
49
|
|
|
50
|
-
winston.
|
|
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,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
|
-
|
|
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
|
-
|
|
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
|
});
|