Compare commits

..

7 Commits

12 changed files with 71 additions and 47 deletions

View File

@@ -5,12 +5,12 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-20.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-node@v1 - uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: lts/*
- run: npm ci - run: npm ci
- run: npm run jslint - run: npm run jslint
- run: docker pull drachtio/sipp - run: docker pull drachtio/sipp

View File

@@ -20,7 +20,7 @@ jobs:
if: github.event_name == 'push' if: github.event_name == 'push'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Build image - name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME run: docker build . --file Dockerfile --tag $IMAGE_NAME

View File

@@ -41,7 +41,7 @@ function retrieveCallSession(callSid, opts) {
router.post('/:callSid', async(req, res) => { router.post('/:callSid', async(req, res) => {
const logger = req.app.locals.logger; const logger = req.app.locals.logger;
const callSid = req.params.callSid; const callSid = req.params.callSid;
logger.debug({body: req.body}, 'got upateCall request'); logger.debug({body: req.body}, 'got updateCall request');
try { try {
const cs = retrieveCallSession(callSid, req.body); const cs = retrieveCallSession(callSid, req.body);
if (!cs) { if (!cs) {

View File

@@ -225,29 +225,30 @@ module.exports = function(srf, logger) {
* create a requestor that we will use for all http requests we make during the call. * create a requestor that we will use for all http requests we make during the call.
* also create a notifier for call status events (if not needed, its a no-op). * also create a notifier for call status events (if not needed, its a no-op).
*/ */
/* allow for caching data - when caching treat retrieved data as immutable */
const app2 = process.env.JAMBONES_MYSQL_REFRESH_TTL ? JSON.parse(JSON.stringify(app)) : app;
if ('WS' === app.call_hook?.method || if ('WS' === app.call_hook?.method ||
app.call_hook?.url.startsWith('ws://') || app.call_hook?.url.startsWith('wss://')) { app.call_hook?.url.startsWith('ws://') || app.call_hook?.url.startsWith('wss://')) {
app.requestor = new WsRequestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret) ; app2.requestor = new WsRequestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret) ;
app.notifier = app.requestor; app2.notifier = app.requestor;
app.call_hook.method = 'WS'; app2.call_hook.method = 'WS';
} }
else { else {
app.requestor = new HttpRequestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret); app2.requestor = new HttpRequestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret);
if (app.call_status_hook) app.notifier = new HttpRequestor(logger, account_sid, app.call_status_hook, if (app.call_status_hook) app2.notifier = new HttpRequestor(logger, account_sid, app.call_status_hook,
accountInfo.account.webhook_secret); accountInfo.account.webhook_secret);
else app.notifier = {request: () => {}}; else app2.notifier = {request: () => {}};
} }
req.locals.application = app; req.locals.application = app2;
const obj = Object.assign({}, app);
delete obj.requestor;
delete obj.notifier;
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const {call_hook, call_status_hook, ...appInfo} = obj; // mask sensitive data like user/pass on webhook const {call_hook, call_status_hook, ...appInfo} = app; // mask sensitive data like user/pass on webhook
logger.info({app: appInfo}, `retrieved application for incoming call to ${req.locals.calledNumber}`); logger.info({app: appInfo}, `retrieved application for incoming call to ${req.locals.calledNumber}`);
req.locals.callInfo = new CallInfo({ req.locals.callInfo = new CallInfo({
req, req,
app, app: app2,
direction: CallDirection.Inbound, direction: CallDirection.Inbound,
traceId: rootSpan.traceId traceId: rootSpan.traceId
}); });

View File

