Ao começar esse post estava disposto apenas a apresentar novos packages do Microsoft Bot Framework no .NET Core, que endereçam o desenvolvimento de bots com .NET Core 1.1 e .NET Standard 1.4 e 1.6, já disponíveis em versão alpha no Nuget.org, no entanto como está no meu roadmap reconstruir, já pela 4ª vez, minha infraestrutura de bots, aproveitei para então entender um pouco o que esses assemblies trazem para poder já começar minha implementação. Aproveitei intervalos nesse domingo de páscoa e obtive ótimos resultados com os novos pacotes. Bots simples já podem usar esses novos pacotes sem problema algum.
Baseado no exemplo .NET Core and Microsoft Bot Framework que apresenta um tutorial que funciona exclusivamente para o Skype, pude ter uma visão do pipeline de autenticação e autorização no Bot Framework. Como também tenho exemplos em NodeJS, usei o Microsoft/BotBuilder como base para compreender o comportamento, fluxo de negócio e necessidades peculiares à integração com o Bot Framework. No início comecei desenhando um modelo anêmico que representasse as activities do Bot Framework. Em relação ao exemplo que citei acima, tive de fazer pequenas mudanças, principalmente no endereçamento da resposta da mensagem, que no Bot Framework 3 passa a trabalhar com endpoints novos.
Para que você entenda o fluxo, sempre que houver alguma iteração do usuário com seu chat, você receberá uma requisição HTTP (fica mais fácil testar usando NGrock), da mesma forma que quando você precisa devolver uma mensagem para o usuário, basta chamar uma API WEB do Bot Framework usando um simples POST enviando um objeto do mesmo schema daquele que você recebeu.
Após perder algum tempo construindo o meu modelo anêmico, e já conseguir receber e devolver mensagens para qualquer chat, passei algumas horas remoendo a existência de uma versão alpha do Microsoft Bot Framework no .NET Core, então resolvi abortar meus esforços para entender o que esses novos pacotes traziam de funcionalidade e optei por integrá-lo à minha plataforma e me tornar um early adoptor, em um momento em que sequer documentação existe. Bom, mas para minha sorte, o código fala!
Entendendo os Pacotes do Bot Framework
Há 10 dias os pacotes Microsoft.Bot.Connector.Common, Microsoft.Bot.Connector.AspNetCore e Microsoft.Bot.Connector.AspNetCore.Mvc são lançados sob a versão 3.6.0.0-alpha, junto com os demais pacotes já conhecidos. Embora sejam pacotes alpha podemos esperar alguma estabilidade visto que a Microsoft usa o MyGet como repositório intermediário em diversos de seus projetos. Assim, mesmo sendo um alpha, e não haver garantia alguma de retrocompatibilidade nas versões futuras, podemos esperar um mínimo de estabilidade.
Esse é um risco que estou disposto a correr, e posso pagar pelo eventual prejuízo causado por quebra de compatibilidade no futuro.
Ao inspecionar os 3 pacotes é possível ver as dependências do .NET Standard, e assim começar a planejar, por conseqüência, minhas próprias dependências. Dessa forma criei um projeto ASP.NET Core sob o .NET Core 1.1 e um projeto .NET Standard 1.6.1, destinado à arquitetura. Esse é só o início, no meu desenho final, tenho ainda outros projetos destinados a endereçar diversos aspectos de negócio.
Agora, com os projetos criados posso jogar fora minha iniciativa de recriar o modelo de entidades que representam o contrato de troca de mensagens com a plataforma de bots. Vamos passar a usar as classes presentes no próprio Microsoft.Bot.Connector.Common.
Nesse momento para começar configurar o Bot Framework no ASP.NET Core é necessário antes olhar cada um dos assemblies e entender suas responsabilidades e torcer para encontrar nomes e tipos que remetam a algo que já conheço no .NET Core. Por sorte minha ou por eficiência de design de quem criou esses assemblies, foi bem fácil por sinal.
Microsoft.Bot.Connector.Common
Logo de cara encontramos no assembly Microsoft.Bot.Connector.Common um domínio rico que expressa todos os dados de uma Activity, nesse ponto, por mais que não usemos seus métodos, só sua estrutura de dados (e mais a frente veremos que isso não é verdade, usarei sim), já podemos garantir que é algo consistente com uma versão atual ou futura do Bot Framework e com isso evitamos o desenvolvimento e sua manutenção. Basta seguir os updates do pacote para garantir que esse domínio se manterá consistente.
Microsoft.Bot.Connector.AspNetCore
O assembly possui míseras 6 classes, destinadas à configuração do .NET Core. Todas possuem nomes conhecidos, com sufixo Handler, Middleware, ServiceProvider, e outros nomes como AppBuilder, entre outros. Não é muito difícil endereçar cada um desses aspectos nas configurações do .NET Core, esse conteúdo está explícito em diversos materiais isolados e no site de documentação do .NET Core. Então fica fácil saber que por exemplo que precisamos usar o método UseBotAuthentication da extension BotAuthenticationAppBuilderExtensions no método Configure da classe Startup.
Microsoft.Bot.Connector.AspNetCore.Mvc
Para surpresa: Uma única classe, que é na verdade o atributo TrustServiceUrlAttribute.
Mão na massa
Agora que temos uma vaga ideia do que devemos fazer, é hora de entender o que é obrigatório e o que é opcional, esse é o aspecto mais trabalhoso na hora de analisar assemblies sem exemplos ou documentação. Por sorte uma série de exceptions são lançadas durante o processo o que nos dá um norte. A utilização de prefixos e sufixos que remetem a elementos da arquitetura do ASP.NET Core, são encontrados nesses assemblies como o caso de BotAuthenticationAppBuilderExtensions, BotAuthenticationHandler, BotAuthenticationMiddleware BotAuthenticationOptions, BotServiceProvider e BotServiceProviderExtensions, graciosamente 100% dos tipos contidos no assembly Microsoft.Bot.Connector.AspNetCore.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Bot.Connector; namespace Ebix.Wbot.API { public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); if (env.IsDevelopment()) { // This will push telemetry data through Application Insights // pipeline faster, allowing you to view results immediately. builder.AddApplicationInsightsSettings(developerMode: true); } Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(_ => this.Configuration); // Add framework services. services.AddMvc(options => { options.Filters.Add(typeof(TrustServiceUrlAttribute)); }); services.AddOptions(); services.AddMemoryCache(); services.UseBotConnector(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { //app.UseDeveloperExceptionPage(); } app.UseBotAuthentication(microsoftAppId: Configuration.GetValue<string>("Microsoft:BotFramework:Credentials:ClientId"), microsoftAppPassword: Configuration.GetValue<string>("Microsoft:BotFramework:Credentials:ClientSecret")); app.UseMvc(); } } }
Mas tive sorte, no meio da minha saga encontrei um sample que me ajudou a concluir as configurações, exceto pela ausência da linha 51 do exemplo acima. Infelizmente não há relato da necessidade de chamar essa extensão em lugar algum, mas recorrendo ao código é possível entender que ela muda o jogo. Sem ela seu endpoint recebe a chamada, mas não consegue responder a elas. O problema é relatado nas threads do github e stack oferflow, foi bom poder ajudar essa galera.
Outra característica não documentada e muito interessante que pude encontrar vasculhando os assemblies, e consequente seu fonte, é a interface Microsoft.Bot.Connector.ICredentialProvider que consiste em uma abstração para a obtenção das credenciais do Bot Framwork. Dessa forma torna-se fácil a tarefa de utilizar um único backend que consiga gerenciar mensagens de diversos Bots. Uma das features que mais me chamam a atenção, pois possibilita que você possa usar um repositório seu, seja SQL ou NoSQL para gerenciar esse tipo de informação.
public interface ICredentialProvider { Task<bool> IsValidAppIdAsync(string appId); Task<string> GetAppPasswordAsync(string appId); Task<bool> IsAuthenticationDisabledAsync(); }
Embora tenha implementado apenas um modelo simplista com um único bot, pretendo avaliar e começar a usar no dia-a-dia com diversos bots. Trocar escalabilidade vertical por horizontal faz bem a qualquer projeto.
Bom, por enquanto é isso. O sample vai te ajudar e você não precisará entrar nesses detalhes para fazer uma implementação de sucesso, no entanto achei bom documentar aqui, isso ajuda a entender como podemos compreender melhor o código dos outros, conhecer melhor a plataforma, enfim, crescer!
0 comentários