我正在使用 slack 命令(Python 代码在其后面运行),它工作正常,但这会产生错误
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
如何避免这种情况?
根据 Slack slash 命令文档,需要在 3000ms(三秒)内做出响应。如果您的命令需要更长的时间,那么您会收到
Timeout was reached
错误。您的代码显然不会停止运行,但用户不会得到对其命令的任何响应。
对于命令可以即时访问数据的快速操作来说,三秒就足够了,但如果您调用外部 API 或执行复杂的操作,则可能不够长。如果您确实需要更长的时间,请参阅文档的延迟响应和多个响应部分:
200
回复,也许类似于 {'text': 'ok, got that'}
response_url
参数。向该 URL 发出 POST
请求并附上您的后续消息:Content-type
必须是 application/json
{'text': 'all done :)'}
根据文档,“您可以在用户调用后的 30 分钟内响应用户命令最多 5 次”。
在我自己处理这个问题并将我的 Flask 应用程序托管在 Heroku 上之后,我发现最简单的解决方案是使用线程。我按照这里的例子: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xi-email-support
from threading import Thread
def backgroundworker(somedata,response_url):
# your task
payload = {"text":"your task is complete",
"username": "bot"}
requests.post(response_url,data=json.dumps(payload))
@app.route('/appmethodaddress',methods=['POST','GET'])
def receptionist():
response_url = request.form.get("response_url")
somedata = {}
thr = Thread(target=backgroundworker, args=[somedata,response_url])
thr.start()
return jsonify(message= "working on your request")
所有缓慢繁重的工作都是由
backgroundworker()
功能完成的。我的 slack 命令指向 https://myappaddress.com/appmethodaddress
,其中 receptionist()
函数获取接收到的 Slack 消息的 response_url
,并将其与任何其他可选数据一起传递到 backgroundworker()
。由于流程现在已拆分,因此它几乎立即将 "working on your request"
消息返回到您的 Slack 频道,并在完成后 backgroundworker()
发送第二条消息 "your task is complete"
。
我也经常遇到这个错误:
“该死 - 斜杠命令不起作用(错误消息:
)。在斜杠命令中管理命令”Timeout was reached
我正在 AWS Lambda 上编写一个 Slack 斜杠命令“机器人”,有时需要执行缓慢的操作(调用其他外部 API 等)。在某些情况下,Lambda 函数将花费超过 3 秒的时间,从而导致 Slack 出现 Timeout was reached
错误。我在这里找到了 @rcoup 的优秀答案,并将其应用到 AWS Lambda 的上下文中。错误不再出现。
我使用两个单独的 Lambda 函数来完成此操作。一种是“调度员”或“接待员”,用“200 OK”迎接传入的 Slack 斜线命令,并向用户返回简单的“好的,明白了”类型的消息。另一个是实际的“worker”Lambda 函数,它异步启动较长的操作,并稍后将该操作的结果发布到 Slack
response_url
。这是调度员/接待员 Lambda 函数:
def lambda_handler(event, context):
req_body = event['body']
try:
retval = {}
# the param_map contains the 'response_url' that the worker will need to post back to later
param_map = _formparams_to_dict(req_body)
# command_list is a sequence of strings in the slash command such as "slashcommand weather pune"
command_list = param_map['text'].split('+')
# publish SNS message to delegate the actual work to worker lambda function
message = {
"param_map": param_map,
"command_list": command_list
}
sns_response = sns_client.publish(
TopicArn=MY_SNS_TOPIC_ARN,
Message=json.dumps({'default': json.dumps(message)}),
MessageStructure='json'
)
retval['text'] = "Ok, working on your slash command ..."
except Exception as e:
retval['text'] = '[ERROR] {}'.format(str(e))
return retval
def _formparams_to_dict(req_body):
""" Converts the incoming form_params from Slack into a dictionary. """
retval = {}
for val in req_body.split('&'):
k, v = val.split('=')
retval[k] = v
return retval
从上面可以看出,我没有直接从调度程序调用worker Lambda 函数(尽管这是可能的)。我选择
使用 AWS SNS 发布工作人员接收和处理的消息。
基于这个 StackOverflow 答案,这是更好的方法,因为它是非阻塞(异步)和可扩展的。此外,在 AWS Lambda 上下文中使用 SNS 来解耦两个函数更容易,对于此用例,直接调用更加棘手。
最后,这是我如何在我的工作人员 Lambda 函数中使用 SNS 事件:
def lambda_handler(event, context):
message = json.loads(event['Records'][0]['Sns']['Message'])
param_map = message['param_map']
response_url = param_map['response_url']
command_list = message['command_list']
main_command = command_list[0].lower()
# process the command as you need to and finally post results to `response_url`