@tiledesk/tiledesk-server 2.3.18 → 2.3.20
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/event/messageEvent.js +82 -2
- package/package.json +1 -3
- package/pubmodules/pubModulesManager.js +57 -5
- package/pubmodules/queue/index.js +4 -0
- package/pubmodules/queue/reconnect.js +247 -0
- package/pubmodules/queue/reconnectFanout.js +250 -0
- package/pubmodules/routing-queue/index.js +3 -0
- package/pubmodules/routing-queue/listener.js +328 -0
- package/services/modulesManager.js +34 -33
package/event/messageEvent.js
CHANGED
|
@@ -7,7 +7,7 @@ var MessageConstants = require("../models/messageConstants");
|
|
|
7
7
|
var message2Event = require("../event/message2Event");
|
|
8
8
|
|
|
9
9
|
var cacheUtil = require('../utils/cacheUtil');
|
|
10
|
-
|
|
10
|
+
// var requestService = require("../services/requestService");
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class MessageEvent extends EventEmitter {
|
|
@@ -40,7 +40,8 @@ function populateMessageCreate(message) {
|
|
|
40
40
|
return populateMessageWithRequest(message, 'message.create');
|
|
41
41
|
}
|
|
42
42
|
function populateMessageUpdate(message) {
|
|
43
|
-
return populateMessageWithRequest(message, 'message.update');
|
|
43
|
+
// return populateMessageWithRequest(message, 'message.update');
|
|
44
|
+
return; // do not populate message.update it's not used by anyone. Not used by webhook. populate for message.update is slow.
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
|
|
@@ -48,6 +49,7 @@ function populateMessageWithRequest(message, eventPrefix) {
|
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
winston.debug("populateMessageWithRequest "+eventPrefix, message.toObject());
|
|
52
|
+
winston.debug("populateMessageWithRequest "+eventPrefix +" "+ message.text);
|
|
51
53
|
|
|
52
54
|
var messageJson = message.toJSON();
|
|
53
55
|
|
|
@@ -159,4 +161,82 @@ messageEvent.on('message.create.simple', populateMessageCreate);
|
|
|
159
161
|
messageEvent.on('message.update.simple', populateMessageUpdate);
|
|
160
162
|
|
|
161
163
|
|
|
164
|
+
|
|
165
|
+
// riattiva commentato per performance
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
// var messageCreateKey = 'message.create';
|
|
169
|
+
// if (messageEvent.queueEnabled) {
|
|
170
|
+
// messageCreateKey = 'message.create.queue';
|
|
171
|
+
// }
|
|
172
|
+
// winston.debug("messageEvent.queueEnabled: "+messageEvent.queueEnabled);
|
|
173
|
+
|
|
174
|
+
// winston.debug("messageCreateKey: "+messageCreateKey);
|
|
175
|
+
|
|
176
|
+
// messageEvent.on(messageCreateKey, function(message) {
|
|
177
|
+
// setImmediate(() => {
|
|
178
|
+
// winston.debug("message.create before");
|
|
179
|
+
// if (!message.request) {
|
|
180
|
+
// return;
|
|
181
|
+
// }
|
|
182
|
+
// let request_id = message.request.request_id;
|
|
183
|
+
// let id_project = message.request.id_project;
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
// //update waiitng time if write an agent (member of participants)
|
|
187
|
+
// let visitor_sent_last_message = false;
|
|
188
|
+
// // winston.info(" message.request.snapshot.lead.lead_id: "+ message.request.snapshot.lead.lead_id);
|
|
189
|
+
// // winston.info(" message.sender: "+ message.sender);
|
|
190
|
+
|
|
191
|
+
// if (message.request.snapshot && message.request.snapshot.lead.lead_id == message.sender) {
|
|
192
|
+
// visitor_sent_last_message = true;
|
|
193
|
+
// }
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
// // don't work for recursive call
|
|
198
|
+
// // requestService.incrementMessagesCountByRequestId(message.request._id, message.request.id_project).then(function (savedRequest) {
|
|
199
|
+
// // winston.info("incremented request", savedRequest);
|
|
200
|
+
// // });
|
|
201
|
+
// let clonedmessage = Object.assign({}, message);
|
|
202
|
+
// delete clonedmessage.request
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
// let data = {
|
|
206
|
+
// $push: {
|
|
207
|
+
// "snapshot.messages.data": {
|
|
208
|
+
// $each: [ clonedmessage ],
|
|
209
|
+
// $slice: -30
|
|
210
|
+
// }
|
|
211
|
+
// },
|
|
212
|
+
// $inc : {'snapshot.messages.messages_count' : 1},
|
|
213
|
+
// "snapshot.messages.visitor_sent_last_message": visitor_sent_last_message,
|
|
214
|
+
// "snapshot.messages.last_message_timestamp": message.createdAt
|
|
215
|
+
// };
|
|
216
|
+
|
|
217
|
+
// // db.getCollection('requests').find({"$expr": { "$gt": [ "$snapshot.messages.visitor_last_message_timestamp", "$snapshot.messages.agent_last_message_timestamp"]}})
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
// if (visitor_sent_last_message) {
|
|
221
|
+
// data["snapshot.messages.visitor_last_message_timestamp"]= message.createdAt;
|
|
222
|
+
// } else {
|
|
223
|
+
// data["snapshot.messages.agent_last_message_timestamp"]= message.createdAt;
|
|
224
|
+
// }
|
|
225
|
+
// // db.getCollection('requests').updateOne({"request_id":"support-group-630600bfaf7cd942116bc993-3da378ec63924bb9b4934b2835b37a7c"},{"$push":{"snapshot.messages.data":{"$each":["s"],"$slice":-5}}}}})
|
|
226
|
+
// winston.debug("data", data);
|
|
227
|
+
|
|
228
|
+
// return Request
|
|
229
|
+
// .findOneAndUpdate({request_id: request_id, id_project: id_project}, data, {new: true, upsert:false}, function(err, updatedRequest) {
|
|
230
|
+
// if (err) {
|
|
231
|
+
// winston.error(err);
|
|
232
|
+
// return reject(err);
|
|
233
|
+
// }
|
|
234
|
+
// winston.info("Message count +1");
|
|
235
|
+
|
|
236
|
+
// });
|
|
237
|
+
|
|
238
|
+
// });
|
|
239
|
+
// });
|
|
240
|
+
|
|
241
|
+
|
|
162
242
|
module.exports = messageEvent;
|
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.
|
|
4
|
+
"version": "2.3.20",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "node ./bin/www",
|
|
7
7
|
"pretest": "mongodb-runner start",
|
|
@@ -33,9 +33,7 @@
|
|
|
33
33
|
"@tiledesk-ent/tiledesk-server-dialogflow": "^1.1.6",
|
|
34
34
|
"@tiledesk-ent/tiledesk-server-jwthistory": "^1.1.9",
|
|
35
35
|
"@tiledesk-ent/tiledesk-server-payments": "^1.1.6",
|
|
36
|
-
"@tiledesk-ent/tiledesk-server-queue": "^1.1.11",
|
|
37
36
|
"@tiledesk-ent/tiledesk-server-request-history": "^1.1.5",
|
|
38
|
-
"@tiledesk-ent/tiledesk-server-routing-queue": "^1.1.11",
|
|
39
37
|
"@tiledesk-ent/tiledesk-server-visitorcounter": "^1.1.1",
|
|
40
38
|
"@tiledesk-ent/tiledesk-server-enterprise": "^1.0.0"
|
|
41
39
|
},
|
|
@@ -33,6 +33,12 @@ class PubModulesManager {
|
|
|
33
33
|
|
|
34
34
|
this.tilebot = undefined;
|
|
35
35
|
this.tilebotRoute = undefined;
|
|
36
|
+
|
|
37
|
+
this.queue = undefined;
|
|
38
|
+
|
|
39
|
+
this.jobsManager = undefined;
|
|
40
|
+
|
|
41
|
+
this.routingQueue = undefined;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
|
|
@@ -88,6 +94,8 @@ class PubModulesManager {
|
|
|
88
94
|
init(config) {
|
|
89
95
|
winston.debug("PubModulesManager init");
|
|
90
96
|
|
|
97
|
+
// this.jobsManager = config.jobsManager;
|
|
98
|
+
|
|
91
99
|
try {
|
|
92
100
|
this.appRules = require('./rules/appRules');
|
|
93
101
|
// this.appRules.start();
|
|
@@ -128,7 +136,6 @@ class PubModulesManager {
|
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
|
|
131
|
-
|
|
132
139
|
try {
|
|
133
140
|
this.emailNotification = require('./emailNotification');
|
|
134
141
|
winston.debug("this.emailNotification:"+ this.emailNotification);
|
|
@@ -203,7 +210,6 @@ class PubModulesManager {
|
|
|
203
210
|
|
|
204
211
|
|
|
205
212
|
|
|
206
|
-
|
|
207
213
|
try {
|
|
208
214
|
this.activityArchiver = require('./activities').activityArchiver;
|
|
209
215
|
// this.activityArchiver.listen();
|
|
@@ -282,6 +288,39 @@ class PubModulesManager {
|
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
this.queue = require('./queue');
|
|
295
|
+
winston.debug("this.queue:"+ this.queue);
|
|
296
|
+
|
|
297
|
+
winston.info("PubModulesManager initialized queue.");
|
|
298
|
+
} catch(err) {
|
|
299
|
+
if (err.code == 'MODULE_NOT_FOUND') {
|
|
300
|
+
winston.info("PubModulesManager init queue module not found");
|
|
301
|
+
}else {
|
|
302
|
+
winston.info("PubModulesManager error initializing init queue module", err);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
this.routingQueue = require('./routing-queue').listener;
|
|
311
|
+
// this.routingQueue.listen();
|
|
312
|
+
winston.debug("this.routingQueue:"+ this.routingQueue);
|
|
313
|
+
|
|
314
|
+
winston.info("PubModulesManager routing queue initialized");
|
|
315
|
+
} catch(err) {
|
|
316
|
+
if (err.code == 'MODULE_NOT_FOUND') {
|
|
317
|
+
winston.info("PubModulesManager init routing queue module not found");
|
|
318
|
+
}else {
|
|
319
|
+
winston.error("PubModulesManager error initializing init routing queue module", err);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
|
|
285
324
|
}
|
|
286
325
|
|
|
287
326
|
start() {
|
|
@@ -316,9 +355,11 @@ class PubModulesManager {
|
|
|
316
355
|
|
|
317
356
|
}
|
|
318
357
|
|
|
358
|
+
// job_here
|
|
319
359
|
if (this.emailNotification) {
|
|
320
360
|
try {
|
|
321
361
|
this.emailNotification.requestNotification.listen();
|
|
362
|
+
// this.jobsManager.listenEmailNotification(this.emailNotification);
|
|
322
363
|
winston.info("PubModulesManager emailNotification started.");
|
|
323
364
|
} catch(err) {
|
|
324
365
|
winston.info("PubModulesManager error starting requestNotification module", err);
|
|
@@ -334,13 +375,24 @@ class PubModulesManager {
|
|
|
334
375
|
}
|
|
335
376
|
}
|
|
336
377
|
|
|
337
|
-
|
|
378
|
+
// job_here
|
|
338
379
|
if (this.activityArchiver) {
|
|
339
380
|
try {
|
|
340
381
|
this.activityArchiver.listen();
|
|
341
|
-
|
|
382
|
+
// this.jobsManager.listenActivityArchiver(this.activityArchiver);
|
|
383
|
+
winston.info("PubModulesManager activityArchiver started");
|
|
384
|
+
} catch(err) {
|
|
385
|
+
winston.info("PubModulesManager error starting activityArchiver module", err);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
if (this.routingQueue) {
|
|
391
|
+
try {
|
|
392
|
+
this.routingQueue.listen();
|
|
393
|
+
winston.info("PubModulesManager routingQueue started");
|
|
342
394
|
} catch(err) {
|
|
343
|
-
winston.info("
|
|
395
|
+
winston.info("PubModulesManager error starting routingQueue module", err);
|
|
344
396
|
}
|
|
345
397
|
}
|
|
346
398
|
|
|
@@ -0,0 +1,247 @@
|
|
|
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
|
+
|
|
13
|
+
var url = process.env.CLOUDAMQP_URL + "?heartbeat=60" || "amqp://localhost";
|
|
14
|
+
// attento devi aggiornare configMap di PRE E PROD
|
|
15
|
+
// var url = process.env.AMQP_URL + "?heartbeat=60" || "amqp://localhost?heartbeat=60";
|
|
16
|
+
|
|
17
|
+
var exchange = 'amq.topic';
|
|
18
|
+
|
|
19
|
+
function start() {
|
|
20
|
+
amqp.connect(url, function(err, conn) {
|
|
21
|
+
if (err) {
|
|
22
|
+
winston.error("[AMQP]", err.message);
|
|
23
|
+
return setTimeout(start, 1000);
|
|
24
|
+
}
|
|
25
|
+
conn.on("error", function(err) {
|
|
26
|
+
if (err.message !== "Connection closing") {
|
|
27
|
+
winston.error("[AMQP] conn error", err);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
conn.on("close", function() {
|
|
31
|
+
winston.error("[AMQP] reconnecting");
|
|
32
|
+
return setTimeout(start, 1000);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
winston.info("[AMQP] connected");
|
|
36
|
+
amqpConn = conn;
|
|
37
|
+
|
|
38
|
+
whenConnected();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function whenConnected() {
|
|
43
|
+
startPublisher();
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
let jobWorkerEnabled = false;
|
|
47
|
+
if (process.env.JOB_WORKER_ENABLED=="true" || process.env.JOB_WORKER_ENABLED == true) {
|
|
48
|
+
jobWorkerEnabled = true;
|
|
49
|
+
}
|
|
50
|
+
winston.info("JobsManager jobWorkerEnabled: "+ jobWorkerEnabled);
|
|
51
|
+
|
|
52
|
+
if (jobWorkerEnabled == false) {
|
|
53
|
+
winston.info("Queue Reconnect start worker");
|
|
54
|
+
startWorker();
|
|
55
|
+
} else {
|
|
56
|
+
winston.info("Queue Reconnect without worker because external worker is enabled");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var pubChannel = null;
|
|
62
|
+
var offlinePubQueue = [];
|
|
63
|
+
function startPublisher() {
|
|
64
|
+
amqpConn.createConfirmChannel(function(err, ch) {
|
|
65
|
+
if (closeOnErr(err)) return;
|
|
66
|
+
ch.on("error", function(err) {
|
|
67
|
+
winston.error("[AMQP] channel error", err);
|
|
68
|
+
});
|
|
69
|
+
ch.on("close", function() {
|
|
70
|
+
winston.info("[AMQP] channel closed");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
pubChannel = ch;
|
|
74
|
+
while (true) {
|
|
75
|
+
var m = offlinePubQueue.shift();
|
|
76
|
+
if (!m) break;
|
|
77
|
+
publish(m[0], m[1], m[2]);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// method to publish a message, will queue messages internally if the connection is down and resend later
|
|
83
|
+
function publish(exchange, routingKey, content) {
|
|
84
|
+
try {
|
|
85
|
+
pubChannel.publish(exchange, routingKey, content, { persistent: true },
|
|
86
|
+
function(err, ok) {
|
|
87
|
+
if (err) {
|
|
88
|
+
winston.error("[AMQP] publish", err);
|
|
89
|
+
offlinePubQueue.push([exchange, routingKey, content]);
|
|
90
|
+
pubChannel.connection.close();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
} catch (e) {
|
|
94
|
+
winston.error("[AMQP] publish", e);
|
|
95
|
+
offlinePubQueue.push([exchange, routingKey, content]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// A worker that acks messages only if processed succesfully
|
|
100
|
+
// var channel;
|
|
101
|
+
function startWorker() {
|
|
102
|
+
amqpConn.createChannel(function(err, ch) {
|
|
103
|
+
if (closeOnErr(err)) return;
|
|
104
|
+
ch.on("error", function(err) {
|
|
105
|
+
winston.error("[AMQP] channel error", err);
|
|
106
|
+
});
|
|
107
|
+
ch.on("close", function() {
|
|
108
|
+
winston.info("[AMQP] channel closed");
|
|
109
|
+
});
|
|
110
|
+
ch.prefetch(10);//leggila da env
|
|
111
|
+
ch.assertExchange(exchange, 'topic', {
|
|
112
|
+
durable: true
|
|
113
|
+
});
|
|
114
|
+
ch.assertQueue("jobs", { durable: true }, function(err, _ok) {
|
|
115
|
+
if (closeOnErr(err)) return;
|
|
116
|
+
ch.bindQueue(_ok.queue, exchange, "request_create", {}, function(err3, oka) {
|
|
117
|
+
winston.info("Queue bind: "+_ok.queue+ " err: "+err3+ " key: request_create");
|
|
118
|
+
winston.info("Data queue", oka)
|
|
119
|
+
});
|
|
120
|
+
ch.bindQueue(_ok.queue, exchange, "request_update", {}, function(err3, oka) {
|
|
121
|
+
winston.info("Queue bind: "+_ok.queue+ " err: "+err3+ " key: request_update");
|
|
122
|
+
winston.info("Data queue", oka)
|
|
123
|
+
});
|
|
124
|
+
ch.bindQueue(_ok.queue, exchange, "message_create", {}, function(err3, oka) {
|
|
125
|
+
winston.info("Queue bind: "+_ok.queue+ " err: "+err3+ " key: message_create");
|
|
126
|
+
winston.info("Data queue", oka)
|
|
127
|
+
});
|
|
128
|
+
ch.bindQueue(_ok.queue, exchange, "project_user_update", {}, function(err3, oka) {
|
|
129
|
+
winston.info("Queue bind: "+_ok.queue+ " err: "+err3+ " key: project_user_update");
|
|
130
|
+
winston.info("Data queue", oka)
|
|
131
|
+
});
|
|
132
|
+
ch.consume("jobs", processMsg, { noAck: false });
|
|
133
|
+
winston.info("Worker is started");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
function processMsg(msg) {
|
|
138
|
+
work(msg, function(ok) {
|
|
139
|
+
try {
|
|
140
|
+
if (ok)
|
|
141
|
+
ch.ack(msg);
|
|
142
|
+
else
|
|
143
|
+
ch.reject(msg, true);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
closeOnErr(e);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function work(msg, cb) {
|
|
153
|
+
const message_string = msg.content.toString();
|
|
154
|
+
const topic = msg.fields.routingKey //.replace(/[.]/g, '/');
|
|
155
|
+
|
|
156
|
+
winston.debug("Got msg topic:" + topic);
|
|
157
|
+
|
|
158
|
+
winston.debug("Got msg:"+ message_string + " topic:" + topic);
|
|
159
|
+
|
|
160
|
+
if (topic === 'request_create') {
|
|
161
|
+
winston.info("here topic:" + topic);
|
|
162
|
+
// requestEvent.emit('request.create.queue', msg.content);
|
|
163
|
+
requestEvent.emit('request.create.queue', JSON.parse(message_string));
|
|
164
|
+
}
|
|
165
|
+
if (topic === 'request_update') {
|
|
166
|
+
winston.info("here topic:" + topic);
|
|
167
|
+
// requestEvent.emit('request.update.queue', msg.content);
|
|
168
|
+
requestEvent.emit('request.update.queue', JSON.parse(message_string));
|
|
169
|
+
}
|
|
170
|
+
if (topic === 'message_create') {
|
|
171
|
+
winston.debug("here topic:" + topic);
|
|
172
|
+
// requestEvent.emit('request.create.queue', msg.content);
|
|
173
|
+
messageEvent.emit('message.create.queue', JSON.parse(message_string));
|
|
174
|
+
}
|
|
175
|
+
if (topic === 'project_user_update') {
|
|
176
|
+
winston.debug("here topic:" + topic);
|
|
177
|
+
// requestEvent.emit('request.create.queue', msg.content);
|
|
178
|
+
authEvent.emit('project_user.update.queue', JSON.parse(message_string));
|
|
179
|
+
}
|
|
180
|
+
cb(true);
|
|
181
|
+
// WebSocket.cb(true);
|
|
182
|
+
// requestEvent.on(msg.KEYYYYYYY+'.ws', msg.content);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
function closeOnErr(err) {
|
|
187
|
+
if (!err) return false;
|
|
188
|
+
winston.error("[AMQP] error", err);
|
|
189
|
+
amqpConn.close();
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// setInterval(function() {
|
|
194
|
+
// var d = new Date();
|
|
195
|
+
// publish(exchange, "request_create", Buffer.from("work work work: "+d));
|
|
196
|
+
// publish(exchange, "request_update", Buffer.from("work2 work work: "+d));
|
|
197
|
+
// }, 1000);
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
function listen() {
|
|
201
|
+
|
|
202
|
+
// http://www.squaremobius.net/amqp.node/channel_api.html
|
|
203
|
+
// https://docs.parseplatform.org/parse-server/guide/#scalability
|
|
204
|
+
|
|
205
|
+
requestEvent.on('request.create', function(request) {
|
|
206
|
+
setImmediate(() => {
|
|
207
|
+
winston.info("reconnect request.create")
|
|
208
|
+
publish(exchange, "request_create", Buffer.from(JSON.stringify(request)));
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
requestEvent.on('request.update', function(request) {
|
|
213
|
+
setImmediate(() => {
|
|
214
|
+
winston.info("reconnect request.update")
|
|
215
|
+
publish(exchange, "request_update", Buffer.from(JSON.stringify(request)));
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
messageEvent.on('message.create', function(message) {
|
|
221
|
+
setImmediate(() => {
|
|
222
|
+
publish(exchange, "message_create", Buffer.from(JSON.stringify(message)));
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
authEvent.on('project_user.update',function(data) {
|
|
227
|
+
setImmediate(() => {
|
|
228
|
+
let user = undefined;
|
|
229
|
+
if (data.req && data.req.user) { //i think is null from chat21webhook
|
|
230
|
+
user = data.req.user;
|
|
231
|
+
}
|
|
232
|
+
var dat = {updatedProject_userPopulated: data.updatedProject_userPopulated, req: {user: user}}; //remove request
|
|
233
|
+
publish(exchange, "project_user_update", Buffer.from(JSON.stringify(dat)));
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (process.env.QUEUE_ENABLED === "true") {
|
|
240
|
+
requestEvent.queueEnabled = true;
|
|
241
|
+
messageEvent.queueEnabled = true;
|
|
242
|
+
authEvent.queueEnabled = true;
|
|
243
|
+
listen();
|
|
244
|
+
start();
|
|
245
|
+
winston.info("Queue enabled. endpint: " + url );
|
|
246
|
+
}
|
|
247
|
+
|
|
@@ -0,0 +1,250 @@
|
|
|
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.message);
|
|
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", 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("here topic:" + topic);
|
|
167
|
+
winston.info("reconnect request.update")
|
|
168
|
+
requestEvent.emit('request.create.queue.pubsub', JSON.parse(message_string));
|
|
169
|
+
}
|
|
170
|
+
if (topic === 'request_update') {
|
|
171
|
+
winston.debug("here topic:" + topic);
|
|
172
|
+
requestEvent.emit('request.update.queue.pubsub', JSON.parse(message_string));
|
|
173
|
+
}
|
|
174
|
+
if (topic === 'message_create') {
|
|
175
|
+
winston.debug("here topic:" + topic);
|
|
176
|
+
messageEvent.emit('message.create.queue.pubsub', JSON.parse(message_string));
|
|
177
|
+
}
|
|
178
|
+
if (topic === 'project_user_update') {
|
|
179
|
+
winston.debug("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
|
+
|
|
231
|
+
let user = undefined;
|
|
232
|
+
if (data.req && data.req.user) { //i think is null from chat21webhook
|
|
233
|
+
user = data.req.user;
|
|
234
|
+
}
|
|
235
|
+
var dat = {updatedProject_userPopulated: data.updatedProject_userPopulated, req: {user: user}}; //remove request
|
|
236
|
+
|
|
237
|
+
publish(exchange, "project_user_update", Buffer.from(JSON.stringify(dat)));
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (process.env.QUEUE_ENABLED === "true") {
|
|
243
|
+
requestEvent.queueEnabled = true;
|
|
244
|
+
messageEvent.queueEnabled = true;
|
|
245
|
+
authEvent.queueEnabled = true;
|
|
246
|
+
listen();
|
|
247
|
+
start();
|
|
248
|
+
winston.info("Queue Fanout enabled. endpint: " + url );
|
|
249
|
+
}
|
|
250
|
+
|
|
@@ -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.info('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;
|
|
@@ -24,6 +24,7 @@ var licenseKey = process.env.LICENSE_KEY;
|
|
|
24
24
|
if (licenseKey) {
|
|
25
25
|
var maskedLicenseKey = MaskData.maskPhone(licenseKey, maskOptions);
|
|
26
26
|
winston.info("LicenseKey: " + maskedLicenseKey);
|
|
27
|
+
// winston.info("LicenseKey: " + licenseKey);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
class ModulesManager {
|
|
@@ -38,8 +39,8 @@ class ModulesManager {
|
|
|
38
39
|
this.dialogflowListener = undefined;
|
|
39
40
|
this.requestHistoryArchiver = undefined;
|
|
40
41
|
this.requestHistoryRoute = undefined;
|
|
41
|
-
this.routingQueue = undefined;
|
|
42
|
-
this.queue = undefined;
|
|
42
|
+
// this.routingQueue = undefined;
|
|
43
|
+
// this.queue = undefined;
|
|
43
44
|
this.cache = undefined;
|
|
44
45
|
this.visitorCounterRoute = undefined;
|
|
45
46
|
this.visitorCounterMiddleware = undefined;
|
|
@@ -216,34 +217,34 @@ class ModulesManager {
|
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
|
|
219
|
-
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
// try {
|
|
221
|
+
// this.routingQueue = require('@tiledesk-ent/tiledesk-server-routing-queue').listener;
|
|
222
|
+
// // this.routingQueue.listen();
|
|
223
|
+
// winston.debug("this.routingQueue:"+ this.routingQueue);
|
|
223
224
|
|
|
224
|
-
|
|
225
|
-
} catch(err) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
225
|
+
// winston.info("ModulesManager routing queue initialized");
|
|
226
|
+
// } catch(err) {
|
|
227
|
+
// if (err.code == 'MODULE_NOT_FOUND') {
|
|
228
|
+
// winston.info("ModulesManager init routing queue module not found");
|
|
229
|
+
// }else {
|
|
230
|
+
// winston.error("ModulesManager error initializing init routing queue module", err);
|
|
231
|
+
// }
|
|
232
|
+
// }
|
|
232
233
|
|
|
233
234
|
|
|
234
235
|
|
|
235
|
-
try {
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
// try {
|
|
237
|
+
// this.queue = require('@tiledesk-ent/tiledesk-server-queue');
|
|
238
|
+
// winston.debug("this.queue:"+ this.queue);
|
|
238
239
|
|
|
239
|
-
|
|
240
|
-
} catch(err) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
240
|
+
// winston.info("ModulesManager queue initialized");
|
|
241
|
+
// } catch(err) {
|
|
242
|
+
// if (err.code == 'MODULE_NOT_FOUND') {
|
|
243
|
+
// winston.info("ModulesManager init queue module not found");
|
|
244
|
+
// }else {
|
|
245
|
+
// winston.error("ModulesManager error initializing init queue module", err);
|
|
246
|
+
// }
|
|
247
|
+
// }
|
|
247
248
|
|
|
248
249
|
|
|
249
250
|
try {
|
|
@@ -329,14 +330,14 @@ class ModulesManager {
|
|
|
329
330
|
winston.info("ModulesManager error starting requestHistoryArchiver module", err);
|
|
330
331
|
}
|
|
331
332
|
}
|
|
332
|
-
if (this.routingQueue) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
333
|
+
// if (this.routingQueue) {
|
|
334
|
+
// try {
|
|
335
|
+
// this.routingQueue.listen();
|
|
336
|
+
// winston.info("ModulesManager routingQueue started");
|
|
337
|
+
// } catch(err) {
|
|
338
|
+
// winston.info("ModulesManager error starting routingQueue module", err);
|
|
339
|
+
// }
|
|
340
|
+
// }
|
|
340
341
|
if (this.dialogflowListener) {
|
|
341
342
|
try {
|
|
342
343
|
this.dialogflowListener.listen();
|