@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.
@@ -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();