@tiledesk/tiledesk-server 2.15.4 → 2.15.6
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 +7 -0
- package/app.js +1 -1
- package/package.json +3 -3
- package/pubmodules/emailNotification/requestNotification.js +42 -1
- package/pubmodules/pubModulesManager.js +4 -0
- package/pubmodules/voice/listener.js +8 -1
- package/pubmodules/voice-twilio/listener.js +42 -31
- package/utils/TdCache.js +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
🚀 IN PRODUCTION 🚀
|
|
6
6
|
(https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
|
|
7
7
|
|
|
8
|
+
# 2.15.6
|
|
9
|
+
- Updated voice-twilio-connector to 0.3.2
|
|
10
|
+
- Updated vxml-connector to 0.1.91
|
|
11
|
+
|
|
12
|
+
# 2.15.5
|
|
13
|
+
- Fixed email flooding when smart assignment is active and there are no operators available
|
|
14
|
+
|
|
8
15
|
# 2.15.4
|
|
9
16
|
- Updated endpoint to get project users adding the query parameter "trashed" in order to obtain also the trashed users.
|
|
10
17
|
- Added endpoint /restore to restore a deleted project user
|
package/app.js
CHANGED
|
@@ -200,7 +200,7 @@ var faqBotHandler = require('./services/faqBotHandler');
|
|
|
200
200
|
faqBotHandler.listen();
|
|
201
201
|
|
|
202
202
|
var pubModulesManager = require('./pubmodules/pubModulesManager');
|
|
203
|
-
pubModulesManager.init({express:express, mongoose:mongoose, passport:passport, databaseUri:databaseUri, routes:{}, jobsManager:jobsManager});
|
|
203
|
+
pubModulesManager.init({express:express, mongoose:mongoose, passport:passport, databaseUri:databaseUri, routes:{}, jobsManager:jobsManager, tdCache:tdCache});
|
|
204
204
|
|
|
205
205
|
jobsManager.listen(); //listen after pubmodules to enabled queued *.queueEnabled events
|
|
206
206
|
|
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.
|
|
4
|
+
"version": "2.15.6",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "node ./bin/www",
|
|
7
7
|
"pretest": "mongodb-runner start",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"@tiledesk/tiledesk-sms-connector": "^0.1.13",
|
|
51
51
|
"@tiledesk/tiledesk-telegram-connector": "^0.1.14",
|
|
52
52
|
"@tiledesk/tiledesk-tybot-connector": "^2.0.44",
|
|
53
|
-
"@tiledesk/tiledesk-voice-twilio-connector": "^0.
|
|
54
|
-
"@tiledesk/tiledesk-vxml-connector": "^0.1.
|
|
53
|
+
"@tiledesk/tiledesk-voice-twilio-connector": "^0.3.2",
|
|
54
|
+
"@tiledesk/tiledesk-vxml-connector": "^0.1.91",
|
|
55
55
|
"@tiledesk/tiledesk-whatsapp-connector": "1.0.25",
|
|
56
56
|
"@tiledesk/tiledesk-whatsapp-jobworker": "^0.0.13",
|
|
57
57
|
"amqplib": "^0.5.5",
|
|
@@ -14,6 +14,7 @@ var winston = require('../../config/winston');
|
|
|
14
14
|
var RoleConstants = require("../../models/roleConstants");
|
|
15
15
|
var ChannelConstants = require("../../models/channelConstants");
|
|
16
16
|
var cacheUtil = require('../../utils/cacheUtil');
|
|
17
|
+
const { TdCache } = require('../../utils/TdCache');
|
|
17
18
|
|
|
18
19
|
const messageEvent = require('../../event/messageEvent');
|
|
19
20
|
var mongoose = require('mongoose');
|
|
@@ -40,6 +41,23 @@ if (pKey) {
|
|
|
40
41
|
let apiUrl = process.env.API_URL || configGlobal.apiUrl;
|
|
41
42
|
winston.debug('********* RequestNotification apiUrl: ' + apiUrl);
|
|
42
43
|
|
|
44
|
+
/** Redis cache for throttling pooled-request email. Injected by app (shared TdCache) or lazy-created fallback. */
|
|
45
|
+
let _pooledEmailTdCache = null;
|
|
46
|
+
function getPooledEmailCache() {
|
|
47
|
+
if (_pooledEmailTdCache) return _pooledEmailTdCache;
|
|
48
|
+
const fallback = new TdCache({
|
|
49
|
+
host: process.env.CACHE_REDIS_HOST,
|
|
50
|
+
port: process.env.CACHE_REDIS_PORT,
|
|
51
|
+
password: process.env.CACHE_REDIS_PASSWORD
|
|
52
|
+
});
|
|
53
|
+
fallback.connect();
|
|
54
|
+
_pooledEmailTdCache = fallback;
|
|
55
|
+
return _pooledEmailTdCache;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** TTL in seconds: do not send pooled email again for the same request within this window (avoids flooding when smart assignment retries) */
|
|
59
|
+
const POOLED_EMAIL_THROTTLE_TTL = Number(process.env.POOLED_EMAIL_THROTTLE_TTL) || 259200;
|
|
60
|
+
|
|
43
61
|
class RequestNotification {
|
|
44
62
|
|
|
45
63
|
constructor() {
|
|
@@ -48,7 +66,18 @@ class RequestNotification {
|
|
|
48
66
|
this.enabled = false;
|
|
49
67
|
}
|
|
50
68
|
winston.debug("RequestNotification this.enabled: "+ this.enabled);
|
|
51
|
-
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Reuse app's TdCache instance instead of opening a new Redis connection (optional, called by PubModulesManager).
|
|
73
|
+
* @param {TdCache} tdCache - shared Redis cache from app.get('redis_client')
|
|
74
|
+
*/
|
|
75
|
+
setTdCache(tdCache) {
|
|
76
|
+
if (tdCache && typeof tdCache.setNX === 'function') {
|
|
77
|
+
_pooledEmailTdCache = tdCache;
|
|
78
|
+
winston.debug("RequestNotification using shared TdCache for pooled email throttle");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
52
81
|
|
|
53
82
|
listen() {
|
|
54
83
|
|
|
@@ -892,6 +921,18 @@ sendAgentEmail(projectid, savedRequest) {
|
|
|
892
921
|
return winston.warn("RequestNotification savedRequest.snapshot is null :(. You are closing an old request?");
|
|
893
922
|
}
|
|
894
923
|
|
|
924
|
+
// Throttle: send pooled email only once per request within TTL (avoids flooding when smart assignment keeps retrying)
|
|
925
|
+
const requestId = (savedRequest._id || savedRequest.id).toString();
|
|
926
|
+
const pooledEmailKey = 'pooled_request_email:' + requestId;
|
|
927
|
+
try {
|
|
928
|
+
const shouldSend = await getPooledEmailCache().setNX(pooledEmailKey, '1', POOLED_EMAIL_THROTTLE_TTL);
|
|
929
|
+
if (!shouldSend) {
|
|
930
|
+
return winston.debug("RequestNotification pooled email already sent for request " + requestId + " (Redis throttle, TTL " + POOLED_EMAIL_THROTTLE_TTL + "s)");
|
|
931
|
+
}
|
|
932
|
+
} catch (redisErr) {
|
|
933
|
+
winston.warn("RequestNotification Redis setNX for pooled email throttle failed, skipping send", { error: redisErr, requestId });
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
895
936
|
|
|
896
937
|
|
|
897
938
|
var snapshotAgents = savedRequest; //riassegno varibile cosi nn cambio righe successive
|
|
@@ -175,6 +175,7 @@ class PubModulesManager {
|
|
|
175
175
|
winston.debug("PubModulesManager init");
|
|
176
176
|
|
|
177
177
|
this.jobsManager = config.jobsManager;
|
|
178
|
+
this.tdCache = config.tdCache;
|
|
178
179
|
|
|
179
180
|
try {
|
|
180
181
|
this.appRules = require('./rules/appRules');
|
|
@@ -631,6 +632,9 @@ class PubModulesManager {
|
|
|
631
632
|
// job_here
|
|
632
633
|
if (this.emailNotification) {
|
|
633
634
|
try {
|
|
635
|
+
if (this.tdCache) {
|
|
636
|
+
this.emailNotification.requestNotification.setTdCache(this.tdCache);
|
|
637
|
+
}
|
|
634
638
|
// this.emailNotification.requestNotification.listen();
|
|
635
639
|
this.jobsManager.listenEmailNotification(this.emailNotification);
|
|
636
640
|
winston.info("PubModulesManager emailNotification started.");
|
|
@@ -39,10 +39,17 @@ class Listener {
|
|
|
39
39
|
let log = process.env.VOICE_LOG || false
|
|
40
40
|
winston.debug("Voice log: "+ log);
|
|
41
41
|
|
|
42
|
+
const baseUrl = apiUrl + "/modules/voice";
|
|
43
|
+
winston.debug("Voice baseUrl: "+ baseUrl);
|
|
44
|
+
|
|
45
|
+
const relativeBaseUrl = process.env.API_ENDPOINT + "/modules/voice";
|
|
46
|
+
winston.debug("Voice relativeBaseUrl: "+ relativeBaseUrl);
|
|
47
|
+
|
|
42
48
|
voice.startApp({
|
|
43
49
|
MONGODB_URI: config.databaseUri,
|
|
44
50
|
dbconnection: dbConnection,
|
|
45
|
-
BASE_URL:
|
|
51
|
+
BASE_URL: baseUrl,
|
|
52
|
+
RELATIVE_BASE_URL: relativeBaseUrl,
|
|
46
53
|
REDIS_HOST: host,
|
|
47
54
|
REDIS_PORT: port,
|
|
48
55
|
REDIS_PASSWORD: password,
|
|
@@ -10,25 +10,21 @@ const dbConnection = mongoose.connection;
|
|
|
10
10
|
|
|
11
11
|
class Listener {
|
|
12
12
|
|
|
13
|
-
listen(config) {
|
|
14
|
-
|
|
13
|
+
async listen(config) {
|
|
15
14
|
winston.info("TwilioVoice Listener listen");
|
|
16
|
-
if (config.databaseUri) {
|
|
17
|
-
winston.debug("TwilioVoice config databaseUri: " + config.databaseUri);
|
|
18
|
-
}
|
|
19
15
|
|
|
20
16
|
var port = process.env.CACHE_REDIS_PORT || 6379;
|
|
21
|
-
winston.debug("Redis port: "+ port);
|
|
17
|
+
winston.debug("Redis port: " + port);
|
|
22
18
|
|
|
23
|
-
var host = process.env.CACHE_REDIS_HOST || "127.0.0.1"
|
|
24
|
-
winston.debug("Redis host: "+ host);
|
|
19
|
+
var host = process.env.CACHE_REDIS_HOST || "127.0.0.1";
|
|
20
|
+
winston.debug("Redis host: " + host);
|
|
25
21
|
|
|
26
22
|
var password = process.env.CACHE_REDIS_PASSWORD;
|
|
27
|
-
winston.debug("Redis password: "+ password);
|
|
23
|
+
winston.debug("Redis password: " + password);
|
|
28
24
|
|
|
29
25
|
let brand_name = null;
|
|
30
26
|
if (process.env.BRAND_NAME) {
|
|
31
|
-
brand_name = process.env.BRAND_NAME
|
|
27
|
+
brand_name = process.env.BRAND_NAME;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
let openai_endpoint = process.env.OPENAI_ENDPOINT;
|
|
@@ -36,31 +32,46 @@ class Listener {
|
|
|
36
32
|
|
|
37
33
|
let gpt_key = process.env.GPTKEY;
|
|
38
34
|
|
|
39
|
-
let
|
|
35
|
+
let elevenlabs_endpoint = process.env.ELEVENLABS_ENDPOINT || "https://api.elevenlabs.io";
|
|
36
|
+
winston.debug("ElevenLabs Endpoint: ", elevenlabs_endpoint);
|
|
37
|
+
|
|
38
|
+
const baseUrl = apiUrl + "/modules/voice-twilio";
|
|
39
|
+
winston.debug("Voice baseUrl: "+ baseUrl);
|
|
40
|
+
|
|
41
|
+
const relativeBaseUrl = process.env.API_ENDPOINT + "/modules/voice-twilio";
|
|
42
|
+
winston.debug("Voice relativeBaseUrl: "+ relativeBaseUrl);
|
|
43
|
+
|
|
44
|
+
let log = process.env.VOICE_TWILIO_LOG || 'error'
|
|
40
45
|
winston.debug("Voice log: "+ log);
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
try {
|
|
48
|
+
// startServer is async and returns a Promise (no callback)
|
|
49
|
+
await voice_twilio.init({
|
|
50
|
+
MONGODB_URI: config.databaseUri,
|
|
51
|
+
dbconnection: dbConnection,
|
|
52
|
+
BASE_URL: baseUrl,
|
|
53
|
+
RELATIVE_BASE_URL: relativeBaseUrl,
|
|
54
|
+
BASE_FILE_URL: apiUrl,
|
|
55
|
+
REDIS_HOST: host,
|
|
56
|
+
REDIS_PORT: port,
|
|
57
|
+
REDIS_PASSWORD: password,
|
|
58
|
+
BRAND_NAME: brand_name,
|
|
59
|
+
OPENAI_ENDPOINT: openai_endpoint,
|
|
60
|
+
GPT_KEY: gpt_key,
|
|
61
|
+
ELEVENLABS_ENDPOINT: elevenlabs_endpoint,
|
|
62
|
+
VOICE_TWILIO_LOG: log
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
winston.info("Tiledesk Twilio Voice Connector proxy server successfully started.");
|
|
66
|
+
|
|
67
|
+
} catch (err) {
|
|
68
|
+
winston.error("Unable to start Tiledesk Twilio Voice Connector. " + err);
|
|
69
|
+
throw err; // Re-throw if you want to handle the error upstream
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
let listener = new Listener();
|
|
65
76
|
|
|
66
|
-
module.exports = listener;
|
|
77
|
+
module.exports = listener;
|
package/utils/TdCache.js
CHANGED
|
@@ -76,7 +76,18 @@ class TdCache {
|
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Set key only if it does not exist, with TTL (seconds). Returns true if key was set, false if key already existed.
|
|
81
|
+
* Uses Redis SET key value EX ttl NX to avoid email/notification flooding.
|
|
82
|
+
*/
|
|
83
|
+
async setNX(key, value, ttlSeconds) {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
this.client.set(key, value, 'EX', ttlSeconds, 'NX', (err, reply) => {
|
|
86
|
+
if (err) return reject(err);
|
|
87
|
+
resolve(reply === 'OK');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
80
91
|
|
|
81
92
|
|
|
82
93
|
|