@tiledesk/tiledesk-server 2.15.8 → 2.16.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@
5
5
  🚀 IN PRODUCTION 🚀
6
6
  (https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
7
7
 
8
+ # 2.16.0
9
+ - Added possibility to update Knowledge Base content
10
+ - Added rated only filter in Conversations History
11
+ - Improved pending requests management
12
+
8
13
  # 2.15.8
9
14
  - Updated tybot-connector to 2.0.45
10
15
  - Added support for tags management in knowledge base routes
@@ -5,6 +5,7 @@ var Message = require("../models/message");
5
5
  var Faq_kb = require("../models/faq_kb");
6
6
  var MessageConstants = require("../models/messageConstants");
7
7
  var message2Event = require("../event/message2Event");
8
+ var requestEvent = require("../event/requestEvent");
8
9
 
9
10
  var cacheUtil = require('../utils/cacheUtil');
10
11
  var cacheEnabler = require("../services/cacheEnabler");
@@ -178,6 +179,29 @@ function populateMessageWithRequest(message, eventPrefix) {
178
179
  messageEvent.on('message.create.simple', populateMessageCreate);
179
180
  messageEvent.on('message.update.simple', populateMessageUpdate);
180
181
 
182
+ // When the user (lead/requester) sends a message, reopen the conversation if it was pending
183
+ messageEvent.on('message.create.from.requester', function (messageJson) {
184
+ if (!messageJson.request || messageJson.request.workingStatus !== 'pending') return;
185
+ var request_id = messageJson.request.request_id;
186
+ var id_project = messageJson.request.id_project;
187
+ Request.findOneAndUpdate(
188
+ { request_id: request_id, id_project: id_project },
189
+ { $set: { workingStatus: 'open' } },
190
+ { new: true },
191
+ function (err, updatedRequest) {
192
+ if (err) {
193
+ winston.error("Error updating request workingStatus from pending to open", err);
194
+ return;
195
+ }
196
+ if (updatedRequest) {
197
+ winston.debug("Request workingStatus set to open (was pending)", { request_id, id_project });
198
+ requestEvent.emit('request.workingStatus.update', { request: updatedRequest });
199
+ requestEvent.emit('request.update', updatedRequest);
200
+ }
201
+ }
202
+ );
203
+ });
204
+
181
205
 
182
206
 
183
207
  // // riattiva commentato per performance
package/models/request.js CHANGED
@@ -507,6 +507,7 @@ RequestSchema.index({ id_project: 1, createdAt: -1, status: 1 })
507
507
  RequestSchema.index({ id_project: 1, preflight: 1, smartAssignment: 1, "snapshot.department.routing": 1, createdAt: 1, status: 1 })
508
508
 
509
509
  RequestSchema.index({ status: 1, hasBot: 1, updatedAt: 1 }) // For closing unresponsive requests
510
+ RequestSchema.index({ status: 1, hasBot: 1, workingStatus: 1, updatedAt: 1 }) // For closing unresponsive requests
510
511
 
511
512
  // Contact search by phone / email
512
513
  RequestSchema.index({ id_project: 1, 'contact.phone': 1 });
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.15.8",
4
+ "version": "2.16.0",
5
5
  "scripts": {
6
6
  "start": "node ./bin/www",
7
7
  "pretest": "mongodb-runner start",
@@ -175,6 +175,11 @@ function startWorker() {
175
175
  winston.info("Data queue", oka)
176
176
  });
177
177
 
178
+ ch.bindQueue(_ok.queue, exchange, "request_workingStatus_update", {}, function(err3, oka) {
179
+ winston.info("Queue bind: "+_ok.queue+ " err: "+err3+ " key: request_workingStatus_update");
180
+ winston.info("Data queue", oka)
181
+ });
182
+
178
183
  ch.bindQueue(_ok.queue, exchange, "message_create", {}, function(err3, oka) {
179
184
  winston.info("Queue bind: "+_ok.queue+ " err: "+err3+ " key: message_create");
180
185
  winston.info("Data queue", oka)
@@ -286,6 +291,11 @@ function work(msg, cb) {
286
291
  requestEvent.emit('request.close.extended.queue', JSON.parse(message_string));
287
292
  }
288
293
 
294
+ if (topic === 'request_workingStatus_update') {
295
+ winston.debug("reconnect here topic:" + topic);
296
+ requestEvent.emit('request.workingStatus.update.queue', JSON.parse(message_string));
297
+ }
298
+
289
299
  if (topic === 'message_create') {
290
300
  winston.debug("reconnect here topic:" + topic);
291
301
  // requestEvent.emit('request.create.queue', msg.content);
@@ -410,6 +420,12 @@ function listen() {
410
420
  });
411
421
  });
412
422
 
423
+ requestEvent.on('request.workingStatus.update', function(request) {
424
+ setImmediate(() => {
425
+ publish(exchange, "request_workingStatus_update", Buffer.from(JSON.stringify(request)));
426
+ });
427
+ });
428
+
413
429
  requestEvent.on('request.snapshot.update', function(data) {
414
430
  setImmediate(() => {
415
431
  winston.debug("reconnect request.snapshot.update")
@@ -50,7 +50,7 @@ class Listener {
50
50
  return winston.warn("Chatbot is not a project_user. Skip update.")
51
51
  }
52
52
 
53
- return Request.countDocuments({ id_project: id_project, participantsAgents: id_user, status: { $lt: 1000 }, draft: { $in: [null, false] } }, (err, requestsCount) => {
53
+ return Request.countDocuments({ id_project: id_project, participantsAgents: id_user, status: { $lt: 1000 }, draft: { $in: [null, false] }, workingStatus: { $ne: 'pending' } }, (err, requestsCount) => {
54
54
  winston.verbose("requestsCount for id_user: ", id_user, "and project: ", id_project, "-->", requestsCount);
55
55
  if (err) {
56
56
  return winston.error(err);
@@ -236,8 +236,27 @@ class Listener {
236
236
  });
237
237
  });
238
238
 
239
-
240
-
239
+ var requestWorkingStatusUpdateKey = 'request.workingStatus.update';
240
+ if (requestEvent.queueEnabled) {
241
+ requestWorkingStatusUpdateKey = 'request.workingStatus.update.queue';
242
+ }
243
+ winston.debug('Route queue requestWorkingStatusUpdateKey: ' + requestWorkingStatusUpdateKey);
244
+
245
+ requestEvent.on(requestWorkingStatusUpdateKey, async (data) => {
246
+ winston.debug('Route queue WorkingStatus Update');
247
+
248
+ var request = data.request;
249
+ var participantIds = (request.participantsAgents && request.participantsAgents.length)
250
+ ? request.participantsAgents
251
+ : (request.participatingAgents || []).map(u => u._id || u.id);
252
+ setImmediate(() => {
253
+ participantIds.forEach(id_user => {
254
+ if (id_user && !String(id_user).startsWith('bot_')) {
255
+ this.updateProjectUser(id_user, request.id_project, 0);
256
+ }
257
+ });
258
+ });
259
+ });
241
260
  }
242
261
 
243
262
  }
@@ -65,7 +65,8 @@ class CloseAgentUnresponsiveRequestTask {
65
65
  const query = {
66
66
  hasBot: false,
67
67
  status: { $lt: 1000 },
68
- updatedAt: { $lte: cutoffDate }
68
+ updatedAt: { $lte: cutoffDate },
69
+ workingStatus: { $ne: 'pending' }
69
70
  };
70
71
 
71
72
  if (this.queryProject) {
@@ -73,7 +73,8 @@ class CloseBotUnresponsiveRequestTask {
73
73
  const query = {
74
74
  hasBot: true,
75
75
  status: { $lt: 1000 },
76
- updatedAt: { $lte: cutoffDate }
76
+ updatedAt: { $lte: cutoffDate },
77
+ workingStatus: { $ne: 'pending' }
77
78
  };
78
79
 
79
80
  if (this.queryProject) {
package/routes/kb.js CHANGED
@@ -1773,38 +1773,181 @@ router.post('/sitemap/import', async (req, res) => {
1773
1773
 
1774
1774
  router.put('/:kb_id', async (req, res) => {
1775
1775
 
1776
- let kb_id = req.params.kb_id;
1777
- winston.verbose("update kb_id " + kb_id);
1776
+ const id_project = req.projectid;
1777
+ const project = req.project;
1778
+ const kb_id = req.params.kb_id;
1779
+
1780
+ const { name, type, source, content, refresh_rate, scrape_type, scrape_options, tags } = req.body;
1781
+ const namespace_id = req.body.namespace;
1778
1782
 
1779
- let update = {};
1783
+ if (!namespace_id) {
1784
+ return res.status(400).send({ success: false, error: "Missing 'namespace' body parameter" })
1785
+ }
1780
1786
 
1781
- if (req.body.name != undefined) {
1782
- update.name = req.body.name;
1787
+ let namespace;
1788
+ try {
1789
+ namespace = await aiManager.checkNamespace(id_project, namespace_id);
1790
+ } catch (err) {
1791
+ let errorCode = err?.errorCode ?? 500;
1792
+ return res.status(errorCode).send({ success: false, error: err.error });
1783
1793
  }
1784
1794
 
1785
- if (req.body.status != undefined) {
1786
- update.status = req.body.status;
1795
+ let kb = await KB.findOne({ id_project, namespace: namespace_id, _id: kb_id }).lean().exec();
1796
+
1797
+ if (!kb) {
1798
+ return res.status(404).send({ success: false, error: "Content not found. Unable to update a non-existing content" })
1787
1799
  }
1788
1800
 
1789
- winston.debug("kb update: ", update);
1801
+ let data = {
1802
+ id: kb._id,
1803
+ namespace: namespace_id,
1804
+ engine: namespace.engine || default_engine
1805
+ }
1790
1806
 
1791
- KB.findByIdAndUpdate(kb_id, update, { new: true }, (err, savedKb) => {
1807
+ // if (kb.type === 'sitemap') {
1808
+ // let new_sitemap = {
1809
+ // id_project,
1810
+ // name,
1811
+ // source,
1812
+ // type: 'sitemap',
1813
+ // content: "",
1814
+ // namespace: namespace_id,
1815
+ // status: -1,
1816
+ // scrape_type,
1817
+ // scrape_options,
1818
+ // refresh_rate,
1819
+ // ...(Array.isArray(tags) && tags.length > 0 ? { tags } : {})
1820
+ // }
1792
1821
 
1793
- if (err) {
1794
- winston.error("KB findByIdAndUpdate error: ", err);
1795
- return res.status(500).send({ success: false, error: err });
1822
+ // try {
1823
+ // let result = await aiManager.updateSitemap(id_project, namespace, kb, new_sitemap);
1824
+ // return res.status(200).send(result);
1825
+ // } catch (err) {
1826
+ // winston.error("Error updating sitemap: ", err);
1827
+ // return res.status(500).send({ success: false, error: err });
1828
+ // }
1829
+ // }
1830
+
1831
+ try {
1832
+ let delete_response = await aiService.deleteIndex(data);
1833
+
1834
+ if (delete_response.data.success === true) {
1835
+ await KB.findOneAndDelete({ id_project, namespace: namespace_id, source }).lean().exec();
1836
+ // continue the flow
1837
+ } else {
1838
+ winston.error("Unable to update content due to an error: ", delete_response.data.error);
1839
+ return res.status(500).send({ success: false, error: delete_response.data.error });
1796
1840
  }
1797
1841
 
1798
- if (!savedKb) {
1799
- winston.debug("Try to updating a non-existing kb");
1800
- return res.status(400).send({ success: false, message: "Content not found" })
1842
+ } catch (err) {
1843
+ winston.error("Error updating content: ", err);
1844
+ return res.status(500).send({ success: false, error: err });
1845
+ }
1846
+
1847
+ let new_content = {
1848
+ id_project,
1849
+ name,
1850
+ type,
1851
+ source,
1852
+ content,
1853
+ namespace: namespace_id,
1854
+ status: -1,
1855
+ }
1856
+
1857
+ if (new_content.type === 'url') {
1858
+ new_content.refresh_rate = refresh_rate || 'never';
1859
+ if (!scrape_type || scrape_type === 2) {
1860
+ new_content.scrape_type = 2;
1861
+ new_content.scrape_options = aiManager.setDefaultScrapeOptions();
1862
+ } else {
1863
+ new_content.scrape_type = scrape_type;
1864
+ new_content.scrape_options = scrape_options;
1801
1865
  }
1866
+ }
1802
1867
 
1803
- res.status(200).send(savedKb)
1804
- })
1868
+ if (kb.sitemap_origin_id) {
1869
+ new_content.sitemap_origin_id = kb.sitemap_origin_id;
1870
+ new_content.sitemap_origin = kb.sitemap_origin;
1871
+ }
1872
+
1873
+ if (tags && Array.isArray(tags) && tags.every(tag => typeof tag === "string")) {
1874
+ new_content.tags = tags;
1875
+ }
1876
+
1877
+ winston.debug("Update content. New content: ", new_content);
1878
+
1879
+ let updated_content;
1880
+ try {
1881
+ updated_content = await KB.findOneAndUpdate({ id_project, namespace: namespace_id, source }, new_content, { upsert: true, new: true }).lean().exec();
1882
+ } catch (err) {
1883
+ winston.error("Error updating content: ", err);
1884
+ return res.status(500).send({ success: false, error: err });
1885
+ }
1886
+
1887
+ const embedding = normalizeEmbedding(namespace.embedding);
1888
+ embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
1889
+ let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
1890
+
1891
+ const json = {
1892
+ id: updated_content._id,
1893
+ type: updated_content.type,
1894
+ source: updated_content.source,
1895
+ content: updated_content.content || "",
1896
+ namespace: updated_content.namespace,
1897
+ webhook: webhook,
1898
+ hybrid: namespace.hybrid,
1899
+ engine: namespace.engine || default_engine,
1900
+ embedding: embedding,
1901
+ ...(updated_content.scrape_type && { scrape_type: updated_content.scrape_type }),
1902
+ ...(updated_content.scrape_options && { parameters_scrape_type_4: updated_content.scrape_options }),
1903
+ ...(updated_content.tags && { tags: updated_content.tags }),
1904
+ }
1905
+
1906
+ winston.debug("json: ", json);
1907
+
1908
+ if (process.env.NODE_ENV === 'test') {
1909
+ return res.status(200).send({ success: true, message: "Schedule scrape skipped in test environment", data: updated_content, schedule_json: json });
1910
+ }
1911
+
1912
+ aiManager.scheduleScrape([json], namespace.hybrid);
1913
+ return res.status(200).send(updated_content);
1805
1914
 
1806
1915
  })
1807
1916
 
1917
+ // router.put('/:kb_id', async (req, res) => {
1918
+
1919
+ // let kb_id = req.params.kb_id;
1920
+ // winston.verbose("update kb_id " + kb_id);
1921
+
1922
+ // let update = {};
1923
+
1924
+ // if (req.body.name != undefined) {
1925
+ // update.name = req.body.name;
1926
+ // }
1927
+
1928
+ // if (req.body.status != undefined) {
1929
+ // update.status = req.body.status;
1930
+ // }
1931
+
1932
+ // winston.debug("kb update: ", update);
1933
+
1934
+ // KB.findByIdAndUpdate(kb_id, update, { new: true }, (err, savedKb) => {
1935
+
1936
+ // if (err) {
1937
+ // winston.error("KB findByIdAndUpdate error: ", err);
1938
+ // return res.status(500).send({ success: false, error: err });
1939
+ // }
1940
+
1941
+ // if (!savedKb) {
1942
+ // winston.debug("Try to updating a non-existing kb");
1943
+ // return res.status(400).send({ success: false, message: "Content not found" })
1944
+ // }
1945
+
1946
+ // res.status(200).send(savedKb)
1947
+ // })
1948
+
1949
+ // })
1950
+
1808
1951
  router.delete('/:kb_id', async (req, res) => {
1809
1952
 
1810
1953
  let project_id = req.projectid;
package/routes/request.js CHANGED
@@ -318,6 +318,10 @@ router.patch('/:requestid', function (req, res) {
318
318
  return res.status(404).send({ success: false, msg: 'Request not found' });
319
319
  }
320
320
 
321
+ if (update.workingStatus !== undefined) {
322
+ requestEvent.emit('request.workingStatus.update', { request });
323
+ }
324
+
321
325
  requestEvent.emit("request.update", request);
322
326
  requestEvent.emit("request.update.comment", { comment: "PATCH", request: request }); //Deprecated
323
327
  requestEvent.emit("request.updated", { comment: "PATCH", request: request, patch: update });
@@ -1376,6 +1380,10 @@ router.get('/', function (req, res, next) {
1376
1380
  }
1377
1381
  }
1378
1382
 
1383
+ if (req.query.workingStatus?.ne) {
1384
+ query.workingStatus = { $ne: req.query.workingStatus.ne };
1385
+ }
1386
+
1379
1387
  if (req.query.priority) {
1380
1388
  query.priority = req.query.priority;
1381
1389
  }
@@ -1421,10 +1429,31 @@ router.get('/', function (req, res, next) {
1421
1429
  query["attributes.fully_abandoned"] = true
1422
1430
  }
1423
1431
 
1432
+ if (req.query.rated && (req.query.rated === true || req.query.rated === 'true')) {
1433
+ query.rating = { $exists: true }
1434
+ }
1435
+
1424
1436
  if (req.query.draft && (req.query.draft === 'false' || req.query.draft === false)) {
1425
1437
  query.draft = { $in: [false, null] }
1426
1438
  }
1427
1439
 
1440
+ let inWStatus = req.query.workingStatus?.in?.split(',').map(s => s.trim()).filter(Boolean);
1441
+ let ninWStatus = req.query.workingStatus?.nin?.split(',').map(s => s.trim()).filter(Boolean);
1442
+
1443
+ if (ninWStatus && ninWStatus.length > 0) {
1444
+ if (ninWStatus.length === 1) {
1445
+ query.workingStatus = { $ne: ninWStatus[0] };
1446
+ } else {
1447
+ query.workingStatus = { $nin: ninWStatus };
1448
+ }
1449
+ } else if (inWStatus && inWStatus.length > 0) {
1450
+ if (inWStatus.length === 1) {
1451
+ query.workingStatus = inWStatus[0];
1452
+ } else {
1453
+ query.workingStatus = { $in: inWStatus };
1454
+ }
1455
+ }
1456
+
1428
1457
  var projection = undefined;
1429
1458
 
1430
1459
  if (req.query.full_text) {
@@ -1872,57 +1901,67 @@ router.get('/csv', function (req, res, next) {
1872
1901
 
1873
1902
  var limit = 100000; // Number of request per page
1874
1903
  var page = 0;
1904
+ var skip = 0;
1905
+ let statusArray = [];
1906
+ var projectuser = req.projectuser;
1907
+ let filterRangeField = req.query.filterRangeField || 'createdAt';
1875
1908
 
1876
1909
  if (req.query.page) {
1877
1910
  page = req.query.page;
1878
1911
  }
1879
1912
 
1880
- var skip = page * limit;
1913
+ skip = page * limit;
1881
1914
  winston.debug('REQUEST ROUTE - SKIP PAGE ', skip);
1882
1915
 
1883
- let statusArray = [];
1916
+ // Default query (same as GET /)
1917
+ var query = { "id_project": req.projectid, "status": { $lt: 1000, $nin: [50, 150] }, preflight: false };
1884
1918
 
1885
- var query = { "id_project": req.projectid };
1919
+ if (req.user instanceof Subscription) {
1920
+ // All request
1921
+ } else if (projectuser && (projectuser.role == "owner" || projectuser.role == "admin")) {
1922
+ if (req.query.mine) {
1923
+ query["$or"] = [{ "snapshot.agents.id_user": req.user.id }, { "participants": req.user.id }];
1924
+ }
1925
+ } else {
1926
+ query["$or"] = [{ "snapshot.agents.id_user": req.user.id }, { "participants": req.user.id }];
1927
+ }
1886
1928
 
1887
1929
  if (req.query.dept_id) {
1888
1930
  query.department = req.query.dept_id;
1889
1931
  winston.debug('REQUEST ROUTE - QUERY DEPT ID', query.department);
1890
1932
  }
1891
1933
 
1934
+ if (req.query.requester_email) {
1935
+ query["snapshot.lead.email"] = req.query.requester_email;
1936
+ }
1937
+
1892
1938
  if (req.query.full_text) {
1893
1939
  winston.debug('req.query.fulltext', req.query.full_text);
1894
1940
  query.$text = { "$search": req.query.full_text };
1895
1941
  }
1896
1942
 
1897
- if (req.query.status) {
1943
+ if (req.query.phone) {
1944
+ var phoneDigits = req.query.phone.replace(/\D/g, '');
1945
+ if (phoneDigits.length > 0) {
1946
+ query["contact.phone"] = new RegExp(phoneDigits);
1947
+ }
1948
+ }
1949
+
1950
+ var history_search = false;
1898
1951
 
1952
+ // Multiple status management (same as GET /)
1953
+ if (req.query.status) {
1899
1954
  if (req.query.status === 'all') {
1900
1955
  delete query.status;
1901
1956
  } else {
1902
- let statusArray = req.query.status.split(',').map(Number);
1903
- statusArray = statusArray.map(status => { return isNaN(status) ? null : status }).filter(status => status !== null)
1957
+ statusArray = req.query.status.split(',').map(Number);
1958
+ statusArray = statusArray.map(status => { return isNaN(status) ? null : status }).filter(status => status !== null);
1904
1959
  if (statusArray.length > 0) {
1905
- query.status = {
1906
- $in: statusArray
1907
- }
1960
+ query.status = { $in: statusArray };
1908
1961
  } else {
1909
1962
  delete query.status;
1910
1963
  }
1911
1964
  }
1912
-
1913
- if (statusArray.length > 0) {
1914
- query.status = {
1915
- $in: statusArray
1916
- }
1917
- }
1918
-
1919
- }
1920
-
1921
- if (req.query.preflight) {
1922
- let preflight = (req.query.preflight === 'false');
1923
- if (preflight) {
1924
- query.preflight = false;
1925
- }
1926
1965
  }
1927
1966
 
1928
1967
  if (req.query.lead) {
@@ -1942,49 +1981,96 @@ router.get('/csv', function (req, res, next) {
1942
1981
  query.hasBot = req.query.hasbot;
1943
1982
  }
1944
1983
 
1945
- /**
1946
- * DATE RANGE */
1947
- if (req.query.start_date && req.query.end_date) {
1948
- winston.debug('REQUEST ROUTE - REQ QUERY start_date ', req.query.start_date);
1949
- winston.debug('REQUEST ROUTE - REQ QUERY end_date ', req.query.end_date);
1984
+ if (req.query.tags) {
1985
+ query["tags.tag"] = req.query.tags;
1986
+ }
1987
+
1988
+ if (req.query.location) {
1989
+ query.location = req.query.location;
1990
+ }
1991
+
1992
+ if (req.query.ticket_id) {
1993
+ query.ticket_id = req.query.ticket_id;
1994
+ }
1995
+
1996
+ if (req.query.preflight && (req.query.preflight === 'true' || req.query.preflight === true)) {
1997
+ delete query.preflight;
1998
+ }
1999
+
2000
+ let timezone = req.query.timezone || 'Europe/Rome';
2001
+ let queryDateRange = false;
2002
+ let queryStartDate;
2003
+ let queryEndDate;
2004
+
2005
+ if (history_search === true && req.project && req.project.profile && ((req.project.profile.type === 'free' && req.project.trialExpired === true) || (req.project.profile.type === 'payment' && req.project.isActiveSubscription === false))) {
2006
+ queryDateRange = true;
2007
+ queryStartDate = moment().subtract(14, "days").format("YYYY/MM/DD");
2008
+ queryEndDate = null;
2009
+ }
1950
2010
 
1951
- /**
1952
- * USING TIMESTAMP in MS */
1953
- // var formattedStartDate = new Date(+req.query.start_date);
1954
- // var formattedEndDate = new Date(+req.query.end_date);
1955
- // query.createdAt = { $gte: formattedStartDate, $lte: formattedEndDate }
2011
+ if (req.query.start_date || req.query.end_date) {
2012
+ queryDateRange = true;
2013
+ queryStartDate = req.query.start_date;
2014
+ queryEndDate = req.query.end_date;
2015
+ } else if (req.query.start_date_time || req.query.end_date_time) {
2016
+ queryDateRange = true;
2017
+ queryStartDate = req.query.start_date_time;
2018
+ queryEndDate = req.query.end_date_time;
2019
+ }
1956
2020
 
2021
+ if (queryDateRange) {
2022
+ try {
2023
+ let rangeQuery = datesUtil.createDateRangeQuery(queryStartDate, queryEndDate, timezone, filterRangeField);
2024
+ Object.assign(query, rangeQuery);
2025
+ } catch (error) {
2026
+ winston.error('Error creating date range query: ', error);
2027
+ return res.status(500).send({ success: false, error: error?.message });
2028
+ }
2029
+ }
1957
2030
 
1958
- /**
1959
- * USING MOMENT */
1960
- var startDate = moment(req.query.start_date, 'DD/MM/YYYY').format('YYYY-MM-DD');
1961
- var endDate = moment(req.query.end_date, 'DD/MM/YYYY').format('YYYY-MM-DD');
2031
+ if (req.query.snap_department_routing) {
2032
+ query["snapshot.department.routing"] = req.query.snap_department_routing;
2033
+ }
1962
2034
 
1963
- winston.debug('REQUEST ROUTE - REQ QUERY FORMATTED START DATE ', startDate);
1964
- winston.debug('REQUEST ROUTE - REQ QUERY FORMATTED END DATE ', endDate);
2035
+ if (req.query.snap_department_default) {
2036
+ query["snapshot.department.default"] = req.query.snap_department_default;
2037
+ }
2038
+
2039
+ if (req.query.snap_department_id_bot) {
2040
+ query["snapshot.department.id_bot"] = req.query.snap_department_id_bot;
2041
+ }
1965
2042
 
1966
- // ADD ONE DAY TO THE END DAY
1967
- var date = new Date(endDate);
1968
- var newdate = new Date(date);
1969
- var endDate_plusOneDay = newdate.setDate(newdate.getDate() + 1);
1970
- winston.debug('REQUEST ROUTE - REQ QUERY FORMATTED END DATE + 1 DAY ', endDate_plusOneDay);
1971
- // var endDate_plusOneDay = moment('2018-09-03').add(1, 'd')
1972
- // var endDate_plusOneDay = endDate.add(1).day();
1973
- // var toDate = new Date(Date.parse(endDate_plusOneDay)).toISOString()
2043
+ if (req.query.snap_department_id_bot_exists) {
2044
+ query["snapshot.department.id_bot"] = { "$exists": req.query.snap_department_id_bot_exists };
2045
+ }
1974
2046
 
1975
- query.createdAt = { $gte: new Date(Date.parse(startDate)).toISOString(), $lte: new Date(endDate_plusOneDay).toISOString() }
1976
- winston.debug('REQUEST ROUTE - QUERY CREATED AT ', query.createdAt);
2047
+ if (req.query.snap_lead_lead_id) {
2048
+ query["snapshot.lead.lead_id"] = req.query.snap_lead_lead_id;
2049
+ }
1977
2050
 
1978
- } else if (req.query.start_date && !req.query.end_date) {
1979
- winston.debug('REQUEST ROUTE - REQ QUERY END DATE IS EMPTY (so search only for start date)');
1980
- var startDate = moment(req.query.start_date, 'DD/MM/YYYY').format('YYYY-MM-DD');
2051
+ if (req.query.snap_lead_email) {
2052
+ query["snapshot.lead.email"] = req.query.snap_lead_email;
2053
+ }
1981
2054
 
1982
- query.createdAt = { $gte: new Date(Date.parse(startDate)).toISOString() };
1983
- winston.debug('REQUEST ROUTE - QUERY CREATED AT (only for start date)', query.createdAt);
2055
+ if (req.query.smartAssignment) {
2056
+ query.smartAssignment = req.query.smartAssignment;
1984
2057
  }
1985
- winston.debug("csv query", query);
1986
2058
 
1987
- var direction = 1; //-1 descending , 1 ascending
2059
+ if (req.query.channel) {
2060
+ if (req.query.channel === "offline") {
2061
+ query["channel.name"] = { "$in": ["email", "form"] };
2062
+ } else if (req.query.channel === "online") {
2063
+ query["channel.name"] = { "$nin": ["email", "form"] };
2064
+ } else {
2065
+ query["channel.name"] = req.query.channel;
2066
+ }
2067
+ }
2068
+
2069
+ if (req.query.priority) {
2070
+ query.priority = req.query.priority;
2071
+ }
2072
+
2073
+ var direction = -1; // same default as GET /
1988
2074
  if (req.query.direction) {
1989
2075
  direction = req.query.direction;
1990
2076
  }
@@ -1998,20 +2084,9 @@ router.get('/csv', function (req, res, next) {
1998
2084
 
1999
2085
  var sortQuery = {};
2000
2086
  sortQuery[sortField] = direction;
2001
-
2002
2087
  winston.debug("sort query", sortQuery);
2003
2088
 
2004
- if (req.query.channel) {
2005
- if (req.query.channel === "offline") {
2006
- query["channel.name"] = { "$in": ["email", "form"] }
2007
- } else if (req.query.channel === "online") {
2008
- query["channel.name"] = { "$nin": ["email", "form"] }
2009
- } else {
2010
- query["channel.name"] = req.query.channel
2011
- }
2012
- }
2013
-
2014
- // VOICE FILTERS - Start
2089
+ // VOICE FILTERS
2015
2090
  if (req.query.caller) {
2016
2091
  query["attributes.caller_phone"] = req.query.caller;
2017
2092
  }
@@ -2021,45 +2096,52 @@ router.get('/csv', function (req, res, next) {
2021
2096
  if (req.query.call_id) {
2022
2097
  query["attributes.call_id"] = req.query.call_id;
2023
2098
  }
2024
- // VOICE FILTERS - End
2025
-
2026
- // TODO ORDER BY SCORE
2027
- // return Faq.find(query, {score: { $meta: "textScore" } })
2028
- // .sort( { score: { $meta: "textScore" } } ) //https://docs.mongodb.com/manual/reference/operator/query/text/#sort-by-text-search-score
2029
-
2030
- // aggiungi filtro per data marco
2031
2099
 
2032
2100
  if (req.query.duration && req.query.duration_op) {
2033
2101
  let duration = Number(req.query.duration) * 60 * 1000;
2034
2102
  if (req.query.duration_op === 'gt') {
2035
- query.duration = { $gte: duration }
2103
+ query.duration = { $gte: duration };
2036
2104
  } else if (req.query.duration_op === 'lt') {
2037
- query.duration = { $lte: duration }
2105
+ query.duration = { $lte: duration };
2038
2106
  } else {
2039
- winston.verbose("Duration operator can be 'gt' or 'lt'. Skip duration_op " + req.query.duration_op)
2107
+ winston.verbose("Duration operator can be 'gt' or 'lt'. Skip duration_op " + req.query.duration_op);
2040
2108
  }
2041
2109
  }
2042
2110
 
2111
+ if (req.query.abandonded && (req.query.abandoned === true || req.query.abandoned === 'true')) {
2112
+ query["attributes.fully_abandoned"] = true;
2113
+ }
2114
+
2115
+ if (req.query.rated && (req.query.rated === true || req.query.rated === 'true')) {
2116
+ query.rating = { $exists: true };
2117
+ }
2118
+
2043
2119
  if (req.query.draft && (req.query.draft === 'false' || req.query.draft === false)) {
2044
- query.draft = { $in: [false, null] }
2120
+ query.draft = { $in: [false, null] };
2045
2121
  }
2046
2122
 
2047
- winston.debug('REQUEST ROUTE - REQUEST FIND ', query)
2048
- return Request.find(query, '-transcript -status -__v').
2123
+ var csvProjection = '-transcript -status -__v';
2124
+ if (req.query.full_text && req.query.no_textscore != "true" && req.query.no_textscore != true) {
2125
+ winston.verbose('fulltext projection on');
2126
+ csvProjection = { transcript: 0, status: 0, __v: 0, score: { $meta: "textScore" } };
2127
+ }
2128
+
2129
+ winston.debug("csv query", query);
2130
+ winston.debug('REQUEST ROUTE - REQUEST FIND ', query);
2131
+
2132
+ var q = Request.find(query, csvProjection).
2049
2133
  skip(skip).limit(limit).
2050
- //populate('department', {'_id':-1, 'name':1}).
2051
2134
  populate('department').
2052
2135
  populate('lead').
2053
- // populate('participatingBots').
2054
- // populate('participatingAgents').
2055
- lean().
2056
- // populate({
2057
- // path: 'department',
2058
- // //select: { '_id': -1,'name':1}
2059
- // select: {'name':1}
2060
- // }).
2061
- sort(sortQuery).
2062
- exec(function (err, requests) {
2136
+ lean();
2137
+
2138
+ if (req.query.full_text && req.query.no_textscore != "true" && req.query.no_textscore != true) {
2139
+ q.sort({ score: { $meta: "textScore" } });
2140
+ } else {
2141
+ q.sort(sortQuery);
2142
+ }
2143
+
2144
+ return q.exec(function (err, requests) {
2063
2145
  if (err) {
2064
2146
  winston.error('REQUEST ROUTE - REQUEST FIND ERR ', err)
2065
2147
  return res.status(500).send({ success: false, msg: 'Error getting csv requests.', err: err });
@@ -5,6 +5,7 @@ const { Scheduler } = require("./Scheduler");
5
5
  const { default: Sitemapper } = require('sitemapper');
6
6
  const winston = require('../config/winston');
7
7
  const configGlobal = require('../config/global');
8
+ const _ = require('lodash');
8
9
  const JobManager = require('../utils/jobs-worker-queue-manager/JobManagerV2');
9
10
 
10
11
  // Constants
@@ -142,6 +143,73 @@ class AiManager {
142
143
  })
143
144
  }
144
145
 
146
+ async updateSitemap(id_project, namespace, old_sitemap, new_sitemap) {
147
+
148
+ let fieldsToCheck = ['scrape_type', 'scrape_options', 'refresh_rate', 'tags'];
149
+ let { stop } = this.shouldStop(old_sitemap, new_sitemap, fieldsToCheck);
150
+
151
+ let updated_sitemap;
152
+ try {
153
+ updated_sitemap = await KB.findOneAndUpdate({ id_project, namespace: namespace.id, _id: old_sitemap._id }, new_sitemap, { new: true });
154
+ } catch (err) {
155
+ winston.error("Error updating sitemap: ", err);
156
+ throw err;
157
+ }
158
+
159
+ if (stop) {
160
+ return;
161
+ }
162
+
163
+ // Find all url contents with sitemap_origin_id
164
+ let urlContents = await KB.find({ id_project, namespace: namespace.id, sitemap_origin_id: old_sitemap._id }).lean().exec();
165
+ if (urlContents.length === 0) {
166
+ winston.error("No url contents found with sitemap_origin_id: ", old_sitemap._id);
167
+ throw new Error("No url contents found with sitemap_origin_id: " + old_sitemap._id);
168
+ }
169
+
170
+ // Remove all url contents found with sitemap_origin_id
171
+ try {
172
+ await this.removeMultipleContents(namespace, urlContents);
173
+ } catch (err) {
174
+ winston.error("Error removing multiple contents: ", err);
175
+ throw err;
176
+ }
177
+
178
+ // Recreate all url contents with sitemap_origin_id
179
+ let result;
180
+ try {
181
+ result = await this.addMultipleUrls(namespace, urlContents.map(urlContent => urlContent.source), {
182
+ sitemap_origin_id: updated_sitemap._id,
183
+ sitemap_origin: updated_sitemap.source,
184
+ scrape_type: updated_sitemap.scrape_type,
185
+ scrape_options: updated_sitemap.scrape_options,
186
+ refresh_rate: updated_sitemap.refresh_rate,
187
+ tags: updated_sitemap.tags,
188
+ });
189
+ } catch (err) {
190
+ winston.error("Error recreating multiple contents: ", err);
191
+ throw err;
192
+ }
193
+
194
+ return result;
195
+
196
+ }
197
+
198
+
199
+
200
+ shouldStop(oldObj, newObj, fieldsToCheck) {
201
+ for (const field of fieldsToCheck) {
202
+ const oldValue = _.get(oldObj, field);
203
+ const newValue = _.get(newObj, field);
204
+
205
+ if (!_.isEqual(oldValue, newValue)) {
206
+ return { stop: false };
207
+ }
208
+ }
209
+
210
+ return { stop: true };
211
+ }
212
+
145
213
  async checkNamespace(id_project, namespace_id) {
146
214
  return new Promise( async (resolve, reject) => {
147
215