Je travaille avec la commande slack (le code python fonctionne derrière cela), cela fonctionne bien, mais cela donne une erreur
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
Comment éviter cela?
Selon le Slack documentation de la commande slash , vous devez répondre dans les 3000 ms (trois secondes). Si votre commande prend plus de temps, vous obtenez l'erreur Timeout was reached
. Votre code ne s'arrêtera évidemment pas, mais l'utilisateur n'obtiendra aucune réponse à sa commande.
Trois secondes, c'est bien pour une chose rapide où votre commande a un accès instantané aux données, mais peut ne pas être assez longue si vous appelez des API externes ou faites quelque chose de compliqué. Si vous avez besoin de prendre plus de temps, alors voyez Réponses différées et réponses multiples section de la documentation:
200
, Peut-être quelque chose comme {'text': 'ok, got that'}
response_url
Unique. Faites une demande de POST
à cette URL avec votre message de suivi: Content-type
Doit être application/json
{'text': 'all done :)'}
Selon les documents, "vous pouvez répondre aux commandes d'un utilisateur jusqu'à 5 fois dans les 30 minutes suivant l'appel de l'utilisateur".
Après avoir résolu ce problème moi-même et avoir hébergé mon application Flask sur Heroku, j'ai trouvé que la solution la plus simple était d'utiliser le filetage. J'ai suivi l'exemple d'ici: 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")
Tout le travail lent et lourd est effectué par la fonction backgroundworker()
. Ma commande slack pointe vers https://myappaddress.com/appmethodaddress
Où la fonction receptionist()
prend le response_url
Du message Slack reçu et le transmet avec toutes les autres données facultatives à la backgroundworker()
. Comme le processus est maintenant divisé, il renvoie simplement le message "working on your request"
À votre canal Slack presque instantanément et une fois terminé, backgroundworker()
envoie le deuxième message "your task is complete"
.
Moi aussi, je faisais souvent face à cette erreur:
"Darn - cette commande slash n'a pas fonctionné (message d'erreur:
Timeout was reached
). Gérez la commande à slash-command "
J'écrivais un Slack slash-command "bot" sur AWS Lambda qui devait parfois effectuer des opérations lentes (invoquer d'autres API externes, etc.). La fonction Lambda prendrait plus de 3 secondes dans certains cas, provoquant le Timeout was reached
erreur de Slack.
J'ai trouvé ici l'excellente réponse de @ rcoup et je l'ai appliquée dans le contexte d'AWS Lambda. L'erreur n'apparaît plus.
Je l'ai fait avec deux fonctions Lambda distinctes. L'un est un "répartiteur" ou "réceptionniste" qui salue la commande Slash entrante avec un "200 OK" et renvoie le simple type de message "Ok, got that" à l'utilisateur. L'autre est la fonction Lambda "de travail" réelle qui démarre l'opération de longue durée de manière asynchrone et publie le résultat de cette opération sur le Slack response_url
plus tard.
Voici la fonction Lambda du répartiteur/réceptionniste:
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
Comme vous pouvez le voir ci-dessus, je n'ai pas appelé la fonction Lambda de travail directement depuis le répartiteur (bien que cela soit possible). J'ai choisi de tiliser AWS SNS pour publier un message que le travailleur reçoit et traite .
Basé sur cette réponse StackOverflow , c'est la meilleure approche car elle est non bloquante (asynchrone) et évolutive. De plus, il était plus facile d'utiliser SNS pour découpler les deux fonctions dans le contexte d'AWS Lambda, l'invocation directe est plus délicate pour ce cas d'utilisation.
Enfin, voici comment je consomme l'événement SNS dans ma fonction Lambda de travail:
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`