Echo Bot .NET Core 3
Tags: bot, ai, netcore, botframework
Published: 2019-11-27 00:02:11

ASP.NET Core 3.x ha llegado con novedades muy interesantes, con el fin de aprovecharlas también en Bot Framework, se ha creado este Echo Bot que nos permitirá comenzar desde el típico Echo Bot ya migrado a la nueva versión del framework.
Es importante que hayamos actualizado recientemente VS, ya que es a partir de la versión 16.3 de Visual Studio 2019 cuando .NET Core 3.x está soportado.

Clona [este](https://github.com/esalcedoo/EchoBotNETCore3) proyecto.

Como resultado tendremos una solución como la que se muestra abajo. Veremos uno a una las clases que nos vienen dadas.
![Solution explorer pointing at 4 classes we are going to explain next](https://dev.azure.com/esalcedoo/a9ddfdb1-0226-4f4f-a89d-42e9d69b4f3b/_apis/git/repositories/66d8fc7f-f7ae-4b26-a4aa-03920094c86a/Items?path=%2F/.attachments/image-5f490f7b-a8b0-4c88-90c3-a7dfec71aee0.png)
## 1.	Program
El HostBuilder se crea instanciando la clase startup; se construye y se levanta.
``` C
public static void Main(string[] args)
{
            CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
            webBuilder.UseStartup<Startup>();
});
```
## 2.	Startup
El bot no es más que una API, la aplicación se describe en base a las configuraciones que creamos en la clase Startup.
En el método ConfigureServices se registra como transient el EchoBot como implementación de IBot; y se registra como Singleton el AdapterWithErrorHandler como implementación de IBotFrameworkAdapter.

``` C
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot,Bot>();
```
Detallamos ambas implementaciones más adelante.
En el método Configure al haber migrado a ASP.NET Core 3.x, ahora se usa el middleware de routing, encargado de analizar la ruta de la petición http que nos llegará.
Actualmente no hay necesidad de cargar el middleware completo de MVC; podremos mapear endpoints, y la acción que se ejecutará cuando la petición http ataque esa ruta.
``` C
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.Map("api/messages", async context =>
    {
        var bot = context.RequestServices
                    .GetRequiredService<IBot>();
        var adapter = context.RequestServices
                    .GetRequiredService<IBotFrameworkHttpAdapter>();

        await adapter.ProcessAsync(context.Request, context.Response, bot);
    });
});
```
En este caso, estamos diciendo que cuando recibamos una petición en el endpoint “..api/messages”, pidamos al adapter que procese esa petición.
Es necesario que el endpoint sea api/messages, si queremos utilizar el Bot Service, el endpoint ha de ser exactamente ese.
## 3.	Adapter With Error Handler
El BotFrameworkHttpAdapter es el encargado de interconectar el Bot Service con nuestro bot. A grandes rasgos, será el nexo entre nuestra aplicación de bot y los canales en los que está publicado.
La implementación AdapterWithErrorHandler del adapter, extiende su funcionalidad para informar al usuario de que ha habido un error cada vez que se produzca una excepción no controlada.
## 4.	Bot
EchoBot hereda de ActivityHandler y ésta implementa la interfaz IBot.
El activity handler nos permite sobreescribir varios métodos que se corresponderán con diferentes situaciones con las que nos encontraremos con el usuario.
En este caso, estamos manejando dos de estas situaciones:
Cuando alguien se une a la conversación, si no es el bot (Recipient), se imprime el saludo:
``` C
protected override async Task OnMembersAddedAsync(
    IList<ChannelAccount> membersAdded, 
    ITurnContext<IConversationUpdateActivity> turnContext, 
    CancellationToken cancellationToken)
{
    foreach (var member in membersAdded)
    {
        if (member.Id != turnContext.Activity.Recipient.Id)
        {
            await turnContext.SendActivityAsync(
                MessageFactory.Text($"Welcome CommitConf2019!"),
                cancellationToken);
        }
    }
}
```
Cuando se recibe un mensaje, en esta versión de echo bot, le devolveremos al usuario exactamente lo mismo que ha dicho:
``` C
protected override async Task OnMessageActivityAsync (
    ITurnContext<IMessageActivity> turnContext, 
    CancellationToken cancellationToken)
{
    await turnContext.SendActivityAsync(
        MessageFactory.Text($"Echo: {turnContext.Activity.Text}"),
        cancellationToken);
}
```