Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to send Welcome Message in Web Chat #2120

Closed
PurnaChandraPanda opened this issue Jun 25, 2019 · 22 comments
Closed

How to send Welcome Message in Web Chat #2120

PurnaChandraPanda opened this issue Jun 25, 2019 · 22 comments
Assignees
Labels
Bot Services Required for internal Azure reporting. Do not delete. Do not change color. customer-replied-to Required for internal reporting. Do not delete. customer-reported Required for internal Azure reporting. Do not delete. question Further information is requested. Stack Overflow candidate

Comments

@PurnaChandraPanda
Copy link

Flow

ASP.NET Core MVC View [loads webchat] -> Controller gets token generated -> Bot Channels Registration -> Bot service

Though followed backchannel sample, it does not get the welcome message. The same backchannel works for simple html page, but not Razor based view page.

Any thoughts what could be missing here?

View page (token is generated on server side)

@model EchoAuthBot.ClientApp.ChatConfig;

@{
    ViewData["Title"] = "Index";
}

<!DOCTYPE html>
<html>
<body>
    <h1>Index</h1>

    <div id="webchat" role="main"></div>
    @*<script src="https://cdn.botframework.com/botframework-webchat/master/webchat-es5.js"></script>*@
    <script src="https://cdn.botframework.com/botframework-webchat/master/webchat.js"></script>
    <script>
        // Get welcome message
        // We are using a customized store to add hooks to connect event
        const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
            //console.log(action);
            if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
                dispatch({
                    type: 'WEB_CHAT/SEND_EVENT',
                    payload: {
                        name: 'webchat/join',
                        value: { language: window.navigator.language }
                    }
                });
            }

            return next(action);
        });

        // Set the StyleOptions for avatar
        const styleOptions = {
            botAvatarInitials: 'WC',
            userAvatarInitials: 'WW'
        };

        // Render the webchat control
        window.WebChat.renderWebChat({
            directLine: window.WebChat.createDirectLine({ token: `@Model.Token.ToString()` }),
            store,
            userID: `@Model.UserId.ToString()`,
            username: 'Web Chat User',
            locale: 'en-US',
            styleOptions
        }, document.getElementById('webchat'));
        document.querySelector('#webchat > *').focus();
    </script>
</body>
</html>
@PurnaChandraPanda PurnaChandraPanda added Pending question Further information is requested. Stack Overflow candidate labels Jun 25, 2019
@tdurnford
Copy link
Contributor

tdurnford commented Jun 25, 2019

Everything with your Web Chat configuration looks fine. Do you have an onEventAsync handler set up on your bot?

protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
{
    if (turnContext.Activity.Name == "webchat/join") {
      await turnContext.SendActivityAsync("Got webchat/join event.");
    }
}

@PurnaChandraPanda
Copy link
Author

@tdurnford - I do not have that kind of logic written on bot service side. I assume that kind of API needs to be added on bot service side. Is it correct? Without that OnEventActivityAsync API, how does the same flow work with a plain html page, but not ASP.NET Core MVC based view page? Any comments?

@tdurnford
Copy link
Contributor

tdurnford commented Jun 25, 2019

Correct, you would need to handle event activities on the bot server side. Are you just running the the Backchannel Welcome Event sample? If so, that sample is connected to MockBot which handles the welcome event.

@PurnaChandraPanda
Copy link
Author

On my service end, I have OnMembersAddedAsync API that handles the welcome message part. This is actually triggered for plain vanilla html to bot service welcome message. However, the same is not triggered for a View page. Is it an anomaly?

@tdurnford
Copy link
Contributor

Could you add your code for the OnMembersAddedAsync handler? It seems like an abnormality since the conversation update for the user is not triggered until the user messages the bot.

@PurnaChandraPanda
Copy link
Author

Here is how the method looks like:

  protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in turnContext.Activity.MembersAdded)
            {
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to AuthenticationBot. Type anything to get logged in. Type 'logout' to sign-out."), cancellationToken);
                }
            }
        }

@tdurnford
Copy link
Contributor

That looks fine. Typically, the channel sends two conversation update events when the conversation is initialized - one for the bot and another for the user. The second one - the event for the user - is intended to trigger the welcome message.

