Conversión de un bot de .NET versión 3 en una aptitudConvert a .NET v3 bot to a skill

se aplica a: SDK V4APPLIES TO: SDK v4

En este artículo se describe cómo convertir 3 bots de .NET versión 3 de ejemplo en aptitudes y crear un consumidor de aptitudes de la versión 4 que pueda acceder a estas aptitudes.This article describes how to convert 3 sample .NET v3 bots to skills and to create a v4 skill consumer that can access these skills. Para convertir un bot de JavaScript versión 3 en una aptitud, consulte Conversión de un bot de JavaScript versión 3 en una aptitud.To convert a JavaScript v3 bot to a skill, see how to Convert a JavaScript v3 bot to a skill. Para migrar un bot de .NET de la versión 3 a la versión 4, consulte Migración de un bot de .NET versión 3 a un bot de .NET Framework versión 4.To migrate a .NET bot from v3 to v4, see how to Migrate a .NET v3 bot to a .NET Framework v4 bot.

PrerrequisitosPrerequisites

  • Visual Studio 2019.Visual Studio 2019.
  • .NET Core 3.1..NET Core 3.1.
  • .NET Framework 4.6.1 o superior..NET Framework 4.6.1 or later.
  • Suscripción a Azure.An Azure subscription. Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.If you don't have an Azure subscription, create a free account before you begin.
  • Copias de los bots de ejemplo de .NET versión 3 que se van a convertir: un bot de eco, PizzaBot y SimpleSandwichBot.Copies of the v3 .NET sample bots to convert: an echo bot, the PizzaBot, and the SimpleSandwichBot.
  • Una copia del consumidor de aptitudes de ejemplo de .NET versión 4: SimpleRootBot.A copy of the v4 .NET sample skill consumer: SimpleRootBot.

Acerca de los botsAbout the bots

En este artículo, cada bot de la versión 3 se actualiza para que actúe como una aptitud.In this article, each v3 bot is updated to act as a skill. Se incluye un consumidor de aptitudes de la versión 4, para que pueda probar los bots convertidos en aptitudes.A v4 skill consumer is included, so that you can test the converted bots as skills.

  • EchoBot devuelve los mensajes que recibe.The EchoBot echoes back messages it receives. Como aptitud, finaliza cuando recibe un mensaje "end" o "stop".As a skill, it completes when it receives an "end" or "stop" message. El bot que se va a convertir se basa en la plantilla del proyecto de bot de eco de Bot Builder de la versión 3.The bot to convert is based on the v3 Bot Builder Echo Bot project template.
  • PizzaBot guía al usuario a la hora de pedir una pizza.The PizzaBot walks a user through ordering a pizza. Como aptitud, envía el pedido del usuario de vuelta al elemento primario cuando finaliza.As a skill, it sends the user's order back to the parent when finished.
  • SimpleSandwichBot guía al usuario a la hora de pedir un sándwich.The SimpleSandwichBot walks a user through ordering a sandwich. Como aptitud, envía el pedido del usuario de vuelta al elemento primario cuando finaliza.As a skill, it sends the user's order back to the parent when finished.

Además, un consumidor de aptitudes de la versión 4, SimpleRootBot, demuestra cómo consumir las aptitudes y le permite probarlas.Also, a v4 skill consumer, the SimpleRootBot, demonstrates how to consume the skills and allows you to test them.

Para usar el consumidor de aptitudes para probar las aptitudes, los 4 bots deben estar en ejecución al mismo tiempo.To use the skill consumer to test the skills, all 4 bots need to be running at the same time. Los bots se pueden probar localmente mediante Bot Framework Emulator, con cada bot en un puerto local diferente.The bots can be tested locally using the Bot Framework Emulator, with each bot using a different local port.

Creación de los recursos de Azure para los botsCreate Azure resources for the bots

La autenticación de bot a bot requiere que cada bot participante tenga un identificador de aplicación y una contraseña válidos.Bot-to-bot authentication requires that each participating bot has a valid app ID and password.

  1. Cree un registro de canales del bot para los bots según sea necesario.Create a Bot Channels Registration for the bots as needed.
  2. Tome nota del identificador de aplicación y la contraseña de cada uno de ellos.Record the app ID and password for each one.

Proceso de conversiónConversion process

Para convertir un bot existente en un bot de aptitudes, solo se requieren algunos pasos, como se describe en las siguientes secciones.To convert an existing bot to a skill bot takes just a few steps, as outlined in the next couple sections. Para obtener información más detallada, consulte Acerca de las aptitudes.For more in-depth information, see about skills.

  • Actualice el archivo web.config del bot para establecer el identificador de aplicación y la contraseña del bot, y para agregar una propiedad para los llamadores permitidos.Update the bot's web.config file to set the bot's app ID and password and to add an allowed callers property.
  • Agregue la validación de notificaciones.Add claims validation. Esto restringirá el acceso a la aptitud para que solo los usuarios o el bot principal puedan acceder a la aptitud.This will restrict access to the skill so that only users or your root bot can access the skill. Consulte la sección Información adicional para más información sobre la validación de notificaciones predeterminada y personalizada.See the additional information section for more information about default and custom claims validation.
  • Modifique el controlador de mensajes del bot para controlar las actividades endOfConversation desde el bot principal.Modify the bot's messages controller to handle endOfConversation activities from the root bot.
  • Modifique el código del bot para devolver una actividad endOfConversation cuando finaliza la aptitud.Modify the bot code to return an endOfConversation activity when the skill completes.
  • Cuando finaliza la aptitud, si esta tiene el estado de la conversación o mantiene recursos, debe borrar el estado de la conversación y liberar los recursos.Whenever the skill completes, if it has conversation state or maintains resources, it should clear its conversation state and release resources.
  • Opcionalmente, agregue un archivo de manifiesto.Optionally add a manifest file. Como un consumidor de aptitudes no tiene necesariamente acceso al código de la aptitud, use un manifiesto de aptitud para describir las actividades que la aptitud puede recibir y generar, sus parámetros de entrada y salida, y los puntos de conexión de la aptitud.Since a skill consumer does not necessarily have access to the skill code, use a skill manifest to describe the activities the skill can receive and generate, its input and output parameters, and the skill's endpoints. La esquema de manifiesto actual es skill-manifest-2.0.0.json.The current manifest schema is skill-manifest-2.0.0.json.

