Create custom actions
APPLIES TO: Composer v2.x
In Bot Framework Composer, actions are the main contents of a trigger. Composer provides different types of actions, such as Send a response, Ask a question, and Create a condition. Besides these built-in actions, more actions can be added through packages or by creating components that includes custom actions.
This article explains how to create a custom action that multiplies two inputs together.
Prerequisites
- A basic understanding of actions, language generation, and language understanding in Composer.
- A basic understanding of extending a bot with components.
- Familiarity with JSON Schema and the JSON format.
- A bot built using Composer.
- The latest version of the Bot Framework CLI.
Complete sample
See the GitHub Bot Framework samples repo for the multiply dialog sample in C# or JavaScript.
Set up the Bot Framework CLI tool
The Bot Framework CLI tools include the bf-dialog
command for working with .schema
files. If the Bot Framework CLI tool isn't already installed, open an elevated command prompt and run the following command to install the Bot Framework tools:
npm i -g @microsoft/botframework-cli
Set up the component project
To create a custom action (or any component), first set up a new project, then add the necessary package dependencies for working with adaptive dialogs and the Bot Framework SDK.
Locate the bot's <myBot>.sln file and open it in an editor (like Visual Studio or Visual Studio Code).
Add a new project named MultiplyDialog to your solution. In Visual Studio, right-click on the solution in Solution Explorer and select Add > New Project. Use the Class Library project template.
Add a reference to the Microsoft.Bot.Builder.Adaptive.Runtime package. Use the same version the bot depends on.
<PackageReference Include="Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime" Version="4.13.1" />
Add a project reference from the bot project to the component project. Right-click on the <myBot> project and select Add > Project Reference. Choose the MultiplyDialog project and select OK.
Build the entire solution to restore all packages and validate the dependency tree.
Create the custom action
Actions in Composer are special implementations of the Dialog
base class. This allows each action in the trigger to be pushed onto the dialog stack, and executed in turn.
In the new project, rename the Class1.cs file to MultiplyDialog.cs, and update its contents to look like the below:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
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.Bot.Components.Samples.MultiplyDialog
{
/// <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
RegisterSourceLocation(sourceFilePath, sourceLineNumber);
}
/// <summary>
/// Gets the unique name (class identifier) of this trigger.
/// </summary>
/// <remarks>
/// There should be at least a .schema file of the same name. There can optionally be a
/// .uischema file of the same name that describes how Composer displays this trigger.
/// </remarks>
[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);
}
}
}
Create the schema file
The .schema
file for the component is a partial schema that will be merged into the main .schema
file for the bot. Although it's possible to edit the main sdk.schema file for the bot directly, doing so isn't recommended. Merging partial schema files will isolate changes, allow for easier recovery from errors, and enable easier packaging of your component for reuse.
Create a new file in the project named MultiplyDialog.schema and update the contents to the below:
Important
The name of the .schema
file must match the Kind
variable defined in the MultiplyDialog.cs
file exactly, including casing.
{
"$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"
}
}
}
For the structure of .schema
files, see the JSON Schema definition for Bot Framework dialog schemas.
Create the BotComponent
class
The adaptive runtime will dynamically discover and inject components at startup time.
Create a MultiplyDialogBotComponent.cs file in the project and update the contents to:
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs.Declarative; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Bot.Components.Samples.MultiplyDialog { /// <summary> /// Definition of a <see cref="Microsoft.Bot.Builder.BotComponent"/> that allows registration of /// services, custom actions, memory scopes and adapters. /// </summary> /// To make your components available to the system you derive from BotComponent and register services to add functionality. /// These components then are consumed in appropriate places by the systems that need them. When using Composer, Startup gets called /// automatically on the components by the bot runtime, as long as the components are registered in the configuration. public class MultiplyDialogBotComponent : BotComponent { /// <summary> /// Entry point for bot components to register types in resource explorer, consume configuration and register services in the /// services collection. /// </summary> /// <param name="services">Services collection to register dependency injection.</param> /// <param name="configuration">Configuration for the bot component.</param> public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) { // Anything that could be done in Startup.ConfigureServices can be done here. // In this case, the MultiplyDialog needs to be added as a new DeclarativeType. services.AddSingleton<DeclarativeType>(sp => new DeclarativeType<MultiplyDialog>(MultiplyDialog.Kind)); } } }
In the appsettings.json file of the bot project (located at
<mybot>\settings
), add your newMultiplyDialogBotComponent
to the values in theruntimeSettings/components
array."runtimeSettings": { "components": [ { "name": "MultiplyDialog" } ] }
Build the entire solution to validate everything was added correctly.
Merge schema files
Note
This step only needs to be performed when a new .schema
file is added or updated.
The final step is to merge the partial schema file from the MultiplyDialog project into the main sdk.schema file for the bot. This makes the custom action available for use in Composer.
Go to the schemas folder in the myBot project. This folder contains a PowerShell script and a bash script. Use an elevated PowerShell terminal to execute the PowerShell script. You'll need to either copy/paste the contents of the script or ensure your execution policy allows for running unsigned scripts.
To validate whether the script executed successfully, search for "MultiplyDialog" inside the MyBot\schemas\sdk.schema file and validate that the partial schema from the MultiplyDialog.schema file is included in sdk.schema.
Note
Alternatively, you can click-to-run the update-schema.sh file inside the MyEmptyBot\schemas folder to run the bash script.
Test
Open the bot project in Composer to test the added custom action. If the project is already loaded, return to Home in Composer, and reload the project.
- Open the bot in Composer, then select a trigger to add the custom action to.
- Select + under the trigger node to see the actions menu. Select Custom Actions, then Multiply.
- In the properties pane, enter two numbers in the argument fields: Arg1 and Arg2. Enter
dialog.result
in the Result property field. - Add a Send a response action. Enter
The result is: ${dialog.result}
in the Language Generation editor. - Select Start Bot to test the bot in Web Chat. When triggered, the bot will respond with the test result entered in the previous step.