merge features from hosted branch (#32)

major merge of features from the hosted branch that was created temporarily during the initial launch of jambonz.org
This commit is contained in:
Dave Horton
2021-06-17 16:25:50 -04:00
committed by GitHub
parent 473a34ec9f
commit 9b59d08dcf
68 changed files with 3436 additions and 1066 deletions

View File

@@ -135,13 +135,11 @@ class TaskDial extends Task {
this.epOther.play(this.dialMusic).catch((err) => {});
}
}
this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector);
if (this.epOther) 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);
if (this.epOther) this._removeDtmfDetection(cs, this.epOther);
this._removeDtmfDetection(cs, this.ep);
} catch (err) {
this.logger.error({err}, 'TaskDial:exec terminating with error');
@@ -151,10 +149,8 @@ class TaskDial extends Task {
async kill(cs) {
super.kill(cs);
this._removeDtmfDetection(this.cs, this.epOther);
if (this.epOther) 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();
@@ -183,7 +179,7 @@ class TaskDial extends Task {
await task.exec(cs, callSid === this.callSid ? this.ep : this.epOther);
}
this.logger.debug('Dial:whisper tasks complete');
if (!cs.callGone) this.epOther.bridge(this.ep);
if (!cs.callGone && this.epOther) this.epOther.bridge(this.ep);
} catch (err) {
this.logger.error(err, 'Dial:whisper error');
}
@@ -221,7 +217,6 @@ class TaskDial extends Task {
_removeDtmfDetection(cs, ep) {
if (ep) {
delete ep.dtmfDetector;
this.logger.debug(`Dial:_removeDtmfDetection endpoint ${ep.uuid}`);
ep.removeAllListeners('dtmf');
}
}
@@ -238,7 +233,7 @@ class TaskDial extends Task {
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))
requestor.request(this.dtmfHook, {dtmf: match, ...cs.callInfo})
.catch((err) => this.logger.info(err, 'Dial:_onDtmf - error'));
}
}
@@ -250,6 +245,9 @@ class TaskDial extends Task {
this.epOther = ep;
debug(`Dial:__initializeInbound allocated ep for incoming call: ${ep.uuid}`);
/* send outbound legs back to the same SBC (to support static IP feature) */
if (!this.proxy) this.proxy = `${cs.req.source_address}:${cs.req.source_port};transport=tcp`;
if (this.dialMusic) {
// play dial music to caller while we outdial
ep.play(this.dialMusic).catch((err) => {
@@ -272,7 +270,10 @@ class TaskDial extends Task {
proxy: `sip:${sbcAddress}`,
callingNumber: this.callerId || req.callingNumber
};
Object.assign(opts.headers, {'X-Account-Sid': cs.accountSid});
opts.headers = {
...opts.headers,
'X-Account-Sid': cs.accountSid
};
const t = this.target.find((t) => t.type === 'teams');
if (t) {
@@ -387,8 +388,10 @@ class TaskDial extends Task {
_connectSingleDial(cs, sd) {
if (!this.bridged) {
this.logger.debug('Dial:_connectSingleDial bridging endpoints');
this.epOther.api('uuid_break', this.epOther.uuid);
this.epOther.bridge(sd.ep);
if (this.epOther) {
this.epOther.api('uuid_break', this.epOther.uuid);
this.epOther.bridge(sd.ep);
}
this.bridged = true;
}

View File

@@ -107,6 +107,8 @@ class Dialogflow extends Task {
this.language = cs.speechSynthesisLanguage;
this.voice = cs.speechSynthesisVoice;
}
this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts');
this.ep.addCustomEventListener('dialogflow::intent', this._onIntent.bind(this, ep, cs));
this.ep.addCustomEventListener('dialogflow::transcription', this._onTranscription.bind(this, ep, cs));
this.ep.addCustomEventListener('dialogflow::audio_provided', this._onAudioProvided.bind(this, ep, cs));
@@ -211,11 +213,13 @@ class Dialogflow extends Task {
vendor: this.vendor,
language: this.language,
voice: this.voice,
salt: cs.callSid
salt: cs.callSid,
credentials: this.ttsCredentials
};
this.logger.debug({obj}, 'Dialogflow:_onIntent - playing message via tts');
const {filePath} = await synthAudio(obj);
const {filePath, servedFromCache} = await synthAudio(obj);
if (filePath) cs.trackTmpFile(filePath);
if (!this.ttsCredentials && !servedFromCache) cs.billForTts(intent.fulfillmentText.length);
if (this.playInProgress) {
await ep.api('uuid_break', ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio'));

View File

@@ -51,9 +51,23 @@ class TaskGather extends Task {
async exec(cs, ep) {
await super.exec(cs);
const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf);
this.ep = ep;
if ('default' === this.vendor || !this.vendor) this.vendor = cs.speechRecognizerVendor;
if ('default' === this.language || !this.language) this.language = cs.speechRecognizerLanguage;
this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt');
if (!this.sttCredentials) {
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info(`TaskGather:exec - ERROR stt using ${this.vendor} requested but not creds supplied`);
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.STT_NOT_PROVISIONED,
vendor: this.vendor
}).catch((err) => this.logger.info({err}, 'Error generating alert for no stt'));
throw new Error(`no speech-to-text service credentials for ${this.vendor} have been configured`);
}
try {
if (this.sayTask) {
@@ -71,8 +85,10 @@ class TaskGather extends Task {
else this._startTimer();
if (this.input.includes('speech')) {
await this._initSpeech(ep);
await this._initSpeech(cs, ep);
this._startTranscribing(ep);
updateSpeechCredentialLastUsed(this.sttCredentials.speech_credential_sid)
.catch(() => {/*already logged error */});
}
if (this.input.includes('digits')) {
@@ -105,10 +121,11 @@ class TaskGather extends Task {
this._killAudio();
}
async _initSpeech(ep) {
async _initSpeech(cs, ep) {
const opts = {};
if ('google' === this.vendor) {
if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials);
Object.assign(opts, {
GOOGLE_SPEECH_USE_ENHANCED: true,
GOOGLE_SPEECH_SINGLE_UTTERANCE: true,
@@ -121,6 +138,8 @@ class TaskGather extends Task {
if (this.profanityFilter === true) {
Object.assign(opts, {'GOOGLE_SPEECH_PROFANITY_FILTER': true});
}
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, cs, ep));
}
else {
if (this.vocabularyName) opts.AWS_VOCABULARY_NAME = this.vocabularyName;
@@ -129,18 +148,16 @@ class TaskGather extends Task {
opts.AWS_VOCABULARY_FILTER_METHOD = this.filterMethod || 'mask';
}
Object.assign(opts, {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
AWS_REGION: process.env.AWS_REGION
AWS_ACCESS_KEY_ID: this.sttCredentials.accessKeyId,
AWS_SECRET_ACCESS_KEY: this.sttCredentials.secretAccessKey,
AWS_REGION: this.sttCredentials.region
});
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
}
this.logger.debug(`setting freeswitch vars ${JSON.stringify(opts)}`);
this.logger.debug({vars: opts}, 'setting freeswitch vars');
await ep.set(opts)
.catch((err) => this.logger.info(err, 'Error setting channel variables'));
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, ep));
}
_startTranscribing(ep) {
@@ -148,7 +165,16 @@ class TaskGather extends Task {
vendor: this.vendor,
locale: this.language,
interim: this.partialResultCallback ? true : false,
}).catch((err) => this.logger.error(err, 'TaskGather:_startTranscribing error'));
}).catch((err) => {
const {writeAlerts, AlertType} = this.cs.srf.locals;
this.logger.error(err, 'TaskGather:_startTranscribing error');
writeAlerts({
account_sid: this.cs.accountSid,
alert_type: AlertType.STT_FAILURE,
vendor: this.vendor,
detail: err.message
});
}).catch((err) => this.logger.info({err}, 'Error generating alert for tts failure'));
}
_startTimer() {
@@ -175,19 +201,23 @@ class TaskGather extends Task {
}
}
_onTranscription(ep, evt) {
_onTranscription(cs, ep, evt) {
if ('aws' === this.vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0];
this.logger.debug(evt, 'TaskGather:_onTranscription');
const final = evt.is_final;
if (final) this._resolve('speech', evt);
if (final) {
this._resolve('speech', evt);
}
else if (this.partialResultHook) {
this.cs.requestor.request(this.partialResultHook, Object.assign({speech: evt}, this.cs.callInfo))
.catch((err) => this.logger.info(err, 'GatherTask:_onTranscription error'));
}
}
_onEndOfUtterance(ep, evt) {
this.logger.info(evt, 'TaskGather:_onEndOfUtterance');
this._startTranscribing(ep);
_onEndOfUtterance(cs, ep) {
this.logger.info('TaskGather:_onEndOfUtterance');
if (!this.resolved && !this.killed) {
this._startTranscribing(ep);
}
}
async _resolve(reason, evt) {
@@ -202,13 +232,10 @@ class TaskGather extends Task {
this._clearTimer();
if (reason.startsWith('dtmf')) {
await this.performAction({reason: 'dtmfDetected', digits: this.digitBuffer});
await this.performAction({digits: this.digitBuffer});
}
else if (reason.startsWith('speech')) {
await this.performAction({reason: 'speechDetected', speech: evt});
}
else if (reason.startsWith('timeout')) {
await this.performAction({reason: 'inputTimeout'});
await this.performAction({speech: evt});
}
this.notifyTaskDone();
}

View File

@@ -103,6 +103,8 @@ class Lex extends Task {
this.language = cs.speechSynthesisLanguage;
this.voice = cs.speechSynthesisVoice;
}
this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts');
this.ep.addCustomEventListener('lex::intent', this._onIntent.bind(this, ep, cs));
this.ep.addCustomEventListener('lex::transcription', this._onTranscription.bind(this, ep, cs));
this.ep.addCustomEventListener('lex::audio_provided', this._onAudioProvided.bind(this, ep, cs));
@@ -184,20 +186,23 @@ class Lex extends Task {
try {
this.logger.debug(`tts with ${this.vendor} ${this.voice}`);
const {filepath} = await synthAudio({
// eslint-disable-next-line no-unused-vars
const {filePath, servedFromCache} = await synthAudio({
text: msg,
vendor: this.vendor,
language: this.language,
voice: this.voice,
salt: cs.callSid
salt: cs.callSid,
credentials: this.ttsCredentials
});
if (filepath) cs.trackTmpFile(filepath);
if (filePath) cs.trackTmpFile(filePath);
if (this.events.includes('start-play')) {
this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: filepath}});
this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: filePath}});
}
await ep.play(filepath);
await ep.play(filePath);
if (this.events.includes('stop-play')) {
this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: filepath}});
this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: filePath}});
}
this.logger.debug(`finished tts, sending play_done ${this.vendor} ${this.voice}`);
this.ep.api('aws_lex_play_done', this.ep.uuid)

View File

@@ -8,6 +8,7 @@ class TaskMessage extends Task {
this.preconditions = TaskPreconditions.None;
this.payload = {
message_sid: this.data.message_sid,
provider: this.data.provider,
to: this.data.to,
from: this.data.from,
@@ -23,21 +24,50 @@ class TaskMessage extends Task {
/**
* Send outbound SMS
*/
async exec(cs, dlg) {
const {srf} = cs;
async exec(cs) {
const {srf, accountSid} = cs;
const {res} = cs.callInfo;
let payload = this.payload;
await super.exec(cs);
try {
const {getSBC} = srf.locals;
const sbcAddress = getSBC();
if (sbcAddress) {
const url = `http://${sbcAddress}:3000/`;
const post = bent(url, 'POST', 'json', 200);
this.logger.info({payload: this.payload, sbcAddress}, 'Message:exec sending outbound SMS');
const response = await post('v1/outboundSMS', this.payload);
const {getSBC, getSmpp, dbHelpers} = srf.locals;
const {lookupSmppGateways} = dbHelpers;
this.logger.info(`looking up gateways for account_sid: ${accountSid}`);
const r = await lookupSmppGateways(accountSid);
let gw, url, relativeUrl;
if (r.length > 0) {
if (this.payload.provider) gw = r.find((o) => o.vc.name === this.payload.provider);
else gw = r[0];
}
if (gw) {
this.logger.info({gw, accountSid}, 'Message:exec - using smpp to send message');
url = getSmpp();
relativeUrl = '/sms';
payload = {
...payload,
...gw.sg,
...gw.vc
};
}
else {
this.logger.info({gw, accountSid, provider: this.payload.provider},
'Message:exec - no smpp gateways found to send message');
relativeUrl = 'v1/outboundSMS';
const sbcAddress = getSBC();
if (sbcAddress) url = `http://${sbcAddress}:3000/`;
//TMP: smpp only at the moment, need to add http back in
return res.sendStatus(404);
}
if (url) {
const post = bent(url, 'POST', 'json', 201);
this.logger.info({payload, url}, 'Message:exec sending outbound SMS');
const response = await post(relativeUrl, payload);
this.logger.info({response}, 'Successfully sent SMS');
if (cs.callInfo.res) {
this.logger.info('Message:exec sending 200 OK response to HTTP POST from api server');
cs.callInfo.res.status(200).json({
res.status(200).json({
sid: cs.callInfo.messageSid,
providerResponse: response
});
@@ -46,10 +76,12 @@ class TaskMessage extends Task {
// TODO: action Hook
}
else {
this.logger.info('Message:exec - unable to send SMS as there are no available SBCs');
this.logger.info('Message:exec - unable to send SMS as there are no available SMS gateways');
res.status(422).json({message: 'no configured SMS gateways'});
}
} catch (err) {
this.logger.error(err, 'TaskMessage:exec - Error sending SMS');
res.status(422).json({message: 'no configured SMS gateways'});
}
}
}

View File

@@ -15,34 +15,64 @@ class TaskSay extends Task {
get name() { return TaskName.Say; }
async exec(cs, ep) {
const {srf} = cs;
const {synthAudio} = srf.locals.dbHelpers;
await super.exec(cs);
const {srf} = cs;
const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, srf);
const {writeAlerts, AlertType} = srf.locals;
const {synthAudio} = srf.locals.dbHelpers;
const vendor = this.synthesizer.vendor || cs.speechSynthesisVendor;
const language = this.synthesizer.language || cs.speechSynthesisLanguage;
const voice = this.synthesizer.voice || cs.speechSynthesisVoice;
const salt = cs.callSid;
const credentials = cs.getSpeechCredentials(vendor, 'tts');
this.ep = ep;
try {
if (!credentials) {
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.TTS_NOT_PROVISIONED,
vendor
}).catch((err) => this.logger.info({err}, 'Error generating alert for no tts'));
throw new Error('no provisioned speech credentials for TTS');
}
// synthesize all of the text elements
const files = (await Promise.all(this.text.map(async(text) => {
const {filePath} = await synthAudio({
let lastUpdated = false;
const filepath = (await Promise.all(this.text.map(async(text) => {
const {filePath, servedFromCache} = await synthAudio({
text,
vendor: this.synthesizer.vendor || cs.speechSynthesisVendor,
language: this.synthesizer.language || cs.speechSynthesisLanguage,
voice: this.synthesizer.voice || cs.speechSynthesisVoice,
salt: cs.callSid
}).catch((err) => this.logger.error(err, 'Error synthesizing text'));
vendor,
language,
voice,
salt,
credentials
}).catch((err) => {
this.logger.info(err, 'Error synthesizing tts');
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.TTS_NOT_PROVISIONED,
vendor,
detail: err.message
});
}).catch((err) => this.logger.info({err}, 'Error generating alert for tts failure'));
this.logger.debug(`file ${filePath}, served from cache ${servedFromCache}`);
if (filePath) cs.trackTmpFile(filePath);
if (!servedFromCache && !lastUpdated) {
lastUpdated = true;
updateSpeechCredentialLastUsed(credentials.speech_credential_sid)
.catch(() => {/*already logged error */});
}
return filePath;
})))
.filter((fp) => fp && fp.length);
}))).filter((fp) => fp && fp.length);
this.logger.debug({files, loop: this.loop}, 'synthesized files for tts');
if (!this.ep.connected) this.logger.debug('say: endpoint is not connected!');
this.logger.debug({filepath}, 'synthesized files for tts');
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);
await ep.play(filepath[segment]);
} while (!this.killed && ++segment < filepath.length);
}
} catch (err) {
this.logger.info(err, 'TaskSay:exec error');

View File

@@ -166,10 +166,6 @@
"passDtmf": "boolean",
"actionHook": "object|string",
"eventHook": "object|string",
"prompt": {
"type": "string",
"enum": ["lex", "tts"]
},
"noInputTimeout": "number",
"tts": "#synthesizer"
},
@@ -177,7 +173,7 @@
"botId",
"botAlias",
"region",
"prompt"
"credentials"
]
},
"listen": {
@@ -206,7 +202,9 @@
},
"message": {
"properties": {
"provider": "string",
"carrier": "string",
"account_sid": "string",
"message_sid": "string",
"to": "string",
"from": "string",
"text": "string",
@@ -292,8 +290,7 @@
"sipUri": "string",
"auth": "#auth",
"vmail": "boolean",
"tenant": "string",
"overrideTo": "string"
"tenant": "string"
},
"required": [
"type"

View File

@@ -200,6 +200,7 @@ class Task extends Emitter {
}
if (required.length > 0) throw new Error(`${name}: missing value for ${required}`);
}
}
module.exports = Task;

View File

@@ -44,11 +44,22 @@ class TaskTranscribe extends Task {
async exec(cs, ep, parentTask) {
super.exec(cs);
const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf);
this.ep = ep;
if ('default' === this.vendor || !this.vendor) this.vendor = cs.speechRecognizerVendor;
if ('default' === this.language || !this.language) this.language = cs.speechRecognizerLanguage;
this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt');
try {
await this._startTranscribing(ep);
if (!this.sttCredentials) {
// TODO: generate alert (actually should be done by cs.getSpeechCredentials)
throw new Error('no provisioned speech credentials for TTS');
}
await this._startTranscribing(cs, ep);
updateSpeechCredentialLastUsed(this.sttCredentials.speech_credential_sid)
.catch(() => {/*already logged error */});
await this.awaitTaskDone();
} catch (err) {
this.logger.info(err, 'TaskTranscribe:exec - error');
@@ -74,19 +85,20 @@ class TaskTranscribe extends Task {
await this.awaitTaskDone();
}
async _startTranscribing(ep) {
async _startTranscribing(cs, ep) {
const opts = {};
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, ep));
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, cs, ep));
ep.addCustomEventListener(GoogleTranscriptionEvents.MaxDurationExceeded,
this._onMaxDurationExceeded.bind(this, ep));
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, ep));
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, cs, ep));
ep.addCustomEventListener(AwsTranscriptionEvents.MaxDurationExceeded,
this._onMaxDurationExceeded.bind(this, ep));
this._onMaxDurationExceeded.bind(this, cs, ep));
if (this.vendor === 'google') {
if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials);
[
['enhancedModel', 'GOOGLE_SPEECH_USE_ENHANCED'],
['separateRecognitionPerChannel', 'GOOGLE_SPEECH_SEPARATE_RECOGNITION_PER_CHANNEL'],
@@ -134,11 +146,20 @@ class TaskTranscribe extends Task {
opts.AWS_VOCABULARY_FILTER_METHOD = this.filterMethod || 'mask';
}
Object.assign(opts, {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
AWS_REGION: process.env.AWS_REGION
});
if (this.sttCredentials) {
Object.assign(opts, {
AWS_ACCESS_KEY_ID: this.sttCredentials.accessKeyId,
AWS_SECRET_ACCESS_KEY: this.sttCredentials.secretAccessKey,
AWS_REGION: this.sttCredentials.region
});
}
else {
Object.assign(opts, {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
AWS_REGION: process.env.AWS_REGION
});
}
await ep.set(opts)
.catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with aws'));
@@ -155,8 +176,10 @@ class TaskTranscribe extends Task {
});
}
_onTranscription(ep, evt) {
_onTranscription(cs, ep, evt) {
if ('aws' === this.vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0];
this.logger.debug(evt, 'TaskTranscribe:_onTranscription');
this.cs.requestor.request(this.transcriptionHook, Object.assign({speech: evt}, this.cs.callInfo))
.catch((err) => this.logger.info(err, 'TranscribeTask:_onTranscription error'));
if (this.killed) {
@@ -166,12 +189,12 @@ class TaskTranscribe extends Task {
}
}
_onNoAudio(ep) {
_onNoAudio(cs, ep) {
this.logger.debug('TaskTranscribe:_onNoAudio restarting transcription');
this._transcribe(ep);
}
_onMaxDurationExceeded(ep) {
_onMaxDurationExceeded(cs, ep) {
this.logger.debug('TaskTranscribe:_onMaxDurationExceeded restarting transcription');
this._transcribe(ep);
}