mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-02-11 17:01:30 +00:00
Compare commits
9 Commits
v0.7.7-rc3
...
v0.7.7-rc6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a7746537d | ||
|
|
b2cac47d05 | ||
|
|
8a2b2962f8 | ||
|
|
90cb5e1348 | ||
|
|
cf821569b3 | ||
|
|
218f2d6c67 | ||
|
|
c2c8f00978 | ||
|
|
32714d73f3 | ||
|
|
8da85ebd5a |
@@ -11,6 +11,7 @@ class CallInfo {
|
||||
let srf;
|
||||
this.direction = opts.direction;
|
||||
this.traceId = opts.traceId;
|
||||
this.callTerminationBy = undefined;
|
||||
if (opts.req) {
|
||||
const u = opts.req.getParsedHeader('from');
|
||||
const uri = parseUri(u.uri);
|
||||
@@ -119,7 +120,7 @@ class CallInfo {
|
||||
applicationSid: this.applicationSid,
|
||||
fsSipAddress: this.localSipAddress
|
||||
};
|
||||
['parentCallSid', 'originatingSipIp', 'originatingSipTrunkName'].forEach((prop) => {
|
||||
['parentCallSid', 'originatingSipIp', 'originatingSipTrunkName', 'callTerminationBy'].forEach((prop) => {
|
||||
if (this[prop]) obj[prop] = this[prop];
|
||||
});
|
||||
if (typeof this.duration === 'number') obj.duration = this.duration;
|
||||
|
||||
@@ -549,7 +549,9 @@ class CallSession extends Emitter {
|
||||
return {
|
||||
speech_credential_sid: credential.speech_credential_sid,
|
||||
api_key: credential.api_key,
|
||||
region: credential.region
|
||||
region: credential.region,
|
||||
use_custom_stt: credential.use_custom_stt,
|
||||
custom_stt_endpoint: credential.custom_stt_endpoint
|
||||
};
|
||||
}
|
||||
else if ('wellsaid' === vendor) {
|
||||
@@ -638,6 +640,7 @@ class CallSession extends Emitter {
|
||||
this._onTasksDone();
|
||||
this._clearResources();
|
||||
|
||||
|
||||
if (!this.isConfirmCallSession && !this.isSmsCallSession) sessionTracker.remove(this.callSid);
|
||||
}
|
||||
|
||||
@@ -1188,6 +1191,7 @@ class CallSession extends Emitter {
|
||||
} catch (err) {
|
||||
if (err === CALLER_CANCELLED_ERR_MSG) {
|
||||
this.logger.error(err, 'caller canceled quickly before we could respond, ending call');
|
||||
this.callInfo.callTerminationBy = 'caller';
|
||||
this._notifyCallStatusChange({
|
||||
callStatus: CallStatus.NoAnswer,
|
||||
sipStatus: 487,
|
||||
@@ -1302,7 +1306,8 @@ class CallSession extends Emitter {
|
||||
this.dlg = await this.srf.createUAS(this.req, this.res, {
|
||||
headers: {
|
||||
'X-Trace-ID': this.req.locals.traceId,
|
||||
'X-Call-Sid': this.req.locals.callSid
|
||||
'X-Call-Sid': this.req.locals.callSid,
|
||||
...(this.applicationSid && {'X-Application-Sid': this.applicationSid})
|
||||
},
|
||||
localSdp: this.ep.local.sdp
|
||||
});
|
||||
@@ -1494,8 +1499,9 @@ class CallSession extends Emitter {
|
||||
dlg.connected = false;
|
||||
dlg.destroy = origDestroy;
|
||||
const duration = moment().diff(this.dlg.connectTime, 'seconds');
|
||||
this.callInfo.callTerminationBy = 'jambonz';
|
||||
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
|
||||
this.logger.debug('CallSession: call terminated by jambones');
|
||||
this.logger.debug('CallSession: call terminated by jambonz');
|
||||
this.rootSpan.setAttributes({'call.termination': 'hangup by jambonz'});
|
||||
origDestroy().catch((err) => this.logger.info({err}, 'CallSession - error destroying dialog'));
|
||||
if (this.wakeupResolver) {
|
||||
|
||||
@@ -34,6 +34,7 @@ class InboundCallSession extends CallSession {
|
||||
|
||||
_onCancel() {
|
||||
this.rootSpan.setAttributes({'call.termination': 'caller abandoned'});
|
||||
this.callInfo.callTerminationBy = 'caller';
|
||||
this._notifyCallStatusChange({
|
||||
callStatus: CallStatus.NoAnswer,
|
||||
sipStatus: 487,
|
||||
@@ -69,6 +70,7 @@ class InboundCallSession extends CallSession {
|
||||
assert(this.dlg.connectTime);
|
||||
const duration = moment().diff(this.dlg.connectTime, 'seconds');
|
||||
this.rootSpan.setAttributes({'call.termination': 'hangup by caller'});
|
||||
this.callInfo.callTerminationBy = 'caller';
|
||||
this.emit('callStatusChange', {
|
||||
callStatus: CallStatus.Completed,
|
||||
duration
|
||||
|
||||
@@ -44,6 +44,7 @@ class RestCallSession extends CallSession {
|
||||
* This is invoked when the called party hangs up, in order to calculate the call duration.
|
||||
*/
|
||||
_callerHungup() {
|
||||
this.callInfo.callTerminationBy = 'caller';
|
||||
const duration = moment().diff(this.dlg.connectTime, 'seconds');
|
||||
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
|
||||
this.logger.debug('RestCallSession: called party hung up');
|
||||
|
||||
@@ -36,7 +36,8 @@ class SipRecCallSession extends InboundCallSession {
|
||||
headers: {
|
||||
'Content-Type': 'application/sdp',
|
||||
'X-Trace-ID': this.req.locals.traceId,
|
||||
'X-Call-Sid': this.req.locals.callSid
|
||||
'X-Call-Sid': this.req.locals.callSid,
|
||||
...(this.applicationSid && {'X-Application-Sid': this.applicationSid})
|
||||
},
|
||||
localSdp: combinedSdp
|
||||
});
|
||||
|
||||
@@ -82,6 +82,7 @@ class TaskGather extends Task {
|
||||
this.requestSnr = recognizer.requestSnr || false;
|
||||
this.initialSpeechTimeoutMs = recognizer.initialSpeechTimeoutMs || 0;
|
||||
this.azureServiceEndpoint = recognizer.azureServiceEndpoint;
|
||||
this.azureSttEndpointId = recognizer.azureSttEndpointId;
|
||||
}
|
||||
else {
|
||||
this.hints = [];
|
||||
@@ -301,7 +302,7 @@ class TaskGather extends Task {
|
||||
}
|
||||
|
||||
if ('google' === this.vendor) {
|
||||
this.bugname = 'google_trancribe';
|
||||
this.bugname = 'google_transcribe';
|
||||
if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials);
|
||||
[
|
||||
['enhancedModel', 'GOOGLE_SPEECH_USE_ENHANCED'],
|
||||
@@ -339,7 +340,7 @@ class TaskGather extends Task {
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
||||
}
|
||||
else if (['aws', 'polly'].includes(this.vendor)) {
|
||||
this.bugname = 'aws_trancribe';
|
||||
this.bugname = 'aws_transcribe';
|
||||
if (this.vocabularyName) opts.AWS_VOCABULARY_NAME = this.vocabularyName;
|
||||
if (this.vocabularyFilterName) {
|
||||
opts.AWS_VOCABULARY_NAME = this.vocabularyFilterName;
|
||||
@@ -358,10 +359,18 @@ class TaskGather extends Task {
|
||||
else if ('microsoft' === this.vendor) {
|
||||
this.bugname = 'azure_transcribe';
|
||||
if (this.sttCredentials) {
|
||||
const {api_key, region, use_custom_stt, custom_stt_endpoint} = this.sttCredentials;
|
||||
|
||||
Object.assign(opts, {
|
||||
'AZURE_SUBSCRIPTION_KEY': this.sttCredentials.api_key,
|
||||
'AZURE_REGION': this.sttCredentials.region
|
||||
'AZURE_SUBSCRIPTION_KEY': api_key,
|
||||
'AZURE_REGION': region
|
||||
});
|
||||
if (this.azureSttEndpointId) {
|
||||
Object.assign(opts, {'AZURE_SERVICE_ENDPOINT_ID': this.azureSttEndpointId});
|
||||
}
|
||||
else if (use_custom_stt && custom_stt_endpoint) {
|
||||
Object.assign(opts, {'AZURE_SERVICE_ENDPOINT_ID': custom_stt_endpoint});
|
||||
}
|
||||
}
|
||||
if (this.hints && this.hints.length > 0) {
|
||||
opts.AZURE_SPEECH_HINTS = this.hints.map((h) => h.trim()).join(',');
|
||||
@@ -641,7 +650,7 @@ class TaskGather extends Task {
|
||||
}
|
||||
|
||||
this.span.setAttributes({'stt.resolve': reason, 'stt.result': JSON.stringify(evt)});
|
||||
if (this.ep && this.ep.connected) {
|
||||
if (this.needsStt && this.ep && this.ep.connected) {
|
||||
this.ep.stopTranscription({vendor: this.vendor})
|
||||
.catch((err) => this.logger.error({err}, 'Error stopping transcription'));
|
||||
}
|
||||
|
||||
@@ -497,6 +497,7 @@
|
||||
"requestSnr": "boolean",
|
||||
"initialSpeechTimeoutMs": "number",
|
||||
"azureServiceEndpoint": "string",
|
||||
"azureSttEndpointId": "string",
|
||||
"asrDtmfTerminationDigit": "string",
|
||||
"asrTimeout": "number"
|
||||
},
|
||||
|
||||
@@ -54,6 +54,7 @@ class TaskTranscribe extends Task {
|
||||
this.requestSnr = recognizer.requestSnr || false;
|
||||
this.initialSpeechTimeoutMs = recognizer.initialSpeechTimeoutMs || 0;
|
||||
this.azureServiceEndpoint = recognizer.azureServiceEndpoint;
|
||||
this.azureSttEndpointId = recognizer.azureSttEndpointId;
|
||||
}
|
||||
|
||||
get name() { return TaskName.Transcribe; }
|
||||
@@ -161,7 +162,7 @@ class TaskTranscribe extends Task {
|
||||
ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected, this._onNoAudio.bind(this, cs, ep, channel));
|
||||
|
||||
if (this.vendor === 'google') {
|
||||
this.bugname = 'google_trancribe';
|
||||
this.bugname = 'google_transcribe';
|
||||
if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials);
|
||||
[
|
||||
['enhancedModel', 'GOOGLE_SPEECH_USE_ENHANCED'],
|
||||
@@ -199,7 +200,7 @@ class TaskTranscribe extends Task {
|
||||
.catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with google'));
|
||||
}
|
||||
else if (this.vendor === 'aws') {
|
||||
this.bugname = 'aws_trancribe';
|
||||
this.bugname = 'aws_transcribe';
|
||||
[
|
||||
['diarization', 'AWS_SHOW_SPEAKER_LABEL'],
|
||||
['identifyChannels', 'AWS_ENABLE_CHANNEL_IDENTIFICATION']
|
||||
@@ -231,11 +232,18 @@ class TaskTranscribe extends Task {
|
||||
.catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with aws'));
|
||||
}
|
||||
else if (this.vendor === 'microsoft') {
|
||||
this.bugname = 'azure_trancribe';
|
||||
this.bugname = 'azure_transcribe';
|
||||
const {api_key, region, use_custom_stt, custom_stt_endpoint} = this.sttCredentials;
|
||||
Object.assign(opts, {
|
||||
'AZURE_SUBSCRIPTION_KEY': this.sttCredentials.api_key,
|
||||
'AZURE_REGION': this.sttCredentials.region
|
||||
'AZURE_SUBSCRIPTION_KEY': api_key,
|
||||
'AZURE_REGION': region
|
||||
});
|
||||
if (this.azureSttEndpointId) {
|
||||
Object.assign(opts, {'AZURE_SERVICE_ENDPOINT_ID': this.azureSttEndpointId});
|
||||
}
|
||||
else if (use_custom_stt && custom_stt_endpoint) {
|
||||
Object.assign(opts, {'AZURE_SERVICE_ENDPOINT_ID': custom_stt_endpoint});
|
||||
}
|
||||
if (this.hints && this.hints.length > 0) {
|
||||
opts.AZURE_SPEECH_HINTS = this.hints.map((h) => h.trim()).join(',');
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ const speechMapper = (cred) => {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = o.api_key;
|
||||
obj.region = o.region;
|
||||
obj.use_custom_stt = o.use_custom_stt;
|
||||
obj.custom_stt_endpoint = o.custom_stt_endpoint;
|
||||
}
|
||||
else if ('wellsaid' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
|
||||
@@ -67,7 +67,8 @@ class SingleDialer extends Emitter {
|
||||
...opts.headers,
|
||||
...(this.target.headers || {}),
|
||||
'X-Jambonz-Routing': this.target.type,
|
||||
'X-Call-Sid': this.callSid
|
||||
'X-Call-Sid': this.callSid,
|
||||
...(this.applicationSid && {'X-Application-Sid': this.applicationSid})
|
||||
};
|
||||
if (srf.locals.fsUUID) {
|
||||
opts.headers = {
|
||||
|
||||
@@ -138,7 +138,7 @@ class WsRequestor extends BaseRequestor {
|
||||
success: (response) => {
|
||||
clearTimeout(timer);
|
||||
const rtt = this._roundTrip(startAt);
|
||||
this.logger.info({response}, `WsRequestor:request ${url} succeeded in ${rtt}ms`);
|
||||
this.logger.debug({response}, `WsRequestor:request ${url} succeeded in ${rtt}ms`);
|
||||
this.stats.histogram('app.hook.ws_response_time', rtt, ['hook_type:app']);
|
||||
resolve(response);
|
||||
},
|
||||
@@ -286,7 +286,7 @@ class WsRequestor extends BaseRequestor {
|
||||
const obj = JSON.parse(content);
|
||||
const {type, msgid, command, call_sid = this.call_sid, queueCommand = false, data} = obj;
|
||||
|
||||
this.logger.debug({obj}, 'WsRequestor:request websocket: received');
|
||||
//this.logger.debug({obj}, 'WsRequestor:request websocket: received');
|
||||
assert.ok(type, 'type property not supplied');
|
||||
|
||||
switch (type) {
|
||||
@@ -323,7 +323,7 @@ class WsRequestor extends BaseRequestor {
|
||||
|
||||
_recvCommand(msgid, command, call_sid, queueCommand, data) {
|
||||
// TODO: validate command
|
||||
this.logger.info({msgid, command, call_sid, queueCommand, data}, 'received command');
|
||||
this.logger.debug({msgid, command, call_sid, queueCommand, data}, 'received command');
|
||||
this.emit('command', {msgid, command, call_sid, queueCommand, data});
|
||||
}
|
||||
}
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.6.18",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.30",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.32",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.2.1",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
@@ -543,9 +543,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jambonz/realtimedb-helpers": {
|
||||
"version": "0.4.30",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.30.tgz",
|
||||
"integrity": "sha512-bF1crcyz/sYDCnOX0odbI1KLZUo6TADc548MHX5/O2+gY6Yi6u9GdEaf2u5q5fgmpoJ+Qrf3fuiENxzro+Pq8g==",
|
||||
"version": "0.4.32",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.32.tgz",
|
||||
"integrity": "sha512-m08rqFEg6I43JVwjiyt0ahvljxJ3YvL3xsKHMoSJw48hdy/8V4w9ovYnUNaZGhaerhJVw36fCfTk4YNlByTnVw==",
|
||||
"dependencies": {
|
||||
"@google-cloud/text-to-speech": "^3.4.0",
|
||||
"@jambonz/promisify-redis": "^0.0.6",
|
||||
@@ -6669,9 +6669,9 @@
|
||||
}
|
||||
},
|
||||
"@jambonz/realtimedb-helpers": {
|
||||
"version": "0.4.30",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.30.tgz",
|
||||
"integrity": "sha512-bF1crcyz/sYDCnOX0odbI1KLZUo6TADc548MHX5/O2+gY6Yi6u9GdEaf2u5q5fgmpoJ+Qrf3fuiENxzro+Pq8g==",
|
||||
"version": "0.4.32",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.32.tgz",
|
||||
"integrity": "sha512-m08rqFEg6I43JVwjiyt0ahvljxJ3YvL3xsKHMoSJw48hdy/8V4w9ovYnUNaZGhaerhJVw36fCfTk4YNlByTnVw==",
|
||||
"requires": {
|
||||
"@google-cloud/text-to-speech": "^3.4.0",
|
||||
"@jambonz/promisify-redis": "^0.0.6",
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/jambonz/jambonz-feature-server.git"
|
||||
},
|
||||
"bugs": {
|
||||
},
|
||||
"bugs": {},
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"test": "NODE_ENV=test JAMBONES_HOSTING=1 HTTP_POOL=1 DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 DRACHTIO_SECRET=cymru JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=127.0.0.1 JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error ENABLE_METRICS=0 HTTP_PORT=3000 JAMBONES_SBCS=172.38.0.10 JAMBONES_FREESWITCH=127.0.0.1:8022:ClueCon:docker-host JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_NETWORK_CIDR=172.38.0.0/16 node test/ ",
|
||||
@@ -27,7 +26,7 @@
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.6.18",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.30",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.32",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.2.1",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
|
||||
Reference in New Issue
Block a user