Add custom actions

In Bot Framework Composer, actions are the main contents of a trigger. Actions help to maintain the conversation flow and instruct bots to fulfill user's requests. Composer provides different types of actions such as Send a response, Ask a question, and Create a condition. Besides these built-in actions, you can create and customize your own actions in Composer.

This article will walk you through how to include a sample custom action named MultiplyDialog that multiplies two numbers passed as inputs. The sample custom action lives inside the runtime/customaction subfolder of the bot, and can be viewed here on GitHub.

Prerequisites

Setup the Bot Framework CLI tools

The Bot Framework CLI tools include the bf-dialog tool which will create a "schema file" that describes the built-in and custom capabilities of your bot project. It does this by merging partial schema files included with each component with the root schema provided by Bot Framework.

Open a command line and run the following command to install the Bot Framework tools:

npm i -g @microsoft/botframework-cli

Tip

For more information about Bot Framework SDK schemas, read here. For more information about how to create schema files, read here.

About the example custom action

The sample custom action lives inside the runtime/customaction subfolder of the bot, and can be viewed here on GitHub. After you export runtime, you will have the example custom action inside your bot's exported runtime/customaction folder.

The example custom action component consists of the following:

  • A CustomAction.sln solution file.

  • A Microsoft.BotFramework.Composer.CustomAction.csproj project file.

  • A CustomActionComponentRegistration.cs code file for component registration.

  • A Schemas folder that contains the MultiplyDialog.schema file. This schema file describes the properties of the example dialog component, arg1, arg2, and resultProperty.

    {
        "$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema",
        "$role": "implements(Microsoft.IDialog)",
        "title": "Multiply",
        "description": "This will return the result of arg1*arg2",
        "type": "object",
        "additionalProperties": false,
        "properties": {
           "arg1": {
                 "$ref": "schema:#/definitions/integerExpression",
                 "title": "Arg1",
                 "description": "Value from callers memory to use as arg 1"
           },
           "arg2": {
                 "$ref": "schema:#/definitions/integerExpression",
                 "title": "Arg2",
                 "description": "Value from callers memory to use as arg 2"
           },
           "resultProperty": {
                 "$ref": "schema:#/definitions/stringExpression",
                 "title": "Result",
                 "description": "Value from callers memory to store the result"
           }
        }
    }
    
    

    Bot Framework Schemas are specifications for JSON data. They define the shape of the data and can be used to validate JSON. All of Bot Framework's Adaptive Dialogs are defined using this JSON schema. The schema files tell Composer what capabilities the bot runtime supports. Composer uses the schema to help it render the user interface when using the action in a dialog.

    Important

    You can follow instructions here to create schema files.

  • An Action folder that contains the MultiplyDialog.cs class, which defines the business logic of the custom action, in this example, multiply two numbers passed as inputs and output result.

    
    using System;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using System.Threading.Tasks;
    using AdaptiveExpressions.Properties;
    using Microsoft.Bot.Builder.Dialogs;
    using Newtonsoft.Json;
    
    namespace Microsoft.BotFramework.Composer.CustomAction
    {
        /// <summary>
        /// Custom command which takes takes 2 data bound arguments (arg1 and arg2) and multiplies them returning that as a databound result.
        /// </summary>
        public class MultiplyDialog : Dialog
        {
           [JsonConstructor]
           public MultiplyDialog([CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
                 : base()
           {
                 // enable instances of this command as debug break point
                 this.RegisterSourceLocation(sourceFilePath, sourceLineNumber);
           }
    
           [JsonProperty("$kind")]
           public const string Kind = "MultiplyDialog";
    
           /// <summary>
           /// Gets or sets memory path to bind to arg1 (ex: conversation.width).
           /// </summary>
           /// <value>
           /// Memory path to bind to arg1 (ex: conversation.width).
           /// </value>
           [JsonProperty("arg1")]
           public NumberExpression Arg1 { get; set; }
    
           /// <summary>
           /// Gets or sets memory path to bind to arg2 (ex: conversation.height).
           /// </summary>
           /// <value>
           /// Memory path to bind to arg2 (ex: conversation.height).
           /// </value>
           [JsonProperty("arg2")]
           public NumberExpression Arg2 { get; set; }
    
           /// <summary>
           /// Gets or sets caller's memory path to store the result of this step in (ex: conversation.area).
           /// </summary>
           /// <value>
           /// Caller's memory path to store the result of this step in (ex: conversation.area).
           /// </value>
           [JsonProperty("resultProperty")]
           public StringExpression ResultProperty { get; set; }
    
           public override Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
           {
                 var arg1 = Arg1.GetValue(dc.State);
                 var arg2 = Arg2.GetValue(dc.State);
    
                 var result = Convert.ToInt32(arg1) * Convert.ToInt32(arg2);
                 if (this.ResultProperty != null)
                 {
                    dc.State.SetValue(this.ResultProperty.GetValue(dc.State), result);
                 }
    
                 return dc.EndDialogAsync(result: result, cancellationToken: cancellationToken);
           }
        }
    }
    

To create a class such as MultiplyDialog.cs shown above:

  • Create a class which inherits from the Dialog class.
  • Define the properties for input and output. These will appear in Composer's property editor, and need to be described in the schema file.
  • Implement the required BeginDialogAsync() method, which will contain the logic of the custom action. You can use Property.GetValue(dc.State) to get value, and dc.State.SetValue(Property, value) to set value.
  • Register the custom action component where it is called.
  • (optional) If there is more than one turn, you might need to add the ContinueDialogAsync class. Read more in the Actions sample code in the Bot Framework SDK.

In the following sections, we will walk you through the steps to add the custom action in Composer and test it.

Export runtime

The first step to add a custom action is to export the bot runtime through the Runtime Config in Composer. This process will generate a copy of your bot's runtime so that you can modify the code and add your custom action.

Note

Currently Composer supports the C# runtime and JavaScript (preview) runtime.

Once you have the exported bot runtime, you can make changes to the schema. The exported runtime folder will broadly have the following structure.

bot
  /bot.dialog
  /language-generation
  /language-understanding
  /dialogs
  /runtime
     /azurewebapp
     /azurefunctions
  /schemas
     sdk.schema

To export your bot runtime:

  1. Navigate to the Settings page of your Composer and select Runtime Config from the Configuration navigation pane.

  2. Enable Use custom runtime under Bot runtime settings and click Get a new copy of the runtime code.

  3. In the pop-up window select C# and select Okay. Then you will see the exported runtime directory in the Runtime code location field.

    runtime location

Customize your exported runtime

After you get a copy of your bot's runtime, you can start to modify the code to include the custom action.

Note

The following steps assume you are using azurewebapp as your deployment solution. If yo use azurefunctions the steps are similar.

Follow these steps to customize the runtime:

  1. Navigate to the runtime location (for example, C:\Users\UserName\Documents\Composer\bot\runtime) generated from the export bot runtime section.

  2. Navigate to the csproj file inside the runtime folder (for example, bot\runtime\azurewebapp\Microsoft.BotFramework.Composer.WebApp.csproj). Include a project reference to the custom action project like:

       <ProjectReference Include="..\customaction\Microsoft.BotFramework.Composer.CustomAction.csproj" />
    
  3. Then still in the azurewebapp folder, open the Startup.cs file. Uncomment the following two lines to register this action.

    using Microsoft.BotFramework.Composer.CustomAction;
    
    // This is for custom action component registration.
    ComponentRegistration.Add(new CustomActionComponentRegistration());
    
  4. Run the command dotnet build on the azurewebapp project to verify if it passes build after adding custom actions to it. You should be able to see the "Build succeeded" message after this command.

Update the schema file

Now you have customized your runtime, the next step is to update the sdk.schema file to include the MultiplyDialog.Schema file.

Navigate to the C:\Users\UserName\Composer\Bot\schemas folder. This folder contains a PowerShell script and a bash script. Run either one of the following commands:

   ./update-schema.ps1 -runtime azurewebapp
   sh ./update-schema.sh -runtime azurewebapp

Note

Please note that the runtime azurewebapp is chosen by default if no argument is passed.

You can validate that the partial schema (MultiplyDialog.schema inside the customaction/Schema folder) has been appended to the default sdk.schema file to generate one single consolidated sdk.schema file.

The above steps should have generated a new sdk.schema file inside the schemas folder for Composer to use. Reload the bot and you should be able to include your custom action!

Test

Reopen the bot project in Composer and you should be able to test your added custom action!

  1. Open your bot in Composer. Select a trigger you want to associate this custom action with.

  2. Select + under the trigger node to see the actions menu. You will see Custom Actions added to the menu. Select Multiply from the menu.

    Custom actions multiply

  3. On the Properties panel on the right side, enter two numbers in the argument fields: Arg1 and Arg2. Enter dialog.result in the Result property field. For example, you can enter the following:

    Multiply property

  4. Add a Send a response action. Enter 99*99=${dialog.result} in the Language Generation editor.

    Send a response

  5. Select Restart Bot and you can see the testing result in the Emulator.

    Emulator image

Debug a custom action

Follow these steps to open the exported runtime in Visual Studio and debug the custom action.

  1. Open the exported runtime in Visual Studio. In Solution Explorer expand the customaction folder then expand the Action folder and open the MultiplyDialog.cs file.

  2. In the MultiplyDialog.cs file, set breakpoints as necessary.

  3. Attach a debugger to the exported runtime.

    1. In Visual Studio, select Debug > Attach to Process (or press Ctrl+Alt+P).

    2. In the Attach to Process window, select Microsoft.BotFramework.Composer.WebApp.exe from the Available processes section. Select Attach.

    Attach to process

    Tip

    If you have multiple bots running, you will see multiple Microsoft.BotFramework.Composer.WebApp.exe listed in the Available processes window. Hover mouse over Microsoft.BotFramework.Composer.WebApp.exe and see the bot project name. Make sure you select Microsoft.BotFramework.Composer.WebApp.exe of the bot project you want to debug.

  4. Once you attach the debugger, make sure you start the bot in Composer and select Test in Emulator in Composer to open the Emulator window.

  5. From the Emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint.

    Hit breakpoint

  6. In the process of debugging, if you see an Exception Thrown error in the Contains.cs file, uncheck Break when this exception type if thrown from Exception Settings and Continue. This is a known issue.

    Exception thrown

Additional information