Compare commits

...

10 Commits

Author SHA1 Message Date
surajshivakumar
1143cab6df swagger updated for call records 2024-07-15 06:58:52 -06:00
surajshivakumar
9bdc859227 added api get request for call retrieval 2024-07-15 06:58:52 -06:00
Hoan Luu Huu
48e1a72ef3 support use sips scheme for outbound tls gateway (#332)
* support use sips scheme for outbound tls gateway

* support use sips scheme for outbound tls gateway

* update license
2024-06-15 09:17:05 -04:00
Hoan Luu Huu
4337a55a27 update getAwsAuthToken to use parameters as object (#330)
* update getAwsAuthToken to use parameters as object

* update speech utils version
2024-06-15 08:10:58 -04:00
Hoan Luu Huu
6041b1d595 fix cannot update verbio engine_version (#327) 2024-06-04 09:48:24 -04:00
Hoan Luu Huu
d33d0aa519 support verbio speech (#323)
* support verbio speech

* wip

* update speech version

* update verb specification
2024-05-29 07:35:40 -04:00
Dave Horton
ffe9cb23eb update speech-utils (#325) 2024-05-28 18:24:57 -04:00
Hoan Luu Huu
dbbc894832 support list conference (#321)
* support list conference

* add test case

* fix conference action requires tag

* fix failing test case
2024-05-28 10:31:16 -04:00
Hoan Luu Huu
82c16380f5 fix Speech credential test for azure (#322) 2024-05-14 06:56:06 -04:00
Hoan Luu Huu
c0fab2880b fix cannot send multipart to aws due to min size (#319) 2024-05-03 07:37:38 -04:00
18 changed files with 470 additions and 42 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Drachtio Communications Services, LLC
Copyright (c) 2018-2024 FirstFive8, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

6
app.js
View File

@@ -46,13 +46,15 @@ const {
addKey,
retrieveKey,
deleteKey,
incrKey
incrKey,
listConferences
} = require('./lib/helpers/realtimedb-helpers');
const {
getTtsVoices,
getTtsSize,
purgeTtsCache,
getAwsAuthToken,
getVerbioAccessToken,
synthAudio
} = require('@jambonz/speech-utils')({}, logger);
const {
@@ -88,6 +90,7 @@ app.locals = {
deleteCall,
listCalls,
listSortedSets,
listConferences,
purgeCalls,
retrieveSet,
addKey,
@@ -97,6 +100,7 @@ app.locals = {
getTtsVoices,
getTtsSize,
getAwsAuthToken,
getVerbioAccessToken,
purgeTtsCache,
synthAudio,
lookupAppBySid,

View File

@@ -459,6 +459,7 @@ outbound BOOLEAN NOT NULL COMMENT 'if true, include in least-cost routing when p
voip_carrier_sid CHAR(36) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT 1,
send_options_ping BOOLEAN NOT NULL DEFAULT 0,
use_sips_scheme BOOLEAN NOT NULL DEFAULT 0,
pad_crypto BOOLEAN NOT NULL DEFAULT 0,
protocol ENUM('udp','tcp','tls', 'tls/srtp') DEFAULT 'udp' COMMENT 'Outbound call protocol',
PRIMARY KEY (sip_gateway_sid)

View File

@@ -551,7 +551,7 @@
</location>
<size>
<width>293.00</width>
<height>560.00</height>
<height>540.00</height>
</size>
<zorder>6</zorder>
<SQLField>
@@ -2332,7 +2332,7 @@
</location>
<size>
<width>281.00</width>
<height>260.00</height>
<height>280.00</height>
</size>
<zorder>7</zorder>
<SQLField>
@@ -2406,6 +2406,13 @@
<notNull><![CDATA[1]]></notNull>
<uid><![CDATA[E04C19A2-12BF-443F-AB61-96990224A18D]]></uid>
</SQLField>
<SQLField>
<name><![CDATA[use_sips_scheme]]></name>
<type><![CDATA[BOOLEAN]]></type>
<defaultValue><![CDATA[0]]></defaultValue>
<notNull><![CDATA[1]]></notNull>
<uid><![CDATA[5DCBDD48-913B-4580-B78C-9B06C939FEA8]]></uid>
</SQLField>
<SQLField>
<name><![CDATA[pad_crypto]]></name>
<type><![CDATA[BOOLEAN]]></type>
@@ -3111,11 +3118,11 @@
<SourceSidebarWidth><![CDATA[312.000000]]></SourceSidebarWidth>
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
<windowHeight><![CDATA[870.000000]]></windowHeight>
<windowLocationX><![CDATA[-1164.000000]]></windowLocationX>
<windowLocationY><![CDATA[1131.000000]]></windowLocationY>
<windowScrollOrigin><![CDATA[{0.5, 0}]]></windowScrollOrigin>
<windowWidth><![CDATA[1512.000000]]></windowWidth>
<windowHeight><![CDATA[1079.000000]]></windowHeight>
<windowLocationX><![CDATA[0.000000]]></windowLocationX>
<windowLocationY><![CDATA[0.000000]]></windowLocationY>
<windowScrollOrigin><![CDATA[{1, 0}]]></windowScrollOrigin>
<windowWidth><![CDATA[1676.000000]]></windowWidth>
</SQLDocumentInfo>
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>
<defaultLabelExpanded><![CDATA[1]]></defaultLabelExpanded>

View File

@@ -196,6 +196,7 @@ const sql = {
'ALTER TABLE sip_gateways ADD COLUMN send_options_ping BOOLEAN NOT NULL DEFAULT 0',
'ALTER TABLE applications MODIFY COLUMN speech_synthesis_voice VARCHAR(256)',
'ALTER TABLE applications MODIFY COLUMN fallback_speech_synthesis_voice VARCHAR(256)',
'ALTER TABLE sip_gateways ADD COLUMN use_sips_scheme BOOLEAN NOT NULL DEFAULT 0',
]
};

View File

@@ -13,6 +13,7 @@ const {
deleteKey,
incrKey,
client: redisClient,
listConferences
} = require('@jambonz/realtimedb-helpers')({}, logger);
module.exports = {
@@ -27,5 +28,6 @@ module.exports = {
retrieveKey,
deleteKey,
redisClient,
incrKey
incrKey,
listConferences
};

View File

@@ -16,7 +16,7 @@ class S3MultipartUploadStream extends Writable {
this.partNumber = 1;
this.multipartETags = [];
this.buffer = Buffer.alloc(0);
this.minPartSize = 2 * 1024 * 1024; // 5 MB
this.minPartSize = 5 * 1024 * 1024; // 5 MB
this.s3 = new S3Client(opts.bucketCredential);
this.metadata = opts.metadata;
}

View File

@@ -323,10 +323,10 @@ function validateUpdateCall(opts) {
throw new DbErrorBadRequest(
`conferenceParticipantAction invalid action property ${opts.conferenceParticipantAction.action}`);
}
if ('tag' == opts.conferenceParticipantAction.action && !opts.tag) {
if ('tag' == opts.conferenceParticipantAction.action && !opts.conferenceParticipantAction.tag) {
throw new DbErrorBadRequest('conferenceParticipantAction requires tag property when action is \'tag\'');
}
if ('coach' == opts.conferenceParticipantAction.action && !opts.tag) {
if ('coach' == opts.conferenceParticipantAction.action && !opts.conferenceParticipantAction.tag) {
throw new DbErrorBadRequest('conferenceParticipantAction requires tag property when action is \'coach\'');
}
}
@@ -1103,5 +1103,21 @@ router.get('/:sid/Queues', async(req, res) => {
}
});
/**
* retrieve info for a list of conferences under an account
*/
router.get('/:sid/Conferences', async(req, res) => {
const {logger, listConferences} = req.app.locals;
try {
const accountSid = parseAccountSid(req);
await validateRequest(req, accountSid);
const conferences = await listConferences(accountSid);
logger.debug(`retrieved ${conferences.length} queues for account sid ${accountSid}`);
res.status(200).json(conferences.map((c) => c.split(':').pop()));
updateLastUsed(logger, accountSid, req).catch((err) => {});
} catch (err) {
sysError(logger, res, err);
}
});
module.exports = router;

View File

@@ -8,7 +8,9 @@ const {parseAccountSid, parseServiceProviderSid, parseSpeechCredentialSid} = req
const {decryptCredential, testWhisper, testDeepgramTTS,
getLanguagesAndVoicesForVendor,
testPlayHT,
testRimelabs} = require('../../utils/speech-utils');
testRimelabs,
testVerbioTts,
testVerbioStt} = require('../../utils/speech-utils');
const {DbErrorUnprocessableRequest, DbErrorForbidden, DbErrorBadRequest} = require('../../utils/errors');
const {
testGoogleTts,
@@ -116,6 +118,7 @@ const encryptCredential = (obj) => {
role_arn,
region,
client_id,
client_secret,
secret,
nuance_tts_uri,
nuance_stt_uri,
@@ -140,6 +143,7 @@ const encryptCredential = (obj) => {
model_id,
user_id,
voice_engine,
engine_version,
options
} = obj;
@@ -255,6 +259,13 @@ const encryptCredential = (obj) => {
const whisperData = JSON.stringify({api_key, model_id});
return encrypt(whisperData);
case 'verbio':
assert(engine_version, 'invalid verbio speech credential: client_id is required');
assert(client_id, 'invalid verbio speech credential: client_id is required');
assert(client_secret, 'invalid verbio speech credential: secret is required');
const verbioData = JSON.stringify({client_id, client_secret, engine_version});
return encrypt(verbioData);
default:
if (vendor.startsWith('custom:')) {
const customData = JSON.stringify({auth_token, custom_stt_url, custom_tts_url});
@@ -447,6 +458,7 @@ router.put('/:sid', async(req, res) => {
options,
deepgram_stt_uri,
deepgram_stt_use_tls,
engine_version
} = req.body;
const newCred = {
@@ -473,6 +485,7 @@ router.put('/:sid', async(req, res) => {
options,
deepgram_stt_uri,
deepgram_stt_use_tls,
engine_version
};
logger.info({o, newCred}, 'updating speech credential with this new credential');
obj.credential = encryptCredential(newCred);
@@ -501,7 +514,7 @@ router.put('/:sid', async(req, res) => {
* Test a credential
*/
router.get('/:sid/test', async(req, res) => {
const {logger, synthAudio} = req.app.locals;
const {logger, synthAudio, getVerbioAccessToken} = req.app.locals;
try {
const sid = parseSpeechCredentialSid(req);
const creds = await SpeechCredential.retrieve(sid);
@@ -613,7 +626,7 @@ router.get('/:sid/test', async(req, res) => {
}
if (cred.use_for_stt) {
try {
await testMicrosoftStt(logger, {api_key, region});
await testMicrosoftStt(logger, {api_key, region, use_custom_stt, custom_stt_endpoint_url});
results.stt.status = 'ok';
SpeechCredential.sttTestResult(sid, true);
} catch (err) {
@@ -672,8 +685,7 @@ router.get('/:sid/test', async(req, res) => {
SpeechCredential.sttTestResult(sid, false);
}
}
}
else if (cred.vendor === 'deepgram') {
} else if (cred.vendor === 'deepgram') {
const {api_key} = credential;
if (cred.use_for_tts) {
try {
@@ -803,6 +815,27 @@ router.get('/:sid/test', async(req, res) => {
SpeechCredential.ttsTestResult(sid, false);
}
}
} else if (cred.vendor === 'verbio') {
if (cred.use_for_tts) {
try {
await testVerbioTts(logger, synthAudio, credential);
results.tts.status = 'ok';
SpeechCredential.ttsTestResult(sid, true);
} catch (err) {
results.tts = {status: 'fail', reason: err.message};
SpeechCredential.ttsTestResult(sid, false);
}
}
if (cred.use_for_stt) {
try {
await testVerbioStt(logger, getVerbioAccessToken, credential);
results.stt.status = 'ok';
SpeechCredential.sttTestResult(sid, true);
} catch (err) {
results.stt = {status: 'fail', reason: err.message};
SpeechCredential.sttTestResult(sid, false);
}
}
}
res.status(200).json(results);

View File

@@ -130,7 +130,71 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
$ref: '#/components/schemas/GeneralError'
/Accounts/{accountSid}/RecentCalls/{callSid}/record/{year}/{month}/{day}/{format}:
get:
tags:
- Call Records
summary: 'Retrieve a recording of a recent call in specified format'
description: 'Gets the recording of a recent call by specifying the account ID, call ID, the date of the call, and the format of the recording (e.g., MP3 or WAV).'
operationId: getRecordingByFormat
security:
- ApiKeyAuth: [ ]
parameters:
- name: accountSid
in: path
required: true
schema:
type: string
- name: callSid
in: path
required: true
schema:
type: string
- name: year
in: path
required: true
schema:
type: string
- name: month
in: path
required: true
schema:
type: string
- name: day
in: path
required: true
schema:
type: string
- name: format
in: path
required: true
schema:
type: string
enum:
- mp3
- wav
responses:
200:
description: Successfully retrieved the recording file.
content:
audio/*:
schema:
type: string
format: binary
404:
description: Call record not found
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
500:
description: System error
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
/Sbcs:
post:
tags:
@@ -3882,6 +3946,34 @@ paths:
$ref: '#/components/schemas/GeneralError'
/Accounts/{AccountSid}/Conferences:
get:
tags:
- Conferences
summary: list conferences
operationId: listConferences
parameters:
- name: AccountSid
in: path
required: true
schema:
type: string
responses:
200:
description: list of conferences for a specified account
content:
application/json:
schema:
type: array
items:
type: string
500:
description: system error
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
/Accounts/{AccountSid}/Calls:
post:
tags:

View File

@@ -0,0 +1,14 @@
module.exports = [
{ name: 'US English', value: 'en-US' },
{ name: 'British English', value: 'en-GB' },
{ name: 'LATAM Spanish', value: 'en-USes-419' },
{ name: 'Spanish', value: 'es' },
{ name: 'Catalan', value: 'ca-ES', version: 'v2' },
{ name: 'Brazilian Portuguese', value: 'pt-BR' },
{ name: 'French', value: 'fr', version: 'v1' },
{ name: 'Canadian French', value: 'fr-CA', version: 'v1' },
{ name: 'German', value: 'de', version: 'v1' },
{ name: 'Italian', value: 'it', version: 'v1' },
{ name: 'Turkish', value: 'tr', version: 'v1' },
{ name: 'Japanese', value: 'ja', version: 'v1' },
];

View File

@@ -0,0 +1,62 @@
module.exports = [
{
value: 'en-US',
name: 'US English',
voices: [
{
value: 'tommy_en_us',
name: 'Tommy-Male',
},
],
},
{
value: 'es-ES',
name: 'Castilian Spanish',
voices: [
{
value: 'david_es_es',
name: 'David-Male',
},
],
},
{
value: 'es-PE',
name: 'Peruvian Spanish',
voices: [
{
value: 'miguel_es_pe',
name: 'Miguel-Male',
},
],
},
{
value: 'es-PE',
name: 'Peruvian Spanish',
voices: [
{
value: 'luz_es_pe',
name: 'Luz-Female',
},
],
},
{
value: 'pt-BR',
name: 'Brazilian Portuguese',
voices: [
{
value: 'bel_pt_br',
name: 'Bel-Female',
},
],
},
{
value: 'ca-ES',
name: 'Catalan',
voices: [
{
value: 'anna_ca',
name: 'Anna-Female',
},
],
},
];

View File

@@ -18,6 +18,7 @@ const TtsNvidiaLanguagesVoices = require('./speech-data/tts-nvidia');
const TtsElevenlabsLanguagesVoices = require('./speech-data/tts-elevenlabs');
const TtsWhisperLanguagesVoices = require('./speech-data/tts-whisper');
const TtsPlayHtLanguagesVoices = require('./speech-data/tts-playht');
const TtsVerbioLanguagesVoices = require('./speech-data/tts-verbio');
const TtsModelDeepgram = require('./speech-data/tts-model-deepgram');
const TtsModelElevenLabs = require('./speech-data/tts-model-elevenlabs');
@@ -35,6 +36,7 @@ const SttNvidiaLanguagesVoices = require('./speech-data/stt-nvidia');
const SttCobaltLanguagesVoices = require('./speech-data/stt-cobalt');
const SttSonioxLanguagesVoices = require('./speech-data/stt-soniox');
const SttAssemblyaiLanguagesVoices = require('./speech-data/stt-assemblyai');
const SttVerbioLanguagesVoices = require('./speech-data/stt-verbio');
const testSonioxStt = async(logger, credentials) => {
const api_key = credentials;
@@ -117,9 +119,10 @@ const testDeepgramStt = async(logger, credentials) => {
};
const testMicrosoftStt = async(logger, credentials) => {
const {api_key, region} = credentials;
const speechConfig = sdk.SpeechConfig.fromSubscription(api_key, region);
const {api_key, region, use_custom_stt, custom_stt_endpoint_url} = credentials;
const speechConfig = use_custom_stt ? sdk.SpeechConfig.fromEndpoint(
new URL(custom_stt_endpoint_url), api_key) :
sdk.SpeechConfig.fromSubscription(api_key, region);
const audioConfig = sdk.AudioConfig.fromWavFileInput(fs.readFileSync(`${__dirname}/../../data/test_audio.wav`));
speechConfig.speechRecognitionLanguage = 'en-US';
@@ -180,7 +183,10 @@ const testAwsStt = async(logger, getAwsAuthToken, credentials) => {
} else if (roleArn) {
client = new TranscribeClient({
region,
credentials: await getAwsAuthToken(null, null, region, roleArn),
credentials: await getAwsAuthToken({
region,
roleArn
}),
});
} else {
client = new TranscribeClient({region});
@@ -189,7 +195,7 @@ const testAwsStt = async(logger, getAwsAuthToken, credentials) => {
const response = await client.send(command);
return response;
} catch (err) {
logger.info({err}, 'testMicrosoftTts - failed to list voices for region ${region}');
logger.info({err}, 'testAwsStt - failed to list voices for region ${region}');
throw err;
}
};
@@ -202,7 +208,8 @@ const testMicrosoftTts = async(logger, synthAudio, credentials) => {
credentials,
language: 'en-US',
voice: 'en-US-JennyMultilingualNeural',
text: 'Hi there and welcome to jambones!'
text: 'Hi there and welcome to jambones!',
renderForCaching: true
}
);
} catch (err) {
@@ -364,6 +371,43 @@ const testWellSaidStt = async(logger, credentials) => {
return true;
};
const testVerbioTts = async(logger, synthAudio, credentials) => {
try {
await synthAudio(
{
increment: () => {},
histogram: () => {}
},
{
vendor: 'verbio',
credentials,
language: 'en-US',
voice: 'tommy_en-us',
text: 'Hi there and welcome to jambones!'
}
);
} catch (err) {
logger.info({err}, 'synth Verbio returned error');
throw err;
}
};
const testVerbioStt = async(logger, getVerbioAccessToken, credentials) => {
const token = await getVerbioAccessToken(credentials);
try {
const post = bent('https://us.rest.speechcenter.verbio.com', 'POST', 'json', {
'Authorization': `Bearer ${token.access_token}`,
'User-Agent': 'jambonz',
'Content-Type': 'audio/wav'
});
const json = await post('/api/v1/recognize?language=en-US&version=V1',
fs.readFileSync(`${__dirname}/../../data/test_audio.wav`));
logger.debug({json}, 'successfully speech to text from verbio');
} catch (err) {
logger.info({err}, 'testWellSaidTts returned error');
throw err;
}
};
const testAssemblyStt = async(logger, credentials) => {
const {api_key} = credentials;
@@ -510,6 +554,11 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
obj.model_id = o.model_id;
} else if ('verbio' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.client_id = o.client_id;
obj.client_secret = isObscureKey ? obscureKey(o.client_secret) : o.client_secret;
obj.engine_version = o.engine_version;
}
}
@@ -566,6 +615,8 @@ async function getLanguagesAndVoicesForVendor(logger, vendor, credential, getTts
return await getLanguagesVoicesForAssemblyAI(credential, getTtsVoices, logger);
case 'whisper':
return await getLanguagesVoicesForWhisper(credential, getTtsVoices, logger);
case 'verbio':
return await getLanguagesVoicesForVerbio(credential, getTtsVoices, logger);
default:
logger.info(`invalid vendor ${vendor}, return empty result`);
throw new Error(`Invalid vendor ${vendor}`);
@@ -814,6 +865,23 @@ async function getLanguagesVoicesForWhisper(credential) {
return tranform(TtsWhisperLanguagesVoices, undefined, TtsModelWhisper);
}
async function getLanguagesVoicesForVerbio(credentials, getTtsVoices, logger) {
const stt = SttVerbioLanguagesVoices.reduce((acc, v) => {
if (!v.version || credentials.engine_version === v.version) {
acc.push(v);
}
return acc;
}, []);
try {
const data = await getTtsVoices({vendor: 'verbio', credentials});
const voices = parseVerbioLanguagesVoices(data);
return tranform(voices, stt, undefined);
} catch (err) {
logger.info({err}, 'there is error while fetching verbio speech voices');
return tranform(TtsVerbioLanguagesVoices, stt, undefined);
}
}
function tranform(tts, stt, models) {
return {
...(tts && {tts}),
@@ -941,6 +1009,29 @@ function parseMicrosoftLanguagesVoices(data) {
}, []);
}
function parseVerbioLanguagesVoices(data) {
return data.reduce((acc, voice) => {
const languageCode = voice.language;
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push({
value: voice.voice_id,
name: voice.name,
});
} else {
acc.push({
value: voice.language,
name: voice.language,
voices: [{
value: voice.voice_id,
name: voice.name,
}]
});
}
return acc;
}, []);
}
module.exports = {
testGoogleTts,
testGoogleStt,
@@ -964,5 +1055,7 @@ module.exports = {
getSpeechCredential,
decryptCredential,
testWhisper,
testVerbioTts,
testVerbioStt,
getLanguagesAndVoicesForVendor
};

24
package-lock.json generated
View File

@@ -18,10 +18,10 @@
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/lamejs": "^1.2.2",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.8",
"@jambonz/speech-utils": "^0.1.0",
"@jambonz/realtimedb-helpers": "^0.8.9",
"@jambonz/speech-utils": "^0.1.11",
"@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.69",
"@jambonz/verb-specifications": "^0.0.72",
"@soniox/soniox-node": "^1.2.2",
"argon2": "^0.40.1",
"assemblyai": "^4.3.4",
@@ -2018,18 +2018,18 @@
}
},
"node_modules/@jambonz/realtimedb-helpers": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.8.8.tgz",
"integrity": "sha512-bzVz2EqJ7Gma5ysOh8J8Z1amw7szCBS1RSCLWEhtwPgYt6yWRdjgXbH7wHJ1qbZYYEfetfn7jeEaee8BYnoQlg==",
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.8.9.tgz",
"integrity": "sha512-+kVH+dgL6ZIaPDxZM9pwHGGZSYRrAZPvIMqNYd4dLrpduKxpYmdEG3ShfmFgXBZOQJq/Ysz5a75vrYCjtyBkyQ==",
"dependencies": {
"debug": "^4.3.4",
"ioredis": "^5.3.2"
}
},
"node_modules/@jambonz/speech-utils": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.1.0.tgz",
"integrity": "sha512-45K6Vrl2PMEbbcnvm65afCDujDxck/bEUq7+P6KRw/cei3mrKtwjGh3HXi1cKhC1gA5UF1+5YrUoPO9LdoZnog==",
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.1.11.tgz",
"integrity": "sha512-VgljBLUF871adib/3yWpzd7kv26ioxiLVkAIxm94CSk9WeZuzX1lVcE2SohojW3mjCYdYY6+B8FRyzlTD+en3g==",
"dependencies": {
"@aws-sdk/client-polly": "^3.496.0",
"@aws-sdk/client-sts": "^3.496.0",
@@ -2082,9 +2082,9 @@
}
},
"node_modules/@jambonz/verb-specifications": {
"version": "0.0.69",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.69.tgz",
"integrity": "sha512-DWnz7XRkCzpzyCVJH7NtScv+wSlUC414/EO8j/gPZs3RT4WBW1OBXwXpfjURHcSrDG7lycz+tfA+2WoUdW/W+g==",
"version": "0.0.72",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.72.tgz",
"integrity": "sha512-sjA+/LQP2p1zE02UByy9OaAaSxbfQNxQ6D0pwYoMG42U8n+8Det+GFM/9+oFVnbNjUH9bvgT8vrR57U0lU4Cpw==",
"dependencies": {
"debug": "^4.3.4",
"pino": "^8.8.0"

View File

@@ -28,10 +28,10 @@
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/lamejs": "^1.2.2",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.8",
"@jambonz/speech-utils": "^0.1.0",
"@jambonz/realtimedb-helpers": "^0.8.9",
"@jambonz/speech-utils": "^0.1.11",
"@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.69",
"@jambonz/verb-specifications": "^0.0.72",
"@soniox/soniox-node": "^1.2.2",
"argon2": "^0.40.1",
"assemblyai": "^4.3.4",

View File

@@ -14,7 +14,7 @@ const {
createPhoneNumber,
deleteObjectBySid} = require('./utils');
const logger = require('../lib/logger');
const { addToSortedSet } = require('@jambonz/realtimedb-helpers')({
const { addToSortedSet, createHash } = require('@jambonz/realtimedb-helpers')({
host: process.env.JAMBONES_REDIS_HOST,
port: process.env.JAMBONES_REDIS_PORT || 6379
}, logger);
@@ -314,6 +314,19 @@ test('account tests', async(t) => {
});
t.ok(result.statusCode === 200 && result.body.length === 0, 'successfully queried account queue info with for an invalid account');
// query conferences
await createHash(`conf:${sid}:conf1`, 'url1');
await createHash(`conf:${sid}:conf2`, 'url2');
await createHash(`conf:${sid}:conf3`, 'url3');
await createHash(`conf:${sid}:conf4`, 'url4');
result = await request.get(`/Accounts/${sid}/Conferences`, {
auth: authAdmin,
resolveWithFullResponse: true,
json: true,
});
t.ok(result.statusCode === 200 && result.body.length === 4, 'successfully queried account conferences info for an account');
/* delete account */
result = await request.delete(`/Accounts/${sid}`, {
auth: authAdmin,

View File

@@ -75,6 +75,44 @@ test('sip gateway tests', async(t) => {
});
//console.log(`result: ${JSON.stringify(result)}`);
t.ok(result.statusCode === 204, 'successfully deleted sip gateway');
/* add a sip gateway */
result = await request.post('/SipGateways', {
resolveWithFullResponse: true,
auth: authAdmin,
json: true,
body: {
voip_carrier_sid,
ipv4: '192.168.1.2',
netmask: 32,
inbound: true,
outbound: true,
protocol: 'tls',
use_sips_scheme: true
}
});
t.ok(result.statusCode === 201, 'successfully created sip gateway ');
const sipsSid = result.body.sid;
/* query one sip gateway */
result = await request.get(`/SipGateways/${sipsSid}`, {
auth: authAdmin,
json: true,
});
//console.log(`result: ${JSON.stringify(result)}`);
t.ok(result.ipv4 === '192.168.1.2' , 'successfully retrieved voip carrier by sid');
t.ok(result.protocol === 'tls' , 'successfully retrieved voip carrier by sid');
t.ok(result.use_sips_scheme, 'successfully retrieved voip carrier by sid');
/* delete sip gateways */
result = await request.delete(`/SipGateways/${sipsSid}`, {
resolveWithFullResponse: true,
simple: false,
json: true,
auth: authAdmin
});
//console.log(`result: ${JSON.stringify(result)}`);
t.ok(result.statusCode === 204, 'successfully deleted sip gateway');
await deleteObjectBySid(request, '/VoipCarriers', voip_carrier_sid);

View File

@@ -750,7 +750,7 @@ test('speech credentials tests', async(t) => {
json: true,
body: {
vendor: 'aws',
labe: 'aws_polly_with_arn',
label: 'aws_polly_with_arn',
use_for_tts: true,
use_for_stt: false,
role_arn: 'Arn::aws::role',
@@ -767,6 +767,58 @@ test('speech credentials tests', async(t) => {
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
/* add a credential for verbio */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'verbio',
use_for_tts: true,
use_for_stt: true,
client_id: 'client:id',
client_secret: 'client:secret',
engine_version: 'V1'
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for Verbio');
const verbioSid = result.body.sid;
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${verbioSid}`, {
resolveWithFullResponse: true,
simple: false,
auth: authAdmin,
json: true,
});
t.ok(result.body.engine_version === "V1", 'successfully get verbio speech credential');
result = await request.put(`/Accounts/${account_sid}/SpeechCredentials/${verbioSid}`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
use_for_tts: true,
use_for_stt: true,
engine_version: 'V2'
}
});
t.ok(result.statusCode === 204, 'successfully updated speech credential for verbio');
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${verbioSid}`, {
resolveWithFullResponse: true,
simple: false,
auth: authAdmin,
json: true,
});
t.ok(result.body.engine_version === "V2", 'successfully Updated verbio speech credential');
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${verbioSid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
/* Check google supportedLanguagesAndVoices */
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/speech/supportedLanguagesAndVoices?vendor=google`, {
resolveWithFullResponse: true,