Files
jambonz-feature-server/lib/tasks/rest_dial.js
Hoan Luu Huu 32a84b7b19 feat: rest:dial amd (#339)
Add support for sending 'amd' property in createCall REST API and also added support for using any of the speech vendors for STT
---------

Co-authored-by: Dave Horton <daveh@beachdognet.com>
2023-05-16 16:20:08 -04:00

133 lines
3.6 KiB
JavaScript

const Task = require('./task');
const {TaskName} = require('../utils/constants');
const makeTask = require('./make_task');
const { normalizeJambones } = require('@jambonz/verb-specifications');
/**
* Manages an outdial made via REST API
*/
class TaskRestDial extends Task {
constructor(logger, opts) {
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;
this.timeout = this.data.timeout || 60;
this.on('connect', this._onConnect.bind(this));
this.on('callStatus', this._onCallStatus.bind(this));
}
get name() { return TaskName.RestDial; }
/**
* INVITE has just been sent at this point
*/
async exec(cs) {
await super.exec(cs);
this.cs = cs;
this.canCancel = true;
if (this.data.amd) {
this.startAmd = cs.startAmd;
this.stopAmd = cs.stopAmd;
this.on('amd', this._onAmdEvent.bind(this, cs));
}
this._setCallTimer();
await this.awaitTaskDone();
}
turnOffAmd() {
if (this.callSession.ep && this.callSession.ep.amd) this.stopAmd(this.callSession.ep, this);
}
kill(cs) {
super.kill(cs);
this._clearCallTimer();
if (this.canCancel) {
this.canCancel = false;
cs?.req?.cancel();
}
this.notifyTaskDone();
}
async _onConnect(dlg) {
this.canCancel = false;
const cs = this.callSession;
cs.setDialog(dlg);
try {
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const params = {
...cs.callInfo,
defaults: {
synthesizer: {
vendor: cs.speechSynthesisVendor,
language: cs.speechSynthesisLanguage,
voice: cs.speechSynthesisVoice
},
recognizer: {
vendor: cs.speechRecognizerVendor,
language: cs.speechRecognizerLanguage
}
}
};
if (this.startAmd) {
try {
this.startAmd(this.callSession, this.callSession.ep, this, this.data.amd);
} catch (err) {
this.logger.info({err}, 'Rest:dial:Call established - Error calling startAmd');
}
}
const tasks = await cs.requestor.request('session:new', this.call_hook, params, httpHeaders);
if (tasks && Array.isArray(tasks)) {
this.logger.debug({tasks: tasks}, `TaskRestDial: replacing application with ${tasks.length} tasks`);
cs.replaceApplication(normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata)));
}
} catch (err) {
this.logger.error(err, 'TaskRestDial:_onConnect error retrieving or parsing application, ending call');
this.notifyTaskDone();
}
}
_onCallStatus(status) {
this.logger.debug(`CallStatus: ${status}`);
if (status >= 200) {
this.canCancel = false;
this._clearCallTimer();
if (status !== 200) this.notifyTaskDone();
}
}
_setCallTimer() {
this.timer = setTimeout(this._onCallTimeout.bind(this), this.timeout * 1000);
}
_clearCallTimer() {
if (this.timer) clearTimeout(this.timer);
this.timer = null;
}
_onCallTimeout() {
this.logger.debug('TaskRestDial: timeout expired without answer, killing task');
this.timer = null;
this.kill(this.cs);
}
_onAmdEvent(cs, evt) {
this.logger.info({evt}, 'Rest:dial:_onAmdEvent');
const {actionHook} = this.data.amd;
this.performHook(cs, actionHook, evt)
.catch((err) => {
this.logger.error({err}, 'Rest:dial:_onAmdEvent - error calling actionHook');
});
}
}
module.exports = TaskRestDial;