2

Botframework V4 webchat

My bot displays a SigninCard to the user, which when clicked user is redirected to an external website where the user will enter login credentials. This external website will perform a service call to a separate endpoint in my bot controller with a token. Now I want to make the bot to display a message to the user, and let the user follow through the rest of the conversation.

For all intents and purposes, it needs to be as same as displaying a proactive message to the user, and jumping to the next waterfall step afterwards.

This is my code so far.

1st approach

Conversation reference information is passed from here.

        private async Task<DialogTurnResult> SigninStepStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var convRef = stepContext.Context.Activity.GetConversationReference();
            var stateCol = System.Web.HttpUtility.ParseQueryString(string.Empty);
            stateCol["userId"] = convRef.User.Id;
            stateCol["botId"] = convRef.Bot.Id;
            stateCol["conversationId"] = convRef.Conversation.Id;
            stateCol["serviceUrl"] = convRef.ServiceUrl;
            stateCol["channelId"] = convRef.ChannelId;

            SigninCard signinCard = new SigninCard
            {

                Text = "Please sign-in to continue",
                Buttons = new List<CardAction> {
                    new CardAction(ActionTypes.Signin, "Sign-in",
                        value: "http://some-external-url?state="+ System.Web.HttpUtility.UrlEncode(stateCol.ToString())) },
            };


            var reply = stepContext.Context.Activity.CreateReply();
            reply.Attachments = new List<Attachment>
                {
                    signinCard.ToAttachment(),
                };
            return await stepContext.PromptAsync(
                "testprompt",
                new PromptOptions
                {
                    Prompt = reply,
                },
                cancellationToken);
        }

Conversation reference information is received here. This endpoint is invoked by an external website.

        [HttpGet]
        public async Task<HttpResponseMessage> Callback(string state)
        {
            // Resume conversation
            var stateCol = System.Web.HttpUtility.ParseQueryString(state);
            
            ConnectorClient connector = new ConnectorClient(new Uri(stateCol["serviceUrl"]));
            IMessageActivity newMessage = Activity.CreateMessageActivity();
            newMessage.From = new ChannelAccount(stateCol["userId"]);
            newMessage.Conversation = new ConversationAccount(id: stateCol["conversationId"]);
            newMessage.Recipient = new ChannelAccount(stateCol["botId"]);
            newMessage.ChannelId = stateCol["channelId"];
            newMessage.Text = "hello user";
            newMessage.ReplyToId = stateCol["botId"];
            await connector.Conversations.SendToConversationAsync((Activity)newMessage);

            //...
        }

This piece of code correctly posts "Hello user" message to conversation flow, as a message coming from the bot. I tried connector.Conversations.ReplyTo method, also tried swapping botid and userid values to make it the user replying to the bot. But none of the cases made the waterfall step to jump to the next step in the flow like manually entering a reply text through emulator.

I followed the Idea 2 scenario of this question : https://stackoverflow.com/a/48977832 which is on botfraemwork v3. In botframework v3, it has a resume method as indicated in this answer. But I cannot find this method or anything that makes the conversation to resume, in botframework v4.

Conversation.ResumeAsync(reference, message);

Wondering if there is any special kind of activity that I can send, end the current prompt and make it start the next waterfall step.

2nd approach

On the side, I also tried to follow the ProactiveBot sample code in Botframework 4, to see if that approach will cause the waterfall flow to jump to the next step in the line.

        [HttpGet]
        public async Task<HttpResponseMessage> Callback(string code, string state, string session_state = null)
        {
            // Resume conversation
            var stateCol = System.Web.HttpUtility.ParseQueryString(state);
            
            MicrosoftAppCredentials.TrustServiceUrl(stateCol["serviceUrl"]);

            ConversationReference cr = new ConversationReference();
            cr.Bot = new ChannelAccount(stateCol["botId"]);
            cr.User = new ChannelAccount(stateCol["userId"]);
            cr.ChannelId = stateCol["channelId"];
            cr.Conversation = new ConversationAccount(id: stateCol["conversationId"]);
            cr.ServiceUrl = stateCol["serviceUrl"];

            var msg = cr.GetContinuationActivity();
            msg.Text = "login_succeed";

            await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, cr, BotCallback, default(CancellationToken));

            //..
        }

But at ContinueConversationAsync it threw below error, even though I am not using any clientSecret parameter anywhere. Not sure if this is because of using the Signin card to invoke the external web site url in the first code block above.

An unhandled exception occurred while processing the request. ArgumentNullException: Value cannot be null. (Parameter 'clientSecret') Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential..ctor(string clientId, string clientSecret)

chamzdee
  • 21
  • 3
  • I guess you need somethin called an ActivityPrompt. This question https://stackoverflow.com/questions/52514248/wait-for-even-type-activity-in-a-waterfallstep-dialog-bot-framework-4-0 might help you in the right direction – Hessel Nov 20 '20 at 08:33
  • Welcome to Stack Overflow. What is your question? (Since there are multiple other people in this thread, you will need to @ mention me if you want me to see your reply.) – Kyle Delaney Nov 20 '20 at 19:11
  • @KyleDelaney Thank you :) 1. Assume the bot is in mid waterfall dialog flow, waiting for user to reply on a prompt. 2. From a different action method in the bot controller class, I want to resume this dialog, and make it start on the next waterfall step without waiting on the user anymore. (At this point I have all the information to create a conversation reference or a message activity) 3. I tried programmatically sending a message to the bot, pausing as the user, but this did not make the bot to go to next waterfall step. (See the 'Callback' method code above) How do I achieve this? – chamzdee Nov 20 '20 at 19:50
  • You seem to be on the right track, but all you've told us is that an exception is being thrown outside of your code. I don't know where a ClientCredential would be constructed so you'll need to edit the stack trace into your question. Please format it inside a code block so that it's easy to read. – Kyle Delaney Nov 20 '20 at 20:01
  • @chamzdee - Are you still working on this? – Kyle Delaney Nov 24 '20 at 19:17
  • Hi @KyleDelaney Yes I am, thanks for asking. I have attempted two approaches. I am biased towards getting the first one to work. In first approach I do not get any exception. What I want to achieve is to send a message/activity to the bot programmatically, that will make the waterfall dialog to move to next step in the flow. (Assume the bot has already prompted for user input and is waiting for user to enter some text. Naturally once the user enter some text, bot will jump to the next waterfall step. But when I programmatically send a text pausing as the user, it won't go to next step. ) – chamzdee Nov 25 '20 at 13:17
  • @chamzdee - Why not just use an OAuth prompt? OAuth prompts are designed to do exactly what you're trying to do. – Kyle Delaney Nov 26 '20 at 19:33
  • @KyleDelaney My understanding with OAuth is that we need to use Azure AD and register bot for authentication. In my case I'm going to have to use an existing service ow our own, which will do nothing more than sending a token back (to whatever url we specify), after the user is authenticated. – chamzdee Nov 26 '20 at 20:23
  • @chamzdee - I may be misunderstanding, so I want to make sure I understand correctly. It sounds like you've read a document like [this](https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-authentication) which talks about how to implement an AAD OAuth connection in the Bot Framework. Did you take that to mean that AAD is the only supported OAuth provider, or are you aware that the Bot Framework supports a long list of OAuth providers including generic OAuth (which could possibly work for your own service)? – Kyle Delaney Nov 30 '20 at 23:06
  • @chamzdee - Do you still need help? – Kyle Delaney Dec 05 '20 at 00:25

0 Answers0