Conversión del bot de ecoConvert the echo bot

  1. Cree un nuevo proyecto a partir de la plantilla de proyecto de la versión 3 Bot de eco de Bot Builder y establézcalo para que use el puerto 3979.Create a new project from the v3 Bot Builder Echo Bot project template, and set it to use port 3979.

    1. Cree el proyecto.Create the project.
    2. Abra las propiedades del proyecto.Open the project's properties.
    3. Seleccione la categoría Web y establezca Dirección URL del proyecto en http://localhost:3979/.Select the Web category, and set the Project Url to http://localhost:3979/.
    4. Guarde los cambios y cierre la pestaña de propiedades.Save your changes and close the properties tab.
  2. En el archivo de configuración, agregue el identificador de aplicación y la contraseña del bot de eco.To the configuration file, add the echo bot's app ID and password. Además, en configuración de la aplicación, agregue una propiedad EchoBotAllowedCallers y agregue el identificador de aplicación del bot principal simple a su valor.Also in app settings, add an EchoBotAllowedCallers property and add the simple root bot's app ID to its value.

    V3EchoBot\Web.configV3EchoBot\Web.config

    <appSettings>
      <!-- update these with your Microsoft App Id and your Microsoft App Password-->
      <add key="MicrosoftAppId" value="YOUR Echo bot's MicrosoftAppId" />
      <add key="MicrosoftAppPassword" value="YOUR Echo bot's MicrosoftAppPassword" />
      <add key="EchoBotAllowedCallers" value="YOUR root bot's MicrosoftAppId" />
    </appSettings>
    
  3. Agregue un validador de notificaciones personalizado y una clase de configuración de autenticación compatible.Add a custom claims validator and a supporting authentication configuration class.

    V3EchoBot\Authentication\CustomAllowedCallersClaimsValidator.csV3EchoBot\Authentication\CustomAllowedCallersClaimsValidator.cs

    Esto implementa la validación de notificaciones personalizada y genera un error UnauthorizedAccessException si se produce un error en la validación.This implements custom claims validation and throws an UnauthorizedAccessException if validation fails.

    using Microsoft.Bot.Connector.SkillAuthentication;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    
    namespace Microsoft.Bot.Sample.EchoBot.Authentication
    {
        /// <summary>
        /// Sample claims validator that loads an allowed list from configuration if present
        /// and checks that requests are coming from allowed parent bots.
        /// </summary>
        public class CustomAllowedCallersClaimsValidator : ClaimsValidator
        {
            private readonly IList<string> _allowedCallers;
    
            public CustomAllowedCallersClaimsValidator(IList<string> allowedCallers)
            {
                // AllowedCallers is the setting in web.config file
                // that consists of the list of parent bot IDs that are allowed to access the skill.
                // To add a new parent bot simply go to the AllowedCallers and add
                // the parent bot's Microsoft app ID to the list.
    
                _allowedCallers = allowedCallers ?? throw new ArgumentNullException(nameof(allowedCallers));
                if (!_allowedCallers.Any())
                {
                    throw new ArgumentNullException(nameof(allowedCallers), "AllowedCallers must contain at least one element of '*' or valid MicrosoftAppId(s).");
                }
            }
    
            /// <summary>
            /// This method is called from JwtTokenValidation.ValidateClaimsAsync
            /// </summary>
            /// <param name="claims"></param>
            public override Task ValidateClaimsAsync(IList<Claim> claims)
            {
                if (claims == null)
                {
                    throw new ArgumentNullException(nameof(claims));
                }
    
                if (!claims.Any())
                {
                    throw new UnauthorizedAccessException("ValidateClaimsAsync.claims parameter must contain at least one element.");
                }
    
                if (SkillValidation.IsSkillClaim(claims))
                {
                    // if _allowedCallers has one item of '*', allow all parent bot calls and do not validate the appid from claims
                    if (_allowedCallers.Count == 1 && _allowedCallers[0] == "*")
                    {
                        return Task.CompletedTask;
                    }
    
                    // Check that the appId claim in the skill request is in the list of skills configured for this bot.
                    var appId = JwtTokenValidation.GetAppIdFromClaims(claims).ToUpperInvariant();
                    if (_allowedCallers.Contains(appId))
                    {
                        return Task.CompletedTask;
                    }
    
                    throw new UnauthorizedAccessException($"Received a request from a bot with an app ID of \"{appId}\". To enable requests from this caller, add the app ID to your configuration file.");
                }
    
                throw new UnauthorizedAccessException($"ValidateClaimsAsync called without a Skill claim in claims.");
            }
        }
    }
    

    V3EchoBot\Authentication\CustomSkillAuthenticationConfiguration.csV3EchoBot\Authentication\CustomSkillAuthenticationConfiguration.cs

    Esto carga la información de los llamadores permitidos del archivo de configuración y usa CustomAllowedCallersClaimsValidator para la validación de notificaciones.This loads the allowed callers information from the configuration file and uses the CustomAllowedCallersClaimsValidator for claims validation.

    using Microsoft.Bot.Connector.SkillAuthentication;
    using System.Configuration;
    using System.Linq;
    
    namespace Microsoft.Bot.Sample.EchoBot.Authentication
    {
        public class CustomSkillAuthenticationConfiguration : AuthenticationConfiguration
        {
            private const string AllowedCallersConfigKey = "EchoBotAllowedCallers";
            public CustomSkillAuthenticationConfiguration()
            {
                // Could pull this list from a DB or anywhere.
                var allowedCallers = ConfigurationManager.AppSettings[AllowedCallersConfigKey].Split(',').Select(s => s.Trim().ToUpperInvariant()).ToList();
                ClaimsValidator = new CustomAllowedCallersClaimsValidator(allowedCallers);
            }
        }
    }
    
  4. Actualice la clase MessagesController.Update the MessagesController class.

    V3EchoBot\Controllers\MessagesController.csV3EchoBot\Controllers\MessagesController.cs

    Actualice las instrucciones using.Update the using statements.

    using System.Diagnostics;
    using System.Net;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http;
    using Autofac;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.Dialogs.Internals;
    using Microsoft.Bot.Connector;
    using Microsoft.Bot.Connector.SkillAuthentication;
    using Microsoft.Bot.Sample.EchoBot.Authentication;
    

    Actualice el atributo de la clase de BotAuthentication a SkillBotAuthentication.Update the class attribute from BotAuthentication to SkillBotAuthentication. Use el parámetro opcional AuthenticationConfigurationProviderType para usar el proveedor de validación de notificaciones personalizadas.Use the optional AuthenticationConfigurationProviderType parameter to use the custom claims validation provider.

    // Specify which type provides the authentication configuration to allow for validation for skills.
    [SkillBotAuthentication(AuthenticationConfigurationProviderType = typeof(CustomSkillAuthenticationConfiguration))]
    public class MessagesController : ApiController
    

    En el método HandleSystemMessage, agregue una condición para controlar un mensaje endOfConversation.In the HandleSystemMessage method, add a condition to handle an endOfConversation message. Esto permite que la aptitud limpie el estado y libere los recursos cuando la conversación finaliza desde el consumidor de aptitudes.This allows the skill to clear state and release resources when the conversation is ended from the skill consumer.

    if (messageType == ActivityTypes.EndOfConversation)
    {
        Trace.TraceInformation($"EndOfConversation: {message}");
    
        // This Recipient null check is required for PVA manifest validation.
        // PVA will send an EOC activity with null Recipient.
        if (message.Recipient != null)
        {
            // Clear the dialog stack if the root bot has ended the conversation.
            using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
            {
                var botData = scope.Resolve<IBotData>();
                await botData.LoadAsync(default(CancellationToken));
    
                var stack = scope.Resolve<IDialogStack>();
                stack.Reset();
    
                await botData.FlushAsync(default(CancellationToken));
            }
        }
    }
    
  5. Modifique el código del bot para permitir que la aptitud marque que la conversación ha finalizado cuando recibe un mensaje "end" o "stop" del usuario.Modify the bot code to allow the skill to flag that the conversation is complete when it receives an "end" or "stop" message from the user. La aptitud también debe borrar el estado y liberar los recursos cuando finaliza la conversación.The skill should also clear state and release resources when it ends the conversation.

    V3EchoBot\Dialogs\RootDialog.csV3EchoBot\Dialogs\RootDialog.cs

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
    {
        var activity = await result as Activity;
    
        // Send an `endOfconversation` activity if the user cancels the skill.
        if (activity.Text.ToLower().Contains("end") || activity.Text.ToLower().Contains("stop"))
        {
            await context.PostAsync($"Ending conversation from the skill...");
            var endOfConversation = activity.CreateReply();
            endOfConversation.Type = ActivityTypes.EndOfConversation;
            endOfConversation.Code = EndOfConversationCodes.UserCancelled;
            await context.PostAsync(endOfConversation);
        }
        else
        {
            await context.PostAsync($"Echo (dotnet V3): {activity.Text}");
            await context.PostAsync($"Say 'end' or 'stop' and I'll end the conversation and back to the parent.");
        }
    
        context.Wait(MessageReceivedAsync);
    }
    
  6. Use este manifiesto para el bot de eco.Use this manifest for the echo bot. Establezca el identificador de aplicación del punto de conexión en el identificador de aplicación del bot.Set the endpoint app ID to the bot's app ID.

    V3EchoBot\wwwroot\echo-bot-manifest.jsonV3EchoBot\wwwroot\echo-bot-manifest.json

    {
      "$schema": "https://raw.githubusercontent.com/microsoft/botframework-sdk/master/schemas/skills/skill-manifest-2.0.0.json",
      "$id": "YourEchoBotHandle",
      "name": "V3 Echo Skill Bot",
      "version": "1.0",
      "description": "This is a sample skill for echoing what the user sent the bot.",
      "publisherName": "Microsoft",
      "privacyUrl": "https://microsoft.com/privacy",
      "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
      "license": "",
      "iconUrl": "https://myskill.contoso.com/icon.png",
      "tags": [
        "sample",
        "echo"
      ],
      "endpoints": [
        {
          "name": "YourEchoBotName",
          "protocol": "BotFrameworkV3",
          "description": "Default endpoint for the skill",
          "endpointUrl": "http://localhost:3979/api/messages",
          "msAppId": "Your Echo Bot's MicrosoftAppId"
        }
      ],
      "activities": {
        "EchoDotNetV3": {
          "description": "Echo user responses",
          "type": "message",
          "name": "V3Echo"
        }
      }
    }
    

    Para ver el esquema del manifiesto de aptitud, consulte skill-manifest-2.0.0.json.For the skill-manifest schema, see skill-manifest-2.0.0.json.

