Search code examples
c#botframeworkadaptive-cards

Botframework c# : reprompt user input within a stepcontext and pass the value into another class


I have actually two issues.

First : I would like to reprompt the user input within an case.intent (which is in a waterfall). I use var value = ((string)stepContext.Result); but it just store the input already given by the user instead of asking a new one.

Second : then i would like to pass the value into another class : cards.cs, to be then rendered in an adaptive card. I would like to use it instead of "Mickey Mouse" here in cards.cs

How can i store the variable from one class to another. I come from a python background, can I use a global variable in C#?

In maindialog.cs I wrote the following.

                case FlightBooking.Intent.order:

                    var modifiermessagetext = "What is your order";
                    var value = ((string)stepContext.Result);



                    var modifiermessage = MessageFactory.Text(modifiermessagetext, modifiermessagetext, InputHints.IgnoringInput);
                    var messageText70 = stepContext.Options?.ToString() ?? "you can contact the customer service at 098789876";
                    var promptMessage70 = MessageFactory.Text(messageText70, messageText70, InputHints.ExpectingInput);
                    await stepContext.Context.SendActivityAsync(modifiermessage, cancellationToken);
                    var attachments70 = new List<Attachment>();
                    var reply70 = MessageFactory.Attachment(attachments70);
                    reply70.Attachments.Add(Cards.CardJson());
                    await stepContext.Context.SendActivityAsync(reply70, cancellationToken);
                    return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage70 }, cancellationToken);

In Cards.cs:

        public static Attachment CardJson()
        {
            var templateJson = @"
                                            {
                                                ""type"": ""AdaptiveCard"",
                                                ""version"": ""1.0"",
                                                ""body"": [
                                                    {
                                                        ""type"": ""TextBlock"",
                                                        ""text"": ""Hello {name}""
                                                    }
                                                ]
                                            }";

        var dataJson = @"
                    {
                        ""name"": ""Mickey Mouse""
                    }";

        var transformer = new AdaptiveTransformer();
        var cardJson = transformer.Transform(templateJson, dataJson);
        var adaptiveCardAttachment = new Attachment()
        {
            ContentType = "application/vnd.microsoft.card.adaptive",
            Content = JsonConvert.DeserializeObject(cardJson),
        };
            return adaptiveCardAttachment;
        }

I am using the new feature of adaptive cards : https://learn.microsoft.com/en-us/adaptive-cards/templating/sdk

Is there a simple way to do this ? Any suggestion would be welcome. Thanks!


Solution

  • First let me explain how a waterfall dialog works

    According to Microsoft Docs

    A waterfall dialog is a specific implementation of a dialog that is commonly used to collect information from the user or guide the user through a series of tasks. Each step of the conversation is implemented as an asynchronous function that takes a waterfall step context (step) parameter. At each step, the bot prompts the user for input (or can begin a child dialog, but that it is often a prompt), waits for a response, and then passes the result to the next step. The result of the first function is passed as an argument into the next function, and so on.

    The following diagram shows a sequence of waterfall steps and the stack operations that take place.

    Image from Microsoft Docs

    That means when you prompt a dialog in a waterfall step, you can get that result in the NEXT step of the waterfall not within the same step as you have tried.

    • stepContext.Result: gets the result from the Previous waterfall step
    • stepContext.Options: contains input information for the dialog. It gets any options the waterfall dialog was called with.
      For example lets say you did await context.BeginDialogAsync("yourWaterfallDialog", false);
      "false" is the Options that can be fetched from your waterfall step like this
      if (sc.Options != null && sc.Options is bool) { valid = (bool)sc.Options; }
    • stepContext.Values: contains information you can add to the context, and is carried forward into subsequent steps. Its a dictionary of values which will be persisted across all waterfall steps Example: stepContext.Values["name"] = "Marc"

    Take a look at this example:

        private async Task<DialogTurnResult> StepOne(WaterfallStepContext stepContext, CancellationToken cancellationToken)
             {
                return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions Prompt = promptMessage70 }, cancellationToken);
             }
    
    
    
        private async Task<DialogTurnResult> StepTwo(WaterfallStepContext stepContext, CancellationToken cancellationToken)
            {
              // Get the user response from StepOne: 
              var value = ((string)stepContext.Result);
              // we pass the value to CardJson
              reply70.Attachments.Add(Cards.CardJson(value));
              await stepContext.Context.SendActivityAsync(reply70,cancellationToken);
            }
    

    As you can see, in StepTwo we fetched the result of the user input from StepOne.
    To get the Value fetched instead of "Mickey Mouse" in your adaptive card you can simply pass it as a string parameter like so Cards.CardJson(value)

    public static Attachment CardJson(string value){
    ...
                var dataJson = @"{'name': '" + value + "'}";
    ...
    }