Unlike some other channels, Web Chat waits to send the second conversation update event until after the user messages the bot. Evidently, the welcome message won't be sent until after the first message. To workaround this issue, developers can send a back channel welcome event event to the bot when the DirectLine connection is established and send a welcome message from the onEventAsync handler.

In the case you mentioned above, is the bot sending another welcome message after the user messages the bot? The first conversation update might be unintentionally triggering the welcome message.

@tdurnford
Copy link
Contributor

There appears to be some discrepancy in how conversation update events are sent depending on which secret you are using. If you are using a DirectLine Secret, the conversation update is only being sent when the bot joins the conversation. However, if you are using a Web Chat secret, it sends both of the conversation update events. Can you check that you are using a Web Chat secret for both the simple html page and the Razor based view page?

@tdurnford
Copy link
Contributor

tdurnford commented Jun 25, 2019

So two questions. Are you using DirectLine enhanced authentication and are you adding the user id to either of your post requests when you exchange the secret for a token? If so, try removing the body from the request temporarily. We believe adding the user id to the request might be causing an issue with the conversation updates. Removing the user id is to test our hypothesis - not a solution.

@PurnaChandraPanda
Copy link
Author

So two questions. Are you using DirectLine enhanced authentication and are you adding the user id to either of your post requests when you exchange the secret for a token? If so, try removing the body from the request temporarily. We believe adding the user id to the request might be causing an issue with the conversation updates. Removing the user id is to test our hypothesis - not a solution.

Yes, on my Bot Channels Registration page, enhanced authentication is ON with trusted origins test - for ngrok URI, and directline.botframework.com. Added and removed the userID - however, there's no impact! It seems "OnMembersAddedAsync" API is not being hit .. so, not getting the welcome message.

What could be next course on this then?

@PurnaChandraPanda
Copy link
Author

There appears to be some discrepancy in how conversation update events are sent depending on which secret you are using. If you are using a DirectLine Secret, the conversation update is only being sent when the bot joins the conversation. However, if you are using a Web Chat secret, it sends both of the conversation update events. Can you check that you are using a Web Chat secret for both the simple html page and the Razor based view page?

For simple html page, using "webchat" secret key. However, for view page, using "DirectLine" secret key.

@PurnaChandraPanda
Copy link
Author

That looks fine. Typically, the channel sends two conversation update events when the conversation is initialized - one for the bot and another for the user. The second one - the event for the user - is intended to trigger the welcome message.

Unlike some other channels, Web Chat waits to send the second conversation update event until after the user messages the bot. Evidently, the welcome message won't be sent until after the first message. To workaround this issue, developers can send a back channel welcome event event to the bot when the DirectLine connection is established and send a welcome message from the onEventAsync handler.

In the case you mentioned above, is the bot sending another welcome message after the user messages the bot? The first conversation update might be unintentionally triggering the welcome message.

For view page case, not able to hit OnMembersAddedAsync API, but can hit from a simple html page.

@tdurnford
Copy link
Contributor

Can you disable Enhanced Authentication in Azure - it May take up to 15 minutes for the change to take effect - remove the user id from the body of the request, request the token using a DirectLine secret, and see if onMembersAddedAsync is being triggered after you message the bot? In a perfect world, you should see the welcome message sent after the initial message.

We believe the issue is surrounding the user id being added to the token, and our development team is working to resolve the issue.

If you could provide your bot id, bot handle, and a conversation id for a conversation where the welcome message is not being triggered that would be helpful.

@PurnaChandraPanda
Copy link
Author

Can you disable Enhanced Authentication in Azure - it May take up to 15 minutes for the change to take effect - remove the user id from the body of the request, request the token using a DirectLine secret, and see if onMembersAddedAsync is being triggered after you message the bot? In a perfect world, you should see the welcome message sent after the initial message.

We believe the issue is surrounding the user id being added to the token, and our development team is working to resolve the issue.

If you could provide your bot id, bot handle, and a conversation id for a conversation where the welcome message is not being triggered that would be helpful.