Conversión del bot de pedidos de pizzaConvert the pizza bot

  1. Abra su copia del proyecto PizzaBot y establézcala para que use el puerto 3980.Open your copy of the PizzaBot project, and set it to use port 3980.

    1. Abra las propiedades del proyecto.Open the project's properties.
    2. Seleccione la categoría Web y establezca Dirección URL del proyecto en http://localhost:3980/.Select the Web category, and set the Project Url to http://localhost:3980/.
    3. Guarde los cambios y cierre la pestaña de propiedades.Save your changes and close the properties tab.
  2. En el archivo de configuración, agregue el identificador de aplicación y la contraseña del bot de pedidos de pizza.To the configuration file, add the pizza bot's app ID and password. Además, en configuración de la aplicación, agregue una propiedad AllowedCallers y agregue el identificador de aplicación del bot principal simple a su valor.Also in app settings, add an AllowedCallers property and add the simple root bot's app ID to its value.

    V3PizzaBot\Web.configV3PizzaBot\Web.config

    <appSettings>
      <!-- update these with your Microsoft App Id and your Microsoft App Password-->
      <add key="MicrosoftAppId" value="YOUR Pizza bot's MicrosoftAppId" />
      <add key="MicrosoftAppPassword" value="YOUR Pizza bot's MicrosoftAppPassword" />
      <add key="AllowedCallers" value="YOUR root bot's MicrosoftAppId" />
    </appSettings>
    
  3. Agregue una clase ConversationHelper que tenga métodos auxiliares para:Add a ConversationHelper class that has helper methods to

    • Enviar la actividad endOfConversation cuando finaliza la aptitud.Send the endOfConversation activity when the skill ends. Esta puede devolver la información del pedido en la propiedad Value de la actividad y establecer la propiedad Code para reflejar el motivo de la finalización de la conversación.This can return the order information in the activity's Value property and set the Code property to reflect why the conversation ended.
    • Limpiar el estado de la conversación y liberar los recursos asociados.Clear conversation state and release any associated resources.

    V3PizzaBot\ConversationHelper.csV3PizzaBot\ConversationHelper.cs

    using System;
    using System.Collections.Concurrent;
    using System.Configuration;
    using System.Threading;
    using System.Threading.Tasks;
    using Autofac;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.Dialogs.Internals;
    using Microsoft.Bot.Connector;
    using Newtonsoft.Json;
    
    namespace Microsoft.Bot.Sample.PizzaBot
    {
        internal static class ConversationHelper
        {
            private static readonly ConcurrentDictionary<string, ConnectorClient> _connectorClientCache = new ConcurrentDictionary<string, ConnectorClient>();
    
            /// <summary>
            /// Helper method that sends an `endOfConversation` activity.
            /// </summary>
            /// <param name="incomingActivity">The incoming user activity for this turn.</param>
            /// <param name="order">Optional. The completed order.</param>
            /// <param name="endOfConversationCode">Optional. The EndOfConversationCode to send to the parent bot.
            /// Defaults to EndOfConversationCodes.CompletedSuccessfully.</param>
            /// <remarks>Sending the `endOfConversation` activity when the conversation completes allows
            /// the bot to be consumed as a skill.</remarks>
            internal static async Task EndConversation(Activity incomingActivity, PizzaOrder order = null, string endOfConversationCode = EndOfConversationCodes.CompletedSuccessfully)
            {
                var connectorClient = _connectorClientCache.GetOrAdd(incomingActivity.ServiceUrl, key =>
                {
                    var appId = ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppIdKey];
                    var appPassword = ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppPasswordKey];
                    return new ConnectorClient(new Uri(incomingActivity.ServiceUrl), appId, appPassword);
                });
    
                // Send End of conversation as reply.
                await connectorClient.Conversations.SendToConversationAsync(incomingActivity.CreateReply("Ending conversation from the skill..."));
                var endOfConversation = incomingActivity.CreateReply();
                if (order != null)
                {
                    endOfConversation.Value = JsonConvert.SerializeObject(order);
                }
                endOfConversation.Type = ActivityTypes.EndOfConversation;
                endOfConversation.Code = endOfConversationCode;
                await connectorClient.Conversations.SendToConversationAsync(endOfConversation);
            }
    
            /// <summary>
            /// Clear the dialog stack and data bags.
            /// </summary>
            /// <param name="activity">The incoming activity to use for scoping the Conversation.Container.</param>
            internal static async Task ClearState(Activity activity)
            {
                // This is required for PVA manifest validation.
                // PVA will send an EOC activity with null Recipient.
                if (activity.Recipient == null)
                    return;
    
                using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                {
                    var botData = scope.Resolve<IBotData>();
                    await botData.LoadAsync(default(CancellationToken));
    
                    // Some skills might persist data between invokations.
                    botData.UserData.Clear();
                    botData.ConversationData.Clear();
                    botData.PrivateConversationData.Clear();
    
                    var stack = scope.Resolve<IDialogStack>();
                    stack.Reset();
                    
                    await botData.FlushAsync(default(CancellationToken));
                }
            }
        }
    }
    
  4. Actualice la clase MessagesController.Update the MessagesController class.

    V3PizzaBot\Controllers\MessagesController.csV3PizzaBot\Controllers\MessagesController.cs

    Actualice las instrucciones using.Update the using statements.

    using System.Web.Http;
    using System.Threading.Tasks;
    
    using Microsoft.Bot.Connector;
    using Microsoft.Bot.Builder.FormFlow;
    using Microsoft.Bot.Builder.Dialogs;
    using System.Web.Http.Description;
    using System.Net.Http;
    using System.Diagnostics;
    using Microsoft.Bot.Connector.SkillAuthentication;
    using Microsoft.Bot.Builder.Dialogs.Internals;
    using Autofac;
    using System.Threading;
    

    Actualice el atributo de la clase de BotAuthentication a SkillBotAuthentication.Update the class attribute from BotAuthentication to SkillBotAuthentication. Este bot usa el validador de notificaciones predeterminado.This bot uses the default claims validator.

    [SkillBotAuthentication]
    public class MessagesController : ApiController
    

    En el método Post, modifique la condición de la actividad message para permitir que el usuario cancele el proceso de pedido desde dentro de la aptitud.In the Post method, modify the message activity condition to allow the user to cancel their ordering process from within the skill. Además, agregue una condición de la actividad endOfConversation para permitir que la aptitud limpie el estado y libere los recursos cuando la conversación finaliza desde el consumidor de aptitudes.Also, add an endOfConversation activity condition to allow the skill to clear state and release resources when the conversation is ended from the skill consumer.

    case ActivityTypes.Message:
        // Send an `endOfconversation` activity if the user cancels the skill.
        if (activity.Text.ToLower().Contains("end") || activity.Text.ToLower().Contains("stop"))
        {
            await ConversationHelper.ClearState(activity);
            await ConversationHelper.EndConversation(activity, endOfConversationCode: EndOfConversationCodes.UserCancelled);
        }
        else
        {
            await Conversation.SendAsync(activity, MakeRoot);
        }
        break;
    case ActivityTypes.EndOfConversation:
        Trace.TraceInformation($"EndOfConversation: {activity}");
    
        // Clear the dialog stack if the root bot has ended the conversation.
        await ConversationHelper.ClearState(activity);
    
        break;
    
  5. Modifique el código del bot.Modify the bot code.

    V3PizzaBot\PizzaOrderDialog.csV3PizzaBot\PizzaOrderDialog.cs

    Actualice las instrucciones using.Update the using statements.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.FormFlow;
    using Microsoft.Bot.Builder.Luis;
    using Newtonsoft.Json;
    using Microsoft.Bot.Builder.Luis.Models;
    using Microsoft.Bot.Connector;
    

    Agregue un mensaje de bienvenida.Add a welcome message. Esto ayudará al usuario a saber lo que ocurre cuando el bot principal invoca al bot de pedidos de pizza como una aptitud.This will help the user know what's going on when the root bot invokes the pizza bot as a skill.

    public override async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync("Welcome to the Pizza Order Bot. Let me know if you would like to order a pizza, or know our store hours.");
        await context.PostAsync("Say 'end' or 'stop' and I'll end the conversation and back to the parent.");
    
        await base.StartAsync(context);
    }
    

    Modifique el código del bot para permitir que la aptitud marque que la conversación ha finalizado cuando el usuario cancela o finaliza su pedido.Modify the bot code to allow the skill to flag that the conversation is complete when the user cancels or completes their order. La aptitud también debe borrar el estado y liberar los recursos cuando finaliza la conversación.The skill should also clear state and release resources when it ends the conversation.

    private async Task PizzaFormComplete(IDialogContext context, IAwaitable<PizzaOrder> result)
    {
        PizzaOrder order = null;
        try
        {
            order = await result;
        }
        catch (OperationCanceledException)
        {
            await context.PostAsync("You canceled the form!");
    
            // If the user cancels the skill, send an `endOfConversation` activity to the skill consumer.
            await ConversationHelper.EndConversation(context.Activity as Activity, endOfConversationCode: EndOfConversationCodes.UserCancelled);
            return;
        }
    
        if (order != null)
        {
            await context.PostAsync("Your Pizza Order: " + order.ToString());
        }
        else
        {
            await context.PostAsync("Form returned empty response!");
        }
    
        // When the skill completes, send an `endOfConversation` activity and include the finished order.
        await ConversationHelper.EndConversation(context.Activity as Activity, order);
        context.Wait(MessageReceived);
    }
    
  6. Use este manifiesto para el bot de pedidos de pizza.Use this manifest for the pizza bot. Establezca el identificador de aplicación del punto de conexión en el identificador de aplicación del bot.Set the endpoint app ID to the bot's app ID.

    V3PizzaBot\wwwroot\pizza-bot-manifest.jsonV3PizzaBot\wwwroot\pizza-bot-manifest.json

    {
      "$schema": "https://raw.githubusercontent.com/microsoft/botframework-sdk/master/schemas/skills/skill-manifest-2.0.0.json",
      "$id": "YourPizzaBotHandle",
      "name": "Pizza Skill Bot",
      "version": "1.0",
      "description": "This is a sample skill for ordering a Pizza.",
      "publisherName": "Microsoft",
      "privacyUrl": "https://microsoft.com/privacy",
      "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
      "license": "",
      "iconUrl": "https://myskill.contoso.com/icon.png",
      "tags": [
        "sample",
        "pizza"
      ],
      "endpoints": [
        {
          "name": "YourPizzaBotName",
          "protocol": "BotFrameworkV3",
          "description": "Default endpoint for the skill",
          "endpointUrl": "http://localhost:3980/api/messages",
          "msAppId": "YOUR Pizza Bot's MicrosoftAppId"
        }
      ],
      "activities": {
        "OrderPizza": {
          "description": "Order a Pizza",
          "type": "message",
          "name": "OrderPizza"
        }
      }
    }
    

    Para ver el esquema del manifiesto de aptitud, consulte skill-manifest-2.0.0.json.For the skill-manifest schema, see skill-manifest-2.0.0.json.

