mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
add support for live call control
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
lib/session/session-tracker.js
Normal file
42
lib/session/session-tracker.js
Normal 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;
|
||||
Reference in New Issue
Block a user