Python 如何避免 slack 命令超时错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34896954/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to avoid slack command timeout error?
提问by Vikas Saini
I am working with slack command (python code is running behind this), it works fine, but this gives error
我正在使用 slack 命令(python 代码在此后面运行),它工作正常,但这会产生错误
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
How to avoid this ?
如何避免这种情况?
回答by rcoup
According to the Slack slash command documentation, you need to respond within 3000ms (three seconds). If your command takes longer then you get the Timeout was reached
error. Your code obviously won't stop running, but the user won't get any response to their command.
根据 Slack slash 命令文档,你需要在 3000ms(三秒)内响应。如果您的命令需要更长的时间,则会Timeout was reached
出现错误。您的代码显然不会停止运行,但用户不会收到对其命令的任何响应。
Three seconds is fine for a quick thing where your command has instant access to data, but might not be long enough if you're calling out to external APIs or doing something complicated. If you doneed to take longer, then see the Delayed responses and multiple responsessection of the documentation:
对于命令可以即时访问数据的快速操作,三秒钟是可以的,但如果您要调用外部 API 或执行一些复杂的操作,则可能不够长。如果您确实需要更长的时间,请参阅文档的延迟响应和多重响应部分:
- Validate the request is okay.
- Return a
200
response immediately, maybe something along the lines of{'text': 'ok, got that'}
- Go and perform the actual action you want to do.
- In the original request, you get passed a unique
response_url
parameter. Make aPOST
request to that URL with your follow-up message:Content-type
needs to beapplication/json
- With the body as a JSON-encoded message:
{'text': 'all done :)'}
- you can return ephemeral or in-channel responses, and add attachments the same as the immediate approach
- 验证请求是否正常。
200
立即返回响应,可能类似于{'text': 'ok, got that'}
- 去执行您想做的实际操作。
- 在原始请求中,您传递了一个唯一
response_url
参数。做一个POST
与你的后续消息网址:Content-type
需要是application/json
- 将正文作为 JSON 编码的消息:
{'text': 'all done :)'}
- 您可以返回临时或频道内响应,并添加与立即方法相同的附件
According to the docs, "you can respond to a user commands up to 5 times within 30 minutes of the user's invocation".
根据文档,“在用户调用后的 30 分钟内,您最多可以响应用户命令 5 次”。
回答by nonbeing
I too was facing this error frequently:
我也经常面临这个错误:
"Darn – that slash command didn't work (error message:
Timeout was reached
). Manage the command at slash-command"
“该死 - 那个斜杠命令不起作用(错误消息:)
Timeout was reached
。在 slash-command 管理命令”
I was writing a Slack slash-command "bot" on AWS Lambdathat sometimes needed to perform slow operations (invoking other external APIs etc). The Lambda function would take greater than 3 seconds in some cases causing the Timeout was reached
error from Slack.
我在 AWS Lambda 上编写了一个Slack 斜杠命令“bot”,有时需要执行缓慢的操作(调用其他外部 API 等)。在某些情况下,Lambda 函数会花费超过 3 秒的时间,从而导致Timeout was reached
Slack的错误。
I found @rcoup's excellent answer here and applied it in the context of AWS Lambda. The error doesn't appear any more.
我在这里找到了@rcoup 的出色答案,并将其应用于 AWS Lambda 的上下文中。错误不再出现。
I did this with two separate Lambda functions. One is a "dispatcher" or "receptionist" that greets the incoming Slack slash command with a "200 OK" and returns the simple "Ok, got that" type of message to the user. The other is the actual "worker" Lambda function that starts the long-ish operation asynchronously and posts the result of that operation to the Slack response_url
later.
我用两个单独的 Lambda 函数做到了这一点。一个是“调度员”或“接待员”,它用“200 OK”迎接传入的 Slack 斜线命令,并向用户返回简单的“好的,收到”类型的消息。另一个是实际的“worker”Lambda 函数,它异步启动 long-ish 操作并response_url
稍后将该操作的结果发布到 Slack 。
This is the dispatcher/receptionist Lambda function:
这是调度员/接待员 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
As you can see from the above, I didn't invoke the worker Lambda Function directly from the dispatcher (though this is possible). I chose to use AWS SNS to publish a message that the worker receives and processes.
从上面可以看出,我没有直接从调度程序调用工作程序 Lambda 函数(尽管这是可能的)。我选择使用 AWS SNS 发布工作人员接收和处理的消息。
Based on this StackOverflow answer, this is the better approach as it's non-blocking (asynchronous) and scalable. Also it was easier to use SNS to decouple the two functions in the context of AWS Lambda, direct invocation is trickier for this use-case.
基于这个 StackOverflow answer,这是更好的方法,因为它是非阻塞(异步)和可扩展的。此外,在 AWS Lambda 的上下文中使用 SNS 来分离两个函数更容易,直接调用对于这个用例来说更棘手。
Finally, here's how I consume the SNS event in my worker Lambda Function:
最后,这是我如何在我的工作程序 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`
回答by dom
After dealing with this issue myself and having my Flask app hosted on Heroku I found that the simplest solution was to use threading. I followed the example from here: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xi-email-support
在自己处理了这个问题并将 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")
All the slow heavy work is performed by the backgroundworker()
function. My slack command points to https://myappaddress.com/appmethodaddress
where the receptionist()
function takes the response_url
of the received Slack message and passes it alongside any other optional data to the backgroundworker()
. As the process is now split it simply returns the "working on your request"
message to your Slack channel pretty much instantly and upon completion backgroundworker()
sends the second message "your task is complete"
.
所有缓慢繁重的工作都由backgroundworker()
函数执行。我的松弛命令指向https://myappaddress.com/appmethodaddress
的其中receptionist()
函数采用response_url
接收到的时差信息的,并将其传递沿着任何其它任选的数据到backgroundworker()
。由于流程现在已拆分,因此它"working on your request"
几乎立即将消息返回到您的 Slack 频道,并在完成后backgroundworker()
发送第二条消息"your task is complete"
。