Bot Framework 发送不必要的错误消息

Posted

技术标签:

【中文标题】Bot Framework 发送不必要的错误消息【英文标题】:Bot Framework Sending Unnecessary Error Messages 【发布时间】:2018-06-03 06:51:12 【问题描述】:

我使用 Microsoft 的 Bot Framework 创建了一个名为 picturesaver 的机器人,我添加了一个 GroupMe 频道,并将它托管在 Azure 中。该机器人完美运行,将图片保存到 Google 云端硬盘。

但是,机器人给出了一个错误提示“服务错误:POST 到图片保护程序在 15 秒后超时”是否可以延长超时时间?或者甚至阻止机器人发布任何内容。这可能是 Azure 问题还是 GroupMe 问题?

【问题讨论】:

是图片太大,还是网速太慢,15秒内无法上传图片? 【参考方案1】:

如果您的机器人执行的操作处理消息的时间超过 15 秒,您可以在另一个线程上处理该消息,并立即确认调用。比如:

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)

    if (activity.Type == ActivityTypes.Message)
    
        if ([determine if this will take > 15s]) 
        
            // process the message asyncronously
            Task.Factory.StartNew(async () => await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()));
        
        else
        
            //process the message normally
            await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
        
    

    return Request.CreateResponse(HttpStatusCode.OK); //ack the call

这将避免连接器和机器人之间的 15 秒超时。


编辑:以上内容无法扩展,只是使用了 Task.Factory。请参阅https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-long-operations-guidance,了解有关处理来自机器人的长操作的推荐指南。

【讨论】:

该解决方案仅适用于应用服务上的机器人。但是对于 Functions Bot,如果我立即从此方法返回,这样做将完成 Azure Function。请参阅下面的解决方案。 是的,当然可以(取决于场景)。关键是,尽快确认呼叫并单独处理传入的消息。【参考方案2】:

在另一个线程上处理消息并立即确认调用的解决方案仅适用于应用服务上的机器人

但是对于 a Functions Bot,如果我立即从该方法返回,这样做将完成 Azure Function。

我试过了。 Azure 函数停止运行,对聊天的真正响应永远不会到来。所以这根本不是功能机器人的解决方案。

我最终得到了函数机器人的这段代码,它解决了这个问题。

使用 Azure 队列

public static class Functions

    [FunctionName("messages")]
    [return: Queue("somequeue")]
    public static async Task<MessagePayload> Messages([HttpTrigger
            (WebHookType = "genericJson")]HttpRequestMessage req) =>
        // return from this Azure Function immediately to avoid timeout warning message 
        // in the chat.
        // just put the request into "somequeue". 
        // We can't pass the whole request via the Queue, so pass only what we need for 
        // the message to be processed by Bot Framework
        new MessagePayload
        
            RequestUri = req.RequestUri,
            Content = await req.Content.ReadAsStringAsync(),
            AuthScheme = req.Headers.Authorization.Scheme,
            AuthParameter = req.Headers.Authorization.Parameter
        ;

    // Do the actual message processing in another Azure Function, which is 
    // triggered by a message enqueued in the Azure Queue "somequeue"
    [FunctionName("processTheMessage")]
    public static async Task ProcessTheMessage([QueueTrigger("somequeue")]
        MessagePayload payload, TraceWriter logger)
    
        // we don't want the queue to process this message 5 times if it fails, 
        // so we won't throw any exceptions here at all, but we'll handle them properly.
        try
        
            // recreate the request
            var request = new HttpRequestMessage
            
                Content = new StringContent(payload.Content),
                RequestUri = payload.RequestUri
            ;
            request.Headers.Authorization = new  
                AuthenticationHeaderValue(payload.AuthScheme, payload.AuthParameter);

            // initialize dependency injection container, services, etc.
            var initializer = new SomeInitializer(logger);
            initializer.Initialize();

            // handle the request in a usual way and reply back to the chat
            await initializer.HandleRequestAsync(request);
        
        catch (Exception ex)
        
            try
            
                // TODO: handle the exception
            
            catch (Exception anotherException)
            
                // swallow any exceptions in the exceptions handler?
            
        
    



[Serializable]
public class MessagePayload

    public string Content  get; set; 
    public string AuthParameter  get; set; 
    public string AuthScheme  get; set; 
    public Uri RequestUri  get; set; 

(请确保使用不同的 Azure 队列 使用 Bot Framework 模拟器进行本地开发和云部署的 Function App。否则,从真实客户发送到您的机器人的消息可能会在本地处理,而你正在你的机器上调试)

使用 HTTP 请求

当然,不使用 Azure 队列也可以直接调用另一个 Azure 函数的公共 URL - https://&lt;my-bot&gt;.azurewebsites.net/api/processTheMessage?code=&lt;function-secret&gt;。此调用必须在另一个线程上完成,而无需等待 messages 函数中的结果。

[FunctionName("messages")]
public static async Task Run([HttpTrigger(WebHookType = "genericJson")]
    HttpRequestMessage req)

    // return from this Azure Function immediately to avoid timeout warning message 
    // in the chat.
    using (var client = new HttpClient())
    
        string secret = ConfigurationManager.AppSettings["processMessageHttp_secret"];
        // change the RequestUri of the request to processMessageHttp Function's 
        // public URL, providing the secret code, stored in app settings 
        // with key 'processMessageHttp_secret'
        req.RequestUri = new Uri(req.RequestUri.AbsoluteUri.Replace(
            req.RequestUri.PathAndQuery, $"/api/processMessageHttp?code=secret"));

        // don't 'await' here. Simply send.
#pragma warning disable CS4014
        client.SendAsync(req);
#pragma warning restore CS4014

        // wait a little bit to ensure the request is sent. It will not 
        // send the request at all without this line, because it would 
        // terminate this Azure Function immediately
        await Task.Delay(500);
    


[FunctionName("processMessageHttp")]
public static async Task ProcessMessageHttp([HttpTrigger(WebHookType = "genericJson")]
    HttpRequestMessage req,
    Microsoft.Extensions.Logging.ILogger log)

    // first and foremost: initialize dependency 
    // injection container, logger, services, set default culture/language, etc.
    var initializer = FunctionAppInitializer.Initialize(log);

    // handle the request in a usual way and reply back to the chat
    await initializer.HandleRequest(req);

【讨论】:

现在有了 Azure Durable Functions【参考方案3】:

机器人连接器服务有 15 秒的超时时间,因此您需要确保在该时间范围内处理任何异步 API 调用,或者确保您的机器人在等待其他操作完成时以某种消息进行响应。目前无法修改 15s 超时时间。

【讨论】:

以上是关于Bot Framework 发送不必要的错误消息的主要内容,如果未能解决你的问题,请参考以下文章

如何获取从 Bot Framework Composer 发送的消息的 ResourceResponse.Id

使用 Bot Framework V4 在 Teams 中发送主动式 1:1 消息

传出 Skype 消息不适用于 Bot Framework

在 context.postAsync() 和发送打字活动时,任务被取消错误 Bot Framework

Microsoft Bot Framework - 使用 ngrok 在本地构建和调试客户端

Discord bot 发送消息特定通道错误