mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-02-15 10:49:07 +00:00
Compare commits
8 Commits
v0.8.6-rc1
...
v0.9.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b722ae36d | ||
|
|
370b046fac | ||
|
|
fca391c32e | ||
|
|
043860c4a3 | ||
|
|
a021ee3112 | ||
|
|
8999c85a71 | ||
|
|
72147a8110 | ||
|
|
93d0e41e31 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run jslint
|
- run: npm run jslint
|
||||||
- run: docker pull drachtio/sipp
|
- run: docker pull drachtio/sipp
|
||||||
|
|||||||
@@ -112,13 +112,17 @@ class CallSession extends Emitter {
|
|||||||
this.requestor.removeAllListeners();
|
this.requestor.removeAllListeners();
|
||||||
this.application.requestor = newRequestor;
|
this.application.requestor = newRequestor;
|
||||||
this.requestor.on('command', this._onCommand.bind(this));
|
this.requestor.on('command', this._onCommand.bind(this));
|
||||||
|
this.logger.debug(`CallSession: ${this.callSid} listener count ${this.requestor.listenerCount('command')}`);
|
||||||
this.requestor.on('connection-dropped', this._onWsConnectionDropped.bind(this));
|
this.requestor.on('connection-dropped', this._onWsConnectionDropped.bind(this));
|
||||||
this.requestor.on('handover', handover.bind(this));
|
this.requestor.on('handover', handover.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.requestor.on('command', this._onCommand.bind(this));
|
if (!this.isConfirmCallSession) {
|
||||||
this.requestor.on('connection-dropped', this._onWsConnectionDropped.bind(this));
|
this.requestor.on('command', this._onCommand.bind(this));
|
||||||
this.requestor.on('handover', handover.bind(this));
|
this.logger.debug(`CallSession: ${this.callSid} listener count ${this.requestor.listenerCount('command')}`);
|
||||||
|
this.requestor.on('connection-dropped', this._onWsConnectionDropped.bind(this));
|
||||||
|
this.requestor.on('handover', handover.bind(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1367,6 +1371,30 @@ Duration=${duration} `
|
|||||||
task.whisper(tasks, callSid).catch((err) => this.logger.error(err, 'CallSession:_lccWhisper'));
|
task.whisper(tasks, callSid).catch((err) => this.logger.error(err, 'CallSession:_lccWhisper'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _lccConfig(opts) {
|
||||||
|
this.logger.debug({opts}, 'CallSession:_lccConfig');
|
||||||
|
const t = normalizeJambones(this.logger, [
|
||||||
|
{
|
||||||
|
verb: 'config',
|
||||||
|
...opts
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.map((tdata) => makeTask(this.logger, tdata));
|
||||||
|
|
||||||
|
const task = t[0];
|
||||||
|
|
||||||
|
const {span, ctx} = this.rootSpan.startChildSpan(`verb:${task.summary}`);
|
||||||
|
span.setAttributes({'verb.summary': task.summary});
|
||||||
|
task.span = span;
|
||||||
|
task.ctx = ctx;
|
||||||
|
try {
|
||||||
|
await task.exec(this, {ep: this.ep});
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error(err, 'CallSession:_lccConfig');
|
||||||
|
}
|
||||||
|
task.span.end();
|
||||||
|
}
|
||||||
|
|
||||||
async _lccDub(opts, callSid) {
|
async _lccDub(opts, callSid) {
|
||||||
this.logger.debug({opts}, `CallSession:_lccDub on call_sid ${callSid}`);
|
this.logger.debug({opts}, `CallSession:_lccDub on call_sid ${callSid}`);
|
||||||
const t = normalizeJambones(this.logger, [
|
const t = normalizeJambones(this.logger, [
|
||||||
@@ -1377,23 +1405,24 @@ Duration=${duration} `
|
|||||||
])
|
])
|
||||||
.map((tdata) => makeTask(this.logger, tdata));
|
.map((tdata) => makeTask(this.logger, tdata));
|
||||||
|
|
||||||
const dubTask = t[0];
|
const task = t[0];
|
||||||
const ep = this.currentTask?.name === TaskName.Dial && callSid === this.currentTask?.callSid ?
|
const ep = this.currentTask?.name === TaskName.Dial && callSid === this.currentTask?.callSid ?
|
||||||
this.currentTask.ep :
|
this.currentTask.ep :
|
||||||
this.ep;
|
this.ep;
|
||||||
|
|
||||||
const {span, ctx} = this.rootSpan.startChildSpan(`verb:${dubTask.summary}`);
|
const {span, ctx} = this.rootSpan.startChildSpan(`verb:${task.summary}`);
|
||||||
span.setAttributes({'verb.summary': dubTask.summary});
|
span.setAttributes({'verb.summary': task.summary});
|
||||||
dubTask.span = span;
|
task.span = span;
|
||||||
dubTask.ctx = ctx;
|
task.ctx = ctx;
|
||||||
try {
|
try {
|
||||||
await dubTask.exec(this, {ep});
|
await task.exec(this, {ep});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error(err, 'CallSession:_lccDub');
|
this.logger.error(err, 'CallSession:_lccDub');
|
||||||
}
|
}
|
||||||
dubTask.span.end();
|
task.span.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async _lccBoostAudioSignal(opts, callSid) {
|
async _lccBoostAudioSignal(opts, callSid) {
|
||||||
const ep = this.currentTask?.name === TaskName.Dial && callSid === this.currentTask?.callSid ?
|
const ep = this.currentTask?.name === TaskName.Dial && callSid === this.currentTask?.callSid ?
|
||||||
this.currentTask.ep :
|
this.currentTask.ep :
|
||||||
@@ -1664,6 +1693,10 @@ Duration=${duration} `
|
|||||||
this._lccCallStatus(data);
|
this._lccCallStatus(data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'config':
|
||||||
|
this._lccConfig(data, call_sid);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'dial':
|
case 'dial':
|
||||||
this._lccCallDial(data);
|
this._lccCallDial(data);
|
||||||
break;
|
break;
|
||||||
@@ -1978,6 +2011,10 @@ Duration=${duration} `
|
|||||||
}
|
}
|
||||||
this.logger.debug(`CallSession:propagateAnswer - answered callSid ${this.callSid}`);
|
this.logger.debug(`CallSession:propagateAnswer - answered callSid ${this.callSid}`);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.logger.debug('CallSession:propagateAnswer - call already answered - re-anchor media with a reinvite');
|
||||||
|
await this.dlg.modify(this.ep.local.sdp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onRequestWithinDialog(req, res) {
|
async _onRequestWithinDialog(req, res) {
|
||||||
|
|||||||
@@ -125,10 +125,12 @@ class TaskDub extends TtsTask {
|
|||||||
const path = filepath[0];
|
const path = filepath[0];
|
||||||
if (!path.startsWith('say:{')) {
|
if (!path.startsWith('say:{')) {
|
||||||
/* we have a local file of mp3 or r8 of synthesized speech audio to play */
|
/* we have a local file of mp3 or r8 of synthesized speech audio to play */
|
||||||
|
this.logger.info(`playing synthesized speech from file on track ${this.track}: ${path}`);
|
||||||
this.play = path;
|
this.play = path;
|
||||||
await this._playOnTrack(cs, ep);
|
await this._playOnTrack(cs, ep);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
this.logger.info(`doing actual text to speech file on track ${this.track}: ${path}`);
|
||||||
await ep.dub({
|
await ep.dub({
|
||||||
action: 'sayOnTrack',
|
action: 'sayOnTrack',
|
||||||
track: this.track,
|
track: this.track,
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ class TaskEnqueue extends Task {
|
|||||||
this.logger.error({err}, `TaskEnqueue:_playHook error retrieving list info for queue ${this.queueName}`);
|
this.logger.error({err}, `TaskEnqueue:_playHook error retrieving list info for queue ${this.queueName}`);
|
||||||
}
|
}
|
||||||
const json = await cs.application.requestor.request('verb:hook', hook, params, httpHeaders);
|
const json = await cs.application.requestor.request('verb:hook', hook, params, httpHeaders);
|
||||||
|
this.logger.debug({json}, 'TaskEnqueue:_playHook: received response from waitHook');
|
||||||
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
||||||
|
|
||||||
const allowedTasks = tasks.filter((t) => allowed.includes(t.name));
|
const allowedTasks = tasks.filter((t) => allowed.includes(t.name));
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ const DTMF_SPAN_NAME = 'dtmf';
|
|||||||
class TaskListen extends Task {
|
class TaskListen extends Task {
|
||||||
constructor(logger, opts, parentTask) {
|
constructor(logger, opts, parentTask) {
|
||||||
super(logger, opts);
|
super(logger, opts);
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* use bidirectionalAudio.enabled
|
||||||
|
*/
|
||||||
this.disableBidirectionalAudio = opts.disableBidirectionalAudio;
|
this.disableBidirectionalAudio = opts.disableBidirectionalAudio;
|
||||||
this.preconditions = TaskPreconditions.Endpoint;
|
this.preconditions = TaskPreconditions.Endpoint;
|
||||||
|
|
||||||
@@ -25,6 +29,15 @@ class TaskListen extends Task {
|
|||||||
this.results = {};
|
this.results = {};
|
||||||
this.playAudioQueue = [];
|
this.playAudioQueue = [];
|
||||||
this.isPlayingAudioFromQueue = false;
|
this.isPlayingAudioFromQueue = false;
|
||||||
|
this.bidirectionalAudio = {
|
||||||
|
enabled: this.disableBidirectionalAudio === true ? false : true,
|
||||||
|
...(this.data['bidirectionalAudio']),
|
||||||
|
};
|
||||||
|
|
||||||
|
// From drachtio-version 3.0.40, forkAudioStart will send empty bugname, metadata together with
|
||||||
|
// bidirectionalAudio params that cause old version of freeswitch missunderstand between bugname and
|
||||||
|
// bidirectionalAudio params
|
||||||
|
this._bugname = 'audio_fork';
|
||||||
|
|
||||||
if (this.transcribe) this.transcribeTask = makeTask(logger, {'transcribe': opts.transcribe}, this);
|
if (this.transcribe) this.transcribeTask = makeTask(logger, {'transcribe': opts.transcribe}, this);
|
||||||
}
|
}
|
||||||
@@ -133,7 +146,8 @@ class TaskListen extends Task {
|
|||||||
mixType: this.mixType,
|
mixType: this.mixType,
|
||||||
sampling: this.sampleRate,
|
sampling: this.sampleRate,
|
||||||
...(this._bugname && {bugname: this._bugname}),
|
...(this._bugname && {bugname: this._bugname}),
|
||||||
metadata
|
metadata,
|
||||||
|
bidirectionalAudio: this.bidirectionalAudio || {}
|
||||||
});
|
});
|
||||||
this.recordStartTime = moment();
|
this.recordStartTime = moment();
|
||||||
if (this.maxLength) {
|
if (this.maxLength) {
|
||||||
@@ -153,7 +167,7 @@ class TaskListen extends Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* support bi-directional audio */
|
/* support bi-directional audio */
|
||||||
if (!this.disableBidirectionalAudio) {
|
if (this.bidirectionalAudio.enabled) {
|
||||||
ep.addCustomEventListener(ListenEvents.PlayAudio, this._onPlayAudio.bind(this, ep));
|
ep.addCustomEventListener(ListenEvents.PlayAudio, this._onPlayAudio.bind(this, ep));
|
||||||
}
|
}
|
||||||
ep.addCustomEventListener(ListenEvents.KillAudio, this._onKillAudio.bind(this, ep));
|
ep.addCustomEventListener(ListenEvents.KillAudio, this._onKillAudio.bind(this, ep));
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class TaskTranscribe extends SttTask {
|
|||||||
this.isContinuousAsr = true;
|
this.isContinuousAsr = true;
|
||||||
}
|
}
|
||||||
/* buffer speech for continuous asr */
|
/* buffer speech for continuous asr */
|
||||||
this._bufferedTranscripts = [];
|
this._bufferedTranscripts = [ [], [] ]; // for channel 1 and 2
|
||||||
this.bugname_prefix = 'transcribe_';
|
this.bugname_prefix = 'transcribe_';
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
}
|
}
|
||||||
@@ -326,6 +326,7 @@ class TaskTranscribe extends SttTask {
|
|||||||
// make sure this is not a transcript from answering machine detection
|
// make sure this is not a transcript from answering machine detection
|
||||||
const bugname = fsEvent.getHeader('media-bugname');
|
const bugname = fsEvent.getHeader('media-bugname');
|
||||||
const finished = fsEvent.getHeader('transcription-session-finished');
|
const finished = fsEvent.getHeader('transcription-session-finished');
|
||||||
|
const bufferedTranscripts = this._bufferedTranscripts[channel - 1];
|
||||||
if (bugname && this.bugname !== bugname) return;
|
if (bugname && this.bugname !== bugname) return;
|
||||||
if (this.paused) {
|
if (this.paused) {
|
||||||
this.logger.debug({evt}, 'TaskTranscribe:_onTranscription - paused, ignoring transcript');
|
this.logger.debug({evt}, 'TaskTranscribe:_onTranscription - paused, ignoring transcript');
|
||||||
@@ -335,14 +336,14 @@ class TaskTranscribe extends SttTask {
|
|||||||
|
|
||||||
if (this.vendor === 'deepgram' && evt.type === 'UtteranceEnd') {
|
if (this.vendor === 'deepgram' && evt.type === 'UtteranceEnd') {
|
||||||
/* we will only get this when we have set utterance_end_ms */
|
/* we will only get this when we have set utterance_end_ms */
|
||||||
if (this._bufferedTranscripts.length === 0) {
|
if (bufferedTranscripts.length === 0) {
|
||||||
this.logger.debug('Gather:_onTranscription - got UtteranceEnd event from deepgram but no buffered transcripts');
|
this.logger.debug('Gather:_onTranscription - got UtteranceEnd event from deepgram but no buffered transcripts');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.logger.debug('Gather:_onTranscription - got UtteranceEnd event from deepgram, return buffered transcript');
|
this.logger.debug('Gather:_onTranscription - got UtteranceEnd event from deepgram, return buffered transcript');
|
||||||
evt = this.consolidateTranscripts(this._bufferedTranscripts, 1, this.language, this.vendor);
|
evt = this.consolidateTranscripts(bufferedTranscripts, channel, this.language, this.vendor);
|
||||||
evt.is_final = true;
|
evt.is_final = true;
|
||||||
this._bufferedTranscripts = [];
|
this._bufferedTranscripts[channel - 1] = [];
|
||||||
this._resolve(channel, evt);
|
this._resolve(channel, evt);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -359,11 +360,11 @@ class TaskTranscribe extends SttTask {
|
|||||||
|
|
||||||
let emptyTranscript = false;
|
let emptyTranscript = false;
|
||||||
if (evt.is_final) {
|
if (evt.is_final) {
|
||||||
if (evt.alternatives[0].transcript === '' && !cs.callGone && !this.killed) {
|
if (evt.alternatives.length === 0 || evt.alternatives[0].transcript === '' && !cs.callGone && !this.killed) {
|
||||||
emptyTranscript = true;
|
emptyTranscript = true;
|
||||||
if (finished === 'true' &&
|
if (finished === 'true' &&
|
||||||
['microsoft', 'deepgram'].includes(this.vendor) &&
|
['microsoft', 'deepgram'].includes(this.vendor) &&
|
||||||
this._bufferedTranscripts.length === 0) {
|
bufferedTranscripts.length === 0) {
|
||||||
this.logger.debug({evt}, 'TaskGather:_onTranscription - got empty transcript from old gather, disregarding');
|
this.logger.debug({evt}, 'TaskGather:_onTranscription - got empty transcript from old gather, disregarding');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -376,7 +377,7 @@ class TaskTranscribe extends SttTask {
|
|||||||
'TaskGather:_onTranscription - got empty deepgram transcript during continous asr, continue listening');
|
'TaskGather:_onTranscription - got empty deepgram transcript during continous asr, continue listening');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (this.vendor === 'deepgram' && this._bufferedTranscripts.length > 0) {
|
else if (this.vendor === 'deepgram' && bufferedTranscripts.length > 0) {
|
||||||
this.logger.info({evt},
|
this.logger.info({evt},
|
||||||
'TaskGather:_onTranscription - got empty transcript from deepgram, return the buffered transcripts');
|
'TaskGather:_onTranscription - got empty transcript from deepgram, return the buffered transcripts');
|
||||||
}
|
}
|
||||||
@@ -392,11 +393,12 @@ class TaskTranscribe extends SttTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.logger.info({evt}, 'TaskGather:_onTranscription - got transcript during continous asr');
|
this.logger.info({evt}, 'TaskGather:_onTranscription - got transcript during continous asr');
|
||||||
this._bufferedTranscripts.push(evt);
|
bufferedTranscripts.push(evt);
|
||||||
this._startAsrTimer(channel);
|
this._startAsrTimer(channel);
|
||||||
|
|
||||||
/* some STT engines will keep listening after a final response, so no need to restart */
|
/* some STT engines will keep listening after a final response, so no need to restart */
|
||||||
if (!['soniox', 'aws', 'microsoft', 'deepgram'].includes(this.vendor)) this._startTranscribing(cs, ep, channel);
|
if (!['soniox', 'aws', 'microsoft', 'deepgram', 'google']
|
||||||
|
.includes(this.vendor)) this._startTranscribing(cs, ep, channel);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (this.vendor === 'soniox') {
|
if (this.vendor === 'soniox') {
|
||||||
@@ -407,19 +409,20 @@ class TaskTranscribe extends SttTask {
|
|||||||
}
|
}
|
||||||
else if (this.vendor === 'deepgram') {
|
else if (this.vendor === 'deepgram') {
|
||||||
/* compile transcripts into one */
|
/* compile transcripts into one */
|
||||||
if (!emptyTranscript) this._bufferedTranscripts.push(evt);
|
if (!emptyTranscript) bufferedTranscripts.push(evt);
|
||||||
|
|
||||||
/* deepgram can send an empty and final transcript; only if we have any buffered should we resolve */
|
/* deepgram can send an empty and final transcript; only if we have any buffered should we resolve */
|
||||||
if (this._bufferedTranscripts.length === 0) return;
|
if (bufferedTranscripts.length === 0) return;
|
||||||
evt = this.consolidateTranscripts(this._bufferedTranscripts, channel, this.language);
|
evt = this.consolidateTranscripts(bufferedTranscripts, channel, this.language);
|
||||||
this._bufferedTranscripts = [];
|
this._bufferedTranscripts[channel - 1] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* here is where we return a final transcript */
|
/* here is where we return a final transcript */
|
||||||
this.logger.debug({evt}, 'TaskTranscribe:_onTranscription - sending final transcript');
|
this.logger.debug({evt}, 'TaskTranscribe:_onTranscription - sending final transcript');
|
||||||
this._resolve(channel, evt);
|
this._resolve(channel, evt);
|
||||||
/* some STT engines will keep listening after a final response, so no need to restart */
|
/* some STT engines will keep listening after a final response, so no need to restart */
|
||||||
if (!['soniox', 'aws', 'microsoft', 'deepgram'].includes(this.vendor)) this._startTranscribing(cs, ep, channel);
|
if (!['soniox', 'aws', 'microsoft', 'deepgram', 'google']
|
||||||
|
.includes(this.vendor)) this._startTranscribing(cs, ep, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -430,7 +433,7 @@ class TaskTranscribe extends SttTask {
|
|||||||
const originalEvent = evt.vendor.evt;
|
const originalEvent = evt.vendor.evt;
|
||||||
if (originalEvent.is_final && evt.alternatives[0].transcript !== '') {
|
if (originalEvent.is_final && evt.alternatives[0].transcript !== '') {
|
||||||
this.logger.debug({evt}, 'Gather:_onTranscription - buffering a completed (partial) deepgram transcript');
|
this.logger.debug({evt}, 'Gather:_onTranscription - buffering a completed (partial) deepgram transcript');
|
||||||
this._bufferedTranscripts.push(evt);
|
bufferedTranscripts.push(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,8 +594,9 @@ class TaskTranscribe extends SttTask {
|
|||||||
this._clearAsrTimer(channel);
|
this._clearAsrTimer(channel);
|
||||||
this._asrTimer = setTimeout(() => {
|
this._asrTimer = setTimeout(() => {
|
||||||
this.logger.debug(`TaskTranscribe:_startAsrTimer - asr timer went off for channel: ${channel}`);
|
this.logger.debug(`TaskTranscribe:_startAsrTimer - asr timer went off for channel: ${channel}`);
|
||||||
const evt = this.consolidateTranscripts(this._bufferedTranscripts, channel, this.language, this.vendor);
|
const evt = this.consolidateTranscripts(
|
||||||
this._bufferedTranscripts = [];
|
this._bufferedTranscripts[channel - 1], channel, this.language, this.vendor);
|
||||||
|
this._bufferedTranscripts[channel - 1] = [];
|
||||||
this._resolve(channel, evt);
|
this._resolve(channel, evt);
|
||||||
}, this.asrTimeout);
|
}, this.asrTimeout);
|
||||||
this.logger.debug(`TaskTranscribe:_startAsrTimer: set for ${this.asrTimeout}ms for channel ${channel}`);
|
this.logger.debug(`TaskTranscribe:_startAsrTimer: set for ${this.asrTimeout}ms for channel ${channel}`);
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
"session:new",
|
"session:new",
|
||||||
"session:reconnect",
|
"session:reconnect",
|
||||||
"session:redirect",
|
"session:redirect",
|
||||||
|
"session:adulting",
|
||||||
"call:status",
|
"call:status",
|
||||||
"queue:status",
|
"queue:status",
|
||||||
"dial:confirm",
|
"dial:confirm",
|
||||||
|
|||||||
@@ -413,6 +413,7 @@ class SingleDialer extends Emitter {
|
|||||||
const app = {...application};
|
const app = {...application};
|
||||||
if ('WS' === app.call_hook?.method ||
|
if ('WS' === app.call_hook?.method ||
|
||||||
app.call_hook?.url.startsWith('ws://') || app.call_hook?.url.startsWith('wss://')) {
|
app.call_hook?.url.startsWith('ws://') || app.call_hook?.url.startsWith('wss://')) {
|
||||||
|
if (app.call_hook?.url) app.call_hook.url += '/adulting';
|
||||||
const requestor = new WsRequestor(logger, this.accountInfo.account.account_sid,
|
const requestor = new WsRequestor(logger, this.accountInfo.account.account_sid,
|
||||||
app.call_hook, this.accountInfo.account.webhook_secret);
|
app.call_hook, this.accountInfo.account.webhook_secret);
|
||||||
app.requestor = requestor;
|
app.requestor = requestor;
|
||||||
@@ -438,6 +439,13 @@ class SingleDialer extends Emitter {
|
|||||||
tasks,
|
tasks,
|
||||||
rootSpan
|
rootSpan
|
||||||
});
|
});
|
||||||
|
app.requestor.request('session:adulting', '/adulting', {
|
||||||
|
...cs.callInfo.toJSON(),
|
||||||
|
parentCallInfo: this.parentCallInfo
|
||||||
|
}).catch((err) => {
|
||||||
|
newLogger.error({err}, 'doAdulting: error sending adulting request');
|
||||||
|
});
|
||||||
|
|
||||||
cs.req = this.req;
|
cs.req = this.req;
|
||||||
cs.exec().catch((err) => newLogger.error({err}, 'doAdulting: error executing session'));
|
cs.exec().catch((err) => newLogger.error({err}, 'doAdulting: error executing session'));
|
||||||
return cs;
|
return cs;
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ const normalizeDeepgram = (evt, channel, language, shortUtterance) => {
|
|||||||
language_code: language,
|
language_code: language,
|
||||||
channel_tag: channel,
|
channel_tag: channel,
|
||||||
is_final: shortUtterance ? evt.is_final : evt.speech_final,
|
is_final: shortUtterance ? evt.is_final : evt.speech_final,
|
||||||
alternatives: [alternatives[0]],
|
alternatives: alternatives.length ? [alternatives[0]] : [],
|
||||||
vendor: {
|
vendor: {
|
||||||
name: 'deepgram',
|
name: 'deepgram',
|
||||||
evt: copy
|
evt: copy
|
||||||
@@ -487,14 +487,10 @@ module.exports = (logger) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ('google' === vendor) {
|
if ('google' === vendor) {
|
||||||
const model = task.name === TaskName.Gather ? 'command_and_search' : 'latest_long';
|
const useV2 = rOpts.googleOptions?.serviceVersion === 'v2';
|
||||||
/**
|
const model = task.name === TaskName.Gather ?
|
||||||
* When we support google v2 the models are different and we will want something like:
|
(useV2 ? 'telephony_short' : 'command_and_search') :
|
||||||
* const useV2 = sttCredentials?.credentials?.project_id; //TODO: v2 pref should be set in googleOptions
|
(useV2 ? 'long' : 'latest_long');
|
||||||
* const model = task.name === TaskName.Gather ?
|
|
||||||
* (useV2 ? 'telephony_short' : 'command_and_search') :
|
|
||||||
* (useV2 ? 'long' : 'latest_long');
|
|
||||||
*/
|
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
...(sttCredentials && {GOOGLE_APPLICATION_CREDENTIALS: JSON.stringify(sttCredentials.credentials)}),
|
...(sttCredentials && {GOOGLE_APPLICATION_CREDENTIALS: JSON.stringify(sttCredentials.credentials)}),
|
||||||
@@ -527,12 +523,26 @@ module.exports = (logger) => {
|
|||||||
...{GOOGLE_SPEECH_MODEL: rOpts.model || model},
|
...{GOOGLE_SPEECH_MODEL: rOpts.model || model},
|
||||||
...(rOpts.naicsCode > 0 && {GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE: rOpts.naicsCode}),
|
...(rOpts.naicsCode > 0 && {GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE: rOpts.naicsCode}),
|
||||||
GOOGLE_SPEECH_METADATA_RECORDING_DEVICE_TYPE: 'phone_line',
|
GOOGLE_SPEECH_METADATA_RECORDING_DEVICE_TYPE: 'phone_line',
|
||||||
/*
|
|
||||||
...(useV2 && {
|
...(useV2 && {
|
||||||
GOOGLE_SPEECH_RECOGNIZER_PARENT: `projects/${sttCredentials.credentials.project_id}/locations/global`,
|
GOOGLE_SPEECH_RECOGNIZER_PARENT: `projects/${sttCredentials.credentials.project_id}/locations/global`,
|
||||||
GOOGLE_SPEECH_CLOUD_SERVICES_VERSION: 'v2'
|
GOOGLE_SPEECH_CLOUD_SERVICES_VERSION: 'v2',
|
||||||
}),
|
...(rOpts.googleOptions?.speechStartTimeoutMs && {
|
||||||
*/
|
GOOGLE_SPEECH_START_TIMEOUT_MS: rOpts.googleOptions.speechStartTimeoutMs
|
||||||
|
}),
|
||||||
|
...(rOpts.googleOptions?.speechEndTimeoutMs && {
|
||||||
|
GOOGLE_SPEECH_END_TIMEOUT_MS: rOpts.googleOptions.speechEndTimeoutMs
|
||||||
|
}),
|
||||||
|
...(rOpts.googleOptions?.transcriptNormalization && {
|
||||||
|
GOOGLE_SPEECH_TRANSCRIPTION_NORMALIZATION: JSON.stringify(rOpts.googleOptions.transcriptNormalization)
|
||||||
|
}),
|
||||||
|
...(rOpts.googleOptions?.enableVoiceActivityEvents && {
|
||||||
|
GOOGLE_SPEECH_ENABLE_VOICE_ACTIVITY_EVENTS: rOpts.googleOptions.enableVoiceActivityEvents
|
||||||
|
}),
|
||||||
|
...(rOpts.sgoogleOptions?.recognizerId) && {GOOGLE_SPEECH_RECOGNIZER_ID: rOpts.googleOptions.recognizerId},
|
||||||
|
...(rOpts.googleOptions?.enableVoiceActivityEvents && {
|
||||||
|
GOOGLE_SPEECH_ENABLE_VOICE_ACTIVITY_EVENTS: rOpts.googleOptions.enableVoiceActivityEvents
|
||||||
|
}),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (['aws', 'polly'].includes(vendor)) {
|
else if (['aws', 'polly'].includes(vendor)) {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class WsRequestor extends BaseRequestor {
|
|||||||
type,
|
type,
|
||||||
msgid,
|
msgid,
|
||||||
call_sid: this.call_sid,
|
call_sid: this.call_sid,
|
||||||
hook: type === 'verb:hook' ? url : undefined,
|
hook: ['verb:hook', 'session:redirect'].includes(type) ? url : undefined,
|
||||||
data: {...payload},
|
data: {...payload},
|
||||||
...b3
|
...b3
|
||||||
};
|
};
|
||||||
@@ -346,7 +346,9 @@ class WsRequestor extends BaseRequestor {
|
|||||||
/* messages must be JSON format */
|
/* messages must be JSON format */
|
||||||
try {
|
try {
|
||||||
const obj = JSON.parse(content);
|
const obj = JSON.parse(content);
|
||||||
const {type, msgid, command, call_sid = this.call_sid, queueCommand = false, data} = obj;
|
//const {type, msgid, command, call_sid = this.call_sid, queueCommand = false, data} = obj;
|
||||||
|
const {type, msgid, command, queueCommand = false, data} = obj;
|
||||||
|
const call_sid = obj.callSid || this.call_sid;
|
||||||
|
|
||||||
//this.logger.debug({obj}, 'WsRequestor:request websocket: received');
|
//this.logger.debug({obj}, 'WsRequestor:request websocket: received');
|
||||||
assert.ok(type, 'type property not supplied');
|
assert.ok(type, 'type property not supplied');
|
||||||
|
|||||||
9588
package-lock.json
generated
9588
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jambonz-feature-server",
|
"name": "jambonz-feature-server",
|
||||||
"version": "0.8.6",
|
"version": "0.9.0",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.x"
|
"node": ">= 18.x"
|
||||||
@@ -25,57 +25,57 @@
|
|||||||
"jslint:fix": "eslint app.js tracer.js lib --fix"
|
"jslint:fix": "eslint app.js tracer.js lib --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-auto-scaling": "^3.360.0",
|
"@aws-sdk/client-auto-scaling": "^3.549.0",
|
||||||
"@aws-sdk/client-sns": "^3.360.0",
|
"@aws-sdk/client-sns": "^3.549.0",
|
||||||
"@jambonz/db-helpers": "^0.9.3",
|
"@jambonz/db-helpers": "^0.9.3",
|
||||||
"@jambonz/http-health-check": "^0.0.1",
|
"@jambonz/http-health-check": "^0.0.1",
|
||||||
"@jambonz/mw-registrar": "^0.2.4",
|
"@jambonz/mw-registrar": "^0.2.7",
|
||||||
"@jambonz/realtimedb-helpers": "^0.8.7",
|
"@jambonz/realtimedb-helpers": "^0.8.8",
|
||||||
"@jambonz/speech-utils": "^0.0.44",
|
"@jambonz/speech-utils": "^0.0.49",
|
||||||
"@jambonz/stats-collector": "^0.1.9",
|
"@jambonz/stats-collector": "^0.1.9",
|
||||||
"@jambonz/time-series": "^0.2.8",
|
"@jambonz/time-series": "^0.2.8",
|
||||||
"@jambonz/verb-specifications": "^0.0.64",
|
"@jambonz/verb-specifications": "^0.0.69",
|
||||||
"@opentelemetry/api": "^1.4.0",
|
"@opentelemetry/api": "^1.8.0",
|
||||||
"@opentelemetry/exporter-jaeger": "^1.9.0",
|
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-http": "^0.35.0",
|
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
||||||
"@opentelemetry/exporter-zipkin": "^1.9.0",
|
"@opentelemetry/exporter-zipkin": "^1.23.0",
|
||||||
"@opentelemetry/instrumentation": "^0.35.0",
|
"@opentelemetry/instrumentation": "^0.50.0",
|
||||||
"@opentelemetry/resources": "^1.9.0",
|
"@opentelemetry/resources": "^1.23.0",
|
||||||
"@opentelemetry/sdk-trace-base": "^1.9.0",
|
"@opentelemetry/sdk-trace-base": "^1.23.0",
|
||||||
"@opentelemetry/sdk-trace-node": "^1.9.0",
|
"@opentelemetry/sdk-trace-node": "^1.23.0",
|
||||||
"@opentelemetry/semantic-conventions": "^1.9.0",
|
"@opentelemetry/semantic-conventions": "^1.23.0",
|
||||||
"bent": "^7.3.12",
|
"bent": "^7.3.12",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"deepcopy": "^2.1.0",
|
"deepcopy": "^2.1.0",
|
||||||
"drachtio-fsmrf": "^3.0.39",
|
"drachtio-fsmrf": "^3.0.40",
|
||||||
"drachtio-srf": "^4.5.31",
|
"drachtio-srf": "^4.5.31",
|
||||||
"express": "^4.18.2",
|
"express": "^4.19.2",
|
||||||
"express-validator": "^7.0.1",
|
"express-validator": "^7.0.1",
|
||||||
"ip": "^1.1.9",
|
"ip": "^2.0.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.30.1",
|
||||||
"parse-url": "^8.1.0",
|
"parse-url": "^9.2.0",
|
||||||
"pino": "^8.8.0",
|
"pino": "^8.20.0",
|
||||||
"polly-ssml-split": "^0.1.0",
|
"polly-ssml-split": "^0.1.0",
|
||||||
"proxyquire": "^2.1.3",
|
"proxyquire": "^2.1.3",
|
||||||
"sdp-transform": "^2.14.1",
|
"sdp-transform": "^2.14.2",
|
||||||
"short-uuid": "^4.2.2",
|
"short-uuid": "^4.2.2",
|
||||||
"sinon": "^15.0.1",
|
"sinon": "^17.0.1",
|
||||||
"to-snake-case": "^1.0.0",
|
"to-snake-case": "^1.0.0",
|
||||||
"undici": "^5.28.3",
|
"undici": "^6.11.1",
|
||||||
"uuid-random": "^1.3.2",
|
"uuid-random": "^1.3.2",
|
||||||
"verify-aws-sns-signature": "^0.1.0",
|
"verify-aws-sns-signature": "^0.1.0",
|
||||||
"ws": "^8.9.0",
|
"ws": "^8.16.0",
|
||||||
"xml2js": "^0.6.2"
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"clear-module": "^4.1.2",
|
"clear-module": "^4.1.2",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-plugin-promise": "^4.3.1",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"tape": "^5.6.1"
|
"tape": "^5.7.5"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"bufferutil": "^4.0.6",
|
"bufferutil": "^4.0.8",
|
||||||
"utf-8-validate": "^5.0.8"
|
"utf-8-validate": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
freeswitch:
|
freeswitch:
|
||||||
image: drachtio/drachtio-freeswitch-mrf:0.6.2
|
image: drachtio/drachtio-freeswitch-mrf:0.7.3
|
||||||
restart: always
|
restart: always
|
||||||
command: freeswitch --rtp-range-start 20000 --rtp-range-end 20100
|
command: freeswitch --rtp-range-start 20000 --rtp-range-end 20100
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
Reference in New Issue
Block a user