Conversión del bot de pedidos de sandwichConvert the sandwich bot

  1. Abra su copia del proyecto SimpleSandwichBot y establézcala para que use el puerto 3981.Open your copy of the SimpleSandwichBot project, and set it to use port 3981.

    1. Abra las propiedades del proyecto.Open the project's properties.
    2. Seleccione la categoría Web y establezca Dirección URL del proyecto en http://localhost:3981/.Select the Web category, and set the Project Url to http://localhost:3981/.
    3. Guarde los cambios y cierre la pestaña de propiedades.Save your changes and close the properties tab.
  2. En el archivo de configuración, agregue el identificador de aplicación y la contraseña del bot de pedidos de pizza.To the configuration file, add the pizza bot's app ID and password. Además, en configuración de la aplicación, agregue una propiedad AllowedCallers y agregue el identificador de aplicación del bot principal simple a su valor.Also in app settings, add an AllowedCallers property and add the simple root bot's app ID to its value.

    V3SimpleSandwichBot\Web.configV3SimpleSandwichBot\Web.config

    <appSettings>
      <!-- update these with your Microsoft App Id and your Microsoft App Password-->
      <add key="MicrosoftAppId" value="YOUR Sandwich bot's MicrosoftAppId" />
      <add key="MicrosoftAppPassword" value="YOUR Sandwich bot's MicrosoftAppPassword" />
      <add key="AllowedCallers" value="YOUR root bot's MicrosoftAppId" />
    </appSettings>
    
  3. Agregue una clase ConversationHelper que tenga métodos auxiliares para:Add a ConversationHelper class that has helper methods to

    • Enviar la actividad endOfConversation cuando finaliza la aptitud.Send the endOfConversation activity when the skill ends. Esta puede devolver la información del pedido en la propiedad Value de la actividad y establecer la propiedad Code para reflejar el motivo de la finalización de la conversación.This can return the order information in the activity's Value property and set the Code property to reflect why the conversation ended.
    • Limpiar el estado de la conversación y liberar los recursos asociados.Clear conversation state and release any associated resources.

    V3SimpleSandwichBot\ConversationHelper.csV3SimpleSandwichBot\ConversationHelper.cs

    using System;
    using System.Collections.Concurrent;
    using System.Configuration;
    using System.Threading;
    using System.Threading.Tasks;
    using Autofac;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.Dialogs.Internals;
    using Microsoft.Bot.Connector;
    using Newtonsoft.Json;
    
    namespace Microsoft.Bot.Sample.SimpleSandwichBot
    {
        internal static class ConversationHelper
        {
            private static readonly ConcurrentDictionary<string, ConnectorClient> _connectorClientCache = new ConcurrentDictionary<string, ConnectorClient>();
    
            /// <summary>
            /// Helper method that sends an `endOfConversation` activity.
            /// </summary>
            /// <param name="incomingActivity">The incoming user activity for this turn.</param>
            /// <param name="order">Optional. The completed order.</param>
            /// <param name="endOfConversationCode">Optional. The EndOfConversationCode to send to the parent bot.
            /// Defaults to EndOfConversationCodes.CompletedSuccessfully.</param>
            /// <remarks>Sending the `endOfConversation` activity when the conversation completes allows
            /// the bot to be consumed as a skill.</remarks>
            internal static async Task EndConversation(Activity incomingActivity, SandwichOrder order = null, string endOfConversationCode = EndOfConversationCodes.CompletedSuccessfully)
            {
                var connectorClient = _connectorClientCache.GetOrAdd(incomingActivity.ServiceUrl, key =>
                {
                    var appId = ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppIdKey];
                    var appPassword = ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppPasswordKey];
                    return new ConnectorClient(new Uri(incomingActivity.ServiceUrl), appId, appPassword);
                });
    
                // Send End of conversation as reply.
                await connectorClient.Conversations.SendToConversationAsync(incomingActivity.CreateReply("Ending conversation from the skill..."));
                var endOfConversation = incomingActivity.CreateReply();
                if (order != null)
                {
                    endOfConversation.Value = JsonConvert.SerializeObject(order);
                }
                endOfConversation.Type = ActivityTypes.EndOfConversation;
                endOfConversation.Code = endOfConversationCode;
                await connectorClient.Conversations.SendToConversationAsync(endOfConversation);
            }
    
            /// <summary>
            /// Clear the dialog stack and data bags.
            /// </summary>
            /// <param name="activity">The incoming activity to use for scoping the Conversation.Container.</param>
            internal static async Task ClearState(Activity activity)
            {
                // This Recipient null check is required for PVA manifest validation.
                // PVA will send an EOC activity with null Recipient.
                if (activity.Recipient == null)
                    return;
    
                using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                {
                    var botData = scope.Resolve<IBotData>();
                    await botData.LoadAsync(default(CancellationToken));
    
                    // Some skills might persist data between invokations.
                    botData.UserData.Clear();
                    botData.ConversationData.Clear();
                    botData.PrivateConversationData.Clear();
    
                    var stack = scope.Resolve<IDialogStack>();
                    stack.Reset();
    
                    await botData.FlushAsync(default(CancellationToken));
                }
            }
        }
    }
    
  4. Actualice la clase MessagesController.Update the MessagesController class.

    V3SimpleSandwichBot\Controllers\MessagesController.csV3SimpleSandwichBot\Controllers\MessagesController.cs

    Actualice las instrucciones using.Update the using statements.

    using System.Threading.Tasks;
    using System.Web.Http;
    
    using Microsoft.Bot.Connector;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.FormFlow;
    using System.Net.Http;
    using System.Web.Http.Description;
    using System.Diagnostics;
    using Microsoft.Bot.Connector.SkillAuthentication;
    using Newtonsoft.Json;
    using Microsoft.Bot.Builder.Dialogs.Internals;
    using Autofac;
    using System.Threading;
    

    Actualice el atributo de la clase de BotAuthentication a SkillBotAuthentication.Update the class attribute from BotAuthentication to SkillBotAuthentication. Este bot usa el validador de notificaciones predeterminado.This bot uses the default claims validator.

    [SkillBotAuthentication]
    public class MessagesController : ApiController
    

    En el método Post, modifique la condición de la actividad message para permitir que el usuario cancele el proceso de pedido desde dentro de la aptitud.In the Post method, modify the message activity condition to allow the user to cancel their ordering process from within the skill. Además, agregue una condición de la actividad endOfConversation para permitir que la aptitud limpie el estado y libere los recursos cuando la conversación finaliza desde el consumidor de aptitudes.Also, add an endOfConversation activity condition to allow the skill to clear state and release resources when the conversation is ended from the skill consumer.

    case ActivityTypes.Message:
        if (activity.Text.ToLower().Contains("end") || activity.Text.ToLower().Contains("stop"))
        {
            await ConversationHelper.ClearState(activity);
            await ConversationHelper.EndConversation(activity, endOfConversationCode: EndOfConversationCodes.UserCancelled);
        }
        else
        {
            await Conversation.SendAsync(activity, MakeRootDialog);
        }
    
        break;
    case ActivityTypes.EndOfConversation:
        Trace.TraceInformation($"EndOfConversation: {activity}");
    
        // Clear the dialog stack if the root bot has ended the conversation.
        await ConversationHelper.ClearState(activity);
    
        break;
    
  5. Modifique el formulario de pedidos de sandwich.Modify the sandwich form.

    V3SimpleSandwichBot\Sandwich.csV3SimpleSandwichBot\Sandwich.cs

    Actualice las instrucciones using.Update the using statements.

    using Microsoft.Bot.Builder.FormFlow;
    using Microsoft.Bot.Connector;
    using System;
    using System.Collections.Generic;
    using System.Runtime.Remoting.Messaging;
    

    Modifique el método BuildForm del formulario para permitir que la aptitud marque que se ha completado la conversación.Modify the form's BuildForm method to allow the skill to flag that the conversation is complete.

    public static IForm<SandwichOrder> BuildForm()
    {
        // When the skill completes (OnCompletion), send an `endOfConversation` activity and include the finished order.
        return new FormBuilder<SandwichOrder>()
                .Message("Welcome to the simple sandwich order bot! Say 'end' or 'stop' and I'll end the conversation and back to the parent.")
                .OnCompletion((context, order) => ConversationHelper.EndConversation(context.Activity as Activity, order))
                .Build();
    }
    
  6. Use este manifiesto para el bot de pedidos de sándwich.Use this manifest for the sandwich bot. Establezca el identificador de aplicación del punto de conexión en el identificador de aplicación del bot.Set the endpoint app ID to the bot's app ID.

    V3SimpleSandwichBot\wwwroot\sandwich-bot-manifest.jsonV3SimpleSandwichBot\wwwroot\sandwich-bot-manifest.json

    {
      "$schema": "https://raw.githubusercontent.com/microsoft/botframework-sdk/master/schemas/skills/skill-manifest-2.0.0.json",
      "$id": "YourSandwichBotHandle",
      "name": "Sandwich Skill Bot",
      "version": "1.0",
      "description": "This is a sample skill for ordering a sandwich.",
      "publisherName": "Microsoft",
      "privacyUrl": "https://microsoft.com/privacy",
      "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
      "license": "",
      "iconUrl": "https://myskill.contoso.com/icon.png",
      "tags": [
        "sample",
        "pizza"
      ],
      "endpoints": [
        {
          "name": "YourSandwichBotName",
          "protocol": "BotFrameworkV3",
          "description": "Default endpoint for the skill",
          "endpointUrl": "http://localhost:3981/api/messages",
          "msAppId": "YOUR Sandwich Bots MicrosoftAppId"
        }
      ],
      "activities": {
        "OrderSandwich": {
          "description": "Order a sandwich",
          "type": "message",
          "name": "OrderSandwich"
        }
      }
    }
    

    Para ver el esquema del manifiesto de aptitud, consulte skill-manifest-2.0.0.json.For the skill-manifest schema, see skill-manifest-2.0.0.json.

