add support for live call control

This commit is contained in:
Dave Horton
2020-02-07 10:26:35 -05:00
parent 2811e35c6b
commit 3ca2d982cc
18 changed files with 332 additions and 73 deletions

View File

@@ -16,7 +16,7 @@ class CallInfo {
this.callId = req.get('Call-ID');
this.sipStatus = 100;
this.callStatus = CallStatus.Trying;
this.originatingSipIP = req.get('X-Forwarded-For');
this.originatingSipIp = req.get('X-Forwarded-For');
this.originatingSipTrunkName = req.get('X-Originating-Carrier');
}
else if (opts.parentCallInfo) {
@@ -74,7 +74,7 @@ class CallInfo {
accountSid: this.accountSid,
applicationSid: this.applicationSid
};
['parentCallSid', 'originatingSipIP', 'originatingSipTrunkName'].forEach((prop) => {
['parentCallSid', 'originatingSipIp', 'originatingSipTrunkName'].forEach((prop) => {
if (this[prop]) obj[prop] = this[prop];
});
if (typeof this.duration === 'number') obj.duration = this.duration;

View File

@@ -4,6 +4,7 @@ const {CallDirection, TaskPreconditions, CallStatus} = require('../utils/constan
const hooks = require('../utils/notifiers');
const moment = require('moment');
const assert = require('assert');
const sessionTracker = require('./session-tracker');
const BADPRECONDITIONS = 'preconditions not met';
class CallSession extends Emitter {
@@ -18,9 +19,14 @@ class CallSession extends Emitter {
const {notifyHook} = hooks(this.logger, this.callInfo);
this.notifyHook = notifyHook;
this.updateCallStatus = srf.locals.dbHelpers.updateCallStatus;
this.serviceUrl = srf.locals.serviceUrl;
this.taskIdx = 0;
this.stackIdx = 0;
this.callGone = false;
sessionTracker.add(this.callSid, this);
}
get callSid() {
@@ -87,6 +93,8 @@ class CallSession extends Emitter {
this._onTasksDone();
this._clearCalls();
this.ms && this.ms.destroy();
sessionTracker.remove(this.callSid);
}
_onTasksDone() {
@@ -96,7 +104,42 @@ class CallSession extends Emitter {
_callReleased() {
this.logger.debug('CallSession:_callReleased - caller hung up');
this.callGone = true;
if (this.currentTask) this.currentTask.kill();
if (this.currentTask) {
this.currentTask.kill();
this.currentTask = null;
}
}
normalizeUrl(url, method, auth) {
const hook = {url, method};
if (auth && auth.username && auth.password) Object.assign(hook, auth);
if (url.startsWith('/')) {
const or = this.originalRequest;
if (or) {
hook.url = `${or.baseUrl}${url}`;
hook.method = hook.method || or.method || 'POST';
if (!hook.auth && or.auth) Object.assign(hook, or.auth);
}
}
this.logger.debug({hook}, 'Task:normalizeUrl');
return hook;
}
async updateCall(opts) {
this.logger.debug(opts, 'CallSession:updateCall');
if (opts.call_status === 'completed' && this.dlg) {
this.logger.info('CallSession:updateCall hanging up call due to request from api');
this._callerHungup();
}
else if (opts.call_hook && opts.call_hook.url) {
const hook = this.normalizeUrl(opts.call_hook.url, opts.call_hook.method, opts.call_hook.auth);
this.logger.info({hook}, 'CallSession:updateCall replacing application due to request from api');
const {actionHook} = hooks(this.logger, this.callInfo);
const tasks = await actionHook(hook);
this.logger.info({tasks}, 'CallSession:updateCall new task list');
this.replaceApplication(tasks);
}
}
/**
@@ -105,9 +148,14 @@ class CallSession extends Emitter {
*/
replaceApplication(tasks) {
this.tasks = tasks;
this.logger.info({tasks}, `CallSession:replaceApplication - reset application with ${tasks.length} new tasks`);
this.taskIdx = 0;
this.stackIdx++;
this.logger.info({tasks},
`CallSession:replaceApplication reset with ${tasks.length} new tasks, stack depth is ${this.stackIdx}`);
if (this.currentTask) {
this.currentTask.kill();
this.currentTask = null;
}
}
_evaluatePreconditions(task) {
switch (task.preconditions) {
@@ -227,6 +275,9 @@ class CallSession extends Emitter {
} catch (err) {
this.logger.info(err, `CallSession:_notifyCallStatusChange error sending ${callStatus} ${sipStatus}`);
}
// update calls db
this.updateCallStatus(this.callInfo, this.serviceUrl).catch((err) => this.logger.error(err, 'redis error'));
}
}

View File

@@ -0,0 +1,42 @@
const Emitter = require('events');
const assert = require('assert');
class SessionTracker extends Emitter {
constructor() {
super();
this.sessions = new Map();
}
get logger() {
if (!this._logger) {
const {logger} = require('../../app');
this._logger = logger;
}
return this._logger;
}
add(callSid, callSession) {
assert(callSid);
this.sessions.set(callSid, callSession);
this.logger.info(`SessionTracker:add callSid ${callSid}, we have ${this.sessions.size} session being tracked`);
}
remove(callSid) {
assert(callSid);
this.sessions.delete(callSid);
this.logger.info(`SessionTracker:remove callSid ${callSid}, we have ${this.sessions.size} being tracked`);
}
has(callSid) {
return this.sessions.has(callSid);
}
get(callSid) {
return this.sessions.get(callSid);
}
}
const singleton = new SessionTracker();
module.exports = singleton;