Disabled enhanced auth in DirectLine - helped me get the welcome message, i.e. OnMembersAddedAsync triggered. Got it triggered with userid and without userid. It is not userid related. It is definitely something else - you could quickly create a test flow and observe the same behavior for sure.

bot id: channelsregwithaad (it's Bot Channels Registration)
OnMembersAddedAsync is triggered from View page, and "conversationId": "1TsBAIx1zpwIfKuT4tbBwM-k".
OnMembersAddedAsync is triggered from View page, and "conversationId": "HmIO7PSmlom8GOnLJejjWU-k". (i.e. enhanced auth added)

@tdurnford
Copy link
Contributor

Were you using the a Web Chat or a DirectLine secret? In this case, it does make a difference.

@PurnaChandraPanda
Copy link
Author

Were you using the a Web Chat or a DirectLine secret? In this case, it does make a difference.

It's DirectLine secret that being used in controller, and generated token passed to view page.

@Kaiqb Kaiqb added the Customer label Jul 23, 2019
@tdurnford
Copy link
Contributor

tdurnford commented Jul 29, 2019

Here is how Welcome Messages in Web Chat should be handled with the updates to the Direct Line Connector Service.

Web Chat Welcome Messages

Channels generally send two conversation update events when the conversation is initialized - the first for the bot and another for the user. The second conversation update - the event for the user - is intended to trigger the welcome message. At the moment, Web Chat has two different welcome message scenarios that are slightly different from other channels and based on how the developer generates the Direct Line token.

Tokens with User IDs

The first scenario is dependent on the token request including a user ID. If the developer includes a user ID when generating the token, Direct Line will only send one conversation update event to the bot that includes two user IDs in the activity's membersAdded property - one for the bot and one for the user. Following this configuration should trigger the traditional welcome message in the onMembersAdded handler before the user messages the bot.

In the example below, the user ID is added to the token request and the welcome message is sent from the onMembersAdded handler.

Web Chat

(async function () {
  // Note, for the simplicity of this example, we are generating the Direct Line token on client side;
  // however, this is not a recommended practice and you should create and manage your tokens from the server. 
  // You should never put the Direct Line secret in the browser or client app.
  // https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication
  const secret = '<DIRECT_LINE_SECRET> | <WEB_CHAT_SECRET>';
  const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', { 
    body: JSON.stringify({ user: { id: 'dl_user_id', name: 'username' }}),
    headers: {
      Authorization: `Bearer ${secret}`,
      'Content-type': 'application/json'
    },
    method: 'POST',
  });
  const { token } = await res.json();

  window.WebChat.renderWebChat({
    directLine: window.WebChat.createDirectLine({ token })
  }, document.getElementById('webchat'));

  document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));

Bot Framework SDK v4 (Node.js)

this.onMembersAdded(async (context, next) => {
  const { membersAdded } = context.activity;

  for (let member of membersAdded) {
    if (member.id !== context.activity.recipient.id) {
      await context.sendActivity("Welcome Message from `onMembersAdded` handler!");
    }
  }
  await next();
});

Tokens, User IDs, and iFrames

To achieve this in an iFrame of Web Chat, retrieve your token with user ID as described above and pass the token within the src attribute of the iFrame:
<iframe src='https://webchat.botframework.com/embed/YOUR_BOT_HERE?t=YOUR_TOKEN_HERE' style='min-width: 400px; width: 100%; min-height: 500px;'></iframe>

Secrets and Tokens without User IDs

Alternatively, conversations created with tokens that do not include a user ID send two conversation update events. Direct Line sends the first conversation update - the one for the bot - when the connection with the bot is established. Direct Line sends the second conversation update for the user after they send their first message.

Generally, users anticipate the bot to send a welcome message before they send a message. To do this, you can dispatch a backchannel welcome event from Web Chat's store middleware when the Direct Line connection is established. Then in the onEvent handler, you can send a welcome message. Note, in the onMembersAdded handler you should check which channel is sending the event before sending the welcome message. If the channel id is "webchat" or "directline" you should not send the traditional welcome message to avoid sending multiple welcome messages.

Web Chat

