Я работаю с командой slack (за этим работает код Python), он работает нормально, но выдает ошибку
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
Как этого избежать?
Я работаю с командой slack (за этим работает код Python), он работает нормально, но выдает ошибку
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
Как этого избежать?
Согласно документации по команде Slack, вам нужно ответить в течение 3000 мс (трех секунд). Если ваша команда занимает больше времени, вы получаете ошибку Timeout was reached
. Ваш код, очевидно, не перестанет работать, но пользователь не получит никакого ответа на свою команду.
Трех секунд достаточно для быстрой работы, когда ваша команда имеет мгновенный доступ к данным, но может быть недостаточно, если вы обращаетесь к внешним API или делаете что-то сложное. Если вам действительно нужно больше времени, см. раздел документации Отложенные ответы и множественные ответы:
200
, возможно, что-то вроде {'text': 'ok, got that'}
response_url
parameter. Make a POST
request to that URL with your follow-up message:
Content-type
needs to be application/json
{'text': 'all done :)'}
Согласно документам, «вы можете отвечать на команды пользователя до 5 раз в течение 30 минут после вызова пользователя».
После того, как я сам разобрался с этой проблемой и разместив свое приложение 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"
.
Я тоже часто сталкивался с этой ошибкой:
«Черт возьми, эта косая черта не сработала (сообщение об ошибке:
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`