Display Welcome Message in v4 Bot Framework Bot (C

2020-04-17 05:52发布

问题:

I had created a bot with v3 (C#) SDK and the welcome message used to work just fine without any sweat. And it still does for me in production. The code is handled in HandleSystemMessage like this -

.. v3 Code additional code removed for clarity...

else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels

//Code to show Welcome Message
if (message.MembersAdded.Any(o => o.Id == message.Recipient.Id))
{
var reply = message.CreateReply();
reply.Attachments = new List<Attachment>();
// Create the attachment.
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = AdaptiveCardHelper.GetOptionsCard()
};
reply.Attachments.Add(attachment);
ConnectorClient connector = new ConnectorClient(new Uri(message.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(reply);
}
}

The Web Chat version that I use is BotFramework-WebChat-0.11.4, I have done certain customizations in it to implement facebook Like/Unlike feature with comment.

Now I am migrating the bots to v4 SDK (C# + .Net Core Web App), and I am intending to use the same old version of webchat. But I am struggling for two days to get a welcome message displayed in the same web chat while it works well on emulator (given to that two ConversationUpdate) events.

I have tried sending a message as well as an event using the solution provided in this article and tried to catch that in Bot on different async methods OnEventAsync, OnEventActivityAsync, OnMessageActivityAsync.

https://blog.botframework.com/2018/07/12/how-to-properly-send-a-greeting-message-and-common-issues-from-customers/

V4 Code looks like below:

 protected override async Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            if (turnContext.Activity.MembersAdded != null)
            {
                if (turnContext.Activity.MembersAdded.Any(m => m.Id != turnContext.Activity.Recipient?.Id))
                {
                    //var welcomeCard = CreateAdaptiveCardAttachment();
                    //var response = CreateResponse(turnContext.Activity, welcomeCard);
                    //await turnContext.SendActivityAsync(response, cancellationToken);

                    await Utility.LogTraceAsync("Inside OnConversationUpdateActivityAsync");

                        var eventActivity = turnContext.Activity.AsConversationUpdateActivity();

                        ConnectorClient connector = new ConnectorClient(new Uri(eventActivity.ServiceUrl), Configuration.MicrosoftAppId, Configuration.MicrosoftAppPassword);

                        await Utility.LogTraceAsync("Service URL OnConversationUpdateActivityAsync" + eventActivity.ServiceUrl);

                        await Utility.LogTraceAsync("Recipient ID OnConversationUpdateActivityAsync" + turnContext.Activity.Recipient?.Id);

                        var welcomeCard = CreateAdaptiveCardAttachment();

                        var reply = ((Activity)eventActivity).CreateReply();
                        reply.Attachments.Add(welcomeCard);

                    //var response = CreateResponse(turnContext.Activity, welcomeCard);
                    await connector.Conversations.ReplyToActivityAsync(reply, cancellationToken);// turnContext.SendActivityAsync(response, cancellationToken);

                        await Utility.LogTraceAsync("OnConversationUpdateActivityAsync Response Returned.");

                    await Utility.LogTraceAsync("Exit OnConversationUpdateActivityAsync");
                }
            }
        }

        protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
        {
            await Utility.LogTraceAsync("Inside OnEventActivityAsync");
            if (turnContext.Activity.Type == ActivityTypes.Event)
            {
                var eventActivity = turnContext.Activity.AsEventActivity();

                await Utility.LogTraceAsync("Event Activity from WebChat matched.");

                ConnectorClient connector = new ConnectorClient(new Uri(eventActivity.ServiceUrl), Configuration.MicrosoftAppId, Configuration.MicrosoftAppPassword);

                await Utility.LogTraceAsync("Service URL " + eventActivity.ServiceUrl);

                var welcomeCard = CreateAdaptiveCardAttachment();

                var reply = ((Activity)eventActivity).CreateReply();
                reply.Attachments.Add(welcomeCard);

                var members = await connector.Conversations.GetConversationMembersAsync(eventActivity.Conversation.Id.ToString());
                var membernames = "";
                foreach (var member in members) {
                    membernames += member.Name + ",";
                }

                await Utility.LogTraceAsync(membernames);

                await connector.Conversations.SendToConversationAsync(reply, cancellationToken);

                await connector.Conversations.ReplyToActivityAsync(reply, cancellationToken);// turnContext.SendActivityAsync(response, cancellationToken);

                await Utility.LogTraceAsync("Event Response Returned.");
            }

            await Utility.LogTraceAsync("Exit OnEventActivityAsync");
        }

But it does not seem to work at all. I am pulling my hairs out and there is no clue on how to do for .Net Core App. I will be glad to know if someone has solved this problem.

Update - I have used the JS code on client-side as provided by @tdurnford and at Bot Side had following two methods -

//Required to Show Welcome Message on Emulator
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in membersAdded ?? Array.Empty<ChannelAccount>())
            {
                // Greet anyone that was not the target (recipient) of this message.
                // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    Activity reply = ((Activity)turnContext.Activity).CreateReply();
                    AdaptiveCard card = AdaptiveCardHelper.GetWelcomeCard();
                    Attachment attachment = new Attachment()
                    {
                        ContentType = AdaptiveCard.ContentType,
                        Content = card
                    };
                    reply.Attachments.Add(attachment);
                    await turnContext.SendActivityAsync(reply, cancellationToken);
                }
            }
        }

