如何避免Slack命令超时错误?

2024-05-12 22:45:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用slack命令(后面运行的是python代码),它工作得很好,但这会产生错误

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

如何避免这种情况?


Tags: 代码命令错误timeouterrorthisslackcommand
3条回答

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

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

  1. 验证请求是否正确。
  2. 立即返回一个200响应,可能是{'text': 'ok, got that'}行中的内容
  3. 去做你想做的实际行动。
  4. 在最初的请求中,传递了一个唯一的response_url参数。使用后续消息向该URL发出POST请求:
    • Content-type需要application/json
    • 将正文作为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"消息返回到松弛通道,完成后backgroundworker()发送第二条消息"your task is complete"

我也经常面临这个错误:

"Darn – that slash command didn't work (error message: Timeout was reached). Manage the command at slash-command"

我正在写一个Slack slash-command "bot" on AWS Lambda,有时需要执行缓慢的操作(调用其他外部api等)。在某些情况下,Lambda函数需要3秒以上的时间,这会导致Slack产生Timeout was reached错误。

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

我是用两个独立的Lambda函数来实现的。一个是“dispatcher”或“receptor”,它用“200ok”来迎接输入的Slack slash命令,并向用户返回简单的“OK,got that”类型的消息。另一个是实际的“worker”Lambda函数,它异步启动长ish操作,并在稍后将该操作的结果发布到Slackresponse_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函数(尽管这是可能的)。我选择了use AWS SNS to publish a message that the worker receives and processes

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

最后,我将在worker 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`

相关问题 更多 >