add tag task and varioius cleanup

This commit is contained in:
Dave Horton
2020-01-29 15:27:20 -05:00
parent bed4fa1f42
commit 92acd50595
17 changed files with 278 additions and 111 deletions

View File

@@ -10,6 +10,7 @@
"SipNotify": "sip:notify",
"SipRedirect": "sip:redirect",
"Say": "say",
"Tag": "tag",
"Transcribe": "transcribe"
},
"CallStatus": {

View File

@@ -1,23 +1,36 @@
const request = require('request');
require('request-debug')(request);
const retrieveApp = require('./retrieve-app');
function hooks(logger, callAttributes) {
function actionHook(url, method, auth, opts, expectResponse = true) {
const params = Object.assign({}, callAttributes, opts);
let basicauth, qs, body;
if (auth && typeof auth === 'object' && Object.keys(auth) === 2) basicauth = auth;
if ('GET' === method.toUpperCase()) qs = params;
else body = params;
const obj = {url, method, auth: basicauth, json: expectResponse || !!body, qs, body};
logger.debug({opts: obj}, 'actionHook');
function hooks(logger, callInfo) {
function actionHook(hook, obj, expectResponse = true) {
const method = hook.method.toUpperCase();
const auth = (hook.username && hook.password) ?
{username: hook.username, password: hook.password} :
null;
const data = Object.assign({}, obj, callInfo);
if ('GET' === method) {
// remove customer data - only for POSTs since it might be quite complex
delete data.customerData;
}
const opts = {
url: hook.url,
method,
json: 'POST' === method || expectResponse
};
if (auth) obj.auth = auth;
if ('POST' === method) obj.body = data;
else obj.qs = data;
return new Promise((resolve, reject) => {
request(obj, (err, response, body) => {
request(opts, (err, response, body) => {
if (err) {
logger.info(`actionHook error ${method} ${url}: ${err.message}`);
logger.info(`actionHook error ${method} ${hook.url}: ${err.message}`);
return reject(err);
}
if (body && expectResponse) {
logger.debug(body, `actionHook response ${method} ${url}`);
logger.debug(body, `actionHook response ${method} ${hook.url}`);
return resolve(retrieveApp(logger, body));
}
resolve(body);
@@ -25,7 +38,7 @@ function hooks(logger, callAttributes) {
});
}
function notifyHook(url, method, auth, opts) {
function notifyHook(url, method, auth, opts = {}) {
return actionHook(url, method, auth, opts, false);
}

View File

@@ -1,12 +1,13 @@
const Emitter = require('events');
const {CallStatus} = require('./constants');
const uuidv4 = require('uuid/v4');
const SipError = require('drachtio-srf').SipError;
const {TaskPreconditions} = require('../utils/constants');
const {TaskPreconditions, CallDirection} = require('../utils/constants');
const CallInfo = require('../session/call-info');
const assert = require('assert');
const ConfirmCallSession = require('../session/confirm-call-session');
const hooks = require('./notifiers');
const moment = require('moment');
const parseUrl = require('parse-url');
class SingleDialer extends Emitter {
constructor({logger, sbcAddress, target, opts, application, callInfo}) {
@@ -18,13 +19,21 @@ class SingleDialer extends Emitter {
this.sbcAddress = sbcAddress;
this.opts = opts;
this.application = application;
this.url = opts.url;
this.method = opts.method;
this.url = target.url;
this.method = target.method;
this._callSid = uuidv4();
this.bindings = logger.bindings();
this.callInfo = Object.assign({}, callInfo, {callSid: this._callSid});
this.sipStatus;
this.parentCallInfo = callInfo;
/*
this.callInfo = Object.assign({}, callInfo, {
callSid: this._callSid,
parentCallSid: callInfo.callSid,
direction: CallDirection.Outbound,
callStatus: CallStatus.Trying,
sipStatus: 100
});
*/
this.callGone = false;
this.on('callStatusChange', this._notifyCallStatusChange.bind(this));
@@ -86,18 +95,21 @@ class SingleDialer extends Emitter {
* (a) create a logger for this call
* (b) augment this.callInfo with additional call info
*/
this.callInfo = new CallInfo({
direction: CallDirection.Outbound,
parentCallInfo: this.parentCallInfo,
req
});
this.logger = srf.locals.parentLogger.child({
callSid: this.callSid,
parentCallSid: this.bindings.callSid,
callId: req.get('Call-ID')
callSid: this.callInfo.callSid,
parentCallSid: this.parentCallInfo.callSid,
callId: this.callInfo.callId
});
this.inviteInProgress = req;
const status = {callStatus: CallStatus.Trying, sipStatus: 100};
Object.assign(this.callInfo, {callId: req.get('Call-ID'), from: req.callingNumber, to});
const {actionHook, notifyHook} = hooks(this.logger, this.callInfo);
this.actionHook = actionHook;
this.notifyHook = notifyHook;
this.emit('callStatusChange', status);
this.emit('callStatusChange', {callStatus: CallStatus.Trying, sipStatus: 100});
},
cbProvisional: (prov) => {
const status = {sipStatus: prov.status};
@@ -168,7 +180,24 @@ class SingleDialer extends Emitter {
async _executeApp(url) {
this.logger.debug(`SingleDialer:_executeApp: executing ${url} after connect`);
try {
const tasks = await this.actionHook(this.url, this.method);
let auth;
const app = Object.assign({}, this.application);
if (url.startsWith('/')) {
const savedUrl = url;
const or = app.originalRequest;
url = `${or.baseUrl}${url}`;
auth = or.auth;
this.logger.debug({originalUrl: savedUrl, normalizedUrl: url}, 'SingleDialer:_executeApp normalized url');
}
else {
const u = parseUrl(url);
const myPort = u.port ? `:${u.port}` : '';
app.originalRequest = {
baseUrl: `${u.protocol}://${u.resource}${myPort}`
};
}
const tasks = await this.actionHook(url, this.method, auth);
const allowedTasks = tasks.filter((task) => {
return [
TaskPreconditions.StableCall,
@@ -180,7 +209,7 @@ class SingleDialer extends Emitter {
}
this.logger.debug(`SingleDialer:_executeApp: executing ${tasks.length} tasks`);
const cs = new ConfirmCallSession(this.logger, this.application, this.dlg, this.ep, tasks);
const cs = new ConfirmCallSession({logger: this.logger, application: app, dlg: this.dlg, ep: this.ep, tasks});
await cs.exec();
this.emit(this.dlg.connected ? 'accept' : 'decline');
} catch (err) {
@@ -190,18 +219,13 @@ class SingleDialer extends Emitter {
}
}
_notifyCallStatusChange(callStatus) {
_notifyCallStatusChange({callStatus, sipStatus}) {
this.logger.debug(`SingleDialer:_notifyCallStatusChange: ${callStatus} ${sipStatus}`);
this.callInfo.updateStatus(callStatus, sipStatus);
try {
const auth = {};
if (this.application.hook_basic_auth_user && this.application.hook_basic_auth_password) {
Object.assign(auth, {user: this.application.hook_basic_auth_user, password: this.hook_basic_auth_password});
}
this.notifyHook(this.application.call_status_hook,
this.application.hook_http_method,
auth,
callStatus);
this.notifyHook(this.application.call_status_hook);
} catch (err) {
this.logger.info(err, `SingleDialer:_notifyCallStatusChange: error sending ${JSON.stringify(callStatus)}`);
this.logger.info(err, `SingleDialer:_notifyCallStatusChange error sending ${callStatus} ${sipStatus}`);
}
}
}

View File

@@ -4,26 +4,25 @@ const makeTask = require('../tasks/make_task');
const normalizeJamones = require('./normalize-jamones');
function retrieveUrl(logger, url, method, auth, qs, body) {
logger.debug(`body: ${body}`);
const opts = {url, method, auth, qs, json: true};
if (body) {
logger.debug('adding body');
Object.assign(opts, {body});
}
function retrieveUrl(logger, url, method, auth, obj) {
const opts = {url, method, auth, json: true};
if (method === 'GET') Object.assign(opts, {qs: obj});
else Object.assign(opts, {body: obj});
return new Promise((resolve, reject) => {
request(opts, (err, response, body) => {
if (err) throw err;
if (body) logger.debug({body}, 'retrieveUrl: customer returned an application');
resolve(body);
});
});
}
async function retrieveApp(logger, url, method, auth, qs, body) {
async function retrieveApp(logger, url, method, auth, obj) {
let json;
if (typeof url === 'object') json = url;
else json = await retrieveUrl(logger, url, method, auth, qs, body);
else json = await retrieveUrl(logger, url, method, auth, obj);
return normalizeJamones(logger, json).map((tdata) => makeTask(logger, tdata));
}