Compare commits

..

6 Commits

Author SHA1 Message Date
surajshivakumar
14d1c0d260 rest dial dtmf 2024-08-10 15:22:38 -04:00
surajshivakumar
d75ae26db0 rest dial dtmf 2024-08-09 17:20:11 -04:00
surajshivakumar
41f6397090 rest dial dtmf 2024-08-09 17:08:10 -04:00
surajshivakumar
35e67b01d0 rest dial dtmf 2024-08-09 17:05:33 -04:00
surajshivakumar
ca6287534e rest dial dtmf 2024-08-09 16:45:13 -04:00
surajshivakumar
d5876c6820 rest dial dtmf 2024-08-09 16:12:22 -04:00
6 changed files with 133 additions and 39 deletions

View File

@@ -135,10 +135,15 @@ class Conference extends Task {
* @param {SipDialog} dlg
*/
async _init(cs, dlg) {
const friendlyName = this.confName;
const {createHash, retrieveHash} = cs.srf.locals.dbHelpers;
this.friendlyName = this.confName;
this.confName = `conf:${cs.accountSid}:${this.confName}`;
this.statusParams = Object.assign({
conferenceSid: this.confName,
friendlyName
}, cs.callInfo);
// check if conference is in progress
const obj = await retrieveHash(this.confName);
if (obj) {
@@ -688,24 +693,8 @@ class Conference extends Task {
if (!params.time) params.time = (new Date()).toISOString();
if (!params.members && typeof this.participantCount === 'number') params.members = this.participantCount;
cs.application.requestor
.request(
'verb:hook',
this.statusHook,
Object.assign(
params,
Object.assign(
{
conferenceSid: this.confName,
friendlyName: this.friendlyName,
},
cs.callInfo.toJSON()
),
httpHeaders
)
)
.catch((err) =>
this.logger.info(err, 'Conference:notifyConferenceEvent - error')
);
.request('verb:hook', this.statusHook, Object.assign(params, this.statusParams, httpHeaders))
.catch((err) => this.logger.info(err, 'Conference:notifyConferenceEvent - error'));
}
}

View File

@@ -2,10 +2,44 @@ const Task = require('./task');
const {TaskName} = require('../utils/constants');
const makeTask = require('./make_task');
const { normalizeJambones } = require('@jambonz/verb-specifications');
const DtmfCollector = require('../utils/dtmf-collector');
/**
* Manages an outdial made via REST API
*/
function parseDtmfOptions(logger, dtmfCapture) {
console.log('DTMF Capture Settings:', dtmfCapture); // Log the initial DTMF capture settings
let parentDtmfCollector, childDtmfCollector;
const parentKeys = [], childKeys = [];
if (Array.isArray(dtmfCapture)) {
Array.prototype.push.apply(parentKeys, dtmfCapture);
Array.prototype.push.apply(childKeys, dtmfCapture);
} else if (dtmfCapture.childCall || dtmfCapture.parentCall) {
if (dtmfCapture.childCall && Array.isArray(dtmfCapture.childCall)) {
Array.prototype.push.apply(childKeys, dtmfCapture.childCall);
}
if (dtmfCapture.parentCall && Array.isArray(dtmfCapture.parentCall)) {
Array.prototype.push.apply(parentKeys, dtmfCapture.parentCall);
}
}
console.log('Parent DTMF Keys:', parentKeys); // Log the keys for the parent call
console.log('Child DTMF Keys:', childKeys); // Log the keys for the child call
if (childKeys.length) {
childDtmfCollector = new DtmfCollector({logger, patterns: childKeys});
console.log('Child DtmfCollector created:', childDtmfCollector); // Log when child DTMF collector is created
}
if (parentKeys.length) {
parentDtmfCollector = new DtmfCollector({logger, patterns: parentKeys});
console.log('Parent DtmfCollector created:', parentDtmfCollector); // Log when parent DTMF collector is created
}
return {childDtmfCollector, parentDtmfCollector};
}
class TaskRestDial extends Task {
constructor(logger, opts) {
super(logger, opts);
@@ -15,10 +49,21 @@ class TaskRestDial extends Task {
this.fromHost = this.data.fromHost;
this.to = this.data.to;
this.call_hook = this.data.call_hook;
this.dtmfHook = this.data.dtmfHook;
this.timeout = this.data.timeout || 60;
this.sipRequestWithinDialogHook = this.data.sipRequestWithinDialogHook;
this.referHook = this.data.referHook;
if (this.dtmfHook) {
const {parentDtmfCollector, childDtmfCollector} = parseDtmfOptions(logger, this.data.dtmfCapture || {});
if (parentDtmfCollector) {
this.parentDtmfCollector = parentDtmfCollector;
}
if (childDtmfCollector) {
this.childDtmfCollector = childDtmfCollector;
}
}
this.on('connect', this._onConnect.bind(this));
this.on('callStatus', this._onCallStatus.bind(this));
}
@@ -31,8 +76,9 @@ class TaskRestDial extends Task {
/**
* INVITE has just been sent at this point
*/
*/
async exec(cs) {
console.log('Executing TaskRestDial'); // Log when exec is called
await super.exec(cs);
this.cs = cs;
this.canCancel = true;
@@ -45,8 +91,10 @@ class TaskRestDial extends Task {
this._setCallTimer();
await this.awaitTaskDone();
console.log('TaskRestDial execution completed'); // Log when exec is finished
}
turnOffAmd() {
if (this.callSession.ep && this.callSession.ep.amd) this.stopAmd(this.callSession.ep, this);
}
@@ -58,15 +106,27 @@ class TaskRestDial extends Task {
this.canCancel = false;
cs?.req?.cancel();
}
// Remove DTMF detection
this._removeDtmfDetection(cs.dlg);
this.notifyTaskDone();
}
async _onConnect(dlg) {
console.log('Call connected, setting up dialog and DTMF detection');
this.canCancel = false;
const cs = this.callSession;
cs.setDialog(dlg);
cs.referHook = this.referHook;
this.logger.debug('TaskRestDial:_onConnect - call connected');
// Attach DTMF detection
if (this.parentDtmfCollector || this.childDtmfCollector) {
console.log('Setting up DTMF detection'); // Log when DTMF detection is being set up
this._installDtmfDetection(cs, dlg);
}
if (this.sipRequestWithinDialogHook) this._initSipRequestWithinDialogHandler(cs, dlg);
try {
const b3 = this.getTracingPropagation();
@@ -141,9 +201,57 @@ class TaskRestDial extends Task {
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');
});
.catch((err) => {
this.logger.error({err}, 'Rest:dial:_onAmdEvent - error calling actionHook');
});
}
_installDtmfDetection(cs, dlg) {
console.log('Installing DTMF detection'); // Log when installing DTMF detection
dlg.on('info', this._onInfo.bind(this, cs, dlg));
}
_onInfo(cs, dlg, req, res) {
console.log('INFO message received:', req.body); // Log the incoming INFO message
res.send(200);
if (req.get('Content-Type') !== 'application/dtmf-relay') {
console.log('INFO message is not DTMF relay'); // Log if the INFO message is not a DTMF relay
return;
}
const dtmfDetector = dlg === cs.dlg ? this.parentDtmfCollector : this.childDtmfCollector;
if (!dtmfDetector) {
console.log('No DTMF detector available'); // Log if no DTMF detector is available
return;
}
const arr = /Signal=([0-9#*])/.exec(req.body);
if (!arr) {
console.log('No DTMF signal found in INFO message'); // Log if no DTMF signal is found
return;
}
const key = arr[1];
console.log('DTMF Signal received:', key); // Log the detected DTMF signal
const match = dtmfDetector.keyPress(key);
if (match) {
console.log('DTMF match found:', match); // Log if a DTMF match is found
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
this.logger.info({callSid: cs.callSid}, `RestDial:_onInfo triggered dtmf match: ${match}`);
cs.requestor.request('verb:hook', this.dtmfHook, {dtmf: match, ...cs.callInfo.toJSON()}, httpHeaders)
.catch((err) => this.logger.info(err, 'RestDial:_onDtmf - error'));
} else {
console.log('No DTMF match found for key:', key); // Log if no match is found for the DTMF signal
}
}
_removeDtmfDetection(dlg) {
console.log('Removing DTMF detection'); // Log when removing DTMF detection
dlg && dlg.removeAllListeners('info');
}
_initSipRequestWithinDialogHandler(cs, dlg) {

View File

@@ -441,7 +441,7 @@ class SingleDialer extends Emitter {
});
app.requestor.request('session:adulting', '/adulting', {
...cs.callInfo.toJSON(),
parentCallInfo: this.parentCallInfo.toJSON()
parentCallInfo: this.parentCallInfo
}).catch((err) => {
newLogger.error({err}, 'doAdulting: error sending adulting request');
});

View File

@@ -45,8 +45,7 @@ const stickyVars = {
'DEEPGRAM_SPEECH_ENDPOINTING',
'DEEPGRAM_SPEECH_UTTERANCE_END_MS',
'DEEPGRAM_SPEECH_VAD_TURNOFF',
'DEEPGRAM_SPEECH_TAG',
'DEEPGRAM_SPEECH_MODEL_VERSION'
'DEEPGRAM_SPEECH_TAG'
],
aws: [
'AWS_VOCABULARY_NAME',
@@ -710,9 +709,7 @@ module.exports = (logger) => {
...(deepgramOptions.vadTurnoff) &&
{DEEPGRAM_SPEECH_VAD_TURNOFF: deepgramOptions.vadTurnoff},
...(deepgramOptions.tag) &&
{DEEPGRAM_SPEECH_TAG: deepgramOptions.tag},
...(deepgramOptions.version) &&
{DEEPGRAM_SPEECH_MODEL_VERSION: deepgramOptions.version}
{DEEPGRAM_SPEECH_TAG: deepgramOptions.tag}
};
}
else if ('soniox' === vendor) {
@@ -848,8 +845,7 @@ module.exports = (logger) => {
{hints: rOpts.hints}),
...(rOpts.hints?.length > 0 && typeof rOpts.hints[0] === 'object' &&
{hints: JSON.stringify(rOpts.hints)}),
...(typeof rOpts.hintsBoost === 'number' && {hintsBoost: rOpts.hintsBoost}),
...(task.cs?.callSid && {callSid: task.cs.callSid})
...(typeof rOpts.hintsBoost === 'number' && {hintsBoost: rOpts.hintsBoost})
};
opts = {
...opts,

15
package-lock.json generated
View File

@@ -18,7 +18,7 @@
"@jambonz/speech-utils": "^0.1.13",
"@jambonz/stats-collector": "^0.1.10",
"@jambonz/time-series": "^0.2.9",
"@jambonz/verb-specifications": "^0.0.76",
"@jambonz/verb-specifications": "^0.0.75",
"@opentelemetry/api": "^1.8.0",
"@opentelemetry/exporter-jaeger": "^1.23.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
@@ -1575,9 +1575,10 @@
}
},
"node_modules/@jambonz/verb-specifications": {
"version": "0.0.76",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.76.tgz",
"integrity": "sha512-7s61qAsG07xLLaEAHW236rSYzEoh9Qg0aRWHPbTfxCsuTKDNeq+5EwGAShDU5R5ZpjgweZJLhArQm8Ym+4xJ2A==",
"version": "0.0.75",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.75.tgz",
"integrity": "sha512-THM4mzhyEDfOqfE6cI4Z4aKCR9TURXUaO5gtVBfeup6p7mab+foxHoDDLZxPLhbPqYMTSI0yYzdW+wNqtE6O1Q==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.4",
"pino": "^8.8.0"
@@ -10539,9 +10540,9 @@
}
},
"@jambonz/verb-specifications": {
"version": "0.0.76",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.76.tgz",
"integrity": "sha512-7s61qAsG07xLLaEAHW236rSYzEoh9Qg0aRWHPbTfxCsuTKDNeq+5EwGAShDU5R5ZpjgweZJLhArQm8Ym+4xJ2A==",
"version": "0.0.75",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.75.tgz",
"integrity": "sha512-THM4mzhyEDfOqfE6cI4Z4aKCR9TURXUaO5gtVBfeup6p7mab+foxHoDDLZxPLhbPqYMTSI0yYzdW+wNqtE6O1Q==",
"requires": {
"debug": "^4.3.4",
"pino": "^8.8.0"

View File

@@ -34,7 +34,7 @@
"@jambonz/speech-utils": "^0.1.13",
"@jambonz/stats-collector": "^0.1.10",
"@jambonz/time-series": "^0.2.9",
"@jambonz/verb-specifications": "^0.0.76",
"@jambonz/verb-specifications": "^0.0.75",
"@opentelemetry/api": "^1.8.0",
"@opentelemetry/exporter-jaeger": "^1.23.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",