Store Locator Chatbot

Store Locator Chatbot

·

10 min read

In this article, I will show you how to create a chatbot using Power Virtual Agent (PVA) that can help you find store locations. We will assume that you already have a website with a store locator feature. The chatbot will provide a different way for users to search for store locations. Here are the steps we will be taking:

  1. Create a PVA chatbot that starts a conversation and prompts the user to provide a location for the search.

  2. Use PVA to call a Cloud Flow, which in turn calls another Desktop Flow.

  3. Use the Desktop Flow to perform web scraping using Robotic Processing Automation (RPA) on the existing website's store locator.

  4. Use the web scraping to search for the store location on the website.

  5. Return the search results to a Desktop Flow variable and then to the Cloud Flow.

  6. Pass the variables back to PVA as an Adaptive Card.

Here is the Youtube version of this tutorial.

Power Automate Desktop (PAD) - Web scraping

  1. Create a new PAD flow, and add an action to launch the Edge browser with the store locator URL. Do not select "Minimised", it won't work with UI element.

  2. Add a "Populate text field on web page" action to provide input to the search window. Create a new UI element and provide a default search text.

    Hover the mouse on the input field, use Ctrl+Left Click to create the UI Element

  3. Now we need to hit the "Search" button to perform the search. Add a new step called "Press button on web page". Create a new UI element on the button.

    Hover your mouse on the "Search" button this time

  4. In case the website loads slowly, we will add a waiting step. Add "Wait for web page content" action. We observed on the search result page, the "Showing branches" text appears new.

  5. Now we can perform web scraping on the search result page. Add a step to "Extract data from web page". Without clicking the "Save" button, move to the search result page.

  6. Hover your mouse onto the name, right-click and select the "Extract element value" and select "Text" field.

  7. Now extract the name data from the second item.

    By repeating the same data field on different items, PAD can extract the data automatically after learning the pattern.

  8. Now repeat this process on the address and distance. You will see the helper window populates all the extracted data in columns. Select "Finish" on the helper window and "Save" the step in action.

  9. Lastly, for this desktop flow to work with cloud flow, we will create an Input variable and an Output variable.

  10. Create an Input variable from the top right, and assign a default value.

  11. Edit the step "Polulate text field on web page", removed the text and replacc with the Input variable

  12. Create an Output variable.

  13. Add the last action to assign the flow variable value to the Output variable.

  14. You can save and close the PAD now.

  15. If you want to have a quick test, add a "Display message" action to try it. You can delete this step afterwards. Also delete the default value for "varPADIN_Query".


