Reputation: 165
I'm using Direct Line, MS Speech Service and the Webchat.js on bot running on Botframework V4.
I start the bot from a web page using webchat.js passing values to the back end app like that
https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/04.api/d.post-activity-event
Code used to instanciate the bot on web channel:
<!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
}
});
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'userGenre',
value: male
}
});
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'activityName',
value: chatBotMission
}
});
}
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>
Code snippet of the bot sent data collection in my bot:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
var studentProfile = await _studentProfileAccessor.GetAsync(turnContext, () => new StudentProfile(), cancellationToken);
DialogContext dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
//Bot Channel Message Exchange Collection
if (turnContext.Activity.Name == "userName")
{
studentProfile.Name = turnContext.Activity.Value.ToString();
studentName = turnContext.Activity.Value.ToString();
}
if (turnContext.Activity.Name == "userGenre")
{
studentProfile.Gender = turnContext.Activity.Value.ToString();
conversationSessionID = turnContext.Activity.From.Id;
if (conversationSessionID.ToLower() == "user")
{
var myConversationSessionID = turnContext.Activity.Id;
if (myConversationSessionID.IndexOf("|") != -1)
{
conversationSessionID = myConversationSessionID.Substring(0, myConversationSessionID.IndexOf("|"));
}
}
studentGender = turnContext.Activity.Value.ToString();
//Variables persistance on DB
SessionVariable SessionVariable = new SessionVariable();
SessionVariable.AddSessionVariable(BellaMain.GlobalVariable.SystemID, BellaMain.GlobalVariable.CustomerID, conversationSessionID, "StudentName", studentName, "StudentGender", studentGender, "", "", "", "", "", "", "", "", "", "", "", 0);
}
if (turnContext.Activity.Name == "activityName")
{
if (turnContext.Activity.Value.ToString().ToLower() =="atividade2")
{
var message = $"**Olá {studentName}, bem vindo ao Quant Bot!**";
await turnContext.SendActivityAsync(message);
await dc.BeginDialogAsync(Activity2MainDialog, "activity2MainDialog", cancellationToken);
}
else
{
var message = $"**Olá {studentName}, bem vindo ao Quant Bot!**";
await turnContext.SendActivityAsync(message);
}
}
The application works fine but when there is more than one user connected to it exchanged data gets mismatched many times e.g.
Webclient 1 sends user name: Paul and Gender: male ==> app receives Paul and male
Webclient 2 sends user name: Mary and Gender: female ==> app sometimes receives Paul and male !? It should receive Mary and female instead
I have a feeling the sent data is buffered on Azure or somewhere else and needs to be cleared/refreshed every time a new web app bot instance starts.
Does it make sense? How can it be fixed? Thx
Upvotes: 1
Views: 241
Reputation: 165
Thanks all for your support on that.
Sharing how we found a solution for the case.
First we removed the userID: "User"
entry.
So each bot session will have its own random ID.
Then we created just one 'website-to-bot' data exchange variable.
This will make sure all data will be sent in just one browser cycle.
It seems this was causing the issue in our app.
Code snippet as follows:
var enviromentContextData = 'studentName=&' + userName + '&studentGender=|' + userGenre + '|';
//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: 'enviromentContextDataPass',
value: enviromentContextData
}
});
}
return next(action);
});
Upvotes: 0
Reputation: 6393
First, in case you are actually doing this, don't pass your Direct Line authorization in your client. This is a major security issue and you open yourself up to being breached.
As for your issue, consider generating the unique userID
and passing it in the createDirectLine()
token call. In this way, the userID will be baked into the token and will not specifically be a function of the page. As the token should be unique to each user, this should help, if not solve, your user crossover issue.
If it doesn't, I would recommend removing the Direct Line direct API call from your Web Chat code. In my case, I have my own token generating server which I call from Web Chat. The token server makes the call to Direct Line when my server's API is called by Web Chat. I also generate the userID in the token server so it is baked into the token which is then returned to Web Chat for use.
If neither of these options fix your issue, then I would look closer at your external data collection, which you mentioned elsewhere.
Hope of help!
Upvotes: 1
Reputation: 3024
In your example, you are assigning 'User' to userID
, which is a static string.
Assigning userID as a static value is not recommended since this will cause all users to share state. Please see the API userID entry for more information. (source)
Remove the userID part from your snippet or change "User" to something dynamic. Otherwise the state will be shared, which means that user data is shared over all users accessing the bot.
userID: "User"
Upvotes: 0