(async function () {
  // Note, for the simplicity of this example, we are generating the Direct Line token on client side;
  // however, this is not a recommended practice and you should create and manage your tokens from the server. 
  // You should never put the Direct Line secret in the browser or client app.
  // https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication
  const secret = '<DIRECT_LINE_SECRET> | <WEB_CHAT_SECRET>';
  const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', { 
    headers: {
      Authorization: `Bearer ${secret}`,
    },
    method: 'POST'
  });
  const { token } = await res.json();

  const store = window.WebChat.createStore(
    {},
    ({ dispatch }) => next => action => {
      if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
        dispatch({
          type: 'WEB_CHAT/SEND_EVENT',
          payload: {
            name: 'webchat/join',
          }
        });
      }
      return next(action);
    }
  );

  window.WebChat.renderWebChat({
    directLine: window.WebChat.createDirectLine({ token }),
    store
  }, document.getElementById('webchat'));

  document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));

Bot Framework SDK v4 (Node.js)

this.onEvent(async (context, next) => {
  if (context.activity.name === 'webchat/join') {
    await context.sendActivity('Back Channel Welcome Message!');
  }
  await next();
});

this.onMembersAdded(async (context, next) => {
  const { channelId, membersAdded } = context.activity;

  if (channelId !== 'directline' && channelId !== 'webchat') {
    for (let member of membersAdded) {
      if (member.id !== context.activity.recipient.id) {
        await context.sendActivity("Welcome Message from `onMembersAdded` handler!");
      }
    }
  }
  await next();
});

For more details regarding backchannel welcome events in Web Chat, take a look at this sample.

Additional Context

@corinagum corinagum removed the Pending label Jul 29, 2019
@CoHealer CoHealer added Bot Services Required for internal Azure reporting. Do not delete. Do not change color. customer-replied-to Required for internal reporting. Do not delete. customer-reported Required for internal Azure reporting. Do not delete. labels Aug 19, 2019
@corinagum corinagum mentioned this issue Nov 14, 2019
37 tasks
@tdurnford tdurnford pinned this issue Nov 18, 2019
@tdurnford tdurnford changed the title Not able to fire welcome message from server side View page for WebChat control How to send Welcome Message in Web Chat Nov 19, 2019
@edreux
Copy link

edreux commented Feb 3, 2020

I'm trying to implement this to pass parameters when a chat session starts.

const store = window.WebChat.createStore(
{},
function() {
return function(next) {
return function(action) {
if (action.type === 'DIRECT_LINE/POST_ACTIVITY') { // tried various type such as DIRECT_LINE/CONNECT_FULFILLED
action = window.simpleUpdateIn(
action,
['payload', 'activity', 'channelData'],
() => ({
'email': "testemail@test.com",
'projectId': projectId,
})
)
}
return next(action);
}
}
}
);

The question is: How can I access this data from the first waterfall step of MainDialog ?

Right now, In DialogAndWelcomeBot, where OnMembersAddedAsync resides, I have managed to override OnEventAsync and I have access to the parameters but I'm unable to access them and use them from Maindialog.

protected override async Task OnEventAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
await base.OnEventAsync(turnContext, cancellationToken);
string channelObj = string.Empty;
Newtonsoft.Json.Linq.JObject channelData = null;
string email = string.Empty;
try
{
channelObj = turnContext.Activity.ChannelData.ToString();
}
catch (Exception exc)
{
string errorMsg = string.Empty;
errorMsg = "channelObj error in OnEventAsync: " + exc.Message;
LogMessage(turnContext, cancellationToken, errorMsg);
}

        try
        {
            channelData = Newtonsoft.Json.Linq.JObject.Parse(channelObj);
        }
        catch (Exception exc)
        {
            string errorMsg = string.Empty;
            errorMsg = "channelData error in OnEventAsync" + exc.Message;
            LogMessage(turnContext, cancellationToken, errorMsg);
        }
        try
        {
            email = channelData["email"].ToString();
        }
        catch (Exception exc)
        {
            string errorMsg = string.Empty;
            errorMsg = "email error in OnEventAsync: " + exc.Message;
            LogMessage(turnContext, cancellationToken, errorMsg);
        }

