diff --git a/db/jambones.sqs b/db/jambones.sqs
index a6087ac..cb1fac9 100644
--- a/db/jambones.sqs
+++ b/db/jambones.sqs
@@ -87,7 +87,7 @@
-
+
@@ -148,7 +148,7 @@
-
+
@@ -225,7 +225,7 @@
-
+
@@ -279,7 +279,7 @@
-
+
@@ -426,7 +426,7 @@
-
+
@@ -492,7 +492,7 @@
-
+
@@ -670,7 +670,7 @@
-
+
@@ -752,7 +752,7 @@
-
+
@@ -880,7 +880,7 @@
-
+
@@ -901,6 +901,8 @@
+
+
@@ -960,7 +962,7 @@
-
+
@@ -1010,7 +1012,7 @@
-
+
@@ -1110,7 +1112,7 @@
-
+
@@ -1179,7 +1181,7 @@
-
+
@@ -1216,7 +1218,7 @@
-
+
@@ -1325,7 +1327,7 @@
-
+
@@ -1395,7 +1397,7 @@
-
+
@@ -1555,7 +1557,7 @@
-
+
@@ -1594,7 +1596,7 @@
-
+
@@ -1686,7 +1688,7 @@
-
+
@@ -1737,7 +1739,7 @@
-
+
@@ -1833,7 +1835,7 @@
-
+
@@ -1933,7 +1935,7 @@
-
+
@@ -2102,7 +2104,7 @@
-
+
@@ -2210,7 +2212,7 @@
-
+
@@ -2280,7 +2282,7 @@
-
+
@@ -2325,7 +2327,7 @@
-
+
@@ -2392,7 +2394,7 @@
-
+
@@ -2400,7 +2402,7 @@
-
+
@@ -2411,17 +2413,17 @@
-
+
-
-
-
-
+
+
+
+
diff --git a/lib/routes/api/speech-credentials.js b/lib/routes/api/speech-credentials.js
index 075a3c8..528c653 100644
--- a/lib/routes/api/speech-credentials.js
+++ b/lib/routes/api/speech-credentials.js
@@ -8,12 +8,24 @@ const {
testGoogleTts,
testGoogleStt,
testAwsTts,
- testAwsStt
+ testAwsStt,
+ testMicrosoftStt,
+ testMicrosoftTts
} = require('../../utils/speech-utils');
router.post('/', async(req, res) => {
const logger = req.app.locals.logger;
- const {use_for_stt, use_for_tts, vendor, service_key, access_key_id, secret_access_key, aws_region} = req.body;
+ const {
+ use_for_stt,
+ use_for_tts,
+ vendor,
+ service_key,
+ access_key_id,
+ secret_access_key,
+ aws_region,
+ api_key,
+ region
+ } = req.body;
const account_sid = req.user.account_sid || req.body.account_sid;
let service_provider_sid;
if (!account_sid) {
@@ -47,6 +59,13 @@ router.post('/', async(req, res) => {
});
encrypted_credential = encrypt(data);
}
+ else if (vendor === 'microsoft') {
+ const data = JSON.stringify({
+ region,
+ api_key
+ });
+ encrypted_credential = encrypt(data);
+ }
else throw new DbErrorBadRequest(`invalid speech vendor ${vendor}`);
const uuid = await SpeechCredential.make({
account_sid,
@@ -85,6 +104,11 @@ router.get('/', async(req, res) => {
obj.access_key_id = o.access_key_id;
obj.secret_access_key = o.secret_access_key;
}
+ else if ('microsoft' === obj.vendor) {
+ const o = decrypt(credential);
+ obj.api_key = o.api_key;
+ obj.region = o.region;
+ }
return obj;
}));
} catch (err) {
@@ -110,6 +134,11 @@ router.get('/:sid', async(req, res) => {
obj.access_key_id = o.access_key_id;
obj.secret_access_key = o.secret_access_key;
}
+ else if ('microsoft' === obj.vendor) {
+ const o = JSON.parse(decrypt(credential));
+ obj.api_key = o.api_key;
+ obj.region = o.region;
+ }
res.status(200).json(obj);
} catch (err) {
sysError(logger, res, err);
@@ -239,6 +268,29 @@ router.get('/:sid/test', async(req, res) => {
}
}
}
+ else if (cred.vendor === 'microsoft') {
+ const {api_key, region} = credential;
+ if (cred.use_for_tts) {
+ try {
+ await testMicrosoftTts(logger, {api_key, region});
+ 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 testMicrosoftStt(logger, {api_key, region});
+ 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);
} catch (err) {
sysError(logger, res, err);
diff --git a/lib/utils/speech-utils.js b/lib/utils/speech-utils.js
index def6b66..5a34714 100644
--- a/lib/utils/speech-utils.js
+++ b/lib/utils/speech-utils.js
@@ -2,6 +2,7 @@ const ttsGoogle = require('@google-cloud/text-to-speech');
const sttGoogle = require('@google-cloud/speech').v1p1beta1;
const Polly = require('aws-sdk/clients/polly');
const AWS = require('aws-sdk');
+const bent = require('bent');
const fs = require('fs');
const testGoogleTts = async(logger, credentials) => {
@@ -52,9 +53,33 @@ const testAwsStt = (logger, credentials) => {
});
};
+const testMicrosoftTts = async(logger, credentials) => {
+ const {api_key, region} = credentials;
+
+ if (!api_key) throw new Error('testMicrosoftTts: credentials are missing api_key');
+ if (!region) throw new Error('testMicrosoftTts: credentials are missing region');
+ try {
+ const getJSON = bent('json', {
+ 'Ocp-Apim-Subscription-Key': api_key
+ });
+ const response = await getJSON(`https://${region}.tts.speech.microsoft.com/cognitiveservices/voices/list`);
+ return response;
+ } catch (err) {
+ logger.info({err}, `testMicrosoftTts - failed to list voices for region ${region}`);
+ throw err;
+ }
+};
+
+const testMicrosoftStt = async(logger, credentials) => {
+ //TODO
+ return true;
+};
+
module.exports = {
testGoogleTts,
testGoogleStt,
testAwsTts,
- testAwsStt
+ testAwsStt,
+ testMicrosoftTts,
+ testMicrosoftStt
};
diff --git a/package.json b/package.json
index f7c9af9..a165898 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"dependencies": {
"@google-cloud/speech": "^4.2.0",
"@google-cloud/text-to-speech": "^3.1.3",
+ "microsoft-cognitiveservices-speech-sdk": "^1.19.0",
"@jambonz/db-helpers": "^0.6.12",
"@jambonz/realtimedb-helpers": "^0.4.3",
"@jambonz/time-series": "^0.1.5",
diff --git a/test/speech-credentials.js b/test/speech-credentials.js
index 5c7d918..82791ac 100644
--- a/test/speech-credentials.js
+++ b/test/speech-credentials.js
@@ -7,6 +7,7 @@ const request = require('request-promise-native').defaults({
baseUrl: 'http://127.0.0.1:3000/v1'
});
const {createServiceProvider, createAccount, deleteObjectBySid} = require('./utils');
+const { noopLogger } = require('@jambonz/realtimedb-helpers/lib/utils');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
@@ -109,6 +110,37 @@ test('speech credentials tests', async(t) => {
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
+ /* add a credential for microsoft */
+ if (process.env.MICROSOFT_API_KEY && process.env.MICROSOFT_REGION) {
+ result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
+ resolveWithFullResponse: true,
+ auth: authUser,
+ json: true,
+ body: {
+ vendor: 'microsoft',
+ use_for_tts: true,
+ api_key: process.env.MICROSOFT_API_KEY,
+ region: process.env.MICROSOFT_REGION
+ }
+ });
+ t.ok(result.statusCode === 201, 'successfully added speech credential');
+ 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));
+
+ /* 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');
+ }
await deleteObjectBySid(request, '/Accounts', account_sid);
await deleteObjectBySid(request, '/ServiceProviders', service_provider_sid);