Teams Bot开发系列:Teams的Activity处理

    科技2022-08-11  107

    上一篇文章讲了activity处理的流程,我们bot的核心处理逻辑放在ActivityHandler的子类里,通过重载OnMessageActivityAsync()方法来实现。

    这篇文章我来讲一下对于Teams的bot来说,整个处理的逻辑会有哪些不同点。

    通过之前的文章,大家应该已经知道,Teams bot是Azure bot service支持的众多bot聊天平台里的一种channel(注意:这里的channel指bot service里的channel,和Teams里的channel是完全不同的概念)。但是Teams实际上提供了很多特有的事件和动作。使用bot sdk的通用模型,我们当然可以处理这些事情,但是Teams作为微软的主打产品,微软的bot sdk当然要为它提供更多的开发便利性。

    SDK提供了一个针对Teams的ActivityHandler。这个handler有下面这些特殊的ConversationUpdateActivity的处理函数

    事件函数说明channelCreatedOnTeamsChannelCreatedAsync当Teams的channel被创建channelDeletedOnTeamsChannelDeletedAsync当Teams的channel被删除channelRenamedOnTeamsChannelRenamedAsync当Teams的channel被重命名teamRenamedOnTeamsTeamRenamedAsync当Teams的一个team被重命名MembersAddedOnTeamsMembersAddedAsync当Teams的一个team中有新用户加入MembersRemovedOnTeamsMembersRemovedAsync当Teams的一个team中有用户被移除

    除了ConversationUpdateActivity这些Teams的特殊事件,handler还提供了一些Teams特有的invoke动作的处理

    Invoke类型函数说明CardAction.InvokeOnTeamsCardActionInvokeAsync关于卡片的动作,比如卡片上一个按钮被点击了fileConsent/invokeOnTeamsFileConsentAcceptAsync用户同意了上传文件fileConsent/invokeOnTeamsFileConsentAsync用户要上传文件.fileConsent/invokeOnTeamsFileConsentDeclineAsync用户拒绝了上传文件.actionableMessage/executeActionOnTeamsO365ConnectorCardActionAsyncO365连接器的卡片动作signin/verifyStateOnTeamsSigninVerifyStateAsync登入验证状态task/fetchOnTeamsTaskModuleFetchAsyncTeams的Task Module的获取task/submitOnTeamsTaskModuleSubmitAsyncTeams的Task Module的提交

    上面表格中的OnTeamsFileConsentAsync实际上是OnTeamsFileConsentAcceptAsync和OnTeamsFileConsentDeclineAsync的一个综合处理,你可以重载OnTeamsFileConsentAsync,或者分别重载 accept 和 decline 函数。下面的sdk代码可以让你有直观的了解

    protected virtual async Task<InvokeResponse> OnTeamsFileConsentAsync(ITurnContext<IInvokeActivity> turnContext, FileConsentCardResponse fileConsentCardResponse, CancellationToken cancellationToken) { switch (fileConsentCardResponse.Action) { case "accept": await OnTeamsFileConsentAcceptAsync(turnContext, fileConsentCardResponse, cancellationToken).ConfigureAwait(false); return CreateInvokeResponse(); case "decline": await OnTeamsFileConsentDeclineAsync(turnContext, fileConsentCardResponse, cancellationToken).ConfigureAwait(false); return CreateInvokeResponse(); default: throw new InvokeResponseException(HttpStatusCode.BadRequest, $"{fileConsentCardResponse.Action} is not a supported Action."); } }

    对于喜欢把问题研究透彻的朋友可能会问,Teams的ActivityHandler到底是怎么处理的?让我们跳入sdk源代码一探究竟。

    public class TeamsActivityHandler : ActivityHandler { protected override async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken) { ... switch (turnContext.Activity.Name) { case "fileConsent/invoke": return await OnTeamsFileConsentAsync(turnContext, SafeCast<FileConsentCardResponse>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false); case "task/fetch": return CreateInvokeResponse(await OnTeamsTaskModuleFetchAsync(turnContext, SafeCast<TaskModuleRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false)); case "task/submit": return CreateInvokeResponse(await OnTeamsTaskModuleSubmitAsync(turnContext, SafeCast<TaskModuleRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false)); ...... default: return await base.OnInvokeActivityAsync(turnContext, cancellationToken).ConfigureAwait(false); } ... } protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) { ... switch (channelData.EventType) { case "channelCreated": return OnTeamsChannelCreatedAsync(channelData.Channel, channelData.Team, turnContext, cancellationToken); case "channelDeleted": return OnTeamsChannelDeletedAsync(channelData.Channel, channelData.Team, turnContext, cancellationToken); case "channelRenamed": return OnTeamsChannelRenamedAsync(channelData.Channel, channelData.Team, turnContext, cancellationToken); case "teamRenamed": return OnTeamsTeamRenamedAsync(channelData.Team, turnContext, cancellationToken); default: return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken); } ... } }

    从上面的代码里可以看到没有什么特别的magic,TeamsActivityHandler重载了OnConversationUpdateActivityAsync,并且根据channelData.EventType判断出不同teams的事件,然后调用相应的方法。对于invoke也类似,重载了OnInvokeActivityAsync,根据turnContext.Activity.Name来调用不同的方法。

    回到我们的EchoBot代码,让EchoBot从TeamsActivityHandler继承下来,然后我们可以添加OnTeamsChannelRenamedAsync方法。把EchoBot设置到Teams里,修改安装了EchoBot的channel的名字,就可以看到这个方法被促发的。

    public class EchoBot : TeamsActivityHandler { protected virtual Task OnTeamsChannelRenamedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) { var replyText = "Channel renamed."; await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken); } }
    Processed: 0.010, SQL: 9