From 7abc7866dd6d0a19aaf1fb215bece7ba2fec633b Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Wed, 2 Sep 2020 12:14:53 -0400 Subject: [PATCH 1/3] add tts option for playing dialogflow audio --- lib/tasks/dialogflow/index.js | 70 ++++++++- lib/tasks/lex.js | 273 ++++++++++++++++++++++++++++++++++ lib/tasks/make_task.js | 3 + lib/tasks/specs.json | 29 +++- lib/utils/constants.json | 1 + 5 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 lib/tasks/lex.js diff --git a/lib/tasks/dialogflow/index.js b/lib/tasks/dialogflow/index.js index 4bba0e06..6dd0e85d 100644 --- a/lib/tasks/dialogflow/index.js +++ b/lib/tasks/dialogflow/index.js @@ -41,6 +41,11 @@ class Dialogflow extends Task { } if (this.data.actionHook) this.actionHook = this.data.actionHook; if (this.data.thinkingMusic) this.thinkingMusic = this.data.thinkingMusic; + if (this.data.tts) { + this.vendor = this.data.tts.vendor || 'default'; + this.language = this.data.tts.language || 'default'; + this.voice = this.data.tts.voice || 'default'; + } } get name() { return TaskName.Dialogflow; } @@ -94,6 +99,11 @@ class Dialogflow extends Task { async init(cs, ep) { this.ep = ep; try { + if (this.vendor === 'default') { + this.vendor = cs.speechSynthesisVendor; + this.language = cs.speechSynthesisLanguage; + this.voice = cs.speechSynthesisVoice; + } 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)); @@ -117,7 +127,7 @@ class Dialogflow extends Task { * @param {*} ep - media server endpoint * @param {*} evt - event data */ - _onIntent(ep, cs, evt) { + async _onIntent(ep, cs, evt) { const intent = new Intent(this.logger, evt); if (intent.isEmpty) { @@ -178,6 +188,61 @@ class Dialogflow extends Task { this.digitBuffer = new DigitBuffer(this.logger, opts); this.digitBuffer.once('fulfilled', this._onDtmfEntryComplete.bind(this, ep)); } + + /* if we are using tts and a message was provided, play it out */ + if (this.vendor && intent.fulfillmentText && intent.fulfillmentText.length > 0) { + const {srf} = cs; + const {synthAudio} = srf.locals.dbHelpers; + this.waitingForPlayStart = false; + + // start a new intent, (we want to continue to listen during the audio playback) + // _unless_ we are transferring or ending the session + if (!this.hangupAfterPlayDone) { + ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang}`); + } + + try { + const obj = { + text: intent.fulfillmentText, + vendor: this.vendor, + language: this.language, + voice: this.voice, + salt: cs.callSid + }; + this.logger.debug({obj}, 'Dialogflow:_onIntent - playing message via tts'); + const fp = await synthAudio(obj); + if (fp) cs.trackTmpFile(fp); + this.playInProgress = true; + this.curentAudioFile = fp; + + this.logger.debug(`starting to play tts ${fp}`); + if (this.events.includes('start-play')) { + this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: fp}}); + } + await ep.play(fp); + if (this.events.includes('stop-play')) { + this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: fp}}); + } + this.logger.debug(`finished ${fp}`); + + if (this.curentAudioFile === fp) { + this.playInProgress = false; + } + this.greetingPlayed = true; + + if (this.hangupAfterPlayDone) { + this.logger.info('hanging up since intent was marked end interaction and we completed final prompt'); + this.performAction({dialogflowResult: 'completed'}); + this.notifyTaskDone(); + } + else { + // every time we finish playing a prompt, start the no-input timer + this._startNoinputTimer(ep, cs); + } + } catch (err) { + this.logger.error({err}, 'Dialogflow:_onIntent - error playing tts'); + } + } } /** @@ -231,6 +296,9 @@ class Dialogflow extends Task { * @param {*} evt - event data */ async _onAudioProvided(ep, cs, evt) { + + if (this.vendor) return; + this.waitingForPlayStart = false; // kill filler audio diff --git a/lib/tasks/lex.js b/lib/tasks/lex.js new file mode 100644 index 00000000..811049ac --- /dev/null +++ b/lib/tasks/lex.js @@ -0,0 +1,273 @@ +const Task = require('./task'); +const {TaskName, TaskPreconditions} = require('../utils/constants'); +const normalizeJambones = require('../utils/normalize-jambones'); + +class Lex extends Task { + constructor(logger, opts) { + super(logger, opts); + this.preconditions = TaskPreconditions.Endpoint; + + this.credentials = this.data.credentials; + this.bot = this.data.bot; + this.alias = this.data.alias; + this.region = this.data.region; + this.bargein = this.data.bargein || false; + this.passDtmf = this.data.passDtmf || false; + if (this.data.noInputTimeout) this.noInputTimeout = this.data.noInputTimeout * 1000; + if (this.data.tts) { + this.vendor = this.data.tts.vendor || 'default'; + this.language = this.data.tts.language || 'default'; + this.voice = this.data.tts.voice || 'default'; + } + + this.botName = `${this.bot}:${this.alias}:${this.region}`; + if (this.data.eventHook) this.eventHook = this.data.eventHook; + if (this.eventHook && Array.isArray(this.data.events)) { + this.events = this.data.events; + } + else if (this.eventHook) { + // send all events by default - except interim transcripts + this.events = [ + 'intent', + 'transcription', + 'dtmf', + 'start-play', + 'stop-play', + 'play-interrupted', + 'response-text' + ]; + } + else { + this.events = []; + } + if (this.data.actionHook) this.actionHook = this.data.actionHook; + } + + get name() { return TaskName.Lex; } + + async exec(cs, ep) { + await super.exec(cs); + + try { + await this.init(cs, ep); + + this.logger.debug(`starting lex bot ${this.botName}`); + + // kick it off + this.ep.api('aws_lex_start', `${this.ep.uuid} ${this.bot} ${this.alias} ${this.region}`) + .catch((err) => { + this.logger.error({err}, `Error starting lex bot ${this.botName}`); + this.notifyTaskDone(); + }); + + await this.awaitTaskDone(); + } catch (err) { + this.logger.error({err}, 'Lex:exec error'); + } + } + + async kill(cs) { + super.kill(cs); + if (this.ep.connected) { + this.logger.debug('Lex:kill'); + this.ep.removeCustomEventListener('lex::intent'); + this.ep.removeCustomEventListener('lex::transcription'); + this.ep.removeCustomEventListener('lex::audio_provided'); + this.ep.removeCustomEventListener('lex::text_response'); + this.ep.removeCustomEventListener('lex::playback_interruption'); + this.ep.removeCustomEventListener('lex::error'); + this.ep.removeAllListeners('dtmf'); + + this.performAction({lexResult: 'caller hungup'}) + .catch((err) => this.logger.error({err}, 'lex - error w/ action webook')); + + await this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); + } + this.notifyTaskDone(); + } + + async init(cs, ep) { + this.ep = ep; + try { + if (this.vendor === 'default') { + this.vendor = cs.speechSynthesisVendor; + this.language = cs.speechSynthesisLanguage; + this.voice = cs.speechSynthesisVoice; + } + 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)); + this.ep.addCustomEventListener('lex::text_response', this._onTextResponse.bind(this, ep, cs)); + this.ep.addCustomEventListener('lex::playback_interruption', this._onPlaybackInterruption.bind(this, ep, cs)); + this.ep.addCustomEventListener('lex::error', this._onError.bind(this, ep, cs)); + this.ep.on('dtmf', this._onDtmf.bind(this, ep, cs)); + + if (this.bargein) { + await this.ep.set('x-amz-lex:barge-in-enabled', 1); + } + if (this.noInputTimeout) { + await this.ep.set('x-amz-lex:start-silence-threshold-ms', this.noInputTimeout); + } + + } catch (err) { + this.logger.error({err}, 'Error setting listeners'); + throw err; + } + } + + /** + * An intent has been returned. + * we may get an empty intent, signified by ... + * In such a case, we just restart the bot. + * @param {*} ep - media server endpoint + * @param {*} evt - event data + */ + _onIntent(ep, cs, evt) { + this.logger.debug({evt}, `got intent for ${this.botName}`); + if (this.events.includes('intent')) { + this._performHook(cs, this.eventHook, {event: 'intent', data: evt}); + } + } + + /** + * A transcription - either interim or final - has been returned. + * If we are doing barge-in based on hotword detection, check for the hotword or phrase. + * If we are playing a filler sound, like typing, during the fullfillment phase, start that + * if this is a final transcript. + * @param {*} ep - media server endpoint + * @param {*} evt - event data + */ + _onTranscription(ep, cs, evt) { + this.logger.debug({evt}, `got transcription for ${this.botName}`); + if (this.events.includes('transcription')) { + this._performHook(cs, this.eventHook, {event: 'transcription', data: evt}); + } + } + + /** + * @param {*} evt - event data + */ + async _onTextResponse(ep, cs, evt) { + this.logger.debug({evt}, `got text response for ${this.botName}`); + if (this.events.includes('response-text')) { + this._performHook(cs, this.eventHook, {event: 'response-text', data: evt}); + } + if (this.vendor && ['PlainText', 'SSML'].includes(evt.type) && evt.msg) { + const {srf} = cs; + const {synthAudio} = srf.locals.dbHelpers; + + try { + this.logger.debug(`tts with ${this.vendor} ${this.voice}`); + const fp = await synthAudio({ + text: evt.msg, + vendor: this.vendor, + language: this.language, + voice: this.voice, + salt: cs.callSid + }); + if (fp) cs.trackTmpFile(fp); + if (this.events.includes('start-play')) { + this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: fp}}); + } + await ep.play(fp); + if (this.events.includes('stop-play')) { + this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: fp}}); + } + this.logger.debug(`finished tts, sending play_done ${this.vendor} ${this.voice}`); + this.ep.api('aws_lex_play_done', this.ep.uuid) + .catch((err) => { + this.logger.error({err}, `Error sending play_done ${this.botName}`); + }); + } catch (err) { + this.logger.error({err}, 'Lex:_onTextResponse - error playing tts'); + } + } + } + + /** + * @param {*} evt - event data + */ + _onPlaybackInterruption(ep, cs, evt) { + this.logger.debug({evt}, `got playback interruption for ${this.botName}`); + if (this.bargein) { + if (this.events.includes('play-interrupted')) { + this._performHook(cs, this.eventHook, {event: 'play-interrupted', data: {}}); + } + this.ep.api('uuid_break', this.ep.uuid) + .catch((err) => this.logger.info(err, 'Lex::_onPlaybackInterruption - Error killing audio')); + } + } + + /** + * Lex has returned an error of some kind. + * @param {*} evt - event data + */ + _onError(ep, cs, evt) { + this.logger.error({evt}, `got error for bot ${this.botName}`); + } + + /** + * Audio has been received from lex and written to a temporary disk file. + * Start playing the audio, after killing any filler sound that might be playing. + * When the audio completes, start the no-input timer. + * @param {*} ep - media server endpoint + * @param {*} evt - event data + */ + async _onAudioProvided(ep, cs, evt) { + if (this.vendor) return; + + this.waitingForPlayStart = false; + this.logger.debug({evt}, `got audio file for bot ${this.botName}`); + + try { + if (this.events.includes('start-play')) { + this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: evt.path}}); + } + await ep.play(evt.path); + if (this.events.includes('stop-play')) { + this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: evt.path}}); + } + this.logger.debug({evt}, `done playing audio file for bot ${this.botName}`); + this.ep.api('aws_lex_play_done', this.ep.uuid) + .catch((err) => { + this.logger.error({err}, `Error sending play_done ${this.botName}`); + }); + } catch (err) { + this.logger.error({err}, `Error playing file ${evt.path} for both ${this.botName}`); + } + + } + + /** + * receive a dmtf entry from the caller. + * If we have active dtmf instructions, collect and process accordingly. + */ + _onDtmf(ep, cs, evt) { + this.logger.debug({evt}, 'Lex:_onDtmf'); + if (this.events.includes('dtmf')) { + this._performHook(cs, this.eventHook, {event: 'dtmf', data: evt}); + } + if (this.passDtmf) { + this.ep.api('aws_lex_dtmf', `${this.ep.uuid} ${evt.dtmf}`) + .catch((err) => { + this.logger.error({err}, `Error sending dtmf ${evt.dtmf} ${this.botName}`); + }); + } + } + + async _performHook(cs, hook, results) { + const json = await this.cs.requestor.request(hook, results); + if (json && Array.isArray(json)) { + const makeTask = require('./make_task'); + const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); + if (tasks && tasks.length > 0) { + this.logger.info({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`); + this.performAction({lexResult: 'redirect'}, false); + cs.replaceApplication(tasks); + } + } + } + +} + +module.exports = Lex; diff --git a/lib/tasks/make_task.js b/lib/tasks/make_task.js index 85cb6274..2d75aade 100644 --- a/lib/tasks/make_task.js +++ b/lib/tasks/make_task.js @@ -39,6 +39,9 @@ function makeTask(logger, obj, parent) { case TaskName.Leave: const TaskLeave = require('./leave'); return new TaskLeave(logger, data, parent); + case TaskName.Lex: + const TaskLex = require('./lex'); + return new TaskLex(logger, data, parent); case TaskName.Say: const TaskSay = require('./say'); return new TaskSay(logger, data, parent); diff --git a/lib/tasks/specs.json b/lib/tasks/specs.json index 597f4862..f4ca4a73 100644 --- a/lib/tasks/specs.json +++ b/lib/tasks/specs.json @@ -132,7 +132,8 @@ "noInputTimeout": "number", "noInputEvent": "string", "passDtmfAsTextInput": "boolean", - "thinkingMusic": "string" + "thinkingMusic": "string", + "tts": "#synthesizer" }, "required": [ "project", @@ -140,6 +141,30 @@ "lang" ] }, + "lex": { + "properties": { + "bot": "string", + "alias": "string", + "region": "string", + "bargein": "boolean", + "passDtmf": "boolean", + "actionHook": "object|string", + "eventHook": "object|string", + "events": "[string]", + "prompt": { + "type": "string", + "enum": ["lex", "tts"] + }, + "noInputTimeout": "number", + "tts": "#synthesizer" + }, + "required": [ + "bot", + "alias", + "region", + "prompt" + ] + }, "listen": { "properties": { "actionHook": "object|string", @@ -257,7 +282,7 @@ "properties": { "vendor": { "type": "string", - "enum": ["google", "aws", "polly"] + "enum": ["google", "aws", "polly", "default"] }, "language": "string", "voice": "string", diff --git a/lib/utils/constants.json b/lib/utils/constants.json index 50456251..8a27d357 100644 --- a/lib/utils/constants.json +++ b/lib/utils/constants.json @@ -8,6 +8,7 @@ "Gather": "gather", "Hangup": "hangup", "Leave": "leave", + "Lex": "lex", "Listen": "listen", "Pause": "pause", "Play": "play", From e1528da8b11085a5b283c21bc4c7f7d20fe421b8 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Fri, 18 Sep 2020 09:12:31 -0400 Subject: [PATCH 2/3] bugfix #21: multiple teams target --- lib/tasks/dial.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/tasks/dial.js b/lib/tasks/dial.js index 13a467a8..fac79b7c 100644 --- a/lib/tasks/dial.js +++ b/lib/tasks/dial.js @@ -37,12 +37,13 @@ function compareTasks(t1, t2) { if (t1.type !== t2.type) return false; switch (t1.type) { case 'phone': - return t1.number === t1.number; + return t1.number === t2.number; case 'user': + return t1.name === t2.name; case 'teams': - return t2.name === t1.name; + return t1.number === t2.number; case 'sip': - return t2.sipUri === t1.sipUri; + return t1.sipUri === t2.sipUri; } } From b47e4904243d8c7aeddc0edb058d663089969b3d Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Mon, 26 Oct 2020 09:59:10 -0400 Subject: [PATCH 3/3] updates for lex v2 --- lib/tasks/lex.js | 104 +++++++++++++++++++++++++------------------ lib/tasks/specs.json | 13 +++--- package-lock.json | 84 +++++++++++++++++----------------- package.json | 4 +- 4 files changed, 112 insertions(+), 93 deletions(-) diff --git a/lib/tasks/lex.js b/lib/tasks/lex.js index 811049ac..a8703220 100644 --- a/lib/tasks/lex.js +++ b/lib/tasks/lex.js @@ -7,10 +7,16 @@ class Lex extends Task { super(logger, opts); this.preconditions = TaskPreconditions.Endpoint; - this.credentials = this.data.credentials; - this.bot = this.data.bot; - this.alias = this.data.alias; + if (this.data.credentials) { + this.awsAccessKeyId = this.data.credentials.accessKey; + this.awsSecretAccessKey = this.data.credentials.secretAccessKey; + } + this.bot = this.data.botId; + this.alias = this.data.botAlias; this.region = this.data.region; + this.locale = this.data.locale || 'en_US'; + this.intent = this.data.intent; + this.welcomeMessage = this.data.welcomeMessage; this.bargein = this.data.bargein || false; this.passDtmf = this.data.passDtmf || false; if (this.data.noInputTimeout) this.noInputTimeout = this.data.noInputTimeout * 1000; @@ -22,12 +28,8 @@ class Lex extends Task { this.botName = `${this.bot}:${this.alias}:${this.region}`; if (this.data.eventHook) this.eventHook = this.data.eventHook; - if (this.eventHook && Array.isArray(this.data.events)) { - this.events = this.data.events; - } - else if (this.eventHook) { - // send all events by default - except interim transcripts - this.events = [ + this.events = this.eventHook ? + [ 'intent', 'transcription', 'dtmf', @@ -35,11 +37,7 @@ class Lex extends Task { 'stop-play', 'play-interrupted', 'response-text' - ]; - } - else { - this.events = []; - } + ] : []; if (this.data.actionHook) this.actionHook = this.data.actionHook; } @@ -51,10 +49,10 @@ class Lex extends Task { try { await this.init(cs, ep); - this.logger.debug(`starting lex bot ${this.botName}`); + this.logger.debug(`starting lex bot ${this.botName} with locale ${this.locale}`); // kick it off - this.ep.api('aws_lex_start', `${this.ep.uuid} ${this.bot} ${this.alias} ${this.region}`) + this.ep.api('aws_lex_start', `${this.ep.uuid} ${this.bot} ${this.alias} ${this.region} ${this.locale}`) .catch((err) => { this.logger.error({err}, `Error starting lex bot ${this.botName}`); this.notifyTaskDone(); @@ -102,12 +100,25 @@ class Lex extends Task { this.ep.addCustomEventListener('lex::error', this._onError.bind(this, ep, cs)); this.ep.on('dtmf', this._onDtmf.bind(this, ep, cs)); + const channelVars = {}; if (this.bargein) { - await this.ep.set('x-amz-lex:barge-in-enabled', 1); + Object.assign(channelVars, {'x-amz-lex:barge-in-enabled': 1}); } if (this.noInputTimeout) { - await this.ep.set('x-amz-lex:start-silence-threshold-ms', this.noInputTimeout); + Object.assign(channelVars, {'x-amz-lex:audio:start-timeout-ms': this.noInputTimeout}); } + if (this.awsAccessKeyId && this.awsSecretAccessKey) { + Object.assign(channelVars, { + AWS_ACCESS_KEY_ID: this.awsAccessKeyId, + AWS_SECRET_ACCESS_KEY: this.awsSecretAccessKey + }); + } + if (this.vendor) Object.assign(channelVars, {LEX_USE_TTS: 1}); + if (this.intent && this.intent.length) Object.assign(channelVars, {LEX_WELCOME_INTENT: this.intent}); + if (this.welcomeMessage && this.welcomeMessage.length) { + Object.assign(channelVars, {LEX_WELCOME_MESSAGE: this.welcomeMessage}); + } + if (Object.keys(channelVars).length) await this.ep.set(channelVars); } catch (err) { this.logger.error({err}, 'Error setting listeners'); @@ -149,37 +160,42 @@ class Lex extends Task { */ async _onTextResponse(ep, cs, evt) { this.logger.debug({evt}, `got text response for ${this.botName}`); + const messages = evt.messages; if (this.events.includes('response-text')) { this._performHook(cs, this.eventHook, {event: 'response-text', data: evt}); } - if (this.vendor && ['PlainText', 'SSML'].includes(evt.type) && evt.msg) { - const {srf} = cs; - const {synthAudio} = srf.locals.dbHelpers; + if (this.vendor && Array.isArray(messages) && messages.length) { + const msg = messages[0].msg; + const type = messages[0].type; + if (['PlainText', 'SSML'].includes(type) && msg) { + const {srf} = cs; + const {synthAudio} = srf.locals.dbHelpers; - try { - this.logger.debug(`tts with ${this.vendor} ${this.voice}`); - const fp = await synthAudio({ - text: evt.msg, - vendor: this.vendor, - language: this.language, - voice: this.voice, - salt: cs.callSid - }); - if (fp) cs.trackTmpFile(fp); - if (this.events.includes('start-play')) { - this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: fp}}); - } - await ep.play(fp); - if (this.events.includes('stop-play')) { - this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: fp}}); - } - this.logger.debug(`finished tts, sending play_done ${this.vendor} ${this.voice}`); - this.ep.api('aws_lex_play_done', this.ep.uuid) - .catch((err) => { - this.logger.error({err}, `Error sending play_done ${this.botName}`); + try { + this.logger.debug(`tts with ${this.vendor} ${this.voice}`); + const fp = await synthAudio({ + text: msg, + vendor: this.vendor, + language: this.language, + voice: this.voice, + salt: cs.callSid }); - } catch (err) { - this.logger.error({err}, 'Lex:_onTextResponse - error playing tts'); + if (fp) cs.trackTmpFile(fp); + if (this.events.includes('start-play')) { + this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: fp}}); + } + await ep.play(fp); + if (this.events.includes('stop-play')) { + this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: fp}}); + } + this.logger.debug(`finished tts, sending play_done ${this.vendor} ${this.voice}`); + this.ep.api('aws_lex_play_done', this.ep.uuid) + .catch((err) => { + this.logger.error({err}, `Error sending play_done ${this.botName}`); + }); + } catch (err) { + this.logger.error({err}, 'Lex:_onTextResponse - error playing tts'); + } } } } diff --git a/lib/tasks/specs.json b/lib/tasks/specs.json index 6bee22a1..0c610818 100644 --- a/lib/tasks/specs.json +++ b/lib/tasks/specs.json @@ -152,14 +152,17 @@ }, "lex": { "properties": { - "bot": "string", - "alias": "string", + "botId": "string", + "botAlias": "string", + "credentials": "object", "region": "string", + "locale": "string", + "intent": "string", + "welcomeMessage": "string", "bargein": "boolean", "passDtmf": "boolean", "actionHook": "object|string", "eventHook": "object|string", - "events": "[string]", "prompt": { "type": "string", "enum": ["lex", "tts"] @@ -168,8 +171,8 @@ "tts": "#synthesizer" }, "required": [ - "bot", - "alias", + "botId", + "botAlias", "region", "prompt" ] diff --git a/package-lock.json b/package-lock.json index dce753e3..3cfb91f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -376,12 +376,12 @@ } }, "@jambonz/realtimedb-helpers": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.2.17.tgz", - "integrity": "sha512-ferdg3ze4juzHKB8ZkR5s9c31V6YDZPwFQIoHWne2DG2ZhKL9dNwWyHBBjT0jdUjzWCGDM8Ly9o9VPbQC+fbOA==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.2.18.tgz", + "integrity": "sha512-OihQ6/mx8yXv8D8pVOnGWAe4VNiS7Iu7UGmFA6X/iVefs4DZAnlGsGcNpY6lxTqrPgirPF3tFQWY+4gQmhzKqg==", "requires": { "@google-cloud/text-to-speech": "^3.1.1", - "@jambonz/stats-collector": "0.0.4", + "@jambonz/stats-collector": "0.1.0", "aws-sdk": "^2.631.0", "bluebird": "^3.7.2", "debug": "^4.1.1", @@ -390,12 +390,12 @@ } }, "@jambonz/stats-collector": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@jambonz/stats-collector/-/stats-collector-0.0.4.tgz", - "integrity": "sha512-HgkdJmxNk0LcQ/eB6CTh9C+mxMnFhoR8ykY2CHnEa8IC8wymv2WqeIi9IZ/lKfzUVVzH+EpYqw7GBVghHjdYxQ==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@jambonz/stats-collector/-/stats-collector-0.1.0.tgz", + "integrity": "sha512-5ChS8x4ACAxXv0dNphV27bR3LKJk5wrkljpNW7xUWPmY4UpdGC5GhTlqlxumMYwBOy1N1nw3P50unQNTppm5Vw==", "requires": { - "debug": "^4.1.1", - "hot-shots": "^6.8.7" + "debug": "^4.2.0", + "hot-shots": "^8.2.0" } }, "@protobufjs/aspromise": { @@ -463,9 +463,9 @@ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "12.12.62", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.62.tgz", - "integrity": "sha512-qAfo81CsD7yQIM9mVyh6B/U47li5g7cfpVQEDMfQeF8pSZVwzbhwU3crc0qG4DmpsebpJPR49AKOExQyJ05Cpg==" + "version": "12.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.1.tgz", + "integrity": "sha512-/xaVmBBjOGh55WCqumLAHXU9VhjGtmyTGqJzFBXRWZzByOXI5JAJNx9xPVGEsNizrNwcec92fQMj458MWfjN1A==" }, "abort-controller": { "version": "3.0.0", @@ -497,9 +497,9 @@ "dev": true }, "agent-base": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", - "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "requires": { "debug": "4" } @@ -602,9 +602,9 @@ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" }, "aws-sdk": { - "version": "2.765.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.765.0.tgz", - "integrity": "sha512-FQdPKJ5LAhNxkpqwrjQ+hiEqEOezV/PfZBn5RcBG6vu8K3VuT4dE12mGTY3qkdHy7lhymeRS5rWcagTmabKFtA==", + "version": "2.778.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.778.0.tgz", + "integrity": "sha512-sIJRO7tMaztLs+gvHF/Wo+iek/rhH99+2OzharQJMS0HATPl5/EdhKgWGv1n/bNpVH+kD3n0QMQgdFu0FNUt0Q==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -1855,25 +1855,25 @@ } }, "google-auth-library": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.0.tgz", - "integrity": "sha512-GbalszIADE1YPWhUyfFMrkLhFHnlAgoRcqGVW+MsLDPsuaOB5MRPk7NNafPDv9SherNE4EKzcYuxMJjaxzXMOw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.2.tgz", + "integrity": "sha512-X9EUX8R+kIpsf55KdSPhFWF0RNyBGuBc1zeYc/5Sjuk65eIYqq91rINJVBD22pp+w/PuM2fasHiA6H2xYjxTIQ==", "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "fast-text-encoding": "^1.0.0", "gaxios": "^3.0.0", - "gcp-metadata": "^4.1.0", - "gtoken": "^5.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", "jws": "^4.0.0", "lru-cache": "^6.0.0" } }, "google-gax": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.9.0.tgz", - "integrity": "sha512-MFMwA7Fb8PEwjnYwfGXjZMidCNyMl3gSnvS/+kS8TQioJZQDpzK+W3dmwyNyig/U13+kbABqDnbkkAXJ5NiUkw==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.9.1.tgz", + "integrity": "sha512-KQ7HiMTB/PAzKv3OU00x6tC1H7MHvSxQfon5BSyW5o+lkMgRA8xoqvlxZCBC1dlW1azOPGF8vScy8QgFmhaQ9Q==", "requires": { "@grpc/grpc-js": "~1.1.1", "@grpc/proto-loader": "^0.5.1", @@ -1901,12 +1901,12 @@ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "gtoken": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.3.tgz", - "integrity": "sha512-Nyd1wZCMRc2dj/mAD0LlfQLcAO06uKdpKJXvK85SGrF5+5+Bpfil9u/2aw35ltvEHjvl0h5FMKN5knEU+9JrOg==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.4.tgz", + "integrity": "sha512-U9wnSp4GZ7ov6zRdPuRHG4TuqEWqRRgT1gfXGNArhzBUn9byrPeH8uTmBWU/ZiWJJvTEmkjhDIC3mqHWdVi3xQ==", "requires": { "gaxios": "^3.0.0", - "google-p12-pem": "^3.0.0", + "google-p12-pem": "^3.0.3", "jws": "^4.0.0", "mime": "^2.2.0" }, @@ -1967,9 +1967,9 @@ } }, "hot-shots": { - "version": "6.8.7", - "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-6.8.7.tgz", - "integrity": "sha512-XH8iezBSZgVw2jegu96pUfF1Zv0VZ/iXjb7L5yE3F7mn7/bdhf4qeniXjO0wQWeefe433rhOsazNKLxM+XMI9w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-8.2.0.tgz", + "integrity": "sha512-k0J7QAMGWAawZr8b+Bgornns9IlBaV1p0X6Ju/OTye2QLf+u1KuD9NEbSU4IShviyr1tSOUBUxrEm2QSlx2Nfw==", "requires": { "unix-dgram": "2.0.x" } @@ -2604,9 +2604,9 @@ } }, "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, "natural-compare": { @@ -2968,9 +2968,9 @@ }, "dependencies": { "@types/node": { - "version": "13.13.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.21.tgz", - "integrity": "sha512-tlFWakSzBITITJSxHV4hg4KvrhR/7h3xbJdSFbYJBVzKubrASbnnIFuSgolUh7qKGo/ZeJPKUfbZ0WS6Jp14DQ==" + "version": "13.13.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.28.tgz", + "integrity": "sha512-EM/qFeRH8ZCD+TlsaIPULyyFm9vOhFIvgskY2JmHbEsWsOPgN+rtjSXrcHGgJpob4Nu17VfO95FKewr0XY7iOQ==" } } }, @@ -3858,9 +3858,9 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unix-dgram": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.3.tgz", - "integrity": "sha512-Bay5CkSLcdypcBCsxvHEvaG3mftzT5FlUnRToPWEAVxwYI8NI/8zSJ/Gknlp86MPhV6hBA8I8TBsETj2tssoHQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.4.tgz", + "integrity": "sha512-7tpK6x7ls7J7pDrrAU63h93R0dVhRbPwiRRCawR10cl+2e1VOvF3bHlVJc6WI1dl/8qk5He673QU+Ogv7bPNaw==", "optional": true, "requires": { "bindings": "^1.3.0", diff --git a/package.json b/package.json index 48a4ece4..fe12e306 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ }, "dependencies": { "@jambonz/db-helpers": "^0.5.1", - "@jambonz/realtimedb-helpers": "^0.2.17", - "@jambonz/stats-collector": "^0.0.4", + "@jambonz/realtimedb-helpers": "^0.2.18", + "@jambonz/stats-collector": "^0.1.0", "bent": "^7.3.12", "cidr-matcher": "^2.1.1", "debug": "^4.2.0",