Creación del bot principal de la versión 4Create the v4 root bot

El bot principal simple consume las 3 aptitudes y le permite comprobar que los pasos de conversión funcionan según lo previsto.The simple root bot consumes the 3 skills and lets you verify that the conversion steps worked as planned. Este bot se ejecutará localmente en el puerto 3978.This bot will run locally on port 3978.

  1. En el archivo de configuración, agregue el identificador de aplicación y la contraseña del bot principal.To the configuration file, add the root bot's app ID and password. Para cada una de las aptitudes de la versión 3, agregue el identificador de aplicación de la aptitud.For each of the v3 skills, add the skill's app ID.

    V4SimpleRootBot\appsettings.jsonV4SimpleRootBot\appsettings.json

    {
      "MicrosoftAppId": "YOUR Root Skill Host bot's MicrosoftAppId",
      "MicrosoftAppPassword": "YOUR Root Skill Host bot's MicrosoftAppPassword",
      "SkillHostEndpoint": "http://localhost:3978/api/skills",
      "BotFrameworkSkills": [
        {
          "Id": "Echo",
          "AppId": "YOUR Echo bot's MicrosoftAppId",
          "SkillEndpoint": "http://localhost:3979/api/messages"
        },
        {
          "Id": "Pizza",
          "AppId": "YOUR Pizza bot's MicrosoftAppId",
          "SkillEndpoint": "http://localhost:3980/api/messages"
        },
        {
          "Id": "Sandwich",
          "AppId": "YOUR Sandwich bot's MicrosoftAppId",
          "SkillEndpoint": "http://localhost:3981/api/messages"
        }
      ]
    }
    