//Required to Show Welcome Message on Web Chat
        protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
        {
            if (turnContext.Activity.Name == "webchat/join")
            {
                Activity reply = ((Activity)turnContext.Activity).CreateReply();
                AdaptiveCard card = AdaptiveCardHelper.GetWelcomeCard();
                Attachment attachment = new Attachment()
                {
                    ContentType = AdaptiveCard.ContentType,
                    Content = card
                };
                reply.Attachments.Add(attachment);
                await turnContext.SendActivityAsync(reply, cancellationToken);
            }
        }

With both methods two welcome messages are shown in the chat window -

Bot Window with two Welcome Messages

Then I've commented the OnEventActivityAsync method in C# and deployed again. Now it shows only one welcome message returned from OnMembersAddedAsync as shown in the window.

Bot Window with only one Welcome Message

If I comment the following code lines in the webchat code i.e. don't send the post-activity -

botConnection.postActivity({
    from: {
        id: 'myUserId',
        name: 'myUserName'
    },
    type: 'event',
    name: 'webchat/join',
    value: {
        locale: 'en-US'
    }
}).subscribe(
    id => console.log("Posted welcome event, assigned ID ", id),
    error => console.log("Error posting activity", error)
);

In this case, no welcome message is displayed. @tdurnford, please check if you are able to replicate this behavior.

Although there is another problem here in this way that when a user types a question in the bot, then a welcome message is displayed again. Bot window with two welcome messages one on load and another after the first question

回答1:

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 to the bot when the DirectLine connection is established and send a welcome message from the onEventAsync handler instead of onMembersAdded. For more details, take a look at the code snippets below.

Bot Chat Code

<!DOCTYPE html>
<html>
  <head>
    <link href="https://cdn.botframework.com/botframework-webchat/0.11.4/botchat.css" rel="stylesheet" />
    <style>
      #webchat {
        height: 100%;
        width: 100%;
      }
    </style>

  </head>
  <body>
    <div style="display: flex">
      <div style="position: relative; height: 500px; width: 500px"><div id="bot" ></div></div>
    </div>


    <script src="https://cdn.botframework.com/botframework-webchat/0.11.4/botchat.js"></script>

    <script>

      (async function() {

        const res = await fetch('/directline/token', { method: 'POST' });
        const { token }  = await res.json();

        var userinfo = {
              id: 'user-id',
              name: 'user name',
              locale: 'es'
          };

        var botConnection = new window.BotChat.DirectLine({ token });

        botConnection.connectionStatus$
          .subscribe(connectionStatus => {
              switch(connectionStatus) {
                  case window.BotChat.ConnectionStatus.Online:
                    botConnection.postActivity({
                        from: { id: 'myUserId', name: 'myUserName' },
                        type: 'event',
                        name: 'webchat/join',
                        value: { locale: 'en-US' }
                    }).subscribe(
                        id => console.log("Posted welcome event, assigned ID ", id),
                        error => console.log("Error posting activity", error)
                    );
                    break;
              }
          });


        BotChat.App({
          botConnection: botConnection,
          user: userinfo,
          bot: { id: 'botid' },
          resize: 'detect'
        }, document.getElementById("bot"));

      })().catch(err => console.log(err));

    </script>
  </body>
</html>

Bot Code - C#

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

protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
    if (turnContext.Activity.ChannelId != "webchat" && turnContext.Activity.ChannelId != "directline") {

        foreach (var member in membersAdded)
        {
            if (member.Id != turnContext.Activity.Recipient.Id)
            {
                await turnContext.SendActivityAsync($"Hi there - {member.Name}. {WelcomeMessage}", cancellationToken: cancellationToken);
                await turnContext.SendActivityAsync(InfoMessage, cancellationToken: cancellationToken);
                await turnContext.SendActivityAsync(PatternMessage, cancellationToken: cancellationToken);
            }
        }
    }
}

Screen Capture

Also note, Web Chat v0.11.4 is referred to as either Bot Chat or Web Chat v3. Sorry I got tripped up on that.

Hope this helps!



回答2:

In v4 of the Bot Framework SDK this functionality is handled by making your bot class inherit from the ActivityHandler class then overriding the OnMembersAddedAsync method.

How this looks in practice is:

...

public class MyBot : ActivityHandler
{
    ...

        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in membersAdded)
            {
                // Greet anyone that was not the target (recipient) of this message.
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    var welcomeMessage = "Hello and welcome!";

                    await turnContext.SendActivityAsync(welcomeMessage, cancellationToken);
                }
            }
        }
    ...
}

There are examples of how to do this here, here, and here.

You may have to update the version of the Microsoft.Bot.Builder package that you are using.



回答3:

tdurnford's answer didn't work for me. I'm using a new v4 bot (no migration) and embedding iframe into Sharepoint. OnEventActivityAsync just doesn't trigger. I get a conversation update with my bot being a MembersAdded so I did this:

    protected override async Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        await base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);

        if (turnContext.Activity.ChannelId == "webchat" && turnContext.Activity.MembersAdded?.FirstOrDefault(m => m?.Name == Configuration["BotName"]) != null)
        {
            await SendWelcomeMessageAsync(turnContext, cancellationToken);
        }
    }

BotName is "Bot" for local development and the app name for azure deployed apps. SendWelcomeMessage is my own method. Also as above, I added a check in OnMembersAddedAsync:

protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        if (turnContext.Activity.ChannelId == "webchat" || turnContext.Activity.ChannelId == "directline")
            return;

        foreach (var member in membersAdded)
        {
            // Greet anyone that was not the target (recipient) of this message.
            // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
            if (member.Id != turnContext.Activity.Recipient.Id)
            {
                await SendWelcomeMessageAsync(turnContext, cancellationToken);
            }
        }
    }