首页 > 解决方案 > ITurnContext turnContext 在使用 MS Proactive 示例中的 NotifyController 成功进行主动消息传递 1 小时后抛出 nullreference 错误

问题描述

我正在使用 microsoft-bot-framework 和使用 NotifyController 的主动示例。https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages

到目前为止一切顺利,我可以向用户发送主动消息: 在 Teams 聊天中收到邮递员电话 + 消息

问题:turnContext 抛出 nullref error,但仅在约 1 小时后。如果用户再次与机器人聊天,则没有问题,并且 turnContext 是已知的。

在 ((BotAdapter)_adapter) 处引发 nullref 错误。ContinueConversationAsync...

namespace ProactiveBot.Controllers
{
    [Route("api/notify")]
    [ApiController]
    public class NotifyController : ControllerBase
    {
        private readonly IBotFrameworkHttpAdapter _adapter;
        private readonly string _appId;

        public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
        {
            _adapter = adapter;
            _appId = configuration["MicrosoftAppId"] ?? string.Empty;
        }

        [HttpPost]
        public async Task<IActionResult> Post([FromBody] ChatbotResponse response)
        {
            // Set basic reply
            var reply = MessageFactory.Text(response.Text);

            if(response?.Buttons?.Count > 0) // a reply with buttons
            {
                reply = SendSuggestedActions(response.Text,default,response?.Buttons);
            }

            // Target a specific user
            if (response.Recipient_id != string.Empty)
            {
                var recipient = UserCollection.UserSession.FirstOrDefault(x => x.Value.ID == response.Recipient_id).Value;
                await ((BotAdapter)_adapter).ContinueConversationAsync(_appId,recipient.ConversationReference,(ITurnContext turnContext,CancellationToken cancellationToken) => turnContext.SendActivityAsync(reply),default);

错误日志

2021-03-31 06:22:07.559 +00:00 [Error] Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer: Connection ID "(xxxxx)", Request ID "(xxxxxx)": An unhandled exception was thrown by the application.System.NullReferenceException: Object reference not set to an instance of an object.at ProactiveBot.Controllers.NotifyController.Post(ChatbotResponse response) in C:\xxxxx\xxxxxxxx\workspace\microsoft-bot-framework\CoreBot\Controllers\NotifyController.cs:line 51at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

------------- 更新:替代方法 -------------

await ((BotAdapter)_adapter).ContinueConversationAsync(_appId,recipient.ConversationReference,async (context,token) =>
                    await BotCallback(activity,context,token),default(CancellationToken));

BotCallback 是

private async Task BotCallback(Activity activity, ITurnContext turnContext, CancellationToken token)
        {
            var response = await turnContext.SendActivityAsync(activity);
        }

抛出相同的错误。困惑为什么 turnContext 在第一个小时有效,但一个多小时后为空。

标签: c#botframeworkmicrosoft-teams

解决方案


好的,正如承诺的那样。以下是我如何主动向正在工作的用户发送消息...

你需要什么:

  • Teams 中的用户 ID(userId在以下示例中)。您可以通过之前的交互或仅通过查询用户列表 ( GetConversationPagedMembersAsync) 获得此信息。
  • 团队的租户 ID(tenantId在下面的示例中)。当用户最初安装我们的机器人时,我会收集这些信息并将其存储在我们的数据库中。请注意,它显然会随着时间而改变,所以我会定期再次检查它。

编码:

// we only need the user's ID, which we'll get from the DB
var user = new ChannelAccount(userId);

var conversationParams = new ConversationParameters
{
    IsGroup = false,
    Members = new List<ChannelAccount>
    {
        user
    },
    TenantId = tenantId
};

try
{
    var conversation = await _client.Conversations.CreateConversationAsync(conversationParams);
    message.Conversation = new ConversationAccount(id: conversation.Id);

    var response = await _client.Conversations.SendToConversationAsync(message);
    return new MessageReference
    {
        ConversationId = conversation.Id,
        MessageId = response.Id
    };
}
catch (Exception ex)
{
    // log, or whatever
}

请注意,首先创建对话似乎很重要,然后才调用SendToConversationAsync,这与消息通道不同,您似乎可以将消息活动作为ConversationParameters.

这就是我进行所有主动消息传递的方式。我根本不使用ITurnContext,除非我响应用户发起的消息,例如当他们第一次将机器人安装到他们的频道/个人范围时。


推荐阅读