Teams Bot开发系列:Activity处理流程
上篇文章介绍了什么是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 里找到上面的源代码。