mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-01-25 02:08:24 +00:00
Compare commits
26 Commits
fix/micros
...
v0.9.2-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c7bac91a8 | ||
|
|
de250c8d58 | ||
|
|
84d83a0a48 | ||
|
|
b5bede7a08 | ||
|
|
6e779f6744 | ||
|
|
77b9ca4cba | ||
|
|
0451b6982c | ||
|
|
71adc577e9 | ||
|
|
e8b32103fe | ||
|
|
57d8d0a02c | ||
|
|
a41760fa9f | ||
|
|
c6bae80a03 | ||
|
|
4cddbd83a1 | ||
|
|
6275aac341 | ||
|
|
52de41c9bc | ||
|
|
ed71abd675 | ||
|
|
2d2b98dab5 | ||
|
|
7553e2b617 | ||
|
|
b921cab867 | ||
|
|
48e1a72ef3 | ||
|
|
4337a55a27 | ||
|
|
6041b1d595 | ||
|
|
d33d0aa519 | ||
|
|
ffe9cb23eb | ||
|
|
dbbc894832 | ||
|
|
82c16380f5 |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -7,11 +7,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
docker-compose --version
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: lts/*
|
||||
- run: npm install
|
||||
- run: npm run jslint
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
docker-compose --version
|
||||
- run: npm test
|
||||
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -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
6
app.js
@@ -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,
|
||||
|
||||
@@ -358,7 +358,9 @@ CREATE TABLE system_information
|
||||
(
|
||||
domain_name VARCHAR(255),
|
||||
sip_domain_name VARCHAR(255),
|
||||
monitoring_domain_name VARCHAR(255)
|
||||
monitoring_domain_name VARCHAR(255),
|
||||
private_network_cidr VARCHAR(8192),
|
||||
log_level ENUM('info', 'debug') NOT NULL DEFAULT 'info'
|
||||
);
|
||||
|
||||
CREATE TABLE users
|
||||
@@ -459,6 +461,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)
|
||||
@@ -551,6 +554,7 @@ siprec_hook_sid CHAR(36),
|
||||
record_all_calls BOOLEAN NOT NULL DEFAULT false,
|
||||
record_format VARCHAR(16) NOT NULL DEFAULT 'mp3',
|
||||
bucket_credential VARCHAR(8192) COMMENT 'credential used to authenticate with storage service',
|
||||
enable_debug_log BOOLEAN NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (account_sid)
|
||||
) COMMENT='An enterprise that uses the platform for comm services';
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -196,6 +196,12 @@ 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',
|
||||
],
|
||||
9002: [
|
||||
'ALTER TABLE system_information ADD COLUMN private_network_cidr VARCHAR(8192)',
|
||||
'ALTER TABLE system_information ADD COLUMN log_level ENUM(\'info\', \'debug\') NOT NULL DEFAULT \'info\'',
|
||||
'ALTER TABLE accounts ADD COLUMN enable_debug_log BOOLEAN NOT NULL DEFAULT false',
|
||||
]
|
||||
};
|
||||
|
||||
@@ -229,6 +235,7 @@ const doIt = async() => {
|
||||
if (val < 8004) upgrades.push(...sql['8004']);
|
||||
if (val < 8005) upgrades.push(...sql['8005']);
|
||||
if (val < 9000) upgrades.push(...sql['9000']);
|
||||
if (val < 9002) upgrades.push(...sql['9002']);
|
||||
|
||||
// perform all upgrades
|
||||
logger.info({upgrades}, 'applying schema upgrades..');
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -23,10 +23,11 @@ class SpeechCredential extends Model {
|
||||
static async getSpeechCredentialsByVendorAndLabel(service_provider_sid, account_sid, vendor, label) {
|
||||
let sql;
|
||||
if (account_sid) {
|
||||
sql = `SELECT * FROM speech_credentials WHERE account_sid = ? AND vendor = ? ${label ? 'AND label = ?' : ''}`;
|
||||
sql = `SELECT * FROM speech_credentials WHERE account_sid = ? AND vendor = ?
|
||||
AND label ${label ? '= ?' : 'is NULL'}`;
|
||||
} else {
|
||||
sql = `SELECT * FROM speech_credentials WHERE service_provider_sid = ? AND vendor = ?
|
||||
${label ? 'AND label = ?' : ''}`;
|
||||
AND label ${label ? '= ?' : 'is NULL'}`;
|
||||
}
|
||||
const [rows] = await promisePool.query(sql, [account_sid ? account_sid : service_provider_sid, vendor, label]);
|
||||
return rows;
|
||||
|
||||
@@ -33,6 +33,10 @@ SystemInformation.fields = [
|
||||
name: 'monitoring_domain_name',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'private_network_cidr',
|
||||
type: 'string',
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = SystemInformation;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -41,6 +41,7 @@ const checkUserScope = async(req, voip_carrier_sid) => {
|
||||
|
||||
const validate = async(req, sid) => {
|
||||
const {lookupSipGatewayBySid} = req.app.locals;
|
||||
const {netmask} = req.body;
|
||||
let voip_carrier_sid;
|
||||
|
||||
if (sid) {
|
||||
@@ -52,6 +53,12 @@ const validate = async(req, sid) => {
|
||||
voip_carrier_sid = req.body.voip_carrier_sid;
|
||||
if (!voip_carrier_sid) throw new DbErrorBadRequest('missing voip_carrier_sid');
|
||||
}
|
||||
if (netmask &&
|
||||
process.env.JAMBONZ_MIN_GATEWAY_NETMASK &&
|
||||
parseInt(netmask) < process.env.JAMBONZ_MIN_GATEWAY_NETMASK) {
|
||||
throw new DbErrorBadRequest(
|
||||
`netmask required to have value equal or greater than ${process.env.JAMBONZ_MIN_GATEWAY_NETMASK}`);
|
||||
}
|
||||
await checkUserScope(req, voip_carrier_sid);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ const {parseAccountSid, parseServiceProviderSid, parseSpeechCredentialSid} = req
|
||||
const {decryptCredential, testWhisper, testDeepgramTTS,
|
||||
getLanguagesAndVoicesForVendor,
|
||||
testPlayHT,
|
||||
testRimelabs} = require('../../utils/speech-utils');
|
||||
testRimelabs,
|
||||
testVerbioTts,
|
||||
testVerbioStt,
|
||||
testSpeechmaticsStt} = require('../../utils/speech-utils');
|
||||
const {DbErrorUnprocessableRequest, DbErrorForbidden, DbErrorBadRequest} = require('../../utils/errors');
|
||||
const {
|
||||
testGoogleTts,
|
||||
@@ -116,11 +119,14 @@ const encryptCredential = (obj) => {
|
||||
role_arn,
|
||||
region,
|
||||
client_id,
|
||||
client_secret,
|
||||
secret,
|
||||
nuance_tts_uri,
|
||||
nuance_stt_uri,
|
||||
speechmatics_stt_uri,
|
||||
deepgram_stt_uri,
|
||||
deepgram_stt_use_tls,
|
||||
deepgram_tts_uri,
|
||||
use_custom_tts,
|
||||
custom_tts_endpoint,
|
||||
custom_tts_endpoint_url,
|
||||
@@ -140,6 +146,7 @@ const encryptCredential = (obj) => {
|
||||
model_id,
|
||||
user_id,
|
||||
voice_engine,
|
||||
engine_version,
|
||||
options
|
||||
} = obj;
|
||||
|
||||
@@ -200,10 +207,10 @@ const encryptCredential = (obj) => {
|
||||
|
||||
case 'deepgram':
|
||||
// API key is optional if onprem
|
||||
if (!deepgram_stt_uri) {
|
||||
if (!deepgram_stt_uri || !deepgram_tts_uri) {
|
||||
assert(api_key, 'invalid deepgram speech credential: api_key is required');
|
||||
}
|
||||
const deepgramData = JSON.stringify({api_key, deepgram_stt_uri, deepgram_stt_use_tls});
|
||||
const deepgramData = JSON.stringify({api_key, deepgram_stt_uri, deepgram_stt_use_tls, deepgram_tts_uri});
|
||||
return encrypt(deepgramData);
|
||||
|
||||
case 'ibm':
|
||||
@@ -231,6 +238,12 @@ const encryptCredential = (obj) => {
|
||||
const elevenlabsData = JSON.stringify({api_key, model_id, options});
|
||||
return encrypt(elevenlabsData);
|
||||
|
||||
case 'speechmatics':
|
||||
assert(api_key, 'invalid speechmatics speech credential: api_key is required');
|
||||
assert(speechmatics_stt_uri, 'invalid speechmatics speech credential: speechmatics_stt_uri is required');
|
||||
const speechmaticsData = JSON.stringify({api_key, speechmatics_stt_uri, options});
|
||||
return encrypt(speechmaticsData);
|
||||
|
||||
case 'playht':
|
||||
assert(api_key, 'invalid playht speech credential: api_key is required');
|
||||
assert(user_id, 'invalid playht speech credential: user_id is required');
|
||||
@@ -255,6 +268,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 +467,8 @@ router.put('/:sid', async(req, res) => {
|
||||
options,
|
||||
deepgram_stt_uri,
|
||||
deepgram_stt_use_tls,
|
||||
deepgram_tts_uri,
|
||||
engine_version
|
||||
} = req.body;
|
||||
|
||||
const newCred = {
|
||||
@@ -473,6 +495,8 @@ router.put('/:sid', async(req, res) => {
|
||||
options,
|
||||
deepgram_stt_uri,
|
||||
deepgram_stt_use_tls,
|
||||
deepgram_tts_uri,
|
||||
engine_version
|
||||
};
|
||||
logger.info({o, newCred}, 'updating speech credential with this new credential');
|
||||
obj.credential = encryptCredential(newCred);
|
||||
@@ -501,7 +525,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 +637,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 +696,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 {
|
||||
@@ -753,6 +776,18 @@ router.get('/:sid/test', async(req, res) => {
|
||||
SpeechCredential.ttsTestResult(sid, false);
|
||||
}
|
||||
}
|
||||
} else if (cred.vendor === 'speechmatics') {
|
||||
const {api_key} = credential;
|
||||
if (cred.use_for_stt) {
|
||||
try {
|
||||
await testSpeechmaticsStt(logger, {api_key});
|
||||
results.stt.status = 'ok';
|
||||
SpeechCredential.ttsTestResult(sid, true);
|
||||
} catch (err) {
|
||||
results.stt = {status: 'fail', reason: err.message};
|
||||
SpeechCredential.ttsTestResult(sid, false);
|
||||
}
|
||||
}
|
||||
} else if (cred.vendor === 'playht') {
|
||||
if (cred.use_for_tts) {
|
||||
try {
|
||||
@@ -803,6 +838,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);
|
||||
@@ -819,7 +875,7 @@ router.get('/:sid/test', async(req, res) => {
|
||||
router.get('/speech/supportedLanguagesAndVoices', async(req, res) => {
|
||||
const {logger, getTtsVoices} = req.app.locals;
|
||||
try {
|
||||
const {vendor, label} = req.query;
|
||||
const {vendor, label, create_new} = req.query;
|
||||
if (!vendor) {
|
||||
throw new DbErrorBadRequest('vendor is required');
|
||||
}
|
||||
@@ -827,7 +883,7 @@ router.get('/speech/supportedLanguagesAndVoices', async(req, res) => {
|
||||
const service_provider_sid = req.user.service_provider_sid ||
|
||||
req.body.service_provider_sid || parseServiceProviderSid(req);
|
||||
|
||||
const credentials = await SpeechCredential.getSpeechCredentialsByVendorAndLabel(
|
||||
const credentials = create_new ? null : await SpeechCredential.getSpeechCredentialsByVendorAndLabel(
|
||||
service_provider_sid, account_sid, vendor, label);
|
||||
const tmp = credentials && credentials.length > 0 ? credentials[0] : null;
|
||||
const cred = tmp ? JSON.parse(decrypt(tmp.credential)) : null;
|
||||
|
||||
@@ -3882,6 +3882,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:
|
||||
|
||||
218
lib/utils/speech-data/stt-speechmatics.js
Normal file
218
lib/utils/speech-data/stt-speechmatics.js
Normal file
@@ -0,0 +1,218 @@
|
||||
module.exports = [
|
||||
{
|
||||
name: 'Automatic',
|
||||
value: 'auto',
|
||||
},
|
||||
{
|
||||
name: 'Arabic',
|
||||
value: 'ar',
|
||||
},
|
||||
{
|
||||
name: 'Bashkir',
|
||||
value: 'ba',
|
||||
},
|
||||
{
|
||||
name: 'Basque',
|
||||
value: 'eu',
|
||||
},
|
||||
{
|
||||
name: 'Belarusian',
|
||||
value: 'be',
|
||||
},
|
||||
{
|
||||
name: 'Bulgarian',
|
||||
value: 'bg',
|
||||
},
|
||||
{
|
||||
name: 'Cantonese',
|
||||
value: 'yue',
|
||||
},
|
||||
{
|
||||
name: 'Catalan',
|
||||
value: 'ca',
|
||||
},
|
||||
{
|
||||
name: 'Croatian',
|
||||
value: 'hr',
|
||||
},
|
||||
{
|
||||
name: 'Czech',
|
||||
value: 'cs',
|
||||
},
|
||||
{
|
||||
name: 'Danish',
|
||||
value: 'da',
|
||||
},
|
||||
{
|
||||
name: 'Dutch',
|
||||
value: 'nl',
|
||||
},
|
||||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: 'Esperanto',
|
||||
value: 'eo',
|
||||
},
|
||||
{
|
||||
name: 'Estonian',
|
||||
value: 'et',
|
||||
},
|
||||
{
|
||||
name: 'Finnish',
|
||||
value: 'fi',
|
||||
},
|
||||
{
|
||||
name: 'French',
|
||||
value: 'fr',
|
||||
},
|
||||
{
|
||||
name: 'Galician',
|
||||
value: 'gl',
|
||||
},
|
||||
{
|
||||
name: 'German',
|
||||
value: 'de',
|
||||
},
|
||||
{
|
||||
name: 'Greek',
|
||||
value: 'el',
|
||||
},
|
||||
{
|
||||
name: 'Hebrew',
|
||||
value: 'he',
|
||||
},
|
||||
{
|
||||
name: 'Hindi',
|
||||
value: 'hi',
|
||||
},
|
||||
{
|
||||
name: 'Hungarian',
|
||||
value: 'hu',
|
||||
},
|
||||
{
|
||||
name: 'Irish',
|
||||
value: 'ga',
|
||||
},
|
||||
{
|
||||
name: 'Interlingua',
|
||||
value: 'ia',
|
||||
},
|
||||
{
|
||||
name: 'Italian',
|
||||
value: 'it',
|
||||
},
|
||||
{
|
||||
name: 'Indonesian',
|
||||
value: 'id',
|
||||
},
|
||||
{
|
||||
name: 'Japanese',
|
||||
value: 'ja',
|
||||
},
|
||||
{
|
||||
name: 'Korean',
|
||||
value: 'ko',
|
||||
},
|
||||
{
|
||||
name: 'Latvian',
|
||||
value: 'lv',
|
||||
},
|
||||
{
|
||||
name: 'Lithuanian',
|
||||
value: 'lt',
|
||||
},
|
||||
{
|
||||
name: 'Maltese',
|
||||
value: 'mt',
|
||||
},
|
||||
{
|
||||
name: 'Malay',
|
||||
value: 'ms',
|
||||
},
|
||||
{
|
||||
name: 'Mandarin',
|
||||
value: 'cmn',
|
||||
},
|
||||
{
|
||||
name: 'Marathi',
|
||||
value: 'mr',
|
||||
},
|
||||
{
|
||||
name: 'Mongolian',
|
||||
value: 'mn',
|
||||
},
|
||||
{
|
||||
name: 'Norwegian',
|
||||
value: 'no',
|
||||
},
|
||||
{
|
||||
name: 'Persian',
|
||||
value: 'fa',
|
||||
},
|
||||
{
|
||||
name: 'Polish',
|
||||
value: 'pl',
|
||||
},
|
||||
{
|
||||
name: 'Portuguese',
|
||||
value: 'pt',
|
||||
},
|
||||
{
|
||||
name: 'Romanian',
|
||||
value: 'ro',
|
||||
},
|
||||
{
|
||||
name: 'Russian',
|
||||
value: 'ru',
|
||||
},
|
||||
{
|
||||
name: 'Slovakian',
|
||||
value: 'sk',
|
||||
},
|
||||
{
|
||||
name: 'Slovenian',
|
||||
value: 'sl',
|
||||
},
|
||||
{
|
||||
name: 'Spanish',
|
||||
value: 'es',
|
||||
},
|
||||
{
|
||||
name: 'Spanish & English bilingual',
|
||||
value: 'es',
|
||||
},
|
||||
{
|
||||
name: 'Swedish',
|
||||
value: 'sv',
|
||||
},
|
||||
{
|
||||
name: 'Tamil',
|
||||
value: 'ta',
|
||||
},
|
||||
{
|
||||
name: 'Thai',
|
||||
value: 'th',
|
||||
},
|
||||
{
|
||||
name: 'Turkish',
|
||||
value: 'tr',
|
||||
},
|
||||
{
|
||||
name: 'Uyghur',
|
||||
value: 'ug',
|
||||
},
|
||||
{
|
||||
name: 'Ukrainian',
|
||||
value: 'uk',
|
||||
},
|
||||
{
|
||||
name: 'Vietnamese',
|
||||
value: 'vi',
|
||||
},
|
||||
{
|
||||
name: 'Welsh',
|
||||
value: 'cy',
|
||||
},
|
||||
];
|
||||
14
lib/utils/speech-data/stt-verbio.js
Normal file
14
lib/utils/speech-data/stt-verbio.js
Normal 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' },
|
||||
];
|
||||
152
lib/utils/speech-data/tts-languages-playht.js
Normal file
152
lib/utils/speech-data/tts-languages-playht.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// languages.js
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
name: 'English',
|
||||
value: 'english'
|
||||
},
|
||||
{
|
||||
name: 'Mandarin',
|
||||
value: 'mandarin'
|
||||
},
|
||||
{
|
||||
name: 'Hindi',
|
||||
value: 'hindi'
|
||||
},
|
||||
{
|
||||
name: 'Japanese',
|
||||
value: 'japanese'
|
||||
},
|
||||
{
|
||||
name: 'Korean',
|
||||
value: 'korean'
|
||||
},
|
||||
{
|
||||
name: 'Arabic',
|
||||
value: 'arabic'
|
||||
},
|
||||
{
|
||||
name: 'Spanish',
|
||||
value: 'spanish'
|
||||
},
|
||||
{
|
||||
name: 'French',
|
||||
value: 'french'
|
||||
},
|
||||
{
|
||||
name: 'Italian',
|
||||
value: 'italian'
|
||||
},
|
||||
{
|
||||
name: 'Portuguese',
|
||||
value: 'portuguese'
|
||||
},
|
||||
{
|
||||
name: 'German',
|
||||
value: 'german'
|
||||
},
|
||||
{
|
||||
name: 'Dutch',
|
||||
value: 'dutch'
|
||||
},
|
||||
{
|
||||
name: 'Swedish',
|
||||
value: 'swedish'
|
||||
},
|
||||
{
|
||||
name: 'Czech',
|
||||
value: 'czech'
|
||||
},
|
||||
{
|
||||
name: 'Polish',
|
||||
value: 'polish'
|
||||
},
|
||||
{
|
||||
name: 'Russian',
|
||||
value: 'russian'
|
||||
},
|
||||
{
|
||||
name: 'Bulgarian',
|
||||
value: 'bulgarian'
|
||||
},
|
||||
{
|
||||
name: 'Hebrew',
|
||||
value: 'hebrew'
|
||||
},
|
||||
{
|
||||
name: 'Greek',
|
||||
value: 'greek'
|
||||
},
|
||||
{
|
||||
name: 'Turkish',
|
||||
value: 'turkish'
|
||||
},
|
||||
{
|
||||
name: 'Afrikaans',
|
||||
value: 'afrikaans'
|
||||
},
|
||||
{
|
||||
name: 'Xhosa',
|
||||
value: 'xhosa'
|
||||
},
|
||||
{
|
||||
name: 'Tagalog',
|
||||
value: 'tagalog'
|
||||
},
|
||||
{
|
||||
name: 'Malay',
|
||||
value: 'malay'
|
||||
},
|
||||
{
|
||||
name: 'Indonesian',
|
||||
value: 'indonesian'
|
||||
},
|
||||
{
|
||||
name: 'Bengali',
|
||||
value: 'bengali'
|
||||
},
|
||||
{
|
||||
name: 'Serbian',
|
||||
value: 'serbian'
|
||||
},
|
||||
{
|
||||
name: 'Thai',
|
||||
value: 'thai'
|
||||
},
|
||||
{
|
||||
name: 'Urdu',
|
||||
value: 'urdu'
|
||||
},
|
||||
{
|
||||
name: 'Croatian',
|
||||
value: 'croatian'
|
||||
},
|
||||
{
|
||||
name: 'Hungarian',
|
||||
value: 'hungarian'
|
||||
},
|
||||
{
|
||||
name: 'Danish',
|
||||
value: 'danish'
|
||||
},
|
||||
{
|
||||
name: 'Amharic',
|
||||
value: 'amharic'
|
||||
},
|
||||
{
|
||||
name: 'Albanian',
|
||||
value: 'albanian'
|
||||
},
|
||||
{
|
||||
name: 'Catalan',
|
||||
value: 'catalan'
|
||||
},
|
||||
{
|
||||
name: 'Ukrainian',
|
||||
value: 'ukrainian'
|
||||
},
|
||||
{
|
||||
name: 'Galician',
|
||||
value: 'galician'
|
||||
}
|
||||
];
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = [
|
||||
{ name: 'Turbo v2', value: 'eleven_turbo_v2' },
|
||||
{ name: 'Turbo v2.5', value: 'eleven_turbo_v2_5' },
|
||||
{ name: 'Multilingual v2', value: 'eleven_multilingual_v2' },
|
||||
{ name: 'Multilingual v1', value: 'eleven_multilingual_v1' },
|
||||
{ name: 'English v1', value: 'eleven_monolingual_v1' },
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = [
|
||||
{ name: 'Play3.0', value: 'Play3.0' },
|
||||
{ name: 'PlayHT2.0-turbo', value: 'PlayHT2.0-turbo' },
|
||||
{ name: 'PlayHT2.0', value: 'PlayHT2.0' },
|
||||
{ name: 'PlayHT1.0', value: 'PlayHT1.0' },
|
||||
|
||||
62
lib/utils/speech-data/tts-verbio.js
Normal file
62
lib/utils/speech-data/tts-verbio.js
Normal 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',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -7,6 +7,7 @@ const bent = require('bent');
|
||||
const fs = require('fs');
|
||||
const { AssemblyAI } = require('assemblyai');
|
||||
const {decrypt, obscureKey} = require('./encrypt-decrypt');
|
||||
const { RealtimeSession } = require('speechmatics');
|
||||
|
||||
const TtsGoogleLanguagesVoices = require('./speech-data/tts-google');
|
||||
const TtsAwsLanguagesVoices = require('./speech-data/tts-aws');
|
||||
@@ -18,11 +19,13 @@ 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');
|
||||
const TtsModelWhisper = require('./speech-data/tts-model-whisper');
|
||||
const TtsModelPlayHT = require('./speech-data/tts-model-playht');
|
||||
const ttsLanguagesPlayHt = require('./speech-data/tts-languages-playht');
|
||||
const TtsModelRimelabs = require('./speech-data/tts-model-rimelabs');
|
||||
|
||||
const SttGoogleLanguagesVoices = require('./speech-data/stt-google');
|
||||
@@ -34,7 +37,10 @@ const SttIbmLanguagesVoices = require('./speech-data/stt-ibm');
|
||||
const SttNvidiaLanguagesVoices = require('./speech-data/stt-nvidia');
|
||||
const SttCobaltLanguagesVoices = require('./speech-data/stt-cobalt');
|
||||
const SttSonioxLanguagesVoices = require('./speech-data/stt-soniox');
|
||||
const SttSpeechmaticsLanguagesVoices = require('./speech-data/stt-speechmatics');
|
||||
const SttAssemblyaiLanguagesVoices = require('./speech-data/stt-assemblyai');
|
||||
const SttVerbioLanguagesVoices = require('./speech-data/stt-verbio');
|
||||
|
||||
|
||||
const testSonioxStt = async(logger, credentials) => {
|
||||
const api_key = credentials;
|
||||
@@ -52,6 +58,61 @@ const testSonioxStt = async(logger, credentials) => {
|
||||
});
|
||||
};
|
||||
|
||||
const testSpeechmaticsStt = async(logger, credentials) => {
|
||||
const {api_key, speechmatics_stt_uri} = credentials;
|
||||
return new Promise(async(resolve, reject) => {
|
||||
try {
|
||||
const session = new RealtimeSession({ apiKey: api_key, realtimeUrl: speechmatics_stt_uri });
|
||||
let transcription = '';
|
||||
session.addListener('Error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
session.addListener('AddTranscript', (message) => {
|
||||
transcription += message.metadata.transcript;
|
||||
});
|
||||
|
||||
session.addListener('EndOfTranscript', () => {
|
||||
resolve(transcription);
|
||||
});
|
||||
|
||||
session
|
||||
.start({
|
||||
transcription_config: {
|
||||
language: 'en',
|
||||
operating_point: 'enhanced',
|
||||
enable_partials: true,
|
||||
max_delay: 2,
|
||||
},
|
||||
audio_format: { type: 'file' },
|
||||
})
|
||||
.then(() => {
|
||||
//prepare file stream
|
||||
const fileStream = fs.createReadStream(`${__dirname}/../../data/test_audio.wav`);
|
||||
|
||||
//send it
|
||||
fileStream.on('data', (sample) => {
|
||||
session.sendAudio(sample);
|
||||
});
|
||||
|
||||
//end the session
|
||||
fileStream.on('end', () => {
|
||||
session.stop();
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
} catch (error) {
|
||||
logger.info({error}, 'failed to get speechmatics transcript');
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const testNuanceTts = async(logger, getTtsVoices, credentials) => {
|
||||
const voices = await getTtsVoices({vendor: 'nuance', credentials});
|
||||
return voices;
|
||||
@@ -90,8 +151,8 @@ const testGoogleStt = async(logger, credentials) => {
|
||||
};
|
||||
|
||||
const testDeepgramStt = async(logger, credentials) => {
|
||||
const {api_key} = credentials;
|
||||
const deepgram = new Deepgram(api_key);
|
||||
const {api_key, deepgram_stt_uri, deepgram_stt_use_tls} = credentials;
|
||||
const deepgram = new Deepgram(api_key, deepgram_stt_uri, deepgram_stt_uri && deepgram_stt_use_tls);
|
||||
|
||||
const mimetype = 'audio/wav';
|
||||
const source = {
|
||||
@@ -117,9 +178,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 +242,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 +254,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 +267,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) {
|
||||
@@ -263,9 +329,10 @@ const testPlayHT = async(logger, synthAudio, credentials) => {
|
||||
{
|
||||
vendor: 'playht',
|
||||
credentials,
|
||||
language: 'en-US',
|
||||
language: 'english',
|
||||
voice: 's3://voice-cloning-zero-shot/d9ff78ba-d016-47f6-b0ef-dd630f59414e/female-cs/manifest.json',
|
||||
text: 'Hi there and welcome to jambones!'
|
||||
text: 'Hi there and welcome to jambones!',
|
||||
renderForCaching: true
|
||||
}
|
||||
);
|
||||
// Test if playHT can fetch voices
|
||||
@@ -288,7 +355,8 @@ const testRimelabs = async(logger, synthAudio, credentials) => {
|
||||
credentials,
|
||||
language: 'en-US',
|
||||
voice: 'amber',
|
||||
text: 'Hi there and welcome to jambones!'
|
||||
text: 'Hi there and welcome to jambones!',
|
||||
renderForCaching: true
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -305,7 +373,8 @@ const testWhisper = async(logger, synthAudio, credentials) => {
|
||||
credentials,
|
||||
language: 'en-US',
|
||||
voice: 'alloy',
|
||||
text: 'Hi there and welcome to jambones!'
|
||||
text: 'Hi there and welcome to jambones!',
|
||||
renderForCaching: true
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -321,7 +390,8 @@ const testDeepgramTTS = async(logger, synthAudio, credentials) => {
|
||||
vendor: 'deepgram',
|
||||
credentials,
|
||||
model: 'aura-asteria-en',
|
||||
text: 'Hi there and welcome to jambones!'
|
||||
text: 'Hi there and welcome to jambones!',
|
||||
renderForCaching: true
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -364,6 +434,44 @@ 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!',
|
||||
renderForCaching: true
|
||||
}
|
||||
);
|
||||
} 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;
|
||||
|
||||
@@ -465,6 +573,7 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
|
||||
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
|
||||
obj.deepgram_stt_uri = o.deepgram_stt_uri;
|
||||
obj.deepgram_stt_use_tls = o.deepgram_stt_use_tls;
|
||||
obj.deepgram_tts_uri = o.deepgram_tts_uri;
|
||||
}
|
||||
else if ('ibm' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
@@ -482,6 +591,10 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
|
||||
} else if ('soniox' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
|
||||
} else if ('speechmatics' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
|
||||
obj.speechmatics_stt_uri = o.speechmatics_stt_uri;
|
||||
} else if ('elevenlabs' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
|
||||
@@ -510,6 +623,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 +684,10 @@ 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);
|
||||
case 'speechmatics':
|
||||
return await getLanguagesVoicesForSpeechmatics(credential, getTtsVoices, logger);
|
||||
default:
|
||||
logger.info(`invalid vendor ${vendor}, return empty result`);
|
||||
throw new Error(`Invalid vendor ${vendor}`);
|
||||
@@ -678,6 +800,10 @@ async function getLanguagesVoicesForSoniox(credential) {
|
||||
return tranform(undefined, SttSonioxLanguagesVoices);
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForSpeechmatics(credential) {
|
||||
return tranform(undefined, SttSpeechmaticsLanguagesVoices);
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForElevenlabs(credential) {
|
||||
if (credential) {
|
||||
const get = bent('https://api.elevenlabs.io', 'GET', 'json', {
|
||||
@@ -745,6 +871,7 @@ const fetchLayHTVoices = async(credential) => {
|
||||
|
||||
async function getLanguagesVoicesForPlayHT(credential) {
|
||||
if (credential) {
|
||||
const {voice_engine} = credential;
|
||||
const [cloned_voice, voices] = await fetchLayHTVoices(credential);
|
||||
const list_voices = [...cloned_voice, ...voices];
|
||||
|
||||
@@ -752,35 +879,57 @@ async function getLanguagesVoicesForPlayHT(credential) {
|
||||
let name = `${d.name} -${concat(d.accent)}${concat(d.age)}${concat(d.gender)}${concat(d.loudness)}` +
|
||||
`${concat(d.style)}${concat(d.tempo)}${concat(d.texture)}` ;
|
||||
name = name.endsWith(',') ? name.trim().slice(0, -1) : name;
|
||||
name += !d.language_code ? ' - Custom Voice' : '';
|
||||
|
||||
return {
|
||||
value: `${d.id}`,
|
||||
name
|
||||
};
|
||||
};
|
||||
|
||||
const ttsVoices = list_voices.reduce((acc, voice) => {
|
||||
if (!credential.voice_engine.includes(voice.voice_engine)) {
|
||||
const buildPlay30Payload = () => {
|
||||
// PlayHT3.0 can play different languages with differrent voice.
|
||||
// all voices will be added to english language by default and orther langauges will get voices from english.
|
||||
const ttsVoices = ttsLanguagesPlayHt.map((l) => ({
|
||||
...l,
|
||||
voices: l.value === 'english' ? list_voices.map((v) => buildVoice(v)) : []
|
||||
}));
|
||||
return tranform(ttsVoices, undefined, TtsModelPlayHT);
|
||||
};
|
||||
|
||||
const buildPayload = () => {
|
||||
const ttsVoices = list_voices.reduce((acc, voice) => {
|
||||
if (!voice_engine.includes(voice.voice_engine)) {
|
||||
return acc;
|
||||
}
|
||||
const languageCode = voice.language_code;
|
||||
// custom voice does not have language code
|
||||
if (!languageCode) {
|
||||
voice.language_code = 'en';
|
||||
voice.language = 'Custom-English';
|
||||
}
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push(buildVoice(voice));
|
||||
} else {
|
||||
acc.push({
|
||||
value: voice.language_code,
|
||||
name: voice.language,
|
||||
voices: [buildVoice(voice)]
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
const languageCode = voice.language_code;
|
||||
// custom voice does not have language code
|
||||
if (!languageCode) {
|
||||
voice.language_code = 'en';
|
||||
voice.language = 'Custom-English';
|
||||
}
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push(buildVoice(voice));
|
||||
} else {
|
||||
acc.push({
|
||||
value: voice.language_code,
|
||||
name: voice.language,
|
||||
voices: [buildVoice(voice)]
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
return tranform(ttsVoices, undefined, TtsModelPlayHT);
|
||||
}, []);
|
||||
return tranform(ttsVoices, undefined, TtsModelPlayHT);
|
||||
};
|
||||
|
||||
switch (voice_engine) {
|
||||
case 'Play3.0':
|
||||
return buildPlay30Payload();
|
||||
|
||||
default:
|
||||
return buildPayload();
|
||||
}
|
||||
}
|
||||
return tranform(TtsPlayHtLanguagesVoices, undefined, TtsModelPlayHT);
|
||||
}
|
||||
@@ -814,6 +963,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 +1107,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 +1153,8 @@ module.exports = {
|
||||
getSpeechCredential,
|
||||
decryptCredential,
|
||||
testWhisper,
|
||||
getLanguagesAndVoicesForVendor
|
||||
testVerbioTts,
|
||||
testVerbioStt,
|
||||
getLanguagesAndVoicesForVendor,
|
||||
testSpeechmaticsStt
|
||||
};
|
||||
|
||||
2898
package-lock.json
generated
2898
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jambonz-api-server",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.2",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
@@ -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.10",
|
||||
"@jambonz/speech-utils": "^0.1.20",
|
||||
"@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",
|
||||
@@ -46,12 +46,13 @@
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mailgun.js": "^10.2.1",
|
||||
"microsoft-cognitiveservices-speech-sdk": "1.36.0",
|
||||
"mysql2": "^3.9.3",
|
||||
"mysql2": "^3.11.0",
|
||||
"nocache": "4.0.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"pino": "^8.20.0",
|
||||
"short-uuid": "^4.2.2",
|
||||
"speechmatics": "^4.0.0",
|
||||
"stream-buffers": "^3.0.2",
|
||||
"stripe": "^14.24.0",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
|
||||
@@ -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);
|
||||
@@ -152,7 +152,9 @@ test('account tests', async(t) => {
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
});
|
||||
console.log(result);
|
||||
t.ok(result.name === 'daveh' , 'successfully retrieved account by sid');
|
||||
t.ok(result.enable_debug_log === 0 , 'enable_debug_log default value ok');
|
||||
|
||||
/* update account with account level token */
|
||||
result = await request.put(`/Accounts/${sid}`, {
|
||||
@@ -177,8 +179,8 @@ test('account tests', async(t) => {
|
||||
name: 'recordings',
|
||||
access_key_id: 'access_key_id',
|
||||
secret_access_key: 'secret access key'
|
||||
}
|
||||
|
||||
},
|
||||
enable_debug_log: true
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully updated account using account level token');
|
||||
@@ -194,6 +196,7 @@ test('account tests', async(t) => {
|
||||
t.ok(result.bucket_credential.access_key_id === 'access_key_id', 'bucket_access_key_id was updated');
|
||||
t.ok(result.record_all_calls === 1, 'record_all_calls was updated');
|
||||
t.ok(result.record_format === 'wav', 'record_format was updated');
|
||||
t.ok(result.enable_debug_log, 'enable_debug_log was updated');
|
||||
|
||||
/* verify that account level api key last_used was updated*/
|
||||
result = await request.get(`/Accounts/${sid}/ApiKeys`, {
|
||||
@@ -314,6 +317,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,
|
||||
|
||||
@@ -17,6 +17,53 @@ test('sip gateway tests', async(t) => {
|
||||
let result;
|
||||
const voip_carrier_sid = await createVoipCarrier(request);
|
||||
|
||||
/* add a invalid sip gateway */
|
||||
const STORED_JAMBONZ_MIN_GATEWAY_NETMASK = process.env.JAMBONZ_MIN_GATEWAY_NETMASK;
|
||||
process.env.JAMBONZ_MIN_GATEWAY_NETMASK = 24;
|
||||
|
||||
result = await request.post('/SipGateways', {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
simple: false,
|
||||
body: {
|
||||
voip_carrier_sid,
|
||||
ipv4: '1.2.3.4',
|
||||
netmask: 1,
|
||||
inbound: true,
|
||||
outbound: true,
|
||||
protocol: 'tcp'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 400, 'successfully created sip gateway ');
|
||||
|
||||
result = await request.post('/SipGateways', {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
body: {
|
||||
voip_carrier_sid,
|
||||
ipv4: '1.2.3.4',
|
||||
netmask: 24,
|
||||
inbound: true,
|
||||
outbound: true,
|
||||
protocol: 'tcp'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully created sip gateway ');
|
||||
|
||||
process.env.JAMBONZ_MIN_GATEWAY_NETMASK = STORED_JAMBONZ_MIN_GATEWAY_NETMASK;
|
||||
|
||||
/* delete sip gateways */
|
||||
result = await request.delete(`/SipGateways/${result.body.sid}`, {
|
||||
resolveWithFullResponse: true,
|
||||
simple: false,
|
||||
json: true,
|
||||
auth: authAdmin
|
||||
});
|
||||
//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,
|
||||
@@ -75,6 +122,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);
|
||||
|
||||
|
||||
@@ -371,7 +371,8 @@ test('speech credentials tests', async(t) => {
|
||||
vendor: 'deepgram',
|
||||
use_for_stt: true,
|
||||
deepgram_stt_uri: "127.0.0.1:50002",
|
||||
deepgram_stt_use_tls: true
|
||||
deepgram_stt_use_tls: true,
|
||||
deepgram_tts_uri: 'https://server.com'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully added speech credential for deepgram');
|
||||
@@ -386,6 +387,7 @@ test('speech credentials tests', async(t) => {
|
||||
t.ok(result.statusCode === 200, 'successfully get speech credential for deepgram');
|
||||
t.ok(result.body.deepgram_stt_uri === '127.0.0.1:50002', "deepgram_stt_uri is correct for deepgram");
|
||||
t.ok(result.body.deepgram_stt_use_tls === true, "deepgram_stt_use_tls is correct for deepgram");
|
||||
t.ok(result.body.deepgram_tts_uri === 'https://server.com', "deepgram_tts_uri is correct for deepgram")
|
||||
|
||||
result = await request.put(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
|
||||
resolveWithFullResponse: true,
|
||||
@@ -395,7 +397,8 @@ test('speech credentials tests', async(t) => {
|
||||
vendor: 'deepgram',
|
||||
use_for_stt: true,
|
||||
deepgram_stt_uri: "127.0.0.2:50002",
|
||||
deepgram_stt_use_tls: false
|
||||
deepgram_stt_use_tls: false,
|
||||
deepgram_tts_uri: 'https://server2.com'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully updated speech credential for deepgram onprem');
|
||||
@@ -409,6 +412,7 @@ test('speech credentials tests', async(t) => {
|
||||
t.ok(result.statusCode === 200, 'successfully get speech credential for deepgram onprem');
|
||||
t.ok(result.body.deepgram_stt_uri === '127.0.0.2:50002', "deepgram_stt_uri is correct for deepgram onprem");
|
||||
t.ok(result.body.deepgram_stt_use_tls === false, "deepgram_stt_use_tls is correct for deepgram onprem");
|
||||
t.ok(result.body.deepgram_tts_uri === 'https://server2.com', "deepgram_tts_uri is correct for deepgram onprem");
|
||||
|
||||
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
|
||||
auth: authUser,
|
||||
@@ -528,6 +532,39 @@ test('speech credentials tests', async(t) => {
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
|
||||
}
|
||||
|
||||
/* add a credential for Speechmatics */
|
||||
if (process.env.SPEECHMATICS_API_KEY) {
|
||||
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
vendor: 'speechmatics',
|
||||
use_for_stt: true,
|
||||
api_key: process.env.SPEECHMATICS_API_KEY,
|
||||
speechmatics_stt_uri: 'eu2.rt.speechmatics.com'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully added speech credential for speechmatics');
|
||||
const ms_sid = result.body.sid;
|
||||
|
||||
/* test the speech credential */
|
||||
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${ms_sid}/test`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.statusCode === 200 && result.body.stt.status === 'ok', 'successfully tested speech credential for speechmatics');
|
||||
|
||||
/* delete the credential */
|
||||
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${ms_sid}`, {
|
||||
auth: authUser,
|
||||
resolveWithFullResponse: true,
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
|
||||
}
|
||||
|
||||
/* add a credential for nvidia */
|
||||
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
resolveWithFullResponse: true,
|
||||
@@ -750,7 +787,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 +804,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,
|
||||
|
||||
@@ -16,7 +16,9 @@ test('system information test', async(t) => {
|
||||
body: {
|
||||
domain_name: 'test.com',
|
||||
sip_domain_name: 'sip.test.com',
|
||||
monitoring_domain_name: 'monitor.test.com'
|
||||
monitoring_domain_name: 'monitor.test.com',
|
||||
private_network_cidr: '192.168.1.0/24, 10.10.100.1',
|
||||
log_level: 'info'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully created system information ');
|
||||
@@ -24,6 +26,8 @@ test('system information test', async(t) => {
|
||||
t.ok(body.domain_name === 'test.com', 'added domain_name ok');
|
||||
t.ok(body.sip_domain_name === 'sip.test.com', 'added sip_domain_name ok');
|
||||
t.ok(body.monitoring_domain_name === 'monitor.test.com', 'added monitoring_domain_name ok');
|
||||
t.ok(body.private_network_cidr === '192.168.1.0/24, 10.10.100.1', 'added private_network_cidr ok');
|
||||
t.ok(body.log_level === 'info', 'added log_level ok');
|
||||
|
||||
result = await request.get('/SystemInformation', {
|
||||
auth: authAdmin,
|
||||
@@ -32,6 +36,8 @@ test('system information test', async(t) => {
|
||||
t.ok(result.domain_name === 'test.com', 'get domain_name ok');
|
||||
t.ok(result.sip_domain_name === 'sip.test.com', 'get sip_domain_name ok');
|
||||
t.ok(result.monitoring_domain_name === 'monitor.test.com', 'get monitoring_domain_name ok');
|
||||
t.ok(result.private_network_cidr === '192.168.1.0/24, 10.10.100.1', 'get private_network_cidr ok');
|
||||
t.ok(result.log_level === 'info', 'added log_level ok');
|
||||
|
||||
result = await request.post('/SystemInformation', {
|
||||
resolveWithFullResponse: true,
|
||||
@@ -40,7 +46,9 @@ test('system information test', async(t) => {
|
||||
body: {
|
||||
domain_name: 'test1.com',
|
||||
sip_domain_name: 'sip1.test.com',
|
||||
monitoring_domain_name: 'monitor1.test.com'
|
||||
monitoring_domain_name: 'monitor1.test.com',
|
||||
private_network_cidr: '',
|
||||
log_level: 'debug'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully updated system information ');
|
||||
@@ -48,6 +56,8 @@ test('system information test', async(t) => {
|
||||
t.ok(body.domain_name === 'test1.com', 'updated domain_name ok');
|
||||
t.ok(body.sip_domain_name === 'sip1.test.com', 'updated sip_domain_name ok');
|
||||
t.ok(body.monitoring_domain_name === 'monitor1.test.com', 'updated monitoring_domain_name ok');
|
||||
t.ok(body.private_network_cidr === '', 'updated private_network_cidr ok');
|
||||
t.ok(body.log_level === 'debug', 'updated log_level ok');
|
||||
|
||||
result = await request.get('/SystemInformation', {
|
||||
auth: authAdmin,
|
||||
@@ -56,6 +66,7 @@ test('system information test', async(t) => {
|
||||
t.ok(result.domain_name === 'test1.com', 'get domain_name ok');
|
||||
t.ok(result.sip_domain_name === 'sip1.test.com', 'get sip_domain_name ok');
|
||||
t.ok(result.monitoring_domain_name === 'monitor1.test.com', 'get monitoring_domain_name ok');
|
||||
t.ok(result.log_level === 'debug', 'updated log_level ok');
|
||||
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
|
||||
Reference in New Issue
Block a user