---> The following is an attempt to store in UserState but it doesn't work
IStatePropertyAccessor accessor = UserState.CreateProperty(nameof(OnboardingState));
OnboardingState state = await accessor.GetAsync(turnContext, () => new OnboardingState());
state.Ticket = new CIWTicket();
state.Ticket.SourceEmailAddress = email;
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
LogMessage(turnContext, cancellationToken, "email in OnEventAsync : " + email);
}

    private async void LogMessage(ITurnContext sc, CancellationToken cancellationToken, string message)
    {
        var msg = MessageFactory.Text(message, message, InputHints.IgnoringInput);
        await sc.SendActivityAsync(msg, cancellationToken);
    }

@tdurnford
Copy link
Contributor

@edreux
'How to' questions such as this one are better suited for Stack Overflow. Please feel free to post other questions you have about developing your own features over there so the community at large may help out. If you need help with a Web Chat implementation, you can post a question to the Web Chat tag. Thank you!

@edreux
Copy link

edreux commented Feb 3, 2020

ok, thanks.
Regarding botframework, I have never got any answer to any questions in stackoverflow, only "ticket" management wher months later I've been asked if I had still the problem and the project had been given up due to the impossibility to address the issue.
Same issue here. More than 50 hours of human work for nothing.

@edreux
Copy link

edreux commented Feb 3, 2020

@venkatx5
Copy link

I'm trying to implement this to pass parameters when a chat session starts.

const store = window.WebChat.createStore(
{},
function() {
return function(next) {
return function(action) {
if (action.type === 'DIRECT_LINE/POST_ACTIVITY') { // tried various type such as DIRECT_LINE/CONNECT_FULFILLED
action = window.simpleUpdateIn(
action,
['payload', 'activity', 'channelData'],
() => ({
'email': "testemail@test.com",
'projectId': projectId,
})
)
}
return next(action);
}
}
}
);

The question is: How can I access this data from the first waterfall step of MainDialog ?

Right now, In DialogAndWelcomeBot, where OnMembersAddedAsync resides, I have managed to override OnEventAsync and I have access to the parameters but I'm unable to access them and use them from Maindialog.

protected override async Task OnEventAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
await base.OnEventAsync(turnContext, cancellationToken);
string channelObj = string.Empty;
Newtonsoft.Json.Linq.JObject channelData = null;
string email = string.Empty;
try
{
channelObj = turnContext.Activity.ChannelData.ToString();
}
catch (Exception exc)
{
string errorMsg = string.Empty;
errorMsg = "channelObj error in OnEventAsync: " + exc.Message;
LogMessage(turnContext, cancellationToken, errorMsg);
}

        try
        {
            channelData = Newtonsoft.Json.Linq.JObject.Parse(channelObj);
        }
        catch (Exception exc)
        {
            string errorMsg = string.Empty;
            errorMsg = "channelData error in OnEventAsync" + exc.Message;
            LogMessage(turnContext, cancellationToken, errorMsg);
        }
        try
        {
            email = channelData["email"].ToString();
        }
        catch (Exception exc)
        {
            string errorMsg = string.Empty;
            errorMsg = "email error in OnEventAsync: " + exc.Message;
            LogMessage(turnContext, cancellationToken, errorMsg);
        }

---> The following is an attempt to store in UserState but it doesn't work
IStatePropertyAccessor accessor = UserState.CreateProperty(nameof(OnboardingState));
OnboardingState state = await accessor.GetAsync(turnContext, () => new OnboardingState());
state.Ticket = new CIWTicket();
state.Ticket.SourceEmailAddress = email;
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
LogMessage(turnContext, cancellationToken, "email in OnEventAsync : " + email);
}

    private async void LogMessage(ITurnContext sc, CancellationToken cancellationToken, string message)
    {
        var msg = MessageFactory.Text(message, message, InputHints.IgnoringInput);
        await sc.SendActivityAsync(msg, cancellationToken);
    }

Thanks a lot for the code. Saved my day!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bot Services Required for internal Azure reporting. Do not delete. Do not change color. customer-replied-to Required for internal reporting. Do not delete. customer-reported Required for internal Azure reporting. Do not delete. question Further information is requested. Stack Overflow candidate
Projects
None yet
Development

No branches or pull requests

7 participants