Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

Создайте поток, чтобы выполнить большую часть работы и вернуть ответ на слабину. Вы также можете вернуть ответ, когда поток завершится.

Ответ 2

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

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

  • Подтвердить запрос в порядке.
  • Немедленно возвращайте ответ 200, возможно, что-то вроде строк {'text': 'ok, got that'}
  • Перейдите и выполните действительное действие, которое вы хотите сделать.
  • В исходном запросе вы получите уникальный параметр response_url. Сделайте запрос POST на этот URL с последующим сообщением:
    • Content-type должен быть application/json
    • С телом как JSON-кодированное сообщение: {'text': 'all done :)'}
    • вы можете возвращать эфемерные или внутриканальные ответы и добавлять вложения так же, как непосредственный подход

В соответствии с документами "вы можете отвечать на команды пользователя до 5 раз в течение 30 минут после вызова пользователя".

Ответ 3

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

"Darn - эта команда косой черты не работает (сообщение об ошибке: Timeout was reached). Управляйте командой с помощью косой черты"

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

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

Я сделал это с двумя отдельными лямбда-функциями. Один из них - "диспетчер" или "регистратор", который приветствует входящую команду Slack slash с "200 OK" и возвращает пользователю сообщение "Ok, got that". Другая - действительная "рабочая" функция лямбда, которая запускает длинную операцию асинхронно и позже отправляет результат этой операции в 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

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

Основываясь на fooobar.com/questions/48851/..., это лучший подход, поскольку он не блокирует (асинхронно) и масштабируется. Также было проще использовать SNS для развязки двух функций в контексте AWS Lambda, прямой вызов для этого прецедента более сложный.

Наконец, вот как я использую событие SNS в моей рабочей Lambda Function:

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`

Ответ 4

После решения этой проблемы сам и имея мое приложение 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".