mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-01-25 02:07:56 +00:00
Compare commits
10 Commits
v0.9.2
...
fix/v0.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7521fadbd6 | ||
|
|
ef9edd2125 | ||
|
|
4dd3a4a4e7 | ||
|
|
db3f7937e9 | ||
|
|
380cd7f792 | ||
|
|
6889f0e4ab | ||
|
|
1efb198f72 | ||
|
|
4b5df855e1 | ||
|
|
24126ef1ec | ||
|
|
8e4995ec02 |
@@ -385,7 +385,7 @@ module.exports = function(srf, logger) {
|
||||
const {rootSpan, siprec, application:app} = req.locals;
|
||||
let span;
|
||||
try {
|
||||
if (app.tasks && !JAMBONES_MYSQL_REFRESH_TTL) {
|
||||
if (app.tasks && app.tasks?.length > 0 && !JAMBONES_MYSQL_REFRESH_TTL) {
|
||||
app.tasks = normalizeJambones(logger, app.tasks).map((tdata) => makeTask(logger, tdata));
|
||||
if (0 === app.tasks.length) throw new Error('no application provided');
|
||||
return next();
|
||||
|
||||
@@ -1172,6 +1172,11 @@ class CallSession extends Emitter {
|
||||
this.wakeupResolver({reason: 'session ended'});
|
||||
this.wakeupResolver = null;
|
||||
}
|
||||
|
||||
if (this._maxCallDurationTimer) {
|
||||
clearTimeout(this._maxCallDurationTimer);
|
||||
this._maxCallDurationTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1630,59 +1635,63 @@ Duration=${duration} `
|
||||
async updateCall(opts, callSid) {
|
||||
this.logger.debug(opts, 'CallSession:updateCall');
|
||||
|
||||
if (opts.call_status) {
|
||||
return this._lccCallStatus(opts);
|
||||
}
|
||||
if (opts.call_hook || opts.child_call_hook) {
|
||||
return await this._lccCallHook(opts);
|
||||
}
|
||||
if (opts.listen_status) {
|
||||
await this._lccListenStatus(opts);
|
||||
}
|
||||
if (opts.transcribe_status) {
|
||||
await this._lccTranscribeStatus(opts);
|
||||
}
|
||||
else if (opts.mute_status) {
|
||||
await this._lccMuteStatus(opts.mute_status === 'mute', callSid);
|
||||
}
|
||||
else if (opts.conf_hold_status) {
|
||||
await this._lccConfHoldStatus(opts);
|
||||
}
|
||||
else if (opts.conf_mute_status) {
|
||||
await this._lccConfMuteStatus(opts);
|
||||
}
|
||||
else if (opts.sip_request) {
|
||||
const res = await this._lccSipRequest(opts, callSid);
|
||||
return {status: res.status, reason: res.reason};
|
||||
} else if (opts.dtmf) {
|
||||
await this._lccDtmf(opts, callSid);
|
||||
}
|
||||
else if (opts.record) {
|
||||
await this.notifyRecordOptions(opts.record);
|
||||
}
|
||||
else if (opts.tag) {
|
||||
return this._lccTag(opts);
|
||||
}
|
||||
else if (opts.conferenceParticipantAction) {
|
||||
return this._lccConferenceParticipantAction(opts.conferenceParticipantAction);
|
||||
}
|
||||
else if (opts.dub) {
|
||||
return this._lccDub(opts);
|
||||
}
|
||||
else if (opts.boostAudioSignal) {
|
||||
return this._lccBoostAudioSignal(opts, callSid);
|
||||
}
|
||||
else if (opts.llm_tool_output) {
|
||||
return this._lccToolOutput(opts.tool_call_id, opts.llm_tool_output, callSid);
|
||||
}
|
||||
else if (opts.llm_update) {
|
||||
return this._lccLlmUpdate(opts.llm_update, callSid);
|
||||
}
|
||||
try {
|
||||
if (opts.call_status) {
|
||||
return this._lccCallStatus(opts);
|
||||
}
|
||||
if (opts.call_hook || opts.child_call_hook) {
|
||||
return await this._lccCallHook(opts);
|
||||
}
|
||||
if (opts.listen_status) {
|
||||
await this._lccListenStatus(opts);
|
||||
}
|
||||
if (opts.transcribe_status) {
|
||||
await this._lccTranscribeStatus(opts);
|
||||
}
|
||||
else if (opts.mute_status) {
|
||||
await this._lccMuteStatus(opts.mute_status === 'mute', callSid);
|
||||
}
|
||||
else if (opts.conf_hold_status) {
|
||||
await this._lccConfHoldStatus(opts);
|
||||
}
|
||||
else if (opts.conf_mute_status) {
|
||||
await this._lccConfMuteStatus(opts);
|
||||
}
|
||||
else if (opts.sip_request) {
|
||||
const res = await this._lccSipRequest(opts, callSid);
|
||||
return {status: res.status, reason: res.reason};
|
||||
} else if (opts.dtmf) {
|
||||
await this._lccDtmf(opts, callSid);
|
||||
}
|
||||
else if (opts.record) {
|
||||
await this.notifyRecordOptions(opts.record);
|
||||
}
|
||||
else if (opts.tag) {
|
||||
return this._lccTag(opts);
|
||||
}
|
||||
else if (opts.conferenceParticipantAction) {
|
||||
return this._lccConferenceParticipantAction(opts.conferenceParticipantAction);
|
||||
}
|
||||
else if (opts.dub) {
|
||||
return this._lccDub(opts);
|
||||
}
|
||||
else if (opts.boostAudioSignal) {
|
||||
return this._lccBoostAudioSignal(opts, callSid);
|
||||
}
|
||||
else if (opts.llm_tool_output) {
|
||||
return this._lccToolOutput(opts.tool_call_id, opts.llm_tool_output, callSid);
|
||||
}
|
||||
else if (opts.llm_update) {
|
||||
return this._lccLlmUpdate(opts.llm_update, callSid);
|
||||
}
|
||||
|
||||
// whisper may be the only thing we are asked to do, or it may that
|
||||
// we are doing a whisper after having muted, paused recording etc..
|
||||
if (opts.whisper) {
|
||||
return this._lccWhisper(opts, callSid);
|
||||
// whisper may be the only thing we are asked to do, or it may that
|
||||
// we are doing a whisper after having muted, paused recording etc..
|
||||
if (opts.whisper) {
|
||||
return this._lccWhisper(opts, callSid);
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.info({err, opts, callSid}, 'CallSession:updateCall - error updating call');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2680,6 +2689,27 @@ Duration=${duration} `
|
||||
this.verbHookSpan = null;
|
||||
}
|
||||
}
|
||||
|
||||
async startMaxCallDurationTimer(timeLimit) {
|
||||
if (!this._maxCallDurationTimer && timeLimit > 0) {
|
||||
this.timeLimit = timeLimit;
|
||||
this._maxCallDurationTimer = setTimeout(this._onMaxCallDuration.bind(this), timeLimit * 1000);
|
||||
this.logger.debug(`CallSession:startMaxCallDurationTimer - started max call duration timer for ${timeLimit}s`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _onMaxCallDuration - called when the call has reached the maximum duration
|
||||
*/
|
||||
_onMaxCallDuration() {
|
||||
this.logger.info(`callSession:_onMaxCallDuration tearing down call as it has reached ${this.timeLimit}s`);
|
||||
if (!this.dlg) {
|
||||
this.logger.debug('CallSession:_onMaxCallDuration - no dialog, call already gone');
|
||||
return;
|
||||
}
|
||||
this._jambonzHangup('Max Call Duration');
|
||||
this._maxCallDurationTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CallSession;
|
||||
|
||||
@@ -72,6 +72,8 @@ class InboundCallSession extends CallSession {
|
||||
|
||||
_jambonzHangup() {
|
||||
this.dlg?.destroy();
|
||||
// kill current task or wakeup the call session.
|
||||
this._callReleased();
|
||||
}
|
||||
|
||||
_hangup(terminatedBy = 'jambonz') {
|
||||
|
||||
@@ -340,15 +340,17 @@ class TaskDial extends Task {
|
||||
|
||||
const to = parseUri(req.getParsedHeader('Refer-To').uri);
|
||||
const by = parseUri(req.getParsedHeader('Referred-By').uri);
|
||||
const referredBy = req.get('Referred-By');
|
||||
const userAgent = req.get('User-Agent');
|
||||
this.logger.info({to}, 'refer to parsed');
|
||||
const json = await cs.requestor.request('verb:hook', this.referHook, {
|
||||
...(callInfo.toJSON()),
|
||||
refer_details: {
|
||||
sip_refer_to: req.get('Refer-To'),
|
||||
sip_referred_by: req.get('Referred-By'),
|
||||
sip_user_agent: req.get('User-Agent'),
|
||||
refer_to_user: to.scheme === 'tel' ? to.number : to.user,
|
||||
referred_by_user: by.scheme === 'tel' ? by.number : by.user,
|
||||
...(referredBy && {sip_referred_by: referredBy}),
|
||||
...(userAgent && {sip_user_agent: userAgent}),
|
||||
...(by && {referred_by_user: by.scheme === 'tel' ? by.number : by.user}),
|
||||
referring_call_sid,
|
||||
referred_call_sid
|
||||
}
|
||||
@@ -379,6 +381,7 @@ class TaskDial extends Task {
|
||||
res.send(202);
|
||||
this.logger.info('DialTask:handleRefer - sent 202 Accepted');
|
||||
} catch (err) {
|
||||
this.logger.info({err}, 'DialTask:handleRefer - error processing incoming REFER');
|
||||
res.send(err.statusCode || 501);
|
||||
}
|
||||
}
|
||||
@@ -497,8 +500,10 @@ class TaskDial extends Task {
|
||||
'X-Account-Sid': cs.accountSid,
|
||||
...(req && req.has('X-CID') && {'X-CID': req.get('X-CID')}),
|
||||
...(direction === 'outbound' && callInfo.sbcCallid && {'X-CID': callInfo.sbcCallid}),
|
||||
...(req && req.has('P-Asserted-Identity') && !JAMBONZ_DISABLE_DIAL_PAI_HEADER &&
|
||||
{'P-Asserted-Identity': req.get('P-Asserted-Identity')}),
|
||||
...(!JAMBONZ_DISABLE_DIAL_PAI_HEADER && req && {
|
||||
...(req.has('P-Asserted-Identity') && {'P-Asserted-Identity': req.get('P-Asserted-Identity')}),
|
||||
...(req.has('Privacy') && {'Privacy': req.get('Privacy')}),
|
||||
}),
|
||||
...(req && req.has('X-Voip-Carrier-Sid') && {'X-Voip-Carrier-Sid': req.get('X-Voip-Carrier-Sid')}),
|
||||
// Put headers at the end to make sure opt.headers override all default behavior.
|
||||
...this.headers
|
||||
|
||||
@@ -924,7 +924,7 @@ class TaskGather extends SttTask {
|
||||
}
|
||||
}
|
||||
// If transcription received, reset timeout timer.
|
||||
if (this._timeoutTimer) {
|
||||
if (this._timeoutTimer && !emptyTranscript) {
|
||||
this._startTimer();
|
||||
}
|
||||
/* restart asr timer if we get a partial transcript (only if the asr timer is already running) */
|
||||
|
||||
@@ -12,6 +12,7 @@ class TaskRestDial extends Task {
|
||||
|
||||
this.from = this.data.from;
|
||||
this.callerName = this.data.callerName;
|
||||
this.timeLimit = this.data.timeLimit;
|
||||
this.fromHost = this.data.fromHost;
|
||||
this.to = this.data.to;
|
||||
this.call_hook = this.data.call_hook;
|
||||
@@ -66,6 +67,9 @@ class TaskRestDial extends Task {
|
||||
const cs = this.callSession;
|
||||
cs.setDialog(dlg);
|
||||
cs.referHook = this.referHook;
|
||||
if (this.timeLimit) {
|
||||
cs.startMaxCallDurationTimer(this.timeLimit);
|
||||
}
|
||||
this.logger.debug('TaskRestDial:_onConnect - call connected');
|
||||
if (this.sipRequestWithinDialogHook) this._initSipRequestWithinDialogHandler(cs, dlg);
|
||||
try {
|
||||
|
||||
@@ -177,32 +177,22 @@ class TaskSay extends TtsTask {
|
||||
account_sid,
|
||||
alert_type: AlertType.TTS_FAILURE,
|
||||
vendor,
|
||||
detail: evt.variable_tts_error
|
||||
detail: evt.variable_tts_error,
|
||||
target_sid
|
||||
}).catch((err) => this.logger.info({err}, 'Error generating alert for no tts'));
|
||||
}
|
||||
else {
|
||||
this.logger.debug({evt}, 'Say got playback-stop');
|
||||
if (evt.variable_tts_error) {
|
||||
writeAlerts({
|
||||
account_sid,
|
||||
alert_type: AlertType.TTS_FAILURE,
|
||||
vendor,
|
||||
detail: evt.variable_tts_error,
|
||||
target_sid
|
||||
}).catch((err) => this.logger.info({err}, 'Error generating alert for no tts'));
|
||||
}
|
||||
if (evt.variable_tts_cache_filename && !this.killed) {
|
||||
const text = parseTextFromSayString(this.text[segment]);
|
||||
addFileToCache(evt.variable_tts_cache_filename, {
|
||||
account_sid,
|
||||
vendor,
|
||||
language,
|
||||
voice,
|
||||
engine,
|
||||
text
|
||||
}).catch((err) => this.logger.info({err}, 'Error adding file to cache'));
|
||||
}
|
||||
if (evt.variable_tts_cache_filename && !this.killed) {
|
||||
const text = parseTextFromSayString(this.text[segment]);
|
||||
addFileToCache(evt.variable_tts_cache_filename, {
|
||||
account_sid,
|
||||
vendor,
|
||||
language,
|
||||
voice,
|
||||
engine,
|
||||
text
|
||||
}).catch((err) => this.logger.info({err}, 'Error adding file to cache'));
|
||||
}
|
||||
|
||||
if (this._playResolve) {
|
||||
evt.variable_tts_error ? this._playReject(new Error(evt.variable_tts_error)) : this._playResolve();
|
||||
}
|
||||
|
||||
29
package-lock.json
generated
29
package-lock.json
generated
@@ -17,8 +17,8 @@
|
||||
"@jambonz/realtimedb-helpers": "^0.8.8",
|
||||
"@jambonz/speech-utils": "^0.1.20",
|
||||
"@jambonz/stats-collector": "^0.1.10",
|
||||
"@jambonz/time-series": "^0.2.9",
|
||||
"@jambonz/verb-specifications": "^0.0.83",
|
||||
"@jambonz/time-series": "^0.2.13",
|
||||
"@jambonz/verb-specifications": "^0.0.91",
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
||||
@@ -1568,18 +1568,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jambonz/time-series": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.9.tgz",
|
||||
"integrity": "sha512-+/Oal0mjjV4iQ8q0ymtvVJP+GqgGpYUb2bc/FD/xDxOzKtl340l9yoM3oczREJg5IvEkpExz6NogJzUiCSeVnQ==",
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.13.tgz",
|
||||
"integrity": "sha512-Kj+l+YUnI27zZA4qoPRzjN7L82W7GuMXYq9ttDjXQ0ZBIdOLAzJjB6R3jJ3b+mvoNEQ6qG5MUtfoc6CpTFH5lw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.1",
|
||||
"influx": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@jambonz/verb-specifications": {
|
||||
"version": "0.0.83",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.83.tgz",
|
||||
"integrity": "sha512-3m1o3gnWw1yEwNfkZ6IIyUhdUqsQ3aWBNoWJHswe4udvVbEIxPHi+8jKGTJVF2vxddzwfOWp7RmMCV9RmKWzkw==",
|
||||
"version": "0.0.91",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.91.tgz",
|
||||
"integrity": "sha512-C9UIKytogOdUp5sGqfEetl82/8lDQ8zgEU5diQKpU4dZeA3M8qItqjJGSXexNh4PK9ECUYNorsPoRGNxGcr2IA==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"pino": "^8.8.0"
|
||||
@@ -10665,18 +10666,18 @@
|
||||
}
|
||||
},
|
||||
"@jambonz/time-series": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.9.tgz",
|
||||
"integrity": "sha512-+/Oal0mjjV4iQ8q0ymtvVJP+GqgGpYUb2bc/FD/xDxOzKtl340l9yoM3oczREJg5IvEkpExz6NogJzUiCSeVnQ==",
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.13.tgz",
|
||||
"integrity": "sha512-Kj+l+YUnI27zZA4qoPRzjN7L82W7GuMXYq9ttDjXQ0ZBIdOLAzJjB6R3jJ3b+mvoNEQ6qG5MUtfoc6CpTFH5lw==",
|
||||
"requires": {
|
||||
"debug": "^4.3.1",
|
||||
"influx": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"@jambonz/verb-specifications": {
|
||||
"version": "0.0.83",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.83.tgz",
|
||||
"integrity": "sha512-3m1o3gnWw1yEwNfkZ6IIyUhdUqsQ3aWBNoWJHswe4udvVbEIxPHi+8jKGTJVF2vxddzwfOWp7RmMCV9RmKWzkw==",
|
||||
"version": "0.0.91",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.91.tgz",
|
||||
"integrity": "sha512-C9UIKytogOdUp5sGqfEetl82/8lDQ8zgEU5diQKpU4dZeA3M8qItqjJGSXexNh4PK9ECUYNorsPoRGNxGcr2IA==",
|
||||
"requires": {
|
||||
"debug": "^4.3.4",
|
||||
"pino": "^8.8.0"
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
"@jambonz/realtimedb-helpers": "^0.8.8",
|
||||
"@jambonz/speech-utils": "^0.1.20",
|
||||
"@jambonz/stats-collector": "^0.1.10",
|
||||
"@jambonz/time-series": "^0.2.9",
|
||||
"@jambonz/verb-specifications": "^0.0.83",
|
||||
"@jambonz/time-series": "^0.2.13",
|
||||
"@jambonz/verb-specifications": "^0.0.91",
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
||||
|
||||
@@ -222,3 +222,62 @@ test('test create-call app_json', async(t) => {
|
||||
t.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
test('test create-call timeLimit', async(t) => {
|
||||
clearModule.all();
|
||||
const {srf, disconnect} = require('../app');
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
|
||||
|
||||
// GIVEN
|
||||
let from = 'create-call-app-json';
|
||||
let account_sid = 'bb845d4b-83a9-4cde-a6e9-50f3743bab3f';
|
||||
|
||||
// Give UAS app time to come up
|
||||
const p = sippUac('uas.xml', '172.38.0.10', from);
|
||||
await waitFor(1000);
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
const app_json = `[
|
||||
{
|
||||
"verb": "pause",
|
||||
"length": 7
|
||||
}
|
||||
]`;
|
||||
|
||||
const post = bent('http://127.0.0.1:3000/', 'POST', 'json', 201);
|
||||
post('v1/createCall', {
|
||||
'account_sid':account_sid,
|
||||
"call_hook": {
|
||||
"url": "http://127.0.0.1:3100/",
|
||||
"method": "POST",
|
||||
"username": "username",
|
||||
"password": "password"
|
||||
},
|
||||
app_json,
|
||||
"from": from,
|
||||
"to": {
|
||||
"type": "phone",
|
||||
"number": "15583084809"
|
||||
},
|
||||
"timeLimit": 1,
|
||||
"speech_recognizer_vendor": "google",
|
||||
"speech_recognizer_language": "en"
|
||||
});
|
||||
|
||||
//THEN
|
||||
await p;
|
||||
const endTime = Date.now();
|
||||
|
||||
t.ok(endTime - startTime < 2000, 'create-call: timeLimit is respected');
|
||||
|
||||
disconnect();
|
||||
} catch (err) {
|
||||
console.log(`error received: ${err}`);
|
||||
disconnect();
|
||||
t.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user