mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-07-04 19:32:01 +00:00
fixed listern verb terminated due to background task listen failure,teardown wrong handler as well (#1554)
This commit is contained in:
+1
-5
@@ -910,11 +910,7 @@ class TaskGather extends SttTask {
|
||||
const bugname = fsEvent.getHeader('media-bugname');
|
||||
const finished = fsEvent.getHeader('transcription-session-finished');
|
||||
this.logger.debug({evt, bugname, finished, vendor: this.vendor}, 'Gather:_onTranscription raw transcript');
|
||||
if (bugname && this.bugname !== bugname) {
|
||||
this.logger.info(
|
||||
`Gather:_onTranscription - ignoring transcript from ${bugname} because our bug is ${this.bugname}`);
|
||||
return;
|
||||
}
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
if (finished === 'true') return;
|
||||
|
||||
if (this.vendor === 'ibm' && evt?.state === 'listening') return;
|
||||
|
||||
+30
-24
@@ -67,6 +67,7 @@ class TaskListen extends Task {
|
||||
get name() { return TaskName.Listen; }
|
||||
|
||||
set bugname(name) { this._bugname = name; }
|
||||
get bugname() { return this._bugname; }
|
||||
|
||||
set ignoreCustomerData(val) { this._ignoreCustomerData = val; }
|
||||
|
||||
@@ -192,32 +193,31 @@ class TaskListen extends Task {
|
||||
}
|
||||
|
||||
_initListeners(ep) {
|
||||
ep.addCustomEventListener(ListenEvents.Connect, this._onConnect.bind(this, ep));
|
||||
ep.addCustomEventListener(ListenEvents.ConnectFailure, this._onConnectFailure.bind(this, ep));
|
||||
ep.addCustomEventListener(ListenEvents.Error, this._onError.bind(this, ep));
|
||||
/* Register via the base-class addCustomEventListener so handlers are tracked
|
||||
* and removed by reference. Multiple forks (e.g. this listen verb and a
|
||||
* background recording fork) share one endpoint and subscribe to the same
|
||||
* event names; removing by reference avoids tearing down the co-tenant
|
||||
* task's handlers (removing with no handler would call removeAllListeners
|
||||
* and unsubscribe the event for everyone). */
|
||||
this.addCustomEventListener(ep, ListenEvents.Connect, this._onConnect.bind(this, ep));
|
||||
this.addCustomEventListener(ep, ListenEvents.ConnectFailure, this._onConnectFailure.bind(this, ep));
|
||||
this.addCustomEventListener(ep, ListenEvents.Error, this._onError.bind(this, ep));
|
||||
this.addCustomEventListener(ep, ListenEvents.KillAudio, this._onKillAudio.bind(this, ep));
|
||||
this.addCustomEventListener(ep, ListenEvents.Disconnect, this._onDisconnect.bind(this, ep));
|
||||
/* support bi-directional audio */
|
||||
if (this.bidirectionalAudio.enabled) {
|
||||
this.addCustomEventListener(ep, ListenEvents.PlayAudio, this._onPlayAudio.bind(this, ep));
|
||||
}
|
||||
if (this.finishOnKey || this.passDtmf) {
|
||||
ep.on('dtmf', this._dtmfHandler);
|
||||
}
|
||||
|
||||
/* support bi-directional audio */
|
||||
if (this.bidirectionalAudio.enabled) {
|
||||
ep.addCustomEventListener(ListenEvents.PlayAudio, this._onPlayAudio.bind(this, ep));
|
||||
}
|
||||
ep.addCustomEventListener(ListenEvents.KillAudio, this._onKillAudio.bind(this, ep));
|
||||
ep.addCustomEventListener(ListenEvents.Disconnect, this._onDisconnect.bind(this, ep));
|
||||
}
|
||||
|
||||
_removeListeners(ep) {
|
||||
ep.removeCustomEventListener(ListenEvents.Connect);
|
||||
ep.removeCustomEventListener(ListenEvents.ConnectFailure);
|
||||
ep.removeCustomEventListener(ListenEvents.Error);
|
||||
this.removeCustomEventListeners(ep);
|
||||
if (this.finishOnKey || this.passDtmf) {
|
||||
ep.removeListener('dtmf', this._dtmfHandler);
|
||||
}
|
||||
ep.removeCustomEventListener(ListenEvents.PlayAudio);
|
||||
ep.removeCustomEventListener(ListenEvents.KillAudio);
|
||||
ep.removeCustomEventListener(ListenEvents.Disconnect);
|
||||
|
||||
}
|
||||
|
||||
_onDtmf(ep, evt) {
|
||||
@@ -253,10 +253,12 @@ class TaskListen extends Task {
|
||||
this._timer = null;
|
||||
}
|
||||
}
|
||||
_onConnect(ep) {
|
||||
_onConnect(ep, evt, fsEvent) {
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
this.logger.info('TaskListen:_onConnect');
|
||||
}
|
||||
_onConnectFailure(ep, evt) {
|
||||
_onConnectFailure(ep, evt, fsEvent) {
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
this.logger.info(evt, 'TaskListen:_onConnectFailure');
|
||||
this.notifyTaskDone();
|
||||
}
|
||||
@@ -279,7 +281,8 @@ class TaskListen extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
async _onPlayAudio(ep, evt) {
|
||||
async _onPlayAudio(ep, evt, fsEvent) {
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
this.logger.info(`received play_audio event: ${JSON.stringify(evt)}`);
|
||||
if (!evt.queuePlay) {
|
||||
this.playAudioQueue = [];
|
||||
@@ -301,17 +304,20 @@ class TaskListen extends Task {
|
||||
this.isPlayingAudioFromQueue = false;
|
||||
}
|
||||
|
||||
_onKillAudio(ep) {
|
||||
_onKillAudio(ep, evt, fsEvent) {
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
this.logger.info('received kill_audio event');
|
||||
ep.api('uuid_break', ep.uuid);
|
||||
}
|
||||
|
||||
_onDisconnect(ep, cs) {
|
||||
_onDisconnect(ep, evt, fsEvent) {
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
this.logger.debug('_onDisconnect: TaskListen terminating task');
|
||||
this.kill(cs);
|
||||
this.kill();
|
||||
}
|
||||
|
||||
_onError(ep, evt) {
|
||||
_onError(ep, evt, fsEvent) {
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
this.logger.info(evt, 'TaskListen:_onError');
|
||||
this.notifyTaskDone();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ class SttTask extends Task {
|
||||
this.compileSonioxTranscripts = compileSonioxTranscripts;
|
||||
this.consolidateTranscripts = consolidateTranscripts;
|
||||
this.updateSpeechmaticsPayload = updateSpeechmaticsPayload;
|
||||
this.eventHandlers = [];
|
||||
this.isHandledByPrimaryProvider = true;
|
||||
/**
|
||||
* Task use taskIncludeRecognizer to identify
|
||||
@@ -245,26 +244,6 @@ class SttTask extends Task {
|
||||
return {host, path: `${pathname}${search}`};
|
||||
}
|
||||
|
||||
addCustomEventListener(ep, event, handler) {
|
||||
this.eventHandlers.push({ep, event, handler});
|
||||
ep.addCustomEventListener(event, handler);
|
||||
}
|
||||
|
||||
removeCustomEventListeners(ep) {
|
||||
if (ep) {
|
||||
// for specific endpoint
|
||||
this.eventHandlers.filter((h) => h.ep === ep).forEach((h) => {
|
||||
h.ep.removeCustomEventListener(h.event, h.handler);
|
||||
});
|
||||
this.eventHandlers = this.eventHandlers.filter((h) => h.ep !== ep);
|
||||
return;
|
||||
} else {
|
||||
// for all endpoints
|
||||
this.eventHandlers.forEach((h) => h.ep.removeCustomEventListener(h.event, h.handler));
|
||||
this.eventHandlers = [];
|
||||
}
|
||||
}
|
||||
|
||||
async _initSpeechCredentials(cs, vendor, label) {
|
||||
const {getNuanceAccessToken, getIbmAccessToken, getAwsAuthToken, getVerbioAccessToken} = cs.srf.locals.dbHelpers;
|
||||
let credentials = cs.getSpeechCredentials(vendor, 'stt', label);
|
||||
|
||||
@@ -24,6 +24,10 @@ class Task extends Emitter {
|
||||
this._killInProgress = false;
|
||||
this._completionPromise = new Promise((resolve) => this._completionResolver = resolve);
|
||||
|
||||
/* tracks {ep, event, handler} for custom event listeners so they can be
|
||||
* removed by reference (see addCustomEventListener/removeCustomEventListeners) */
|
||||
this.eventHandlers = [];
|
||||
|
||||
/* used when we play a prompt to a member in conference */
|
||||
this._confPlayCompletionPromise = new Promise((resolve) => this._confPlayCompletionResolver = resolve);
|
||||
}
|
||||
@@ -103,6 +107,67 @@ class Task extends Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether a FreeSWITCH media-bug custom event belongs to this task.
|
||||
*
|
||||
* Several FreeSWITCH modules (mod_audio_fork, mod_audio_stream, ...) fire the
|
||||
* same custom event names for *every* media bug on a shared endpoint. When more
|
||||
* than one bug runs on a call (e.g. a listen verb plus a background recording
|
||||
* fork, or transcription plus answering-machine detection) each task must only
|
||||
* act on events for its own bug, identified by the 'media-bugname' header.
|
||||
*
|
||||
* Fails open (returns true) when the header is absent or this task has no
|
||||
* bugname, preserving single-bug behavior and tolerating older FreeSWITCH
|
||||
* builds that don't yet stamp the header.
|
||||
*
|
||||
* @param {object} fsEvent - raw FreeSWITCH event (has getHeader)
|
||||
* @returns {boolean} true if the event is for this task's bug
|
||||
*/
|
||||
eventIsForOurBug(fsEvent) {
|
||||
const bugname = fsEvent?.getHeader?.('media-bugname');
|
||||
if (bugname && this.bugname && bugname !== this.bugname) {
|
||||
this.logger.debug(
|
||||
`${this.name}: ignoring media-bug event for ${bugname}, ours is ${this.bugname}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a FreeSWITCH custom event on an endpoint, tracking the handler
|
||||
* so it can later be removed by reference. Endpoints can be shared by several
|
||||
* tasks (e.g. a listen verb and a background recording fork), so listeners must
|
||||
* be removed individually — removing without a handler reference would call
|
||||
* removeAllListeners and tear down co-tenant tasks' handlers.
|
||||
* @param {object} ep - the endpoint
|
||||
* @param {string} event - the custom event name
|
||||
* @param {function} handler - the (already-bound) listener
|
||||
*/
|
||||
addCustomEventListener(ep, event, handler) {
|
||||
this.eventHandlers.push({ep, event, handler});
|
||||
ep.addCustomEventListener(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the custom event listeners this task registered. With an endpoint,
|
||||
* removes only listeners for that endpoint; without one, removes all.
|
||||
* @param {object} [ep] - the endpoint, or undefined for all endpoints
|
||||
*/
|
||||
removeCustomEventListeners(ep) {
|
||||
if (ep) {
|
||||
// for specific endpoint
|
||||
this.eventHandlers.filter((h) => h.ep === ep).forEach((h) => {
|
||||
h.ep.removeCustomEventListener(h.event, h.handler);
|
||||
});
|
||||
this.eventHandlers = this.eventHandlers.filter((h) => h.ep !== ep);
|
||||
return;
|
||||
} else {
|
||||
// for all endpoints
|
||||
this.eventHandlers.forEach((h) => h.ep.removeCustomEventListener(h.event, h.handler));
|
||||
this.eventHandlers = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* when a subclass Task has completed its work, it should call this method
|
||||
*/
|
||||
|
||||
@@ -488,10 +488,9 @@ class TaskTranscribe extends SttTask {
|
||||
ep.gracefulShutdownResolver();
|
||||
}
|
||||
// make sure this is not a transcript from answering machine detection
|
||||
const bugname = fsEvent.getHeader('media-bugname');
|
||||
const finished = fsEvent.getHeader('transcription-session-finished');
|
||||
const bufferedTranscripts = this._bufferedTranscripts[channel - 1];
|
||||
if (bugname && this.bugname !== bugname) return;
|
||||
if (!this.eventIsForOurBug(fsEvent)) return;
|
||||
if (this.paused) {
|
||||
this.logger.debug({evt}, 'TaskTranscribe:_onTranscription - paused, ignoring transcript');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user