如何避免松弛命令超时错误?

时间:2016-01-20 10:04:30

标签: python slack-api slack

我正在使用slack命令(python代码在此后面运行),它工作正常,但这会给出错误

This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).

如何避免这种情况?

4 个答案:

答案 0 :(得分:34)

根据Slack slash command documentation,您需要在3000毫秒(3秒)内响应。如果您的命令需要更长时间,则会出现Timeout was reached错误。您的代码显然不会停止运行,但用户无法获得对其命令的任何响应。

对于您的命令可以即时访问数据的快速事情,可以使用三秒钟,但如果您要调用外部API或执行复杂操作,则可能不够长。如果您需要更长时间,请参阅文档中的延迟响应和多个响应部分:

  1. 验证请求没问题。
  2. 立即返回200响应,可能与{'text': 'ok, got that'}
  3. 一致
  4. 转到并执行您想要执行的实际操作。
  5. 在原始请求中,您会传递一个唯一的response_url参数。使用您的后续消息向该网址发出POST个请求:
    • Content-type需要application/json
    • 将正文作为JSON编码的消息:{'text': 'all done :)'}
    • 您可以返回短暂或渠道内的回复,并添加与即时方法相同的附件
  6. 根据文档,"您可以在用户调用的30分钟内响应用户命令最多5次。

答案 1 :(得分:8)

我也经常遇到这个错误:

  

“Darn - 斜杠命令不起作用(错误消息:Timeout was reached)。在slash-command管理命令”

我正在编写Slack slash-command "bot" on AWS Lambda,有时需要执行慢速操作(调用其他外部API等)。在某些情况下,Lambda函数需要大于3秒才会导致Slack出现Timeout was reached错误。

我在这里找到了@ rcoup的优秀答案,并将其应用于AWS Lambda的上下文中。该错误不再出现。

我用两个独立的Lambda函数做到了这一点。一个是“调度员”或“接待员”,它用“200 OK”迎接传入的Slack斜杠命令,并向用户返回简单的“Ok,got that”类型的消息。另一个是实际的“worker”Lambda函数,它以异步方式启动long-ish操作,并在稍后将该操作的结果发布到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 Function(虽然这是可能的)。我选择了use AWS SNS to publish a message that the worker receives and processes

基于this StackOverflow answer,这是更好的方法,因为它是非阻塞(异步)和可扩展的。此外,使用SNS在AWS Lambda的上下文中解耦这两个函数也更容易,直接调用对于这个用例来说更为棘手。

最后,这是我在工作人员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`

答案 2 :(得分:7)

在我自己处理这个问题并在Heroku上托管我的Flask应用程序后,我发现最简单的解决方案是使用线程。我按照这里的例子: 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"

答案 3 :(得分:-12)

创建线程来完成大部分工作并将响应返回到slack。您还可以在线程完成时返回响应。