我创建了一个名为picturesaver的机器人,使用微软的Bot框架,我添加了一个GroupMe频道,我将它托管在Azure中。机器人完美运行,将图片保存到Google云端硬盘。
然而,机器人提出错误说“服务错误:在15秒后POST到照片机超时”是否可以延长超时时间?甚至可以阻止机器人发布任何东西。这可能是Azure问题还是GroupMe问题?
答案 0 :(得分:1)
Bot Connector服务的超时时间为15秒,因此您需要确保在该时间范围内处理任何异步API调用,或者确保机器人在等待某些其他操作时响应某种消息完成。目前无法修改15秒超时。
答案 1 :(得分:1)
在另一个线程上处理消息的解决方案,并立即确认呼叫仅适用于App Service上的机器人。
但至于一个函数机器人这样做会在我立即从这个方法返回时完成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模拟器和云部署的功能应用程序。否则,从真实客户发送到机器人的消息可能会在本地处理你正在机器上调试)
当然,可以通过直接调用另一个Azure功能的公共网址https://<my-bot>.azurewebsites.net/api/processTheMessage?code=<function-secret>
来完成相同的操作,而不使用Azure队列。此调用必须在另一个线程上完成,而不等待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);
}
答案 2 :(得分:0)
如果您的机器人执行的处理时间超过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秒超时。