diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4bfd0a87..fa26d1be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,3 +20,5 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} + MICROSOFT_REGION: ${{ secrets.MICROSOFT_REGION }} + MICROSOFT_API_KEY: ${{ secrets.MICROSOFT_API_KEY }} \ No newline at end of file diff --git a/lib/session/call-session.js b/lib/session/call-session.js index ab258064..85933519 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -560,6 +560,13 @@ class CallSession extends Emitter { api_key: credential.api_key }; } + else if ('nuance' === vendor) { + return { + speech_credential_sid: credential.speech_credential_sid, + client_id: credential.client_id, + secret: credential.secret + }; + } } else { writeAlerts({ diff --git a/lib/tasks/gather.js b/lib/tasks/gather.js index ebda4bfc..ce269fef 100644 --- a/lib/tasks/gather.js +++ b/lib/tasks/gather.js @@ -3,25 +3,22 @@ const { TaskName, TaskPreconditions, GoogleTranscriptionEvents, + NuanceTranscriptionEvents, AwsTranscriptionEvents, AzureTranscriptionEvents } = require('../utils/constants'); const makeTask = require('./make_task'); const assert = require('assert'); -//const GATHER_STABILITY_THRESHOLD = Number(process.env.JAMBONZ_GATHER_STABILITY_THRESHOLD || 0.7); const compileTranscripts = (logger, evt, arr) => { - //logger.debug({arr, evt}, 'compile transcripts'); if (!Array.isArray(arr) || arr.length === 0) return; let t = ''; for (const a of arr) { - //logger.debug(`adding ${a.alternatives[0].transcript}`); t += ` ${a.alternatives[0].transcript}`; } t += ` ${evt.alternatives[0].transcript}`; evt.alternatives[0].transcript = t.trim(); - //logger.debug(`compiled transcript: ${evt.alternatives[0].transcript}`); }; class TaskGather extends Task { @@ -29,6 +26,15 @@ class TaskGather extends Task { super(logger, opts); this.preconditions = TaskPreconditions.Endpoint; + const { + setChannelVarsForStt, + normalizeTranscription, + removeSpeechListeners + } = require('../utils/transcription-utils')(logger); + this.setChannelVarsForStt = setChannelVarsForStt; + this.normalizeTranscription = normalizeTranscription; + this.removeSpeechListeners = removeSpeechListeners; + [ 'finishOnKey', 'hints', 'input', 'numDigits', 'minDigits', 'maxDigits', 'interDigitTimeout', 'partialResultHook', 'bargein', 'dtmfBargein', @@ -47,47 +53,23 @@ class TaskGather extends Task { const recognizer = this.data.recognizer; this.vendor = recognizer.vendor; this.language = recognizer.language; - this.hints = recognizer.hints || []; - this.hintsBoost = recognizer.hintsBoost; - this.profanityFilter = recognizer.profanityFilter; - this.punctuation = !!recognizer.punctuation; - this.enhancedModel = !!recognizer.enhancedModel; - this.model = recognizer.model || 'command_and_search'; - this.words = !!recognizer.words; - this.singleUtterance = recognizer.singleUtterance || true; - this.diarization = !!recognizer.diarization; - this.diarizationMinSpeakers = recognizer.diarizationMinSpeakers || 0; - this.diarizationMaxSpeakers = recognizer.diarizationMaxSpeakers || 0; - this.interactionType = recognizer.interactionType || 'unspecified'; - this.naicsCode = recognizer.naicsCode || 0; - this.altLanguages = recognizer.altLanguages || []; + + if (recognizer.vendor === 'nuance') { + const {clientId, secret} = recognizer.nuanceOptions; + if (clientId && secret) { + this.sttCredentials = {client_id: clientId, secret}; + } + } /* continuous ASR (i.e. compile transcripts until a special timeout or dtmf key) */ this.asrTimeout = typeof recognizer.asrTimeout === 'number' ? recognizer.asrTimeout * 1000 : 0; if (this.asrTimeout > 0) this.asrDtmfTerminationDigit = recognizer.asrDtmfTerminationDigit; this.isContinuousAsr = this.asrTimeout > 0; - /* vad: if provided, we dont connect to recognizer until voice activity is detected */ - const {enable, voiceMs = 0, mode = -1} = recognizer.vad || {}; - this.vad = {enable, voiceMs, mode}; - - /* aws options */ - this.vocabularyName = recognizer.vocabularyName; - this.vocabularyFilterName = recognizer.vocabularyFilterName; - this.filterMethod = recognizer.filterMethod; - - /* microsoft options */ - this.outputFormat = recognizer.outputFormat || 'simple'; - this.profanityOption = recognizer.profanityOption || 'raw'; - this.requestSnr = recognizer.requestSnr || false; - this.initialSpeechTimeoutMs = recognizer.initialSpeechTimeoutMs || 0; - this.azureServiceEndpoint = recognizer.azureServiceEndpoint; - this.azureSttEndpointId = recognizer.azureSttEndpointId; - } - else { - this.hints = []; - this.altLanguages = []; + this.data.recognizer.hints = this.data.recognizer.hints || []; + this.data.recognizer.altLanguages = this.data.recognizer.altLanguages || []; } + else this.data.recognizer = {hints: [], altLanguages: []}; this.digitBuffer = ''; this._earlyMedia = this.data.earlyMedia === true; @@ -134,21 +116,22 @@ class TaskGather extends Task { this.logger.debug('Gather:exec'); await super.exec(cs); const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf); + const {getNuanceAccessToken} = cs.srf.locals.dbHelpers; if (cs.hasGlobalSttHints) { const {hints, hintsBoost} = cs.globalSttHints; - this.hints = this.hints.concat(hints); - if (!this.hintsBoost && hintsBoost) this.hintsBoost = hintsBoost; - this.logger.debug({hints: this.hints, hintsBoost: this.hintsBoost}, + this.data.recognizer.hints = this.data.recognizer.hints.concat(hints); + if (!this.data.recognizer.hintsBoost && hintsBoost) this.data.recognizer.hintsBoost = hintsBoost; + this.logger.debug({hints: this.data.recognizer.hints, hintsBoost: this.data.recognizer.hintsBoost}, 'Gather:exec - applying global sttHints'); } if (cs.hasAltLanguages) { - this.altLanguages = this.altLanguages.concat(cs.altLanguages); + this.data.recognizer.altLanguages = this.data.recognizer.altLanguages.concat(cs.altLanguages); this.logger.debug({altLanguages: this.altLanguages}, 'Gather:exec - applying altLanguages'); } - if (cs.hasGlobalSttPunctuation) { - this.punctuation = cs.globalSttPunctuation; + if (cs.hasGlobalSttPunctuation && !this.data.recognizer.punctuation) { + this.data.recognizer.punctuation = cs.globalSttPunctuation; } if (!this.isContinuousAsr && cs.isContinuousAsr) { this.isContinuousAsr = true; @@ -162,7 +145,8 @@ class TaskGather extends Task { this.ep = ep; if ('default' === this.vendor || !this.vendor) this.vendor = cs.speechRecognizerVendor; if ('default' === this.language || !this.language) this.language = cs.speechRecognizerLanguage; - this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt'); + + if (this.needsStt && !this.sttCredentials) this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt'); if (this.needsStt && !this.sttCredentials) { const {writeAlerts, AlertType} = cs.srf.locals; this.logger.info(`TaskGather:exec - ERROR stt using ${this.vendor} requested but creds not supplied`); @@ -175,16 +159,27 @@ class TaskGather extends Task { throw new Error(`no speech-to-text service credentials for ${this.vendor} have been configured`); } + this.logger.info({sttCredentials: this.sttCredentials}, 'Gather:exec - sttCredentials'); + if (this.vendor === 'nuance' && this.sttCredentials.client_id) { + /* get nuance access token */ + const {client_id, secret} = this.sttCredentials; + const {access_token, servedFromCache} = await getNuanceAccessToken(client_id, secret, 'asr tts'); + this.logger.debug({client_id}, `Gather:exec - got nuance access token ${servedFromCache ? 'from cache' : ''}`); + this.sttCredentials = {...this.sttCredentials, access_token}; + } const startListening = (cs, ep) => { this._startTimer(); if (this.isContinuousAsr && 0 === this.timeout) this._startAsrTimer(); if (this.input.includes('speech') && !this.listenDuringPrompt) { + this.logger.debug('Gather:exec - calling _initSpeech'); this._initSpeech(cs, ep) .then(() => { this._startTranscribing(ep); return updateSpeechCredentialLastUsed(this.sttCredentials.speech_credential_sid); }) - .catch(() => {}); + .catch((err) => { + this.logger.error({err}, 'error in initSpeech'); + }); } }; @@ -198,7 +193,15 @@ class TaskGather extends Task { span.end(); if (err) this.logger.error({err}, 'Gather:exec Error playing tts'); this.logger.debug('Gather: nested say task completed'); - if (!this.killed) startListening(cs, ep); + if (!this.killed) { + startListening(cs, ep); + if (this.input.includes('speech') && this.vendor === 'nuance' && this.listenDuringPrompt) { + this.logger.debug('Gather:exec - starting transcription timers after say completes'); + ep.startTranscriptionTimers((err) => { + if (err) this.logger.error({err}, 'Gather:exec - error starting transcription timers'); + }); + } + } }); } else if (this.playTask) { @@ -210,7 +213,15 @@ class TaskGather extends Task { span.end(); if (err) this.logger.error({err}, 'Gather:exec Error playing url'); this.logger.debug('Gather: nested play task completed'); - if (!this.killed) startListening(cs, ep); + if (!this.killed) { + startListening(cs, ep); + if (this.input.includes('speech') && this.vendor === 'nuance' && this.listenDuringPrompt) { + this.logger.debug('Gather:exec - starting transcription timers after play completes'); + ep.startTranscriptionTimers((err) => { + if (err) this.logger.error({err}, 'Gather:exec - error starting transcription timers'); + }); + } + } }); } else startListening(cs, ep); @@ -230,14 +241,7 @@ class TaskGather extends Task { } catch (err) { this.logger.error(err, 'TaskGather:exec error'); } - ep.removeCustomEventListener(GoogleTranscriptionEvents.Transcription); - ep.removeCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance); - ep.removeCustomEventListener(GoogleTranscriptionEvents.VadDetected); - ep.removeCustomEventListener(AwsTranscriptionEvents.Transcription); - ep.removeCustomEventListener(AwsTranscriptionEvents.VadDetected); - ep.removeCustomEventListener(AzureTranscriptionEvents.Transcription); - ep.removeCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected); - ep.removeCustomEventListener(AzureTranscriptionEvents.VadDetected); + this.removeSpeechListeners(ep); } kill(cs) { @@ -292,106 +296,52 @@ class TaskGather extends Task { } async _initSpeech(cs, ep) { - const opts = {}; + const opts = this.setChannelVarsForStt(this, this.sttCredentials, this.data.recognizer); + this.logger.debug(opts, 'TaskGather:_initSpeech - channel vars'); + switch (this.vendor) { + case 'google': + this.bugname = 'google_transcribe'; + ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); + ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, cs, ep)); + ep.addCustomEventListener(GoogleTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep)); + break; - if (this.vad?.enable) { - opts.START_RECOGNIZING_ON_VAD = 1; - if (this.vad.voiceMs) opts.RECOGNIZER_VAD_VOICE_MS = this.vad.voiceMs; - else opts.RECOGNIZER_VAD_VOICE_MS = 125; - if (this.vad.mode >= 0 && this.vad.mode <= 3) opts.RECOGNIZER_VAD_MODE = this.vad.mode; - } + case 'aws': + case 'polly': + this.bugname = 'aws_transcribe'; + ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); + ep.addCustomEventListener(AwsTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep)); + break; + case 'microsoft': + this.bugname = 'azure_transcribe'; + ep.addCustomEventListener(AzureTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); + ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected, + this._onNoSpeechDetected.bind(this, cs, ep)); + ep.addCustomEventListener(AzureTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep)); + break; + case 'nuance': + this.bugname = 'nuance_transcribe'; + ep.addCustomEventListener(NuanceTranscriptionEvents.Transcription, + this._onTranscription.bind(this, cs, ep)); + ep.addCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech, + this._onStartOfSpeech.bind(this, cs, ep)); + ep.addCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete, + this._onTranscriptionComplete.bind(this, cs, ep)); + ep.addCustomEventListener(NuanceTranscriptionEvents.VadDetected, + this._onVadDetected.bind(this, cs, ep)); + ep.addCustomEventListener(NuanceTranscriptionEvents.Error, + this._onNuanceError.bind(this, cs, ep)); - if ('google' === this.vendor) { - this.bugname = 'google_transcribe'; - if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials); - [ - ['enhancedModel', 'GOOGLE_SPEECH_USE_ENHANCED'], - ['separateRecognitionPerChannel', 'GOOGLE_SPEECH_SEPARATE_RECOGNITION_PER_CHANNEL'], - ['profanityFilter', 'GOOGLE_SPEECH_PROFANITY_FILTER'], - ['punctuation', 'GOOGLE_SPEECH_ENABLE_AUTOMATIC_PUNCTUATION'], - ['words', 'GOOGLE_SPEECH_ENABLE_WORD_TIME_OFFSETS'], - ['singleUtterance', 'GOOGLE_SPEECH_SINGLE_UTTERANCE'], - ['diarization', 'GOOGLE_SPEECH_PROFANITY_FILTER'] - ].forEach((arr) => { - if (this[arr[0]]) opts[arr[1]] = true; - else if (this[arr[0]] === false) opts[arr[1]] = false; - }); - if (this.hints.length > 0) { - opts.GOOGLE_SPEECH_HINTS = this.hints.join(','); - if (typeof this.hintsBoost === 'number') { - opts.GOOGLE_SPEECH_HINTS_BOOST = this.hintsBoost; + /* stall timers until prompt finishes playing */ + if ((this.sayTask || this.playTask) && this.listenDuringPrompt) { + opts.NUANCE_STALL_TIMERS = 1; } - } - if (this.altLanguages.length > 0) opts.GOOGLE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = this.altLanguages.join(','); - else opts.GOOGLE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = ''; - if ('unspecified' !== this.interactionType) { - opts.GOOGLE_SPEECH_METADATA_INTERACTION_TYPE = this.interactionType; - } - opts.GOOGLE_SPEECH_MODEL = this.model; - if (this.diarization && this.diarizationMinSpeakers > 0) { - opts.GOOGLE_SPEECH_SPEAKER_DIARIZATION_MIN_SPEAKER_COUNT = this.diarizationMinSpeakers; - } - if (this.diarization && this.diarizationMaxSpeakers > 0) { - opts.GOOGLE_SPEECH_SPEAKER_DIARIZATION_MAX_SPEAKER_COUNT = this.diarizationMaxSpeakers; - } - if (this.naicsCode > 0) opts.GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE = this.naicsCode; - ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); - ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, cs, ep)); - ep.addCustomEventListener(GoogleTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep)); - } - else if (['aws', 'polly'].includes(this.vendor)) { - this.bugname = 'aws_transcribe'; - if (this.vocabularyName) opts.AWS_VOCABULARY_NAME = this.vocabularyName; - if (this.vocabularyFilterName) { - opts.AWS_VOCABULARY_NAME = this.vocabularyFilterName; - opts.AWS_VOCABULARY_FILTER_METHOD = this.filterMethod || 'mask'; - } - if (this.sttCredentials) { - Object.assign(opts, { - AWS_ACCESS_KEY_ID: this.sttCredentials.accessKeyId, - AWS_SECRET_ACCESS_KEY: this.sttCredentials.secretAccessKey, - AWS_REGION: this.sttCredentials.region - }); - } - ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); - ep.addCustomEventListener(AwsTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep)); - } - 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': 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(','); - } - if (this.altLanguages && this.altLanguages.length > 0) { - opts.AZURE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = this.altLanguages.join(','); - } - else { - opts.AZURE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = ''; - } - if (this.requestSnr) opts.AZURE_REQUEST_SNR = 1; - if (this.profanityOption && this.profanityOption !== 'raw') opts.AZURE_PROFANITY_OPTION = this.profanityOption; - if (this.azureServiceEndpoint) opts.AZURE_SERVICE_ENDPOINT = this.azureServiceEndpoint; - if (this.initialSpeechTimeoutMs > 0) opts.AZURE_INITIAL_SPEECH_TIMEOUT_MS = this.initialSpeechTimeoutMs; - else if (this.timeout === 0) opts.AZURE_INITIAL_SPEECH_TIMEOUT_MS = 120000; // lengthy - opts.AZURE_USE_OUTPUT_FORMAT_DETAILED = 1; - - ep.addCustomEventListener(AzureTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); - ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected, this._onNoSpeechDetected.bind(this, cs, ep)); - ep.addCustomEventListener(AzureTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep)); + break; + default: + throw new Error(`Invalid vendor ${this.vendor}`); } + await ep.set(opts) .catch((err) => this.logger.info(err, 'Error setting channel variables')); } @@ -489,40 +439,12 @@ class TaskGather extends Task { _onTranscription(cs, ep, evt, fsEvent) { // make sure this is not a transcript from answering machine detection + this.logger.debug({evt}, 'Gather:_onTranscription'); const bugname = fsEvent.getHeader('media-bugname'); const finished = fsEvent.getHeader('transcription-session-finished'); if (bugname && this.bugname !== bugname) return; - if ('aws' === this.vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0]; - if ('microsoft' === this.vendor) { - const final = evt.RecognitionStatus === 'Success'; - if (final) { - // don't sort based on confidence: https://github.com/Azure-Samples/cognitive-services-speech-sdk/issues/1463 - //const nbest = evt.NBest.sort((a, b) => b.Confidence - a.Confidence); - const nbest = evt.NBest; - const language_code = evt.PrimaryLanguage?.Language || this.language; - evt = { - is_final: true, - language_code, - alternatives: [ - { - confidence: nbest[0].Confidence, - transcript: nbest[0].Display - } - ] - }; - } - else { - evt = { - is_final: false, - alternatives: [ - { - transcript: evt.Text - } - ] - }; - } - } + evt = this.normalizeTranscription(evt, this.vendor, 1, this.language); /* count words for bargein feature */ const words = evt.alternatives[0].transcript.split(' ').length; @@ -607,6 +529,24 @@ class TaskGather extends Task { } } + _onStartOfSpeech(cs, ep) { + this.logger.debug('TaskGather:_onStartOfSpeech'); + } + _onTranscriptionComplete(cs, ep) { + this.logger.debug('TaskGather:_onTranscriptionComplete'); + } + _onNuanceError(cs, ep, evt) { + const {code, error, details} = evt; + if (code === 404 && error === 'No speech') { + this.logger.debug({code, error, details}, 'TaskGather:_onNuanceError'); + return this._resolve('timeout'); + } + this.logger.info({code, error, details}, 'TaskGather:_onNuanceError'); + if (code === 413 && error === 'Too much speech') { + return this._resolve('timeout'); + } + } + _onVadDetected(cs, ep) { if (this.bargein && this.minBargeinWordCount === 0) { this.logger.debug('TaskGather:_onVadDetected'); diff --git a/lib/tasks/say.js b/lib/tasks/say.js index 8fa13eaf..2b1d59ba 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -130,14 +130,24 @@ class TaskSay extends Task { const language = this.synthesizer.language && this.synthesizer.language !== 'default' ? this.synthesizer.language : cs.speechSynthesisLanguage ; - const voice = this.synthesizer.voice && this.synthesizer.voice !== 'default' ? + let voice = this.synthesizer.voice && this.synthesizer.voice !== 'default' ? this.synthesizer.voice : cs.speechSynthesisVoice; const engine = this.synthesizer.engine || 'standard'; const salt = cs.callSid; const credentials = cs.getSpeechCredentials(vendor, 'tts'); - this.logger.info({vendor, language, voice}, 'TaskSay:exec'); + /* parse Nuance voces into name and model */ + let model; + if (vendor === 'nuance' && voice) { + const arr = /([A-Za-z-]*)\s+-\s+(enhanced|standard)/.exec(voice); + if (arr) { + voice = arr[1]; + model = arr[2]; + } + } + + this.logger.info({vendor, language, voice, model}, 'TaskSay:exec'); this.ep = ep; try { if (!credentials) { @@ -170,6 +180,7 @@ class TaskSay extends Task { language, voice, engine, + model, salt, credentials }); diff --git a/lib/tasks/specs.json b/lib/tasks/specs.json index d1636e80..b0dda68f 100644 --- a/lib/tasks/specs.json +++ b/lib/tasks/specs.json @@ -445,7 +445,7 @@ "properties": { "vendor": { "type": "string", - "enum": ["google", "aws", "microsoft", "default"] + "enum": ["google", "aws", "microsoft", "nuance", "default"] }, "language": "string", "vad": "#vad", @@ -509,12 +509,121 @@ "azureServiceEndpoint": "string", "azureSttEndpointId": "string", "asrDtmfTerminationDigit": "string", - "asrTimeout": "number" + "asrTimeout": "number", + "nuanceOptions": "#nuanceOptions" }, "required": [ "vendor" ] }, + "nuanceOptions": { + "properties": { + "clientId": "string", + "secret": "string", + "kryptonEndpoint": "string", + "topic": "string", + "utteranceDetectionMode": { + "type": "string", + "enum": [ + "single", + "multiple", + "disabled" + ] + }, + "punctuation": "boolean", + "profanityFilter": "boolean", + "includeTokenization": "boolean", + "discardSpeakerAdaptation": "boolean", + "suppressCallRecording": "boolean", + "maskLoadFailures": "boolean", + "suppressInitialCapitalization": "boolean", + "allowZeroBaseLmWeight": "boolean", + "filterWakeupWord": "boolean", + "resultType": { + "type": "string", + "enum": [ + "final", + "partial", + "immutable_partial" + ] + }, + "noInputTimeoutMs": "number", + "recognitionTimeoutMs": "number", + "utteranceEndSilenceMs": "number", + "maxHypotheses": "number", + "speechDomain": "string", + "formatting": "#formatting", + "clientData": "object", + "userId": "string", + "speechDetectionSensitivity": "number", + "resources": ["#resource"] + }, + "required": [ + ] + }, + "resource": { + "properties": { + "externalReference": "#resourceReference", + "inlineWordset": "string", + "builtin": "string", + "inlineGrammar": "string", + "wakeupWord": "[string]", + "weightName": { + "type": "string", + "enum": [ + "defaultWeight", + "lowest", + "low", + "medium", + "high", + "highest" + ] + }, + "weightValue": "number", + "reuse": { + "type": "string", + "enum": [ + "undefined_reuse", + "low_reuse", + "high_reuse" + ] + } + }, + "required": [ + ] + }, + "resourceReference": { + "properties": { + "type": { + "type": "string", + "enum": [ + "undefined_resource_type", + "wordset", + "compiled_wordset", + "domain_lm", + "speaker_profile", + "grammar", + "settings" + ] + }, + "uri": "string", + "maxLoadFailures": "boolean", + "requestTimeoutMs": "number", + "headers": "object" + }, + "required": [ + ] + }, + "formatting": { + "properties": { + "scheme": "string", + "options": "object" + }, + "required": [ + "scheme", + "options" + ] + }, "lexIntent": { "properties": { "name": "string", diff --git a/lib/tasks/transcribe.js b/lib/tasks/transcribe.js index cb10c09b..6f7ad915 100644 --- a/lib/tasks/transcribe.js +++ b/lib/tasks/transcribe.js @@ -4,8 +4,10 @@ const { TaskPreconditions, GoogleTranscriptionEvents, AzureTranscriptionEvents, - AwsTranscriptionEvents + AwsTranscriptionEvents, + NuanceTranscriptionEvents } = require('../utils/constants'); +const normalizeJambones = require('../utils/normalize-jambones'); class TaskTranscribe extends Task { constructor(logger, opts, parentTask) { @@ -13,6 +15,10 @@ class TaskTranscribe extends Task { this.preconditions = TaskPreconditions.Endpoint; this.parentTask = parentTask; + const {setChannelVarsForStt, normalizeTranscription} = require('../utils/transcription-utils')(logger); + this.setChannelVarsForStt = setChannelVarsForStt; + this.normalizeTranscription = normalizeTranscription; + this.transcriptionHook = this.data.transcriptionHook; this.earlyMedia = this.data.earlyMedia === true || (parentTask && parentTask.earlyMedia); @@ -22,39 +28,15 @@ class TaskTranscribe extends Task { this.interim = !!recognizer.interim; this.separateRecognitionPerChannel = recognizer.separateRecognitionPerChannel; - /* vad: if provided, we dont connect to recognizer until voice activity is detected */ - const {enable, voiceMs = 0, mode = -1} = recognizer.vad || {}; - this.vad = {enable, voiceMs, mode}; + if (recognizer.vendor === 'nuance') { + const {clientId, secret} = recognizer.nuanceOptions; + if (clientId && secret) { + this.sttCredentials = {client_id: clientId, secret}; + } + } - /* google-specific options */ - this.hints = recognizer.hints || []; - this.hintsBoost = recognizer.hintsBoost; - this.profanityFilter = recognizer.profanityFilter; - this.punctuation = !!recognizer.punctuation; - this.enhancedModel = !!recognizer.enhancedModel; - this.model = recognizer.model || 'phone_call'; - this.words = !!recognizer.words; - this.singleUtterance = recognizer.singleUtterance || false; - this.diarization = !!recognizer.diarization; - this.diarizationMinSpeakers = recognizer.diarizationMinSpeakers || 0; - this.diarizationMaxSpeakers = recognizer.diarizationMaxSpeakers || 0; - this.interactionType = recognizer.interactionType || 'unspecified'; - this.naicsCode = recognizer.naicsCode || 0; - this.altLanguages = recognizer.altLanguages || []; - - /* aws-specific options */ - this.identifyChannels = !!recognizer.identifyChannels; - this.vocabularyName = recognizer.vocabularyName; - this.vocabularyFilterName = recognizer.vocabularyFilterName; - this.filterMethod = recognizer.filterMethod; - - /* microsoft options */ - this.outputFormat = recognizer.outputFormat || 'simple'; - this.profanityOption = recognizer.profanityOption || 'raw'; - this.requestSnr = recognizer.requestSnr || false; - this.initialSpeechTimeoutMs = recognizer.initialSpeechTimeoutMs || 0; - this.azureServiceEndpoint = recognizer.azureServiceEndpoint; - this.azureSttEndpointId = recognizer.azureSttEndpointId; + recognizer.hints = recognizer.hints || []; + recognizer.altLanguages = recognizer.altLanguages || []; } get name() { return TaskName.Transcribe; } @@ -62,21 +44,22 @@ class TaskTranscribe extends Task { async exec(cs, {ep, ep2}) { super.exec(cs); const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf); + const {getNuanceAccessToken} = cs.srf.locals.dbHelpers; if (cs.hasGlobalSttHints) { const {hints, hintsBoost} = cs.globalSttHints; - this.hints = this.hints.concat(hints); - if (!this.hintsBoost && hintsBoost) this.hintsBoost = hintsBoost; - this.logger.debug({hints: this.hints, hintsBoost: this.hintsBoost}, - 'Transcribe:exec - applying global `sttHints'); + this.data.recognizer.hints = this.data.recognizer.hints.concat(hints); + if (!this.data.recognizer.hintsBoost && hintsBoost) this.data.recognizer.hintsBoost = hintsBoost; + this.logger.debug({hints: this.data.recognizer.hints, hintsBoost: this.data.recognizer.hintsBoost}, + 'Transcribe:exec - applying global sttHints'); } if (cs.hasAltLanguages) { - this.altLanguages = this.altLanguages.concat(cs.altLanguages); + this.data.recognizer.altLanguages = this.data.recognizer.altLanguages.concat(cs.altLanguages); this.logger.debug({altLanguages: this.altLanguages}, - 'Gather:exec - applying altLanguages'); + 'Transcribe:exec - applying altLanguages'); } - if (cs.hasGlobalSttPunctuation) { - this.punctuation = cs.globalSttPunctuation; + if (cs.hasGlobalSttPunctuation && !this.data.recognizer.punctuation) { + this.data.recognizer.punctuation = cs.globalSttPunctuation; } this.ep = ep; @@ -96,6 +79,16 @@ class TaskTranscribe extends Task { }).catch((err) => this.logger.info({err}, 'Error generating alert for no stt')); throw new Error('no provisioned speech credentials for TTS'); } + + if (this.vendor === 'nuance' && this.sttCredentials.client_id) { + /* get nuance access token */ + const {client_id, secret} = this.sttCredentials; + const {access_token, servedFromCache} = await getNuanceAccessToken(client_id, secret, 'asr tts'); + this.logger.debug({client_id}, + `Transcribe:exec - got nuance access token ${servedFromCache ? 'from cache' : ''}`); + this.sttCredentials = {...this.sttCredentials, access_token}; + } + await this._startTranscribing(cs, ep, 1); if (this.separateRecognitionPerChannel && ep2) { await this._startTranscribing(cs, ep2, 2); @@ -110,13 +103,21 @@ class TaskTranscribe extends Task { this.parentTask && this.parentTask.emit('error', err); } ep.removeCustomEventListener(GoogleTranscriptionEvents.Transcription); - ep.removeCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected); - ep.removeCustomEventListener(GoogleTranscriptionEvents.MaxDurationExceeded); + ep.removeCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance); + ep.removeCustomEventListener(GoogleTranscriptionEvents.VadDetected); + ep.removeCustomEventListener(AwsTranscriptionEvents.Transcription); - ep.removeCustomEventListener(AwsTranscriptionEvents.NoAudioDetected); - ep.removeCustomEventListener(AwsTranscriptionEvents.MaxDurationExceeded); + ep.removeCustomEventListener(AwsTranscriptionEvents.VadDetected); + ep.removeCustomEventListener(AzureTranscriptionEvents.Transcription); ep.removeCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected); + ep.removeCustomEventListener(AzureTranscriptionEvents.VadDetected); + + ep.removeCustomEventListener(NuanceTranscriptionEvents.Transcription); + ep.removeCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete); + ep.removeCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech); + ep.removeCustomEventListener(NuanceTranscriptionEvents.Error); + ep.removeCustomEventListener(NuanceTranscriptionEvents.VadDetected); } async kill(cs) { @@ -140,124 +141,53 @@ class TaskTranscribe extends Task { } async _startTranscribing(cs, ep, channel) { - const opts = {}; + const opts = this.setChannelVarsForStt(this, this.sttCredentials, this.data.recognizer); + switch (this.vendor) { + case 'google': + this.bugname = 'google_transcribe'; + ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, + this._onTranscription.bind(this, cs, ep, channel)); + ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected, + this._onNoAudio.bind(this, cs, ep, channel)); + ep.addCustomEventListener(GoogleTranscriptionEvents.MaxDurationExceeded, + this._onMaxDurationExceeded.bind(this, cs, ep, channel)); + break; - if (this.vad.enable) { - opts.START_RECOGNIZING_ON_VAD = 1; - if (this.vad.voiceMs) opts.RECOGNIZER_VAD_VOICE_MS = this.vad.voiceMs; - if (this.vad.mode >= 0 && this.vad.mode <= 3) opts.RECOGNIZER_VAD_MODE = this.vad.mode; + case 'aws': + case 'polly': + this.bugname = 'aws_transcribe'; + ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, + this._onTranscription.bind(this, cs, ep, channel)); + ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected, + this._onNoAudio.bind(this, cs, ep, channel)); + ep.addCustomEventListener(AwsTranscriptionEvents.MaxDurationExceeded, + this._onMaxDurationExceeded.bind(this, cs, ep, channel)); + break; + case 'microsoft': + this.bugname = 'azure_transcribe'; + ep.addCustomEventListener(AzureTranscriptionEvents.Transcription, + this._onTranscription.bind(this, cs, ep, channel)); + ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected, + this._onNoAudio.bind(this, cs, ep, channel)); + break; + case 'nuance': + this.bugname = 'nuance_transcribe'; + ep.addCustomEventListener(NuanceTranscriptionEvents.Transcription, + this._onTranscription.bind(this, cs, ep, channel)); + ep.addCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech, + this._onStartOfSpeech.bind(this, cs, ep, channel)); + ep.addCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete, + this._onTranscriptionComplete.bind(this, cs, ep, channel)); + ep.addCustomEventListener(AzureTranscriptionEvents.Error, + this._onNuanceError.bind(this, cs, ep, channel)); + break; + default: + throw new Error(`Invalid vendor ${this.vendor}`); } - ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, - this._onTranscription.bind(this, cs, ep, channel)); - ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, cs, ep, channel)); - ep.addCustomEventListener(GoogleTranscriptionEvents.MaxDurationExceeded, - this._onMaxDurationExceeded.bind(this, cs, ep, channel)); - ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep, channel)); - ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, cs, ep, channel)); - ep.addCustomEventListener(AwsTranscriptionEvents.MaxDurationExceeded, - this._onMaxDurationExceeded.bind(this, cs, ep, channel)); - ep.addCustomEventListener(AzureTranscriptionEvents.Transcription, - this._onTranscription.bind(this, cs, ep, channel)); - ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected, this._onNoAudio.bind(this, cs, ep, channel)); + await ep.set(opts) + .catch((err) => this.logger.info(err, 'Error setting channel variables')); - if (this.vendor === 'google') { - this.bugname = 'google_transcribe'; - if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials); - [ - ['enhancedModel', 'GOOGLE_SPEECH_USE_ENHANCED'], - //['separateRecognitionPerChannel', 'GOOGLE_SPEECH_SEPARATE_RECOGNITION_PER_CHANNEL'], - ['profanityFilter', 'GOOGLE_SPEECH_PROFANITY_FILTER'], - ['punctuation', 'GOOGLE_SPEECH_ENABLE_AUTOMATIC_PUNCTUATION'], - ['words', 'GOOGLE_SPEECH_ENABLE_WORD_TIME_OFFSETS'], - ['singleUtterance', 'GOOGLE_SPEECH_SINGLE_UTTERANCE'], - ['diarization', 'GOOGLE_SPEECH_PROFANITY_FILTER'] - ].forEach((arr) => { - if (this[arr[0]]) opts[arr[1]] = true; - else if (this[arr[0]] === false) opts[arr[1]] = false; - }); - if (this.hints.length > 0) { - opts.GOOGLE_SPEECH_HINTS = this.hints.join(','); - if (typeof this.hintsBoost === 'number') { - opts.GOOGLE_SPEECH_HINTS_BOOST = this.hintsBoost; - } - } - if (this.altLanguages.length > 0) opts.GOOGLE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = this.altLanguages.join(','); - else opts.GOOGLE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = ''; - if ('unspecified' !== this.interactionType) { - opts.GOOGLE_SPEECH_METADATA_INTERACTION_TYPE = this.interactionType; - } - opts.GOOGLE_SPEECH_MODEL = this.model; - if (this.diarization && this.diarizationMinSpeakers > 0) { - opts.GOOGLE_SPEECH_SPEAKER_DIARIZATION_MIN_SPEAKER_COUNT = this.diarizationMinSpeakers; - } - if (this.diarization && this.diarizationMaxSpeakers > 0) { - opts.GOOGLE_SPEECH_SPEAKER_DIARIZATION_MAX_SPEAKER_COUNT = this.diarizationMaxSpeakers; - } - if (this.naicsCode > 0) opts.GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE = this.naicsCode; - - await ep.set(opts) - .catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with google')); - } - else if (this.vendor === 'aws') { - this.bugname = 'aws_transcribe'; - [ - ['diarization', 'AWS_SHOW_SPEAKER_LABEL'], - ['identifyChannels', 'AWS_ENABLE_CHANNEL_IDENTIFICATION'] - ].forEach((arr) => { - if (this[arr[0]]) opts[arr[1]] = true; - }); - if (this.vocabularyName) opts.AWS_VOCABULARY_NAME = this.vocabularyName; - if (this.vocabularyFilterName) { - opts.AWS_VOCABULARY_NAME = this.vocabularyFilterName; - opts.AWS_VOCABULARY_FILTER_METHOD = this.filterMethod || 'mask'; - } - - if (this.sttCredentials) { - Object.assign(opts, { - AWS_ACCESS_KEY_ID: this.sttCredentials.accessKeyId, - AWS_SECRET_ACCESS_KEY: this.sttCredentials.secretAccessKey, - AWS_REGION: this.sttCredentials.region - }); - } - else { - Object.assign(opts, { - AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, - AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, - AWS_REGION: process.env.AWS_REGION - }); - } - - await ep.set(opts) - .catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with aws')); - } - else if (this.vendor === 'microsoft') { - this.bugname = 'azure_transcribe'; - const {api_key, region, use_custom_stt, custom_stt_endpoint} = this.sttCredentials; - Object.assign(opts, { - '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(','); - } - if (this.altLanguages.length > 0) opts.AZURE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = this.altLanguages.join(','); - else opts.AZURE_SPEECH_ALTERNATIVE_LANGUAGE_CODES = ''; - if (this.requestSnr) opts.AZURE_REQUEST_SNR = 1; - if (this.profanityOption !== 'raw') opts.AZURE_PROFANITY_OPTION = this.profanityOption; - if (this.initialSpeechTimeoutMs > 0) opts.AZURE_INITIAL_SPEECH_TIMEOUT_MS = this.initialSpeechTimeoutMs; - if (this.outputFormat !== 'simple') opts.AZURE_USE_OUTPUT_FORMAT_DETAILED = 1; - if (this.azureServiceEndpoint) opts.AZURE_SERVICE_ENDPOINT = this.azureServiceEndpoint; - - await ep.set(opts) - .catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with azure')); - } await this._transcribe(ep); } @@ -271,50 +201,43 @@ class TaskTranscribe extends Task { }); } - _onTranscription(cs, ep, channel, evt, fsEvent) { + async _onTranscription(cs, ep, channel, evt, fsEvent) { // make sure this is not a transcript from answering machine detection const bugname = fsEvent.getHeader('media-bugname'); if (bugname && this.bugname !== bugname) return; - this.logger.debug({evt, channel}, 'TaskTranscribe:_onTranscription'); - if ('aws' === this.vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0]; - if ('microsoft' === this.vendor) { - const nbest = evt.NBest; - const language_code = evt.PrimaryLanguage?.Language || this.language; - const alternatives = nbest ? nbest.map((n) => { - return { - confidence: n.Confidence, - transcript: n.Display - }; - }) : - [ - { - transcript: evt.DisplayText || evt.Text - } - ]; + this.logger.debug({evt}, 'TaskTranscribe:_onTranscription - before normalization'); - const newEvent = { - is_final: evt.RecognitionStatus === 'Success', - channel, - language_code, - alternatives - }; - evt = newEvent; - } + evt = this.normalizeTranscription(evt, this.vendor, channel, this.language); + + this.logger.debug({evt}, 'TaskTranscribe:_onTranscription'); if (evt.alternatives[0].transcript === '' && !cs.callGone && !this.killed) { this.logger.info({evt}, 'TaskGather:_onTranscription - got empty transcript, listen again'); return this._transcribe(ep); } - evt.channel_tag = channel; - if (this.transcriptionHook) { const b3 = this.getTracingPropagation(); const httpHeaders = b3 && {b3}; - this.cs.requestor.request('verb:hook', this.transcriptionHook, - Object.assign({speech: evt}, this.cs.callInfo), httpHeaders) - .catch((err) => this.logger.info(err, 'TranscribeTask:_onTranscription error')); + try { + const json = await this.cs.requestor.request('verb:hook', this.transcriptionHook, { + ...this.cs.callInfo, + ...httpHeaders, + speech: evt + }); + this.logger.info({json}, 'sent transcriptionHook'); + if (json && Array.isArray(json) && !this.parentTask) { + const makeTask = require('./make_task'); + const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); + if (tasks && tasks.length > 0) { + this.logger.info({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`); + this.cs.replaceApplication(tasks); + } + } + } catch (err) { + this.logger.info(err, 'TranscribeTask:_onTranscription error'); + } } if (this.parentTask) { this.parentTask.emit('transcription', evt); diff --git a/lib/utils/constants.json b/lib/utils/constants.json index e33bc730..c901a7f6 100644 --- a/lib/utils/constants.json +++ b/lib/utils/constants.json @@ -67,6 +67,13 @@ "MaxDurationExceeded": "google_transcribe::max_duration_exceeded", "VadDetected": "google_transcribe::vad_detected" }, + "NuanceTranscriptionEvents": { + "Transcription": "nuance_transcribe::transcription", + "StartOfSpeech": "nuance_transcribe::start_of_speech", + "TranscriptionComplete": "nuance_transcribe::end_of_transcription", + "Error": "nuance_transcribe::error", + "VadDetected": "nuance_transcribe::vad_detected" + }, "AwsTranscriptionEvents": { "Transcription": "aws_transcribe::transcription", "EndOfTranscript": "aws_transcribe::end_of_transcript", diff --git a/lib/utils/db-utils.js b/lib/utils/db-utils.js index 40ab32d9..ba9ef20d 100644 --- a/lib/utils/db-utils.js +++ b/lib/utils/db-utils.js @@ -44,7 +44,13 @@ const speechMapper = (cred) => { const o = JSON.parse(decrypt(credential)); obj.api_key = o.api_key; } + else if ('nuance' === obj.vendor) { + const o = JSON.parse(decrypt(credential)); + obj.client_id = o.client_id; + obj.secret = o.secret; + } } catch (err) { + console.log(err); } return obj; }; @@ -65,7 +71,8 @@ module.exports = (logger, srf) => { const haveAws = speech.find((s) => s.vendor === 'aws'); const haveMicrosoft = speech.find((s) => s.vendor === 'microsoft'); const haveWellsaid = speech.find((s) => s.vendor === 'wellsaid'); - if (!haveGoogle || !haveAws || !haveMicrosoft) { + const haveNuance = speech.find((s) => s.vendor === 'nuance'); + if (!haveGoogle || !haveAws || !haveMicrosoft || !haveWellsaid || !haveNuance) { const [r3] = await pp.query(sqlSpeechCredentialsForSP, account_sid); if (r3.length) { if (!haveGoogle) { @@ -84,6 +91,10 @@ module.exports = (logger, srf) => { const wellsaid = r3.find((s) => s.vendor === 'wellsaid'); if (wellsaid) speech.push(speechMapper(wellsaid)); } + if (!haveNuance) { + const nuance = r3.find((s) => s.vendor === 'nuance'); + if (nuance) speech.push(speechMapper(nuance)); + } } } @@ -94,6 +105,7 @@ module.exports = (logger, srf) => { }; const updateSpeechCredentialLastUsed = async(speech_credential_sid) => { + if (!speech_credential_sid) return; const pp = pool.promise(); const sql = 'UPDATE speech_credentials SET last_used = NOW() WHERE speech_credential_sid = ?'; try { diff --git a/lib/utils/install-srf-locals.js b/lib/utils/install-srf-locals.js index a5c6baf1..8f84e6a5 100644 --- a/lib/utils/install-srf-locals.js +++ b/lib/utils/install-srf-locals.js @@ -152,7 +152,8 @@ function installSrfLocals(srf, logger) { popFront, removeFromList, lengthOfList, - getListPosition + getListPosition, + getNuanceAccessToken } = require('@jambonz/realtimedb-helpers')({ host: process.env.JAMBONES_REDIS_HOST, port: process.env.JAMBONES_REDIS_PORT || 6379 @@ -204,7 +205,8 @@ function installSrfLocals(srf, logger) { popFront, removeFromList, lengthOfList, - getListPosition + getListPosition, + getNuanceAccessToken }, parentLogger: logger, getSBC, diff --git a/lib/utils/transcription-utils.js b/lib/utils/transcription-utils.js index ec53e4d7..cf01022f 100644 --- a/lib/utils/transcription-utils.js +++ b/lib/utils/transcription-utils.js @@ -1,9 +1,32 @@ +const { + TaskName, + AzureTranscriptionEvents, + GoogleTranscriptionEvents, + AwsTranscriptionEvents, + NuanceTranscriptionEvents +} = require('./constants'); + module.exports = (logger) => { - const normalizeTranscription = (evt, vendor, channel) => { - if ('aws' === vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0]; - if ('microsoft' === vendor) { + const normalizeTranscription = (evt, vendor, channel, language) => { + let newEvent = JSON.parse(JSON.stringify(evt)); + + /* add in channel_tag and provide the full vendor-specific event */ + newEvent = { + ...(vendor === 'aws' ? newEvent[0] : newEvent), + language_code: language, + channel_tag: channel + }; + + + if ('aws' === vendor && Array.isArray(evt) && evt.length > 0) { + newEvent = { + ...newEvent, + vendor: {event: evt, name: vendor} + }; + } + else if ('microsoft' === vendor) { const nbest = evt.NBest; - const language_code = evt.PrimaryLanguage?.Language || this.language; + const language_code = evt.PrimaryLanguage?.Language || language; const alternatives = nbest ? nbest.map((n) => { return { confidence: n.Confidence, @@ -16,18 +39,194 @@ module.exports = (logger) => { } ]; - const newEvent = { + newEvent = { + ...newEvent, is_final: evt.RecognitionStatus === 'Success', channel, language_code, - alternatives + alternatives, + vendor: {event: evt, name: vendor} }; - evt = newEvent; } - evt.channel_tag = channel; - //logger.debug({evt}, 'normalized transcription'); - return evt; + return newEvent; }; - return {normalizeTranscription}; + const setChannelVarsForStt = (task, sttCredentials, rOpts = {}) => { + let opts = {}; + const {enable, voiceMs = 0, mode = -1} = rOpts.vad || {}; + const vad = {enable, voiceMs, mode}; + + /* voice activity detection works across vendors */ + opts = { + ...opts, + ...(vad.enable && {START_RECOGNIZING_ON_VAD: 1}), + ...(vad.enable && vad.voiceMs && {RECOGNIZER_VAD_VOICE_MS: vad.voiceMs}), + ...(vad.enable && typeof vad.mode === 'number' && {RECOGNIZER_VAD_MODE: vad.mode}), + }; + + if ('google' === rOpts.vendor) { + opts = { + ...opts, + ...(sttCredentials && + {GOOGLE_APPLICATION_CREDENTIALS: JSON.stringify(sttCredentials.credentials)}), + ...(rOpts.enhancedModel && + {GOOGLE_SPEECH_USE_ENHANCED: 1}), + ...(rOpts.separateRecognitionPerChannel && + {GOOGLE_SPEECH_SEPARATE_RECOGNITION_PER_CHANNEL: 1}), + ...(rOpts.profanityFilter && + {GOOGLE_SPEECH_PROFANITY_FILTER: 1}), + ...(rOpts.punctuation && + {GOOGLE_SPEECH_ENABLE_AUTOMATIC_PUNCTUATION: 1}), + ...(rOpts.words && + {GOOGLE_SPEECH_ENABLE_WORD_TIME_OFFSETS: 1}), + ...((rOpts.singleUtterance || task.name === TaskName.Gather) && + {GOOGLE_SPEECH_SINGLE_UTTERANCE: 1}), + ...(rOpts.diarization && + {GOOGLE_SPEECH_SPEAKER_DIARIZATION: 1}), + ...(rOpts.diarization && rOpts.diarizationMinSpeakers > 0 && + {GOOGLE_SPEECH_SPEAKER_DIARIZATION_MIN_SPEAKER_COUNT: rOpts.diarizationMinSpeakers}), + ...(rOpts.diarization && rOpts.diarizationMaxSpeakers > 0 && + {GOOGLE_SPEECH_SPEAKER_DIARIZATION_MAX_SPEAKER_COUNT: rOpts.diarizationMaxSpeakers}), + ...(rOpts.enhancedModel === false && + {GOOGLE_SPEECH_USE_ENHANCED: 0}), + ...(rOpts.separateRecognitionPerChannel === false && + {GOOGLE_SPEECH_SEPARATE_RECOGNITION_PER_CHANNEL: 0}), + ...(rOpts.profanityFilter === false && + {GOOGLE_SPEECH_PROFANITY_FILTER: 0}), + ...(rOpts.punctuation === false && + {GOOGLE_SPEECH_ENABLE_AUTOMATIC_PUNCTUATION: 0}), + ...(rOpts.words == false && + {GOOGLE_SPEECH_ENABLE_WORD_TIME_OFFSETS: 0}), + ...((rOpts.singleUtterance === false || task.name === TaskName.Transcribe) && + {GOOGLE_SPEECH_SINGLE_UTTERANCE: 0}), + ...(rOpts.diarization === false && + {GOOGLE_SPEECH_SPEAKER_DIARIZATION: 0}), + ...(rOpts.hints.length > 0 && + {GOOGLE_SPEECH_HINTS: rOpts.hints.join(',')}), + ...(typeof rOpts.hintsBoost === 'number' && + {GOOGLE_SPEECH_HINTS_BOOST: rOpts.hintsBoost}), + ...(rOpts.altLanguages.length > 0 && + {GOOGLE_SPEECH_ALTERNATIVE_LANGUAGE_CODES: rOpts.altLanguages.join(',')}), + ...(rOpts.interactionType && + {GOOGLE_SPEECH_METADATA_INTERACTION_TYPE: rOpts.interactionType}), + ...{GOOGLE_SPEECH_MODEL: rOpts.model || (task.name === TaskName.Gather ? 'command_and_search' : 'phone_call')}, + ...(rOpts.naicsCode > 0 && + {GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE: rOpts.naicsCode}), + }; + } + else if (['aws', 'polly'].includes(rOpts.vendor)) { + opts = { + ...opts, + ...(rOpts.vocabularyName && {AWS_VOCABULARY_NAME: rOpts.vocabularyName}), + ...(rOpts.vocabularyFilterName && {AWS_VOCABULARY_FILTER_NAME: rOpts.vocabularyFilterName}), + ...(rOpts.filterMethod && {AWS_VOCABULARY_FILTER_METHOD: rOpts.filterMethod}), + ...(sttCredentials && { + AWS_ACCESS_KEY_ID: sttCredentials.accessKeyId, + AWS_SECRET_ACCESS_KEY: sttCredentials.secretAccessKey, + AWS_REGION: sttCredentials.region + }), + }; + } + else if ('microsoft' === rOpts.vendor) { + opts = { + ...opts, + ...(rOpts.hints && rOpts.hints.length > 0 && + {AZURE_SPEECH_HINTS: rOpts.hints.map((h) => h.trim()).join(',')}), + ...(rOpts.altLanguages && rOpts.altLanguages.length > 0 && + {AZURE_SERVICE_ENDPOINT_ID: rOpts.sttCredentials}), + ...(rOpts.requestSnr && {AZURE_REQUEST_SNR: 1}), + ...(rOpts.profanityOption && {AZURE_PROFANITY_OPTION: rOpts.profanityOption}), + ...(rOpts.azureServiceEndpoint && {AZURE_SERVICE_ENDPOINT: rOpts.azureServiceEndpoint}), + ...(rOpts.initialSpeechTimeoutMs > 0 && + {AZURE_INITIAL_SPEECH_TIMEOUT_MS: rOpts.initialSpeechTimeoutMs}), + ...(rOpts.requestSnr && {AZURE_REQUEST_SNR: 1}), + ...(rOpts.audioLogging && {AZURE_AUDIO_LOGGING: 1}), + ...{AZURE_USE_OUTPUT_FORMAT_DETAILED: 1}, + ...(sttCredentials && { + AZURE_SUBSCRIPTION_KEY: sttCredentials.api_key, + AZURE_REGION: sttCredentials.region, + }), + ...(sttCredentials.use_custom_stt && sttCredentials.custom_stt_endpoint && + {AZURE_SERVICE_ENDPOINT_ID: sttCredentials.custom_stt_endpoint}) + }; + } + else if ('nuance' === rOpts.vendor) { + /** + * Note: all nuance options are in recognizer.nuanceOptions, should migrate + * other vendor settings to similar nested structure + */ + const {nuanceOptions = {}} = rOpts; + opts = { + ...opts, + ...(sttCredentials.access_token) && + {NUANCE_ACCESS_TOKEN: sttCredentials.access_token}, + ...(sttCredentials.krypton_endpoint) && + {NUANCE_KRYPTON_ENDPOINT: sttCredentials.krypton_endpoint}, + ...(nuanceOptions.topic) && + {NUANCE_TOPIC: nuanceOptions.topic}, + ...(nuanceOptions.utteranceDetectionMode) && + {NUANCE_UTTERANCE_DETECTION_MODE: nuanceOptions.utteranceDetectionMode}, + ...(nuanceOptions.punctuation) && {NUANCE_PUNCTUATION: nuanceOptions.punctuation}, + ...(nuanceOptions.profanityFilter) && + {NUANCE_FILTER_PROFANITY: nuanceOptions.profanityFilter}, + ...(nuanceOptions.includeTokenization) && + {NUANCE_INCLUDE_TOKENIZATION: nuanceOptions.includeTokenization}, + ...(nuanceOptions.discardSpeakerAdaptation) && + {NUANCE_DISCARD_SPEAKER_ADAPTATION: nuanceOptions.discardSpeakerAdaptation}, + ...(nuanceOptions.suppressCallRecording) && + {NUANCE_SUPPRESS_CALL_RECORDING: nuanceOptions.suppressCallRecording}, + ...(nuanceOptions.maskLoadFailures) && + {NUANCE_MASK_LOAD_FAILURES: nuanceOptions.maskLoadFailures}, + ...(nuanceOptions.suppressInitialCapitalization) && + {NUANCE_SUPPRESS_INITIAL_CAPITALIZATION: nuanceOptions.suppressInitialCapitalization}, + ...(nuanceOptions.allowZeroBaseLmWeight) + && {NUANCE_ALLOW_ZERO_BASE_LM_WEIGHT: nuanceOptions.allowZeroBaseLmWeight}, + ...(nuanceOptions.filterWakeupWord) && + {NUANCE_FILTER_WAKEUP_WORD: nuanceOptions.filterWakeupWord}, + ...(nuanceOptions.resultType) && + {NUANCE_RESULT_TYPE: nuanceOptions.resultType || rOpts.interim ? 'partial' : 'final'}, + ...(nuanceOptions.noInputTimeoutMs) && + {NUANCE_NO_INPUT_TIMEOUT_MS: nuanceOptions.noInputTimeoutMs}, + ...(nuanceOptions.recognitionTimeoutMs) && + {NUANCE_RECOGNITION_TIMEOUT_MS: nuanceOptions.recognitionTimeoutMs}, + ...(nuanceOptions.utteranceEndSilenceMs) && + {NUANCE_UTTERANCE_END_SILENCE_MS: nuanceOptions.utteranceEndSilenceMs}, + ...(nuanceOptions.maxHypotheses) && + {NUANCE_MAX_HYPOTHESES: nuanceOptions.maxHypotheses}, + ...(nuanceOptions.speechDomain) && + {NUANCE_SPEECH_DOMAIN: nuanceOptions.speechDomain}, + ...(nuanceOptions.formatting) && + {NUANCE_FORMATTING: nuanceOptions.formatting}, + ...(nuanceOptions.resources) && + {NUANCE_RESOURCES: JSON.stringify(nuanceOptions.resources)}, + }; + } + logger.debug({opts}, 'recognizer channel vars'); + return opts; + }; + + const removeSpeechListeners = (ep) => { + ep.removeCustomEventListener(GoogleTranscriptionEvents.Transcription); + ep.removeCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance); + ep.removeCustomEventListener(GoogleTranscriptionEvents.VadDetected); + + ep.removeCustomEventListener(AwsTranscriptionEvents.Transcription); + ep.removeCustomEventListener(AwsTranscriptionEvents.VadDetected); + + ep.removeCustomEventListener(AzureTranscriptionEvents.Transcription); + ep.removeCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected); + ep.removeCustomEventListener(AzureTranscriptionEvents.VadDetected); + + ep.removeCustomEventListener(NuanceTranscriptionEvents.Transcription); + ep.removeCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete); + ep.removeCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech); + ep.removeCustomEventListener(NuanceTranscriptionEvents.Error); + ep.removeCustomEventListener(NuanceTranscriptionEvents.VadDetected); + + }; + return { + normalizeTranscription, + setChannelVarsForStt, + removeSpeechListeners + }; }; diff --git a/package-lock.json b/package-lock.json index 09b9c80a..75b6fe83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,26 +11,26 @@ "dependencies": { "@jambonz/db-helpers": "^0.7.0", "@jambonz/http-health-check": "^0.0.1", - "@jambonz/realtimedb-helpers": "^0.4.35", + "@jambonz/realtimedb-helpers": "^0.5.1", "@jambonz/stats-collector": "^0.1.6", "@jambonz/time-series": "^0.2.5", - "@opentelemetry/api": "^1.1.0", - "@opentelemetry/exporter-jaeger": "^1.3.1", + "@opentelemetry/api": "^1.2.0", + "@opentelemetry/exporter-jaeger": "^1.7.0", "@opentelemetry/exporter-trace-otlp-http": "^0.27.0", - "@opentelemetry/exporter-zipkin": "^1.3.1", + "@opentelemetry/exporter-zipkin": "^1.7.0", "@opentelemetry/instrumentation": "^0.27.0", - "@opentelemetry/resources": "^1.3.1", - "@opentelemetry/sdk-trace-base": "^1.3.1", - "@opentelemetry/sdk-trace-node": "^1.3.1", - "@opentelemetry/semantic-conventions": "^1.3.1", - "aws-sdk": "^2.1152.0", + "@opentelemetry/resources": "^1.7.0", + "@opentelemetry/sdk-trace-base": "^1.7.0", + "@opentelemetry/sdk-trace-node": "^1.7.0", + "@opentelemetry/semantic-conventions": "^1.7.0", + "aws-sdk": "^2.1233.0", "bent": "^7.3.12", "debug": "^4.3.4", "deepcopy": "^2.1.0", - "drachtio-fsmrf": "^3.0.3", - "drachtio-srf": "^4.5.1", - "express": "^4.18.1", - "helmet": "^5.1.0", + "drachtio-fsmrf": "^3.0.5", + "drachtio-srf": "^4.5.18", + "express": "^4.18.2", + "helmet": "^5.1.1", "ip": "^1.1.8", "moment": "^2.29.4", "parse-url": "^8.1.0", @@ -38,10 +38,10 @@ "sdp-transform": "^2.14.1", "short-uuid": "^4.2.0", "to-snake-case": "^1.0.0", - "undici": "^5.8.2", + "undici": "^5.11.0", "uuid": "^8.3.2", "verify-aws-sns-signature": "^0.1.0", - "ws": "^8.8.0", + "ws": "^8.9.0", "xml2js": "^0.4.23" }, "devDependencies": { @@ -49,7 +49,7 @@ "eslint": "^7.32.0", "eslint-plugin-promise": "^4.3.1", "nyc": "^15.1.0", - "tape": "^5.5.3" + "tape": "^5.6.1" }, "engines": { "node": ">= 10.16.0" @@ -543,16 +543,18 @@ } }, "node_modules/@jambonz/realtimedb-helpers": { - "version": "0.4.35", - "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.35.tgz", - "integrity": "sha512-lfNTlWRnLbOKVRDto1nUgOmr2jlmOHxslg+Zs9dSB8eEEkIqCNZn9f5kxxXThVpRSrCtsgGsX/w1wXygGiTn5w==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.5.1.tgz", + "integrity": "sha512-8elXD2W6tYTPBcDLlBx/n4q6SQXeRIFkv9wib+zDAU9qEdUJgTSA/nXpZqUBO1ucXEoGOrD3KworrkEBmwX58g==", "dependencies": { "@google-cloud/text-to-speech": "^4.0.3", + "@grpc/grpc-js": "^1.7.3", "@jambonz/promisify-redis": "^0.0.6", "aws-sdk": "^2.1238.0", "bent": "^7.3.12", "debug": "^4.3.3", "form-urlencoded": "^6.1.0", + "google-protobuf": "^3.21.2", "microsoft-cognitiveservices-speech-sdk": "^1.24.0", "redis": "^3.1.2", "undici": "^5.11.0" @@ -577,9 +579,9 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.1.0.tgz", - "integrity": "sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==", "engines": { "node": ">=8.0.0" } @@ -593,108 +595,47 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.3.1.tgz", - "integrity": "sha512-NKUY3SGiEEIOD3EpB8erpEF4K1iyXkWald1vJMaa973+EPTASNSXvzf8hZa7nhnUVxYbxtTJqbSRsZFfbZpw4g==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.7.0.tgz", + "integrity": "sha512-g4bMzyVW5dVBeMkyadaf3NRFpmNrdD4Pp9OJsrP29HwIam/zVMNfIWQpT5IBzjtTSMhl/ED5YQYR+UOSjVq3sQ==", "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.7.0.tgz", + "integrity": "sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ==", "dependencies": { - "@opentelemetry/semantic-conventions": "1.3.1" + "@opentelemetry/semantic-conventions": "1.7.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", - "engines": { - "node": ">=8.12.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/exporter-jaeger": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.3.1.tgz", - "integrity": "sha512-uJ9811zn5TTdazyTNc4xmcDnKC8H63VRGp23ujGTxBOCFUnFzfI/kUGUJ8/O7Xok9Ulop7wuuBW3onL1WedfjA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.7.0.tgz", + "integrity": "sha512-Hq7FO7X6bqAHiXq/7aZ6yrSn9fkPQnEyFHfsIEU+pwUJey5YJkplc0LfvTBbLEqoLOkJewbUgSvP5WTz55oSpw==", "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/sdk-trace-base": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1", + "@opentelemetry/core": "1.7.0", + "@opentelemetry/sdk-trace-base": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0", "jaeger-client": "^3.15.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/resources": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.3.1.tgz", - "integrity": "sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==", - "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz", - "integrity": "sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==", - "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", - "engines": { - "node": ">=8.12.0" - } - }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.27.0.tgz", @@ -765,75 +706,22 @@ } }, "node_modules/@opentelemetry/exporter-zipkin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.3.1.tgz", - "integrity": "sha512-Pygzv8gDeeOY2ykbQ3zElz850xJ15e5SfUzTyHJCPVWdsZSMxrMPpCt2uHArGAo+rwZZXDqbAc6oARkrGIdwDQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.7.0.tgz", + "integrity": "sha512-SsF3nt4vXl7ER89Mgysq2tFJZBsNhqX21PbU6jfqXbVfw/hw2IFtcya+IG3m5bI5r6RDbVGeWkOlMPmeH34idQ==", "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/sdk-trace-base": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" + "@opentelemetry/core": "1.7.0", + "@opentelemetry/resources": "1.7.0", + "@opentelemetry/sdk-trace-base": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.3.1.tgz", - "integrity": "sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==", - "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz", - "integrity": "sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==", - "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", - "engines": { - "node": ">=8.12.0" - } - }, "node_modules/@opentelemetry/instrumentation": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.27.0.tgz", @@ -863,125 +751,81 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.3.1.tgz", - "integrity": "sha512-tEAtHsRr6l3glsmKaJiJ/7HDw/isPv+f8OBsWJqkSlfLicKes8T/1D7nEDC6jPACiEbD3f6oK1KQSpMijC9/UQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.7.0.tgz", + "integrity": "sha512-8kKGS1KwArvkThdhubMZlomuREE9FaBcn9L4JrYHh2jly1FZpqOtFNO2byHymVRjH59d43Pa+eJuFpD0Fp7kSw==", "dependencies": { - "@opentelemetry/core": "1.3.1" + "@opentelemetry/core": "1.7.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.3.1.tgz", - "integrity": "sha512-H6swQcjZ8aMCS5caZaEBaadfn205IqLlB3ZyY+tCWDf5YPwJgPpjw3qgYgWulHVSEzK7VQTle/mZG7u9MAe6Pw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.7.0.tgz", + "integrity": "sha512-V7i/L1bx+R/ve4z6dTdn2jtvFxGThRsXS2wNb/tWZVfV8gqnePQp+HfoLrqB/Yz2iRPUcMWrcjx6vV78umvJFA==", "dependencies": { - "@opentelemetry/core": "1.3.1" + "@opentelemetry/core": "1.7.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/resources": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.3.1.tgz", - "integrity": "sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.7.0.tgz", + "integrity": "sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg==", "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" + "@opentelemetry/core": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", - "engines": { - "node": ">=8.12.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz", - "integrity": "sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz", + "integrity": "sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg==", "dependencies": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" + "@opentelemetry/core": "1.7.0", + "@opentelemetry/resources": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "engines": { - "node": ">=8.12.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", - "engines": { - "node": ">=8.12.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.3.1.tgz", - "integrity": "sha512-4sn/pYhaVaEI8WY0arivM77858IM5BjUKvymjJ+HmRNWBocCJKCCCY4P9cL8w8iCGGmst5yxecMyvM7OOFBnmg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.7.0.tgz", + "integrity": "sha512-DCAAbi0Zbb1pIofQcKzoAVy9/6bz24asFYeLb4fW/8QYAaawDnxumA++5Huw/RcYdJs8q8AIRBykwjYWWCm/5A==", "dependencies": { - "@opentelemetry/context-async-hooks": "1.3.1", - "@opentelemetry/core": "1.3.1", - "@opentelemetry/propagator-b3": "1.3.1", - "@opentelemetry/propagator-jaeger": "1.3.1", - "@opentelemetry/sdk-trace-base": "1.3.1", + "@opentelemetry/context-async-hooks": "1.7.0", + "@opentelemetry/core": "1.7.0", + "@opentelemetry/propagator-b3": "1.7.0", + "@opentelemetry/propagator-jaeger": "1.7.0", + "@opentelemetry/sdk-trace-base": "1.7.0", "semver": "^7.3.5" }, "engines": { - "node": ">=8.12.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.2.0" + "@opentelemetry/api": ">=1.0.0 <1.3.0" } }, "node_modules/@opentelemetry/sdk-trace-node/node_modules/semver": { @@ -999,11 +843,11 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz", + "integrity": "sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA==", "engines": { - "node": ">=8.12.0" + "node": ">=14" } }, "node_modules/@protobufjs/aspromise": { @@ -1483,9 +1327,9 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -1495,7 +1339,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", + "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -2030,15 +1874,15 @@ } }, "node_modules/drachtio-fsmrf": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.3.tgz", - "integrity": "sha512-RJpGS+lX3Cmqd725zJGf1KvONH38/9YDHkPxXevmiVBp+iD/EbRps/ze5wpE0MKbjEA93DOmiXGTWqsOChWttw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.5.tgz", + "integrity": "sha512-ll5YJYflvLSLxkms8uIg+dzhGGdLtc/+fb1ch/oRxqyaJSGQ3ZQ7X7itieyORJOCxtfuuDmVQZR3ZjJ/mF2Wug==", "dependencies": { "camel-case": "^4.1.2", "debug": "^2.6.9", "delegates": "^0.1.0", "drachtio-modesl": "^1.2.4", - "drachtio-srf": "^4.5.0", + "drachtio-srf": "^4.5.18", "only": "^0.0.2", "sdp-transform": "^2.14.1", "snake-case": "^3.0.4", @@ -2085,9 +1929,9 @@ } }, "node_modules/drachtio-srf": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.1.tgz", - "integrity": "sha512-7U8hhMrVIc33ysSUcbg64fDcx2jvqY4h/NdzhYsiU6tQ2fbkd0jzNYR6KD3dqhEzGxRAeEgcaPuQEG0+K2qVIA==", + "version": "4.5.18", + "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.18.tgz", + "integrity": "sha512-qF9uTLsG5kotdXwG6QXdymzzLCVqwEGjWJOBnU41855I+yP8XaUKdtpwCN8xSI1TGd0Mww/00yEsxofKpZuwsA==", "dependencies": { "debug": "^3.2.7", "delegates": "^0.1.0", @@ -2248,17 +2092,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-abstract/node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-abstract/node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -2684,13 +2517,13 @@ } }, "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", @@ -2709,7 +2542,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -3197,6 +3030,11 @@ "node": ">=12.0.0" } }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, "node_modules/graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -3268,9 +3106,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" }, @@ -3292,17 +3130,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag/node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasha": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.1.tgz", @@ -3333,9 +3160,9 @@ "integrity": "sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg==" }, "node_modules/helmet": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.0.tgz", - "integrity": "sha512-klsunXs8rgNSZoaUrNeuCiWUxyc+wzucnEnFejUg3/A+CaF589k9qepLZZ1Jehnzig7YbD4hEuscGXuBY3fq+g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.1.tgz", + "integrity": "sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ==", "engines": { "node": ">=12.0.0" } @@ -3771,17 +3598,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-symbol/node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", @@ -4744,13 +4560,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -5218,9 +5034,9 @@ "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, @@ -5965,9 +5781,9 @@ "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==" }, "node_modules/tape": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-5.5.3.tgz", - "integrity": "sha512-hPBJZBL9S7bH9vECg/KSM24slGYV589jJr4dmtiJrLD71AL66+8o4b9HdZazXZyvnilqA7eE8z5/flKiy0KsBg==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.6.1.tgz", + "integrity": "sha512-reNzS3rzsJtKk0f+zJx2XlzIsjJXlIcOIrIxk5shHAG/DzW3BKyMg8UfN79oluYlcWo4lIt56ahLqwgpRT4idg==", "dev": true, "dependencies": { "array.prototype.every": "^1.1.3", @@ -5977,23 +5793,26 @@ "dotignore": "^0.1.2", "for-each": "^0.3.3", "get-package-type": "^0.1.0", - "glob": "^7.2.0", + "glob": "^7.2.3", "has": "^1.0.3", "has-dynamic-import": "^2.0.1", "inherits": "^2.0.4", "is-regex": "^1.1.4", "minimist": "^1.2.6", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", + "object.assign": "^4.1.4", "resolve": "^2.0.0-next.3", "resumer": "^0.0.0", - "string.prototype.trim": "^1.2.5", + "string.prototype.trim": "^1.2.6", "through": "^2.3.8" }, "bin": { "tape": "bin/tape" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/tape/node_modules/glob": { @@ -6238,17 +6057,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unbox-primitive/node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", @@ -6559,9 +6367,9 @@ } }, "node_modules/ws": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", - "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz", + "integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==", "engines": { "node": ">=10.0.0" }, @@ -7080,16 +6888,18 @@ } }, "@jambonz/realtimedb-helpers": { - "version": "0.4.35", - "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.35.tgz", - "integrity": "sha512-lfNTlWRnLbOKVRDto1nUgOmr2jlmOHxslg+Zs9dSB8eEEkIqCNZn9f5kxxXThVpRSrCtsgGsX/w1wXygGiTn5w==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.5.1.tgz", + "integrity": "sha512-8elXD2W6tYTPBcDLlBx/n4q6SQXeRIFkv9wib+zDAU9qEdUJgTSA/nXpZqUBO1ucXEoGOrD3KworrkEBmwX58g==", "requires": { "@google-cloud/text-to-speech": "^4.0.3", + "@grpc/grpc-js": "^1.7.3", "@jambonz/promisify-redis": "^0.0.6", "aws-sdk": "^2.1238.0", "bent": "^7.3.12", "debug": "^4.3.3", "form-urlencoded": "^6.1.0", + "google-protobuf": "^3.21.2", "microsoft-cognitiveservices-speech-sdk": "^1.24.0", "redis": "^3.1.2", "undici": "^5.11.0" @@ -7114,9 +6924,9 @@ } }, "@opentelemetry/api": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.1.0.tgz", - "integrity": "sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==" }, "@opentelemetry/api-metrics": { "version": "0.27.0", @@ -7124,69 +6934,28 @@ "integrity": "sha512-tB79288bwjkdhPNpw4UdOEy3bacVwtol6Que7cAu8KEJ9ULjRfSiwpYEwJY/oER3xZ7zNFz0uiJ7N1jSiotpVA==" }, "@opentelemetry/context-async-hooks": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.3.1.tgz", - "integrity": "sha512-NKUY3SGiEEIOD3EpB8erpEF4K1iyXkWald1vJMaa973+EPTASNSXvzf8hZa7nhnUVxYbxtTJqbSRsZFfbZpw4g==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.7.0.tgz", + "integrity": "sha512-g4bMzyVW5dVBeMkyadaf3NRFpmNrdD4Pp9OJsrP29HwIam/zVMNfIWQpT5IBzjtTSMhl/ED5YQYR+UOSjVq3sQ==", "requires": {} }, "@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.7.0.tgz", + "integrity": "sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ==", "requires": { - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "dependencies": { - "@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" - } + "@opentelemetry/semantic-conventions": "1.7.0" } }, "@opentelemetry/exporter-jaeger": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.3.1.tgz", - "integrity": "sha512-uJ9811zn5TTdazyTNc4xmcDnKC8H63VRGp23ujGTxBOCFUnFzfI/kUGUJ8/O7Xok9Ulop7wuuBW3onL1WedfjA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.7.0.tgz", + "integrity": "sha512-Hq7FO7X6bqAHiXq/7aZ6yrSn9fkPQnEyFHfsIEU+pwUJey5YJkplc0LfvTBbLEqoLOkJewbUgSvP5WTz55oSpw==", "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/sdk-trace-base": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1", + "@opentelemetry/core": "1.7.0", + "@opentelemetry/sdk-trace-base": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0", "jaeger-client": "^3.15.0" - }, - "dependencies": { - "@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "requires": { - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/resources": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.3.1.tgz", - "integrity": "sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==", - "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/sdk-trace-base": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz", - "integrity": "sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==", - "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" - } } }, "@opentelemetry/exporter-trace-otlp-http": { @@ -7234,48 +7003,14 @@ } }, "@opentelemetry/exporter-zipkin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.3.1.tgz", - "integrity": "sha512-Pygzv8gDeeOY2ykbQ3zElz850xJ15e5SfUzTyHJCPVWdsZSMxrMPpCt2uHArGAo+rwZZXDqbAc6oARkrGIdwDQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.7.0.tgz", + "integrity": "sha512-SsF3nt4vXl7ER89Mgysq2tFJZBsNhqX21PbU6jfqXbVfw/hw2IFtcya+IG3m5bI5r6RDbVGeWkOlMPmeH34idQ==", "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/sdk-trace-base": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "dependencies": { - "@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "requires": { - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/resources": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.3.1.tgz", - "integrity": "sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==", - "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/sdk-trace-base": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz", - "integrity": "sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==", - "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" - } + "@opentelemetry/core": "1.7.0", + "@opentelemetry/resources": "1.7.0", + "@opentelemetry/sdk-trace-base": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0" } }, "@opentelemetry/instrumentation": { @@ -7300,80 +7035,50 @@ } }, "@opentelemetry/propagator-b3": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.3.1.tgz", - "integrity": "sha512-tEAtHsRr6l3glsmKaJiJ/7HDw/isPv+f8OBsWJqkSlfLicKes8T/1D7nEDC6jPACiEbD3f6oK1KQSpMijC9/UQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.7.0.tgz", + "integrity": "sha512-8kKGS1KwArvkThdhubMZlomuREE9FaBcn9L4JrYHh2jly1FZpqOtFNO2byHymVRjH59d43Pa+eJuFpD0Fp7kSw==", "requires": { - "@opentelemetry/core": "1.3.1" + "@opentelemetry/core": "1.7.0" } }, "@opentelemetry/propagator-jaeger": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.3.1.tgz", - "integrity": "sha512-H6swQcjZ8aMCS5caZaEBaadfn205IqLlB3ZyY+tCWDf5YPwJgPpjw3qgYgWulHVSEzK7VQTle/mZG7u9MAe6Pw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.7.0.tgz", + "integrity": "sha512-V7i/L1bx+R/ve4z6dTdn2jtvFxGThRsXS2wNb/tWZVfV8gqnePQp+HfoLrqB/Yz2iRPUcMWrcjx6vV78umvJFA==", "requires": { - "@opentelemetry/core": "1.3.1" + "@opentelemetry/core": "1.7.0" } }, "@opentelemetry/resources": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.3.1.tgz", - "integrity": "sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.7.0.tgz", + "integrity": "sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg==", "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "dependencies": { - "@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "requires": { - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" - } + "@opentelemetry/core": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0" } }, "@opentelemetry/sdk-trace-base": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz", - "integrity": "sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz", + "integrity": "sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg==", "requires": { - "@opentelemetry/core": "1.3.1", - "@opentelemetry/resources": "1.3.1", - "@opentelemetry/semantic-conventions": "1.3.1" - }, - "dependencies": { - "@opentelemetry/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.3.1.tgz", - "integrity": "sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==", - "requires": { - "@opentelemetry/semantic-conventions": "1.3.1" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" - } + "@opentelemetry/core": "1.7.0", + "@opentelemetry/resources": "1.7.0", + "@opentelemetry/semantic-conventions": "1.7.0" } }, "@opentelemetry/sdk-trace-node": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.3.1.tgz", - "integrity": "sha512-4sn/pYhaVaEI8WY0arivM77858IM5BjUKvymjJ+HmRNWBocCJKCCCY4P9cL8w8iCGGmst5yxecMyvM7OOFBnmg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.7.0.tgz", + "integrity": "sha512-DCAAbi0Zbb1pIofQcKzoAVy9/6bz24asFYeLb4fW/8QYAaawDnxumA++5Huw/RcYdJs8q8AIRBykwjYWWCm/5A==", "requires": { - "@opentelemetry/context-async-hooks": "1.3.1", - "@opentelemetry/core": "1.3.1", - "@opentelemetry/propagator-b3": "1.3.1", - "@opentelemetry/propagator-jaeger": "1.3.1", - "@opentelemetry/sdk-trace-base": "1.3.1", + "@opentelemetry/context-async-hooks": "1.7.0", + "@opentelemetry/core": "1.7.0", + "@opentelemetry/propagator-b3": "1.7.0", + "@opentelemetry/propagator-jaeger": "1.7.0", + "@opentelemetry/sdk-trace-base": "1.7.0", "semver": "^7.3.5" }, "dependencies": { @@ -7388,9 +7093,9 @@ } }, "@opentelemetry/semantic-conventions": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", - "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz", + "integrity": "sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA==" }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -7770,9 +7475,9 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -7782,7 +7487,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", + "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -8212,15 +7917,15 @@ } }, "drachtio-fsmrf": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.3.tgz", - "integrity": "sha512-RJpGS+lX3Cmqd725zJGf1KvONH38/9YDHkPxXevmiVBp+iD/EbRps/ze5wpE0MKbjEA93DOmiXGTWqsOChWttw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.5.tgz", + "integrity": "sha512-ll5YJYflvLSLxkms8uIg+dzhGGdLtc/+fb1ch/oRxqyaJSGQ3ZQ7X7itieyORJOCxtfuuDmVQZR3ZjJ/mF2Wug==", "requires": { "camel-case": "^4.1.2", "debug": "^2.6.9", "delegates": "^0.1.0", "drachtio-modesl": "^1.2.4", - "drachtio-srf": "^4.5.0", + "drachtio-srf": "^4.5.18", "only": "^0.0.2", "sdp-transform": "^2.14.1", "snake-case": "^3.0.4", @@ -8261,9 +7966,9 @@ } }, "drachtio-srf": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.1.tgz", - "integrity": "sha512-7U8hhMrVIc33ysSUcbg64fDcx2jvqY4h/NdzhYsiU6tQ2fbkd0jzNYR6KD3dqhEzGxRAeEgcaPuQEG0+K2qVIA==", + "version": "4.5.18", + "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.18.tgz", + "integrity": "sha512-qF9uTLsG5kotdXwG6QXdymzzLCVqwEGjWJOBnU41855I+yP8XaUKdtpwCN8xSI1TGd0Mww/00yEsxofKpZuwsA==", "requires": { "debug": "^3.2.7", "delegates": "^0.1.0", @@ -8396,11 +8101,6 @@ "unbox-primitive": "^1.0.2" }, "dependencies": { - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -8721,13 +8421,13 @@ "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" }, "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", @@ -8746,7 +8446,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -9120,6 +8820,11 @@ "node-forge": "^1.3.1" } }, + "google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -9173,9 +8878,9 @@ } }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.0", @@ -9183,13 +8888,6 @@ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "requires": { "has-symbols": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - } } }, "hasha": { @@ -9218,9 +8916,9 @@ } }, "helmet": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.0.tgz", - "integrity": "sha512-klsunXs8rgNSZoaUrNeuCiWUxyc+wzucnEnFejUg3/A+CaF589k9qepLZZ1Jehnzig7YbD4hEuscGXuBY3fq+g==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.1.tgz", + "integrity": "sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ==" }, "hexer": { "version": "1.5.0", @@ -9521,13 +9219,6 @@ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "requires": { "has-symbols": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - } } }, "is-typed-array": { @@ -10277,13 +9968,13 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, @@ -10636,9 +10327,9 @@ "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } @@ -11209,9 +10900,9 @@ "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==" }, "tape": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-5.5.3.tgz", - "integrity": "sha512-hPBJZBL9S7bH9vECg/KSM24slGYV589jJr4dmtiJrLD71AL66+8o4b9HdZazXZyvnilqA7eE8z5/flKiy0KsBg==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.6.1.tgz", + "integrity": "sha512-reNzS3rzsJtKk0f+zJx2XlzIsjJXlIcOIrIxk5shHAG/DzW3BKyMg8UfN79oluYlcWo4lIt56ahLqwgpRT4idg==", "dev": true, "requires": { "array.prototype.every": "^1.1.3", @@ -11221,19 +10912,19 @@ "dotignore": "^0.1.2", "for-each": "^0.3.3", "get-package-type": "^0.1.0", - "glob": "^7.2.0", + "glob": "^7.2.3", "has": "^1.0.3", "has-dynamic-import": "^2.0.1", "inherits": "^2.0.4", "is-regex": "^1.1.4", "minimist": "^1.2.6", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", + "object.assign": "^4.1.4", "resolve": "^2.0.0-next.3", "resumer": "^0.0.0", - "string.prototype.trim": "^1.2.5", + "string.prototype.trim": "^1.2.6", "through": "^2.3.8" }, "dependencies": { @@ -11421,13 +11112,6 @@ "has-bigints": "^1.0.2", "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - } } }, "underscore": { @@ -11682,9 +11366,9 @@ } }, "ws": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", - "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz", + "integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==", "requires": {} }, "xml2js": { diff --git a/package.json b/package.json index aeb2b42c..715ac4ab 100644 --- a/package.json +++ b/package.json @@ -19,33 +19,33 @@ "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/ ", + "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:JambonzR0ck$:docker-host JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_NETWORK_CIDR=172.38.0.0/16 node test/ ", "coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test", "jslint": "eslint app.js lib" }, "dependencies": { - "@jambonz/http-health-check": "^0.0.1", "@jambonz/db-helpers": "^0.7.0", - "@jambonz/realtimedb-helpers": "^0.4.35", + "@jambonz/realtimedb-helpers": "^0.5.1", + "@jambonz/http-health-check": "^0.0.1", "@jambonz/stats-collector": "^0.1.6", "@jambonz/time-series": "^0.2.5", - "@opentelemetry/api": "^1.1.0", - "@opentelemetry/exporter-jaeger": "^1.3.1", + "@opentelemetry/api": "^1.2.0", + "@opentelemetry/exporter-jaeger": "^1.7.0", "@opentelemetry/exporter-trace-otlp-http": "^0.27.0", - "@opentelemetry/exporter-zipkin": "^1.3.1", + "@opentelemetry/exporter-zipkin": "^1.7.0", "@opentelemetry/instrumentation": "^0.27.0", - "@opentelemetry/resources": "^1.3.1", - "@opentelemetry/sdk-trace-base": "^1.3.1", - "@opentelemetry/sdk-trace-node": "^1.3.1", - "@opentelemetry/semantic-conventions": "^1.3.1", - "aws-sdk": "^2.1152.0", + "@opentelemetry/resources": "^1.7.0", + "@opentelemetry/sdk-trace-base": "^1.7.0", + "@opentelemetry/sdk-trace-node": "^1.7.0", + "@opentelemetry/semantic-conventions": "^1.7.0", + "aws-sdk": "^2.1233.0", "bent": "^7.3.12", "debug": "^4.3.4", "deepcopy": "^2.1.0", - "drachtio-fsmrf": "^3.0.3", - "drachtio-srf": "^4.5.1", - "express": "^4.18.1", - "helmet": "^5.1.0", + "drachtio-fsmrf": "^3.0.5", + "drachtio-srf": "^4.5.18", + "express": "^4.18.2", + "helmet": "^5.1.1", "ip": "^1.1.8", "moment": "^2.29.4", "parse-url": "^8.1.0", @@ -53,10 +53,10 @@ "sdp-transform": "^2.14.1", "short-uuid": "^4.2.0", "to-snake-case": "^1.0.0", - "undici": "^5.8.2", + "undici": "^5.11.0", "uuid": "^8.3.2", "verify-aws-sns-signature": "^0.1.0", - "ws": "^8.8.0", + "ws": "^8.9.0", "xml2js": "^0.4.23" }, "devDependencies": { @@ -64,7 +64,7 @@ "eslint": "^7.32.0", "eslint-plugin-promise": "^4.3.1", "nyc": "^15.1.0", - "tape": "^5.5.3" + "tape": "^5.6.1" }, "optionalDependencies": { "bufferutil": "^4.0.6", diff --git a/test/create-test-db.js b/test/create-test-db.js index deaf79f5..d4d8571b 100644 --- a/test/create-test-db.js +++ b/test/create-test-db.js @@ -22,11 +22,17 @@ test('creating schema', (t) => { const google_credential = encrypt(process.env.GCP_JSON_KEY); const aws_credential = encrypt(JSON.stringify({ access_key_id: process.env.AWS_ACCESS_KEY_ID, - secret_access_key: process.env.AWS_SECRET_ACCESS_KEY + secret_access_key: process.env.AWS_SECRET_ACCESS_KEY, + aws_region: process.env.AWS_REGION + })); + const microsoft_credential = encrypt(JSON.stringify({ + region: process.env.MICROSOFT_REGION || 'useast', + api_key: process.env.MICROSOFT_API_KEY || '1234567890' })); const cmd = ` UPDATE speech_credentials SET credential='${google_credential}' WHERE vendor='google'; UPDATE speech_credentials SET credential='${aws_credential}' WHERE vendor='aws'; +UPDATE speech_credentials SET credential='${microsoft_credential}' WHERE vendor='microsoft'; `; const path = `${__dirname}/.creds.sql`; fs.writeFileSync(path, cmd); diff --git a/test/db/create-and-populate-schema.sql b/test/db/create-and-populate-schema.sql index 31072614..dcfebef6 100644 --- a/test/db/create-and-populate-schema.sql +++ b/test/db/create-and-populate-schema.sql @@ -614,7 +614,10 @@ CREATE TABLE `speech_credentials` ( LOCK TABLES `speech_credentials` WRITE; /*!40000 ALTER TABLE `speech_credentials` DISABLE KEYS */; -INSERT INTO `speech_credentials` VALUES ('2add163c-34f2-45c6-a016-f955d218ffb6',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','google','credential-goes-here',1,1,NULL,'2021-04-03 15:42:10',1,1),('84154212-5c99-4c94-8993-bc2a46288daa',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','aws','credential-goes-here',0,0,NULL,NULL,NULL,NULL); +INSERT INTO `speech_credentials` VALUES +('2add163c-34f2-45c6-a016-f955d218ffb6',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','google','credential-goes-here',1,1,NULL,'2021-04-03 15:42:10',1,1), +('2add347f-34f2-45c6-a016-f955d218ffb6',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','microsoft','credential-goes-here',1,1,NULL,'2021-04-03 15:42:10',1,1), +('84154212-5c99-4c94-8993-bc2a46288daa',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','aws','credential-goes-here',1,1,NULL,NULL,NULL,NULL); /*!40000 ALTER TABLE `speech_credentials` ENABLE KEYS */; UNLOCK TABLES; diff --git a/test/docker-compose-testbed.yaml b/test/docker-compose-testbed.yaml index 608dd93d..90a85a13 100644 --- a/test/docker-compose-testbed.yaml +++ b/test/docker-compose-testbed.yaml @@ -57,7 +57,7 @@ services: condition: service_healthy freeswitch: - image: drachtio/drachtio-freeswitch-mrf:v1.10.1-full + image: drachtio/drachtio-freeswitch-mrf:0.4.15 restart: always command: freeswitch --rtp-range-start 20000 --rtp-range-end 20100 environment: @@ -68,7 +68,7 @@ services: - /tmp:/tmp - ./credentials:/opt/credentials healthcheck: - test: ['CMD', 'fs_cli' ,'-x', '"sofia status"'] + test: ['CMD', 'fs_cli' ,'-p', 'JambonzR0ck$$', '-x', '"sofia status"'] timeout: 5s retries: 15 networks: diff --git a/test/gather-tests.js b/test/gather-tests.js index c9679769..31eb9ba5 100644 --- a/test/gather-tests.js +++ b/test/gather-tests.js @@ -17,7 +17,11 @@ function connect(connectable) { }); } -test('\'gather\' and \'transcribe\' tests', async(t) => { +test('\'gather\' test - google', async(t) => { + if (!process.env.GCP_JSON_KEY) { + t.pass('skipping google tests'); + return t.end(); + } clearModule.all(); const {srf, disconnect} = require('../app'); @@ -42,7 +46,7 @@ test('\'gather\' and \'transcribe\' tests', async(t) => { await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from); let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`); t.ok(obj.body.speech.alternatives[0].transcript = 'I\'d like to speak to customer support', - 'gather: succeeds when using account credentials'); + 'gather: succeeds when using google credentials'); disconnect(); } catch (err) { @@ -51,3 +55,81 @@ test('\'gather\' and \'transcribe\' tests', async(t) => { t.error(err); } }); + +test('\'gather\' test - microsoft', async(t) => { + if (!process.env.MICROSOFT_REGION || !process.env.MICROSOFT_API_KEY) { + t.pass('skipping microsoft tests'); + return t.end(); + } + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + // GIVEN + let verbs = [ + { + "verb": "gather", + "input": ["speech"], + "recognizer": { + "vendor": "microsoft", + "hints": ["customer support", "sales", "human resources", "HR"] + }, + "timeout": 10, + "actionHook": "/actionHook" + } + ]; + let from = "gather_success"; + provisionCallHook(from, verbs); + // THEN + await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from); + let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`); + t.ok(obj.body.speech.alternatives[0].transcript = 'I\'d like to speak to customer support', + 'gather: succeeds when using microsoft credentials'); + + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); + +test('\'gather\' test - aws', async(t) => { + if (!process.env.AWS_ACCESS_KEY_ID || !process.env.AWS_SECRET_ACCESS_KEY) { + t.pass('skipping aws tests'); + return t.end(); + } + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + // GIVEN + let verbs = [ + { + "verb": "gather", + "input": ["speech"], + "recognizer": { + "vendor": "aws", + "hints": ["customer support", "sales", "human resources", "HR"] + }, + "timeout": 10, + "actionHook": "/actionHook" + } + ]; + let from = "gather_success"; + provisionCallHook(from, verbs); + // THEN + await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from); + let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`); + t.ok(obj.body.speech.alternatives[0].transcript = 'I\'d like to speak to customer support', + 'gather: succeeds when using aws credentials'); + + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); \ No newline at end of file diff --git a/test/index.js b/test/index.js index 5f0bfdf4..38b1a33d 100644 --- a/test/index.js +++ b/test/index.js @@ -5,6 +5,7 @@ require('./account-validation-tests'); require('./webhooks-tests'); require('./say-tests'); require('./gather-tests'); +require('./transcribe-tests'); require('./sip-request-tests'); require('./create-call-test'); require('./play-tests'); diff --git a/test/sipp.js b/test/sipp.js index 7a4bb38e..d36508d4 100644 --- a/test/sipp.js +++ b/test/sipp.js @@ -41,7 +41,7 @@ obj.sippUac = (file, bindAddress, from='sipp', to='16174000000') => { if (bindAddress) args.splice(5, 0, '--ip', bindAddress); - console.log(args.join(' ')); + //console.log(args.join(' ')); clearOutput(); return new Promise((resolve, reject) => { diff --git a/test/transcribe-tests.js b/test/transcribe-tests.js new file mode 100644 index 00000000..7b51b371 --- /dev/null +++ b/test/transcribe-tests.js @@ -0,0 +1,129 @@ +const test = require('tape'); +const { sippUac } = require('./sipp')('test_fs'); +const bent = require('bent'); +const getJSON = bent('json') +const clearModule = require('clear-module'); +const {provisionCallHook} = require('./utils') + +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); +}); + +function connect(connectable) { + return new Promise((resolve, reject) => { + connectable.on('connect', () => { + return resolve(); + }); + }); +} + +test('\'transcribe\' test - google', async(t) => { + if (!process.env.GCP_JSON_KEY) { + t.pass('skipping google tests'); + return t.end(); + } + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + // GIVEN + let verbs = [ + { + "verb": "transcribe", + "recognizer": { + "vendor": "google", + "hints": ["customer support", "sales", "human resources", "HR"] + }, + "transcriptionHook": "/transcriptionHook" + } + ]; + let from = "gather_success"; + provisionCallHook(from, verbs); + // THEN + await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from); + let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`); + t.ok(obj.body.speech.alternatives[0].transcript = 'I\'d like to speak to customer support', + 'transcribe: succeeds when using google credentials'); + + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); + +test('\'transcribe\' test - microsoft', async(t) => { + if (!process.env.MICROSOFT_REGION || !process.env.MICROSOFT_API_KEY) { + t.pass('skipping microsoft tests'); + return t.end(); + } + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + // GIVEN + let verbs = [ + { + "verb": "transcribe", + "recognizer": { + "vendor": "microsoft", + "hints": ["customer support", "sales", "human resources", "HR"] + }, + "transcriptionHook": "/transcriptionHook" + } + ]; + let from = "gather_success"; + provisionCallHook(from, verbs); + // THEN + await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from); + let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`); + t.ok(obj.body.speech.alternatives[0].transcript = 'I\'d like to speak to customer support', + 'transcribe: succeeds when using microsoft credentials'); + + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); + +test('\'transcribe\' test - aws', async(t) => { + if (!process.env.AWS_ACCESS_KEY_ID || !process.env.AWS_SECRET_ACCESS_KEY) { + t.pass('skipping aws tests'); + return t.end(); + } + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + // GIVEN + let verbs = [ + { + "verb": "transcribe", + "recognizer": { + "vendor": "aws", + "hints": ["customer support", "sales", "human resources", "HR"] + }, + "transcriptionHook": "/transcriptionHook" + } + ]; + let from = "gather_success"; + provisionCallHook(from, verbs); + // THEN + await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from); + let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`); + t.ok(obj.body.speech.alternatives[0].transcript = 'I\'d like to speak to customer support', + 'transcribe: succeeds when using aws credentials'); + + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); \ No newline at end of file diff --git a/test/webhook/app.js b/test/webhook/app.js index 2db82d7f..4d6348ea 100644 --- a/test/webhook/app.js +++ b/test/webhook/app.js @@ -39,7 +39,16 @@ app.post('/callStatus', (req, res) => { return res.sendStatus(200); }); /* - * action Hook + * transcriptionHook + */ +app.post('/transcriptionHook', (req, res) => { + console.log({payload: req.body}, 'POST /transcriptionHook'); + let key = req.body.from + "_actionHook" + addRequestToMap(key, req, hook_mapping); + return res.json([{"verb": "hangup"}]); +}); +/* + * actionHook */ app.post('/actionHook', (req, res) => { console.log({payload: req.body}, 'POST /actionHook');