fixed dial transcribe is not able to receive final transcribe when closing the call (#1073)

* fixed dial transcribe is not able to received final transcribe when close call.

* wip

* fix review comment

* support call session delay detroy ep when current task is transcribe

* wip

* wip

* fixed review comments

* fixed review comments
This commit is contained in:
Hoan Luu Huu
2025-02-27 19:25:01 +07:00
committed by GitHub
parent cd2563ce17
commit af4e17f447
9 changed files with 58 additions and 14 deletions

View File

@@ -139,6 +139,9 @@ const JAMBONES_USE_FREESWITCH_TIMER_FD = process.env.JAMBONES_USE_FREESWITCH_TIM
const JAMBONES_DIAL_SBC_FOR_REGISTERED_USER = process.env.JAMBONES_DIAL_SBC_FOR_REGISTERED_USER || false; const JAMBONES_DIAL_SBC_FOR_REGISTERED_USER = process.env.JAMBONES_DIAL_SBC_FOR_REGISTERED_USER || false;
const JAMBONES_MEDIA_TIMEOUT_MS = process.env.JAMBONES_MEDIA_TIMEOUT_MS || 0; const JAMBONES_MEDIA_TIMEOUT_MS = process.env.JAMBONES_MEDIA_TIMEOUT_MS || 0;
const JAMBONES_MEDIA_HOLD_TIMEOUT_MS = process.env.JAMBONES_MEDIA_HOLD_TIMEOUT_MS || 0; const JAMBONES_MEDIA_HOLD_TIMEOUT_MS = process.env.JAMBONES_MEDIA_HOLD_TIMEOUT_MS || 0;
// jambonz
const JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS =
process.env.JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS;
module.exports = { module.exports = {
JAMBONES_MYSQL_HOST, JAMBONES_MYSQL_HOST,
@@ -227,5 +230,6 @@ module.exports = {
JAMBONES_USE_FREESWITCH_TIMER_FD, JAMBONES_USE_FREESWITCH_TIMER_FD,
JAMBONES_DIAL_SBC_FOR_REGISTERED_USER, JAMBONES_DIAL_SBC_FOR_REGISTERED_USER,
JAMBONES_MEDIA_TIMEOUT_MS, JAMBONES_MEDIA_TIMEOUT_MS,
JAMBONES_MEDIA_HOLD_TIMEOUT_MS JAMBONES_MEDIA_HOLD_TIMEOUT_MS,
JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS
}; };

View File

@@ -30,7 +30,8 @@ const {
AWS_REGION, AWS_REGION,
JAMBONES_USE_FREESWITCH_TIMER_FD, JAMBONES_USE_FREESWITCH_TIMER_FD,
JAMBONES_MEDIA_TIMEOUT_MS, JAMBONES_MEDIA_TIMEOUT_MS,
JAMBONES_MEDIA_HOLD_TIMEOUT_MS JAMBONES_MEDIA_HOLD_TIMEOUT_MS,
JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS
} = require('../config'); } = require('../config');
const bent = require('bent'); const bent = require('bent');
const BackgroundTaskManager = require('../utils/background-task-manager'); const BackgroundTaskManager = require('../utils/background-task-manager');
@@ -38,6 +39,7 @@ const dbUtils = require('../utils/db-utils');
const BADPRECONDITIONS = 'preconditions not met'; const BADPRECONDITIONS = 'preconditions not met';
const CALLER_CANCELLED_ERR_MSG = 'Response not sent due to unknown transaction'; const CALLER_CANCELLED_ERR_MSG = 'Response not sent due to unknown transaction';
const { NonFatalTaskError} = require('../utils/error'); const { NonFatalTaskError} = require('../utils/error');
const { sleepFor } = require('../utils/helpers');
const sqlRetrieveQueueEventHook = `SELECT * FROM webhooks const sqlRetrieveQueueEventHook = `SELECT * FROM webhooks
WHERE webhook_sid = WHERE webhook_sid =
( (
@@ -2840,6 +2842,24 @@ Duration=${duration} `
if (Object.keys(opts).length > 0) { if (Object.keys(opts).length > 0) {
this.ep.set(opts); this.ep.set(opts);
} }
const origDestroy = this.ep.destroy.bind(this.ep);
this.ep.destroy = async() => {
try {
if (this.currentTask?.name === TaskName.Transcribe && JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS) {
// transcribe task is being used, wait for some time before destroy
// if final transcription is received but endpoint is already closed,
// freeswitch module will not be able to send the transcription
this.logger.info('callSession:_configMsEndpoint -' +
' transcribe task, wait for some time before destroy');
await sleepFor(JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS);
}
await origDestroy();
} catch (err) {
this.logger.error(err, 'callSession:_configMsEndpoint - error destroying endpoint');
}
};
} }
async _handleMediaTimeout(evt) { async _handleMediaTimeout(evt) {

View File

@@ -3,8 +3,7 @@ const {TaskName, TaskPreconditions, DequeueResults, BONG_TONE} = require('../uti
const Emitter = require('events'); const Emitter = require('events');
const bent = require('bent'); const bent = require('bent');
const assert = require('assert'); const assert = require('assert');
const { sleepFor } = require('../utils/helpers');
const sleepFor = (ms) => new Promise((resolve) => setTimeout(() => resolve(), ms));
const getUrl = (cs) => `${cs.srf.locals.serviceUrl}/v1/dequeue/${cs.callSid}`; const getUrl = (cs) => `${cs.srf.locals.serviceUrl}/v1/dequeue/${cs.callSid}`;

View File

@@ -24,6 +24,7 @@ const {ANCHOR_MEDIA_ALWAYS,
const { isOnhold, isOpusFirst } = require('../utils/sdp-utils'); const { isOnhold, isOpusFirst } = require('../utils/sdp-utils');
const { normalizeJambones } = require('@jambonz/verb-specifications'); const { normalizeJambones } = require('@jambonz/verb-specifications');
const { selectHostPort } = require('../utils/network'); const { selectHostPort } = require('../utils/network');
const { sleepFor } = require('../utils/helpers');
function parseDtmfOptions(logger, dtmfCapture) { function parseDtmfOptions(logger, dtmfCapture) {
let parentDtmfCollector, childDtmfCollector; let parentDtmfCollector, childDtmfCollector;
@@ -86,8 +87,6 @@ function filterAndLimit(logger, tasks) {
return unique; return unique;
} }
const sleepFor = (ms) => new Promise((resolve) => setTimeout(() => resolve(), ms));
class TaskDial extends Task { class TaskDial extends Task {
constructor(logger, opts) { constructor(logger, opts) {
super(logger, opts); super(logger, opts);

5
lib/utils/helpers.js Normal file
View File

@@ -0,0 +1,5 @@
const sleepFor = (ms) => new Promise((resolve) => setTimeout(() => resolve(), ms));
module.exports = {
sleepFor
};

View File

@@ -19,8 +19,10 @@ const {makeOpusFirst} = require('./sdp-utils');
const { const {
JAMBONES_USE_FREESWITCH_TIMER_FD, JAMBONES_USE_FREESWITCH_TIMER_FD,
JAMBONES_MEDIA_TIMEOUT_MS, JAMBONES_MEDIA_TIMEOUT_MS,
JAMBONES_MEDIA_HOLD_TIMEOUT_MS JAMBONES_MEDIA_HOLD_TIMEOUT_MS,
JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS
} = require('../config'); } = require('../config');
const { sleepFor } = require('./helpers');
class SingleDialer extends Emitter { class SingleDialer extends Emitter {
constructor({logger, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask, constructor({logger, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask,
@@ -358,6 +360,24 @@ class SingleDialer extends Emitter {
this.logger.info(err, 'place-outdial:_configMsEndpoint - error enable inband DTMF'); this.logger.info(err, 'place-outdial:_configMsEndpoint - error enable inband DTMF');
} }
} }
const origDestroy = this.ep.destroy.bind(this.ep);
this.ep.destroy = async() => {
try {
if (this.dialTask.transcribeTask && JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS) {
// transcribe task is being used, wait for some time before destroy
// if final transcription is received but endpoint is already closed,
// freeswitch module will not be able to send the transcription
this.logger.info('SingleDialer:_configMsEndpoint -' +
' Dial with transcribe task, wait for some time before destroy');
await sleepFor(JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS);
}
await origDestroy();
} catch (err) {
this.logger.error(err, 'SingleDialer:_configMsEndpoint - error destroying endpoint');
}
};
} }
/** /**

View File

@@ -3,9 +3,8 @@ const { sippUac } = require('./sipp')('test_fs');
const bent = require('bent'); const bent = require('bent');
const getJSON = bent('json') const getJSON = bent('json')
const clearModule = require('clear-module'); const clearModule = require('clear-module');
const {provisionCallHook} = require('./utils') const {provisionCallHook} = require('./utils');
const { sleepFor } = require('../lib/utils/helpers');
const sleepFor = (ms) => new Promise((r) => setTimeout(r, ms));
process.on('unhandledRejection', (reason, p) => { process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);

View File

@@ -3,6 +3,7 @@ const { sippUac } = require('./sipp')('test_fs');
const clearModule = require('clear-module'); const clearModule = require('clear-module');
const {provisionCallHook, provisionActionHook, provisionAnyHook} = require('./utils'); const {provisionCallHook, provisionActionHook, provisionAnyHook} = require('./utils');
const bent = require('bent'); const bent = require('bent');
const { sleepFor } = require('../lib/utils/helpers');
const getJSON = bent('json'); const getJSON = bent('json');
process.on('unhandledRejection', (reason, p) => { process.on('unhandledRejection', (reason, p) => {
@@ -17,8 +18,6 @@ function connect(connectable) {
}); });
} }
const sleepFor = (ms) => new Promise((resolve) => setTimeout(() => resolve(), ms));
test('\'enqueue-dequeue\' tests', async(t) => { test('\'enqueue-dequeue\' tests', async(t) => {
clearModule.all(); clearModule.all();

View File

@@ -3,10 +3,9 @@ const { sippUac } = require('./sipp')('test_fs');
const clearModule = require('clear-module'); const clearModule = require('clear-module');
const {provisionCallHook, provisionCustomHook, provisionActionHook} = require('./utils') const {provisionCallHook, provisionCustomHook, provisionActionHook} = require('./utils')
const bent = require('bent'); const bent = require('bent');
const { sleepFor } = require('../lib/utils/helpers');
const getJSON = bent('json') const getJSON = bent('json')
const sleepFor = async(ms) => new Promise(resolve => setTimeout(resolve, ms));
process.on('unhandledRejection', (reason, p) => { process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
}); });