@@ -83,6 +83,10 @@ class CallSession extends Emitter {
this.requestor.on('command', this._onCommand.bind(this)); this.requestor.on('command', this._onCommand.bind(this));
this.requestor.on('connection-dropped', this._onWsConnectionDropped.bind(this)); this.requestor.on('connection-dropped', this._onWsConnectionDropped.bind(this));
this.requestor.on('handover', (newRequestor) => {
this.logger.info(`handover to new base url ${newRequestor.url}`);
this.application.requestor = newRequestor;
});
} }
/** /**
@@ -588,13 +592,19 @@ class CallSession extends Emitter {
this.logger.info(`CallSession:exec starting task #${stackNum}:${taskNum}: ${task.name}`); this.logger.info(`CallSession:exec starting task #${stackNum}:${taskNum}: ${task.name}`);
try { try {
const resources = await this._evaluatePreconditions(task); const resources = await this._evaluatePreconditions(task);
let skip = false;
this.currentTask = task; this.currentTask = task;
if (TaskName.Gather === task.name && this.isBotModeEnabled) { if (TaskName.Gather === task.name && this.isBotModeEnabled) {
const timeout = task.timeout; if (this.backgroundGatherTask.updateTaskInProgress(task)) {
this.logger.info(`CallSession:exec skipping #${stackNum}:${taskNum}: ${task.name}`); this.logger.info(`CallSession:exec skipping #${stackNum}:${taskNum}: ${task.name}`);
this.backgroundGatherTask.updateTimeout(timeout); skip = true;
}
else {
this.logger.info('CallSession:exec disabling bot mode to start gather with new options');
this.disableBotMode();
}
} }
else { if (!skip) {
const {span, ctx} = this.rootSpan.startChildSpan(`verb:${task.summary}`); const {span, ctx} = this.rootSpan.startChildSpan(`verb:${task.summary}`);
task.span = span; task.span = span;
task.ctx = ctx; task.ctx = ctx;
@@ -1276,6 +1286,7 @@ class CallSession extends Emitter {
} }
this.tmpFiles.clear(); this.tmpFiles.clear();
this.requestor && this.requestor.close(); this.requestor && this.requestor.close();
this.notifier && this.notifier.close();
this.rootSpan && this.rootSpan.end(); this.rootSpan && this.rootSpan.end();
} }

View File

@@ -260,10 +260,15 @@ class TaskGather extends Task {
this._resolve('killed'); this._resolve('killed');
} }
updateTimeout(timeout) { updateTaskInProgress(opts) {
this.logger.info(`TaskGather:updateTimeout - updating timeout to ${timeout}`); if (!this.needsStt && opts.input.includes('speech')) {
this.logger.info('TaskGather:updateTaskInProgress - adding speech to a background gather');
return false; // this needs be handled by killing the background gather and starting a new one
}
const {timeout} = opts;
this.timeout = timeout; this.timeout = timeout;
this._startTimer(); this._startTimer();
return true;
} }
_onDtmf(cs, ep, evt) { _onDtmf(cs, ep, evt) {
@@ -435,8 +440,7 @@ class TaskGather extends Task {
if (0 === this.timeout) return; if (0 === this.timeout) return;
this._clearTimer(); this._clearTimer();
this._timeoutTimer = setTimeout(() => { this._timeoutTimer = setTimeout(() => {
if (this.isContinuousAsr) this._startAsrTimer(); this._resolve(this.digitBuffer.length >= this.minDigits ? 'dtmf-num-digits' : 'timeout');
else this._resolve(this.digitBuffer.length >= this.minDigits ? 'dtmf-num-digits' : 'timeout');
}, this.timeout); }, this.timeout);
} }

View File

@@ -36,6 +36,7 @@ class TaskSipRefer extends Task {
method: 'REFER', method: 'REFER',
headers: { headers: {
...this.headers, ...this.headers,
...(this.referToIsUri && {'X-Refer-To-Leave-Untouched': true}),
'Refer-To': referTo, 'Refer-To': referTo,
'Referred-By': referredBy 'Referred-By': referredBy
} }
@@ -100,6 +101,7 @@ class TaskSipRefer extends Task {
/* they may have only provided a phone number/user */ /* they may have only provided a phone number/user */
referTo = `sip:${referTo}@${host}`; referTo = `sip:${referTo}@${host}`;
} }
else this.referToIsUri = true;
if (!referredBy) { if (!referredBy) {
/* default */ /* default */
referredBy = cs.req?.callingNumber || dlg.local.uri; referredBy = cs.req?.callingNumber || dlg.local.uri;

View File

@@ -31,6 +31,8 @@ class HttpRequestor extends BaseRequestor {
if (u.port) this._baseUrl = `${u.protocol}://${u.resource}:${u.port}`; if (u.port) this._baseUrl = `${u.protocol}://${u.resource}:${u.port}`;
else this._baseUrl = `${u.protocol}://${u.resource}`; else this._baseUrl = `${u.protocol}://${u.resource}`;
this._protocol = u.protocol; this._protocol = u.protocol;
this._resource = u.resource;
this._port = u.port;
this._search = u.search; this._search = u.search;
this._usePools = process.env.HTTP_POOL && parseInt(process.env.HTTP_POOL); this._usePools = process.env.HTTP_POOL && parseInt(process.env.HTTP_POOL);
@@ -98,7 +100,7 @@ class HttpRequestor extends BaseRequestor {
} }
else { else {
const u = parseUrl(url); const u = parseUrl(url);
if (u.resource === this._resource && u.protocol === this._protocol) { if (u.resource === this._resource && u.port === this._port && u.protocol === this._protocol) {
client = this.client; client = this.client;
path = u.pathname; path = u.pathname;
query = u.query; query = u.query;

View File

@@ -412,7 +412,7 @@ class SingleDialer extends Emitter {
this.callInfo.updateCallStatus(callStatus, sipStatus, sipReason); this.callInfo.updateCallStatus(callStatus, sipStatus, sipReason);
if (typeof duration === 'number') this.callInfo.duration = duration; if (typeof duration === 'number') this.callInfo.duration = duration;
try { try {
this.requestor.request('call:status', this.application.call_status_hook, this.callInfo.toJSON()); this.notifier.request('call:status', this.application.call_status_hook, this.callInfo.toJSON());
} catch (err) { } catch (err) {
this.logger.info(err, `SingleDialer:_notifyCallStatusChange error sending ${callStatus} ${sipStatus}`); this.logger.info(err, `SingleDialer:_notifyCallStatusChange error sending ${callStatus} ${sipStatus}`);
} }

View File

@@ -54,7 +54,11 @@ class WsRequestor extends BaseRequestor {
/* if we have an absolute url, and it is http then do a standard webhook */ /* if we have an absolute url, and it is http then do a standard webhook */
if (this._isAbsoluteUrl(url) && url.startsWith('http')) { if (this._isAbsoluteUrl(url) && url.startsWith('http')) {
this.logger.debug({hook}, 'WsRequestor: sending a webhook (HTTP)'); this.logger.debug({hook}, 'WsRequestor: sending a webhook (HTTP)');
const requestor = new HttpRequestor(this.logger, this.account_sid, hook, this.secret); const requestor = new HttpRequestor(this.logger, this.account_sid, {url: hook}, this.secret);
if (type === 'session:redirect') {
this.close();
this.emit('handover', requestor);
}
return requestor.request(type, hook, params, httpHeaders); return requestor.request(type, hook, params, httpHeaders);
} }

32
package-lock.json generated
View File

@@ -27,8 +27,8 @@
"bent": "^7.3.12", "bent": "^7.3.12",
"debug": "^4.3.4", "debug": "^4.3.4",
"deepcopy": "^2.1.0", "deepcopy": "^2.1.0",
"drachtio-fsmrf": "^3.0.3", "drachtio-fsmrf": "^3.0.8",
"drachtio-srf": "^4.5.1", "drachtio-srf": "^4.5.18",
"express": "^4.18.1", "express": "^4.18.1",
"helmet": "^5.1.0", "helmet": "^5.1.0",
"ip": "^1.1.8", "ip": "^1.1.8",
@@ -1979,14 +1979,14 @@
} }
}, },
"node_modules/drachtio-fsmrf": { "node_modules/drachtio-fsmrf": {
"version": "3.0.6", "version": "3.0.8",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.6.tgz", "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.8.tgz",
"integrity": "sha512-bZDxy4ub7t3JAR6uI/goQl6vmeFLfoWd2OdCX6g1Ue8RTH54Anjt467nwfkcAPPnpafmentl6ik+iw/xaWzbrg==", "integrity": "sha512-hDu5/VncvYgpQd8h9kPZyL/bWeFkwM+SlUr4aJD8AgNdeyWg91DkgnLNHhzH/S4xWg/PTRA53imLMKVQz7Qc0Q==",
"dependencies": { "dependencies": {
"camel-case": "^4.1.2", "camel-case": "^4.1.2",
"debug": "^2.6.9", "debug": "^2.6.9",
"delegates": "^0.1.0", "delegates": "^0.1.0",
"drachtio-modesl": "^1.2.4", "drachtio-modesl": "^1.2.5",
"drachtio-srf": "^4.5.18", "drachtio-srf": "^4.5.18",
"only": "^0.0.2", "only": "^0.0.2",
"sdp-transform": "^2.14.1", "sdp-transform": "^2.14.1",
@@ -2011,9 +2011,9 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
}, },
"node_modules/drachtio-modesl": { "node_modules/drachtio-modesl": {
"version": "1.2.4", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/drachtio-modesl/-/drachtio-modesl-1.2.4.tgz", "resolved": "https://registry.npmjs.org/drachtio-modesl/-/drachtio-modesl-1.2.5.tgz",
"integrity": "sha512-Le6/iAuRhJU2fbxuRksXMXPknjU8GN5vpw1p211CmaH/dZxJ5FSghksD9ubV7Kqc6qE73M/K/boDtu14V/GjeQ==", "integrity": "sha512-LzGpAzsSkmC2E4Vho6iaHZxLbiuz64A/Z82gnpADpNVymMw/mt+aFPEUVrbppBJ6dxP3uU2311DGWGBClTVo0g==",
"dependencies": { "dependencies": {
"debug": "^4.1.1", "debug": "^4.1.1",
"eventemitter2": "^6.4.4", "eventemitter2": "^6.4.4",
@@ -8023,14 +8023,14 @@
} }
}, },
"drachtio-fsmrf": { "drachtio-fsmrf": {
"version": "3.0.6", "version": "3.0.8",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.6.tgz", "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.8.tgz",
"integrity": "sha512-bZDxy4ub7t3JAR6uI/goQl6vmeFLfoWd2OdCX6g1Ue8RTH54Anjt467nwfkcAPPnpafmentl6ik+iw/xaWzbrg==", "integrity": "sha512-hDu5/VncvYgpQd8h9kPZyL/bWeFkwM+SlUr4aJD8AgNdeyWg91DkgnLNHhzH/S4xWg/PTRA53imLMKVQz7Qc0Q==",
"requires": { "requires": {
"camel-case": "^4.1.2", "camel-case": "^4.1.2",
"debug": "^2.6.9", "debug": "^2.6.9",
"delegates": "^0.1.0", "delegates": "^0.1.0",
"drachtio-modesl": "^1.2.4", "drachtio-modesl": "^1.2.5",
"drachtio-srf": "^4.5.18", "drachtio-srf": "^4.5.18",
"only": "^0.0.2", "only": "^0.0.2",
"sdp-transform": "^2.14.1", "sdp-transform": "^2.14.1",
@@ -8054,9 +8054,9 @@
} }
}, },
"drachtio-modesl": { "drachtio-modesl": {
"version": "1.2.4", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/drachtio-modesl/-/drachtio-modesl-1.2.4.tgz", "resolved": "https://registry.npmjs.org/drachtio-modesl/-/drachtio-modesl-1.2.5.tgz",
"integrity": "sha512-Le6/iAuRhJU2fbxuRksXMXPknjU8GN5vpw1p211CmaH/dZxJ5FSghksD9ubV7Kqc6qE73M/K/boDtu14V/GjeQ==", "integrity": "sha512-LzGpAzsSkmC2E4Vho6iaHZxLbiuz64A/Z82gnpADpNVymMw/mt+aFPEUVrbppBJ6dxP3uU2311DGWGBClTVo0g==",
"requires": { "requires": {
"debug": "^4.1.1", "debug": "^4.1.1",
"eventemitter2": "^6.4.4", "eventemitter2": "^6.4.4",

View File

@@ -42,8 +42,8 @@
"bent": "^7.3.12", "bent": "^7.3.12",
"debug": "^4.3.4", "debug": "^4.3.4",
"deepcopy": "^2.1.0", "deepcopy": "^2.1.0",
"drachtio-fsmrf": "^3.0.3", "drachtio-fsmrf": "^3.0.8",
"drachtio-srf": "^4.5.1", "drachtio-srf": "^4.5.18",
"express": "^4.18.1", "express": "^4.18.1",
"helmet": "^5.1.0", "helmet": "^5.1.0",
"ip": "^1.1.8", "ip": "^1.1.8",