Power Automate Cloud Flow - Data Transforming

  1. Create an Instant Cloud Flow with Manual Trigger.

    Trigger will be replaced by Power Virtual Agent in the end

  2. Add a step to call PAD, select the "Desktop flow", use "Attended" mode and provide a default value for "varPADIN_Query".

  3. The Desktop Flow step will return the data to the Cloud Flow. But before it can be used, we need to initialise it. Ensure that the data type is set to "string" and select the "varPADOUT_Locations" under "Dynamic content." This new variable will be used to receive and store the data returned by the Desktop Flow.

  4. Now let's save and run a manual test to see what the result looks like so far. Click "Test" and perform a manual test.

  5. As you can see above, the returned data looks clean with newline breaks. Each line has the branch name, address and distance.

    The address usually consists of three parts: Stree, Suburb and Postcode

    Collins Square Docklands, 727 Collins St, Docklands, VIC 3008, 0.33 km

    221 William St Melbourne, 221 William St, Melbourne, VIC 3000, 1.08 km

    Notice there is also a space after each comma for the first 3 parts of the data

  6. For this demo, we will only take the first three lines of the data, which is already sorted by the shortest distance.

    We used the Compose action to slice the string returned from "Initialize variable" step, taking the top three substrings and convert them into data array.

    First, split the string stored in variable "BranchLocations" by newline breaks (%0A) ,
    Second, take the first substring by using [0]

    Third, split it again using comma plus space

    Location 1:

    split(split(variables('BranchLocations'),decodeUriComponent('%0A'))[0], ', ')

    Location 2:

    split(split(variables('BranchLocations'),decodeUriComponent('%0A'))[1], ', ')

    Location 3:

    split(split(variables('BranchLocations'),decodeUriComponent('%0A'))[2], ', ')

  7. Save and perform a manual test, you can observe the result.

  8. We are almost there. Next, we just need to access each part of the data and pass them back to Power Virtual Agent as variables us array() function

    Take a look at the location one data, we will use array(outputs(Compose))[x] to access each part of the data, remember the number starts from 0.

    array(outputs(Compose))[0] = Chadstone Centre

    array(outputs(Compose))[1] = 1341 Dandenong Rd

    array(outputs(Compose))[2] = Chadstone

    array(outputs(Compose))[3] = VIC 3148

    array(outputs(Compose))[4] = 0.09 km\r

    [
      "Chadstone Centre",
      "1341 Dandenong Rd",
      "Chadstone",
      "VIC 3148",
      "0.09 km\r"
    ]
    
  9. We will create 15 variables, 5 for each branch, and pass them back to PVA. You don't need to do that right now as I will explain in the next section.

    The distance has returned "km\r", probably because we split the string by comma and there are no commas after each distance.

    We will use the substring function, measure the total length of the string, and return the data minus the last 2 tokens to remove "\r"

    substring(array(outputs(Compose))[4], 0, length(array(outputs(Compose))[4])-2)
    

Adaptive Card - Display Store Information

Adaptive Card is another whole new topic that I won't expand on here. If you are interested, watch my video which provides more details.

  1. Go to Microsoft Samples and Templates | Adaptive Cards

  2. Pick a design you like and modify the layout. I chose the "Flight update" template, moved things around and made it look like the below.

  3. Once you are happy with the layout, copy the card payload to PVA Adaptive Card.

    Make sure you change the Target version to 1.3


