上篇文章介紹了什麼是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裡調用
回複一條消息,會由Adapter來調用Azure Bot Service。SendActivityAsync()
大家可以在 微軟botbuilder-dotnet repo 裡找到上面的源代碼。