我浏览了botframework-webchat的文档,但找不到任何有关如何正确处理1小时以上对话的文档。如果网页在后台长时间闲置,则很可能发生这种情况。
只要网络聊天在网页上保持活动状态,便会保持直线连接。页面刷新后会发生此问题。
最初的短期解决方案是将相关对话信息存储在会话存储中,例如令牌。问题在于,会话的令牌每15分钟刷新一次。必须检索刷新的令牌,以便在刷新页面时保持对话。
我确信存在变通的解决方法,该事件使用事件回调从直接客户端对象中检索刷新的令牌。
理想上,我正在寻找一种干净的框架设计的方法来处理这种情况。
尽管有效的解决方案总比没有解决方案要好。
相关链接:https://github.com/microsoft/BotFramework-WebChat
谢谢。
您可以通过设置“令牌”服务器来实现。在下面的示例中,我在开发/测试bot时在本地运行。
您可以使用所需的任何软件包,但是我登陆了“ restify”,因为我将其包含在我的机器人的index.js
文件中。我只需创建一个与bot服务器分开的新服务器,并为其分配一个自己的端口即可。然后,当我运行机器人时,它也会自动运行。将您的appId,appPasswords和机密放入.env文件中。
然后,在托管机器人的网页中,只需调用端点即可获取令牌。您还将注意到,该代码检查令牌是否已经存在。如果是这样,那么它将设置一个带有刷新令牌的计时器的时间间隔。该间隔(设置为1500000 ms)设置为在令牌否则会到期之前运行(1800000 ms)。因此,令牌始终在刷新。 (刚出现在我脑海:可能会很聪明地记录剩余时间和经过的时间,如果用户离开了,以便将时间间隔设置为准确的数字,以便在需要的时候刷新。否则,该时间间隔将会重置,并且到期时间要少得多。)
此外,我还包括一些注释掉的代码。这是如果您希望对话在页面刷新或用户离开并返回后仍保持不变。这样,当前对话不会丢失,令牌仍然有效。根据您的需要,可能不是必需的,但可以与上面的方法很好地结合使用。
希望获得帮助!
令牌服务器
/**
* Creates token server
*/
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
const corsToken = corsMiddleware({
origins: [ '*' ]
});
// Create HTTP server.
let server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
extended: false
}));
server.listen(process.env.port || process.env.PORT || 3500, function() {
console.log(`\n${ server.name } listening to ${ server.url }.`);
});
// Listen for incoming requests.
server.post('/directline/token', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${ process.env.directLineSecret }`
},
json: {
user: {
ID: userId
}
}
};
request.post(options, (error, response, body) => {
// response.statusCode = 400;
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone requested a token...');
} else if (response.statusCode === 400) {
res.send(400);
} else {
res.status(500);
res.send('Call to retrieve token from DirectLine failed');
}
});
});
// Listen for incoming requests.
server.post('/directline/refresh', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/refresh',
headers: {
'Authorization': `Bearer ${ req.body.token }`,
'Content-Type': 'application/json'
},
json: {
user: {
ID: userId
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone refreshed a token...');
} else {
res.status(500);
res.send('Call to retrieve token from DirectLine failed');
}
});
});
webchat.html
<script>
(async function () {
let { token, conversationId } = sessionStorage;
[...]
if ( !token || errorCode === "TokenExpired" ) {
let res = await fetch( 'http://localhost:3500/directline/token', { method: 'POST' } );
const { token: directLineToken, conversationId, error } = await res.json();
// sessionStorage[ 'token' ] = directLineToken;
// sessionStorage[ 'conversationId' ] = conversationId;
token = directLineToken;
}
if (token) {
await setInterval(async () => {
let res = await fetch( 'http://localhost:3500/directline/refresh', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( { token: token } )
} );
const { token: directLineToken, conversationId } = await res.json();
// sessionStorage[ 'token' ] = directLineToken;
// sessionStorage[ 'conversationId' ] = conversationId;
token = directLineToken;
}, 1500000)
}
// if ( conversationId ) {
// let res = await fetch( `https://webchat.botframework.com/v3/directline/conversations/${ conversationId }`, {
// method: 'GET',
// headers: {
// 'Authorization': `Bearer ${ token }`,
// 'Content-Type': 'application/json'
// },
// } );
// const { conversationId: conversation_Id, error } = await res.json();
// if(error) {
// console.log(error.code)
// errorCode = error.code;
// }
// conversationId = conversation_Id;
// }
[...]
window.ReactDOM.render(
<ReactWebChat
directLine={ window.WebChat.createDirectLine({ token });
/>
),
document.getElementById( 'webchat' );
});
</script>
您可以通过在客户端实施cookie来实现。您可以将Cookie的过期时间设置为60分钟,并且可以使用水印使您的聊天持续一小时。Passing cookie to and from Bot Service。
该解决方案涉及将会话ID而不是令牌存储在会话存储中。页面刷新后,将检索到新令牌。
https://github.com/microsoft/BotFramework-WebChat/issues/2899
https://github.com/microsoft/BotFramework-WebChat/issues/2396#issuecomment-530931579
此解决方案有效,但不是最佳方案。更好的解决方案是在直接对象中检索活动令牌并将其存储在会话存储中。问题在于,目前尚不存在从直线对象中完全检索刷新令牌的方法。