botframework - 语音服务令牌持续时间延长问题
问题描述
我需要让语音服务长时间保持活动状态。
语音服务令牌默认持续 10 分钟。
因此需要延长服务期限。
考虑了以下方法:
1 - 允许其持续时间延长的用户令牌生成参数。
2 - 在我的代码中捕获令牌过期异常并请求新的令牌白化,强制我的机器人重新启动。
虽然还没有找到解决方案。令牌过期后,
我的机器人应用程序总是中断消息。
这是我的机器人代码:failed: HTTP Authentication failed; no valid credentials available
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>FTD Bit_Bot</title>
</head>
<body style="background-color: #fff; background-image: url('img/FTDBackground.jpg');">
<div id="chatbot" role="main"
data-cb-width="320px"
data-cb-height="400px"
data-cb-border="1px solid #ededed"
data-cb-border-radius="0px"
data-cb-background-color="white"
data-cb-position="fixed"
data-cb-right="40px"
data-cb-bottom="100px"
data-cb-zIndex="1"
data-cb-bubble-background="rgba(217, 217, 217, 0.15)"
data-cb-bubble-from-user-background="#e22e2c"
data-cb-bubble-from-user-text-color="white"
data-cb-bubble-max-width="600"
data-cb-bot-avatar-image="http://demo.radical-thinking.net/bella/microsoft/chat-assets/img/Chat-in-day-Workshop-Icon.png"
data-cb-hide-send-box="false"
data-cb-hide-upload-button="true"
data-cb-send-box-button-color="#e22e2c"
data-cb-send-box-button-color-on-disabled="#CCC"
data-cb-send-box-button-color-on-focus="#333"
data-cb-send-box-button-color-on-hover="#333"
data-cb-send-box-height="30"
data-cb-suggested-action-text-color="black"
data-cb-suggested-action-border="solid 2px #e22e2c"
data-cb-suggested-action-height="30">
</div>
<!-- Include ajax library for speech service token request call -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<!-- Include webchat client functionality (https://github.com/Microsoft/BotFramework-WebChat) -->
<!-- Change to the latest directory to stop the 'No renderer for this activity' error. However the avatar won't show -->
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js" charset="utf-8"></script>
<script>
var chatbot = document.getElementById('chatbot');
// Style modifications
const styleOptions = {
// Colors
bubbleBackground: chatbot.dataset.cbBubbleBackground,
bubbleFromUserBackground: chatbot.dataset.cbBubbleFromUserBackground,
bubbleFromUserTextColor: chatbot.dataset.cbBubbleFromUserTextColor,
bubbleMaxWidth: parseInt(chatbot.dataset.cbBubbleMaxWidth), // maximum width of text message
// Avatar
botAvatarImage: chatbot.dataset.cbBotAvatarImage,
userAvatarInitials: '',
// Send box
hideSendBox: JSON.parse(chatbot.dataset.cbHideSendBox),
hideUploadButton: JSON.parse(chatbot.dataset.cbHideUploadButton),
sendBoxButtonColor: chatbot.dataset.cbSendBoxButtonColor,
sendBoxButtonColorOnDisabled: chatbot.dataset.cbSendBoxButtonColorOnDisabled,
sendBoxButtonColorOnFocus: chatbot.dataset.cbSendBoxButtonColorOnFocus,
sendBoxButtonColorOnHover: chatbot.dataset.cbSendBoxButtonColorOnHover,
sendBoxHeight: parseInt(chatbot.dataset.cbSendBoxHeight),
// Suggested actions
suggestedActionTextColor: chatbot.dataset.cbSuggestedActionTextColor,
suggestedActionBorder: chatbot.dataset.cbSuggestedActionBorder,
suggestedActionHeight: parseInt(chatbot.dataset.cbSuggestedActionHeight),
}
</script>
<script>
var chatbot = document.getElementById("chatbot");
//style tag start
var script = {
id: 'chatbotStyle',
type: 'text/css',
style: document.createElement('style'),
content: '@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,600"); ::-webkit-scrollbar{width:10px}::-webkit-scrollbar-track{background:#f1f1f1}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#555} #chatbot{ font-family: Roboto, sans-serif; font-weight: 400; position: fixed; right: 10px; bottom: 20px; text-align: center; } #chatbot img{ margin: auto; } #chatbot small{ display: block; font-size: 9px; } .initLaunchPad{ cursor: pointer; text-align: center; transition: .4s all; } .botHeader{min-height:40px;background:#ededed;border-radius:6px 6px 0 0;display:flex;justify-content:space-between;align-items:center;padding:4px} .botHeader img{float:left;width:32px;height:32px; border-radius:50%;background-color:#f9f9f9}.botHeader h5{float:left;padding:0!important;margin:4px 0 3px 6px} .botHeader h5 small{font-size: 12px;} .botHeader > div{width:40%;display:flex;align-items:center;text-align:left;height:32px} #botClose{cursor:pointer;font-size:12px;font-weight:600;padding:6px 8px;border-radius:4px;background-color:#e22e2c;color:#fff; } #botContainer{ display: none; border-radius: 8px; border:1px solid #cbcbcb; transition:.4s all; width: 310px; height: 420px; max-width:420px; max-height: 420px; margin-bottom: 4px; } #botBody{ height: 366px; } ',
append: function () {
this.style.type = this.type;
this.style.appendChild(document.createTextNode(this.content));
document.head.appendChild(this.style);
}
}; script.append();
//style tag end
//initLaunchPad started
var initLaunchPad = document.createElement('div');
initLaunchPad.classList.add('initLaunchPad');
var initBotIcon = document.createElement('img');
initBotIcon.src = "https://farm5.staticflickr.com/4876/39891228293_13c532f352_o.gif";
initBotIcon.width = 86;
initLaunchPad.appendChild(initBotIcon);
chatbot.appendChild(initLaunchPad);
$(function getSpeechAuthToken() {
var params = {
// Request parameters
};
$.ajax({
url: "https://brazilsouth.api.cognitive.microsoft.com/sts/v1.0/issuetoken" + $.param(params),
beforeSend: function (xhrObj) {
// Request headers
xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", "XXXXXX");
},
type: "POST",
// Request body
data: "{body}",
})
.done(function (data) {
speechAuthToken = data;
})
.fail(function () {
alert("error");
});
});
initBotIcon.addEventListener('click', function (e) {
e.preventDefault();
initLaunchPad.style.display = "none";
botContainer.style.display = "block";
/*Call bot API*/
(async function () {
const searchParams = new URLSearchParams(window.location.search);
//Speech Service Token Generation
const subscriptionKey = 'XXXXXX';
const region = 'brazilsouth';
let webSpeechPonyfillFactory;
//Speech Service Setting
webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({
credentials: {
authorizationToken: speechAuthToken,
region: region
}
});
//Direct-Line BotToken Generation
const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', { method: 'POST', headers: { Authorization: 'Bearer ' + 'XXXXXX' } });
const { token } = await res.json();
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
userID: "User",
styleOptions,
locale: 'pt-BR',
selectVoice: (voices, activity) =>
activity.locale === 'pt-BR'
? voices.find(({ name }) => /Daniel/iu.test(name))
: voices.find(({ name }) => /Daniel/iu.test(name)) ||
voices.find(({ name }) => /Daniel/iu.test(name)),
webSpeechPonyfillFactory
}, document.getElementById('botBody'));
document.querySelector('#botBody > *').focus();
var mainTag = chatbot.getElementsByClassName('main')[0];
mainTag.style.borderTop = chatbot.dataset.cbBorder;
})().catch(err => console.error(err));
//Bot Message Exchange Setting
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'userName',
value: Paul
}
});
}
return next(action);
});
//initLaunchPad ended
var botContainer = document.createElement('div');
botContainer.id = "botContainer";
var botHeader = document.createElement('div');
botHeader.classList.add('botHeader');
var botClose = document.createElement('a');
botClose.id = "botClose";
botClose.innerText = "X";
botClose.title = "Close";
botHeader.innerHTML = "<div><img src='" + chatbot.dataset.cbBotAvatarImage + "'/> <h5>Bit_Bot <small>FTD Educação</small> </div></h5>";
botHeader.appendChild(botClose);
botClose.addEventListener('click', function (e) {
e.preventDefault();
initLaunchPad.style.display = "";
botContainer.style.display = "none";
return false;
});
botContainer.appendChild(botHeader);
var botBody = document.createElement('div');
botBody.id = "botBody";
botContainer.appendChild(botBody);
var poweredBy = document.createElement('div');
poweredBy.innerHTML = "<small>Powered by <img src='http://ainetw.com/img/icon.png' width=12 /> AI Networks</small>";
chatbot.appendChild(botContainer);
chatbot.appendChild(poweredBy);
</script>
</body>
</html>
我未能成功捕捉到代码以下部分的错误。
1) 在语音服务设置中。
//Speech Service Setting
try {
webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({
credentials: {
authorizationToken: speechAuthToken,
region: region
}
});
} catch (error) {
console.log('Error Speech Service');
console.log(error);
//Refreshes token
getSpeechAuthToken();
webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({
credentials: {
authorizationToken: speechAuthToken,
region: region
}
});
}
2) 在网络聊天设置中。
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
//userID: "Randomly Generated by the Webchat",
styleOptions,
locale: 'pt-BR',
selectVoice: (voices, activity) =>
activity.locale === 'pt-BR'
? voices.find(({ name }) => /Daniel/iu.test(name))
: voices.find(({ name }) => /Daniel/iu.test(name)) ||
voices.find(({ name }) => /Daniel/iu.test(name)),
webSpeechPonyfillFactory
}, document.getElementById('botBody'));
document.querySelector('#botBody > *').focus();
var mainTag = chatbot.getElementsByClassName('main')[0];
mainTag.style.borderTop = chatbot.dataset.cbBorder;
})().catch(err =>
{
console.log('Error Webchat');
console.log(error);
//Refreshes token
getSpeechAuthToken();
if (err && err.status === 401) {
speechAuthToken = speechAuthToken();
webSpeechPonyfillFactory = window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({
credentials: {
authorizationToken: speechAuthToken,
region: region
}
});
}
}
);
我们如何避免令牌过期问题?
谢谢
解决方案
从认知服务语音服务文档,
每个访问令牌的有效期为 10 分钟。您可以随时获取新令牌,但是,为了最大程度地减少网络流量和延迟,我们建议您使用相同的令牌九分钟。
在我的设置中,我在本地运行一个“令牌”服务器(也可以部署),运行 Restify,我从中进行各种令牌生成 API 服务调用。在 Restify 令牌服务器中,我设置了自己的 API,可以从网络聊天中调用。当我调用我的令牌服务器 API 时,会调用服务 API,获取令牌并将其返回给网络聊天。
这样,我一次解决了两个问题。一,我的服务的秘密在服务器端是安全的,二,我可以设置一个setInterval()
在令牌过期之前在网络聊天中调用有问题的 API。
令牌服务器.js
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');
const corsMiddleware = require('restify-cors-middleware');
const cors = corsMiddleware({
origins: ['*']
});
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create HTTP server.
const server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
extended: false
}));
server.post('/speechservices/token', async (req, res) => {
const options = {
// method: 'POST',
uri: `https://${ process.env.SPEECH_SERVICES_REGION }.api.cognitive.microsoft.com/sts/v1.0/issueToken`,
headers: {
'Ocp-Apim-Subscription-Key': process.env.SPEECH_SERVICES_SUBSCRIPTION_KEY
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
body = { region: process.env.SPEECH_SERVICES_REGION, authorizationToken: body };
res.send({
authorizationToken: body.authorizationToken,
region: body.region
});
console.log(`Someone requested a speech token...(${ response.statusCode })`);
} else if (response.statusCode >= 400 && response.statusCode < 500) {
res.send({ statusCode: response.statusCode, error: "CS's speech token API returned error" });
} else if (response.statusCode >= 500) {
res.status(response.statusCode);
res.send({ statusCode: response.statusCode, error: "CS's speech token API couldn't be reached" });
}
});
});
网络聊天- 我在服务端设置区域值,然后简单地将其与令牌一起返回。
setInterval(() => {
const fetchPromise = fetch(
`http://localhost:3500/speechservices/token`, { method: 'POST' } );
return fetchPromise.then(async res => {
if ( res.status === 200 ) {
const { authorizationToken, region, error: error } = await res.json();
credentials['authorizationToken'] = authorizationToken;
credentials['region'] = region;
return credentials;
} else {
console.log( `Speech Services: ${ error }` )
}
});
}, 540000); // Is set to 9 minutes
希望有帮助!
推荐阅读
- jquery - 如何使用 Jquery 获取 HTML 标签的完整定义?
- java - 如何正确构建 REST-API 端点
- node.js - 开玩笑绝对进口
- c++ - 为什么tellp() 是非常量?
- c - 如何根据用户的输入/选择打印和编辑数组列表中的特定元素?
- php - 使用 Laravel Valet 为非 Laravel 项目提供服务时,如何访问公共目录中的 PHP 文件?
- c# - 修复 C# 中的“System.Collections.Generic.List`1[Event_Manager.Form3+MyClass]”
- java - Scene Builder 未在 IntelliJ Idea 社区版中打开
- javascript - React Native Button onPress 不起作用
- android - 如何在 webView android 中修复 net::ERR_INVALID_RESPONSE