Compare commits

...

15 Commits

Author SHA1 Message Date
Quan HL
6c4c5b6ba8 feat: lcr 2023-04-24 21:50:02 +07:00
Quan HL
381eee9bcb feat: lcr 2023-04-24 21:48:59 +07:00
Quan HL
7dc6b782f5 feat: lcr 2023-04-24 21:45:21 +07:00
Quan HL
18d2dfa053 feat: lcr 2023-04-24 21:41:48 +07:00
Quan HL
2e3d783dee feat: lcr 2023-04-24 21:40:19 +07:00
Dave Horton
6dc019e836 fix: amd support for language other than en-US (#322) 2023-04-19 15:12:51 -04:00
Dave Horton
a22bc8ea42 fix issue where multiple gathers running simultaneously (#321) 2023-04-18 21:58:30 -04:00
Hoan Luu Huu
0356b996ba fix: wss requestor incase mysql cache is used (#319) 2023-04-18 06:34:39 -04:00
Dave Horton
271587617e update verb specifications 2023-04-13 13:28:21 -04:00
Dave Horton
0b29e67a0c better logging of ws commands 2023-04-13 13:26:52 -04:00
EgleH
e656d275fe update mase image to node:18.15-alpine3.16 (#316) 2023-04-12 16:18:54 -04:00
Hoan Luu Huu
fabf01f8b5 feat: callerName to rest_dial and dial verb (#312)
* feat: callerName to rest_dial and dial verb

* update verb specification
2023-04-12 10:04:55 -04:00
Dave Horton
85ab75d8e3 cherry-pick commit from pr 313 2023-04-12 08:15:32 -04:00
Dave Horton
5c2630fe1f webapp-scaffold can not reference code in higher level app (#314) 2023-04-12 07:54:21 -04:00
Dave Horton
9942313ea1 update drachtio-fsmrf to fix bug in prev commit 2023-04-11 22:55:19 -04:00
14 changed files with 94 additions and 41 deletions

View File

@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 node:18.14.1-alpine3.16 as base
FROM --platform=linux/amd64 node:18.15-alpine3.16 as base
RUN apk --update --no-cache add --virtual .builds-deps build-base python3

View File

@@ -5,14 +5,11 @@
"at the tone",
"leave a message",
"leave me a message",
"not available right now",
"not available to take your call",
"not available",
"can't take your call",
"I will get back to you",
"will get back to you",
"I'll get back to you",
"we will get back to you",
"we are unable",
"we are not available"
"we are unable"
],
"es-ES": [
"le pasamos la llamada",
@@ -48,5 +45,18 @@
"ens posarem en contacto",
"ara no estem disponibles",
"no hi som"
],
"de-DE": [
"nicht erreichbar",
"nnruf wurde weitergeleitet",
"beim piepsen",
"am ton",
"eine nachricht hinterlassen",
"hinterlasse mir eine Nachricht",
"nicht verfügbar",
"kann ihren anruf nicht entgegennehmen",
"wird sich bei Ihnen melden",
"ich melde mich bei dir",
"wir können nicht"
]
}

View File

@@ -27,6 +27,7 @@ router.post('/', async(req, res) => {
const target = restDial.to;
const opts = {
callingNumber: restDial.from,
...(restDial.callerName && {callingName: restDial.callerName}),
headers: req.body.headers || {}
};
@@ -90,8 +91,13 @@ router.post('/', async(req, res) => {
* check if from-number matches any existing numbers on Jambonz
* */
if (target.type === 'phone' && !target.trunk) {
const {lookupCarrierByPhoneNumber} = dbUtils(this.logger, srf);
const voip_carrier_sid = await lookupCarrierByPhoneNumber(req.body.account_sid, restDial.from);
const {lookupCarrierByPhoneNumber, lookupCarrierByLcr} = dbUtils(this.logger, srf);
// firstly LCR
let voip_carrier_sid = await lookupCarrierByLcr(req.body.account_sid, to);
if (!voip_carrier_sid) {
// later by phone
voip_carrier_sid = await lookupCarrierByPhoneNumber(req.body.account_sid, restDial.from);
}
logger.info(
`createCall: selected ${voip_carrier_sid} for requested phone number: ${restDial.from || 'unspecified'})`);
if (voip_carrier_sid) {

View File

@@ -248,8 +248,9 @@ module.exports = function(srf, logger) {
const app2 = JAMBONES_MYSQL_REFRESH_TTL ? JSON.parse(JSON.stringify(app)) : app;
if ('WS' === app.call_hook?.method ||
app.call_hook?.url.startsWith('ws://') || app.call_hook?.url.startsWith('wss://')) {
app2.requestor = new WsRequestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret) ;
app2.notifier = app.requestor;
const requestor = new WsRequestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret) ;
app2.requestor = requestor;
app2.notifier = requestor;
app2.call_hook.method = 'WS';
}
else {

View File

@@ -529,7 +529,7 @@ class CallSession extends Emitter {
this.logger.info({currInput, newInput},
'CallSession:enableBotMode - restarting background gather to apply new input type');
this.backgroundGatherTask.sticky = false;
this.disableBotMode();
await this.disableBotMode();
}
}
this.backgroundGatherTask = task;
@@ -568,12 +568,12 @@ class CallSession extends Emitter {
this.logger.info({err, gather}, 'CallSession:enableBotMode - Error creating gather task');
}
}
disableBotMode() {
async disableBotMode() {
this._bargeInEnabled = false;
if (this.backgroundGatherTask) {
try {
this.backgroundGatherTask.removeAllListeners();
this.backgroundGatherTask.kill().catch((err) => {});
await this.backgroundGatherTask.kill();
} catch (err) {}
this.backgroundGatherTask = null;
}
@@ -724,7 +724,7 @@ class CallSession extends Emitter {
}
else {
this.logger.info('CallSession:exec disabling bot mode to start gather with new options');
this.disableBotMode();
await this.disableBotMode();
}
}
if (!skip) {
@@ -1192,7 +1192,7 @@ class CallSession extends Emitter {
}
_onCommand({msgid, command, call_sid, queueCommand, data}) {
this.logger.info({msgid, command, queueCommand}, 'CallSession:_onCommand - received command');
this.logger.info({msgid, command, queueCommand, data}, 'CallSession:_onCommand - received command');
const resolution = {reason: 'received command', queue: queueCommand, command};
switch (command) {
case 'redirect':

View File

@@ -85,6 +85,7 @@ class TaskDial extends Task {
this.earlyMedia = this.data.answerOnBridge === true;
this.callerId = this.data.callerId;
this.callerName = this.data.callerName;
this.dialMusic = this.data.dialMusic;
this.headers = this.data.headers || {};
this.method = this.data.method || 'POST';
@@ -398,7 +399,7 @@ class TaskDial extends Task {
const {req, srf} = cs;
const {getSBC} = srf.locals;
const {lookupTeamsByAccount, lookupAccountBySid} = srf.locals.dbHelpers;
const {lookupCarrier, lookupCarrierByPhoneNumber} = dbUtils(this.logger, cs.srf);
const {lookupCarrier, lookupCarrierByLcr} = dbUtils(this.logger, cs.srf);
const sbcAddress = this.proxy || getSBC();
const teamsInfo = {};
let fqdn;
@@ -415,7 +416,8 @@ class TaskDial extends Task {
const opts = {
headers: this.headers,
proxy: `sip:${sbcAddress}`,
callingNumber: this.callerId || req.callingNumber
callingNumber: this.callerId || req.callingNumber,
...(this.callerName && {callingName: this.callerName})
};
const t = this.target.find((t) => t.type === 'teams');
@@ -465,10 +467,10 @@ class TaskDial extends Task {
/**
* trunk isn't specified,
* check if number matches any existing numbers
* check if number matches any LCR routes
* */
if (t.type === 'phone' && !t.trunk) {
const voip_carrier_sid = await lookupCarrierByPhoneNumber(req.body.account_sid, t.number);
const voip_carrier_sid = lookupCarrierByLcr(req.body.account_sid, t.number);
this.logger.info(
`Dial:_attemptCalls: selected ${voip_carrier_sid} for requested phone number: ${t.number})`);
if (voip_carrier_sid) {

View File

@@ -11,6 +11,7 @@ class TaskRestDial extends Task {
super(logger, opts);
this.from = this.data.from;
this.callerName = this.data.callerName;
this.fromHost = this.data.fromHost;
this.to = this.data.to;
this.call_hook = this.data.call_hook;

View File

@@ -184,7 +184,7 @@ module.exports = (logger) => {
const {vendor, language} = ep.amd;
ep.startTranscription({
vendor,
language,
locale: language,
interim: true,
bugname
}).catch((err) => {

View File

@@ -150,10 +150,41 @@ module.exports = (logger, srf) => {
}
};
const sqlQueryLcrByAccountSid = `SELECT lcr_sid FROM lcr WHERE account_sid = ? OR
service_provider_sid = (SELECT service_provider_sid from accounts where account_sid = ?)`;
const sqlQueryLcrRouteByLcrSid = 'SELECT * FROM lcr_routes WHERE lcr_sid = ? ORDER BY priority';
const sqlQueryLcrCarrierSetEntryByLcrRouteSid = `SELECT * FROM lcr_carrier_set_entry
WHERE lcr_route_sid = ? ORDER BY priority`;
const lookupCarrierByLcr = async(account_sid, toNumber) => {
const pp = pool.promise();
try {
const [lcrs] = await pp.query(sqlQueryLcrByAccountSid, [account_sid, account_sid]);
if (lcrs.length) {
const lcr_sid = lcrs[0];
const [lcr_routes] = await pp.query(sqlQueryLcrRouteByLcrSid, [lcr_sid]);
if (lcr_routes.length) {
for (const r of lcr_routes) {
var matcher = new RegExp(r.regex);
if (matcher.test(toNumber)) {
const [entries] = await pp.query(sqlQueryLcrCarrierSetEntryByLcrRouteSid, [r.lcr_route_sid]);
// Currently just support first entry;
if (entries.length) {
return entries[0].voip_carrier_sid;
}
}
}
}
}
} catch (error) {
logger.error({error}, `lookupCarrierByLcr: Error ${account_sid}:${toNumber}`);
}
};
return {
lookupAccountDetails,
updateSpeechCredentialLastUsed,
lookupCarrier,
lookupCarrierByPhoneNumber
lookupCarrierByPhoneNumber,
lookupCarrierByLcr
};
};

28
package-lock.json generated
View File

@@ -15,7 +15,7 @@
"@jambonz/speech-utils": "^0.0.12",
"@jambonz/stats-collector": "^0.1.8",
"@jambonz/time-series": "^0.2.5",
"@jambonz/verb-specifications": "^0.0.12",
"@jambonz/verb-specifications": "^0.0.16",
"@opentelemetry/api": "^1.4.0",
"@opentelemetry/exporter-jaeger": "^1.9.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.35.0",
@@ -29,7 +29,7 @@
"bent": "^7.3.12",
"debug": "^4.3.4",
"deepcopy": "^2.1.0",
"drachtio-fsmrf": "^3.0.20",
"drachtio-fsmrf": "^3.0.21",
"drachtio-srf": "^4.5.23",
"express": "^4.18.2",
"ip": "^1.1.8",
@@ -1664,9 +1664,9 @@
}
},
"node_modules/@jambonz/verb-specifications": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.12.tgz",
"integrity": "sha512-NU+9iVCqyn8GI/QG1gKhBb9h9poEIAxfydFdDOpXOXcT4nlwuT/RLM+6X2wJcysqcc2hA/4aPhk0lMnw0H6ejg==",
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.16.tgz",
"integrity": "sha512-2ZHpbBmqhRT9a4Cs8HEQncvdp1zS7MbzLsFynVu3gSpvgfeepRtqWX8rxG9VF9Mr+AbJoeCfo+MO0PyJCPWTMA==",
"dependencies": {
"debug": "^4.3.4",
"pino": "^8.8.0"
@@ -3220,9 +3220,9 @@
}
},
"node_modules/drachtio-fsmrf": {
"version": "3.0.20",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.20.tgz",
"integrity": "sha512-ksOe5XiR0LjASHiEIOUT7YMbowKdlB1eFoM+fmSkrBi5ogcbM7RNYg6ugt+WrSc01DMbi/5BN2QhefwdzelaEQ==",
"version": "3.0.21",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.21.tgz",
"integrity": "sha512-j5C1KFFEr+N8Qqp5ZjfqQTox+nDKoII3eJOaRMj0zHmFzr7tFzHbwPf8JSQS9n0OjrY6iM1zJJxHO+zDp8974g==",
"dependencies": {
"camel-case": "^4.1.2",
"debug": "^2.6.9",
@@ -10011,9 +10011,9 @@
}
},
"@jambonz/verb-specifications": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.12.tgz",
"integrity": "sha512-NU+9iVCqyn8GI/QG1gKhBb9h9poEIAxfydFdDOpXOXcT4nlwuT/RLM+6X2wJcysqcc2hA/4aPhk0lMnw0H6ejg==",
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.16.tgz",
"integrity": "sha512-2ZHpbBmqhRT9a4Cs8HEQncvdp1zS7MbzLsFynVu3gSpvgfeepRtqWX8rxG9VF9Mr+AbJoeCfo+MO0PyJCPWTMA==",
"requires": {
"debug": "^4.3.4",
"pino": "^8.8.0"
@@ -11213,9 +11213,9 @@
}
},
"drachtio-fsmrf": {
"version": "3.0.20",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.20.tgz",
"integrity": "sha512-ksOe5XiR0LjASHiEIOUT7YMbowKdlB1eFoM+fmSkrBi5ogcbM7RNYg6ugt+WrSc01DMbi/5BN2QhefwdzelaEQ==",
"version": "3.0.21",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.21.tgz",
"integrity": "sha512-j5C1KFFEr+N8Qqp5ZjfqQTox+nDKoII3eJOaRMj0zHmFzr7tFzHbwPf8JSQS9n0OjrY6iM1zJJxHO+zDp8974g==",
"requires": {
"camel-case": "^4.1.2",
"debug": "^2.6.9",

View File

@@ -31,7 +31,7 @@
"@jambonz/speech-utils": "^0.0.12",
"@jambonz/stats-collector": "^0.1.8",
"@jambonz/time-series": "^0.2.5",
"@jambonz/verb-specifications": "^0.0.12",
"@jambonz/verb-specifications": "^0.0.16",
"@opentelemetry/api": "^1.4.0",
"@opentelemetry/exporter-jaeger": "^1.9.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.35.0",
@@ -45,7 +45,7 @@
"bent": "^7.3.12",
"debug": "^4.3.4",
"deepcopy": "^2.1.0",
"drachtio-fsmrf": "^3.0.20",
"drachtio-fsmrf": "^3.0.21",
"drachtio-srf": "^4.5.23",
"express": "^4.18.2",
"ip": "^1.1.8",

View File

@@ -31,6 +31,7 @@ test('\'dial-phone\'', async(t) => {
{
"verb": "dial",
"callerId": from,
"callerName": "test_callerName",
"actionHook": "/actionHook",
"timeLimit": 5,
"target": [
@@ -56,6 +57,7 @@ test('\'dial-phone\'', async(t) => {
"method": "POST",
},
"from": from,
"callerName": "Tom",
"to": {
"type": "phone",
"number": "15583084808"

View File

@@ -1,8 +1,7 @@
const express = require('express');
const app = express();
const Websocket = require('ws');
const {PORT} = require('../../lib/config');
const listenPort = PORT;
const listenPort = process.env.HTTP_PORT || 3000;
let json_mapping = new Map();
let hook_mapping = new Map();
let ws_packet_count = new Map();

View File

@@ -12,6 +12,7 @@ const {
OTEL_EXPORTER_JAEGER_AGENT_HOST,
OTEL_EXPORTER_JAEGER_ENDPOINT,
OTEL_EXPORTER_ZIPKIN_URL,
OTEL_EXPORTER_COLLECTOR_URL
} = require('./lib/config');
module.exports = (serviceName) => {
@@ -33,7 +34,7 @@ module.exports = (serviceName) => {
}
else {
exporter = new OTLPTraceExporter({
url: process.OTEL_EXPORTER_COLLECTOR_URL
url: OTEL_EXPORTER_COLLECTOR_URL
});
}