@tiledesk/tiledesk-server 2.3.18 → 2.3.20

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.18",
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
- winston.info("ModulesManager activityArchiver started");
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("ModulesManager error starting activityArchiver module", err);
395
+ winston.info("PubModulesManager error starting routingQueue module", err);
344
396
  }
345
397
  }
346
398
 
@@ -0,0 +1,4 @@
1
+ const reconnect = require("./reconnect");
2
+ const reconnectFanout = require("./reconnectFanout");
3
+
4
+ module.exports = {reconnect:reconnect,reconnectFanout: reconnectFanout };
@@ -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,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.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
- this.routingQueue = require('@tiledesk-ent/tiledesk-server-routing-queue').listener;
221
- // this.routingQueue.listen();
222
- winston.debug("this.routingQueue:"+ this.routingQueue);
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
- winston.info("ModulesManager routing queue initialized");
225
- } catch(err) {
226
- if (err.code == 'MODULE_NOT_FOUND') {
227
- winston.info("ModulesManager init routing queue module not found");
228
- }else {
229
- winston.error("ModulesManager error initializing init routing queue module", err);
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
- this.queue = require('@tiledesk-ent/tiledesk-server-queue');
237
- winston.debug("this.queue:"+ this.queue);
236
+ // try {
237
+ // this.queue = require('@tiledesk-ent/tiledesk-server-queue');
238
+ // winston.debug("this.queue:"+ this.queue);
238
239
 
239
- winston.info("ModulesManager queue initialized");
240
- } catch(err) {
241
- if (err.code == 'MODULE_NOT_FOUND') {
242
- winston.info("ModulesManager init queue module not found");
243
- }else {
244
- winston.error("ModulesManager error initializing init queue module", err);
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
- try {
334
- this.routingQueue.listen();
335
- winston.info("ModulesManager routingQueue started");
336
- } catch(err) {
337
- winston.info("ModulesManager error starting routingQueue module", err);
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();