snake case REST payloads, support for LCC with child_call_hook, handle 302 on outdial

This commit is contained in:
Dave Horton
2021-04-22 14:39:54 -04:00
parent 8eb0cd1520
commit 576f645489
11 changed files with 340 additions and 179 deletions

View File

@@ -113,6 +113,11 @@ class TaskDial extends Task {
}
get ep() {
/**
* Note:
* this.ep is the B leg-facing endpoint
* this.epOther is the A leg-facing endpoint
*/
if (this.sd) return this.sd.ep;
}
@@ -133,6 +138,8 @@ class TaskDial extends Task {
this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector);
await this._attemptCalls(cs);
await this.awaitTaskDone();
this.logger.debug({callSid: this.cs.callSid}, 'Dial:exec task is done, sending actionHook if any');
await this.performAction(this.results);
this._removeDtmfDetection(cs, this.epOther);
this._removeDtmfDetection(cs, this.ep);
@@ -146,6 +153,8 @@ class TaskDial extends Task {
super.kill(cs);
this._removeDtmfDetection(this.cs, this.epOther);
this._removeDtmfDetection(this.cs, this.ep);
this.logger.debug({callSid: this.cs.callSid}, 'Dial:kill removed dtmf listeners');
this._killOutdials();
if (this.sd) {
this.sd.kill();
@@ -212,6 +221,7 @@ class TaskDial extends Task {
_removeDtmfDetection(cs, ep) {
if (ep) {
delete ep.dtmfDetector;
this.logger.debug(`Dial:_removeDtmfDetection endpoint ${ep.uuid}`);
ep.removeAllListeners('dtmf');
}
}
@@ -219,13 +229,18 @@ class TaskDial extends Task {
_onDtmf(cs, ep, evt) {
if (ep.dtmfDetector) {
const match = ep.dtmfDetector.keyPress(evt.dtmf);
const requestor = ep.dtmfDetector === this.parentDtmfCollector ?
cs.requestor :
this.sd.requestor;
if (match) {
this.logger.debug(`parentCall triggered dtmf match: ${match}`);
requestor.request(this.dtmfHook, Object.assign({dtmf: match}, cs.callInfo))
.catch((err) => this.logger.info(err, 'Dial:_onDtmf - error'));
this.logger.debug({callSid: this.cs.callSid}, `Dial:_onDtmf triggered dtmf match: ${match}`);
const requestor = ep.dtmfDetector === this.parentDtmfCollector ?
cs.requestor :
(this.sd ? this.sd.requestor : null);
if (!requestor) {
this.logger.info(`Dial:_onDtmf got digits on B leg after adulting: ${evt.dtmf}`);
}
else {
requestor.request(this.dtmfHook, Object.assign({dtmf: match}, cs.callInfo))
.catch((err) => this.logger.info(err, 'Dial:_onDtmf - error'));
}
}
}
}
@@ -351,6 +366,16 @@ class TaskDial extends Task {
this.logger.debug('Dial:_attemptCalls - all calls failed after decline, ending task');
this.kill(cs);
}
})
.once('adulting', () => {
/* child call just adulted and got its own session */
this.logger.info('Dial:on_adulting: detaching child call leg');
if (this.ep) {
this.logger.debug(`Dial:on_adulting: removing dtmf from ${this.ep.uuid}`);
this.ep.removeAllListeners('dtmf');
}
this.sd = null;
this.callSid = null;
});
} catch (err) {
this.logger.error(err, 'Dial:_attemptCalls');
@@ -399,11 +424,14 @@ class TaskDial extends Task {
}
sessionTracker.add(this.callSid, cs);
this.dlg.on('destroy', () => {
this.logger.debug('Dial:_selectSingleDial called party hungup, ending dial operation');
sessionTracker.remove(this.callSid);
if (this.timerMaxCallDuration) clearTimeout(this.timerMaxCallDuration);
this.ep.unbridge();
this.kill(cs);
/* if our child is adulting, he's own his own now.. */
if (this.dlg) {
this.logger.debug('Dial:_selectSingleDial called party hungup, ending dial operation');
sessionTracker.remove(this.callSid);
if (this.timerMaxCallDuration) clearTimeout(this.timerMaxCallDuration);
this.ep.unbridge();
this.kill(cs);
}
});
Object.assign(this.results, {

View File

@@ -275,18 +275,19 @@ class TaskEnqueue extends Task {
this.logger.error({err}, `TaskEnqueue:_playHook error retrieving list info for queue ${this.queueName}`);
}
const json = await cs.application.requestor.request(hook, params);
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
const allowedTasks = json.filter((task) => allowed.includes(task.verb));
if (json.length !== allowedTasks.length) {
this.logger.debug({json, allowedTasks}, 'unsupported task');
throw new Error(`unsupported verb in dial enqueue waitHook: only ${JSON.stringify(allowed)}`);
const allowedTasks = tasks.filter((t) => allowed.includes(t.verb));
if (tasks.length !== allowedTasks.length) {
this.logger.debug({tasks, allowedTasks}, 'unsupported task');
throw new Error(`unsupported verb in enqueue waitHook: only ${JSON.stringify(allowed)}`);
}
this.logger.debug(`TaskEnqueue:_playHook: executing ${json.length} tasks`);
this.logger.debug(`TaskEnqueue:_playHook: executing ${tasks.length} tasks`);
// check for 'leave' verb and only execute tasks up till then
const tasksToRun = [];
let leave = false;
for (const o of json) {
for (const o of tasks) {
if (o.verb === TaskName.Leave) {
leave = true;
this.logger.info('waitHook returned a leave task');
@@ -297,14 +298,13 @@ class TaskEnqueue extends Task {
if (this.killed) return [];
else if (tasksToRun.length > 0) {
const tasks = normalizeJambones(this.logger, tasksToRun).map((tdata) => makeTask(this.logger, tdata));
this._playSession = new ConfirmCallSession({
logger: this.logger,
application: cs.application,
dlg,
ep: cs.ep,
callInfo: cs.callInfo,
tasks
tasksToRun
});
await this._playSession.exec();
this._playSession = null;

View File

@@ -91,6 +91,7 @@ class TaskGather extends Task {
kill(cs) {
super.kill(cs);
this._killAudio();
this.ep.removeAllListeners('dtmf');
this._resolve('killed');
}

View File

@@ -34,11 +34,13 @@ class TaskSay extends Task {
})))
.filter((fp) => fp && fp.length);
this.logger.debug({files}, 'synthesized files for tts');
this.logger.debug({files, loop: this.loop}, 'synthesized files for tts');
if (!this.ep.connected) this.logger.debug('say: endpoint is not connected!');
while (!this.killed && this.loop-- && this.ep.connected) {
let segment = 0;
do {
this.logger.debug(`playing file ${files[segment]}`);
await ep.play(files[segment]);
} while (!this.killed && ++segment < files.length);
}