Как избежать ошибки тайм-аута команды slack?

Я работаю с командой slack (за этим работает код Python), он работает нормально, но выдает ошибку

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

Как этого избежать?


person Vikas Saini    schedule 20.01.2016    source источник


Ответы (3)


Согласно документации по команде Slack, вам нужно ответить в течение 3000 мс (трех секунд). Если ваша команда занимает больше времени, вы получаете ошибку Timeout was reached. Ваш код, очевидно, не перестанет работать, но пользователь не получит никакого ответа на свою команду.

Трех секунд достаточно для быстрой работы, когда ваша команда имеет мгновенный доступ к данным, но может быть недостаточно, если вы обращаетесь к внешним API или делаете что-то сложное. Если вам действительно нужно больше времени, см. раздел документации Отложенные ответы и множественные ответы:

  1. Подтвердите, что запрос в порядке.
  2. Немедленно верните ответ 200, возможно, что-то вроде {'text': 'ok, got that'}
  3. Идите и выполните фактическое действие, которое вы хотите сделать.
  4. In the original request, you get passed a unique response_url parameter. Make a POST request to that URL with your follow-up message:
    • Content-type needs to be application/json
    • С телом сообщения в формате JSON: {'text': 'all done :)'}
    • вы можете возвращать эфемерные или внутриканальные ответы и добавлять вложения так же, как и при немедленном подходе.

Согласно документам, «вы можете отвечать на команды пользователя до 5 раз в течение 30 минут после вызова пользователя».

person rcoup    schedule 22.01.2016
comment
Я просто использую оператор возврата для отправки ответа пользователю. Как отправить несколько ответов в python? - person Vikas Saini; 22.01.2016
comment
@abc, к сожалению, вам нужно усложнить свой код - возможно, с помощью чего-то вроде Celery. Какой веб-сервер/фреймворк/среду вы используете? - person rcoup; 22.01.2016
comment
@rcup есть идеи, чего мне здесь не хватает? stackoverflow.com/questions/36195924/ - person Vimalnath; 25.03.2016

После того, как я сам разобрался с этой проблемой и разместив свое приложение 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() берет response_url полученного сообщения Slack и передает его вместе с любыми другими необязательными данными в backgroundworker(). Поскольку процесс теперь разделен, он просто возвращает сообщение "working on your request" в ваш канал Slack почти мгновенно, а по завершении backgroundworker() отправляет второе сообщение "your task is complete".

person dom    schedule 26.08.2017
comment
Я сделал это, и backgroundoworker сработал, но проблема в том, что он не отправил ответ обратно на тот же канал, а просто распечатал результаты в колбе. Итак, как я могу отправить фоновый ответ на тот же слабый канал? - person NANIS; 10.01.2020
comment
Как без thread.join обеспечить завершение потока, как только завершится требуемое действие? - person zcahfg2; 26.04.2021

Я тоже часто сталкивался с этой ошибкой:

«Черт возьми, эта косая черта не сработала (сообщение об ошибке: Timeout was reached). Управляйте командой через косую черту»

Я писал Slack-command "bot " на AWS Lambda, который иногда требовал выполнения медленных операций (вызов других внешних API и т. д.). В некоторых случаях функция Lambda занимала более 3 секунд, вызывая ошибку Timeout was reached от Slack.

Я нашел здесь отличный ответ @rcoup и применил его в контексте AWS Lambda. Ошибка больше не появляется.

Я сделал это с двумя отдельными функциями Lambda. Одним из них является «диспетчер» или «регистратор», который приветствует входящую команду Slack с косой чертой «200 OK» и возвращает пользователю простое сообщение типа «Хорошо, понял». Другая — это фактическая «рабочая» лямбда-функция, которая асинхронно запускает длинную операцию и отправляет результат этой операции в Slack response_url позже.

Это лямбда-функция диспетчера/регистратора:

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

Как видно из вышеизложенного, я не вызывал рабочую лямбда-функцию напрямую из диспетчера (хотя это возможно). Я решил использовать AWS SNS для публикации сообщения, которое получает рабочий процесс, и процессы.

Основываясь на этом ответе StackOverflow, это лучший подход, поскольку он неблокирующий (асинхронный) и масштабируемый. Также было проще использовать SNS для разделения двух функций в контексте AWS 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`
person nonbeing    schedule 18.08.2016
comment
Разделение работы между «диспетчерской» и «рабочей» лямбда-функцией, как упомянуто здесь, является хорошим выбором дизайна. Однако в одном случае, который у меня был, «диспетчер» использовал более 3 с, и я не мог удалить из него больше кода (была задержка из-за внешних ресурсов в AWS SSM). Мое решение состояло в том, чтобы просто увеличить память функции диспетчера, так как в Lambda производительность процессора связана с настройкой памяти. - person toringe; 05.10.2018