mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 08:40:38 +00:00
major refactor and simplification of actionHookDelay feature (#771)
* major refactor and simplification of actionHookDelay feature * wip for #765 * wip * testing * wip * added validity checks for actionHookDelay properties * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * fix bug where config happens before endpoint is established * wip * hangup and clear ws connection if nogiveuptimer expires * wip * wip * wip
This commit is contained in:
@@ -19,6 +19,7 @@ const { normalizeJambones } = require('@jambonz/verb-specifications');
|
||||
const listTaskNames = require('../utils/summarize-tasks');
|
||||
const HttpRequestor = require('../utils/http-requestor');
|
||||
const WsRequestor = require('../utils/ws-requestor');
|
||||
const ActionHookDelayProcessor = require('../utils/action-hook-delay');
|
||||
const {
|
||||
JAMBONES_INJECT_CONTENT,
|
||||
JAMBONES_EAGERLY_PRE_CACHE_AUDIO,
|
||||
@@ -519,13 +520,6 @@ class CallSession extends Emitter {
|
||||
this._actionHookDelayRetries = e;
|
||||
}
|
||||
|
||||
get actionHookDelayActions() {
|
||||
return this._actionHookDelayActions;
|
||||
}
|
||||
|
||||
set actionHookDelayActions(e) {
|
||||
this._actionHookDelayActions = e;
|
||||
}
|
||||
// Getter/setter for current tts vendor
|
||||
get currentTtsVendor() {
|
||||
return this._currentTtsVendor;
|
||||
@@ -535,6 +529,71 @@ class CallSession extends Emitter {
|
||||
this._currentTtsVendor = vendor;
|
||||
}
|
||||
|
||||
get actionHookDelayProcessor() {
|
||||
return this._actionHookDelayProcessor;
|
||||
}
|
||||
|
||||
set actionHookDelayProperties(opts) {
|
||||
if (this._actionHookDelayProcessor) {
|
||||
this._actionHookDelayProcessor.stop();
|
||||
if (!this._actionHookDelayProcessor.init(opts)) {
|
||||
this._actionHookDelayProcessor.removeAllListeners();
|
||||
this._actionHookDelayProcessor = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
this._actionHookDelayProcessor = new ActionHookDelayProcessor(this.logger, opts, this, this.ep);
|
||||
this._actionHookDelayProcessor.on('giveup', () => {
|
||||
this.logger.info('CallSession: ActionHookDelayProcessor: giveup event - hanging up call');
|
||||
this._jambonzHangup();
|
||||
if (this.wakeupResolver) {
|
||||
this.logger.debug('CallSession: Giveup timer expired - waking up');
|
||||
this.wakeupResolver({reason: 'noResponseGiveUp'});
|
||||
this.wakeupResolver = null;
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'CallSession: Error creating ActionHookDelayProcessor');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async clearOrRestoreActionHookDelayProcessor() {
|
||||
if (this._actionHookDelayProcessor) {
|
||||
await this._actionHookDelayProcessor.stop();
|
||||
if (!this.popActionHookDelayProperties()) {
|
||||
//this.logger.debug('CallSession:clearOrRestoreActionHookDelayProcessor - ahd settings');
|
||||
//await this.clearActionHookDelayProcessor();
|
||||
}
|
||||
else {
|
||||
this.logger.debug('CallSession:clearOrRestoreActionHookDelayProcessor - restore ahd after gather override');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async clearActionHookDelayProcessor() {
|
||||
if (this._actionHookDelayProcessor) {
|
||||
await this._actionHookDelayProcessor.stop();
|
||||
this._actionHookDelayProcessor.removeAllListeners();
|
||||
this._actionHookDelayProcessor = null;
|
||||
}
|
||||
}
|
||||
|
||||
stashActionHookDelayProperties() {
|
||||
this._storedActionHookDelayProperties = this._actionHookDelayProcessor.properties;
|
||||
}
|
||||
|
||||
popActionHookDelayProperties() {
|
||||
if (this._storedActionHookDelayProperties) {
|
||||
this._actionHookDelayProcessor.init(this._storedActionHookDelayProperties);
|
||||
this._storedActionHookDelayProperties = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
hasGlobalSttPunctuation() {
|
||||
return this._globalSttPunctuation !== undefined;
|
||||
}
|
||||
@@ -961,7 +1020,6 @@ class CallSession extends Emitter {
|
||||
task.on('VerbHookSpanWaitForEnd', ({span}) => {
|
||||
this.verbHookSpan = span;
|
||||
});
|
||||
task.on('ActionHookDelayActionOptions', this._onActionHookDelayActions.bind(this));
|
||||
try {
|
||||
const resources = await this._evaluatePreconditions(task);
|
||||
let skip = false;
|
||||
@@ -1007,6 +1065,10 @@ class CallSession extends Emitter {
|
||||
) {
|
||||
try {
|
||||
await this._awaitCommandsOrHangup();
|
||||
|
||||
await this.clearOrRestoreActionHookDelayProcessor();
|
||||
|
||||
//TODO: remove filler noise code and simply create as action hook delay
|
||||
if (this._isPlayingFillerNoise) {
|
||||
this._isPlayingFillerNoise = false;
|
||||
this.ep.api('uuid_break', this.ep.uuid)
|
||||
@@ -1237,9 +1299,8 @@ class CallSession extends Emitter {
|
||||
this.currentTask.kill(this);
|
||||
}
|
||||
this._endVerbHookSpan();
|
||||
// clear all delay action hook timeout if there is
|
||||
this._clearActionHookNoResponseGiveUpTimer();
|
||||
this._clearActionHookNoResponseTimer();
|
||||
|
||||
await this.clearOrRestoreActionHookDelayProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1753,7 +1814,7 @@ Duration=${duration} `
|
||||
this._jambonzHangup();
|
||||
}
|
||||
|
||||
_onCommand({msgid, command, call_sid, queueCommand, data}) {
|
||||
async _onCommand({msgid, command, call_sid, queueCommand, data}) {
|
||||
this.logger.info({msgid, command, queueCommand, data}, 'CallSession:_onCommand - received command');
|
||||
let resolution;
|
||||
switch (command) {
|
||||
@@ -1778,9 +1839,9 @@ Duration=${duration} `
|
||||
}
|
||||
resolution = {reason: 'received command, new tasks', queue: queueCommand, command};
|
||||
resolution.command = listTaskNames(t);
|
||||
|
||||
// clear all delay action hook timeout if there is
|
||||
this._clearActionHookNoResponseGiveUpTimer();
|
||||
this._clearActionHookNoResponseTimer();
|
||||
await this.clearOrRestoreActionHookDelayProcessor();
|
||||
}
|
||||
else this._lccCallHook(data);
|
||||
break;
|
||||
@@ -2040,8 +2101,7 @@ Duration=${duration} `
|
||||
this.rootSpan && this.rootSpan.end();
|
||||
// close all background tasks
|
||||
this.backgroundTaskManager.stopAll();
|
||||
this._clearActionHookNoResponseGiveUpTimer();
|
||||
this._clearActionHookNoResponseTimer();
|
||||
this.clearOrRestoreActionHookDelayProcessor().catch((err) => {});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2450,6 +2510,16 @@ Duration=${duration} `
|
||||
this.logger.info('_awaitCommandsOrHangup - waiting...');
|
||||
this.wakeupResolver = resolve;
|
||||
|
||||
if (this._actionHookDelayProcessor) {
|
||||
this._actionHookDelayProcessor.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: filler noise can be handled as an ActionHookDelayProcessor -
|
||||
* it's just one specific scenario for action hook delay -
|
||||
* remove the code below and simply implement filler noise as an action hook delay
|
||||
*/
|
||||
|
||||
/* start filler noise if configured while we wait for new commands */
|
||||
if (this.fillerNoise?.url && this.ep?.connected && !this.ep2) {
|
||||
this.logger.debug('CallSession:_awaitCommandsOrHangup - playing filler noise');
|
||||
@@ -2494,81 +2564,6 @@ Duration=${duration} `
|
||||
this.verbHookSpan = null;
|
||||
}
|
||||
}
|
||||
// actionHook delay actions
|
||||
_onActionHookDelayActions(options) {
|
||||
this._actionHookDelayRetryCount = 0;
|
||||
this._startActionHookNoResponseTimer(options);
|
||||
this._startActionHookNoResponseGiveUpTimer(options);
|
||||
}
|
||||
|
||||
_startActionHookNoResponseTimer(options) {
|
||||
this._clearActionHookNoResponseTimer();
|
||||
this._actionHookDelayResolved = false;
|
||||
if (options.noResponseTimeoutMs) {
|
||||
this.logger.debug(`CallSession:_startActionHookNoResponseTimer ${options.noResponseTimeoutMs}`);
|
||||
this._actionHookNoResponseTimer = setTimeout(() => {
|
||||
if (this._actionHookDelayRetryCount >= options.retries) {
|
||||
this._jambonzHangup();
|
||||
}
|
||||
const verb = options.actions[this._actionHookDelayRetryCount % options.actions.length];
|
||||
// Inject verb to main stack
|
||||
const t = normalizeJambones(this.logger, [verb])
|
||||
.map((tdata) => makeTask(this.logger, tdata));
|
||||
if (t.length) {
|
||||
t[0].on('playDone', (err) => {
|
||||
if (err) this.logger.error({err}, `Call-Session:exec Error delay action, play ${verb}`);
|
||||
if (!this._actionHookDelayResolved) {
|
||||
this._startActionHookNoResponseTimer(options);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.tasks.push(...t);
|
||||
if (this.wakeupResolver) {
|
||||
this.wakeupResolver({reason: 'actionHook no response, applied delay actions', verb});
|
||||
this.wakeupResolver = null;
|
||||
}
|
||||
|
||||
this.logger.debug(`CallSession:_startActionHookNoResponseTimer, executing verb ${JSON.stringify(verb)}`);
|
||||
|
||||
this._actionHookDelayRetryCount++;
|
||||
}, options.noResponseTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
_clearActionHookNoResponseTimer() {
|
||||
if (this._actionHookNoResponseTimer) {
|
||||
// Action Hook delay is solved.
|
||||
this._actionHookDelayResolved = true;
|
||||
clearTimeout(this._actionHookNoResponseTimer);
|
||||
// if delay action is enabled
|
||||
// and bot has responded with list of new verbs
|
||||
// Only kill current running play task.
|
||||
//https://github.com/jambonz/jambonz-feature-server/issues/710
|
||||
if (this.currentTask?.name === TaskName.Play) {
|
||||
this.currentTask.kill(this);
|
||||
}
|
||||
}
|
||||
this._actionHookNoResponseTimer = null;
|
||||
}
|
||||
|
||||
_startActionHookNoResponseGiveUpTimer(options) {
|
||||
this._clearActionHookNoResponseGiveUpTimer();
|
||||
if (options.noResponseGiveUpTimeoutMs) {
|
||||
this.logger.debug(`CallSession:_startActionHookNoResponseGiveUpTimer ${options.noResponseGiveUpTimeoutMs}`);
|
||||
this._actionHookNoResponseGiveUpTimer = setTimeout(() => {
|
||||
this.logger.debug('CallSession:_startActionHookNoResponseGiveUpTimer Timeout');
|
||||
this._jambonzHangup();
|
||||
this._actionHookNoResponseGiveUpTimer = null;
|
||||
}, options.noResponseGiveUpTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
_clearActionHookNoResponseGiveUpTimer() {
|
||||
if (this._actionHookNoResponseGiveUpTimer) {
|
||||
clearTimeout(this._actionHookNoResponseGiveUpTimer);
|
||||
}
|
||||
this._actionHookNoResponseGiveUpTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CallSession;
|
||||
|
||||
@@ -71,7 +71,7 @@ class InboundCallSession extends CallSession {
|
||||
}
|
||||
|
||||
_jambonzHangup() {
|
||||
this._hangup();
|
||||
this.dlg?.destroy();
|
||||
}
|
||||
|
||||
_hangup(terminatedBy = 'jambonz') {
|
||||
|
||||
Reference in New Issue
Block a user