上篇文章介紹了什么是Activity,Turn,TurnContext和BotAdapter,這篇文章我們看看這些東西是如何竄起來的,他們是如何處理用戶發給bot的消息的。
我們以一個最簡單的bot,echo bot為例子,所謂的echo bot就是用戶發什么消息,它就照樣回復一條消息。為了簡單起見,大家可以先安裝VS2019的一個擴展插件BotBuilderVSIX.vsix template,然后創建一個NET core 3.1的Echo bot。
?
可以看到這個模板為什么創建了一個項目,我們先到Startup.cs看一下:
// 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, EchoBot>();
可以看到DI了兩個類,值得注意的是,AdapterWithErrorHandler
使用的是Singleton,而EchoBot
使用的是Transient,如果大家不同模板來生成的話,這兩個千萬不能寫錯,不然會出意想不到的錯誤,而且非常難查。
打開AdapterWithErrorHandler.cs
文件,可以看到它從BotFrameworkHttpAdapter
繼承下來。主要是提供了一些針對異常錯誤的處理
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{public AdapterWithErrorHandler(IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger): base(configuration, logger){OnTurnError = async (turnContext, exception) =>{...};}
}
從bot sdk的源代碼里,我可以知道?BotFrameworkHttpAdapter
?一層層往上,最終到達?BotAdapter
public class BotFrameworkHttpAdapter : BotFrameworkHttpAdapterBase, IBotFrameworkHttpAdapter
{...
}public class BotFrameworkHttpAdapterBase : BotFrameworkAdapter, IStreamingActivityProcessor
{...
}public class BotFrameworkAdapter : BotAdapter, IAdapterIntegration, IExtendedUserTokenProvider, IConnectorClientBuilder
{...
}public abstract class BotAdapter
{...
}
現在,我們結合下面這張圖來理解整個的處理過程。
?
- 可以看到,當用戶發了一條文字消息 “Hi”,這個消息被發到我們bot服務的時候,我們調用Adapter的
ProcessActivity
方法。我們在BotController.cs
可以看到這個。
[Route("api/messages")]
public class BotController : ControllerBase
{private readonly IBotFrameworkHttpAdapter Adapter;private readonly IBot Bot;public BotController(IBotFrameworkHttpAdapter adapter, IBot bot){Adapter = adapter;Bot = bot;}[HttpPost, HttpGet]public async Task PostAsync(){await Adapter.ProcessAsync(Request, Response, Bot);}
}
- Adapter創建TurnContext后,調用bot上的OnTurn方法,但是生成的Echo bot里并看不到OnTurn方法,我們先看一下
EchoBot.cs
public class EchoBot : ActivityHandler
{protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken){var replyText = $"Echo: {turnContext.Activity.Text}";await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);}
}
可以看到我們的EchoBot從ActivityHandler繼承下來,我們查看一下SDK的源代碼,可以發現:
public class ActivityHandler : IBot
{public virtual async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)){switch (turnContext.Activity.Type){case ActivityTypes.Message:await OnMessageActivityAsync(new DelegatingTurnContext<IMessageActivity>(turnContext), cancellationToken).ConfigureAwait(false);break;...}}protected virtual Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken){return Task.CompletedTask;}...
}
從上面sdk的源代碼就可以發現adapter調用了EchoBot的父類ActivityHandler
的OnTurnAsync()
方法,后者根據Activity的Type來調用到了EchoBot
的OnMessageActivityAsync
。
- 當我們在EchoBot里調用
SendActivityAsync()
回復一條消息,會由Adapter來調用Azure Bot Service。
大家可以在?微軟botbuilder-dotnet repo?里找到上面的源代碼。