Power Virtual Agent - Build the Chatbot

  1. Create a preview bot, select "Test" to edit, and switch off all the lesson topics.

  2. Create a new topic with some simple trigger phrases.

  3. Ask a question, take the user's entire response and save it to a variable.

  4. Click the plus sign to call an action and create a flow.

    Notice here we cannot select an existing flow.

    We will have to create a new one beginning and ending with PVA.

    Everything in the middle of the flow are the same.

  5. Select "Create a flow" and it will take you to the Power Automate portal. Make sure you are already signed in.

    Create a text input called "FlowSuburb". This cloud flow variable will be given the value of varSuburb from the PVA user input.

    The "FlowSuburb" will provide the query for the desktop flow to do the location search.

  6. Add a step to call desktop flow. If you see the below window, provide your local machine details.

  7. Once you connected the local machine, you should see a similar screen as in the previous step.

    Select your desktop flow, set "Run Mode" as "Attended", and assign the "FlowSuburb" variable to the PAD Input variable "varPADIN_Query.

  8. Let's follow the previous Power Automate section to initialize the same variable and created three Compose steps to extract the substring for each line of the address.

  9. Here is our complete flow, it takes the input from the PVA, call the Desktop Flow to search, and return the values back to PVA.

    Power Fx Examples:

    NameOne: array(outputs('LocationOne'))[0]

    DistanceTwo: array(outputs('LocationTwo'))[4]

    PostcodeThree: array(outputs('LocationThree'))[3]

  10. Save the flow and return to PVA, if you do not see the flow populate all the variables, delete the action and call it again.

    Select PVA Variable "VarSuburb" to match the Flow Variable "FlowSuburb"

    I got some warnings initially for unknown reasons.

    Sign out and back into PVA fixed this issue.

  11. Add a message and select the adaptive card, click "Media" to expand the card.

  12. Go to the card designer and copy the payload. Paste to overwrite the sample.

    Once the code is pasted into JSON editor, change the dropdown to Formula.

    Do not reverse the order.

    You should see a green tick to indicate your card code is valid

  13. Next, go through the code and replace the text with PVA Variables. All PVA Variables can be accessed by (Topic.VariableName).

    For example, the following code matches the Postcode area on the card.

              items: [
                {
                  type: "TextBlock",
                  text: "Distance",
                  horizontalAlignment: "Right",
                  size: "ExtraLarge",
                  color: "Accent",
                  spacing: "None",
                  wrap: true
                }
              ]
    

    We will replace the "Distance" with (Topic.PostcodeOne)

              items: [
                {
                  type: "TextBlock",
                  text: (Topic.DistanceOne),
                  horizontalAlignment: "Right",
                  size: "ExtraLarge",
                  color: "Accent",
                  spacing: "None",
                  wrap: true
                }
              ]
    
  14. We will make three adaptive cards and each card will have 5 values to be replaced by (Topic.Variable)

    Make sure you don't replace the text label.

  15. You will repeat this process to create three different Adaptive Cards with all the variables.

    1. Here I also attached the code for the Adaptive Card which you can past straight in.

       {
         '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
         type: "AdaptiveCard",
         version: "1.3",
           body: [
           {
             type: "ColumnSet",
             spacing: "Medium",
             separator: true,
             columns: [
               {
                 type: "Column",
                 width: "1",
                 items: [
                   {
                     type: "TextBlock",
                     text: "Street",
                     isSubtle: true,
                     weight: "Bolder",
                     wrap: true,
                     horizontalAlignment: "Left"
                   },
                   {
                     type: "TextBlock",
                     text: (Topic.AddressThree),
                     spacing: "Small",
                     wrap: true,
                     horizontalAlignment: "Left"
                   }
                 ]
               },
               {
                 type: "Column",
                 width: "1",
                 items: [
                   {
                     type: "TextBlock",
                     text: "Suburb",
                     isSubtle: true,
                     horizontalAlignment: "Right",
                     weight: "Bolder",
                     wrap: true
                   },
                   {
                     type: "TextBlock",
                     text: (Topic.SuburbThree),
                     color: "Attention",
                     weight: "Bolder",
                     horizontalAlignment: "Right",
                     spacing: "Small",
                     wrap: true
                   }
                 ]
               },
               {
                 type: "Column",
                 width: "1",
                 items: [
                   {
                     type: "TextBlock",
                     text: "Postcode",
                     isSubtle: true,
                     horizontalAlignment: "Right",
                     weight: "Bolder",
                     wrap: true
                   },
                   {
                     type: "TextBlock",
                     text: (Topic.PostcodeThree),
                     color: "Attention",
                     horizontalAlignment: "Right",
                     weight: "Bolder",
                     spacing: "Small",
                     wrap: true
                   }
                 ]
               }
             ]
           },
           {
             type: "ColumnSet",
             spacing: "Medium",
             separator: true,
             columns: [
               {
                 type: "Column",
                 width: "1",
                 items: [
                   {
                     type: "TextBlock",
                     text: (Topic.NameThree),
                     size: "ExtraLarge",
                     color: "Accent",
                     spacing: "None",
                     wrap: true,
                     horizontalAlignment: "Center"
                   }
                 ]
               },
               {
                 type: "Column",
                 width: "auto",
                 items: [
                   {
                     type: "TextBlock",
                     text: " ",
                     wrap: true
                   },
                   {
                     type: "Image",
                     url: "https://img.icons8.com/external-becris-lineal-color-becris/2x/external-bank-finance-taxation-becris-lineal-color-becris.png",
                     altText: "Airplane",
                     size: "Small"
                   }
                 ]
               },
               {
                 type: "Column",
                 width: "1",
                 items: [
                   {
                     type: "TextBlock",
                     text: (Topic.DistanceThree),
                     horizontalAlignment: "Right",
                     size: "ExtraLarge",
                     color: "Accent",
                     spacing: "None",
                     wrap: true
                   }
                 ]
               }
             ]
           }
         ]
       }