Prueba del bot raízTest the root bot

Descargue e instale la versión más reciente de Bot Framework Emulator.Download and install the latest Bot Framework Emulator.

  1. Compile y ejecute los cuatro bots localmente en el equipo.Build and run all four bots locally on your machine.
  2. Use el emulador para conectarse al bot principal.Use the Emulator to connect to the root bot.
  3. Pruebe las aptitudes y el consumidor de aptitudes.Test the skills and skill consumer.

Información adicionalAdditional information

Autenticación de bot a botBot-to-bot authentication

El bot raíz y la aptitud se comunican mediante HTTP.The root and skill communicate over HTTP. Este servicio usa tokens de portador e identificadores de aplicación de bot para comprobar la identidad de cada bot.The framework uses bearer tokens and bot application IDs to verify the identity of each bot. Usa un objeto de configuración de la autenticación para validar el encabezado de autenticación de las solicitudes entrantes.It uses an authentication configuration object to validate the authentication header on incoming requests. Puede agregar un validador de notificaciones a la configuración de autenticación.You can add a claims validator to the authentication configuration. Las notificaciones se evalúan después del encabezado de autenticación.The claims are evaluated after the authentication header. El código de validación debe producir un error o una excepción para rechazar la solicitud.Your validation code should throw an error or exception to reject the request.

El validador de notificaciones predeterminado lee la configuración de la aplicación AllowedCallers del archivo de configuración del bot.The default claims validator reads the AllowedCallers application setting from the bot's configuration file. Esta configuración debe contener una lista separada por comas de los identificadores de aplicación de los bots a los que se les permite llamar a la aptitud o "*" para permitir que todos los bots llamen a la aptitud.This setting should contain a comma separated list of the application IDs of the bots that are allowed to call the skill, or "*" to allow all bots to call the skill.

Para implementar un validador de notificaciones personalizado, implemente clases que deriven de AuthenticationConfiguration y ClaimsValidator; a continuación, haga referencia a la configuración de autenticación derivada en el atributo SkillBotAuthentication.To implement a custom claims validator, implement classes that derive from AuthenticationConfiguration and ClaimsValidator and then reference the derived authentication configuration in the SkillBotAuthentication attribute. Los pasos 3 y 4 de la sección Conversión del bot de eco tienen clases de validación de notificaciones de ejemplo.Steps 3 and 4 of the convert the echo bot section has example claims validation classes.