From d15fdcf6639df10f58d300357bd07694a875b445 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Thu, 2 Sep 2021 10:27:59 -0400 Subject: [PATCH] rasa: add support for eventhook which provides user and bot messages in realtime and supports redirecting to a new app --- lib/middleware.js | 2 +- lib/tasks/rasa.js | 59 +++++++++++++++++++++++++++++++++++++++++------ lib/tasks/task.js | 19 +++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/lib/middleware.js b/lib/middleware.js index 4c190c77..8af25ab2 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -45,7 +45,7 @@ module.exports = function(srf, logger) { // TODO: alert return res.send(503, {headers: {'X-Reason': 'Account exists but is inactive'}}); } - logger.debug({accountInfo: req.locals.accountInfo}, `retrieved account info for ${account_sid}`); + logger.debug({accountInfo: req.locals?.accountInfo?.account}, `retrieved account info for ${account_sid}`); next(); } catch (err) { logger.info({err}, `Error retrieving account details for account ${account_sid}`); diff --git a/lib/tasks/rasa.js b/lib/tasks/rasa.js index aeb916c1..78aa73e7 100644 --- a/lib/tasks/rasa.js +++ b/lib/tasks/rasa.js @@ -16,6 +16,10 @@ class Rasa extends Task { get name() { return TaskName.Rasa; } + get hasReportedFinalAction() { + return this.reportedFinalAction || this.isReplacingApplication; + } + async exec(cs, ep) { await super.exec(cs); @@ -39,12 +43,15 @@ class Rasa extends Task { async kill(cs) { super.kill(cs); - if (this.ep.connected) { - this.logger.debug('Rasa:kill'); + this.logger.debug('Rasa:kill'); + if (!this.hasReportedFinalAction) { + this.reportedFinalAction = true; this.performAction({rasaResult: 'caller hungup'}) - .catch((err) => this.logger.error({err}, 'rasa - error w/ action webook')); + .catch((err) => this.logger.info({err}, 'rasa - error w/ action webook')); + } + if (this.ep.connected) { await this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); } this.removeAllListeners(); @@ -70,14 +77,31 @@ class Rasa extends Task { say: sayOpts }; } - this.logger.debug({opts}, 'constructing a nested gather object'); + //this.logger.debug({opts}, 'constructing a nested gather object'); const gather = makeTask(this.logger, {gather: opts}, this); return gather; } async _onTranscription(cs, ep, evt) { - this.logger.debug({evt}, `Rasa: got transcription for callSid ${cs.callSid}`); + //this.logger.debug({evt}, `Rasa: got transcription for callSid ${cs.callSid}`); const utterance = evt.alternatives[0].transcript; + + if (this.eventHook) { + this.performHook(cs, this.eventHook, {event: 'userMessage', message: utterance}) + .then((redirected) => { + if (redirected) { + this.logger.info('Rasa_onTranscription: event handler for user message redirected us to new webhook'); + this.reportedFinalAction = true; + this.performAction({rasaResult: 'redirect'}, false); + if (this.gatherTask) this.gatherTask.kill(cs); + } + return; + }) + .catch(({err}) => { + this.logger.info({err}, 'Rasa_onTranscription: error sending event hook'); + }); + } + try { const payload = { sender: cs.callSid, @@ -90,17 +114,38 @@ class Rasa extends Task { response.reduce((prev, current) => `${prev} ${current.text}`, '') : null; if (botUtterance) { - this.logger.debug({botUtterance}, 'playing out bot utterance'); + this.logger.debug({botUtterance}, 'Rasa:_onTranscription: got user utterance'); this.gatherTask = this._makeGatherTask(botUtterance); this.gatherTask.exec(cs, ep, this) .catch((err) => this.logger.info({err}, 'Rasa gather task returned error')); + if (this.eventHook) { + this.performHook(cs, this.eventHook, {event: 'botMessage', message: botUtterance}) + .then((redirected) => { + if (redirected) { + this.logger.info('Rasa_onTranscription: event handler for bot message redirected us to new webhook'); + this.reportedFinalAction = true; + this.performAction({rasaResult: 'redirect'}, false); + if (this.gatherTask) this.gatherTask.kill(cs); + } + return; + }) + .catch(({err}) => { + this.logger.info({err}, 'Rasa_onTranscription: error sending event hook'); + }); + } } } catch (err) { - this.logger.error({err}, 'Error sending'); + this.logger.error({err}, 'Rasa_onTranscription: Error sending user utterance to Rasa - ending task'); + this.performAction({rasaResult: 'webhookError'}); + this.reportedFinalAction = true; + this.notifyTaskDone(); } } _onTimeout(cs, ep, evt) { this.logger.debug({evt}, 'Rasa: got timeout'); + if (!this.hasReportedFinalAction) this.performAction({rasaResult: 'timeout'}); + this.reportedFinalAction = true; + this.notifyTaskDone(); } diff --git a/lib/tasks/task.js b/lib/tasks/task.js index d7c0f80e..fab39900 100644 --- a/lib/tasks/task.js +++ b/lib/tasks/task.js @@ -99,6 +99,25 @@ class Task extends Emitter { } } + async performHook(cs, hook, results) { + const json = await 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.redirect(cs, tasks); + return true; + } + } + return false; + } + + redirect(cs, tasks) { + this.logger.info({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`); + this.isReplacingApplication = true; + cs.replaceApplication(tasks); + } + async transferCallToFeatureServer(cs, sipAddress, opts) { const uuid = uuidv4(); const {addKey} = cs.srf.locals.dbHelpers;