向 Microsoft Teams 发送对话主动消息时,clientSecret 不能为空

clientSecret can not be null while sending a conversation pro-active message to Microsoft Teams

当链接到 Microsoft Teams 的机器人更改某些内容时,我会发送 通知主动消息。该演示仅使用 (为简单起见) 机器人网站上的一个 API 端点。

开始对话后,我将对话中的对话存储到一个名为 MemoryVariables.ConversationReferences ConcurrentDictionary<string, ConversationReference> 的类型) 的 属性 ].

当我浏览到 API 端点时,我看到没有消息,但是我收到了 200 响应。在查找错误时,我在标记行上添加了一个断点。

public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
    public AdapterWithErrorHandler(IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger : base(configuration, logger)
    {
        OnTurnError = async (turnContext, exception) =>
        {
            logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); // <-- breakpoint added here
        };
    }
}

我看到我有下一个例外:

System.ArgumentNullException: Value cannot be null.
Parameter name: clientSecret

这是堆栈跟踪:

   at Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential..ctor(String clientId, String clientSecret)
   at Microsoft.Bot.Connector.Authentication.MicrosoftAppCredentials.<BuildAuthenticator>b__14_0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
--- End of stack trace from previous location where exception was thrown ---
   at System.Lazy`1.CreateValue()
   at Microsoft.Bot.Connector.Authentication.AppCredentials.GetTokenAsync(Boolean forceRefresh)
   at Microsoft.Bot.Connector.Authentication.AppCredentials.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Bot.Connector.Conversations.ReplyToActivityWithHttpMessagesAsync(String conversationId, String activityId, Activity activity, Dictionary`2 customHeaders, CancellationToken cancellationToken)
   at Microsoft.Bot.Connector.ConversationsExtensions.ReplyToActivityAsync(IConversations operations, String conversationId, String activityId, Activity activity, CancellationToken cancellationToken)
   at Microsoft.Bot.Builder.BotFrameworkAdapter.SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
   at Microsoft.Bot.Builder.TurnContext.<>c__DisplayClass22_0.<<SendActivitiesAsync>g__SendActivitiesThroughAdapter|1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Bot.Builder.TurnContext.SendActivityAsync(IActivity activity, CancellationToken cancellationToken)
   at MyDemoBot.Bot.Controllers.NotifyController.BotCallback(ITurnContext turnContext, CancellationToken cancellationToken) in C:\MyDemoBot\Savaco.KBESAVAC.NotificationBot.Bot\Controllers\NotifyController.cs:line 59
   at Microsoft.Bot.Builder.BotFrameworkAdapter.TenantIdWorkaroundForTeamsMiddleware.OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
   at Microsoft.Bot.Builder.MiddlewareSet.ReceiveActivityWithStatusAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
   at Microsoft.Bot.Builder.BotAdapter.RunPipelineAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)

下面是我的NotifyController,在标记行上抛出异常。

[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
    public async Task<IActionResult> Get()
    {
        MemoryVariables memoryVariables = MemoryVariables.GetInstance();
        List<Task> taskList = new List<Task>();
        ICollection<ConversationReference> conversationReferences = memoryVariables.ConversationReferences.Values;

        foreach (ConversationReference conversationReference in conversationReferences)
        {
            taskList.Add(memoryVariables.BotAdapter.ContinueConversationAsync(
                conversationReference.Bot.Id,
                conversationReference,
                BotCallback,
                new CancellationToken()
            ));
        }

        await Task.WhenAll(taskList); // <-- exceptions throws on this line code

        return GetContentResult(new { messagesSend = conversationReferences.Count }, HttpStatusCode.OK);
    }

    private ContentResult GetContentResult(object content, HttpStatusCode httpStatusCode)
    {
        return new ContentResult()
        {
            Content = JsonConvert.SerializeObject(content),
            ContentType = "application/json",
            StatusCode = (int)httpStatusCode
        };
    }

    private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
    {
        await turnContext.SendActivityAsync(
            MessageFactory.SuggestedActions(
                new string[] { "Create A", "Create B" },
                "Hi, you've received a notification. Choose an action from below to continue."
            ),
            cancellationToken
        );
    }
}

我是否遗漏了我的代码中的任何内容,或者我是否遗漏了有关 Microsoft Teams 的任何特定内容?使用 localhost 和 Bot Framework Emulator,我没有遇到任何问题。

所以这里有一些事情需要考虑。首先,如果您的应用程序出于任何原因回收,将其存储在内存中是一个问题 - 您可能希望更持久地存储它(例如数据库)。此外,我建议阅读我在 How Bots Calls Actually Work 上的博客 post(新的博客引擎,所以现在有点稀疏),因为它有助于解释您目前遗漏的部分。

本质上,如果您不直接回复现有消息,并且想在 稍后 从您的 bot 发送消息,则它被称为"pro-active messaging" 为此,您需要的不仅仅是对话参考,还需要一些其他信息。我已经在 .

上通过链接和示例代码对此进行了更多描述

希望对你有帮助